Handlers, events, and phases
A script does nothing on its own. It reacts to events — someone says something, a creature enters the room, a tick passes — through handlers. A handler binds a block to one phase of one event on the script's owner.
after command (say) {
echo $actor "A cold draft raises the hair on your neck."
}
This handler fires after someone in the room uses the say command. A
script with no handlers never responds to anything;
def/const declarations alone are inert.
Handler shape
<phase> <event> [ (<filter>) ] { <body> }
<phase>isbefore,handle, orafter(see Phases).<event>is one of the event kinds.- the optional
(<filter>)restricts which instances fire — forcommand, the command keywords; forspell, the spell numbers or names. An event with no natural selector (tick,load,idle,combat) rejects a filter. - the body is a block that takes no parameters — instead the event
provides bound variables. A handler body that
declares a
<...>parameter list is a compile-time error.
handle command (push nudge) { ... } # only the push and nudge commands
spell (42 30) { ... } # only those spell numbers
after tick { ... } # no filter allowed
Phases
Each event can be handled in up to three phases, which run around the event's default action (the normal effect of the command or event):
beforeruns before the default action. It is advisory — the default action happens regardless of whatbeforedoes.handleruns in place of / alongside the default action, and is the only phase that can intercept it.afterruns after the default action has completed, so it sees the resulting world state.
Suppression (interception)
When a handle-phase body performs a successful action command — an
echo, do, emit, oload, trans, and
so on — the event's default action is suppressed: it does not happen.
That is how a handle handler replaces the normal behavior with its own.
A command that fails (for example an oload of a vnum that does not
exist) does not suppress. And commands that only touch computation,
variables, or flow — let, set,
store/recall, if — never
suppress: persistent state is the handler's own bookkeeping, not a
world-altering action. before and after handlers never suppress.
There is no explicit "block this event" command; suppression is decided
solely by whether a handle-phase action command succeeded.
Event kinds
There are 23 event kinds, grouped below by what makes them fire. Each entry names the circumstance that triggers the event; for the names it binds, see Bound variables.
Creature actions in the owner's reach
These fire on a mob or room when a creature acts within it. The owner is never triggered by its own actions (see Self-suppression).
command— a creature enters any game command the owner can observe (a mob in the room, or the room itself). Almost always filtered to the verbs you care about:command (say push).enter— a creature arrives in the owner's room.leave— a creature departs the owner's room.chat— a creature directs speech at the owner mob (addresses it by name withsay/sayto); general room chatter does not trigger it.give— a creature hands an object to the owner mob ($objectis the item).spell— a spell is cast on the owner mob, or in the owner room ($spellis the spell number).
Item manipulation
These fire on the object being acted on — the handler lives on that
object's script, with $self the object and $actor the creature acting.
get— picked up from the ground.drop— dropped to the floor.put— placed into a container ($objectis the container).getfrom— taken out of a container ($objectis the container).wear— worn as armor.wield— wielded as a weapon.remove— taken off / unequipped.eat— eaten.drink— drunk from.sacrifice— sacrificed.search— a player searches the object ($arg/$argscarry the search text).
Combat and death
fight— combat is initiated involving the owner mob;$actoris the other combatant.after-only (deferred to the next tick).combat— fires on every activity pulse while the owner mob is in combat — the in-combat counterpart ofidle.handle-only, no actor.death— a creature dies in the owner's room. It fires on the dier, the room, and every witnessing mob, so it covers both "I am dying" and "someone died near me"; tell them apart with[eq $actor $self](see Death).$killermay be null.
Time and lifecycle (no actor)
idle— fires on a mob (only when not fighting), room, or object on the frequent activity pulse (every few seconds).handle-only.tick— fires on a mob or room once per game hour — the slow MUD "tick," far less frequent thanidle.handle-only.load— fires once, just after the owner is created in the world (zone reset,mload/oload, a spawned mob).after-only, deferred to the next tick.
Phase availability. Most events support all three phases, but the restricted ones above do not:
fightandloadrun only inafter;idle,combat, andtickrun only inhandle. A handler written for a phase an event never fires in is simply dead — it never runs.
Bound variables
A handler body never declares parameters. Instead the event injects names
into the body's scope before it runs. Every handler gets $self, the
owner; the rest depend on the event:
| Event(s) | Bound (besides $self) |
|---|---|
command |
$actor (who ran it), $arg (the raw post-verb line, a string), $args (the same line as a word iterable) |
spell |
$actor (caster), $spell (int spell number) |
give |
$actor (giver), $object (the item) |
put, getfrom |
$actor, $object (the container); the moved item is $self |
get, drop, wear, remove, wield, eat, drink, sacrifice, search |
$actor; the item is $self |
enter, leave, chat, fight |
$actor |
death |
$actor (the deceased), $killer (may be null) |
load, tick, idle, combat |
(none beyond $self) |
$self is the script's owner — a creature, object, or room. $actor is
the canonical "the creature that triggered this." Reading a name the event
does not provide — $actor in a tick handler, say — is a run-time
error.
$arg and $args are two views of a command handler's post-verb text:
$argis the raw string — use it withstreqi,eq, or interpolation.$argsis the same text as a word iterable — read it withfirst,count,keyword,each, and so on.
handle command (push) {
each $args { <a> do "say arg from [name $actor]: $a" }
}
The block sees $actor because the injected names are lexical: nested
blocks see them through their enclosing scope. (A top-level
def does not — pass it what it needs.)
Death
All three death phases bind the same names: $self (owner), $actor (the
deceased), $killer (may be null). The dier's own handler sees
$self == $actor. A handle death can abort the death by performing a
successful action command — but it must heal the dier itself (HP is not
restored automatically).
before death {
if [eq $actor $self] {
do "say I... regret nothing!"
}
}
Guards
Multiple handlers can match the same (phase, event). They are tried in
source order, and a handler that runs its body to the end terminates the
script — there is no implicit fall-through to the next handler. The only
ways to advance to the next matching handler are:
- a filter that does not match (the handler is skipped), or
- a guard that fails.
The two guards are require and unless. Each takes a single bool
condition 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 state its preconditions. If a guard fails, the script moves on to the next matching handler; if there is none, the script ends.
after command (say) {
require [ge [level $actor] 10]
do "say only veterans hear this."
}
Only an actor of level 10 or higher reaches the do; a lower-level
actor fails the require and the handler is abandoned. unless is
the negation — unless [isplayer $actor] abandons the handler when
the actor is a player.
Chaining guards lets several handlers share an event, each claiming the cases it wants:
after command (say sayto) {
require [keyword $args chat gossip]
store $self 'state' 'gossip'
}
Cross-tick handlers
A handler normally runs to completion within one game tick. To stretch
across ticks, use pause n, which suspends the handler
for n ticks and resumes with its locals intact:
after enter {
do "bow $actor"
pause 3
do "emote points to a sign."
}
Self-suppression
For command, enter/leave, and spell events, 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 speaker being the owner).
This keeps a script from triggering itself in a loop.
See also
echo,do,emit— action commands that can suppress ahandle-phase event.- Control flow —
pause,halt,return. - Variables · Scope · Persistent state · Iterables