mud/src/mudlib/commands/fly.py
Jared Miller 875ded5762
Migrate fly to use player.location (Zone)
Removed module-level world variable and replaced all world.wrap() calls
with player.location.wrap(). Added Zone assertion for type safety,
matching the pattern in movement.py. Updated tests to remove fly.world
injection since it's no longer needed.
2026-02-11 19:33:15 -05:00

91 lines
2.8 KiB
Python

"""Fly command for aerial movement across the world."""
from mudlib.commands import CommandDefinition, register
from mudlib.commands.movement import DIRECTIONS, send_nearby_message
from mudlib.effects import add_effect
from mudlib.player import Player
from mudlib.render.ansi import BOLD, BRIGHT_WHITE
from mudlib.zone import Zone
# how far you fly
FLY_DISTANCE = 5
# origin cloud gets the base ttl, each step toward destination adds stagger.
# trail visibly shrinks from origin toward the player, one tile at a time.
CLOUD_TTL = 1.5
CLOUD_STAGGER = 0.4 # seconds between each cloud's expiry
# ANSI color for cloud trails
CLOUD_COLOR = BOLD + BRIGHT_WHITE
async def cmd_fly(player: Player, args: str) -> None:
"""Toggle flying or move while airborne.
fly - toggle flying on/off
fly <dir> - move 5 tiles in a direction (must be flying)
"""
direction = args.strip().lower()
# no args = toggle flying state
if not direction:
if player.flying:
player.flying = False
player.writer.write("You land.\r\n")
await player.writer.drain()
await send_nearby_message(
player,
player.x,
player.y,
f"{player.name} lands.\r\n",
)
else:
player.flying = True
player.writer.write("You fly into the air!\r\n")
await player.writer.drain()
await send_nearby_message(
player,
player.x,
player.y,
f"{player.name} lifts into the air!\r\n",
)
return
# direction given but not flying
if not player.flying:
player.writer.write("You aren't flying.\r\n")
await player.writer.drain()
return
delta = DIRECTIONS.get(direction)
if delta is None:
player.writer.write(f"'{direction}' isn't a direction.\r\n")
await player.writer.drain()
return
zone = player.location
assert isinstance(zone, Zone), "Player must be in a zone to fly"
dx, dy = delta
start_x, start_y = player.x, player.y
# lay cloud trail at starting position and each intermediate step.
# origin cloud expires first, near-dest cloud lingers longest.
# the trail shrinks from behind toward the player over time.
for step in range(FLY_DISTANCE):
trail_x, trail_y = zone.wrap(start_x + dx * step, start_y + dy * step)
ttl = CLOUD_TTL + step * CLOUD_STAGGER
add_effect(trail_x, trail_y, "~", CLOUD_COLOR, ttl=ttl)
# move player to destination
dest_x, dest_y = zone.wrap(start_x + dx * FLY_DISTANCE, start_y + dy * FLY_DISTANCE)
player.x = dest_x
player.y = dest_y
# auto-look at new position
from mudlib.commands.look import cmd_look
await cmd_look(player, "")
register(CommandDefinition("fly", cmd_fly))