aboutsummaryrefslogtreecommitdiff
path: root/worldbuilding_guide
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-16 04:17:43 -0400
committerhistoria <[not public]>2026-06-16 04:17:43 -0400
commit085e728d22a3369bd110b77409914cfbe9ebe611 (patch)
treeeeb88cb1ceb91cc9ea2b5a1877c3c66328fab503 /worldbuilding_guide
parent43f21aabae8924f35c12c8485e690aeefe0089fb (diff)
downloadthehouseoficarus-085e728d22a3369bd110b77409914cfbe9ebe611.tar.gz
feat: recipe system, combining items, cooking skill
Diffstat (limited to 'worldbuilding_guide')
-rw-r--r--worldbuilding_guide/README.md25
-rw-r--r--worldbuilding_guide/behaviors.md264
-rw-r--r--worldbuilding_guide/conditions.md66
-rw-r--r--worldbuilding_guide/doors.md102
-rw-r--r--worldbuilding_guide/drops.md52
-rw-r--r--worldbuilding_guide/hidden_objects.md21
-rw-r--r--worldbuilding_guide/index.md10
-rw-r--r--worldbuilding_guide/items.md68
-rw-r--r--worldbuilding_guide/mobs.md54
-rw-r--r--worldbuilding_guide/objects.md37
-rw-r--r--worldbuilding_guide/quests.md107
-rw-r--r--worldbuilding_guide/recipes.md112
-rw-r--r--worldbuilding_guide/rooms.md122
-rw-r--r--worldbuilding_guide/search.md15
-rw-r--r--worldbuilding_guide/state.md8
-rw-r--r--worldbuilding_guide/tips.md21
-rw-r--r--worldbuilding_guide/toggles.md18
-rw-r--r--worldbuilding_guide/wandering_mobs.md20
-rw-r--r--worldbuilding_guide/wandering_objects.md23
19 files changed, 1145 insertions, 0 deletions
diff --git a/worldbuilding_guide/README.md b/worldbuilding_guide/README.md
new file mode 100644
index 0000000..8555e49
--- /dev/null
+++ b/worldbuilding_guide/README.md
@@ -0,0 +1,25 @@
+# World Building Guide
+
+Third Collapse is data-driven. Everything — rooms, items, mobs, objects, behaviors, drop tables, and recipes — is defined in YAML files under `data/`. No code changes needed to build a world.
+
+## Files
+
+| Document | Covers |
+|----------|--------|
+| [Rooms](rooms.md) | Room definitions, exits (simple and conditional), spawns, placed objects and mobs, on_enter scripts |
+| [Items](items.md) | Item definitions, stats, equip slots, weapon types, tool types, firemaking, food/healing |
+| [Mobs](mobs.md) | Mob definitions, combat stats, drops, behavior, idle descriptions |
+| [Objects](objects.md) | Interactive objects, behaviors, hidden objects, in-room descriptions |
+| [Behaviors](behaviors.md) | Gather, use, talk, toggle behaviors with full YAML reference |
+| [Recipes](recipes.md) | Recipe system for cooking, crafting, smithing — station and item-on-item |
+| [Conditions](conditions.md) | All condition types: flag, player_flag, has_item, all_of, any_of, not |
+| [Drop Tables](drops.md) | Shared weighted drop tables for mob loot and search tables |
+| [State](state.md) | World flags vs player flags — global vs per-character state |
+| [Doors](doors.md) | Door examples with world flags and player flags |
+| [Quests](quests.md) | Complete multi-room quest example with talk behaviors |
+| [Wandering Objects](wandering_objects.md) | Objects that teleport between rooms on a timer |
+| [Wandering Mobs](wandering_mobs.md) | Mobs that wander through legal exits |
+| [Toggles](toggles.md) | Player toggle options (tiny_map, xp_drops, automap, etc.) |
+| [Search](search.md) | The search command and searchable items |
+| [Hidden Objects](hidden_objects.md) | Hidden interactive objects |
+| [Tips](tips.md) | Practical tips for building a world |
diff --git a/worldbuilding_guide/behaviors.md b/worldbuilding_guide/behaviors.md
new file mode 100644
index 0000000..a275c60
--- /dev/null
+++ b/worldbuilding_guide/behaviors.md
@@ -0,0 +1,264 @@
+## Behaviors
+
+### Gather (mining, fishing, woodcutting)
+
+Mining — per-drop depletion:
+```yaml
+id: mine_copper
+type: gather
+skill: mining
+level: 1
+xp: 17 # XP awarded per successful gather
+base_wait: 8 # ticks between attempts
+tool: pickaxe # requires item with tool_type: pickaxe
+success:
+ base: 0.40 # 40% base chance
+ per_level: 0.01 # +1% per level above requirement
+ cap: 0.95 # 95% max
+gather_message: "You swing your pickaxe at the rock..."
+fail_message: "You chip away but get nothing useful."
+drops:
+ - item_id: copper_ore
+ weight: 90 # 90% chance when roll succeeds
+ depletes: true # rock becomes depleted after this drop
+ message: "You manage to mine some copper ore."
+ - table: gem_table # reference a shared drop table
+ weight: 10
+ depletes: false # gem drops don't deplete the rock
+ message: "You spot a glint of something valuable!"
+respawn_timer: 50 # ticks until rock respawns
+respawn_message: "You see more ore in the rock."
+respawn_broadcast: "A glint of copper catches your eye from some {name}."
+```
+
+Non-depleting gather (fishing):
+```yaml
+id: fish_trout
+type: gather
+skill: fishing
+level: 1
+xp: 10
+base_wait: 4
+tool: fishing_rod
+success:
+ base: 0.30
+ per_level: 0.01
+ cap: 0.90
+gather_message: "You cast your line into the water..."
+fail_message: "Nothing seems to bite."
+drops:
+ - item_id: raw_trout
+ weight: 100
+ depletes: false # never depletes
+ message: "You catch a trout!"
+```
+
+Woodcutting with shared depletion and bird's nests:
+```yaml
+id: chop_oak
+type: gather
+skill: woodcutting
+level: 15
+xp: 37
+base_wait: 6
+tool: axe
+success:
+ base: 0.40
+ per_level: 0.01
+ cap: 0.90
+gather_message: "You swing your axe at the oak tree..."
+fail_message: "You swing but get no logs."
+drops:
+ - item_id: oak_logs
+ weight: 100
+ depletes: false # depletion is timer-based (deplete_timer)
+ message: "You get some oak logs."
+respawn_timer: 14 # ticks until tree respawns after being cut down
+deplete_timer: 45 # max ticks before next gather depletes (counts down while chopping)
+nest_chance: 256 # 1/256 chance for a bird's nest on each successful gather
+respawn_message: "A new oak sapling grows in its place."
+respawn_broadcast: "An {name} grows back."
+```
+
+Regular tree — always depletes on first gather, no shared timer, no nests:
+```yaml
+id: chop_tree
+type: gather
+skill: woodcutting
+level: 1
+xp: 25
+base_wait: 4
+tool: axe
+success:
+ base: 0.50
+ per_level: 0.01
+ cap: 0.95
+gather_message: "You swing your axe at the tree..."
+fail_message: "You swing but get no logs."
+drops:
+ - item_id: logs
+ weight: 100
+ depletes: true # regular tree depletes on first successful gather
+ message: "You get some logs."
+respawn_timer: 80
+respawn_message: "A new tree grows in its place."
+respawn_broadcast: "A {name} grows back."
+```
+
+#### Shared depletion explained
+
+When `deplete_timer > 0`, the tree has a shared despawn timer:
+- The timer starts at `deplete_timer` max when the first player begins chopping.
+- Each tick, if anyone is chopping, the timer counts down.
+- When the timer reaches 0, the NEXT successful gather depletes the tree.
+- If no one is chopping and the tree isn't depleted, the timer ticks back UP.
+- All players chopping the same tree are interrupted when it depletes.
+
+Use `deplete_timer` for trees. Use `depletes: true` on individual drops for rocks.
+
+#### Bird's nests
+
+When `nest_chance > 0`, each successful gather has a 1/N independent chance to also drop
+a bird's nest. The nest goes to inventory (or to the ground if inventory is full).
+Use the `search` command to open nests — they roll on the `birds_nest_drop` table.
+
+#### XP drops
+
+When `xp > 0`, the gather awards XP on each successful drop. If the player's `xpdrops`
+toggle is on, the output includes the XP gain: `(+37xp wct)`.
+
+### Talk (dialog trees)
+
+Full conversation with conditions, actions, and player flag tracking:
+```yaml
+id: guard_talk
+type: talk
+nodes:
+ start:
+ message: "\"Halt! This area is restricted.\""
+ options:
+ - text: "\"What's behind that gate?\""
+ goto: about_gate
+ - text: "\"I have copper ore.\"" # only shows if player has ore
+ goto: trade_ore
+ condition:
+ has_item: copper_ore
+ - text: "\"I have a pass.\"" # only shows if player earned a pass
+ goto: has_pass
+ condition:
+ player_flag: got_pass
+ value: true
+ - text: "\"Goodbye.\""
+ end: true
+
+ about_gate:
+ message: "\"Bring me some copper ore and I'll stamp you a pass.\""
+ action:
+ set_player_flags: # player-local: only this player
+ talked_to_guard: true
+ options:
+ - text: "\"I'll be back.\""
+ end: true
+ - text: "\"I have some right here.\""
+ goto: trade_ore
+ condition:
+ has_item: copper_ore
+
+ trade_ore:
+ message: "\"Good quality ore.\" He stamps a pass and hands it to you."
+ action:
+ take_item: copper_ore # removes 1 copper ore
+ give_item: pass_stub # gives pass stub
+ set_player_flags:
+ got_pass: true # player now "has a pass"
+ options:
+ - text: "\"Thanks.\""
+ end: true
+
+ has_pass:
+ message: "\"Alright, I'll open the gate for you.\""
+ action:
+ set_flags: # WORLD flag: gate opens for everyone
+ gate_open: true
+ options:
+ - text: "\"Thanks.\""
+ end: true
+```
+
+#### Node action reference
+
+| Field | Effect |
+|---|---|
+| `set_flags` | Sets world 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 |
+| `teleport` | Moves the player to a room ID |
+| `heal` | Restores that many hitpoints |
+
+All fields in a single action are processed together — you can give an item, take an item, set flags, and heal all in one node.
+
+Example — quest completion:
+```yaml
+action:
+ take_item: dragon_head
+ give_item: dragon_slayer_medal
+ set_player_flags:
+ dragon_quest: complete
+ dragon_slain: true
+ heal: 99
+ teleport: 1 # return to town
+```
+
+### Toggle (levers, switches, gates)
+
+Simple toggle that sets a world flag:
+```yaml
+id: iron_gate_toggle
+type: toggle
+message: "You push the heavy iron gate open."
+set_flags:
+ gate_open: true
+check: # only works when gate is closed
+ flag: gate_open
+ value: true
+ not: true
+```
+
+Lever that toggles between two states:
+```yaml
+id: bridge_lever
+type: toggle
+message: "You pull the lever. Mechanisms groan somewhere in the distance."
+set_flags:
+ bridge_extended: true
+check:
+ flag: bridge_extended
+ value: true
+ not: true
+```
+
+### Use (crafting stations)
+
+```yaml
+id: smelt_copper
+type: use
+message: "You place the ore in the furnace..."
+wait: 4 # ticks between crafts
+consume: # items consumed per craft
+ copper_ore: 1
+reward: # item produced
+ item_id: copper_bar
+ quantity: 1
+fail_message: "The ore crumbles to dust."
+success:
+ base: 0.60
+ per_level: 0.01
+ cap: 0.95
+skill: smithing
+level: 1
+xp: 15 # XP awarded per successful craft
+```
+
+---
+
diff --git a/worldbuilding_guide/conditions.md b/worldbuilding_guide/conditions.md
new file mode 100644
index 0000000..ce9f74c
--- /dev/null
+++ b/worldbuilding_guide/conditions.md
@@ -0,0 +1,66 @@
+## Conditions Reference
+
+Conditions are used in talk options, exit gates, on-enter scripts, and toggle checks.
+
+### Simple conditions
+
+```yaml
+# Check a world flag
+condition:
+ flag: gate_open
+ value: true
+
+# Check a world flag is NOT set
+condition:
+ flag: gate_open
+ not: true
+
+# Check a player flag
+condition:
+ player_flag: finished_tutorial
+ value: true
+
+# Check if player has an item
+condition:
+ has_item: bronze_key
+
+# Check if player does NOT have an item
+condition:
+ has_item: bronze_key
+ not: true
+```
+
+### Compound conditions
+
+All must pass:
+```yaml
+condition:
+ all_of:
+ - flag: gate_open
+ value: true
+ - has_item: pass_stub
+```
+
+Any one must pass:
+```yaml
+condition:
+ any_of:
+ - has_item: bronze_key
+ - has_item: iron_key
+ - player_flag: master_of_unlocking
+ value: true
+```
+
+Nested compounds:
+```yaml
+condition:
+ all_of:
+ - player_flag: quest_started
+ value: true
+ - any_of:
+ - has_item: wolf_pelt
+ - has_item: bear_pelt
+```
+
+---
+
diff --git a/worldbuilding_guide/doors.md b/worldbuilding_guide/doors.md
new file mode 100644
index 0000000..afe4556
--- /dev/null
+++ b/worldbuilding_guide/doors.md
@@ -0,0 +1,102 @@
+## Global vs Player State: Door Examples
+
+### Example A: Door with a button in another room (GLOBAL)
+
+A button in room 3 opens a door in room 7. Anyone can press it. Once pressed, the door is open for everyone.
+
+**Button object** (`data/objects/door_button.yaml`):
+```yaml
+id: door_button
+name: stone button
+behavior: button_toggle
+hidden: true
+```
+
+**Button behavior** (`data/behaviors/button_toggle.yaml`):
+```yaml
+id: button_toggle
+type: toggle
+message: "You press the stone button. You hear grinding stone in the distance."
+set_flags:
+ secret_door_open: true # WORLD flag
+check:
+ flag: secret_door_open
+ not: true # only works when door is closed
+```
+
+**Room 3** — contains the button:
+```yaml
+id: 3
+name: "Button Chamber"
+description: "A small stone chamber. A button protrudes from the east wall."
+exits:
+ south: 1
+objects:
+ - id: door_button
+```
+
+**Room 7** — contains the door:
+```yaml
+id: 7
+name: "Hidden Passage"
+description: "A dusty corridor. A heavy stone door blocks the way north."
+exits:
+ south: 2
+ north:
+ room: 8
+ condition:
+ flag: secret_door_open # checks WORLD flag
+ value: true
+ blocked_message: "A heavy stone door blocks the way."
+objects:
+ - id: stone_door
+ hidden: true
+```
+
+### Example B: Key-locked door (PLAYER-LOCAL)
+
+A locked door that only opens for a player carrying the key. Each player must find their own key.
+
+**Key item** (`data/items/rusty_key.yaml`):
+```yaml
+id: rusty_key
+name: rusty key
+description: "An old iron key, still functional."
+value: 0
+stackable: false
+```
+
+**Room 5** — locked door:
+```yaml
+id: 5
+name: "Locked Storage"
+description: "A small storage room. The way east is blocked by a locked iron door."
+exits:
+ west: 2
+ east:
+ room: 6
+ condition:
+ has_item: rusty_key # checks PLAYER inventory
+ blocked_message: "The iron door is locked. You need a key."
+objects:
+ - id: iron_door
+ hidden: true
+```
+
+**Room 6** — the other side (no key needed to exit):
+```yaml
+id: 6
+name: "Storage Closet"
+description: "Shelves of dusty crates."
+exits:
+ west: 5 # exit back — no condition
+spawns:
+ - item_id: uncut_ruby
+ quantity: 1
+ 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).
+
+---
+
diff --git a/worldbuilding_guide/drops.md b/worldbuilding_guide/drops.md
new file mode 100644
index 0000000..e5a17ff
--- /dev/null
+++ b/worldbuilding_guide/drops.md
@@ -0,0 +1,52 @@
+## Drop Tables
+
+Shared drop tables can be referenced by multiple behaviors:
+```yaml
+# data/drops/gem_table.yaml
+id: gem_table
+drops:
+ - item_id: uncut_sapphire
+ weight: 47
+ - item_id: uncut_emerald
+ weight: 16
+ - item_id: uncut_ruby
+ weight: 4
+ - item_id: uncut_diamond
+ weight: 1
+```
+
+Bird's nest drop table:
+```yaml
+# data/drops/birds_nest_drop.yaml
+id: birds_nest_drop
+drops:
+ - item_id: credits
+ weight: 50
+ quantity: 200
+ - item_id: credits
+ weight: 30
+ quantity: 500
+ - item_id: credits
+ weight: 15
+ quantity: 1000
+ - item_id: credits
+ weight: 4
+ quantity: 3000
+ - item_id: credits
+ weight: 1
+ quantity: 10000
+```
+
+Referenced from a gather behavior:
+```yaml
+drops:
+ - item_id: copper_ore
+ weight: 90
+ depletes: true
+ - table: gem_table # pulls from data/drops/gem_table.yaml
+ weight: 10
+ depletes: false
+```
+
+---
+
diff --git a/worldbuilding_guide/hidden_objects.md b/worldbuilding_guide/hidden_objects.md
new file mode 100644
index 0000000..3cb8a1e
--- /dev/null
+++ b/worldbuilding_guide/hidden_objects.md
@@ -0,0 +1,21 @@
+## Hidden Objects
+
+Objects with `hidden: true` don't appear in the room's object listing. Players discover them by reading room descriptions or trying commands. The object is still fully interactable — `push gate`, `look gate`, etc.
+
+```yaml
+# data/objects/secret_lever.yaml
+id: secret_lever
+name: stone lever
+behavior: secret_toggle
+hidden: true
+props:
+ description: "A cleverly concealed lever behind a loose stone."
+```
+
+Room description hints at it:
+```yaml
+description: "A dusty corridor. One of the wall stones looks slightly out of place."
+```
+
+---
+
diff --git a/worldbuilding_guide/index.md b/worldbuilding_guide/index.md
new file mode 100644
index 0000000..e19c207
--- /dev/null
+++ b/worldbuilding_guide/index.md
@@ -0,0 +1,10 @@
+## Quick Reference
+
+| What you want | Where to put it |
+|---|---|
+| A room | `data/rooms/<id>.yaml` |
+| An item (sword, ore, key) | `data/items/<id>.yaml` |
+| A mob (NPC, monster) | `data/mobs/<id>.yaml` |
+| An interactive object (rock, lever, door) | `data/objects/<id>.yaml` |
+| A behavior (mining, dialog, toggle) | `data/behaviors/<id>.yaml` |
+| A shared drop table | `data/drops/<id>.yaml` |
diff --git a/worldbuilding_guide/items.md b/worldbuilding_guide/items.md
new file mode 100644
index 0000000..73f497b
--- /dev/null
+++ b/worldbuilding_guide/items.md
@@ -0,0 +1,68 @@
+## Items
+
+```yaml
+id: bronze_pickaxe
+name: bronze pickaxe
+aliases: ["pick", "pickaxe"]
+description: "A sturdy bronze pickaxe."
+value: 10
+stackable: false
+equip_slot: main_hand # optional — where it equips
+weapon_type: melee # optional — melee or ranged
+stats: # optional — combat bonuses
+ attack_bonus: 2
+ strength_bonus: 1
+speed: 5 # ticks between attacks
+tool_type: pickaxe # used by gather behaviors that require "tool: pickaxe"
+tool_speed: 2 # reduces gather wait time
+```
+
+Key item (quest token, not equippable):
+```yaml
+id: pass_stub
+name: pass stub
+description: "A crumpled slip of paper stamped with the guard's seal."
+value: 0
+stackable: false
+```
+
+---
+
+## Firemaking
+
+```yaml
+id: logs
+name: logs
+fire_level: 1 # firemaking level required
+fire_xp: 40 # XP for burning/stoking
+burn_ticks: 24 # how long the fire burns
+```
+
+## Food / Healing
+
+```yaml
+id: bread
+name: bread
+color: "fg=bright_yellow"
+description: "A fresh loaf of bread, still warm from the oven."
+value: 5
+heal_value: 5 # positive = heal, negative = damage
+eat_message: "You eat the bread. Warm and satisfying."
+```
+
+## MadeFrom — Item Combinations
+
+Define what items combine to make this item. Used by the `use` command.
+
+```yaml
+id: bread_dough
+name: bread dough
+color: "fg=bright_yellow"
+made_from:
+ - items: [pot_of_flour]
+ qty: 1
+ - items: [bucket_of_water, pitcher_of_water]
+ qty: 1
+```
+
+Each entry is an ingredient slot. Multiple `items` means any of them works. The first matching item found in inventory is consumed. See also `recipes.md` for station-based recipes.
diff --git a/worldbuilding_guide/mobs.md b/worldbuilding_guide/mobs.md
new file mode 100644
index 0000000..a025e86
--- /dev/null
+++ b/worldbuilding_guide/mobs.md
@@ -0,0 +1,54 @@
+## Mobs
+
+Basic combat mob:
+```yaml
+id: "man"
+name: "man"
+description: "A shabby-looking man."
+attack: 1
+strength: 1
+defense: 1
+hp: 7
+speed: 5
+aggressive: false
+respawn_ticks: 30
+drops:
+ remains: "bones" # always dropped on death
+ loot:
+ - item_id: "credits"
+ weight: 98
+ quantity: 10
+ - item_id: "credits"
+ weight: 2
+ quantity: 150
+idle_descriptions:
+ - "scribbles something in a small notebook"
+ - "gazes skyward at the clouds"
+combat_descriptions:
+ - "is engaged in a fight to the death with %s"
+```
+
+Mob with a behavior — can be talked to, toggled, etc:
+```yaml
+id: "guard"
+name: "Guard"
+behavior: guard_talk # links to data/behaviors/guard_talk.yaml
+unique: true # displays as "Guard" not "a guard"
+protected: true # cannot be attacked
+attack: 5
+strength: 5
+defense: 5
+hp: 30
+speed: 5
+aggressive: false
+respawn_ticks: 60
+idle_descriptions:
+ - "scans the area with a watchful eye"
+ - "adjusts the grip on his weapon"
+```
+
+Note: `wander_rooms` and `wander_interval` are NOT set on the mob definition. Wander config
+is per-instance in the room YAML (see Rooms > Mobs section above).
+
+---
+
diff --git a/worldbuilding_guide/objects.md b/worldbuilding_guide/objects.md
new file mode 100644
index 0000000..94aead7
--- /dev/null
+++ b/worldbuilding_guide/objects.md
@@ -0,0 +1,37 @@
+## Objects
+
+Object defined — links to a behavior:
+```yaml
+id: copper_rock
+name: copper rock
+behavior: mine_copper
+```
+
+Tree object:
+```yaml
+id: oak_tree
+name: oak tree
+behavior: chop_oak
+```
+
+Hidden object — doesn't appear in room's object list, only discoverable via description or experimentation:
+```yaml
+id: iron_gate
+name: iron gate
+behavior: iron_gate_toggle
+hidden: true
+props:
+ description: "A heavy iron gate set into the north wall."
+```
+
+Decorative object (no behavior):
+```yaml
+id: lumby_fountain
+name: town fountain
+behavior: ""
+props:
+ description: "Clear water sparkles in the sunlight."
+```
+
+---
+
diff --git a/worldbuilding_guide/quests.md b/worldbuilding_guide/quests.md
new file mode 100644
index 0000000..443aa6b
--- /dev/null
+++ b/worldbuilding_guide/quests.md
@@ -0,0 +1,107 @@
+## Complete Quest Example: "Clear the Rats"
+
+### 1. Quest giver mob (`data/mobs/quest_giver.yaml`)
+```yaml
+id: "quest_giver"
+name: "Elder"
+behavior: rat_quest_talk
+unique: true
+protected: true
+attack: 1
+strength: 1
+defense: 1
+hp: 20
+speed: 5
+aggressive: false
+idle_descriptions:
+ - "mutters about the rat infestation"
+```
+
+### 2. Dialog behavior (`data/behaviors/rat_quest_talk.yaml`)
+```yaml
+id: rat_quest_talk
+type: talk
+nodes:
+ start:
+ message: "\"Rats! Rats everywhere in the cellar. Clear them out and I'll reward you.\""
+ options:
+ - text: "\"I'll handle it.\""
+ goto: accept_quest
+ condition:
+ player_flag: rat_quest
+ not: true
+ - text: "\"I killed the rats.\""
+ goto: turn_in
+ condition:
+ player_flag: rat_quest
+ value: started
+ - text: "\"Goodbye.\""
+ end: true
+
+ accept_quest:
+ message: "\"Good lad. The cellar is west of here. Come back when they're dead.\""
+ action:
+ set_player_flags:
+ rat_quest: started
+ options:
+ - text: "\"On my way.\""
+ end: true
+
+ turn_in:
+ message: "\"You did it! The village owes you a debt. Here — take this.\""
+ action:
+ give_item: rusty_sword
+ set_player_flags:
+ rat_quest: complete
+ heal: 10
+ options:
+ - text: "\"Thanks!\""
+ end: true
+```
+
+### 3. Rat mobs (`data/mobs/rat.yaml`)
+```yaml
+id: "rat"
+name: "giant rat"
+attack: 2
+strength: 1
+defense: 1
+hp: 3
+speed: 4
+aggressive: true
+respawn_ticks: 60
+drops:
+ remains: "rat bones"
+```
+
+### 4. Cellar room (`data/rooms/20.yaml`)
+```yaml
+id: 20
+name: "Cellar"
+description: "A damp, dark cellar. The floor scuttles with movement."
+exits:
+ east: 1
+mobs:
+ - "rat"
+ - "rat"
+ - "rat"
+ - "rat"
+ - "rat"
+```
+
+### 5. Room 1 with quest giver (`data/rooms/1.yaml`)
+```yaml
+id: 1
+name: "Town Square"
+description: "Cobblestone paths lead in all directions. The Elder stands near the fountain."
+exits:
+ west: 20
+ north: 2
+mobs:
+ - "quest_giver"
+objects:
+ - id: lumby_fountain
+```
+
+---
+
diff --git a/worldbuilding_guide/recipes.md b/worldbuilding_guide/recipes.md
new file mode 100644
index 0000000..f40a9a7
--- /dev/null
+++ b/worldbuilding_guide/recipes.md
@@ -0,0 +1,112 @@
+# Third Collapse - World Building Guide
+
+## Recipes
+
+Recipes define how items are processed on stations to produce new items. They are the foundation for cooking, smithing, crafting, and fletching.
+
+Recipe files live in `data/recipes/<id>.yaml`.
+
+### Recipe vs MadeFrom
+
+- **Recipes** (`data/recipes/`) use a station (fire, range, furnace, anvil, etc.). The player must be near the station object.
+- **MadeFrom** (on the item YAML directly) is for item-on-item combinations that need no station. The player combines ingredients from their inventory.
+
+### Recipe YAML Reference
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Unique recipe identifier |
+| `type` | string | Recipe type (cooking, smithing, crafting, etc.) |
+| `skill` | string | Skill checked for success |
+| `level` | int | Required skill level |
+| `xp` | int | XP awarded on success |
+| `wait` | float64 | Ticks per craft cycle (default 6) |
+| `station` | []string | Station object IDs required |
+| `consume` | []ConsumeEntry | Ingredients consumed (see below) |
+| `output` | string | ItemID produced on success |
+| `fail` | string | ItemID produced on failure (optional) |
+| `message` | string | Success message |
+| `fail_message` | string | Failure message |
+| `success` | SuccessFormula | Optional skill check formula (see Behaviors) |
+
+The recipe's name in menus comes from the output item's `name` field, colored using the output item's `color`.
+
+### ConsumeEntry — Multi-Item Ingredient Slots
+
+Each consume entry defines an ingredient slot with multiple valid items. The first matching item found in the player's inventory is consumed.
+
+```yaml
+consume:
+ - items: [item_id, alternative_id, ...]
+ qty: 1
+```
+
+### Station-based recipe (cooking on a fire)
+
+```yaml
+id: cook_trout
+type: cooking
+skill: cooking
+level: 15
+xp: 70
+wait: 6
+station: [fire, cooking_range]
+consume:
+ - items: [raw_trout]
+ qty: 1
+output: trout
+fail: burnt_fish
+message: "Cooked to perfection. It looks great!"
+fail_message: "You accidentally burn the trout."
+```
+
+### Station-specific recipe (range only, not fire)
+
+```yaml
+id: cook_bread
+type: cooking
+skill: cooking
+level: 1
+xp: 40
+wait: 6
+station: [cooking_range]
+consume:
+ - items: [bread_dough]
+ qty: 1
+output: bread
+fail: burnt_meat
+message: "You bake the dough into a fresh loaf of bread."
+fail_message: "You burn the bread to a crisp."
+```
+
+### MadeFrom — Item-on-item combinations
+
+Item-on-item combinations are defined directly on the output item via the `made_from` field. No recipe file needed.
+
+```yaml
+# data/items/bread_dough.yaml
+id: bread_dough
+name: bread dough
+color: "fg=bright_yellow"
+made_from:
+ - items: [pot_of_flour]
+ qty: 1
+ - items: [bucket_of_water, pitcher_of_water, vial_of_water]
+ qty: 1
+```
+
+The player uses `use flour on water` to combine them. The system finds that `bread_dough` can be made from these ingredients and produces it.
+
+### Food/Healing Items
+
+Items with `heal_value` and `eat_message` can be consumed via the `eat` command.
+
+```yaml
+id: bread
+name: bread
+color: "fg=bright_yellow"
+description: "A fresh loaf of bread, still warm from the oven."
+value: 5
+heal_value: 5
+eat_message: "You eat the bread. Warm and satisfying."
+```
diff --git a/worldbuilding_guide/rooms.md b/worldbuilding_guide/rooms.md
new file mode 100644
index 0000000..95e1972
--- /dev/null
+++ b/worldbuilding_guide/rooms.md
@@ -0,0 +1,122 @@
+## Rooms
+
+Minimal room:
+```yaml
+id: 1
+name: "Town Square"
+description: "Cobblestone paths lead in all directions. A fountain gurgles peacefully."
+exits:
+ north: 2
+ west: 7
+ east: 3
+```
+
+### Exits — simple vs conditional
+
+Simple exit — always passable:
+```yaml
+exits:
+ north: 2
+```
+
+Conditional exit — blocked until a world flag is set:
+```yaml
+exits:
+ north:
+ room: 11
+ condition:
+ flag: gate_open
+ value: true
+ blocked_message: "A heavy iron gate blocks the way north."
+```
+
+Conditional exit — blocked unless the PLAYER has a flag (key, permission, quest state):
+```yaml
+exits:
+ east:
+ room: 12
+ condition:
+ player_flag: has_vault_key
+ value: true
+ 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:
+```yaml
+exits:
+ north:
+ room: 20
+ condition:
+ all_of:
+ - flag: bridge_repaired
+ value: true
+ - player_flag: paid_toll
+ value: true
+ blocked_message: "The bridge is out, and the toll collector blocks the path."
+```
+
+### Spawns — ground items that respawn
+
+```yaml
+spawns:
+ - item_id: bronze_pickaxe
+ quantity: 1
+ respawn_ticks: 30 # reappears 30 ticks (18 seconds) after being picked up
+ - item_id: copper_ore
+ quantity: 3
+ respawn_ticks: 50
+```
+
+### Mobs — NPCs placed in the room
+
+Simple string (no wandering):
+```yaml
+mobs:
+ - "newbie_trainer"
+ - "man"
+```
+
+With wander config per-instance:
+```yaml
+mobs:
+ - id: man
+ wander_interval: 10 # attempts to wander every 10 ticks
+ - id: man
+ wander_interval: 15
+ wander_rooms: [1, 4, 5] # optional — only exit to these rooms
+```
+
+Mob wander config lives in the room YAML, not in the mob definition. This keeps mobs generic
+so the same `man` can wander differently depending on where it's placed. Mobs wander through
+legal (unconditioned) room exits. If no legal exits exist, the mob stays still. Mobs with
+no `wander_interval` never wander.
+
+### Objects — interactive fixtures
+
+```yaml
+objects:
+ - id: copper_rock # simple placement
+ - id: copper_rock # second instance
+ - id: fishing_spot
+ wander_rooms: [7, 8, 9] # teleports between these rooms
+ wander_interval: 12 # every 12 ticks
+ - id: iron_gate # hidden object (see below)
+```
+
+### On-enter scripts — messages when a player arrives
+
+```yaml
+on_enter:
+ - message: "The guard barks: \"State your business!\""
+ condition:
+ player_flag: talked_to_guard
+ not: true # only first visit
+
+ - message: "The guard nods. \"Back again?\""
+ condition:
+ player_flag: talked_to_guard
+ value: true # subsequent visits
+```
+
+---
+
diff --git a/worldbuilding_guide/search.md b/worldbuilding_guide/search.md
new file mode 100644
index 0000000..e6e2a14
--- /dev/null
+++ b/worldbuilding_guide/search.md
@@ -0,0 +1,15 @@
+## The `search` Command
+
+Used to open searchable items in your inventory (bird's nests, etc.):
+```
+Usage: search <item>
+
+Example: search nest
+ search birds nest
+```
+
+Searches your inventory for the named item. If found, removes it and rolls on a drop table.
+Currently only bird's nests are searchable.
+
+---
+
diff --git a/worldbuilding_guide/state.md b/worldbuilding_guide/state.md
new file mode 100644
index 0000000..2eefcc4
--- /dev/null
+++ b/worldbuilding_guide/state.md
@@ -0,0 +1,8 @@
+## 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.
+
+**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/worldbuilding_guide/tips.md b/worldbuilding_guide/tips.md
new file mode 100644
index 0000000..451b9bc
--- /dev/null
+++ b/worldbuilding_guide/tips.md
@@ -0,0 +1,21 @@
+## 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.
+
+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.
+
+3. **Hidden objects** keep room descriptions clean. Mention them in the room's description text instead of auto-listing them.
+
+4. **Drop tables** are shareable. The `gem_table` is used by copper rocks AND mob loot. Define once, reference everywhere.
+
+5. **Objects are for fixtures, mobs are for living things.** If it has HP and can die, it's a mob. If it's a rock, lever, door, or crafting station, it's an object. Both can have behaviors.
+
+6. **The `behavior` field on mobs** lets you talk to them directly — `talk guard` finds the guard mob, no duplicate object entry needed.
+
+7. **Exits accept both `int` and `map` formats.** `north: 2` is shorthand for `north: {room: 2}`. Add `condition` and `blocked_message` only when needed. Mob room entries accept both `"man"` and `{id: man, ...}`.
+
+8. **Live editing works.** Room, item, mob, object, and behavior YAML files are read from disk on each access. Change a room description or dialog and it takes effect immediately — no restart needed.
+
+9. **Shared depletion vs per-drop depletion.** Use `deplete_timer` for trees — the timer counts down while being chopped and regens when left alone. Use `depletes: true` on individual drops for rocks — they deplete on first successful gather.
+
+10. **Mob wander config goes in the room YAML**, not the mob definition. This lets the same `man` wander differently in different rooms.
diff --git a/worldbuilding_guide/toggles.md b/worldbuilding_guide/toggles.md
new file mode 100644
index 0000000..00b48ba
--- /dev/null
+++ b/worldbuilding_guide/toggles.md
@@ -0,0 +1,18 @@
+## Player Toggles
+
+Players can toggle personal settings with the `toggle` command:
+
+| Toggle | Effect |
+|---|---|
+| `description` | Show full room description when moving |
+| `tiny_map` | Mini-map display: `off`/`right`/`left` |
+| `xp_drops` | Show XP gained in gather/craft/combat messages |
+| `exits` | Show exit destinations inline in look output |
+| `mob_enter` | Notify when a mob enters the room |
+| `mob_leave` | Notify when a mob leaves the room |
+| `mob_spawn` | Notify when a mob spawns in the area |
+| `reserve` | Show full reserved item details in look |
+| `depletion` | Show depletion and despawn timers on objects |
+
+---
+
diff --git a/worldbuilding_guide/wandering_mobs.md b/worldbuilding_guide/wandering_mobs.md
new file mode 100644
index 0000000..8d49ed9
--- /dev/null
+++ b/worldbuilding_guide/wandering_mobs.md
@@ -0,0 +1,20 @@
+## Wandering Mobs
+
+Mob wandering is configured per-instance in the room YAML, not on the mob definition:
+```yaml
+# In a room:
+mobs:
+ - id: man
+ wander_interval: 10 # attempt to wander every 10 ticks
+ - id: man
+ wander_interval: 15
+ wander_rooms: [5, 6, 7] # optional — restrict which rooms via exits
+```
+
+Mobs wander through legal (unconditioned) room exits. When the wander interval expires,
+the mob picks a random exit with no conditions and moves through it. If `wander_rooms`
+is set, only exits leading to those room IDs are legal. Mobs without `wander_interval`
+never wander. Mobs stop wandering while in combat. Dead mobs respawn at their home room.
+
+---
+
diff --git a/worldbuilding_guide/wandering_objects.md b/worldbuilding_guide/wandering_objects.md
new file mode 100644
index 0000000..9a47ed2
--- /dev/null
+++ b/worldbuilding_guide/wandering_objects.md
@@ -0,0 +1,23 @@
+## Wandering Objects
+
+Fishing spots that move between rooms:
+```yaml
+# data/objects/fishing_spot.yaml
+id: fishing_spot
+name: fishing spot
+behavior: fish_trout
+```
+
+```yaml
+# In a room:
+objects:
+ - id: fishing_spot
+ wander_rooms: [7, 8, 9]
+ wander_interval: 12
+```
+
+Objects teleport between rooms in their `wander_rooms` list. Players gathering from a
+wandering object are silently interrupted when it moves.
+
+---
+