Reacting to events
A script does nothing until something happens. You react by writing a handler: a block bound to one phase of one event on the script's owner. Almost every recipe in this guide begins with a handler, so this chapter is about getting that frame right — picking the event, the phase, and the conditions under which your code should run.
after command (say) {
echo $actor "A cold draft raises the hair on your neck."
}
This fires after a creature near the owner uses the say command. The
shape is always <phase> <event> [(filter)] { body }.
Choosing a phase
Each event runs around its default action — the normal effect of the command. You get up to three phases:
beforeruns ahead of the default action. It is advisory: the default still happens. Use it to warn, log, or pre-check.handleruns instead of the default action, and is the only phase that can suppress it (see Intercepting).afterruns once the default action is done, so it sees the resulting world. Use it for reactions and follow-ups.
before command (open) {
echo $actor "The hinges groan ominously." # door still opens
}
Some events are restricted to one phase: fight and load are
after-only; idle, combat, and tick are handle-only. A handler
written for a phase an event never uses simply never runs.
Filters: which instances fire
For command and spell, a parenthesized filter narrows the handler
to the keywords or spell numbers you care about. Almost every command
handler is filtered — an unfiltered one fires on every command the owner
can observe.
handle command (push nudge shove) { ... } # only these three verbs
handle spell (42 30) { ... } # only these spell numbers
handle tick { ... } # no filter allowed here
Events with no natural selector (tick, load, idle, combat) reject a
filter.
Bound variables
A handler body never declares parameters. Instead the event injects names.
Every handler gets $self (the owner); the rest depend on the event. The
ones you reach for most:
$actor— the creature that triggered the event.$arg— forcommand, the raw text after the verb, as a string.$args— the same text as a word iterable (see Finding things).$object— the item, forgive/put/getfrom.$spell— the spell number, forspell.$killer— the slayer, fordeath(may be null).
handle command (poke) {
echo $actor "$self glares at you for poking."
}
Reading a name the event does not provide — $actor in a tick handler,
say — is a run-time error. The full event-to-variable table lives in the
handlers reference.
$arg and $args are two views of the same post-verb text. Use $arg
for whole-line matching and $args when you want the individual words:
handle command (say) {
if [streqi $arg "open sesame"] {
echo $actor "The wall slides aside."
}
}
Guards: stating preconditions
Several handlers can match the same (phase, event). They are tried in
source order, and a handler that runs its body to the end ends the
script — control does not pass to the next matching handler on its own.
The two ways to move on to the next matching handler are a filter that does
not match, and a guard that fails.
The guards are require and unless. Each takes one bool and no
body:
require [cond] # if cond is false, abandon this handler, try the next
unless [cond] # if cond is true, abandon this handler, try the next
Put guards at the top of a handler to declare what must be true for it to act:
after command (say) {
require [isplayer $actor]
require [ge [level $actor] 10]
echo $actor "Only seasoned players hear this whisper."
}
A non-player fails the first guard; a low-level player fails the second;
either way the handler is abandoned. unless is the negation —
unless [isplayer $actor] abandons the handler when the actor is a
player.
Chaining guarded handlers lets each claim the cases it wants:
# two handlers share the say event, splitting the work by keyword
after command (say sayto) {
require [keyword $args hello hi greetings]
echo $actor "$self returns your greeting warmly."
}
after command (say sayto) {
require [keyword $args bye farewell]
echo $actor "$self waves goodbye."
}
keyword matches any word of $args against
the listed words; abbrev does the same but
accepts prefixes, so abbrev $args north also matches n or nor.
Intercepting the default action
In the handle phase, performing a successful action command — an
echo, do,
emit, oload, trans, and so on —
suppresses the event's default action. That is how a handle handler
replaces the normal behavior:
handle command (read) {
echo $actor "The runes are too faded to make out."
}
Because the echo succeeds, the normal read never happens. Two
important caveats:
- A command that fails (for example an
oloadof a vnum that does not exist) does not suppress. - Commands that only touch computation or bookkeeping —
let,store,if— never suppress. Suppression is decided entirely by whether ahandleaction command succeeded.
before and after handlers never suppress, no matter what they do.
Stopping early
halt ends the current handler immediately — nothing after it runs:
handle command (say) {
echo $actor "I said all I am going to say."
halt
echo $actor "you will never see this line" # unreachable
}
halt differs from a failed guard: a guard advances to the next matching
handler, while halt ends the script outright. (For the looser flow forms
return, break, and continue, see
Structuring scripts.)
Self-suppression
For command, enter/leave, and spell, a handler does not fire
when the owner mobile is itself the actor — a mob does not react to its own
induced actions, and chat skips the owner being the speaker. This keeps a
script that makes its owner say something from triggering its own say
handler in a loop.
See also
- Talking and messaging — the action commands a handler runs.
- Handlers reference — the full event catalog and bound-variable table.
- Control flow · Guards