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