aboutsummaryrefslogtreecommitdiff
path: root/building_guide
diff options
context:
space:
mode:
Diffstat (limited to 'building_guide')
-rw-r--r--building_guide/admin.md12
-rw-r--r--building_guide/behaviors.md10
-rw-r--r--building_guide/construction.md5
-rw-r--r--building_guide/courses.md7
-rw-r--r--building_guide/hacking.md40
-rw-r--r--building_guide/hazards.md45
-rw-r--r--building_guide/mobs.md21
-rw-r--r--building_guide/objects.md15
-rw-r--r--building_guide/rooms.md46
-rw-r--r--building_guide/triggers.md16
10 files changed, 86 insertions, 131 deletions
diff --git a/building_guide/admin.md b/building_guide/admin.md
index a99535a..3167147 100644
--- a/building_guide/admin.md
+++ b/building_guide/admin.md
@@ -273,11 +273,9 @@ even across sessions.
#### Live editing and maintenance
-Editor API requests (including undo/redo) execute as one game-owner task:
-file writes and their runtime callbacks/reloads cannot interleave with a tick
-or player command. Large edits briefly pause gameplay; slow browser uploads
-and downloads do not. Requests are limited to 8 MiB and rejected during game
-shutdown.
+Editor API requests (including undo/redo) execute as a single serialized task, so
+large edits briefly pause gameplay while slow browser uploads and downloads do
+not. Requests are limited to 8 MiB and rejected during game shutdown.
This does not make every edit automatically hot-reloadable. Existing reload
requirements still apply to definition editors and raw Files edits. Use the
@@ -308,9 +306,7 @@ The colors page is the single source of truth for everything the game paints wit
**Constants** — colors the engine renders conditionally that players can **never** override: HP-bar tiers (green/yellow/red by remaining %), level-difference bands (white/green/red by how far a target's level is from the viewer's), skill-level tier colors on the datapad, battery/run-energy bars, tech status indicators (`ON`/`off`/`locked`), tech/buff `[ON]`/`[BUFF]` tags and stat names, the score panel "max value" trailing numbers, shared table-header chrome (`Skill`, `Level`, `XP`, `Option`, `Value`, `Description` etc.), the production-menu locked-row tint, the APS datapad "here" highlight, the mob inline `n/maxhp` value, the `(protected)` tag, and the god-mode `[LOCAL]`/`[GLOBAL]`/`(HIDDEN)` tags. These are stored in a separate `constant_colors` block in `config.yaml`.
-**Why constants are separate from player colors.** The in-game `colors` command only knows about names listed in `config.DefaultColors()`. Constants live in a different map (`constant_colors`), which that command never reads. So if a player types `colors hp_bar_high FF`, the game answers "Unknown color target: hp_bar_high" and refuses — there is no path for a player to set one. Editing a constant here is the only way to change it short of editing `config.yaml` directly.
-
-**Save / Reset.** One shared **Save Colors** button persists both sections to `config.yaml` (a backup snapshot of the previous file is written first). **Reset all to defaults** restores both `default_colors` and `constant_colors` to the built-in values from `internal/config/config.go` and saves immediately. **Export JSON** / **Import JSON** round-trip both maps; the import payload may be either the layered `{colors:{...}, constant_colors:{...}}` shape (the format Export produces) or a legacy flat `{category: spec}` object, in which case only player colors are applied.
+**Save / Reset.** One shared **Save Colors** button persists both sections to `config.yaml` (a backup snapshot of the previous file is written first). **Reset all to defaults** restores both `default_colors` and `constant_colors` to their built-in values and saves immediately. **Export JSON** / **Import JSON** round-trip both maps; the import payload may be either the layered `{colors:{...}, constant_colors:{...}}` shape (the format Export produces) or a flat `{category: spec}` object, in which case only player colors are applied.
**Preview.** The right pane renders example output (a full room look, two combat rounds, chat, drops, ticks, assassin/safespot, and a Constants demo block) using the live specs you have staged. The combat HP bars in the preview paint with the real `hp_bar_high` / `hp_bar_med` / `hp_bar_empty` constants, and the `(level …)` annotations in the Room Look paint with `level_diff_*` — so editing a constant is reflected immediately in the same example lines an admin would recognize from gameplay.
diff --git a/building_guide/behaviors.md b/building_guide/behaviors.md
index 26c12ec..976ba3d 100644
--- a/building_guide/behaviors.md
+++ b/building_guide/behaviors.md
@@ -488,20 +488,20 @@ pass fires and its `steps` run as a scripted sequence (first-match-wins).
|-------|-------|-------------|
| `lock` | all | `true` = atomic (player can only `quit`) + resumable on reconnect. Default `false` = interruptable, not persisted. |
| `item_id` | on_use/on_look/on_kill | `on_use`: required item (`use <item> on <obj>`); empty = bare `use <obj>`. `on_look`: only fires if carrying this item. `on_kill`: only fires if wielding this weapon. |
-| `condition` | all | Optional Trigger-level gate (Condition struct). |
+| `condition` | all | Optional Trigger-level gate (Condition). |
| `steps` | all | Ordered list of Step entries run as a scripted sequence. |
| `on_player_flag` | flag-trigger files only | Player flag this trigger watches. |
| `on_global_flag` | flag-trigger files only | Global flag this trigger watches. |
| `value` | flag triggers | Optional value-match filter. |
-Talk node/option actions still use the NodeAction shape (`action:` with the effect fields
-inline) — those have not changed.
+Talk node/option actions use the NodeAction shape (`action:` with the effect fields
+inline).
#### Step vocabulary
Each Step can carry a `wait` (ticks to pause before the step fires) and any combination of
the effects below. All effects on a step fire together when the wait elapses. Messages are
-plain strings — neither `{message, delay}` maps nor the old `delay` field exist.
+plain strings.
| Field | Type | Description |
|-------|------|-------------|
@@ -535,7 +535,7 @@ Conditions gate a Trigger (Trigger-level `condition`) or an individual step. A b
| `not` | `true` inverts the entire condition |
| `has_item` | Passes when the player carries this item |
| `min_credits` | Passes when the player has at least this many credits |
-| `room` | **New.** Passes when the triggering player is currently in this room — use to scope flag-change triggers |
+| `room` | Passes when the triggering player is currently in this room — use to scope flag-change triggers |
| `all_of` | List of sub-conditions; all must pass |
| `any_of` | List of sub-conditions; any one must pass |
diff --git a/building_guide/construction.md b/building_guide/construction.md
index 07b8f7f..5e2c898 100644
--- a/building_guide/construction.md
+++ b/building_guide/construction.md
@@ -26,9 +26,8 @@ description:
inroom_description: "An estate directory terminal stands in the center of the neighborhood."
```
-The directory is handled by Go code: when a player looks at or uses it, it scans character
-YAML files for the `owns_house_local_neighborhood` player flag and lets homeowners teleport
-into their house.
+The directory scans character YAML files for the `owns_house_local_neighborhood`
+player flag and lets homeowners teleport into their house.
### Tools
diff --git a/building_guide/courses.md b/building_guide/courses.md
index 3f571e0..eecfb00 100644
--- a/building_guide/courses.md
+++ b/building_guide/courses.md
@@ -4,7 +4,7 @@ Agility courses are defined in `data/courses/<id>.yaml`. Each course defines a s
**The filename is the ID** (`vent_shaft.yaml` → `vent_shaft`); the loader derives it from the filename. Do not put an `id:` field in the file — it is ignored.
-> Editing courses is done from the **Map screen** in the admin web GUI: select an obstacle room and use its **Course** side-panel tab to create a course, add the room as a step, edit all of its parameters, or remove it. The legacy standalone "Courses" editor tab has been removed.
+> Editing courses is done from the **Map screen** in the admin web GUI: select an obstacle room and use its **Course** side-panel tab to create a course, add the room as a step, edit all of its parameters, or remove it.
>
> When a room is added as a course step, the admin GUI auto-creates **always-blocked one-way exits** between consecutive obstacle rooms so the in-game map can lay them out correctly. The direction of these exits is chosen by the builder. Always-blocked exits are blocked to players (movement is refused with the standard "blocked" message and they show "(blocked)" in `look`) but visible on the map — they exist solely for map rendering. On the map, course links appear as blue dashed one-way arrows (colored via the `map_agility` theme token).
@@ -66,7 +66,7 @@ obstacles:
| Field | Type | Description |
| ------------ | -------- | -------------------------------------------------------------------------- |
| `message` | string | Text printed when this phase advances |
-| `delay` | float64 | Ticks to wait before printing this phase (supports fractional via `engine.ToTicks`) |
+| `delay` | float64 | Ticks to wait before printing this phase (fractional values allowed) |
| `fail_check` | bool | If true, the obstacle's failure roll happens on this phase (set on at most one phase) |
### Phase Advancement
@@ -128,4 +128,5 @@ exits:
| `leap` | leaping |
| `slide` | sliding |
-Obstacle verbs are auto-registered at startup from all course YAML files. No changes to `cmd_registry.go` needed when adding new courses — any verb in a `data/courses/` file will work. \ No newline at end of file
+Obstacle verbs are auto-registered at startup from all course YAML files — any verb in a
+`data/courses/` file will work without additional configuration.
diff --git a/building_guide/hacking.md b/building_guide/hacking.md
index dc4372b..b45363f 100644
--- a/building_guide/hacking.md
+++ b/building_guide/hacking.md
@@ -1,8 +1,8 @@
## Hacking
-Hacking minigames are played by jacking into terminal objects. Terminals are YAML objects that are registered in Go's `terminalDefs` map.
-
-**The filename is the ID** (`terminal_basic.yaml` → `terminal_basic`); like all objects, the loader derives the ID from the filename — don't add an `id:` field (see `objects.md`).
+Hacking minigames are played by jacking into terminal objects. Terminals are YAML
+objects; the filename is the ID (`terminal_basic.yaml` → `terminal_basic`), and
+like all objects you should not add an `id:` field (see `objects.md`).
### Terminal Object YAML
@@ -13,38 +13,20 @@ description: "A battered terminal with a cracked screen. {28}[Level 1 Hacking]{/
inroom_description: "A {28}basic terminal{/} hums quietly against the wall."
```
-- `id` must match a key in `terminalDefs` (Go code in `internal/game/hacking.go`)
- `name` is used for matching in the `jack` command
- `color` controls the display color
- `description` is shown when examining the terminal
- `inroom_description` is shown in room listings
-- No `behavior` field — terminals are registered in Go, not YAML behaviors
-
-### Adding a New Terminal
-
-1. Create an object YAML file in `data/objects/terminal_<name>.yaml`
-2. Add an entry to `terminalDefs` in `internal/game/hacking.go`
-3. Optionally create a new minigame implementation in `internal/game/hacking_<name>.go`
-4. Place the terminal object in a room's `objects:` list
-
-### Adding a New Minigame
+- Terminals have no behavior block; each terminal corresponds to a minigame
+ registered in the game code
-New minigames implement the `HackingMinigame` interface:
+### Placing a Terminal
-```go
-type HackingMinigame interface {
- Init(level int) string
- HandleInput(input string) (output string, done bool, won bool)
- Name() string
-}
-```
-
-Optionally implement `XPBonuser` for bonus XP:
+Add the terminal object to a room's `objects:` list:
-```go
-type XPBonuser interface {
- BonusXP() int
-}
+```yaml
+objects:
+ - id: terminal_basic
```
-
+Adding a brand-new hacking minigame is an engine change, not a world-data change.
diff --git a/building_guide/hazards.md b/building_guide/hazards.md
index 04333c9..3b72840 100644
--- a/building_guide/hazards.md
+++ b/building_guide/hazards.md
@@ -1,17 +1,9 @@
## Hazards
A **hazard** is an environmental threat attached to a room — a sandstorm, solar
-radiation, falling rocks, toxic spores. It rolls an "attack" against everyone in
-the room every few ticks using the **same combat formulas as a mob**: the hazard
-supplies the accuracy and max hit, and the player defends with their Defense
-level plus the equipment defense bonus selected by the hazard's attack type. No
-new combat math, no special gear — your existing armour and Defense skill are
-what keep you alive.
-
-Hazards are **shared, ID-referenced definitions** (like drop tables and modules):
-define a hazard once in `data/hazards/<id>.yaml`, then reference it from any number
-of rooms with `hazard: <id>` (see `rooms.md`). This makes one hazard reusable
-across a whole area and lets a future `examine` command resolve it by name.
+radiation, falling rocks, toxic spores. Define it once in
+`data/hazards/<id>.yaml`, then reference it from any number of rooms with
+`hazard: <id>` (see `rooms.md`).
**The filename is the ID** (`solar_radiation.yaml` → reference it as `hazard: solar_radiation`); the loader derives it from the filename. Do not put an `id:` field in the file — it is ignored.
@@ -76,37 +68,8 @@ attacked — they only attack the player.
| Field | Description |
|---|---|
| `name` | Display name used in warnings and default messages |
-| `description` | Flavour text (for a future `examine`) |
+| `description` | Flavour text for the hazard |
| `warn_message` | Shown when warning the player about the area |
| `hit_message` | Message on a hit; include a single `%d` to show the damage |
| `miss_message` | Message on a miss |
| `required_item` | If set and equipped, the hazard is fully negated for that player |
-
-### How it behaves
-
-- **Applies to everyone in the room, always** — idle, passing through, working a task,
- or fighting a mob. Strong incentive to work fast, take cover, or leave.
-- **Tech protection applies automatically.** Because the hazard has an attack type, the
- matching protection tech reduces its damage: Kinetic Barrier (melee types),
- Projectile Screen (`ranged`), Neural Firewall (`science`).
-- **Safespots block all hazard damage.** A hidden player behind a safespot object takes
- no hazard damage, regardless of hazard type. Note that doing a *melee* attack/work step
- forces you out of cover — so ranged/science work lets you stay sheltered, melee does not.
-- **Right gear negates it.** If `required_item` is set and equipped, the hazard is skipped
- for that player entirely. The item must exist (validated at startup).
-- **Hazards never interrupt movement.** Unlike a mob hit (which aborts a flee with
- "Can't escape!"), a hazard hit does not touch your walk — you can keep moving out of
- the danger zone while taking hits.
-
-### Entry warning
-
-Players walking from a **safe** room into a **hazardous** one get a `[Y/n]` confirmation
-(pressing return defaults to yes; anything other than empty/`y`/`yes` cancels). Moving
-between two hazardous rooms does not re-prompt. Players can disable the prompt with the
-account option `danger_warning` (`option danger_warning false`).
-
-### Examine (forward-compatible)
-
-Because the hazard is a shared def referenced by ID, a future `examine sandstorm` can
-resolve the current room's `hazard` ID, load the def, and render its attack type, accuracy,
-max hit and description — no hidden objects required.
diff --git a/building_guide/mobs.md b/building_guide/mobs.md
index 833fbfd..cbcd728 100644
--- a/building_guide/mobs.md
+++ b/building_guide/mobs.md
@@ -67,7 +67,7 @@ combat:
strength: 3
defense: 3
speed: 5
- max_melee_hit: 1 # OSRS-derived: ((strength+9)*(strength_bonus+64)+320)/640
+ max_melee_hit: 1 # max hit with melee (for melee-attacking mobs)
bonuses:
attack_bonus: 3 # equipment-equivalent attack bonus for mob's attack roll
strength_bonus: 2 # equipment-equivalent strength bonus for mob's max hit
@@ -97,9 +97,9 @@ All combat stats live under `combat.stats:`
| `ranged` | Ranged level (for ranged/science mobs) |
| `science` | Science level |
| `speed` | Attack speed in ticks (default 5) |
-| `max_melee_hit` | Maximum melee hit (for melee-attacking mobs). Computed as ((strength+9)*(strength_bonus+64)+320)/640 |
-| `max_ranged_hit` | Maximum ranged hit (for ranged-attacking mobs). Computed as ((ranged+9)*(ranged_bonus+64)+320)/640 |
-| `max_science_hit` | Maximum science hit (for science-attacking mobs). A set value — magic max hits are not level-derived in OSRS |
+| `max_melee_hit` | Maximum melee hit (for melee-attacking mobs) |
+| `max_ranged_hit` | Maximum ranged hit (for ranged-attacking mobs) |
+| `max_science_hit` | Maximum science hit (for science-attacking mobs). A set value, not level-derived |
#### Bonuses (`combat.stats.bonuses:`)
@@ -352,7 +352,7 @@ Mobs without these fields work normally (assassin_level defaults to 0, finishing
### Task Mobs (Worksites)
-A task mob is a non-violent "worksite" — build a solar panel, survey flora, prospect a rock — that reuses the entire combat engine. Set `combat.kind: task`. Mechanically `combat.stats.hp` is the work pool that drains to 0 to **complete** the work; the player sees a progress bar filling toward 100%. A task mob **never attacks back** — the danger (if any) comes from a room `hazard:` (see `hazards.md`). Use the `work` command (or `attack`) on it.
+A task mob is a non-violent "worksite" — build a solar panel, survey flora, prospect a rock. Set `combat.kind: task` and treat `combat.stats.hp` as the amount of work required to complete it. A task mob **never attacks back** — the danger (if any) comes from a room `hazard:` (see `hazards.md`). Use the `work` command (or `attack`) on it.
Task-specific fields live in the root-level `task:` block:
@@ -402,10 +402,11 @@ drops: # the "payment" for the labour — same drop sy
Key points:
-- **All combat styles work.** Melee, ranged (consumes ammo as "supplies") and science decks (consume junk) all apply progress. The per-type `*_defense` values steer which method is *efficient* without forbidding any — a rock might have low `crush_defense` but high `stab_defense`.
-- **XP and buffs are identical to combat** — driven by your attack style (accurate/aggressive/defensive/balanced) and split into the combat skills. This is a non-violent way to train Accuracy/Strength/Defense/Ranged/Science/Hitpoints.
-- **No special equipment.** Task mobs are designed around the weapons and bonuses you already have. There is no separate "tool" equipment set.
-- **Progress decays exactly like inverted mob regen.** A task you stop working on slowly loses progress over time (the same regen that heals combat mobs, displayed inverted).
-- **One worker at a time**, the same 1-v-1 lock as combat.
+- All combat styles apply progress: melee, ranged (consumes ammo), and science decks
+ (consume junk). The per-type `*_defense` values steer which method is *efficient*
+ without forbidding any — a rock might have low `crush_defense` but high `stab_defense`.
+- `combat.stats.hp` is the amount of work required, not a health bar. Task mobs never
+ attack back; danger comes from a room `hazard:` if you want any.
+- `drops` are the payment for completing the work, using the same drop system as combat.
---
diff --git a/building_guide/objects.md b/building_guide/objects.md
index 958938d..10d50c1 100644
--- a/building_guide/objects.md
+++ b/building_guide/objects.md
@@ -449,15 +449,12 @@ Mobs must have a `size` field for safespot blocking to work (small < medium < la
massive, default medium). A safespot with `max_block_size: large` blocks mobs of size small,
medium, and large, but not massive.
-#### Combat integration
-
-- A safespot blocks melee attacks from mobs within `max_block_size`.
-- When a mob's melee is blocked but it has a ranged/science attack type, it switches to its
- strongest ranged/science attack.
-- Mobs exceeding `max_block_size` are never blocked and always use melee.
-- Melee attacks from a safespotted player force them out of cover.
-- Hiding in combat takes 4 ticks; taking a hit during this cancels the hide.
-- Hiding out of combat takes 1 tick.
+#### Interaction notes
+
+- A safespot blocks melee attacks from mobs up to `max_block_size`; larger mobs always use
+ melee.
+- A blocked mob with a ranged/science attack type switches to its strongest ranged/science
+ attack.
- Safespots block all hazard damage (see `hazards.md`).
#### Option: safespot_alert
diff --git a/building_guide/rooms.md b/building_guide/rooms.md
index 9cb93b1..f1e1712 100644
--- a/building_guide/rooms.md
+++ b/building_guide/rooms.md
@@ -246,7 +246,7 @@ Multiplayer table fields (`casino_tables`):
| Field | Type | Default | Notes |
|---|---|---|---|
| `id` | string | — | Required. Nonempty, no surrounding whitespace, unique among the room's tables and machines. |
-| `game` | string | — | Required. `blackjack`, `baccarat`, `roulette`, `craps`, `paigow`, or `video_poker`. |
+| `game` | string | — | Required. `blackjack`, `baccarat`, `roulette`, `craps`, `paigow`, `video_poker`, or `plinko`. |
| `max_players` | int | 8 | 0–64. Video poker is forced to 1; Pai Gow caps at 6. |
| `betting_ticks` | int | 20 | 0–3600. |
| `min_bet` | int | 1 | Must not exceed `max_bet`. |
@@ -255,6 +255,27 @@ Multiplayer table fields (`casino_tables`):
Game rules are nested in a block named after the game. `id` and `game` are the
only required keys; omitted numeric fields keep their defaults.
+#### Plinko
+
+Plinko is a single-seat fixture under `casino_tables`, addressed with `play plinko`:
+
+```yaml
+casino_tables:
+ - id: plinko
+ game: plinko
+ min_bet: 1
+ max_bet: 10000
+ plinko:
+ rtp: 98
+```
+
+| Field | Type | Default | Notes |
+|---|---|---|---|
+| `rtp` | int | 98 | Target return-to-player percentage, 31–100. |
+
+`max_players` may only be omitted, `0`, or `1`. Bet limits are per ball, and
+`max_bet` cannot exceed 10000000.
+
#### Blackjack
```yaml
@@ -402,7 +423,7 @@ casino_machines:
|---|---|---|---|
| `id` | string | — | Required; same rules as table IDs. |
| `game` | string | — | Required; only `slots`. |
-| `variant` | string | `huff_and_even_more_puff` | Registered in Go (`slotVariants` in `internal/casino/registry.go`); an unregistered name is rejected. |
+| `variant` | string | `huff_and_even_more_puff` | Must be a registered slot variant; an unregistered name is rejected. |
| `min_bet` | int | 1 | |
| `max_bet` | int | 1000000 | Must be at least `min_bet`. |
| `spin_ticks` | int | 2 | 0–3600. |
@@ -422,8 +443,7 @@ invalid room casino configurations. Unknown fields are rejected.
seat counts, split counts, decks, and payouts are rejected.
- Payouts and shuffle fractions must be finite (no NaN or infinity).
- Editing a room hot-reconfigures an **idle** table or machine on the next
- lookup; an occupied game keeps its live rules until it becomes idle, so a
- committed wager is never discarded.
+ lookup.
### Objects — interactive fixtures
@@ -532,9 +552,9 @@ on_enter:
#### Timed sequences
-`wait: N` on a step is the number of ticks to pause **before** that step fires (replaces the
-old `delay`). Messages are plain strings; effects on a step fire together when the wait
-elapses. A step with no `wait` fires on the next tick.
+`wait: N` on a step is the number of ticks to pause **before** that step fires. Messages
+are plain strings; effects on a step fire together when the wait elapses. A step with no
+`wait` fires on the next tick.
#### Step actions
@@ -558,12 +578,11 @@ on_enter:
despawn_on_leave: true
```
-#### First-match-wins and migration
+#### First-match-wins
-Entries in `on_enter` are walked top-to-bottom; the first whose `condition` passes fires
-and its steps run. The old on_enter ran **every** matching step, so migration wraps the old
-step list into a single Trigger entry to preserve behavior. For new content where steps
-are mutually exclusive, use multiple entries with distinct conditions.
+Entries in `on_enter` are walked top-to-bottom; the first whose `condition` passes
+fires and its steps run. For new content where steps are mutually exclusive, use
+multiple entries with distinct conditions.
Notes:
- `wait: 0` (or omitted) fires on the next tick.
@@ -592,8 +611,7 @@ on_exit:
Use on_exit for parting messages, closing out a spawn when a player departs, or recording
that the player has passed through an area. It obeys the same first-match-wins rule and the
-same Step vocabulary as on_enter. (New event block — did not exist before the Trigger
-unification.)
+same Step vocabulary as on_enter.
---
diff --git a/building_guide/triggers.md b/building_guide/triggers.md
index ba44c40..d99230b 100644
--- a/building_guide/triggers.md
+++ b/building_guide/triggers.md
@@ -92,8 +92,7 @@ wait elapses.
| `aps_node` | Marks this room's APS node as unlocked. |
| `condition` | Per-step gate (evaluated once when the sequence reaches this step). |
-Messages are **plain strings**, no longer `{message, delay}` maps. The `delay` field is
-gone — use `wait` instead.
+Messages are plain strings. Use `wait` to pause before a step fires.
### Condition vocabulary
@@ -108,7 +107,7 @@ check passes when the flag is **set to a truthy value**. `not: true` inverts any
| `not` | `true` inverts the entire condition. |
| `has_item` | Passes when the player carries this item. |
| `min_credits` | Passes when the player has at least this many credits. |
-| `room` | **New.** Passes when the triggering player is currently in this room. Use it to scope flag-change triggers to a location. |
+| `room` | Passes when the triggering player is currently in this room. Use it to scope flag-change triggers to a location. |
| `all_of` | List of sub-conditions; all must pass. |
| `any_of` | List of sub-conditions; any one must pass. |
@@ -144,10 +143,9 @@ on_use:
crystal_inserted: true
```
-For `on_enter` specifically, the old flat step list ran **every** matching step. Migration
-wraps the old steps into a single Trigger entry, so all the old steps still run as one
-sequence. New `on_enter` content should use multiple entries with distinct conditions when
-the steps are mutually exclusive.
+For `on_enter` specifically, entries are walked top-to-bottom and the first whose
+condition passes runs. Use multiple entries with distinct conditions when the steps
+are mutually exclusive.
### Lock
@@ -181,7 +179,7 @@ entity:
| Object | `on_use`, `on_look` |
| Mob | `on_kill` |
| Exit (room YAML) | `on_traverse` |
-| Room | `on_enter`, `on_exit` (NEW) |
+| Room | `on_enter`, `on_exit` |
| 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.
@@ -190,7 +188,7 @@ entity:
(not when it's blocked). The exit's own `condition:` / `blocked_message:` belong to the
exit gate, not the Trigger list.
- **on_enter** — fires after the player enters the room.
-- **on_exit** (NEW) — fires when a player leaves the room, **before** `RoomID` is updated
+- **on_exit** — 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 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.