// Procedural audio using Web Audio API const Audio = { ctx: null, enabled: true, init() { try { this.ctx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) { this.enabled = false; } }, resume() { if (this.ctx && this.ctx.state === 'suspended') { this.ctx.resume(); } }, // Pistol shot - short, sharp playPistol() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); const noise = this.createNoise(0.08); osc.type = 'square'; osc.frequency.setValueAtTime(150, now); osc.frequency.exponentialRampToValueAtTime(50, now + 0.08); gain.gain.setValueAtTime(0.3, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.1); osc.connect(gain); gain.connect(ctx.destination); noise.connect(ctx.destination); osc.start(now); osc.stop(now + 0.1); }, // Shotgun blast - loud, wide playShotgun() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const noise = this.createNoise(0.2); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(100, now); osc.frequency.exponentialRampToValueAtTime(30, now + 0.15); gain.gain.setValueAtTime(0.4, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.2); osc.connect(gain); gain.connect(ctx.destination); noise.connect(ctx.destination); osc.start(now); osc.stop(now + 0.2); }, // Plasma shot - sci-fi energy playPlasma() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const osc2 = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(800, now); osc.frequency.exponentialRampToValueAtTime(200, now + 0.15); osc2.type = 'sine'; osc2.frequency.setValueAtTime(1200, now); osc2.frequency.exponentialRampToValueAtTime(300, now + 0.12); gain.gain.setValueAtTime(0.2, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.15); osc.connect(gain); osc2.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.15); osc2.start(now); osc2.stop(now + 0.15); }, // Enemy hurt playEnemyHurt() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(200, now); osc.frequency.exponentialRampToValueAtTime(80, now + 0.15); gain.gain.setValueAtTime(0.15, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.15); osc.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.15); }, // Enemy death playEnemyDeath() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(300, now); osc.frequency.exponentialRampToValueAtTime(30, now + 0.4); gain.gain.setValueAtTime(0.2, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.4); osc.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.4); }, // Player hurt playPlayerHurt() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'square'; osc.frequency.setValueAtTime(120, now); osc.frequency.exponentialRampToValueAtTime(60, now + 0.2); gain.gain.setValueAtTime(0.2, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.2); osc.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.2); }, // Pickup sound playPickup() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(400, now); osc.frequency.setValueAtTime(600, now + 0.05); osc.frequency.setValueAtTime(800, now + 0.1); gain.gain.setValueAtTime(0.15, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.15); osc.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.15); }, // Door open playDoorOpen() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'triangle'; osc.frequency.setValueAtTime(80, now); osc.frequency.linearRampToValueAtTime(120, now + 0.3); gain.gain.setValueAtTime(0.1, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.3); osc.connect(gain); gain.connect(ctx.destination); osc.start(now); osc.stop(now + 0.3); }, // Explosion playExplosion() { if (!this.enabled || !this.ctx) return; const ctx = this.ctx; const now = ctx.currentTime; const noise = this.createNoise(0.4); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(60, now); osc.frequency.exponentialRampToValueAtTime(20, now + 0.4); gain.gain.setValueAtTime(0.3, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.4); osc.connect(gain); gain.connect(ctx.destination); noise.connect(ctx.destination); osc.start(now); osc.stop(now + 0.4); }, createNoise(duration) { const ctx = this.ctx; const bufferSize = ctx.sampleRate * duration; const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate); const data = buffer.getChannelData(0); for (let i = 0; i < bufferSize; i++) { data[i] = (Math.random() * 2 - 1) * Math.max(0, 1 - i / bufferSize); } const source = ctx.createBufferSource(); source.buffer = buffer; const gain = ctx.createGain(); gain.gain.value = 0.15; source.connect(gain); source.start(); return gain; } };