mud/src/mudlib/commands/look.py
Jared Miller b0fcb080d3
Wire client capabilities into Player & terrain
Parse MTTS from telnetlib3 writer during connection and store capabilities
on Player.caps field. Add convenience property Player.color_depth that
delegates to caps.color_depth for easy access by rendering code.

Changes:
- Add caps field to Player with default 16-color ANSI capabilities
- Parse MTTS in server shell after Player creation using parse_mtts()
- Add Player.color_depth property for quick capability checks
- Add tests verifying Player caps integration and color_depth property
2026-02-07 22:44:45 -05:00

92 lines
3.1 KiB
Python

"""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.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 the output with ANSI coloring
# priority: player @ > other players * > 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:
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"]))