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.
91 lines
2.8 KiB
Python
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))
|