From 01d58481788a01a9b278a22fa084a1c4061fa62f Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Tue, 10 Feb 2026 19:07:32 -0500 Subject: [PATCH] Add prompt system design doc --- docs/how/prompt-system.txt | 232 +++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 docs/how/prompt-system.txt diff --git a/docs/how/prompt-system.txt b/docs/how/prompt-system.txt new file mode 100644 index 0000000..413eeb0 --- /dev/null +++ b/docs/how/prompt-system.txt @@ -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)