Calling Behaviors¶
Quick Start: Spice it up with twists.
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-escortshows the same shape with four blocks: anON_STARTpick into<gflag:vip>, anON_DEATHblock that ends the game when the VIP dies, anON_STARTplusON_RESPAWNblock writing one infinite effect entry for the VIP, and anON_CLEANUPblock clearing it when the escort ends.- 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_ORDERorPICK_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 (
acallsbcallsa) 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>isarg1,<args:1>isarg2, 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 asa, 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>>"
One File Per Side Choice (Team Gate)¶
When a modifier can hit either side, ship one file, not one per
role. A team ENUM setting names the side, a short player: gate
compares it against <prole:<p>>, and a called-only block holds
the shared script (called blocks omit runs-on on purpose):
config:
settings:
team:
def:
meta:
name: Team
desc: Which side this modifier affects.
icon: SHIELD
folder:
- affected_team:
def:
meta:
name: Affected Team
desc: Tunable setting for which team this mod affects.
icon: SHIELD
value:
type: ENUM
default: BOTH
allowed: [BOTH, RUNNER, HUNTER]
behavior:
main:
runs-on:
- INTERVAL
options:
interval-settings:
interval: 2.5
behavior: PER_INVOKE
commands:
player:
- '<lf:mine,<prole:<p>>>'
- '<lf:wanted,<setting:team.affected_team>>'
- '<lf:hit,<if:"<lf:wanted> == BOTH and <lf:mine> != null",true,false>>'
- '<if:"<lf:wanted> == RUNNER and <lf:mine> == SPEEDRUNNER","<lf:hit,true>">'
- '<if:"<lf:wanted> == HUNTER and <lf:mine> == HUNTER","<lf:hit,true>">'
- '<if:"<lf:hit> == true","<runblock:logic>">'
logic:
commands:
player:
- '<lf:team,<active-players:<prole:<p>>>>'
- The gate matches
RUNNERagainst theSPEEDRUNNERside (<prole:>only yieldsHUNTERorSPEEDRUNNER); the null guard keepsBOTHto both teams, never spectators. - The one-part
<runblock:logic>call inherits the executor (<p>) and the interval seconds (<args:0>), while local flags and<def:>functions stay per block, so the called block re-derives everything from<p>,<args:0>, and settings.