61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
import { useEffect, useState } from 'react'
|
||
import type { CSSProperties } from 'react'
|
||
|
||
// How long the dice scramble before settling, and how fast the faces flicker
|
||
// while they do. ROLL_MS is shared with BattlePhase so damage lands exactly
|
||
// when the dice settle. Both are the base (1×) durations; the battle speed
|
||
// multiplier divides them so the roll keeps pace with the rest of the replay.
|
||
export const ROLL_MS = 650
|
||
const TICK_MS = 70
|
||
|
||
// DiceRoll shows one rock die per rolled face in fixed slots under the
|
||
// thrower's side: they shake and flicker through faces in place, then settle
|
||
// on the values the server actually rolled. Faces carry rock icons (0, 1, or
|
||
// 2 rocks) — the same die the game uses, not a pip D6. Mount it with a `key`
|
||
// that changes per rock event so the scramble replays every time.
|
||
export function DiceRoll({
|
||
dice,
|
||
side,
|
||
speed = 1,
|
||
}: {
|
||
dice: number[]
|
||
side: 'top' | 'bottom'
|
||
speed?: number
|
||
}) {
|
||
const [display, setDisplay] = useState<number[]>(dice)
|
||
const [settled, setSettled] = useState(false)
|
||
|
||
useEffect(() => {
|
||
const flicker = window.setInterval(() => {
|
||
setDisplay(dice.map(() => Math.floor(Math.random() * 3)))
|
||
}, TICK_MS / speed)
|
||
const stop = window.setTimeout(() => {
|
||
window.clearInterval(flicker)
|
||
setDisplay(dice)
|
||
setSettled(true)
|
||
}, ROLL_MS / speed)
|
||
return () => {
|
||
window.clearInterval(flicker)
|
||
window.clearTimeout(stop)
|
||
}
|
||
// Runs once per mount; the caller remounts (via key) for each new roll.
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [])
|
||
|
||
return (
|
||
<div className={`dice-roll dice-roll-${side}`} aria-hidden>
|
||
{display.map((v, i) => (
|
||
<div
|
||
key={i}
|
||
className={`droll droll-n-${v} ${settled ? 'is-settled' : 'is-rolling'}`}
|
||
style={{ '--i': i } as CSSProperties}
|
||
>
|
||
{Array.from({ length: v }, (_, r) => (
|
||
<span key={r} className="droll-rock" />
|
||
))}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)
|
||
}
|