Improve dice rolls.

This commit is contained in:
Greyson Parrelli
2026-07-23 10:26:15 -04:00
parent 44f8bcfcab
commit 7bb20e5d2d
6 changed files with 173 additions and 184 deletions
+51
View File
@@ -0,0 +1,51 @@
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.
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 }: { dice: number[]; side: 'left' | 'right' }) {
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)
const stop = window.setTimeout(() => {
window.clearInterval(flicker)
setDisplay(dice)
setSettled(true)
}, ROLL_MS)
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>
)
}