Lobby: dice sounds
Clatter per die and a result cue for checks, synthesized with the Web Audio API; mute button and volume slider next to the dice. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import PaperSheet, { ThemeSwitch } from './PaperSheet.jsx'
|
||||
import { sound, playRoll } from './sound.js'
|
||||
import { MapView, MapLibrary, TokenForm, WallLayer } from './MapView.jsx'
|
||||
|
||||
const J = { 'Content-Type': 'application/json' }
|
||||
@@ -303,6 +304,8 @@ function DieIcon({ sides }) {
|
||||
const MODES = [['mystery', 'Mystery', 'Players see grey dice, but not the result'], ['hidden', 'Hidden', 'Only you see the roll'], ['public', 'Public', 'Everybody sees the roll']]
|
||||
|
||||
function DiceBar({ to, me, isDm, count, setCount, mode, setMode, own, setOwn, onRoll, busy }) {
|
||||
const [snd, setSnd] = useState(sound.enabled)
|
||||
const [vol, setVol] = useState(sound.volume)
|
||||
return (
|
||||
<div className="dicebar" role="toolbar" aria-label="Dice">
|
||||
<label className="small muted dicecount">Dice
|
||||
@@ -318,6 +321,10 @@ function DiceBar({ to, me, isDm, count, setCount, mode, setMode, own, setOwn, on
|
||||
<label className="small ownswitch" title="You rolled real dice: type in the result instead of rolling here">
|
||||
<input type="checkbox" checked={own} onChange={(e) => setOwn(e.target.checked)} style={{ width: 'auto' }} /> Own dice
|
||||
</label>
|
||||
<span className="soundctl small" title="Dice sounds">
|
||||
<button className="ghost" aria-pressed={snd} aria-label={snd ? 'Mute dice sounds' : 'Turn dice sounds on'} onClick={() => { sound.setEnabled(!snd); setSnd(!snd); if (!snd) playRoll(1, 'success') }}>{snd ? '🔊' : '🔇'}</button>
|
||||
{snd && <input type="range" min="0" max="1" step="0.05" value={vol} aria-label="Dice sound volume" onChange={(e) => { setVol(+e.target.value); sound.setVolume(+e.target.value) }} onPointerUp={() => playRoll(1)} />}
|
||||
</span>
|
||||
{isDm && !to && (
|
||||
<span className="seg small" role="group" aria-label="Who sees the DM's rolls">
|
||||
{MODES.map(([k, label, tip]) => <button key={k} className={mode === k ? 'on' : 'ghost'} title={tip} onClick={() => setMode(k)}>{label}</button>)}
|
||||
@@ -599,6 +606,8 @@ function LobbyView({ id, rules, user }) {
|
||||
|
||||
const onRoll = useCallback((m) => {
|
||||
const key = `${m.id}`
|
||||
if (m.kind === 'check') playRoll(1, m.check.outcome)
|
||||
else if (m.kind === 'roll') playRoll(m.roll.n)
|
||||
setTray((t) => [...t.slice(-2), { ...m, key }])
|
||||
setTimeout(() => setTray((t) => t.filter((x) => x.key !== key)), 20000)
|
||||
}, [])
|
||||
|
||||
Reference in New Issue
Block a user