diff options
Diffstat (limited to 'building_guide/triggers.md')
| -rw-r--r-- | building_guide/triggers.md | 308 |
1 files changed, 157 insertions, 151 deletions
diff --git a/building_guide/triggers.md b/building_guide/triggers.md index c32830d..ba44c40 100644 --- a/building_guide/triggers.md +++ b/building_guide/triggers.md @@ -181,8 +181,8 @@ entity: | Object | `on_use`, `on_look` | | Mob | `on_kill` | | Exit (room YAML) | `on_traverse` | -| Room | `on_enter`, `on_exit` (NEW), `on_flag_change`, `on_global_flag_change` | -| Global file (`data/triggers/*.yaml`) | one Trigger per file (flag triggers) | +| Room | `on_enter`, `on_exit` (NEW) | +| Flag trigger file (`data/triggers/player_flag/`, `data/triggers/global_flag/`) | one Trigger per file (`on_player_flag` / `on_global_flag`) | - **on_use / on_look** — see `objects.md`. `item_id` filters which item triggers the entry. - **on_kill** — see `mobs.md`. `item_id` filters by wielded weapon. @@ -193,8 +193,7 @@ entity: - **on_exit** (NEW) — fires when a player leaves the room, **before** `RoomID` is updated to the destination. Broadcasts/spawn resolve against the old (departed) room. Mirror of on_enter, useful for parting messages, closing spawns, or recording departures. -- **on_flag_change** / **on_global_flag_change** — flag-watch triggers, replacing the old - room `triggers:` block. See Flag triggers below. +- **on_flag_change** / **on_global_flag_change** — flag-watch triggers are **file-backed**, one per file under `data/triggers/player_flag/` or `data/triggers/global_flag/`. They are not room fields. See Flag triggers below. ### Quick Start: Landing Sequence @@ -259,91 +258,99 @@ triggers watching that flag. ### Flag triggers -`on_flag_change` and `on_global_flag_change` replace the old room `triggers:` block. Each -entry carries the flag it watches (`on_player_flag` or `on_global_flag`), an optional -`condition` (use `player_flag`/`global_flag` + `value` to match a specific flag value), and -a `steps` list. +A flag trigger is a **standalone trigger file**, one per flag, under +`data/triggers/player_flag/` (set `on_player_flag:`) or `data/triggers/global_flag/` (set +`on_global_flag:`). The filename stem is the trigger id. Each file carries the flag it +watches, an optional `condition` (use `player_flag`/`global_flag` + `value` to match a +specific flag value), and a `steps` list. The block sits at the top level of the file — +there is no `on_flag_change:` wrapper and no room-file form. Key behaviors: - **Listen globally.** A flag-change trigger fires regardless of where the flag is set. - The event block lives in a room (for broadcasts/spawns to use that room) or in a global - file (see Global trigger files). + Add `condition: { room: <id> }` when a broadcast/spawn should resolve against a specific + room, or use a global trigger file for server-wide events (see Global trigger files). - **Room scoping is opt-in.** Use `condition: { room: <id> }` to restrict the trigger to - players currently in that room. Migration injects `{ room: <roomID> }` into migrated - room triggers to preserve the old room-scoped behavior; authors can delete it to make the - trigger fire globally. + players currently in that room. - **Fires on the non-existent → existent transition.** Setting a flag that didn't exist before counts as a change and fires the trigger. Setting a flag to the same value it already has does **not** re-fire. - **Triggers activate on becoming truthy.** Setting `true → false` does **not** fire. ```yaml -# A room watches a player flag and plays a sequence for that player -on_flag_change: - - on_player_flag: lever_pulled - steps: - - broadcast: "The lever snaps back into place with a loud clunk." - - set_player_flags: - lever_pulled: false # reset so next pull re-fires +# data/triggers/player_flag/lever_pulled.yaml +on_player_flag: lever_pulled +steps: + - broadcast: "The lever snaps back into place with a loud clunk." + - set_player_flags: + lever_pulled: false # reset so next pull re-fires ``` -To migrate the old room `triggers:` block, replace `triggers:` with `on_flag_change:` (for -`on_player_flag`) or `on_global_flag_change:` (for `on_global_flag`) and add -`condition: { room: <id> }` if you want the old room-scoped behavior. +A trigger file that sets neither `on_player_flag` nor `on_global_flag` is a startup ERROR: +it is loaded but indexed nowhere and can never fire. ### Value-matching flag triggers By default a flag trigger fires when the watched flag becomes truthy. Use a `condition` with a `player_flag`/`global_flag` + `value` to require a specific value — enabling multi-stage -quests off one numeric flag: +quests off one numeric flag. Because each file watches exactly one flag, a multi-stage flag +uses one file per stage: ```yaml -on_flag_change: - - on_player_flag: quest_stage - condition: - player_flag: quest_stage - value: 1 - steps: - - messages: ["Quest started — find the crystal shard."] - - on_player_flag: quest_stage - condition: - player_flag: quest_stage - value: 2 - steps: - - messages: ["You found the shard — return to the elder."] - - on_player_flag: quest_stage - condition: - player_flag: quest_stage - value: 3 - steps: - - messages: ["The ritual begins..."] - - wait: 10 - broadcast: "The temple hums with ancient power!" - - spawn_mob: crystal_guardian +# data/triggers/player_flag/quest_stage_1.yaml +on_player_flag: quest_stage +condition: + player_flag: quest_stage + value: 1 +steps: + - messages: ["Quest started — find the crystal shard."] +``` + +```yaml +# data/triggers/player_flag/quest_stage_2.yaml +on_player_flag: quest_stage +condition: + player_flag: quest_stage + value: 2 +steps: + - messages: ["You found the shard — return to the elder."] +``` + +```yaml +# data/triggers/global_flag/ritual.yaml +on_global_flag: ritual_stage +condition: + global_flag: ritual_stage + value: 3 +steps: + - messages: ["The ritual begins..."] + - wait: 10 + broadcast: "The temple hums with ancient power!" + - spawn_mob: crystal_guardian ``` ### Global flag trigger -A room can watch a global flag and fire once globally (not per-player). Everyone in the -room sees the broadcast: +A global-flag trigger file fires once globally (not per-player), and its broadcast is seen +by every connected player. (`broadcast` inside a player-flag trigger is seen by that player.) ```yaml -on_global_flag_change: - - on_global_flag: floodgate_open - steps: - - broadcast: "Ancient gears grind as the floodgate slowly opens..." - - wait: 15 - broadcast: "Water thunders through the opening!" - - set_global_flags: - valley_flooded: true +# data/triggers/global_flag/floodgate.yaml +on_global_flag: floodgate_open +steps: + - broadcast: "Ancient gears grind as the floodgate slowly opens..." + - wait: 15 + broadcast: "Water thunders through the opening!" + - set_global_flags: + valley_flooded: true ``` ### Global trigger files -Global triggers live one-per-file in `data/triggers/`. They fire regardless of where the -flag-setting player is. Use them for server-wide events. The filename stem is the trigger -ID (must be globally unique). One Trigger per file — the block fields sit at the top level: +Global triggers live one-per-file in `data/triggers/player_flag/` or +`data/triggers/global_flag/`. They fire regardless of where the flag-setting player is. Use +them for server-wide events. The filename stem is the trigger ID (must be globally unique). +One Trigger per file — the block fields sit at the top level: ```yaml # data/triggers/announce_99.yaml @@ -374,24 +381,23 @@ steps: ### Boss arena — puzzle unlocks a boss -Player activates an altar (on a separate object) setting a player flag, and this room's -`on_flag_change` triggers the boss spawn: +Player activates an altar (on a separate object) setting a player flag, and a flag trigger +file spawns the boss: ```yaml -# data/rooms/dungeon/9001.yaml -on_flag_change: - - on_player_flag: activated_altar - steps: - - broadcast: "The altar glows with an eerie light..." - - wait: 10 - broadcast: "The ground trembles beneath your feet." - - wait: 20 - broadcast: "A massive guardian emerges from the shadows!" - - spawn_mob: - id: altar_guardian - owner_only: true - despawn_on_leave: true - despawn_rooms: [450, 451, 452] +# data/triggers/player_flag/activated_altar.yaml +on_player_flag: activated_altar +steps: + - broadcast: "The altar glows with an eerie light..." + - wait: 10 + broadcast: "The ground trembles beneath your feet." + - wait: 20 + broadcast: "A massive guardian emerges from the shadows!" + - spawn_mob: + id: altar_guardian + owner_only: true + despawn_on_leave: true + despawn_rooms: [450, 451, 452] ``` The boss is `owner_only` — only the player who triggered it can interact with it. It @@ -400,23 +406,23 @@ When the owner leaves those rooms, the boss begins despawning. ### Story beat — timed cutscene after NPC conversation -An NPC conversation node sets `quest_ritual: started`. A room trigger plays a dramatic -sequence for that player: +An NPC conversation node sets `quest_ritual: started`. A flag trigger file plays a +dramatic sequence for that player: ```yaml -on_flag_change: - - on_player_flag: quest_ritual - steps: - - wait: 8 - messages: ["The elder begins to chant in a language you don't recognize."] - - wait: 6 - messages: ["Wisps of light swirl around the altar."] - - wait: 4 - messages: ["The ground beneath you shudders as the ritual reaches its peak."] - - wait: 6 - broadcast: "A blinding flash fills the chamber!" - - teleport: 601 - messages: ["You open your eyes. You're somewhere else entirely."] +# data/triggers/player_flag/quest_ritual.yaml +on_player_flag: quest_ritual +steps: + - wait: 8 + messages: ["The elder begins to chant in a language you don't recognize."] + - wait: 6 + messages: ["Wisps of light swirl around the altar."] + - wait: 4 + messages: ["The ground beneath you shudders as the ritual reaches its peak."] + - wait: 6 + broadcast: "A blinding flash fills the chamber!" + - teleport: 601 + messages: ["You open your eyes. You're somewhere else entirely."] ``` The final step uses `teleport` and `messages` (the triggering player is moved and sees a @@ -430,19 +436,22 @@ When a step sets a flag, any triggers watching that flag fire immediately (on th tick). This lets you chain sequences: ```yaml -on_flag_change: - - on_player_flag: phase_1_done - steps: - - broadcast: "The first seal cracks." - - set_player_flags: - phase_2_started: true # triggers the next trigger +# data/triggers/player_flag/phase_1_done.yaml +on_player_flag: phase_1_done +steps: + - broadcast: "The first seal cracks." + - set_player_flags: + phase_2_started: true # triggers the next trigger +``` - - on_player_flag: phase_2_started - steps: - - wait: 10 - broadcast: "The second seal glows brighter..." - - wait: 10 - spawn_mob: phase_2_adds +```yaml +# data/triggers/player_flag/phase_2_started.yaml +on_player_flag: phase_2_started +steps: + - wait: 10 + broadcast: "The second seal glows brighter..." + - wait: 10 + spawn_mob: phase_2_adds ``` Both sequences run concurrently — phase 2's wait countdown starts on the same tick phase @@ -506,13 +515,13 @@ spawn_mob: Use `despawn_mob` in a step to remove previously spawned mobs: ```yaml -on_flag_change: - - on_player_flag: puzzle_solved - steps: - - despawn_mob: puzzle_guardian # remove the puzzle mob - - wait: 10 - broadcast: "The guardian dissolves into mist." - - spawn_mob: boss_guardian # spawn the real boss +# data/triggers/player_flag/puzzle_solved.yaml +on_player_flag: puzzle_solved +steps: + - despawn_mob: puzzle_guardian # remove the puzzle mob + - wait: 10 + broadcast: "The guardian dissolves into mist." + - spawn_mob: boss_guardian # spawn the real boss ``` `despawn_mob` with an owner only removes mobs spawned by that player. Without an owner (in @@ -613,59 +622,56 @@ on_look: **Step 2: Blood trail in room 5002** — flag trigger fires on value 1 ```yaml -# data/rooms/city/5002.yaml -on_flag_change: - - on_player_flag: investigation - condition: - all_of: - - player_flag: investigation - value: 1 - - room: 5002 - steps: - - wait: 3 - messages: ["You notice a trail of blood leading east..."] - - set_player_flags: - investigation: 2 +# data/triggers/player_flag/investigation_1.yaml +on_player_flag: investigation +condition: + all_of: + - player_flag: investigation + value: 1 + - room: 5002 +steps: + - wait: 3 + messages: ["You notice a trail of blood leading east..."] + - set_player_flags: + investigation: 2 ``` **Step 3: Broken dagger in room 5003** — flag trigger fires on value 2 ```yaml -# data/rooms/city/5003.yaml -on_flag_change: - - on_player_flag: investigation - condition: - all_of: - - player_flag: investigation - value: 2 - - room: 5003 - steps: - - wait: 3 - messages: ["A glint of metal catches your eye under a crate."] +# data/triggers/player_flag/investigation_2.yaml +on_player_flag: investigation +condition: + all_of: + - player_flag: investigation + value: 2 + - room: 5003 +steps: + - wait: 3 + messages: ["A glint of metal catches your eye under a crate."] ``` **Step 4: Confrontation** — flag trigger fires on value 3 (set by a separate talk node), spawns boss ```yaml -# data/rooms/city/5004.yaml -on_flag_change: - - on_player_flag: investigation - condition: - all_of: - - player_flag: investigation - value: 3 - - room: 5004 - steps: - - wait: 5 - broadcast: "A shadow detaches itself from the warehouse wall..." - - wait: 5 - broadcast: "The assassin steps into the light, blade drawn." - - spawn_mob: - id: assassin_boss - owner_only: true - despawn_on_leave: true - despawn_rooms: [5004] +# data/triggers/player_flag/investigation_3.yaml +on_player_flag: investigation +condition: + all_of: + - player_flag: investigation + value: 3 + - room: 5004 +steps: + - wait: 5 + broadcast: "A shadow detaches itself from the warehouse wall..." + - wait: 5 + broadcast: "The assassin steps into the light, blade drawn." + - spawn_mob: + id: assassin_boss + owner_only: true + despawn_on_leave: true + despawn_rooms: [5004] ``` The player must stay in room 5004 to fight the boss. If they flee, the boss despawns and |
