'use client'; import { useRef, useState, useCallback, useEffect } from 'react'; import * as THREE from 'three'; import { initScene, Die, DieInstance, setDice, stopRenderLoop, initRenderLoop, getW, getD, } from '../lib/dicePhysics'; export interface RollResult { sides: number; value: number; } export function useDiceRoller() { const mountRef = useRef(null); const [isReady, setIsReady] = useState(false); const [isRolling, setIsRolling] = useState(false); const sceneInitRef = useRef(false); useEffect(() => { if (!mountRef.current || sceneInitRef.current) return; sceneInitRef.current = true; initScene(mountRef.current); initRenderLoop(); const timer = setTimeout(() => setIsReady(true), 300); return () => { clearTimeout(timer); stopRenderLoop(); }; }, []); const rollDice = useCallback( (pool: Record): Promise => { return new Promise((resolve) => { const totalDice = Object.values(pool).reduce((a, b) => a + b, 0); if (totalDice === 0) { resolve([]); return; } setIsRolling(true); const newDice: DieInstance[] = []; const W = getW(); const D = getD(); let idx = 0; const order = [20, 12, 10, 8, 6, 4]; order.forEach((sides) => { const count = pool[sides] || 0; for (let i = 0; i < count; i++) { const die = new Die(sides); const side = idx % 2 === 0 ? -1 : 1; const x = side * (W - 0.8); const z = D - 1 - (idx % 3) * 1.1; const dir = new THREE.Vector3( -side * (9 + Math.random() * 4), 0.5, -(6 + Math.random() * 4) ); die.throwFrom(x, z, dir); die.pos.x -= side * idx * 0.15; newDice.push(die); idx++; } }); setDice(newDice, (completedDice) => { const results: RollResult[] = completedDice.map((d) => ({ sides: d.sides, value: d.value ?? 0, })); setIsRolling(false); resolve(results); }); }); }, [] ); return { mountRef, rollDice, isReady, isRolling }; }