"""Animation kit: deterministic pieces, and the bell theater resolving.""" from __future__ import annotations import random from scrypt.ui import fx def test_reduced_motion_reads_env(monkeypatch): monkeypatch.setenv("SCRYPT_REDUCED_MOTION", "1") assert fx.reduced_motion() monkeypatch.setenv("SCRYPT_REDUCED_MOTION", "0") assert not fx.reduced_motion() monkeypatch.delenv("SCRYPT_REDUCED_MOTION") assert not fx.reduced_motion() def test_noise_and_corrupt_keep_shape(): line = fx.noise_line(40, random.Random(1)) assert len(line) == 40 text = "the scale is honest" glitched = fx.corrupt(text, fraction=0.5, rng=random.Random(2)) assert len(glitched) == len(text) assert glitched != text def test_eye_tracks_blinks_and_widens(): assert fx.eye(0, 5) == fx.EYE_FRAMES[0] assert fx.eye(4, 5) == fx.EYE_FRAMES[-1] assert fx.eye(2, 5, blink=True) == fx.EYE_BLINK assert fx.eye(0, 1, wide=True) == fx.EYE_WIDE assert fx.eye(0, 1) == fx.EYE_FRAMES[2] # singleton hand: centered def test_boardfx_empty(): assert fx.BoardFX().empty assert not fx.BoardFX(flash={("foe", 1)}).empty assert not fx.BoardFX(scale=2).empty async def test_bell_theater_resolves_combat(monkeypatch): """With frames collapsed to zero delay, the theater path must reach the same end state as the instant path: bell rung, fx cleared, input back.""" from scrypt.app import ScryptApp from scrypt.ui.board import BoardScreen async def instant_frame(self, dt): self.refresh_all() async def no_glitch(self): pass monkeypatch.setenv("SCRYPT_REDUCED_MOTION", "0") monkeypatch.setattr(BoardScreen, "_frame", instant_frame) monkeypatch.setattr(BoardScreen, "_glitch", no_glitch) app = ScryptApp(seed=11, skip_menu=True) async with app.run_test() as pilot: await pilot.pause() screen = app.screen assert isinstance(screen, BoardScreen) state = screen.state await pilot.press("s") # draw a bit turn_before = state.turn await pilot.press("b") # ring the bell -> theater worker for _ in range(40): await pilot.pause() if not screen._animating and state.turn > turn_before: break assert state.turn > turn_before assert screen._fx.empty assert not screen._animating