Compare commits

...

3 commits

Author SHA1 Message Date
94a01186c8
Add a doc index 2026-02-11 09:39:48 -05:00
da809bea31
Add zones and building doc 2026-02-10 22:48:44 -05:00
01d5848178
Add prompt system design doc 2026-02-10 19:07:32 -05:00
3 changed files with 892 additions and 0 deletions

232
docs/how/prompt-system.txt Normal file
View file

@ -0,0 +1,232 @@
prompt system
=============
modal prompts that show useful info, with color markup support.
what we have now
================
server.py lines 314-320 picks between three hardcoded strings:
editor mode: " {line_number}> "
IF mode: "> "
everything: "mud> "
no stats, no color, no per-player customization. the prompt function
is inline in the server loop.
what we want
============
a prompt renderer: given a player, return the prompt string (with ANSI
already baked in based on their color_depth). each mode has its own
prompt template. templates use variables that get filled from player
state.
default prompts by mode:
normal mode:
<56%> 200 >
- stamina as percentage (stamina / max_stamina * 100, rounded)
- power level as integer (floor of pl)
- the > delimiter
combat mode:
<56%> 200 [punch left] vs Goku >
- same stamina/pl gauges
- current attack or defense stance in brackets
- opponent name
- if no active move: [idle]
editor mode:
(keep as-is: " {line}> ")
editor prompt is special — it comes from the editor, not the
prompt system. leave it alone.
IF mode:
(keep as-is: "> ")
the z-machine has its own prompt conventions. don't override.
color markup codes
==================
add a lightweight color markup language for prompt templates and
eventually for all builder-authored text. similar to miniboa's caret
codes but using {color} syntax so it reads more naturally and doesn't
collide with content text.
codes (16-color baseline):
{red} {RED} (bright)
{green} {GREEN}
{yellow} {YELLOW}
{blue} {BLUE}
{magenta} {MAGENTA}
{cyan} {CYAN}
{white} {WHITE}
{black} {BLACK}
{bold} {dim}
{reset} {/} (shorthand for reset)
two escaping approaches considered:
1. miniboa-style: ^r for red, ^R for bright red. terse, fast to type
in-game. but ^ collides with mountain terrain char and regex.
2. {tag} style: {red}, {bold}, {/}. more readable, no collisions,
familiar from other MUDs (CircleMUD uses &, ROM uses {).
going with {tag} style. it's what builders will type in room descriptions
and NPC dialogue too, so it should be readable. the render function
converts tags to ANSI based on client color depth, or strips them for
clients with no color support.
implementation: a colorize(text, color_depth) function in render/colors.py
that replaces {tag} tokens with the appropriate ANSI escape. clients with
caps.ansi=False get tags stripped entirely.
prompt template variables
=========================
templates are strings with {variable} references. the prompt renderer
fills them from player state.
core variables (available in all modes):
{stamina_pct} stamina as integer percentage
{stamina} current stamina (float, 1 decimal)
{max_stamina} stamina ceiling
{pl} power level (integer)
{name} player name
{mode} current mode string
{x}, {y} coordinates
combat-only variables:
{move} current attack/defense name, or "idle"
{opponent} opponent name
{combat_state} telegraph/window/idle
these overlap with color tag syntax ({red} vs {pl}). resolution:
color tags are a fixed set of known names. anything not in the color
tag set gets looked up as a template variable. or: process template
variables first (they produce plain text), then process color tags
(they produce ANSI). two-pass approach, clean separation.
the prompt function
===================
def render_prompt(player: Player) -> str
lives in a new module: src/mudlib/prompt.py
logic:
1. pick the template string based on player.mode
2. build a context dict from player state
3. fill template variables (str.format_map or similar)
4. convert color tags to ANSI (based on player.caps.color_depth)
5. return the final string
the server loop replaces its inline if/elif with:
from mudlib.prompt import render_prompt
_writer.write(render_prompt(player))
editor and IF modes short-circuit before template processing (they have
their own prompt logic that shouldn't change).
stamina gauge coloring: the prompt template itself can include
conditional color. simplest approach: the template variable {stamina_pct}
is just a number, but we provide a {stamina_gauge} that includes color:
>= 60%: {green}<{stamina_pct}%>{/}
30-59%: {yellow}<{stamina_pct}%>{/}
< 30%: {red}<{stamina_pct}%>{/}
the gauge is a computed variable, not raw data. the template for normal
mode becomes:
{stamina_gauge} {pl} >
with the gauge expanding to e.g. {green}<56%>{/} which then gets
colorized.
pl coloring: same idea. {pl_gauge} colors based on absolute value or
percentage of starting PL. details TBD — we might want max_pl on Entity
first.
implementation phases
=====================
phase 1: prompt module + normal mode prompt
- create src/mudlib/prompt.py with render_prompt()
- implement stamina_gauge and pl display
- wire into server loop (replace hardcoded "mud> ")
- tests for render_prompt with mock players
- editor and IF modes unchanged (short-circuit)
phase 2: combat mode prompt
- add combat context variables (move, opponent, state)
- combat prompt template
- tests with mock encounters
phase 3: color markup engine
- create src/mudlib/render/colors.py with colorize()
- {tag} to ANSI conversion, respects color_depth
- strip tags for no-color clients
- integrate into prompt renderer
- tests for colorize at each color depth
phase 4: per-player prompt customization (future)
- player.prompt_template stored in sqlite
- "prompt" command to set custom template
- default template per mode if player hasn't customized
- builder mode template (future: brush, color hex, etc.)
files touched
=============
new:
src/mudlib/prompt.py — the prompt renderer
src/mudlib/render/colors.py — color markup engine
tests/test_prompt.py — prompt tests
tests/test_colors.py — color markup tests
modified:
src/mudlib/server.py — replace inline prompt with render_prompt()
src/mudlib/entity.py — maybe add max_pl if we want PL gauge coloring
future ideas
============
- GMCP prompt data: send structured prompt info over GMCP so graphical
clients can render their own gauges. the text prompt is for raw telnet.
- builder mode prompt: when we add the world editor, show brush type,
current terrain char, and palette color.
[brush: T] [#1b5e20] build>
- rest/regen indicator: show a + or ~ when stamina is regenerating.
<56%+> 200 >
- compass rose: show available exits inline.
<56%> 200 [NSEW] >
- danger indicator: flash or color the prompt when PL drops low enough
that one more hit could KO.
- custom prompt command examples:
prompt {stamina_gauge} {pl} >
prompt {stamina_gauge} {pl} [{move}] >
prompt {name} [{mode}] >
- MXP/MNES clickable prompt elements (far future)

View file

@ -0,0 +1,596 @@
zones and building — from grids to worlds
=========================================
a design doc. captures the vision for how players and builders create,
connect, and inhabit spaces in the MUD. born from a riffing session, fed
by research into how other engines handle this. living document — update
as decisions get made and things get built.
see also: ``things-and-building.rst`` (item/verb system),
``architecture-plan.txt`` (engine/content boundary),
``prompt-system.txt`` (modal prompts).
the core idea — everything is a zone
--------------------------------------
a zone is a rectangular grid of tiles. it has dimensions, a palette, and
tile data. that's it. everything is a zone.
- the procedural overworld? a zone with a generator (perlin noise)
- a hand-built treehouse? a zone with tile data from a file
- a single room (office, closet)? a small zone
- a dungeon? a zone with procedural guts
- the flower you spawn in? a tiny zone
the differences between zones are properties, not types:
**wrapping**
does the zone loop? options: toroidal (wraps both axes, like a sphere
— walk far enough and you're back), cylindrical (wraps one axis),
bounded (hard edges, you hit a wall or void).
**generator**
how tiles get filled. ``none`` means blank canvas (you paint it).
``perlin`` means procedural terrain from a seed. ``file`` means tile
data loaded from a TOML. generators and hand-placed tiles can coexist
— generate a base, then paint over specific areas.
**dimensions**
5x5, 100x50, 1000x1000. whatever the space needs.
**palette**
what each symbol means. char + name + color + properties (passable,
movement cost, opacity, etc). every zone has a palette. zones can
share palettes or define their own.
zone data format
-----------------
the web editor already saves something close to this. extend it with
zone metadata and portal definitions.
::
[zone]
name = "treehouse"
description = "a treehouse nestled in the canopy of an ancient oak"
wrapping = "bounded"
spawn = [10, 7]
[map]
width = 20
height = 15
data = """
..oooo..TTT.........
..o..o..TTT.........
..o..o..TTT.........
..oooo....T.........
..........T.........
........+.T.........
..........T.........
..........TTTTT.....
................~~~~
................~~~~
....................
....................
....................
....................
....................
"""
[palette]
entries = """
o|wall|#8b4513|solid
.|floor|#d2b48c|passable
T|leaves|#0a5f0a|passable
~|water|#2244aa|solid
+|door|#c8a456|passable
"""
[[portals]]
x = 10
y = 14
target = "zone:forest_path:20,5"
label = "a rope bridge stretches into the canopy"
[[portals]]
x = 10
y = 0
target = "overworld:450,300"
label = "a ladder descends to the ground far below"
zones live in ``content/zones/``. loaded at startup like mobs and
commands. hot-reloadable via ``reload``.
player location model
----------------------
today a player is ``(x, y)`` on the overworld. with zones, a player is
either on the overworld OR in a zone.
conceptually: ``(zone_name, x, y)`` where ``zone_name`` is ``None`` for
the overworld. the viewport, movement, look — all the same mechanics,
just reading from a different grid.
portals are tiles with a target. step on one, you transition. your
location changes from ``(treehouse, 10, 14)`` to
``(forest_path, 20, 5)``. the viewport redraws with the new zone's
tiles and palette.
open question: do portals auto-trigger (step on tile = transition) or
require explicit action (``enter``, ``use door``)? could depend on the
portal — a path auto-triggers, a locked door requires a key and
``open door`` + ``enter``. the portal definition could specify this::
[[portals]]
x = 5
y = 3
target = "zone:basement:5,0"
trigger = "auto" # step on it and you go
label = "stairs descend into darkness"
[[portals]]
x = 12
y = 7
target = "zone:vault:0,0"
trigger = "manual" # requires 'enter' command
label = "a heavy iron door"
requires = "iron_key" # item check (future)
the flower spawn
-----------------
new player experience. you wake up inside a sealed flower.
the flower is a tiny zone, maybe 7x7. petals surround you. the
description mentions perfume, texture on your hands, dim light filtering
through. you can look around but every direction is sealed — petal walls.
any force command (push, kick, punch, pull) opens the flower. the petal
tiles change (solid → passable, or the zone swaps entirely), you "fall"
— a text transition, maybe a brief animation of symbols scrolling — and
land in the treehouse zone.
the treehouse is the real starting area. hand-built, 20x15 or whatever
feels right. has a few things to interact with, maybe a note or a simple
puzzle. eventually you find the way down (a portal) and land on the
overworld. infinite procedural landscape in every direction.
tutorial funnel: tiny space → medium space → infinite space.
::
flower (7x7, sealed)
↓ player breaks out
treehouse (20x15, hand-built)
↓ portal (ladder, rope, etc)
overworld (1000x1000, procedural)
↓ portals to other zones
dungeons, houses, other players' creations...
creating zones — three workflows
----------------------------------
all three produce the same format. interchangeable.
workflow A: web editor
~~~~~~~~~~~~~~~~~~~~~~~
open the map editor in a browser. paint tiles, define palette, set
dimensions. save as TOML. drop the file in ``content/zones/``. reload
in the MUD (or restart). the zone exists.
the web editor is not special — it's just faster for large-scale work.
photoshop vs MS paint. good for: laying out a big area, precise pixel
work, working offline, iterating visually.
workflow B: in-game commands
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
@new-zone jareds-house 100 50
creates a blank bounded zone. you're placed in the center. the viewport
shows void/empty tiles. now you build.
::
@new-zone the-wilds 500 500 --wrap
creates a toroidal zone. walk far enough and you loop.
::
@new-zone dungeon-3 40 40 --gen perlin
creates a zone with procedural terrain. paint over specific parts.
workflow C: paint mode
~~~~~~~~~~~~~~~~~~~~~~~
this is the one that buzzes. push into paint mode like editor mode or IF
mode. your ``@`` becomes a cursor. movement works freely — no passability
checks, you float over everything.
two states within paint mode:
**survey** (default)
move around, look at things, nothing changes. scouting.
**painting**
every tile you leave behind gets stamped with your brush. walk east
5 tiles, leave 5 walls behind you.
toggle with ``p``. status line shows state::
[paint: . floor] survey
[paint: o wall] PAINTING
change brush: type the symbol character. type ``o`` and your brush is
now wall. type ``.`` for floor. type ``~`` for water. single-char input
sets the brush — no ambiguity because you're in a special mode where
normal commands don't apply.
or explicit: ``b o`` for brush=wall. both work.
you could even render as the brush symbol instead of ``@`` when painting.
you see an ``o`` moving around leaving ``o``'s behind it.
entering and exiting::
@paint enter paint mode (survey by default)
p toggle painting on/off
o set brush to 'o' (wall)
n/s/e/w/ne/etc move (and paint if painting)
@paint exit paint mode (or :q, or @done)
workflow D: @dig (expanding the canvas)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
not room-linking like traditional MUDs. you're extending the zone's tile
grid itself.
::
@dig 5 e zone grows 5 tiles east (new tiles are void)
@dig 10 s zone grows 10 tiles south
@dig n grow 1 tile north (default)
new space is void/empty. paint it into existence.
this means zones aren't fixed-size — they grow as you build. start with
a 1x1 zone and @dig your way to a mansion.
materials and palettes
-----------------------
every symbol needs at minimum: char, name, color. that's the palette.
::
[palette]
entries = """
.|floor|#d2b48c|passable
o|wall|#8b4513|solid
~|water|#2244aa|solid
+|door|#c8a456|passable
#|window|#87ceeb|solid
^|mountain|#666666|solid
T|tree|#0a5f0a|passable
:|gravel|#9a9996|passable
=|brick|#a51d2d|solid
"""
each zone has a palette. could be inline or reference a shared one::
[zone]
palette = "default" # use content/palettes/default.toml
a shared palette means consistent symbols across zones. a custom palette
means a zone can redefine what ``~`` looks like (lava instead of water).
future properties per material: movement cost (forest is slower), opacity
(walls block line of sight), damage (lava hurts), wetness (water makes
you wet). for now, just passable/solid is enough.
portals connect zones
----------------------
a portal is a tile with a target. defined in the zone TOML or placed
in-game.
in-game portal creation::
@portal here → jareds-house:5,3
"this tile, right where I'm standing, is a portal to jareds-house at
coordinates 5,3."
portals are bidirectional by default? or one-way? probably one-way by
default (you set up both ends), with a convenience flag for
bidirectional::
@portal here ↔ jareds-house:5,3 # creates both sides
what stepping on a portal looks like: the viewport dissolves (or snaps)
to the new zone. you get a brief transition message. your prompt updates
if the zone has different ambient info.
connecting to the overworld
----------------------------
the overworld is just another zone — the big one. portals from the
overworld into smaller zones work the same way. a tile at (450, 300) on
the overworld has a portal to ``treehouse:10,0``. the treehouse has a
portal back to ``overworld:450,300``.
buildings, dungeons, caves, interiors — all zones with portals from
the overworld. the overworld tile might show a special symbol (a door
icon, a cave mouth) to hint that something's there.
procedural as a tool, not the default
---------------------------------------
the procedural perlin world is useful but it's a tool. good for:
- wilderness areas (forests, plains, mountains)
- dungeon generation (random layouts with a seed)
- filler terrain between hand-built areas
- prototyping (generate a base, paint over it)
the main game world should be hand-built. the starting experience, key
locations, cities, landmarks — all authored by humans. procedural fills
in the gaps, provides variety, makes the world feel larger than what was
hand-crafted.
a zone can mix both: generate perlin terrain, then overlay hand-placed
features (roads, buildings, rivers) on top. the generator runs first,
then the overlay applies.
layers (future)
----------------
jared mentioned up/down and layers. a zone could have a z-axis — floor 1
is the ground, floor 2 is the second story, floor -1 is the basement.
same x,y grid, different z level. portals (stairs, ladders, holes)
connect layers vertically.
not needed for v1 but the data model should not prevent it. a zone's
tile data could eventually be::
[map]
width = 20
height = 15
layers = 3
data.0 = """...""" # ground floor
data.1 = """...""" # second floor
data.-1 = """...""" # basement
or layers could just be separate zones with portals. simpler, same
result. decide later.
what we learned from other engines
-----------------------------------
research into how DikuMUD, LPC, MOO, Evennia, and others handle
building. key takeaways and things to avoid.
what works
~~~~~~~~~~~
**MOO's immediacy.** in LambdaMOO, @dig creates a room and you're done.
no save step, no compilation, no restart. changes are instant. builders
stay in flow. we want this — @new-zone, @paint, and your changes are
live.
**evennia's XYZGrid.** optional coordinate system drawn as ASCII maps in
python files. rooms at grid intersections, links along paths. transition
nodes connect separate maps. proves grid-based worlds work in a
room-based engine. their wilderness contrib is smart: players get their
own room instance at a coordinate, share when co-located.
**diku OLC menus.** redit/oedit/medit push you into an edit mode with
numbered options. simple, discoverable, no memorization. the nested
mode pattern (game → editor → description editor) maps well to our mode
stack.
**LPC file-based content.** builders write .c files, ``update`` compiles
them live. version controllable, diffable, shareable. our TOML approach
has the same benefits without requiring a programming language.
**ranvier's hybrid coordinates.** optional (x,y,z) per room, local to
each area. explicit exits override inferred ones. areas can mix
coordinate and non-coordinate rooms. flexible and clean.
**dead souls QCS.** wizard-driven creation (``addnpc``, ``addroom``)
generates LPC files via interactive prompts. lowers the barrier —
builders answer questions instead of writing code.
what people hate
~~~~~~~~~~~~~~~~~
**menu-driven OLC is clunky.** everyone acknowledges OLC is better than
manual file editing, but it's still tedious. numbered menus, nested
sub-menus, explicit save commands. modern builders expect something
faster. our paint mode is a direct response to this.
**descriptions nobody reads.** "most players don't read the descriptions
after seeing them the first time, and probably only skim them the first
time anyway." high effort, low return. our tile-based visual approach
sidesteps this — you SEE the room, you don't read a paragraph about it.
descriptions can supplement the visual, not replace it.
**too much coding required.** evennia requires python for anything beyond
basic building. MUSHcode has a brutal learning curve. MOO programming is
its own language. builders want to create content, not learn to program.
our approach: TOML data files for definitions, paint mode for visuals,
minimal programming needed for basic building.
**collaborative building is an afterthought.** evennia's default setup
doesn't support collaborative building well. MOO's permission system
creates unexpected barriers. we should design for collaboration from the
start — multiple builders in a zone, clear ownership, easy sharing.
**framework lock-in.** evennia's ObjectDB forces assumptions that don't
fit all games. our zone model is simple data (TOML files, tile grids) —
no complex object hierarchy to fight against.
**no good copy-paste or templating.** every room built from scratch.
repetitive work. our web editor handles this naturally (select, copy,
paste). in-game: could support @copy-region or stamp/template tools.
**saving and losing work.** diku OLC requires explicit saves; crash
before saving = lost work. MOO's auto-persistence is better. our zones
should auto-save on edit (or at least on leaving paint mode).
**recruiting builders is hard.** the harder the tools, the fewer
builders you get. "building can be an enjoyable activity, but it's got
its downsides. which probably explains why MUD creators have a difficult
time recruiting dedicated builders." paint mode should make building
feel like play, not work.
patterns from zone connectivity
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**evennia XYZGrid transition nodes.** metadata-only nodes that hold
target coordinates on another map. never spawn as rooms. clean
abstraction for zone-to-zone links. similar to our portal concept.
**diku inter-zone exits.** reference format: ``room_name@zone_name``.
globally unique vnums (virtual numbers). our equivalent:
``zone_name:x,y``.
**evennia wilderness fixed locations.** certain wilderness coordinates
are "fixed rooms" — reach those coords, transition into a real room.
this is how building entrances work in their wilderness system. maps
directly to our portal-on-overworld-tile concept.
**alex kallend's wilderness (1999).** generate descriptions
deterministically from coordinates. same coords always produce same
room. memory-efficient. this is basically what our perlin generator
does.
**ranvier cross-area linking.** reference format: ``areaname:roomid``.
works between coordinate and non-coordinate rooms. our format:
``zone:name:x,y`` or ``overworld:x,y``.
.. note::
open research: dig deeper into other MUD codebases for building
patterns. specifically:
- evennia's prototype/spawn system for templating objects across zones
- how discworld (LPC) handles its massive hand-built world
- AresMUSH's web-based building tools (they moved building to the browser)
- graphicMUD's tile-based building (closest to our model — they use
sixel graphics on a tile grid)
- how any engine handles "undo" for building mistakes
- permission models that actually work for collaborative building
without being bureaucratic
build order
------------
rough priority. each step is usable on its own.
1. **zone data model + registry.** load TOML zone files from
``content/zones/``. zone has dimensions, tile grid, palette, portals,
metadata. registry maps zone names to loaded zones.
2. **player-in-zone tracking.** extend player location from ``(x, y)``
to ``(zone, x, y)``. zone=None means overworld. persist to sqlite.
3. **viewport reads from zone.** ``look`` checks which zone the player
is in and reads tiles from that zone's grid instead of the overworld.
movement checks that zone's passability rules. same rendering
pipeline, different data source.
4. **@new-zone + @goto.** create blank zones from in-game. ``@goto
treehouse`` teleports you into a zone. ``@goto overworld`` returns
you. builder/admin commands.
5. **paint mode.** push onto mode stack. cursor movement without
passability. brush selection. paint toggle. status line integration.
6. **file import/export.** load zone from TOML file (web editor output).
save zone state back to TOML. the web editor and in-game building
produce the same files.
7. **portals.** portal definitions in zone TOML. portal tiles trigger
zone transitions. ``@portal`` command for in-game portal creation.
8. **@dig.** expand zone dimensions from in-game. grow the canvas in any
direction.
9. **procedural generator as zone option.** ``--gen perlin`` on zone
creation. generate base terrain, allow painting over it.
10. **the flower spawn.** hand-build the flower and treehouse zones. wire
the new-player experience to start in the flower instead of the
overworld center.
open questions
---------------
- should the overworld be a zone, or is it a special case? making it a
zone simplifies the code (everything goes through the same path) but
the overworld has unique properties (huge, procedural, wrapping).
probably: overworld IS a zone, just a large wrapping one with a
generator.
- zone ownership and permissions. who can edit a zone? the creator?
anyone with builder role? co-ownership? defer this until the permission
model from things-and-building.rst is designed.
- how big can a zone be before performance matters? a 1000x1000 zone is
1MB of chars. loading, saving, and transmitting that is fine. but do
we want to support zones larger than that? probably not initially.
- persistence model. are zones purely file-based (edit file, reload) or
do runtime changes (paint mode edits) persist automatically? leaning
toward: paint mode edits modify in-memory state AND write back to the
TOML file. the file is always the source of truth, and it stays
current.
- what symbol is void/empty? tiles that haven't been painted yet. could
be space (invisible), could be a dim dot, could be configurable. needs
to be visually distinct from any palette symbol.
- should portals be visible on the map? a special symbol, a color, a
blinking character? or do they look like their underlying tile (a door
looks like a door, not like a portal)?
- layers vs separate zones for vertical space. layers are elegant but
add complexity. separate zones with portals (stairs) are simpler and
get the same result. start with separate zones.
relation to things-and-building.rst
-------------------------------------
that doc covers the item/verb system (phase 1-2) and room system
(phase 3). zones and building here are the spatial foundation that
the item system lives on top of.
zones provide the WHERE. things provide the WHAT. a zone has tiles
(terrain, walls, floor). things sit on tiles (items, furniture, NPCs).
portals are a zone-level concept. doors with locks are a thing-level
concept (the door is a @thing with an @verb for ``open`` that checks for
a key).
build zones first (this doc), then populate them with things
(things-and-building.rst).

64
docs/index.rst Normal file
View file

@ -0,0 +1,64 @@
project documentation
=====================
all project docs, grouped by topic.
vision
------
- ``DREAMBOOK.md`` — vision, philosophy, wild ideas
- ``docs/why/telnet-first.txt`` — why telnet as the protocol
- ``docs/why/text-worlds.txt`` — why text worlds matter
engine
------
- ``docs/how/architecture-plan.txt`` — engine/content boundary, mode stack, entity model, game loop
- ``docs/how/commands.txt`` — command registration, async handlers, aliases, mode filtering
- ``docs/how/persistence.txt`` — SQLite storage, what's persisted vs runtime
- ``docs/how/terrain-generation.txt`` — perlin noise, elevation thresholds, river tracing
- ``docs/how/things-and-building.rst`` — interactive objects, python class decorators
- ``docs/how/zones-and-building.rst`` — zones as spatial unit, procedural overworld, player homes
- ``docs/how/prompt-system.txt`` — modal prompts, color markup, per-player customization
combat
------
- ``docs/how/combat.rst`` — state machine, TOML-defined moves, timing windows
interactive fiction
-------------------
- ``docs/how/if-journey.rst`` — master IF doc: five integration levels, audit findings, milestones
- ``docs/how/if-terminal.txt`` — level 1: dfrotz subprocess, spectator broadcasting
- ``docs/how/if-integration.txt`` — original IF research (predates audits)
- ``docs/how/viola-embedding-audit.rst`` — viola interpreter audit
- ``docs/how/zvm-embedding-audit.rst`` — zvm interpreter audit
- ``docs/how/mojozork-audit.rst`` — MojoZork multiplayer C implementation audit
- ``docs/how/zmachine-performance.rst`` — profiling, optimization (cold-start 4720ms to 786ms)
- ``docs/how/multiplayer-zmachine-design.rst`` — level 4 multiplayer design
- ``docs/plans/zmachine-game-compatibility.rst`` — game test plan for interpreter
research
--------
- ``docs/how/mudlib-landscape.txt`` — survey of MUD architectures (Diku, MOO, LPC, evennia)
- ``docs/how/mud-ecosystem.txt`` — catalog of engines, clients, protocols
- ``docs/how/dsls.md`` — scripting language catalog (MOO, LPC, MUSH) with examples
- ``docs/research/dbzfe_notes.rst`` — DBZFE MUD reference notes
- ``docs/research/dbzfe.log`` — sample MUD output
- ``docs/research/dbzfe.tin`` — TinTin++ config
lessons
-------
- ``docs/lessons/charset-vs-mtts.txt`` — telnet CHARSET negotiation hang, MTTS workaround
- ``docs/lessons/dfrotz-prompt-detection.txt`` — dfrotz prompt format varies, detection logic
- ``docs/how/utf8-design-lesson.rst`` — ASCII bias in UTF-8, international MUD implications
- ``docs/how/zmachine-garbled-output-investigation.rst`` — string decoding corruption debug trace
plans
-----
- ``docs/plans/if-terminal.txt`` — level 1 IF architecture design (predates implementation)
- ``docs/plans/zmachine-game-compatibility.rst`` — game test plan for interpreter