radio & sfx

This commit is contained in:
wass08
2026-02-12 08:57:37 +09:00
parent ffef71bb02
commit be7e86975e
23 changed files with 628 additions and 16 deletions
+43
View File
@@ -0,0 +1,43 @@
import mitt from 'mitt'
import { playSFX } from './sfx-player'
/**
* SFX-specific events that tools can trigger
*/
type SFXEvents = {
'sfx:grid-snap': undefined
'sfx:item-delete': undefined
'sfx:item-pick': undefined
'sfx:item-place': undefined
'sfx:structure-build': undefined
'sfx:structure-delete': undefined
}
/**
* Dedicated event emitter for SFX
* Tools should use this to trigger sound effects
*/
export const sfxEmitter = mitt<SFXEvents>()
/**
* Initialize SFX Bus - connects SFX events to actual sound playback
* Call once in your app initialization
*/
export function initSFXBus() {
// Map SFX events to sound playback
sfxEmitter.on('sfx:grid-snap', () => playSFX('gridSnap'))
sfxEmitter.on('sfx:item-delete', () => playSFX('itemDelete'))
sfxEmitter.on('sfx:item-pick', () => playSFX('itemPick'))
sfxEmitter.on('sfx:item-place', () => playSFX('itemPlace'))
sfxEmitter.on('sfx:structure-build', () => playSFX('structureBuild'))
sfxEmitter.on('sfx:structure-delete', () => playSFX('structureDelete'))
}
/**
* Helper function to trigger SFX events from tools
* @example
* triggerSFX('sfx:item-place')
*/
export function triggerSFX(event: keyof SFXEvents) {
sfxEmitter.emit(event)
}
+59
View File
@@ -0,0 +1,59 @@
import { Howl } from 'howler'
import useAudio from '@/store/use-audio'
// SFX sound definitions
export const SFX = {
gridSnap: '/audios/sfx/grid_snap.mp3',
itemDelete: '/audios/sfx/item_delete.mp3',
itemPick: '/audios/sfx/item_pick.mp3',
itemPlace: '/audios/sfx/item_place.mp3',
structureBuild: '/audios/sfx/structure_build.mp3',
structureDelete: '/audios/sfx/structure_delete.mp3',
} as const
export type SFXName = keyof typeof SFX
// Preload all SFX sounds
const sfxCache = new Map<SFXName, Howl>()
// Initialize all sounds
Object.entries(SFX).forEach(([name, path]) => {
const sound = new Howl({
src: [path],
preload: true,
volume: 0.5, // Will be adjusted by the bus
})
sfxCache.set(name as SFXName, sound)
})
/**
* Play a sound effect with volume based on audio settings
*/
export function playSFX(name: SFXName) {
const sound = sfxCache.get(name)
if (!sound) {
console.warn(`SFX not found: ${name}`)
return
}
const { masterVolume, sfxVolume, muted } = useAudio.getState()
if (muted) return
// Calculate final volume (masterVolume and sfxVolume are 0-100)
const finalVolume = (masterVolume / 100) * (sfxVolume / 100)
sound.volume(finalVolume)
sound.play()
}
/**
* Update all cached SFX volumes (useful when settings change)
*/
export function updateSFXVolumes() {
const { masterVolume, sfxVolume } = useAudio.getState()
const finalVolume = (masterVolume / 100) * (sfxVolume / 100)
sfxCache.forEach((sound) => {
sound.volume(finalVolume)
})
}
+2
View File
@@ -0,0 +1,2 @@
export { playSFX, updateSFXVolumes, SFX, type SFXName } from '../sfx-player'
export { initSFXBus, sfxEmitter, triggerSFX } from '../sfx-bus'