Calling Behaviors¶
For: advanced authors fanning out into reusable blocks.
Big bundled mods like infection and vip-escort run a fatter
pipeline than the simple one in Modifiers: event
blocks guard and record shared state, then timer blocks evaluate per
member. Here it is with infection:
1. Event block fires (ON_DEATH)
A speedrunner dies what might be their final death.
|
v
2. Guard lines exit early (<if> plus exit)
Former role SPEEDRUNNER? Eliminated for good?
Anything else leaves quietly.
|
v
3. Shared state remembers (flags)
<pswitch> the victim to HUNTER, append them
to the <gflag:infected> list.
|
v
4. Timer block evaluates per member (INTERVAL)
Every 12s per hunter: on the infected list?
Root hunters exit on line one.
|
v
5. Game-world effects, gated by chance
Sporadic blindness, groceries on luck
(each candidate fails 80% of the time).
vip-escort shows the same shape with three blocks: an ON_START
pick into <gflag:vip>, an ON_DEATH block that ends the game when
the VIP dies, and a 1-second INTERVAL block looping effects over the
VIP and clearing their armor. Bingo cards, bounties, and position
swaps all follow this pattern: guard, remember, evaluate per member,
apply.
<runblock:id> runs another behavior block of the same modifier
inline, in the current run: same executor, same event args. The
target's console list always runs; its player, hunter, and
speedrunner lists run only when a live executor exists. The target
keeps its execution mode (IN_ORDER or PICK_RANDOM) and its
success chance; its triggers, interval, and delay are ignored, so a
called-only block never fires on its own. The classic split is one
interval block that refreshes state, checks completions, and calls a
broadcast block only when something is due:
behavior:
eligibility:
runs-on:
- INTERVAL
options:
interval-settings:
interval: 8
deviation: 2
commands:
console:
- "<runblock:refresh>"
- "<if:<gflag:dirty>==true,<runblock:broadcast>>"
refresh:
commands:
console:
- "<gflag:dirty,false>"
broadcast:
commands:
console:
- "<gmessage:standings updated>"
Chains share the calling line's step budget with loops and
functions, cycles (a calls b calls a) stop with one warning
per match, and unknown ids warn once per match and skip. A block
that calls itself stops the same way, so prefer one direction:
callers call down, called blocks never call back.
<runblock:id,arg1,arg2> passes arguments: inside the target,
<args:0> is arg1, <args:1> is arg2, and so on (override-all:
passed args replace the caller event args entirely, so a block that
needs both must receive the event values explicitly). Args evaluate
in the caller context before the hop, so <i> and <lf> names in
arg position resolve per iteration; one surrounding quote layer is
stripped ("a, b" arrives as a, b). The one-part form keeps
inheriting the caller args, which is how an interval block hands its
elapsed time to a refresh block without naming it.
# bingo: eligibility fans out per member with the name as arg
- "<for:<active-players:ALL>,<runblock:check-member,<i>>>"
# check-member completes one slot with WHO and SLOT as args
- "<runblock:announce-slot,<lf:who>,<i>>"
# announce-slot binds them to locals first
- "<lf:who,<args:0>>"
- "<lf:slot,<args:1>>"