mud/src/mudlib/prompt.py

168 lines
4.7 KiB
Python

"""Pure functions for prompt rendering."""
from __future__ import annotations
from typing import TYPE_CHECKING
from mudlib.render.colors import colorize
from mudlib.world.terrain import World
if TYPE_CHECKING:
from mudlib.player import Player
# Reverse lookup: char → name
_CHAR_TO_TERRAIN = {v: k for k, v in World.TERRAIN_CHARS.items()}
# Default prompt templates by mode
DEFAULT_TEMPLATES: dict[str, str] = {
"normal": "{stamina_gauge} <{pl}/{max_pl}> ",
"combat": "{stamina_gauge} <{pl}/{max_pl}> vs {opponent} > ",
"paint": (
"{stamina_gauge} <{pl}/{max_pl}>"
" ({x},{y}) [brush: {paint_brush}] {paint_state} "
),
"editor": "editor> ",
"if": "> ",
}
ADMIN_TEMPLATES: dict[str, str] = {
"normal": "{stamina_gauge} <{pl}/{max_pl}> ({x},{y}) {terrain} ",
"combat": "{stamina_gauge} <{pl}/{max_pl}> vs {opponent} > ({x},{y}) ",
}
def render_prompt(player: Player) -> str:
"""Render the prompt string based on player state and mode.
Args:
player: The player whose prompt to render
Returns:
Formatted prompt string with ANSI codes based on player color support
"""
# Get template from player override or mode default
if player.prompt_template:
template = player.prompt_template
elif player.paint_mode:
template = DEFAULT_TEMPLATES["paint"]
elif player.is_admin:
template = ADMIN_TEMPLATES.get(
player.mode, DEFAULT_TEMPLATES.get(player.mode, "{stamina_gauge} {pl} > ")
)
else:
template = DEFAULT_TEMPLATES.get(player.mode, "{stamina_gauge} {pl} > ")
# Build variable dictionary
stamina_pct = (
int((player.stamina / player.max_stamina) * 100)
if player.max_stamina > 0
else 0
)
# Compute stamina gauge with conditional coloring
if stamina_pct >= 60:
stamina_gauge = f"{{green}}<{stamina_pct}%>{{/}}"
elif stamina_pct >= 30:
stamina_gauge = f"{{yellow}}<{stamina_pct}%>{{/}}"
else:
stamina_gauge = f"{{red}}<{stamina_pct}%>{{/}}"
variables = {
"stamina_pct": str(stamina_pct),
"stamina": str(round(player.stamina)),
"max_stamina": str(round(player.max_stamina)),
"stamina_gauge": stamina_gauge,
"pl": str(round(player.pl)),
"max_pl": str(round(player.max_pl)),
"opponent": _get_opponent_name(player),
"move": _get_current_move(player),
"name": player.name,
"mode": player.mode,
"x": str(player.x),
"y": str(player.y),
"combat_state": _get_combat_state(player),
"terrain": _get_terrain_name(player),
"paint_brush": player.paint_brush,
"paint_state": "PAINTING" if player.painting else "SURVEYING",
}
# Substitute variables in template
result = template
for key, value in variables.items():
result = result.replace(f"{{{key}}}", value)
# Process color tags based on player's color support
result = colorize(result, player.color_depth)
return result
def _get_opponent_name(player: Player) -> str:
"""Get the name of the player's combat opponent if any.
Args:
player: The player to check
Returns:
Opponent name if in combat, empty string otherwise
"""
from mudlib.combat.engine import get_encounter
encounter = get_encounter(player)
if encounter is None:
return ""
# Determine who the opponent is (the other entity in the encounter)
if encounter.attacker is player:
return encounter.defender.name
return encounter.attacker.name
def _get_current_move(player: Player) -> str:
"""Get the name of the player's current combat move if any.
Args:
player: The player to check
Returns:
Move name if in combat with active move, empty string otherwise
"""
from mudlib.combat.engine import get_encounter
encounter = get_encounter(player)
if encounter is None:
return ""
if encounter.current_move is None:
return ""
return encounter.current_move.name
def _get_combat_state(player: Player) -> str:
"""Get the current combat state for the player.
Args:
player: The player to check
Returns:
Combat state string: "idle", "telegraph", "window", or "resolve"
"""
from mudlib.combat.engine import get_encounter
encounter = get_encounter(player)
if encounter is None:
return "idle"
return encounter.state.value
def _get_terrain_name(player: Player) -> str:
"""Get human-readable terrain name for player's current tile."""
from mudlib.zone import Zone
if not isinstance(player.location, Zone):
return "unknown"
tile = player.location.get_tile(player.x, player.y)
return _CHAR_TO_TERRAIN.get(tile, tile)