Add prompt system design doc
This commit is contained in:
parent
74538756d5
commit
01d5848178
1 changed files with 232 additions and 0 deletions
232
docs/how/prompt-system.txt
Normal file
232
docs/how/prompt-system.txt
Normal 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)
|
||||
Loading…
Reference in a new issue