Flourishes play on the reel too

Replay steps carry the same events the live table saw, so the spells
flare on catch-up reels and whole-game replays alike — retroactively:
stored games re-derive their events on replay, so every fireball ever
thrown bursts again. Step changes clear pending effects so scrubbing
backward does not smear.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 23:55:01 -04:00
co-authored by Claude Fable 5
parent bca34ae9e4
commit 70d7b4babb
+17 -1
View File
@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
import Board from "./Board.svelte"; import Board from "./Board.svelte";
import { humanize } from "./net.svelte"; import { humanize } from "./net.svelte";
import { fxForEvents, fxTtl, type BoardFx } from "./fx";
import type { GameEvent, GameView } from "@wizwar/engine"; import type { GameEvent, GameView } from "@wizwar/engine";
let { let {
@@ -20,6 +21,21 @@
); );
const atEnd = $derived(idx >= steps.length - 1); const atEnd = $derived(idx >= steps.length - 1);
/** Each step's spells flare on the reel exactly as they did at the table. */
let boardFx = $state<BoardFx[]>([]);
$effect(() => {
const step = steps[Math.min(idx, steps.length - 1)];
if (!step) return;
const timers: ReturnType<typeof setTimeout>[] = [];
for (const { fx, delay } of fxForEvents(step.events, step.view)) {
timers.push(setTimeout(() => {
boardFx = [...boardFx, fx];
timers.push(setTimeout(() => (boardFx = boardFx.filter((f) => f.id !== fx.id)), fxTtl(fx.kind)));
}, delay));
}
return () => { timers.forEach(clearTimeout); boardFx = []; };
});
$effect(() => { $effect(() => {
if (!playing) return; if (!playing) return;
const t = setInterval(() => { const t = setInterval(() => {
@@ -47,7 +63,7 @@
<button class="replay-skip" onclick={onclose}>{atEnd ? "back to the game" : "skip to now"}</button> <button class="replay-skip" onclick={onclose}>{atEnd ? "back to the game" : "skip to now"}</button>
</header> </header>
<div class="replay-board"> <div class="replay-board">
<Board view={step.view} /> <Board view={step.view} effects={boardFx} />
</div> </div>
<div class="replay-caption"> <div class="replay-caption">
<strong>{step.actor}</strong> <strong>{step.actor}</strong>