mud/src/mudlib/commands/look.py
Jared Miller 9844749edd
Add fly command with cloud trail effects
fly <direction> moves the player 5 tiles, ignoring terrain. Leaves
a trail of bright white ~ clouds that fade after 2 seconds. Effects
system supports arbitrary timed visual overlays on the viewport.
TinTin aliases: fn/fs/fe/fw/fne/fnw/fse/fsw.
2026-02-07 14:48:42 -05:00

92 lines
3 KiB
Python

"""Look command for viewing the world."""
from typing import Any
from mudlib.commands import 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("@"))
# Check if this is another player's position
elif (x, y) in other_player_positions:
line.append(colorize_terrain("*"))
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))
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("look", cmd_look, aliases=["l"])