Stagger cloud trail dissolution so tiles fade one at a time

Each cloud in the trail gets a slightly longer TTL than the one
before it (0.15s stagger). The origin cloud dissolves first, then
each subsequent tile follows. Two consecutive flights produce a
trail where the oldest clouds are already gone.
This commit is contained in:
Jared Miller 2026-02-07 14:17:48 -05:00
parent 93ad4523e2
commit a10f3d4e70
Signed by: shmup
GPG key ID: 22B5C6D66A38B06C
2 changed files with 19 additions and 9 deletions

View file

@ -14,8 +14,10 @@ world: Any = None
# how far you fly # how far you fly
FLY_DISTANCE = 5 FLY_DISTANCE = 5
# how long cloud trail lingers (seconds) # origin cloud gets the base ttl, each step toward destination adds stagger.
CLOUD_TTL = 2.0 # 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 # ANSI color for cloud trails
CLOUD_COLOR = BOLD + BRIGHT_WHITE CLOUD_COLOR = BOLD + BRIGHT_WHITE
@ -68,10 +70,13 @@ async def cmd_fly(player: Player, args: str) -> None:
dx, dy = delta dx, dy = delta
start_x, start_y = player.x, player.y start_x, start_y = player.x, player.y
# lay cloud trail at starting position and each intermediate step # 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): for step in range(FLY_DISTANCE):
trail_x, trail_y = world.wrap(start_x + dx * step, start_y + dy * step) trail_x, trail_y = world.wrap(start_x + dx * step, start_y + dy * step)
add_effect(trail_x, trail_y, "~", CLOUD_COLOR, ttl=CLOUD_TTL) ttl = CLOUD_TTL + step * CLOUD_STAGGER
add_effect(trail_x, trail_y, "~", CLOUD_COLOR, ttl=ttl)
# move player to destination # move player to destination
dest_x, dest_y = world.wrap( dest_x, dest_y = world.wrap(

View file

@ -219,17 +219,22 @@ async def test_cloud_trail_chars_are_tilde(player):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_cloud_trail_has_ttl(player): async def test_cloud_trail_dissolves_from_origin(player):
"""Cloud effects should expire after roughly 2 seconds.""" """Origin cloud expires first, trail shrinks toward player."""
players[player.name] = player players[player.name] = player
player.flying = True player.flying = True
before = time.monotonic() before = time.monotonic()
await fly.cmd_fly(player, "east") await fly.cmd_fly(player, "east")
for e in active_effects: # effects are in path order (step 0..4)
remaining = e.expires_at - before expiries = [e.expires_at for e in active_effects]
assert 1.5 <= remaining <= 2.5 # each should expire after the previous one (origin fades first)
for i in range(1, len(expiries)):
assert expiries[i] > expiries[i - 1]
# origin cloud ~1.5s, near-dest cloud ~1.5 + 4*0.4 = ~3.1s
assert 1.3 <= expiries[0] - before <= 1.7
assert 2.9 <= expiries[-1] - before <= 3.3
@pytest.mark.asyncio @pytest.mark.asyncio