mud/src/mudlib/commands/look.py
Jared Miller 64dcd8d6e4
Add spawn command and wire mobs into server
Phase 6: spawn command creates mobs at player position from loaded
templates. Server loads mob templates from content/mobs/ at startup,
injects world into combat/commands module, and runs process_mobs()
each game loop tick after process_combat().
2026-02-08 23:07:42 -05:00

115 lines
3.8 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.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"]))