Chapter 6: Defining functions
When your script repeats the same logic in multiple handlers, you can
extract it into a named function with def. This chapter covers def,
top-level const, blocks as values, and how to organize larger scripts.
Defining a function with def
def creates a named command you can call like a builtin:
def greet { <person>
do "say Hello, [name $person]! Welcome to the shop."
do "bow [name $person]"
}
after enter {
require [isplayer $actor]
greet $actor
}
after command (say) {
require [keyword $args hi hello]
greet $actor
}
def greet { <person> ... }defines a command calledgreetthat takes one argument.greet $actorcalls it, just like calling a builtin.- The
<person>parameter receives the argument value.
def is top-level only — it must be outside any handler. It cannot
be nested inside a handler body.
Named constants with const
const binds a name to an immutable value:
const QUEST_VNUM 5001
const REWARD_VNUM 5020
const GREETING 'Welcome, adventurer!'
after enter {
do "say $GREETING"
}
after command (say) {
require [keyword $args quest]
if [isholding $actor $QUEST_VNUM] {
oload $REWARD_VNUM $actor
do "say Here is your reward!"
}
}
Using constants makes scripts easier to update — change the vnum in one place instead of hunting through every handler.
Top-level structure
A script file has three kinds of top-level forms, in any order:
def name { ... }— function definitions.const name value— named constants.phase event { ... }— handlers.
# Constants at the top
const SHOP_GREETING 'Welcome to my shop!'
# Helper functions
def announce { <msg>
echo [room $self] $msg
}
# Handlers
after enter {
require [isplayer $actor]
announce $SHOP_GREETING
}
Blocks as values
A block { ... } is a value, like a number or a string. You can
store it in a variable and call it later:
after command (say) {
require [keyword $args test]
let action { do "say This came from a block!" }
[$action]
}
[$action] calls the block stored in $action. This is how you pass
behavior around — for example, as a predicate to select or each.
Blocks with parameters
Blocks can take parameters, declared with <...>:
let double { <n> [* $n 2] }
let result [$double 5]
do "say Double of 5 is $result"
The & sigil
&name takes a def'd function and returns it as a block value,
useful when you need to pass a function to something like select:
def is_warrior { <c>
[eq [class $c] 'Warrior']
}
after command (say) {
require [keyword $args warriors]
let warriors [select [people [room $self]] &is_warrior]
each $warriors { <w>
do "say [name $w] is a warrior."
}
}
Without &, the bare name is_warrior in argument position would be
the string "is_warrior". &is_warrior gives you the function as a
value to pass to select.
Closures
Inner blocks can read variables from their enclosing scope:
after command (say) {
require [keyword $args above]
let threshold [level $actor]
let stronger [select [people [room $self]] { <c>
[gt [level $c] $threshold]
}]
each $stronger { <c>
do "say [name $c] is above level $threshold."
}
}
The block { <c> [gt [level $c] $threshold] } can see $threshold
from the enclosing handler body. This is a closure — the block
captures the enclosing variable.
def functions cannot see $actor or $self
def functions live at the top level, outside any handler. They do
not see handler-injected variables like $actor or $self — those
only exist inside a handler body. If your function needs them, pass
them as arguments:
def tell { <target msg>
do ">$target $msg"
}
after command (say) {
require [keyword $args help]
tell $actor "I offer: heal, bless."
}
Example: a mob with multiple services
const HEAL_COST 100
def tell { <target msg>
do ">$target $msg"
}
after command (say) {
require [keyword $args heal]
tell $actor "I can heal you for $HEAL_COST gold."
tell $actor "Say 'pay heal' when ready."
}
after command (say) {
require [keyword $args bless]
tell $actor "I will bless you freely."
spell $actor 30 bless
tell $actor "There you go."
}
after command (say) {
require [keyword $args help services]
tell $actor "I offer: heal, bless."
tell $actor "Say the service name to learn more."
}
The tell function takes the target as its first argument, avoiding
do ">$actor ..." in every handler.
Key points
def name { <params> body }defines a reusable command.const name valuedefines an immutable constant.- Both are top-level only and are visible throughout the script.
&nameconverts adeffunction into a block value.- Blocks are closures — they see variables from their enclosing scope.
deffunctions do not see$actor/$self— pass them as arguments.