export class SensoryDiceRoller { constructor(onRoll) { this.onRoll = onRoll; this.samples = []; this.isSampling = false; this.timer = null; this.init(); } mulberry32(a) { return function() { let t = a += 0x6D2B79F5; t = Math.imul(t ^ t >>> 15, t | 1); t ^= t + Math.imul(t ^ t >>> 7, t | 61); return ((t ^ t >>> 14) >>> 0) / 4294967296; }; } init() { if (window.DeviceMotionEvent) { window.addEventListener('devicemotion', (e) => this.handleMotion(e)); } window.addEventListener('mousemove', (e) => this.handleMouse(e)); } startSequence() { this.samples = []; this.isSampling = true; this.timer = setTimeout(() => { if (this.isSampling) { this.triggerRoll(Math.floor(Math.random() * 0xFFFFFFFF)); } }, 5000); } handleMotion(e) { if (!this.isSampling) return; const { x, y, z } = e.accelerationIncludingGravity || {}; if (x && y && z) { this.samples.push(Math.sqrt(x*x + y*y + z*z)); this.check(); } } handleMouse(e) { if (!this.isSampling) return; const v = Math.sqrt(e.movementX**2 + e.movementY**2); this.samples.push(v); this.check(); } check() { if (this.samples.length >= 30) { clearTimeout(this.timer); const seed = this.samples.reduce((a, b) => a + Math.floor(b * 1000), 0); this.triggerRoll(seed); } } triggerRoll(seed) { this.isSampling = false; const rng = this.mulberry32(seed); const result = Math.floor(rng() * 20) + 1; this.onRoll(result); } }