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.
This commit is contained in:
Greyson Parrelli
2026-07-23 01:14:08 -04:00
parent 7eb4812f86
commit e5604cde8e
2 changed files with 66 additions and 6 deletions
+51 -6
View File
@@ -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) {
<div className="battle">
<div className="battle-header">
<h2>Battle! Round {battle.round}</h2>
{!done && (
<button className="btn btn-ghost btn-sm" onClick={() => setStep(events.length)}>
Skip
<div className="battle-controls">
<button className="btn btn-ghost btn-sm" onClick={restart} title="Replay from the start">
</button>
)}
<button
className="btn btn-ghost btn-sm"
onClick={() => stepTo(step - 1)}
disabled={step === 0}
title="Previous step"
>
</button>
<button
className="btn btn-ghost btn-sm"
onClick={() => setPaused((p) => !p)}
disabled={done}
title={paused ? 'Play' : 'Pause'}
>
{paused ? '▶' : '⏸'}
</button>
<button
className="btn btn-ghost btn-sm"
onClick={() => stepTo(step + 1)}
disabled={done}
title="Next step"
>
</button>
<span className="battle-step">
{Math.min(step, events.length)} / {events.length}
</span>
{!done && (
<button className="btn btn-ghost btn-sm" onClick={() => setStep(events.length)}>
Skip
</button>
)}
</div>
</div>
<div className="battle-names">
+15
View File
@@ -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 {