"""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)