# Third Collapse A low-tech sci-fi MUD set on a terraformed asteroid. Runescape-style combat and skill progression, written in Go. ## Theme Not generic fantasy. Takes place on a terraformed asteroid. Technology has regressed — people scrap and scavenge remnants of the old world. "Science" replaces Prayer, "Technology" replaces Magic. Scavenging replaces Runecrafting. ## Skills (23 total, all go from 1-99) ``` Combat: Attack, Strength, Defense, Hitpoints, Ranged, Science, Technology, Assassin Gathering: Fishing, Woodcutting, Mining, Hacking, Scavenging, Farming, Thieving Production: Cooking, Smithing, Crafting, Fletching, Alchemy, Construction, Firemaking Utility: Agility ``` ## Architecture ``` cmd/mud/ Entry point (--config flag, multi-listener) internal/ config/ YAML config loading (telnet, http, https listeners) action/ Behavior system: gather/talk/use/toggle types, drop tables engine/ Tick scheduler (600ms) world/ Rooms, exits, ground items, mobs, object instance state player/ Character sheet, skills, XP, inventory, equipment, validation combat/ Combat formulas, combat state tracking object/ ItemDef, ObjectDef, item/object stores net/ Telnet + HTTPS/WebSocket server, sessions, Conn interface, echo control game/ Session handler, login flow, command dispatch, action tick, input sanitization data/ behaviors/ Behavior YAML files (gather, talk, use, toggle) drops/ Shared drop table YAML files rooms/ Room YAML files objects/ Object definition YAML files items/ Item definition YAML files mobs/ Mob definition YAML files help/ Help topic YAML files players/ accounts/ Account YAML files (gitignored) characters/ Character YAML files (gitignored) ``` ## Behavior System Objects and mobs in rooms can have behaviors. Four types exist: | Type | Verbs | Use | | -------- | --------------------- | -------------------------------------------------------------------------- | | `gather` | mine, chop, cut, fish | Resource gathering with skill checks, tool requirements, depletion/respawn | | `use` | use | Crafting stations — consume items, produce output, optional skill checks | | `talk` | talk, speak, ask | NPC conversation trees with choices, conditions, actions | | `toggle` | pull, push | Levers, switches, gates — set world flags, conditional on existing values | Behaviors are defined in `data/behaviors/.yaml`. Objects reference them with `behavior: `. Mobs can also carry `behavior: ` — StartAction falls back to mob lookup when no object matches. ### Two Depletion Mechanics **Per-drop depletion** (mining, regular trees): Set `depletes: true` on a drop entry. The resource depletes on first successful gather of that drop. **Shared depletion** (higher-tier trees): Set `shared_deplete: ` on the behavior. A shared timer counts down while anyone is chopping; when it hits 0, the next successful gather depletes the tree for all players. The timer regenerates back to max when no one is chopping. ### Bird's Nests Set `nest_chance: 256` on a gather behavior to give a 1/256 independent chance to find a bird's nest alongside the normal drop. Bird's nests are searched with the `search` command, rolling on the `birds_nest_drop` drop table. ### Condition System Used by exits, talk options, on-enter scripts, and toggle checks: | Field | Scope | Example | | ------------- | ------------------------------ | -------------------------------- | | `flag` | World (shared by all players) | `flag: gate_open` | | `player_flag` | Per-character (quest progress) | `player_flag: finished_tutorial` | | `has_item` | Player inventory check | `has_item: bronze_key` | | `all_of` | All sub-conditions must pass | Nested list of conditions | | `any_of` | Any sub-condition passes | Nested list of conditions | | `not` | Invert the check | `not: true` | ### Node Actions (talk dialog) Set on nodes in talk behaviors: | Field | Effect | | ------------------ | ------------------------------------------------------ | | `set_flags` | Sets world flags (global) | | `set_player_flags` | Sets player-local flags (per-character, saved to YAML) | | `give_item` | Gives an item to inventory | | `take_item` | Removes an item from inventory | | `teleport` | Moves player to a room ID | | `heal` | Restores hitpoints | All fields in a single action are processed together. ### Accounts & Aliases Accounts stored in `data/players/accounts/.yaml`. Each account has: - Password hash (sha256 + salt) - Character list - Alias map (`alias ` — account-wide, shared by all characters) Aliases resolve before built-in commands and support argument passthrough (`alias k kill` → `k man` expands to `kill man`). ### World - Rooms are flat YAML files, read from disk on each access (live editing) - Everything is YAML-driven — no database - Exits support conditions (world flags, player flags, items, compound) - Rooms support `on_enter` scripts with per-step conditions - Objects can be `hidden: true` (interactable but not listed in room) - Mobs wander via legal room exits (unconditioned), not hardcoded room lists - Mob wander config (`wander_rooms`, `wander_interval`) set per-instance in room YAML - Object wander config same format — objects teleport between listed rooms ## Implemented ### Core Systems - [x] Telnet server, account creation, hashed passwords, account/character management - [x] HTTPS/WebSocket server with embedded web terminal client - [x] Multi-listener config (telnet + http + https, any combination) - [x] YAML config file (`config.yaml`) with `--config` flag and runtime defaults - [x] 600ms tick engine driving all world simulation - [x] Room-based player hub with enter/exit/action notifications - [x] Single-login enforcement, disconnect timer - [x] Player YAML persistence on every state change - [x] Transport-agnostic Conn interface (tcpConn + wsConn) - [x] Password echo suppression (telnet IAC ECHO, web OSC sequences) - [x] Input sanitization (control character stripping at HandleSession) - [x] Name validation (alphanumeric+spaces, 3-30 chars) - [x] Max input line length (1024 bytes) - [x] WebSocket origin checking (same-origin + loopback) - [x] Ground item spawn stacking fix (Tick + SeedGroundItems) ### Commands - [x] `look / l` — room rendering (mobs, objects, ground items, exits, players) - [x] `look ` — examine mobs, objects, items, players, directions - [x] `n/s/e/w/u/d` — movement (conditional exits, interrupts combat/actions) - [x] `get / drop / drop all` — item pickup/drop with stacking, reservations - [x] `say` — room chat (preserves case) - [x] `score / inventory / equipment / exits` — character and world info - [x] `style` — combat style selection (prefix matching) - [x] `attack / kill ` — combat with numbered targeting - [x] `mine / chop / cut / fish ` — gathering with numbered targeting - [x] `use ` — crafting station interaction - [x] `talk / speak / ask ` — NPC conversations (objects or mobs) - [x] `pull / push ` — toggle interactions (levers, gates) - [x] `alias / unalias ` — account-wide command aliases - [x] `toggle [name]` — 9 character settings - [x] `description / desc / help [topic] / quit` - [x] `search ` — search bird's nests for loot ### Combat - [x] RSC-based formulas (attack/defense rolls, max hit) - [x] Attack styles with bonuses and XP distribution - [x] Equipment bonuses, HP tracking, death mechanics - [x] Combat lock timer, movement interrupts combat - [x] Mob/player regen (1 HP per 100 ticks) - [x] Mob exit-based wandering, enter/leave/spawn notifications - [x] Drop reservation system (100-tick owner lock on loot) ### Skills & Gathering - [x] Mining (copper rocks, pickaxe, depletion/respawn, gem sub-table) - [x] Fishing (wandering spots, non-depleting, fishing rod) - [x] Woodcutting (9 tree types, axes, shared depletion, bird's nests) - [x] Tool system (tool_type + tool_speed on items, tool requirement on behaviors) - [x] Shared drop tables (referenced by multiple behaviors) - [x] XP drops for all skill actions (combat, gathering, use) - [x] 23 skill definitions with RSC XP table (3 implemented: Mining, Fishing, Woodcutting) ### World & Data - [x] Room, item, object, mob, behavior, drop table YAML loaders - [x] Object instance tracking (depletion, wandering, respawn, broadcast) - [x] 21 structured rooms (west gathering wing, east production/utility/combat wing) - [x] Conditional exits (world flags, player flags, items, compound conditions) - [x] Room on_enter scripts with conditions - [x] Hidden objects (interactable but not listed in room output) - [x] Player-local flags vs world flags for multiplayer state separation - [x] Compound conditions: all_of / any_of - [x] Aligned ground item reservation display - [x] Mob wander config per-instance in room YAML (not on MobDef) - [x] Object depletion timer display (depletion toggle) - [x] Bidirectional prefix matching for get/drop/attack/gather commands - [x] Ambiguity detection when multiple object types match a command ### Documentation - [x] WORLDBUILDING.md — comprehensive guide with examples - [x] AGENTS.md — repo structure for LLM context ## Data Format Reference See WORLDBUILDING.md for full examples of every data type with explanations. ## Action Plan ### Phase 1: Tiny ASCII Map - [ ] Limit the room description to 70 characters wide. Add a setting named "maxWidth" in an appropriate section of config.yaml along with comment explaining it. Next to the block of room description text should be an ASCII mini-map showing the current room as @ and a grid of the rooms and connections surrounding it. A full-size ASCII map for the "map" command will eventually be implemented so keep that in mind for methodology and potential functions we can reuse. Each line of the map should print on the maxWidth+2 character after the description (so there's always at least one space between the description and map) If the map is longer than the description, just keep printing new lines for the map. The first line is always ╔═════╗ The last line is always ╚═════╝ Each of the 5 inner lines begins and ends with ║ The map shows up to 9 rooms and any exits connecting them. The 5 inner lines have 7 characters each (including the border) which be defined like this: Line 1: [║, Room A (shown if there is a west exit from North Room or north exit from West Room), — if there is a west exit from North Room, North Room, — if there is an east exit from North Room, Room B (shown if there is an east exit from North Room or north exit from East Room), ║] Line 2: [║, | if there is a north exit from West Room, Blank, | if there is a north exit from Current Room, ↑ if there is an up exit from Current Room, | if there is a north exit from East Room, ║] Line 3: [║, West Room, — if there is a west exit from Current Room, @, — if there is an east exit from Current Room, East Room, ║] Line 4: [║, | if there is a south exit from West Room, ↓ if there is a down exit from Current Room, | if there is a south exit from Current Room, Blank, | if there is a south exit from East Room, ║] Line 5: [║, Room C (shown if there is a west exit from South Room or south exit from West Room), — if there is a west exit from South Room, South Room, — if there is an east exit from South Room, Room D (shown if there is an east exit from South Room or south exit from East Room), ║] Note that North Room, East Room, South Room, and West Room can still show up even if there isn't a direct link from the current room to them. For example if there is a to East Room, then that has an exit north, then that room has an exit west, the 'north room' still shows up on the map. Each room has a parameter map_symbol to identify it on the map. This is how the map looks when every possible room and exit is shown. o is the map_symbol for each room in this example: ╔═════╗ ║o—o—o║ ║| |↑|║ ║o—@—o║ ║|↓| |║ ║o—o—o║ ╚═════╝ This is a map with fewer connections. Note that 'Room A' and 'North Room' show up despite only being connected through East Room and Room B. ╔═════╗ ║o—o—o║ ║ |║ ║o—@—o║ ║| ║ ║o ║ ╚═════╝ Here's how the room description would look limited to 70 characters long with the map to the right: Central Market The market square pulses with the unbridled energy of a thousand ╔═════╗ voices, a symphony of commerce and humanity that hasn't changed in ║o—o—o║ generations. Overhead, September clouds drift lazily across the sky, ║| |↑|║ occasionally releasing weak sunlight that transforms the ancient ║o—@—o║ stone beneath countless feet into patches of burnished gold. ║|↓| |║ ║o—o—o║ ╚═════╝ - [ ] This is a feature that can be toggled with the "tinymap" toggle. If the toggle is off, don't print the map at all. - [ ] Add a toggle "left-tinymap". If this is enabled, the map should be printed on each line before the room description. For example: Central Market ╔═════╗ The market square pulses with the unbridled energy of a thousand ║o—o—o║ voices, a symphony of commerce and humanity that hasn't changed in ║| |↑|║ generations. Overhead, September clouds drift lazily across the sky, ║o—@—o║ occasionally releasing weak sunlight that transforms the ancient ║|↓| |║ stone beneath countless feet into patches of burnished gold. ║o—o—o║ ╚═════╝ ### Phase 2: Core Skill Chains (breadth over depth) - [x] Woodcutting — tree objects + axes, shared depletion, bird's nests - [ ] Cooking — fire/range object (`use` type), raw fish/meat → cooked food - [ ] Smithing — furnace + anvil objects (`use` type), ore → bars → weapons/armor - [ ] Fletching — fletching table object (`use` type), logs → arrow shafts/bows - [ ] Equipment: bronze/iron weapons, axes, cooked trout, arrows, bows ### Phase 3: World Depth - [ ] Shop system — talk node action for buy/sell interface with credits - [ ] Bank system — deposit/withdraw items at bank booth objects - [ ] Ranged combat — bows/crossbows/ammo, add Ranged to combat formulas - [ ] Aggressive mobs — initiate combat on room entry - [ ] Flee command - [ ] Equipment skill requirements ### Phase 4: Polish - [ ] Hacking, Thieving, Agility, Alchemy, Construction, Crafting, Scavenging, Farming, Assassin - [ ] Quest system (already supported via talk nodes + world/player flags) - [ ] PvP combat, multi-combat zones - [ ] ANSI color output - [ ] Admin commands - [ ] Persist ground items and mob state across restarts - [ ] More rooms — flesh out the asteroid world