aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-07 16:24:27 -0400
committerhistoria <[not public]>2026-07-07 16:24:27 -0400
commitab2597b22ccdb71116992aec78080d9858358d04 (patch)
tree8beae4060c2831c4a68f92b682b5350cfa1d0afb
parent3f30fa3eec9c2e2acf9a984ccefb9529a25e688a (diff)
downloadthehouseoficarus-ab2597b22ccdb71116992aec78080d9858358d04.tar.gz
rename flags to global_flags (differentiate from player_flags)
-rw-r--r--AGENTS.md8
-rw-r--r--building_guide/admin.md6
-rw-r--r--building_guide/behaviors.md8
-rw-r--r--building_guide/conditions.md6
-rw-r--r--building_guide/doors.md4
-rw-r--r--building_guide/hidden_objects.md2
-rw-r--r--building_guide/objects.md12
-rw-r--r--building_guide/rooms.md10
-rw-r--r--building_guide/state.md2
-rw-r--r--building_guide/tips.md2
-rw-r--r--building_guide/triggers.md46
-rw-r--r--data/help/inspect.yaml2
-rw-r--r--data/help/setflag.yaml7
-rw-r--r--data/help/setglobalflag.yaml7
-rw-r--r--data/mobs/examples/guard.yaml2
-rw-r--r--data/objects/iron_gate.yaml4
-rw-r--r--data/rooms/example_unused/agility2/99999202.yaml2
-rw-r--r--data/rooms/intro/1001.yaml24
-rw-r--r--internal/admin/api_global_flags.go (renamed from internal/admin/api_flags.go)6
-rw-r--r--internal/admin/server.go2
-rw-r--r--internal/behavior/behavior.go4
-rw-r--r--internal/game/act.go7
-rw-r--r--internal/game/act_room.go10
-rw-r--r--internal/game/act_steal.go2
-rw-r--r--internal/game/act_talk.go8
-rw-r--r--internal/game/cmd_inspect.go8
-rw-r--r--internal/game/cmd_move.go8
-rw-r--r--internal/game/cmd_registry.go2
-rw-r--r--internal/game/cmd_room_insert.go4
-rw-r--r--internal/game/cmd_room_remove.go4
-rw-r--r--internal/game/cmd_setglobalflag.go (renamed from internal/game/cmd_setflag.go)8
-rw-r--r--internal/game/condition_test.go22
-rw-r--r--internal/game/core_flagstore.go30
-rw-r--r--internal/game/exit_discovery_test.go6
-rw-r--r--internal/game/game.go10
-rw-r--r--internal/game/map_test.go16
-rw-r--r--internal/game/sys_triggers.go20
-rw-r--r--internal/player/player.go4
-rw-r--r--internal/world/grid_test.go4
-rw-r--r--internal/world/insert_remove.go2
-rw-r--r--internal/world/room.go6
-rw-r--r--internal/world/room_migrate_test.go6
-rw-r--r--internal/world/trigger.go8
-rw-r--r--internal/world/trigger_store.go68
44 files changed, 215 insertions, 214 deletions
diff --git a/AGENTS.md b/AGENTS.md
index bbf1469..893ba59 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -33,14 +33,14 @@ File prefixes in `internal/game/`: `game.go/cmd_registry.go/tick.go` (core), `co
## Two State Systems
-- **World flags** (`set_flags` / `flag`): Shared by all players.
+- **Global flags** (`set_global_flags` / `global_flag`): Shared by all players.
- **Player flags** (`set_player_flags` / `player_flag`): Per-character, saved to YAML.
## Room Scripting
-- **`on_enter` steps**: message + optional condition. A step may have `delay` and/or `set_flags`/`set_player_flags`. Timed steps run as scheduled enter sequences; otherwise messages print synchronously. Conditions snapshotted once at entry. Resumes on reconnect if interrupted.
+- **`on_enter` steps**: message + optional condition. A step may have `delay` and/or `set_global_flags`/`set_player_flags`. Timed steps run as scheduled enter sequences; otherwise messages print synchronously. Conditions snapshotted once at entry. Resumes on reconnect if interrupted.
- **Conditional descriptions** (`description: [{text, condition}]`): first passing variant wins. For objects, if no variant matches the object is absent for that player. Accepts plain string or list.
-- **Exits** can `set_flags`/`set_player_flags` on successful traverse.
+- **Exits** can `set_global_flags`/`set_player_flags` on successful traverse.
## Data Files
@@ -79,7 +79,7 @@ Actions route through `startAction()` in `act.go`: normalize verb → resolve ta
### Condition System
-Used by exits, talk options, on-enter, use_interactions: `flag` (world), `player_flag` (per-char), `value` (exact match), `has_item`, `min_credits`, `all_of`, `any_of`, `not`. Bare flag/player_flag passes on truthy.
+Used by exits, talk options, on-enter, use_interactions: `global_flag` (global), `player_flag` (per-char), `value` (exact match), `has_item`, `min_credits`, `all_of`, `any_of`, `not`. Bare global_flag/player_flag passes on truthy.
## Two Depletion Mechanics
diff --git a/building_guide/admin.md b/building_guide/admin.md
index fe940e3..f5ddcd1 100644
--- a/building_guide/admin.md
+++ b/building_guide/admin.md
@@ -171,7 +171,7 @@ are never saved to disk.
```
setflag <flag_name> [value]
```
-Sets a world flag. World flags are shared by all players and stored in memory (lost on
+Sets a global flag. Global flags are shared by all players and stored in memory (lost on
server restart). Setting a flag fires any triggers watching that flag. Values default to
`true` if omitted. Accepted value types: `true`/`false` (bool), integer, or string.
@@ -222,7 +222,7 @@ Dumps detailed diagnostic information about your current room:
- Mobs with wander intervals
- Item spawns with respawn timers
- On-enter step and trigger counts
-- All world flags
+- All global flags
- Your player flags
- Connected players in the room
- Live mob instances with HP
@@ -259,7 +259,7 @@ the game server and is configured in `config.yaml` under `admin_http` (plain HTT
| Courses | `/editor/courses` | CRUD for agility courses (obstacle sequences, XP, fail damage). |
| Modules | `/editor/modules` | CRUD for science modules (combat/utility/transport/enchant/processing). |
| Players | `/editor/players` | Browse character YAML files and view player state. |
-| Flags | `/editor/flags` | View and edit world flags in real time. |
+| Flags | `/editor/flags` | View and edit global flags in real time. |
| Files | `/editor/files` | Raw file tree browser for all YAML files under `data/`. |
All CRUD editors support undo/redo (persisted to `data/.admin_history.json`), so changes can be reverted
diff --git a/building_guide/behaviors.md b/building_guide/behaviors.md
index 8030d3d..1857681 100644
--- a/building_guide/behaviors.md
+++ b/building_guide/behaviors.md
@@ -230,7 +230,7 @@ talk:
messages:
- "\"Alright, I'll open the gate.\""
action:
- set_flags:
+ set_global_flags:
gate_open: true
options:
- text: "\"Thanks.\""
@@ -259,7 +259,7 @@ talk:
| Field | Effect |
|---|---|
-| `set_flags` | Sets world flags (global, shared by all players). |
+| `set_global_flags` | Sets global flags (global, shared by all players). |
| `set_player_flags` | Sets player-local flags (per-character, quest progress). |
| `give_item` | Gives an item to the player's inventory. |
| `take_item` | Removes an item from the player's inventory. |
@@ -538,7 +538,7 @@ use_interactions:
not: true
message: "You push the heavy iron gate open."
action:
- set_flags:
+ set_global_flags:
gate_open: true
```
@@ -549,7 +549,7 @@ use_interactions:
- item_id: dusty_tome
message: "The bookshelf slides aside, revealing a secret passage!"
action:
- set_flags:
+ set_global_flags:
secret_passage_open: true
```
diff --git a/building_guide/conditions.md b/building_guide/conditions.md
index 853518b..acfdf57 100644
--- a/building_guide/conditions.md
+++ b/building_guide/conditions.md
@@ -6,17 +6,17 @@ descriptions, and trigger value matching.
### Simple conditions
-A bare `flag:` / `player_flag:` check passes when the flag is **set to a truthy
+A bare `global_flag:` / `player_flag:` check passes when the flag is **set to a truthy
value** (`true`, a non-zero number, a non-empty string). Add `value:` only when
you need to match a specific value (e.g. a numeric quest stage). `not: true`
inverts any check.
```yaml
-# Check a world flag is set
+# Check a global flag is set
condition:
flag: gate_open
-# Check a world flag is NOT set
+# Check a global flag is NOT set
condition:
flag: gate_open
not: true
diff --git a/building_guide/doors.md b/building_guide/doors.md
index 3756a13..358001f 100644
--- a/building_guide/doors.md
+++ b/building_guide/doors.md
@@ -16,7 +16,7 @@ use_interactions:
not: true
message: "You press the stone button. You hear grinding stone in the distance."
action:
- set_flags:
+ set_global_flags:
secret_door_open: true
```
@@ -87,7 +87,7 @@ spawns:
respawn_ticks: 500
```
-Key difference: the button door uses `flag` (shared state — one player presses, everyone benefits), the key door uses `has_item` (per-player inventory check — each player needs their own key).
+Key difference: the button door uses `global_flag` (shared state — one player presses, everyone benefits), the key door uses `has_item` (per-player inventory check — each player needs their own key).
---
diff --git a/building_guide/hidden_objects.md b/building_guide/hidden_objects.md
index 0adb7d9..b1ed7b4 100644
--- a/building_guide/hidden_objects.md
+++ b/building_guide/hidden_objects.md
@@ -16,7 +16,7 @@ use_interactions:
not: true
message: "You pull the lever. A grinding sound echoes from the east."
action:
- set_flags:
+ set_global_flags:
secret_passage_open: true
```
diff --git a/building_guide/objects.md b/building_guide/objects.md
index af693c4..d377f23 100644
--- a/building_guide/objects.md
+++ b/building_guide/objects.md
@@ -149,7 +149,7 @@ use_interactions:
not: true
message: "You push the heavy iron gate open."
action:
- set_flags:
+ set_global_flags:
gate_open: true
```
@@ -183,7 +183,7 @@ on_look:
read_sign: true
```
`on_look` fires the action AFTER showing the object's description. It uses the same
-`NodeAction` type as talk nodes — supports `set_player_flags`, `set_flags`,
+`NodeAction` type as talk nodes — supports `set_player_flags`, `set_global_flags`,
`give_item`, `take_item`, `teleport`, `heal`, `cost`, and everything else in the
[node action reference](behaviors.md#node-action-reference).
@@ -220,7 +220,7 @@ use_interactions:
message: "You insert the crystal key into the slot. It clicks into place."
action:
take_item: crystal_key
- set_flags:
+ set_global_flags:
crystal_inserted: true
```
@@ -235,7 +235,7 @@ use_interactions:
take_item: ancient_scroll
set_player_flags:
quest_complete: true
- set_flags:
+ set_global_flags:
temple_door_open: true
```
@@ -252,7 +252,7 @@ use_interactions:
| Field | Type | Description |
| ------------------ | -------------- | ---------------------------------- |
-| `set_flags` | map[string]any | Set world flags (shared) |
+| `set_global_flags` | map[string]any | Set global flags (shared) |
| `set_player_flags` | map[string]any | Set player flags (per-character) |
| `give_item` | string | Give an item to inventory |
| `take_item` | string | Remove an item from inventory |
@@ -263,7 +263,7 @@ use_interactions:
| Field | Description |
| ------------- | -------------------------------- |
-| `flag` | World flag check |
+| `global_flag` | Global flag check |
| `player_flag` | Per-character flag check |
| `has_item` | Inventory item check |
| `value` | Expected value for flag checks |
diff --git a/building_guide/rooms.md b/building_guide/rooms.md
index 35fd85a..f77aa42 100644
--- a/building_guide/rooms.md
+++ b/building_guide/rooms.md
@@ -46,7 +46,7 @@ exits:
southeast: 4
```
-Conditional exit — blocked until a world flag is set:
+Conditional exit — blocked until a global flag is set:
```yaml
exits:
north:
@@ -66,7 +66,7 @@ exits:
blocked_message: "The vault door is locked. You need a key."
```
-Conditional exit with compound condition — requires both a world flag AND a player flag:
+Conditional exit with compound condition — requires both a global flag AND a player flag:
```yaml
exits:
north:
@@ -78,7 +78,7 @@ exits:
blocked_message: "The bridge is out, and the toll collector blocks the path."
```
-Exit that sets flags when used — `set_flags` / `set_player_flags` are applied
+Exit that sets flags when used — `set_global_flags` / `set_player_flags` are applied
only when the player actually moves through the exit (not when it's blocked):
```yaml
exits:
@@ -215,7 +215,7 @@ on_enter:
```
**Timed sequences.** A step may also carry a `delay` (ticks to wait before it
-fires) and/or set flags (`set_flags` / `set_player_flags`). If any surviving step
+fires) and/or set flags (`set_global_flags` / `set_player_flags`). If any surviving step
has a delay or sets a flag, the whole sequence runs as a scheduled enter sequence;
plain message-only scripts still print instantly.
@@ -300,7 +300,7 @@ mobs:
### Room triggers — scripted events when flags change
-A room can carry a `triggers:` block. Each trigger watches a player or world flag
+A room can carry a `triggers:` block. Each trigger watches a player or global flag
and fires a sequence of timed steps when that flag's value changes. See `triggers.md`
for the full reference.
diff --git a/building_guide/state.md b/building_guide/state.md
index 2e0b5cc..fe526bf 100644
--- a/building_guide/state.md
+++ b/building_guide/state.md
@@ -1,6 +1,6 @@
## State: World vs Player
-**World flags** (`set_flags`, checked with `flag`) are shared by every player on the server. A door opened by one player is open for everyone. A lever pulled once changes the world for all. Stored in memory (`FlagStore`) — **lost on server restart**. Numeric values are compared by coercion (int/int64/float64 are normalized), so a YAML-decoded `3` matches a code-set `int(3)`.
+**Global flags** (`set_global_flags`, checked with `global_flag`) are shared by every player on the server. A door opened by one player is open for everyone. A lever pulled once changes the world for all. Stored in memory (`GlobalFlagStore`) — **lost on server restart**. Numeric values are compared by coercion (int/int64/float64 are normalized), so a YAML-decoded `3` matches a code-set `int(3)`.
**Player flags** (`set_player_flags`, checked with `player_flag`) are per-character. Quest progress, "has read the sign," "paid the toll" — these are different for each player. Saved to the character YAML and persist across logins.
diff --git a/building_guide/tips.md b/building_guide/tips.md
index b4abeb9..61c595f 100644
--- a/building_guide/tips.md
+++ b/building_guide/tips.md
@@ -1,6 +1,6 @@
## Tips
-1. **Use player flags for quest progress, world flags for environmental state.** If a bridge is repaired, that's world state. If a player has read a sign, that's player state.
+1. **Use player flags for quest progress, global flags for environmental state.** If a bridge is repaired, that's world state. If a player has read a sign, that's player state.
2. **Conditions on enter scripts** make rooms feel alive. A guard who only barks the first time, a room that changes after a quest completes.
diff --git a/building_guide/triggers.md b/building_guide/triggers.md
index b88f16d..0d227bd 100644
--- a/building_guide/triggers.md
+++ b/building_guide/triggers.md
@@ -8,7 +8,7 @@ value. They come in two flavors:
- **Global triggers** — defined in `data/triggers/<id>.yaml`. These fire regardless
of where the flag-setting player is, and can broadcast to all online players.
-A trigger watches one flag (`on_player_flag` or `on_flag`). When that flag's value
+A trigger watches one flag (`on_player_flag` or `on_global_flag`). When that flag's value
actually changes (from unset/falsy to a truthy/new value), the trigger's steps
begin executing. Setting a flag to the same value it already has does **not** re-fire
the trigger.
@@ -61,7 +61,7 @@ elapses.
| `message` | player | Text sent to the triggering player only. Supports `%p` (player name) and `%v` (flag value) templates. |
| `broadcast` | room | Text sent to everyone in the room. Inline color tags work; `\n` prefix is added automatically. |
| `broadcast_global` | world | Text sent to every online player. Useful for server-wide announcements. |
-| `set_flags` | world | Mutates world flags (shared by all players). |
+| `set_global_flags` | global | Mutates global flags (shared by all players). |
| `set_player_flags` | player | Mutates player flags (per-character). Only valid for `on_player_flag` triggers. |
| `spawn_mob` | room | Spawns a **transient** mob from a mob definition. See Transient Mobs below. |
| `despawn_mob` | room | Removes all trigger-spawned mobs matching the given mob ID (and optionally owner). |
@@ -127,24 +127,24 @@ The final step uses both `broadcast` (everyone in the ritual room sees the flash
and `teleport` + `message` (the triggering player is moved and sees a personal
message).
-#### Example 3: World-flag room trigger — shared environmental event
+#### Example 3: Global-flag room trigger — shared environmental event
-A player pulls a lever (sets world flag `floodgate_open`). The room trigger
+A player pulls a lever (sets global flag `floodgate_open`). The room trigger
broadcasts to everyone in the dam control room:
```yaml
# data/rooms/wilderness/dam_control.yaml
triggers:
- - on_flag: floodgate_open
+ - on_global_flag: floodgate_open
steps:
- broadcast: "Ancient gears grind as the floodgate slowly opens..."
- delay: 15
broadcast: "Water thunders through the opening!"
- - set_flags:
+ - set_global_flags:
valley_flooded: true
```
-Because this watches a **world** flag (`on_flag`), it fires once globally when
+Because this watches a **global** flag (`on_global_flag`), it fires once globally when
the flag is first set — not per-player. Everyone in the room sees the messages.
#### Example 4: "Push button" — trigger that resets itself
@@ -189,26 +189,26 @@ PlayerName has reached level 99 attack!
#### Example 6: World-first boss kill — global broadcast
-A boss mob's death sets world flag `world_boss_slain`. A global trigger
+A boss mob's death sets global flag `world_boss_slain`. A global trigger
announces it to everyone:
```yaml
# data/triggers/world_boss_slain.yaml
-on_flag: world_boss_slain
+on_global_flag: world_boss_slain
steps:
- broadcast_global: "The Ancient One has been vanquished! The land stirs with new life."
- - set_flags:
+ - set_global_flags:
ancient_lands_access: true # opens a zone for everyone
```
#### Example 7: Global trigger with room context
A global trigger can specify a `room` for broadcasts and mob spawns. This is
-useful when a world flag should trigger effects in a specific location:
+useful when a global flag should trigger effects in a specific location:
```yaml
# data/triggers/obelisk_activated.yaml
-on_flag: desert_obelisk_charged
+on_global_flag: desert_obelisk_charged
room: 1200
steps:
- broadcast: "The obelisk hums with stored power."
@@ -310,7 +310,7 @@ triggers:
```
`despawn_mob` with an owner only removes mobs spawned by that player. Without an
-owner (in world-flag triggers), it removes all matching mobs.
+owner (in global-flag triggers), it removes all matching mobs.
#### Example 11: Fixed-lifetime mob
@@ -357,7 +357,7 @@ triggers:
value: 3
steps:
- message: "Stage 3 begins — the temple doors swing open."
- - set_flags:
+ - set_global_flags:
temple_open: true
```
@@ -453,18 +453,18 @@ flag is already at that value.
### Full Scenario: Server-Wide World Event
-A server event progresses through stages. A global trigger chains world flags to
+A server event progresses through stages. A global trigger chains global flags to
advance the event for everyone.
**Phase 1 trigger:**
```yaml
# data/triggers/event_phase1.yaml
-on_flag: event_phase1_start
+on_global_flag: event_phase1_start
steps:
- broadcast_global: "The sky darkens as an eclipse begins..."
- delay: 100
- set_flags:
+ set_global_flags:
event_phase2_start: true
```
@@ -472,11 +472,11 @@ steps:
```yaml
# data/triggers/event_phase2.yaml
-on_flag: event_phase2_start
+on_global_flag: event_phase2_start
steps:
- broadcast_global: "Monsters pour from the shadows across the land!"
- delay: 300
- set_flags:
+ set_global_flags:
event_phase3_start: true
```
@@ -484,11 +484,11 @@ steps:
```yaml
# data/triggers/event_phase3.yaml
-on_flag: event_phase3_start
+on_global_flag: event_phase3_start
steps:
- broadcast_global: "The eclipse passes. The monsters retreat."
- delay: 50
- set_flags:
+ set_global_flags:
event_active: false
```
@@ -528,7 +528,7 @@ All of these paths activate triggers:
the same trigger running simultaneously. Starting a new one replaces the old.
4. **Player must be online.** Player-flag triggers only fire for connected
- players. World-flag triggers fire regardless.
+ players. Global-flag triggers fire regardless.
5. **Room triggers check room.** Room-level triggers only fire when the
flag-setting player is in that room. This keeps local events local.
@@ -544,6 +544,6 @@ Startup validation checks that trigger `spawn_mob.id` references an existing mob
definition, `give_item`/`take_item` reference existing items, `teleport` and
`despawn_rooms` reference existing rooms, and that global trigger IDs are unique.
-Validation does NOT check that the watched flags (`on_player_flag` / `on_flag`)
+Validation does NOT check that the watched flags (`on_player_flag` / `on_global_flag`)
are ever set — those are dynamic, set by runtime gameplay, and can't be statically
verified.
diff --git a/data/help/inspect.yaml b/data/help/inspect.yaml
index 14dfe45..c52f69f 100644
--- a/data/help/inspect.yaml
+++ b/data/help/inspect.yaml
@@ -2,5 +2,5 @@ name: inspect
description: |-
Admin-only. Dumps detailed diagnostic information about your current
room, including exits, objects, mobs, item spawns, on-enter steps,
- triggers, world flags, your player flags, and connected players.
+ triggers, global flags, your player flags, and connected players.
Usage: inspect
diff --git a/data/help/setflag.yaml b/data/help/setflag.yaml
deleted file mode 100644
index 7a164d6..0000000
--- a/data/help/setflag.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: setflag
-description: |-
- Admin-only. Sets a world flag. World flags are shared by all players.
- If no value is given, sets the flag to true.
- Values can be: true, false, an integer, or a string.
- Usage: setflag <flag_name> [value]
- Example: setflag gate_open true
diff --git a/data/help/setglobalflag.yaml b/data/help/setglobalflag.yaml
new file mode 100644
index 0000000..3f6fc2e
--- /dev/null
+++ b/data/help/setglobalflag.yaml
@@ -0,0 +1,7 @@
+name: setglobalflag
+description: |-
+ Admin-only. Sets a global flag. Global flags are shared by all players.
+ If no value is given, sets the flag to true.
+ Values can be: true, false, an integer, or a string.
+ Usage: setglobalflag <flag_name> [value]
+ Example: setglobalflag gate_open true
diff --git a/data/mobs/examples/guard.yaml b/data/mobs/examples/guard.yaml
index bd20f24..2eee6e0 100644
--- a/data/mobs/examples/guard.yaml
+++ b/data/mobs/examples/guard.yaml
@@ -24,7 +24,7 @@ talk:
- text: '"Goodbye."'
has_pass:
action:
- set_flags:
+ set_global_flags:
gate_open: true
messages:
- '"Let me see that..." He examines your pass and nods. "Alright, I''ll open the gate for you."'
diff --git a/data/objects/iron_gate.yaml b/data/objects/iron_gate.yaml
index ee60d36..44599ae 100644
--- a/data/objects/iron_gate.yaml
+++ b/data/objects/iron_gate.yaml
@@ -4,10 +4,10 @@ hidden: true
name: iron gate
use_interactions:
- condition:
- flag: gate_open
+ global_flag: gate_open
not: true
value: true
message: You push the heavy iron gate open.
action:
- set_flags:
+ set_global_flags:
gate_open: true
diff --git a/data/rooms/example_unused/agility2/99999202.yaml b/data/rooms/example_unused/agility2/99999202.yaml
index c4e068a..346a235 100644
--- a/data/rooms/example_unused/agility2/99999202.yaml
+++ b/data/rooms/example_unused/agility2/99999202.yaml
@@ -11,7 +11,7 @@ on_enter:
- message: Type 'balance' to cross the pipe.
condition: null
delay: 0
- set_flags: {}
+ set_global_flags: {}
set_player_flags: {}
broadcast: ""
broadcast_global: ""
diff --git a/data/rooms/intro/1001.yaml b/data/rooms/intro/1001.yaml
index 1ea5301..93bf7ac 100644
--- a/data/rooms/intro/1001.yaml
+++ b/data/rooms/intro/1001.yaml
@@ -11,7 +11,7 @@ exits:
condition:
all_of: []
any_of: []
- flag: ""
+ global_flag: ""
has_item: ""
min_credits: 0
not: false
@@ -36,7 +36,7 @@ objects:
give_item: ""
heal: 0
reputation_cost: 0
- set_flags: {}
+ set_global_flags: {}
set_player_flags:
1001_look_sign: true
take_item: ""
@@ -66,7 +66,7 @@ on_enter:
condition:
all_of: []
any_of: []
- flag: ""
+ global_flag: ""
has_item: ""
min_credits: 0
not: true
@@ -77,7 +77,7 @@ on_enter:
give_item: ""
heal: 0
message: '{0B bold}Welcome to The House of Icarus{/}'
- set_flags: {}
+ set_global_flags: {}
set_player_flags: {}
spawn_mob: null
take_item: ""
@@ -87,7 +87,7 @@ on_enter:
condition:
all_of: []
any_of: []
- flag: ""
+ global_flag: ""
has_item: ""
min_credits: 0
not: true
@@ -98,7 +98,7 @@ on_enter:
give_item: ""
heal: 0
message: '{0B}Type{/} {0A}look sign{/} {0B}or{/} {0A}talk attendant{/} {0B}to get started{/}'
- set_flags: {}
+ set_global_flags: {}
set_player_flags:
1001_welcome: true
spawn_mob: null
@@ -109,7 +109,7 @@ on_enter:
condition:
all_of: []
any_of: []
- flag: ""
+ global_flag: ""
has_item: ""
min_credits: 0
not: true
@@ -120,14 +120,14 @@ on_enter:
give_item: ""
heal: 0
message: '{0B}Type{/} {0A}help newplayer{/} {0B}for the new player''s guide. That''s it. Explore and have fun.{/}'
- set_flags: {}
+ set_global_flags: {}
set_player_flags: {}
spawn_mob: null
take_item: ""
teleport: 0
triggers:
- id: ""
- on_flag: ""
+ on_global_flag: ""
on_player_flag: 1001_look_sign
room: 0
steps:
@@ -138,7 +138,7 @@ triggers:
give_item: ""
heal: 0
message: The cabin shakes as the small craft touches down
- set_flags: {}
+ set_global_flags: {}
set_player_flags: {}
spawn_mob: null
take_item: ""
@@ -150,7 +150,7 @@ triggers:
give_item: ""
heal: 0
message: The pistons hiss as the rear staircase opens
- set_flags: {}
+ set_global_flags: {}
set_player_flags: {}
spawn_mob: null
take_item: ""
@@ -162,7 +162,7 @@ triggers:
give_item: ""
heal: 0
message: ""
- set_flags: {}
+ set_global_flags: {}
set_player_flags:
1001_touchdown: true
spawn_mob: null
diff --git a/internal/admin/api_flags.go b/internal/admin/api_global_flags.go
index e2594f3..ab286ad 100644
--- a/internal/admin/api_flags.go
+++ b/internal/admin/api_global_flags.go
@@ -2,13 +2,13 @@ package admin
import "net/http"
-func (s *AdminServer) handleFlags(w http.ResponseWriter, r *http.Request) {
+func (s *AdminServer) handleGlobalFlags(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
writeJSON(w, map[string]any{
- "flags": []any{},
- "note": "world flags are in-memory only",
+ "global_flags": []any{},
+ "note": "global flags are in-memory only",
})
}
diff --git a/internal/admin/server.go b/internal/admin/server.go
index 17835db..f06b7cf 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -167,7 +167,7 @@ func NewServer(cfg *config.Config, useTLS bool, accountStore *player.AccountStor
apiMux.HandleFunc("/api/courses/", s.handleCourseByID)
apiMux.HandleFunc("/api/players", s.handlePlayers)
apiMux.HandleFunc("/api/dashboard", s.handleDashboard)
- apiMux.HandleFunc("/api/flags", s.handleFlags)
+ apiMux.HandleFunc("/api/global_flags", s.handleGlobalFlags)
apiMux.HandleFunc("/api/tools", s.handleTools)
apiMux.HandleFunc("/api/search", s.handleSearch)
apiMux.HandleFunc("/api/undo/state", s.handleUndoState)
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go
index 7d8ce8f..ec9fc7c 100644
--- a/internal/behavior/behavior.go
+++ b/internal/behavior/behavior.go
@@ -43,7 +43,7 @@ type TalkOption struct {
}
type NodeAction struct {
- SetFlags map[string]any `yaml:"set_flags"`
+ SetGlobalFlags map[string]any `yaml:"set_global_flags"`
SetPlayerFlags map[string]any `yaml:"set_player_flags"`
GiveItem string `yaml:"give_item"`
TakeItem string `yaml:"take_item"`
@@ -143,7 +143,7 @@ func (c *ShopConfig) FindItem(itemID string) *ShopItem {
}
type Condition struct {
- Flag string `yaml:"flag"`
+ GlobalFlag string `yaml:"global_flag"`
Value any `yaml:"value"`
Not bool `yaml:"not"`
PlayerFlag string `yaml:"player_flag"`
diff --git a/internal/game/act.go b/internal/game/act.go
index f2ea121..ddeed04 100644
--- a/internal/game/act.go
+++ b/internal/game/act.go
@@ -297,8 +297,8 @@ func (g *Game) checkCondition(sess *net.Session, c *behavior.Condition) bool {
return flagMatches(present, val, c.Value, c.Not)
}
- if c.Flag != "" {
- val, present := g.Flags.Get(c.Flag)
+ if c.GlobalFlag != "" {
+ val, present := g.GlobalFlags.Get(c.GlobalFlag)
return flagMatches(present, val, c.Value, c.Not)
}
if c.HasItem != "" {
@@ -318,7 +318,8 @@ func (g *Game) checkCondition(sess *net.Session, c *behavior.Condition) bool {
return true
}
-// flagMatches evaluates a flag/player_flag condition.
+// flagMatches evaluates a global_flag/player_flag condition. The name is
+// historical; it evaluates both flag types.
//
// If the condition specifies no `value`, the flag matches when it is present
// and truthy (so `{player_flag: x}` means "x is set" and
diff --git a/internal/game/act_room.go b/internal/game/act_room.go
index ddca261..da74d6e 100644
--- a/internal/game/act_room.go
+++ b/internal/game/act_room.go
@@ -161,8 +161,8 @@ func (g *Game) fireEnterStep(seq *enterSeq, step world.EnterStep) {
}
}
}
- if len(step.SetFlags) > 0 || len(step.SetPlayerFlags) > 0 {
- g.applyFlagMutations(p, step.SetFlags, step.SetPlayerFlags)
+ if len(step.SetGlobalFlags) > 0 || len(step.SetPlayerFlags) > 0 {
+ g.applyFlagMutations(p, step.SetGlobalFlags, step.SetPlayerFlags)
g.AccountStore.SaveCharacter(p)
}
if step.SpawnMob != nil {
@@ -229,10 +229,10 @@ func (g *Game) isCharLive(name string, sess *net.Session) bool {
return g.loggedInChars[name] == sess
}
-// applyFlagMutations sets world and player flags, shared by on_enter steps and
+// applyFlagMutations sets global and player flags, shared by on_enter steps and
// exit traversal.
-func (g *Game) applyFlagMutations(p *player.Player, setFlags, setPlayerFlags map[string]any) {
- g.Flags.SetAll(setFlags)
+func (g *Game) applyFlagMutations(p *player.Player, setGlobalFlags, setPlayerFlags map[string]any) {
+ g.GlobalFlags.SetAll(setGlobalFlags)
if len(setPlayerFlags) > 0 {
for k, v := range setPlayerFlags {
g.setPlayerFlag(p, k, v)
diff --git a/internal/game/act_steal.go b/internal/game/act_steal.go
index b5c1210..0aae6d1 100644
--- a/internal/game/act_steal.go
+++ b/internal/game/act_steal.go
@@ -47,7 +47,7 @@ var stallGuardTalk = &behavior.TalkConfig{
},
"fight": {
Messages: []string{"Then defend yourself!"},
- Action: &behavior.NodeAction{SetFlags: map[string]any{"guard_hostile": true}},
+ Action: &behavior.NodeAction{SetGlobalFlags: map[string]any{"guard_hostile": true}},
Options: []behavior.TalkOption{{Text: "(The guard attacks!)"}},
},
},
diff --git a/internal/game/act_talk.go b/internal/game/act_talk.go
index 7cfdf7c..ca6054c 100644
--- a/internal/game/act_talk.go
+++ b/internal/game/act_talk.go
@@ -60,8 +60,8 @@ func (g *Game) startTalk(sess *net.Session, p *player.Player, obj *object.Object
func (g *Game) handleNodeAction(sess *net.Session, na *behavior.NodeAction) (intercepted bool) {
g.applyNodeAction(sess, na)
- if v, ok := g.Flags.Get("guard_hostile"); ok && v != nil {
- g.Flags.Delete("guard_hostile")
+ if v, ok := g.GlobalFlags.Get("guard_hostile"); ok && v != nil {
+ g.GlobalFlags.Delete("guard_hostile")
p := sess.Player
if p != nil && p.Action != nil {
if td, ok := p.Action.Data.(*behavior.TalkData); ok && td.StealGuard {
@@ -348,8 +348,8 @@ func (g *Game) applyNodeAction(sess *net.Session, na *behavior.NodeAction) {
g.AccountStore.SaveCharacter(p)
}
- for k, v := range na.SetFlags {
- g.Flags.Set(k, v)
+ for k, v := range na.SetGlobalFlags {
+ g.GlobalFlags.Set(k, v)
}
for k, v := range na.SetPlayerFlags {
g.setPlayerFlag(p, k, v)
diff --git a/internal/game/cmd_inspect.go b/internal/game/cmd_inspect.go
index 62392b7..7488591 100644
--- a/internal/game/cmd_inspect.go
+++ b/internal/game/cmd_inspect.go
@@ -92,8 +92,8 @@ func (g *Game) executeInspect(sess *net.Session, args []string, rawInput string)
sess.WriteLine(fmt.Sprintf("\nRoom Triggers: %d", len(room.Triggers)))
for _, trigger := range room.Triggers {
kind := ""
- if trigger.OnFlag != "" {
- kind = fmt.Sprintf("world flag %q", trigger.OnFlag)
+ if trigger.OnGlobalFlag != "" {
+ kind = fmt.Sprintf("global flag %q", trigger.OnGlobalFlag)
} else if trigger.OnPlayerFlag != "" {
kind = fmt.Sprintf("player flag %q", trigger.OnPlayerFlag)
}
@@ -101,8 +101,8 @@ func (g *Game) executeInspect(sess *net.Session, args []string, rawInput string)
}
}
- sess.WriteLine("\n--- World Flags ---")
- allFlags := g.Flags.All()
+ sess.WriteLine("\n--- Global Flags ---")
+ allFlags := g.GlobalFlags.All()
if len(allFlags) == 0 {
sess.WriteLine(" (none)")
} else {
diff --git a/internal/game/cmd_move.go b/internal/game/cmd_move.go
index f236d67..2f27eed 100644
--- a/internal/game/cmd_move.go
+++ b/internal/game/cmd_move.go
@@ -67,7 +67,7 @@ func (g *Game) doMove(sess *net.Session, dir string, multiplier float64) {
}
}
- p.MovePendingFlags = exitDef.SetFlags
+ p.MovePendingGlobalFlags = exitDef.SetGlobalFlags
p.MovePendingPlayerFlags = exitDef.SetPlayerFlags
inCombat := g.Combat.Get(p.Name) != nil
@@ -132,7 +132,7 @@ func (g *Game) moveTicks(p *player.Player, multiplier float64) int {
func (g *Game) completeMove(sess *net.Session, p *player.Player) {
exitDir := p.MoveDirection
targetID := p.MoveTarget
- pendingFlags := p.MovePendingFlags
+ pendingGlobalFlags := p.MovePendingGlobalFlags
pendingPlayerFlags := p.MovePendingPlayerFlags
p.ClearMoveState()
@@ -163,8 +163,8 @@ func (g *Game) completeMove(sess *net.Session, p *player.Player) {
if p.EnterSeqRoom != 0 && p.EnterSeqRoom == oldRoom {
p.EnterSeqRoom = 0
}
- if len(pendingFlags) > 0 || len(pendingPlayerFlags) > 0 {
- g.applyFlagMutations(p, pendingFlags, pendingPlayerFlags)
+ if len(pendingGlobalFlags) > 0 || len(pendingPlayerFlags) > 0 {
+ g.applyFlagMutations(p, pendingGlobalFlags, pendingPlayerFlags)
}
g.AccountStore.SaveCharacter(p)
diff --git a/internal/game/cmd_registry.go b/internal/game/cmd_registry.go
index 462bf53..e2c34aa 100644
--- a/internal/game/cmd_registry.go
+++ b/internal/game/cmd_registry.go
@@ -150,7 +150,7 @@ var commandRegistry = map[string]commandDef{
"goto": {(*Game).executeGoto, ClassInstant},
"summon": {(*Game).executeSummon, ClassInstant},
"dig": {(*Game).executeDig, ClassInstant},
- "setflag": {(*Game).executeSetFlag, ClassInstant},
+ "setglobalflag": {(*Game).executeSetGlobalFlag, ClassInstant},
"setplayerflag": {(*Game).executeSetPlayerFlag, ClassInstant},
"reload": {(*Game).executeReload, ClassInstant},
"shutdown": {(*Game).executeShutdown, ClassInstant},
diff --git a/internal/game/cmd_room_insert.go b/internal/game/cmd_room_insert.go
index b72591f..713c5ee 100644
--- a/internal/game/cmd_room_insert.go
+++ b/internal/game/cmd_room_insert.go
@@ -138,7 +138,7 @@ func (g *Game) roomInsert(sess *net.Session, args []string) {
Room: newID,
Condition: exitDef.Condition,
BlockedMessage: exitDef.BlockedMessage,
- SetFlags: exitDef.SetFlags,
+ SetGlobalFlags: exitDef.SetGlobalFlags,
SetPlayerFlags: exitDef.SetPlayerFlags,
Hidden: exitDef.Hidden,
AlwaysBlocked: exitDef.AlwaysBlocked,
@@ -158,7 +158,7 @@ func (g *Game) roomInsert(sess *net.Session, args []string) {
Room: newID,
Condition: targetExit.Condition,
BlockedMessage: targetExit.BlockedMessage,
- SetFlags: targetExit.SetFlags,
+ SetGlobalFlags: targetExit.SetGlobalFlags,
SetPlayerFlags: targetExit.SetPlayerFlags,
Hidden: targetExit.Hidden,
AlwaysBlocked: targetExit.AlwaysBlocked,
diff --git a/internal/game/cmd_room_remove.go b/internal/game/cmd_room_remove.go
index 78c1234..28433ff 100644
--- a/internal/game/cmd_room_remove.go
+++ b/internal/game/cmd_room_remove.go
@@ -171,7 +171,7 @@ func (g *Game) roomRemove(sess *net.Session, args []string) {
Room: cID,
Condition: aExit.Condition,
BlockedMessage: aExit.BlockedMessage,
- SetFlags: aExit.SetFlags,
+ SetGlobalFlags: aExit.SetGlobalFlags,
SetPlayerFlags: aExit.SetPlayerFlags,
Hidden: aExit.Hidden,
AlwaysBlocked: aExit.AlwaysBlocked,
@@ -191,7 +191,7 @@ func (g *Game) roomRemove(sess *net.Session, args []string) {
Room: aID,
Condition: cOppExit.Condition,
BlockedMessage: cOppExit.BlockedMessage,
- SetFlags: cOppExit.SetFlags,
+ SetGlobalFlags: cOppExit.SetGlobalFlags,
SetPlayerFlags: cOppExit.SetPlayerFlags,
Hidden: cOppExit.Hidden,
AlwaysBlocked: cOppExit.AlwaysBlocked,
diff --git a/internal/game/cmd_setflag.go b/internal/game/cmd_setglobalflag.go
index a18c635..1ac5080 100644
--- a/internal/game/cmd_setflag.go
+++ b/internal/game/cmd_setglobalflag.go
@@ -7,13 +7,13 @@ import (
"thehouseoficarus/internal/net"
)
-func (g *Game) executeSetFlag(sess *net.Session, args []string, rawInput string) {
+func (g *Game) executeSetGlobalFlag(sess *net.Session, args []string, rawInput string) {
if !g.checkAdmin(sess) {
sess.WriteLine("Unknown command.")
return
}
if len(args) == 0 {
- sess.WriteLine("Usage: setflag <flag_name> [value]")
+ sess.WriteLine("Usage: setglobalflag <flag_name> [value]")
return
}
name := args[0]
@@ -33,6 +33,6 @@ func (g *Game) executeSetFlag(sess *net.Session, args []string, rawInput string)
}
}
}
- g.Flags.Set(name, value)
- sess.WriteLine(fmt.Sprintf("Flag '%s' set to %v.", name, value))
+ g.GlobalFlags.Set(name, value)
+ sess.WriteLine(fmt.Sprintf("Global flag '%s' set to %v.", name, value))
}
diff --git a/internal/game/condition_test.go b/internal/game/condition_test.go
index f365f2e..f7a5dc6 100644
--- a/internal/game/condition_test.go
+++ b/internal/game/condition_test.go
@@ -39,7 +39,7 @@ func TestIsTruthy(t *testing.T) {
}
func TestCheckConditionPlayerFlagTruthy(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
set := sessWithFlags(map[string]any{"x": true})
unset := sessWithFlags(map[string]any{})
@@ -73,7 +73,7 @@ func TestCheckConditionPlayerFlagTruthy(t *testing.T) {
}
func TestCheckConditionValueComparisonPreserved(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
sess := sessWithFlags(map[string]any{"n": 1})
if !g.checkCondition(sess, &behavior.Condition{PlayerFlag: "n", Value: 1}) {
@@ -87,24 +87,24 @@ func TestCheckConditionValueComparisonPreserved(t *testing.T) {
}
}
-func TestCheckConditionWorldFlag(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
- g.Flags.Set("gate_open", true)
+func TestCheckConditionGlobalFlag(t *testing.T) {
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
+ g.GlobalFlags.Set("gate_open", true)
sess := sessWithFlags(map[string]any{})
- if !g.checkCondition(sess, &behavior.Condition{Flag: "gate_open"}) {
- t.Error("world flag truthy check should pass")
+ if !g.checkCondition(sess, &behavior.Condition{GlobalFlag: "gate_open"}) {
+ t.Error("global flag truthy check should pass")
}
- if g.checkCondition(sess, &behavior.Condition{Flag: "gate_open", Not: true}) {
+ if g.checkCondition(sess, &behavior.Condition{GlobalFlag: "gate_open", Not: true}) {
t.Error("negated world flag should fail when set")
}
- if g.checkCondition(sess, &behavior.Condition{Flag: "missing"}) {
+ if g.checkCondition(sess, &behavior.Condition{GlobalFlag: "missing"}) {
t.Error("missing world flag should not pass")
}
}
func TestResolveObjDescPresence(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
def := &object.ObjectDef{
Description: behavior.DescList{
@@ -140,7 +140,7 @@ func TestResolveObjDescPresence(t *testing.T) {
}
func TestRoomDescriptionSelection(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
room := &world.Room{
Description: behavior.DescList{
{Text: "crowd", Condition: &behavior.Condition{PlayerFlag: "done", Not: true}},
diff --git a/internal/game/core_flagstore.go b/internal/game/core_flagstore.go
index ecefccf..18578b7 100644
--- a/internal/game/core_flagstore.go
+++ b/internal/game/core_flagstore.go
@@ -2,37 +2,37 @@ package game
import "sync"
-// FlagChangeCallback is invoked when a world flag value actually changes
+// GlobalFlagChangeCallback is invoked when a global flag value actually changes
// (old value differs from new, or new flag is created with a truthy value).
-type FlagChangeCallback func(name string, value any)
+type GlobalFlagChangeCallback func(name string, value any)
-// FlagStore holds world flags — shared mutable state visible to all players
-// (e.g. opened doors, quest state). Player-specific flags live on
-// *player.Player.Flags instead. FlagStore is safe for concurrent use.
-type FlagStore struct {
+// GlobalFlagStore holds global flags — shared mutable state visible to all
+// players (e.g. opened doors, quest state). Player-specific flags live on
+// *player.Player.Flags instead. GlobalFlagStore is safe for concurrent use.
+type GlobalFlagStore struct {
mu sync.Mutex
flags map[string]any
- callbacks []FlagChangeCallback
+ callbacks []GlobalFlagChangeCallback
}
-func NewFlagStore() *FlagStore {
- return &FlagStore{flags: make(map[string]any)}
+func NewGlobalFlagStore() *GlobalFlagStore {
+ return &GlobalFlagStore{flags: make(map[string]any)}
}
-func (f *FlagStore) OnChange(cb FlagChangeCallback) {
+func (f *GlobalFlagStore) OnChange(cb GlobalFlagChangeCallback) {
f.mu.Lock()
defer f.mu.Unlock()
f.callbacks = append(f.callbacks, cb)
}
-func (f *FlagStore) Get(name string) (any, bool) {
+func (f *GlobalFlagStore) Get(name string) (any, bool) {
f.mu.Lock()
defer f.mu.Unlock()
v, ok := f.flags[name]
return v, ok
}
-func (f *FlagStore) Set(name string, value any) {
+func (f *GlobalFlagStore) Set(name string, value any) {
f.mu.Lock()
old, existed := f.flags[name]
f.flags[name] = value
@@ -46,7 +46,7 @@ func (f *FlagStore) Set(name string, value any) {
}
}
-func (f *FlagStore) SetAll(m map[string]any) {
+func (f *GlobalFlagStore) SetAll(m map[string]any) {
f.mu.Lock()
changed := make(map[string]any)
for k, v := range m {
@@ -66,13 +66,13 @@ func (f *FlagStore) SetAll(m map[string]any) {
}
}
-func (f *FlagStore) Delete(name string) {
+func (f *GlobalFlagStore) Delete(name string) {
f.mu.Lock()
defer f.mu.Unlock()
delete(f.flags, name)
}
-func (f *FlagStore) All() map[string]any {
+func (f *GlobalFlagStore) All() map[string]any {
f.mu.Lock()
defer f.mu.Unlock()
out := make(map[string]any, len(f.flags))
diff --git a/internal/game/exit_discovery_test.go b/internal/game/exit_discovery_test.go
index 3c0b360..4bf1023 100644
--- a/internal/game/exit_discovery_test.go
+++ b/internal/game/exit_discovery_test.go
@@ -28,7 +28,7 @@ func TestExitDiscoveredUndiscovered(t *testing.T) {
}
func TestExitDiscoveredAfterMark(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
p := &player.Player{}
roomID := 42
dir := world.East
@@ -54,7 +54,7 @@ func TestExitDiscoveredGodMode(t *testing.T) {
}
func TestExitDiscoveredDifferentRoom(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
p := &player.Player{}
g.markExitDiscovered(p, 10, world.North)
@@ -67,7 +67,7 @@ func TestExitDiscoveredDifferentRoom(t *testing.T) {
}
func TestExitDisplayState(t *testing.T) {
- g := &Game{Flags: NewFlagStore()}
+ g := &Game{GlobalFlags: NewGlobalFlagStore()}
const roomID = 7
dir := world.North
diff --git a/internal/game/game.go b/internal/game/game.go
index bf7bdc2..b9fb0af 100644
--- a/internal/game/game.go
+++ b/internal/game/game.go
@@ -44,13 +44,13 @@ type Deps struct {
}
// Game is the central orchestrator: it owns the data stores (via the embedded
-// Deps), the shared mutable game state (flags, combat tracker, command queue,
+// Deps), the shared mutable game state (global flags, combat tracker, command queue,
// safespots), and the per-session runtime bookkeeping.
type Game struct {
Deps
Hub *net.Hub
- Flags *FlagStore
+ GlobalFlags *GlobalFlagStore
Combat *combat.Tracker
TriggerStore *world.TriggerStore
queue *CommandQueue
@@ -88,7 +88,7 @@ func New(dataDir string, colorConfig *config.ColorsConfig, valConfig config.Vali
ColorConfig: colorConfig,
DataDir: dataDir,
},
- Flags: NewFlagStore(),
+ GlobalFlags: NewGlobalFlagStore(),
Combat: combat.NewTracker(),
TriggerStore: world.NewTriggerStore(),
queue: NewCommandQueue(),
@@ -127,8 +127,8 @@ func (g *Game) SetHub(hub *net.Hub) {
}
}
})
- g.Flags.OnChange(func(name string, value any) {
- g.TriggerStore.FireWorld(name, value)
+ g.GlobalFlags.OnChange(func(name string, value any) {
+ g.TriggerStore.FireGlobal(name, value)
})
}
diff --git a/internal/game/map_test.go b/internal/game/map_test.go
index 159214c..2fe9c1e 100644
--- a/internal/game/map_test.go
+++ b/internal/game/map_test.go
@@ -29,10 +29,10 @@ func writeTempRoom(t *testing.T, dir string, id int, body string) {
// links draw a bar, one-way links draw a directional arrow, outward-blocked
// links draw a blocked 'X', and links with no traversable direction draw 'X'.
func TestMapConnectorGlyphs(t *testing.T) {
- const condEast = "name: One\nexits:\n east:\n room: 2\n condition:\n flag: gate_open\n"
- const condSouth = "name: Three\nexits:\n south:\n room: 2\n condition:\n flag: gate_open\n"
- const condNE = "name: One\nexits:\n northeast:\n room: 2\n condition:\n flag: gate_open\n"
- const condSE = "name: One\nexits:\n southeast:\n room: 2\n condition:\n flag: gate_open\n"
+ const condEast = "name: One\nexits:\n east:\n room: 2\n condition:\n global_flag: gate_open\n"
+ const condSouth = "name: Three\nexits:\n south:\n room: 2\n condition:\n global_flag: gate_open\n"
+ const condNE = "name: One\nexits:\n northeast:\n room: 2\n condition:\n global_flag: gate_open\n"
+ const condSE = "name: One\nexits:\n southeast:\n room: 2\n condition:\n global_flag: gate_open\n"
cases := []struct {
name string
@@ -161,9 +161,9 @@ func TestMapConnectorGlyphs(t *testing.T) {
writeTempRoom(t, dir, 4, tc.room4)
}
- g := &Game{Deps: Deps{World: world.New(dir)}, Flags: NewFlagStore()}
+ g := &Game{Deps: Deps{World: world.New(dir)}, GlobalFlags: NewGlobalFlagStore()}
if tc.flagOpen {
- g.Flags.Set("gate_open", true)
+ g.GlobalFlags.Set("gate_open", true)
}
sess := &net.Session{Player: &player.Player{Flags: map[string]any{}}}
@@ -401,7 +401,7 @@ func TestMap3DDisconnectedComponent(t *testing.T) {
writeTempRoom(t, dir, 3, "name: Tower Two Base\nexits:\n up: 4\n west: 2\n")
writeTempRoom(t, dir, 4, "name: Tower Two\nexits:\n down: 3\n")
- g := &Game{Deps: Deps{World: world.New(dir)}, Flags: NewFlagStore()}
+ g := &Game{Deps: Deps{World: world.New(dir)}, GlobalFlags: NewGlobalFlagStore()}
mg := mapGlyphsForPlayer(false)
// Player has only visited tower 1. Tower 2 should appear but dimmed (unvisited).
@@ -426,7 +426,7 @@ func TestMap3DDifferentZExcluded(t *testing.T) {
writeTempRoom(t, dir, 1, "name: Ground\nexits:\n up: 2\n")
writeTempRoom(t, dir, 2, "name: Upper\nexits:\n down: 1\n")
- g := &Game{Deps: Deps{World: world.New(dir)}, Flags: NewFlagStore()}
+ g := &Game{Deps: Deps{World: world.New(dir)}, GlobalFlags: NewGlobalFlagStore()}
mg := mapGlyphsForPlayer(false)
sess := &net.Session{Player: &player.Player{
Stats: player.PlayerStats{RoomsVisited: map[int]bool{1: true, 2: true}},
diff --git a/internal/game/sys_triggers.go b/internal/game/sys_triggers.go
index 7c77fc9..b542fdd 100644
--- a/internal/game/sys_triggers.go
+++ b/internal/game/sys_triggers.go
@@ -13,7 +13,7 @@ import (
func (g *Game) TriggerSeqTick() {
g.processPlayerTriggerSequences()
- g.processWorldTriggerSequences()
+ g.processGlobalTriggerSequences()
}
func (g *Game) TransientMobTick() {
@@ -106,8 +106,8 @@ func (g *Game) processPlayerTriggerSequences() {
}
}
-func (g *Game) processWorldTriggerSequences() {
- seqs := g.TriggerStore.SnapshotWorldSeqs()
+func (g *Game) processGlobalTriggerSequences() {
+ seqs := g.TriggerStore.SnapshotGlobalSeqs()
for _, seq := range seqs {
seq.Wait--
if seq.Wait > 0 {
@@ -125,11 +125,11 @@ func (g *Game) processWorldTriggerSequences() {
}
for _, step := range jobs {
- g.executeWorldTriggerStep(&step, seq.RoomID, seq.FlagValue)
+ g.executeGlobalTriggerStep(&step, seq.RoomID, seq.FlagValue)
}
if len(seq.Steps) == 0 {
- g.TriggerStore.RemoveWorldSeq(seq.TriggerID)
+ g.TriggerStore.RemoveGlobalSeq(seq.TriggerID)
}
}
}
@@ -161,8 +161,8 @@ func (g *Game) executeTriggerStep(sess *net.Session, p *player.Player, step *wor
}
}
}
- if len(step.SetFlags) > 0 {
- g.Flags.SetAll(step.SetFlags)
+ if len(step.SetGlobalFlags) > 0 {
+ g.GlobalFlags.SetAll(step.SetGlobalFlags)
}
if len(step.SetPlayerFlags) > 0 {
for k, v := range step.SetPlayerFlags {
@@ -204,7 +204,7 @@ func (g *Game) executeTriggerStep(sess *net.Session, p *player.Player, step *wor
}
}
-func (g *Game) executeWorldTriggerStep(step *world.TriggerStep, roomID int, flagValue any) {
+func (g *Game) executeGlobalTriggerStep(step *world.TriggerStep, roomID int, flagValue any) {
if step.Broadcast != "" && g.Hub != nil {
msg := expandTemplate(step.Broadcast, "", flagValue)
broadcastSpec := g.resolveColor(nil, "broadcast")
@@ -225,8 +225,8 @@ func (g *Game) executeWorldTriggerStep(step *world.TriggerStep, roomID int, flag
}
}
}
- if len(step.SetFlags) > 0 {
- g.Flags.SetAll(step.SetFlags)
+ if len(step.SetGlobalFlags) > 0 {
+ g.GlobalFlags.SetAll(step.SetGlobalFlags)
}
if step.SpawnMob != nil {
g.spawnWorldTriggerMob(step.SpawnMob, roomID)
diff --git a/internal/player/player.go b/internal/player/player.go
index 72eb455..65026e7 100644
--- a/internal/player/player.go
+++ b/internal/player/player.go
@@ -183,7 +183,7 @@ type Player struct {
MoveTicks int `yaml:"-"`
MoveDirection string `yaml:"-"`
MoveTarget int `yaml:"-"`
- MovePendingFlags map[string]any `yaml:"-"`
+ MovePendingGlobalFlags map[string]any `yaml:"-"`
MovePendingPlayerFlags map[string]any `yaml:"-"`
VisualTickCurrent int `yaml:"-"`
AutotriggerMod string `yaml:"-"`
@@ -219,7 +219,7 @@ func (p *Player) ClearMoveState() {
p.MoveTicks = 0
p.MoveDirection = ""
p.MoveTarget = 0
- p.MovePendingFlags = nil
+ p.MovePendingGlobalFlags = nil
p.MovePendingPlayerFlags = nil
}
diff --git a/internal/world/grid_test.go b/internal/world/grid_test.go
index 04d749c..9c2c7c6 100644
--- a/internal/world/grid_test.go
+++ b/internal/world/grid_test.go
@@ -117,11 +117,11 @@ func TestBuildGridConflictsHypotheticalInsert(t *testing.T) {
copy.Exits = make(map[ExitDir]ExitDef, len(r.Exits))
for k, v := range r.Exits {
if id == 1 && k == East {
- copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetFlags: v.SetFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
+ copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetGlobalFlags: v.SetGlobalFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
continue
}
if id == 2 && k == West {
- copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetFlags: v.SetFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
+ copy.Exits[k] = ExitDef{Room: 5, Condition: v.Condition, BlockedMessage: v.BlockedMessage, SetGlobalFlags: v.SetGlobalFlags, SetPlayerFlags: v.SetPlayerFlags, Hidden: v.Hidden, AlwaysBlocked: v.AlwaysBlocked}
continue
}
copy.Exits[k] = v
diff --git a/internal/world/insert_remove.go b/internal/world/insert_remove.go
index 0811587..c55427c 100644
--- a/internal/world/insert_remove.go
+++ b/internal/world/insert_remove.go
@@ -20,7 +20,7 @@ func RewirePreserving(src ExitDef, newTarget int) ExitDef {
Room: newTarget,
Condition: src.Condition,
BlockedMessage: src.BlockedMessage,
- SetFlags: src.SetFlags,
+ SetGlobalFlags: src.SetGlobalFlags,
SetPlayerFlags: src.SetPlayerFlags,
Hidden: src.Hidden,
AlwaysBlocked: src.AlwaysBlocked,
diff --git a/internal/world/room.go b/internal/world/room.go
index 29cce0b..2da40fb 100644
--- a/internal/world/room.go
+++ b/internal/world/room.go
@@ -78,7 +78,7 @@ type ExitDef struct {
Room int `yaml:"room"`
Condition *behavior.Condition `yaml:"condition,omitempty"`
BlockedMessage string `yaml:"blocked_message,omitempty"`
- SetFlags map[string]any `yaml:"set_flags,omitempty"`
+ SetGlobalFlags map[string]any `yaml:"set_global_flags,omitempty"`
SetPlayerFlags map[string]any `yaml:"set_player_flags,omitempty"`
Hidden bool `yaml:"hidden,omitempty"`
AlwaysBlocked bool `yaml:"always_blocked,omitempty"`
@@ -135,7 +135,7 @@ type EnterStep struct {
Message string `yaml:"message"`
Condition *behavior.Condition `yaml:"condition"`
Delay int `yaml:"delay"`
- SetFlags map[string]any `yaml:"set_flags"`
+ SetGlobalFlags map[string]any `yaml:"set_global_flags"`
SetPlayerFlags map[string]any `yaml:"set_player_flags"`
Broadcast string `yaml:"broadcast"`
BroadcastGlobal string `yaml:"broadcast_global"`
@@ -151,7 +151,7 @@ type EnterStep struct {
// non-zero field means it needs per-tick scheduling rather than synchronous
// printing).
func (e EnterStep) IsTimed() bool {
- return e.Delay > 0 || len(e.SetFlags) > 0 || len(e.SetPlayerFlags) > 0 ||
+ return e.Delay > 0 || len(e.SetGlobalFlags) > 0 || len(e.SetPlayerFlags) > 0 ||
e.Message != "" || e.Broadcast != "" || e.BroadcastGlobal != "" ||
e.SpawnMob != nil || e.DespawnMob != "" || e.GiveItem != "" ||
e.TakeItem != "" || e.Teleport != 0 || e.Heal != 0
diff --git a/internal/world/room_migrate_test.go b/internal/world/room_migrate_test.go
index b035172..5696a36 100644
--- a/internal/world/room_migrate_test.go
+++ b/internal/world/room_migrate_test.go
@@ -5,7 +5,7 @@ import "testing"
func TestRoomRewriteRoomIDs(t *testing.T) {
r := &Room{
Exits: map[ExitDir]ExitDef{
- North: {Room: 10, SetFlags: map[string]any{"last": 10}},
+ North: {Room: 10, SetGlobalFlags: map[string]any{"last": 10}},
South: {Room: 99},
},
Triggers: []TriggerDef{
@@ -43,8 +43,8 @@ func TestRoomRewriteRoomIDs(t *testing.T) {
// Author set_flags values are NOT structural room references and must not
// be remapped (the old swapid regex wrongly rewrote these).
- if v := r.Exits[North].SetFlags["last"]; v != 10 {
- t.Errorf("set_flags value remapped: got %v, want 10", v)
+ if v := r.Exits[North].SetGlobalFlags["last"]; v != 10 {
+ t.Errorf("set_global_flags value remapped: got %v, want 10", v)
}
if r.Triggers[0].Room != 31 {
diff --git a/internal/world/trigger.go b/internal/world/trigger.go
index 8e74ff7..e31a5f9 100644
--- a/internal/world/trigger.go
+++ b/internal/world/trigger.go
@@ -7,7 +7,7 @@ import (
type TriggerDef struct {
ID string `yaml:"id"`
OnPlayerFlag string `yaml:"on_player_flag"`
- OnFlag string `yaml:"on_flag"`
+ OnGlobalFlag string `yaml:"on_global_flag"`
Value any `yaml:"value"`
Room int `yaml:"room"`
Steps []TriggerStep `yaml:"steps"`
@@ -15,14 +15,14 @@ type TriggerDef struct {
func (t *TriggerDef) IsPlayerFlagTrigger() bool { return t.OnPlayerFlag != "" }
-func (t *TriggerDef) IsWorldFlagTrigger() bool { return t.OnFlag != "" }
+func (t *TriggerDef) IsGlobalFlagTrigger() bool { return t.OnGlobalFlag != "" }
type TriggerStep struct {
Delay int `yaml:"delay"`
Message string `yaml:"message"`
Broadcast string `yaml:"broadcast"`
BroadcastGlobal string `yaml:"broadcast_global"`
- SetFlags map[string]any `yaml:"set_flags"`
+ SetGlobalFlags map[string]any `yaml:"set_global_flags"`
SetPlayerFlags map[string]any `yaml:"set_player_flags"`
SpawnMob *SpawnMobConfig `yaml:"spawn_mob"`
GiveItem string `yaml:"give_item"`
@@ -54,7 +54,7 @@ func (s *SpawnMobConfig) UnmarshalYAML(value *yaml.Node) error {
}
func (t TriggerStep) IsTimed() bool {
- return t.Delay > 0 || len(t.SetFlags) > 0 || len(t.SetPlayerFlags) > 0 ||
+ return t.Delay > 0 || len(t.SetGlobalFlags) > 0 || len(t.SetPlayerFlags) > 0 ||
t.Message != "" || t.Broadcast != "" || t.BroadcastGlobal != "" ||
t.SpawnMob != nil || t.GiveItem != "" || t.TakeItem != "" ||
t.Teleport != 0 || t.Heal != 0 || t.DespawnMob != ""
diff --git a/internal/world/trigger_store.go b/internal/world/trigger_store.go
index b5fac5b..e18ae80 100644
--- a/internal/world/trigger_store.go
+++ b/internal/world/trigger_store.go
@@ -14,12 +14,12 @@ type TriggerStore struct {
mu sync.Mutex
globalByPlayerFlag map[string][]*TriggerDef
- globalByWorldFlag map[string][]*TriggerDef
+ globalByGlobalFlag map[string][]*TriggerDef
roomByPlayerFlag map[int]map[string][]*TriggerDef
- roomByWorldFlag map[int]map[string][]*TriggerDef
+ roomByGlobalFlag map[int]map[string][]*TriggerDef
playerSeqs map[string]*PlayerTriggerSeq
- worldSeqs map[string]*WorldTriggerSeq
+ globalSeqs map[string]*GlobalTriggerSeq
}
type PlayerTriggerSeq struct {
@@ -32,7 +32,7 @@ type PlayerTriggerSeq struct {
FlagValue any
}
-type WorldTriggerSeq struct {
+type GlobalTriggerSeq struct {
TriggerID string
RoomID int
Steps []TriggerStep
@@ -43,11 +43,11 @@ type WorldTriggerSeq struct {
func NewTriggerStore() *TriggerStore {
return &TriggerStore{
globalByPlayerFlag: make(map[string][]*TriggerDef),
- globalByWorldFlag: make(map[string][]*TriggerDef),
+ globalByGlobalFlag: make(map[string][]*TriggerDef),
roomByPlayerFlag: make(map[int]map[string][]*TriggerDef),
- roomByWorldFlag: make(map[int]map[string][]*TriggerDef),
+ roomByGlobalFlag: make(map[int]map[string][]*TriggerDef),
playerSeqs: make(map[string]*PlayerTriggerSeq),
- worldSeqs: make(map[string]*WorldTriggerSeq),
+ globalSeqs: make(map[string]*GlobalTriggerSeq),
}
}
@@ -55,9 +55,9 @@ func (ts *TriggerStore) ClearTriggers() {
ts.mu.Lock()
defer ts.mu.Unlock()
ts.globalByPlayerFlag = make(map[string][]*TriggerDef)
- ts.globalByWorldFlag = make(map[string][]*TriggerDef)
+ ts.globalByGlobalFlag = make(map[string][]*TriggerDef)
ts.roomByPlayerFlag = make(map[int]map[string][]*TriggerDef)
- ts.roomByWorldFlag = make(map[int]map[string][]*TriggerDef)
+ ts.roomByGlobalFlag = make(map[int]map[string][]*TriggerDef)
}
func (ts *TriggerStore) LoadGlobal(dataDir string) error {
@@ -110,13 +110,13 @@ func (ts *TriggerStore) SeedRoomTriggers(roomID int, triggers []TriggerDef) {
ts.roomByPlayerFlag[roomID][t.OnPlayerFlag] = append(
ts.roomByPlayerFlag[roomID][t.OnPlayerFlag], t)
}
- if t.IsWorldFlagTrigger() {
- if ts.roomByWorldFlag[roomID] == nil {
- ts.roomByWorldFlag[roomID] = make(map[string][]*TriggerDef)
+ if t.IsGlobalFlagTrigger() {
+ if ts.roomByGlobalFlag[roomID] == nil {
+ ts.roomByGlobalFlag[roomID] = make(map[string][]*TriggerDef)
}
t.Room = roomID
- ts.roomByWorldFlag[roomID][t.OnFlag] = append(
- ts.roomByWorldFlag[roomID][t.OnFlag], t)
+ ts.roomByGlobalFlag[roomID][t.OnGlobalFlag] = append(
+ ts.roomByGlobalFlag[roomID][t.OnGlobalFlag], t)
}
}
}
@@ -128,9 +128,9 @@ func (ts *TriggerStore) addGlobal(t *TriggerDef) {
ts.globalByPlayerFlag[t.OnPlayerFlag] = append(
ts.globalByPlayerFlag[t.OnPlayerFlag], t)
}
- if t.IsWorldFlagTrigger() {
- ts.globalByWorldFlag[t.OnFlag] = append(
- ts.globalByWorldFlag[t.OnFlag], t)
+ if t.IsGlobalFlagTrigger() {
+ ts.globalByGlobalFlag[t.OnGlobalFlag] = append(
+ ts.globalByGlobalFlag[t.OnGlobalFlag], t)
}
}
@@ -184,23 +184,23 @@ func (ts *TriggerStore) FirePlayerInRoom(playerName string, roomID int, flagName
return started
}
-func (ts *TriggerStore) FireWorld(flagName string, flagValue any) []*WorldTriggerSeq {
+func (ts *TriggerStore) FireGlobal(flagName string, flagValue any) []*GlobalTriggerSeq {
ts.mu.Lock()
defer ts.mu.Unlock()
- var started []*WorldTriggerSeq
+ var started []*GlobalTriggerSeq
- for _, t := range ts.globalByWorldFlag[flagName] {
+ for _, t := range ts.globalByGlobalFlag[flagName] {
if !ts.valueMatches(t.Value, flagValue) {
continue
}
- seq := ts.startWorldSeqLocked(t, flagValue)
+ seq := ts.startGlobalSeqLocked(t, flagValue)
if seq != nil {
started = append(started, seq)
}
}
- for roomID, roomFlags := range ts.roomByWorldFlag {
+ for roomID, roomFlags := range ts.roomByGlobalFlag {
for _, t := range roomFlags[flagName] {
if !ts.valueMatches(t.Value, flagValue) {
continue
@@ -208,7 +208,7 @@ func (ts *TriggerStore) FireWorld(flagName string, flagValue any) []*WorldTrigge
if t.Room == 0 {
t.Room = roomID
}
- seq := ts.startWorldSeqLocked(t, flagValue)
+ seq := ts.startGlobalSeqLocked(t, flagValue)
if seq != nil {
started = append(started, seq)
}
@@ -238,12 +238,12 @@ func (ts *TriggerStore) startPlayerSeqLocked(playerName string, t *TriggerDef, r
return seq
}
-func (ts *TriggerStore) startWorldSeqLocked(t *TriggerDef, flagValue any) *WorldTriggerSeq {
+func (ts *TriggerStore) startGlobalSeqLocked(t *TriggerDef, flagValue any) *GlobalTriggerSeq {
key := t.ID
- if _, exists := ts.worldSeqs[key]; exists {
+ if _, exists := ts.globalSeqs[key]; exists {
return nil
}
- seq := &WorldTriggerSeq{
+ seq := &GlobalTriggerSeq{
TriggerID: t.ID,
RoomID: t.Room,
Steps: make([]TriggerStep, len(t.Steps)),
@@ -253,7 +253,7 @@ func (ts *TriggerStore) startWorldSeqLocked(t *TriggerDef, flagValue any) *World
if len(seq.Steps) > 0 {
seq.Wait = seq.Steps[0].Delay
}
- ts.worldSeqs[key] = seq
+ ts.globalSeqs[key] = seq
return seq
}
@@ -274,11 +274,11 @@ func (ts *TriggerStore) SnapshotPlayerSeqs() []*PlayerTriggerSeq {
return out
}
-func (ts *TriggerStore) SnapshotWorldSeqs() []*WorldTriggerSeq {
+func (ts *TriggerStore) SnapshotGlobalSeqs() []*GlobalTriggerSeq {
ts.mu.Lock()
defer ts.mu.Unlock()
- out := make([]*WorldTriggerSeq, 0, len(ts.worldSeqs))
- for _, s := range ts.worldSeqs {
+ out := make([]*GlobalTriggerSeq, 0, len(ts.globalSeqs))
+ for _, s := range ts.globalSeqs {
out = append(out, s)
}
return out
@@ -291,10 +291,10 @@ func (ts *TriggerStore) RemovePlayerSeq(playerName, triggerID string) {
delete(ts.playerSeqs, key)
}
-func (ts *TriggerStore) RemoveWorldSeq(triggerID string) {
+func (ts *TriggerStore) RemoveGlobalSeq(triggerID string) {
ts.mu.Lock()
defer ts.mu.Unlock()
- delete(ts.worldSeqs, triggerID)
+ delete(ts.globalSeqs, triggerID)
}
func (ts *TriggerStore) AllGlobalTriggers() []*TriggerDef {
@@ -304,7 +304,7 @@ func (ts *TriggerStore) AllGlobalTriggers() []*TriggerDef {
for _, triggers := range ts.globalByPlayerFlag {
out = append(out, triggers...)
}
- for _, triggers := range ts.globalByWorldFlag {
+ for _, triggers := range ts.globalByGlobalFlag {
out = append(out, triggers...)
}
return out
@@ -340,7 +340,7 @@ func (ts *TriggerStore) AllRoomTriggers() map[int][]*TriggerDef {
out[roomID] = append(out[roomID], triggers...)
}
}
- for roomID, byFlag := range ts.roomByWorldFlag {
+ for roomID, byFlag := range ts.roomByGlobalFlag {
for _, triggers := range byFlag {
out[roomID] = append(out[roomID], triggers...)
}