1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
|
# 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/<id>.yaml`. Objects reference them with `behavior: <id>`. Mobs can also carry `behavior: <id>` — 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: <ticks>` 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/<name>.yaml`. Each account has:
- Password hash (sha256 + salt)
- Character list
- Alias map (`alias <name> <cmd>` — 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 <target>` — 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 <mob>` — combat with numbered targeting
- [x] `mine / chop / cut / fish <object>` — gathering with numbered targeting
- [x] `use <object>` — crafting station interaction
- [x] `talk / speak / ask <target>` — NPC conversations (objects or mobs)
- [x] `pull / push <object>` — toggle interactions (levers, gates)
- [x] `alias <name> <cmd> / unalias <name>` — account-wide command aliases
- [x] `toggle [name]` — 9 character settings
- [x] `description / desc / help [topic] / quit`
- [x] `search <item>` — 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
- [x] 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║
╚═════╝
- [x] This is a feature that can be toggled with the "tinymap" toggle. If the toggle is off, don't print the map at all.
- [x] 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 1b: Map & Option System
- [x] Replaced `toggle` command with `option`/`options` — supports bool (on/off), string (choice lists), and int option types
- [x] All old toggles migrated to the option system (tinymap, left-tinymap, xpdrops, exits, mobenter, mobleave, mobspawn, reserve, depletion, description)
- [x] Added `color` option (none, ansi, xterm256)
- [x] Added `mapwidth` and `mapheight` options for the `map` command viewport size
- [x] `map` command — renders a full ASCII map centered on the player using BFS graph traversal with geometric coordinate assignment
- [x] Shared BFS graph builder (`buildGraph`) reused by both the tiny map (in `look`) and the `map` command
- [x] Added `mappadding` option (none, x, y, xy) — controls blank-row stripping and left-trimming of map output
- [x] Tiny map built via BFS viewport rendering (3x3 room window at 2x zoom) — no hardcoded corner/cardinal rules, no room duplication
### 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
|