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.
128 lines
4.4 KiB
Python
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"]))
|