Add prompt system design doc

This commit is contained in:
Jared Miller 2026-02-10 19:07:32 -05:00
parent 74538756d5
commit 01d5848178
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C

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)