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:

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:

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:

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