From 389e307f205f9b7edf604fb9f86e1038ead736ef Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 16 Jun 2026 23:37:50 -0400 Subject: feat: major xterm256 color overhaul, see worldbuilding docs. --- WORLDBUILDING.md | 975 ------------------------------------------------------- 1 file changed, 975 deletions(-) delete mode 100644 WORLDBUILDING.md (limited to 'WORLDBUILDING.md') diff --git a/WORLDBUILDING.md b/WORLDBUILDING.md deleted file mode 100644 index 93c40aa..0000000 --- a/WORLDBUILDING.md +++ /dev/null @@ -1,975 +0,0 @@ -# World Building Guide - -Third Collapse is data-driven. Everything — rooms, items, mobs, objects, behaviors — is defined in YAML files under `data/`. No code changes needed to build a world. - -## Quick Reference - -| What you want | Where to put it | -|---|---| -| A room | `data/rooms/.yaml` | -| An item (sword, ore, key) | `data/items/.yaml` | -| A mob (NPC, monster) | `data/mobs/.yaml` | -| An interactive object (rock, lever, door) | `data/objects/.yaml` | -| A behavior (mining, dialog, toggle) | `data/behaviors/.yaml` | -| A shared drop table | `data/drops/.yaml` | - -## 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. - ---- - -## 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 -``` - ---- - -## 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 -``` - ---- - -## 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). - ---- - -## 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." -``` - ---- - -## 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 -``` - ---- - -## 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 -``` - ---- - -## 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 -``` - ---- - -## 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). - ---- - -## 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 -``` - ---- - -## 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. - ---- - -## 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. - ---- - -## 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 | - ---- - -## The `search` Command - -Used to open searchable items in your inventory (bird's nests, etc.): -``` -Usage: search - -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. - ---- - -## 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." -``` - ---- - -## 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. -- cgit v1.2.3