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:
@@ -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">
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user