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)