rts-commander / static /sounds.js
Luigi's picture
deploy(web): full clean snapshot with app code and assets
12d64f8
raw
history blame
4.34 kB
/**
* Sound Manager for RTS Game
* Handles loading and playing sound effects
*
* Features:
* - Async sound loading
* - Volume control
* - Enable/disable toggle
* - Multiple concurrent sounds
* - Fallback for unsupported formats
*/
class SoundManager {
constructor() {
this.sounds = {};
this.enabled = true;
this.volume = 0.5;
this.loaded = false;
this.audioContext = null;
// Initialize Audio Context (for better browser support)
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {
console.warn('[SoundManager] Web Audio API not supported, using HTML5 Audio fallback');
}
}
/**
* Load all game sounds
*/
async loadAll() {
console.log('[SoundManager] Loading sounds...');
const soundFiles = {
'unit_fire': '/static/sounds/fire.wav',
'explosion': '/static/sounds/explosion.wav',
'build_complete': '/static/sounds/build.wav',
'unit_ready': '/static/sounds/ready.wav',
};
const loadPromises = Object.entries(soundFiles).map(([name, url]) =>
this.loadSound(name, url)
);
await Promise.all(loadPromises);
this.loaded = true;
console.log(`[SoundManager] Loaded ${Object.keys(this.sounds).length} sounds`);
}
/**
* Load a single sound file
*/
async loadSound(name, url) {
try {
const audio = new Audio(url);
audio.preload = 'auto';
audio.volume = this.volume;
// Wait for audio to be ready
await new Promise((resolve, reject) => {
audio.addEventListener('canplaythrough', resolve, { once: true });
audio.addEventListener('error', reject, { once: true });
audio.load();
});
this.sounds[name] = audio;
console.log(`[SoundManager] Loaded: ${name}`);
} catch (e) {
console.warn(`[SoundManager] Failed to load sound: ${name}`, e);
}
}
/**
* Play a sound effect
* @param {string} name - Sound name
* @param {number} volumeMultiplier - Optional volume multiplier (0.0-1.0)
*/
play(name, volumeMultiplier = 1.0) {
if (!this.enabled || !this.loaded || !this.sounds[name]) {
return;
}
try {
// Clone the audio element to allow multiple concurrent plays
const audio = this.sounds[name].cloneNode();
audio.volume = this.volume * volumeMultiplier;
// Play and auto-cleanup
audio.play().catch(e => {
// Ignore play errors (user interaction required, etc.)
console.debug(`[SoundManager] Play failed: ${name}`, e.message);
});
// Remove after playing to free memory
audio.addEventListener('ended', () => {
audio.src = '';
audio.remove();
});
} catch (e) {
console.debug(`[SoundManager] Error playing sound: ${name}`, e);
}
}
/**
* Toggle sound on/off
*/
toggle() {
this.enabled = !this.enabled;
console.log(`[SoundManager] Sound ${this.enabled ? 'enabled' : 'disabled'}`);
return this.enabled;
}
/**
* Set volume (0.0 - 1.0)
*/
setVolume(volume) {
this.volume = Math.max(0, Math.min(1, volume));
// Update volume for all loaded sounds
Object.values(this.sounds).forEach(audio => {
audio.volume = this.volume;
});
console.log(`[SoundManager] Volume set to ${Math.round(this.volume * 100)}%`);
}
/**
* Check if sounds are loaded
*/
isLoaded() {
return this.loaded;
}
/**
* Get sound status
*/
getStatus() {
return {
enabled: this.enabled,
volume: this.volume,
loaded: this.loaded,
soundCount: Object.keys(this.sounds).length,
};
}
}
// Export for use in game.js
window.SoundManager = SoundManager;