mud/src/mudlib/commands/look.py
Jared Miller 7d4a75f973
Show portals in look output
Look command now displays portals separately from ground items.
Portals at the player's position are shown after ground items with
the format "Portals: name1, name2". This separates portals from
regular items since they serve a different purpose in gameplay.
2026-02-11 20:58:55 -05:00

128 lines
4.4 KiB
Python

"""Look command for viewing the world."""
from mudlib.commands import CommandDefinition, register
from mudlib.commands.things import _format_thing_name
from mudlib.effects import get_effects_at
from mudlib.entity import Entity
from mudlib.player import Player
from mudlib.render.ansi import RESET, colorize_terrain
from mudlib.thing import Thing
from mudlib.zone import Zone
# 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)
"""
zone = player.location
if zone is None or not isinstance(zone, Zone):
player.writer.write("You are nowhere.\r\n")
await player.writer.drain()
return
# Get the viewport from the zone
viewport = zone.get_viewport(player.x, player.y, VIEWPORT_WIDTH, VIEWPORT_HEIGHT)
# Calculate center position
center_x = VIEWPORT_WIDTH // 2
center_y = VIEWPORT_HEIGHT // 2
# Get nearby entities (players and mobs) from the zone
# Viewport half-diagonal distance for range
viewport_range = VIEWPORT_WIDTH // 2 + VIEWPORT_HEIGHT // 2
nearby = zone.contents_near(player.x, player.y, viewport_range)
# Build a list of (relative_x, relative_y) for other entities
entity_positions = []
for obj in nearby:
# Only show entities (players/mobs), not the current player
if not isinstance(obj, Entity) or obj is player:
continue
# Skip dead mobs
if hasattr(obj, "alive") and not obj.alive:
continue
# Calculate relative position (shortest path wrapping)
dx = obj.x - player.x
dy = obj.y - player.y
if zone.toroidal:
if dx > zone.width // 2:
dx -= zone.width
elif dx < -(zone.width // 2):
dx += zone.width
if dy > zone.height // 2:
dy -= zone.height
elif dy < -(zone.height // 2):
dy += zone.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:
entity_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 entity's position
elif (x, y) in entity_positions:
line.append(colorize_terrain("*", player.color_depth))
else:
# Check for active effects at this world position
world_x, world_y = zone.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")
# Show items on the ground at player's position
from mudlib.portal import Portal
contents_here = zone.contents_at(player.x, player.y)
ground_items = [
obj
for obj in contents_here
if isinstance(obj, Thing) and not isinstance(obj, Portal)
]
portals = [obj for obj in contents_here if isinstance(obj, Portal)]
if ground_items:
names = ", ".join(_format_thing_name(item) for item in ground_items)
player.writer.write(f"On the ground: {names}\r\n")
if portals:
names = ", ".join(p.name for p in portals)
player.writer.write(f"Portals: {names}\r\n")
await player.writer.drain()
# Register the look command with its alias
register(CommandDefinition("look", cmd_look, aliases=["l"]))