radio & sfx
This commit is contained in:
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { playSFX, updateSFXVolumes, SFX, type SFXName } from '../sfx-player'
|
||||
export { initSFXBus, sfxEmitter, triggerSFX } from '../sfx-bus'
|
||||
Reference in New Issue
Block a user