From e5604cde8e99a1ed1881654fd2b1b2aa1e1ad2cd Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 23 Jul 2026 01:14:08 -0400 Subject: [PATCH] Add step-through replay controls to the battle view Prev/next/play-pause/restart and a step counter let a player walk the battle log manually and replay it before hitting Next. Local-only view state; nothing is shared or sent to the server. --- web/src/components/BattlePhase.tsx | 57 ++++++++++++++++++++++++++---- web/src/styles.css | 15 ++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/web/src/components/BattlePhase.tsx b/web/src/components/BattlePhase.tsx index 31271f8..6da84f9 100644 --- a/web/src/components/BattlePhase.tsx +++ b/web/src/components/BattlePhase.tsx @@ -180,12 +180,25 @@ export function BattlePhase({ view, send }: Props) { const done = step >= events.length const lastEvent = step > 0 ? events[step - 1] : null + // paused freezes auto-advance so the player can walk the log manually. + const [paused, setPaused] = useState(false) + useEffect(() => { - if (done) return + if (done || paused) return const delay = EVENT_MS[events[step].type] ?? 1000 const t = window.setTimeout(() => setStep((s) => s + 1), delay) return () => window.clearTimeout(t) - }, [step, done, events]) + }, [step, done, paused, events]) + + // Manual controls. Stepping by hand pauses playback so it doesn't fight you. + const stepTo = (n: number) => { + setPaused(true) + setStep(Math.max(0, Math.min(events.length, n))) + } + const restart = () => { + setPaused(false) + setStep(0) + } const sides = useMemo( () => replay(events, battle.stackSizes, step), @@ -318,11 +331,43 @@ export function BattlePhase({ view, send }: Props) {

Battle! Round {battle.round}

- {!done && ( - - )} + + + + + {Math.min(step, events.length)} / {events.length} + + {!done && ( + + )} +
diff --git a/web/src/styles.css b/web/src/styles.css index 008e175..2bf0b76 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -726,6 +726,21 @@ h3 { justify-content: center; align-items: center; gap: 14px; + flex-wrap: wrap; +} + +.battle-controls { + display: flex; + align-items: center; + gap: 6px; +} + +.battle-step { + font-family: var(--font-display); + font-size: 0.85rem; + color: rgba(253, 243, 220, 0.7); + min-width: 3.5em; + text-align: center; } .battle-names {