"""Look command for viewing the world.""" from typing import Any from mudlib.commands import CommandDefinition, register from mudlib.effects import get_effects_at from mudlib.mobs import mobs from mudlib.player import Player, players from mudlib.render.ansi import RESET, colorize_terrain # World instance will be injected by the server world: Any = None # Viewport dimensions VIEWPORT_WIDTH = 21 VIEWPORT_HEIGHT = 11 async def cmd_look(player: Player, args: str) -> None: """Render the current viewport to the player. Args: player: The player executing the command args: Command arguments (unused for now) """ # Get the viewport from the world viewport = world.get_viewport(player.x, player.y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT) # Calculate center position center_x = VIEWPORT_WIDTH // 2 center_y = VIEWPORT_HEIGHT // 2 # Build a list of (relative_x, relative_y) for other players other_player_positions = [] for other in players.values(): if other.name == player.name: continue # Calculate relative position (shortest path wrapping) dx = other.x - player.x dy = other.y - player.y if dx > world.width // 2: dx -= world.width elif dx < -(world.width // 2): dx += world.width if dy > world.height // 2: dy -= world.height elif dy < -(world.height // 2): dy += world.height rel_x = dx + center_x rel_y = dy + center_y # Check if within viewport bounds if 0 <= rel_x < VIEWPORT_WIDTH and 0 <= rel_y < VIEWPORT_HEIGHT: other_player_positions.append((rel_x, rel_y)) # Build a list of (relative_x, relative_y) for alive mobs mob_positions = [] for mob in mobs: if not mob.alive: continue dx = mob.x - player.x dy = mob.y - player.y if dx > world.width // 2: dx -= world.width elif dx < -(world.width // 2): dx += world.width if dy > world.height // 2: dy -= world.height elif dy < -(world.height // 2): dy += world.height rel_x = dx + center_x rel_y = dy + center_y if 0 <= rel_x < VIEWPORT_WIDTH and 0 <= rel_y < VIEWPORT_HEIGHT: mob_positions.append((rel_x, rel_y)) # Build the output with ANSI coloring # priority: player @ > other players * > mobs * > effects > terrain half_width = VIEWPORT_WIDTH // 2 half_height = VIEWPORT_HEIGHT // 2 output_lines = [] for y, row in enumerate(viewport): line = [] for x, tile in enumerate(row): # Check if this is the player's position if x == center_x and y == center_y: line.append(colorize_terrain("@", player.color_depth)) # Check if this is another player's position elif (x, y) in other_player_positions or (x, y) in mob_positions: line.append(colorize_terrain("*", player.color_depth)) else: # Check for active effects at this world position world_x, world_y = world.wrap( player.x - half_width + x, player.y - half_height + y, ) effects = get_effects_at(world_x, world_y) if effects: # use the most recent effect e = effects[-1] line.append(f"{e.color}{e.char}{RESET}") else: line.append(colorize_terrain(tile, player.color_depth)) output_lines.append("".join(line)) # Send to player player.writer.write("\r\n".join(output_lines) + "\r\n") await player.writer.drain() # Register the look command with its alias register(CommandDefinition("look", cmd_look, aliases=["l"]))