/** * AudioTriggerSystem.js * Spatial audio triggers - play sound once when player enters area */ class AudioTriggerSystem { constructor(scene) { this.scene = scene; // Active triggers this.triggers = new Map(); // Triggered history (to prevent re-triggering) this.triggered = new Set(); console.log('🔊 AudioTriggerSystem initialized'); } /** * Add a spatial audio trigger * @param {number} x - Grid X position * @param {number} y - Grid Y position * @param {string} audioKey - Audio file key * @param {object} options - Additional options */ addTrigger(x, y, audioKey, options = {}) { const triggerId = `${x},${y}`; const trigger = { x, y, audioKey, radius: options.radius || 0, // 0 = exact tile only volume: options.volume || 1.0, oneTime: options.oneTime !== false, // Default true delay: options.delay || 0, // Delay before playing (ms) callback: options.callback || null, // Optional callback after playing visualDebug: options.visualDebug || false }; this.triggers.set(triggerId, trigger); // Add visual debug marker if enabled if (trigger.visualDebug && this.scene.add) { const worldX = x * 48 + 24; const worldY = y * 48 + 24; const circle = this.scene.add.circle(worldX, worldY, 20, 0x00ff00, 0.3); circle.setDepth(1000); const text = this.scene.add.text(worldX, worldY - 30, '🔊', { fontSize: '20px', color: '#00ff00' }); text.setOrigin(0.5); text.setDepth(1001); } console.log(`✅ Audio trigger added at (${x}, ${y}): ${audioKey}`); return triggerId; } /** * Remove a trigger */ removeTrigger(triggerId) { this.triggers.delete(triggerId); this.triggered.delete(triggerId); } /** * Reset trigger (allow re-triggering) */ resetTrigger(triggerId) { this.triggered.delete(triggerId); } /** * Reset all triggers */ resetAll() { this.triggered.clear(); } /** * Check if player is in trigger zone */ checkTrigger(playerX, playerY) { const playerGridX = Math.floor(playerX / 48); const playerGridY = Math.floor(playerY / 48); this.triggers.forEach((trigger, triggerId) => { // Skip if already triggered and it's one-time only if (trigger.oneTime && this.triggered.has(triggerId)) { return; } // Check distance const dx = Math.abs(playerGridX - trigger.x); const dy = Math.abs(playerGridY - trigger.y); const distance = Math.sqrt(dx * dx + dy * dy); if (distance <= trigger.radius) { // TRIGGER! this.activateTrigger(trigger, triggerId); } }); } /** * Activate a trigger (play audio) */ activateTrigger(trigger, triggerId) { console.log(`🔊 TRIGGER ACTIVATED: ${triggerId} (${trigger.audioKey})`); // Mark as triggered this.triggered.add(triggerId); // Play audio after delay if (trigger.delay > 0) { this.scene.time.delayedCall(trigger.delay, () => { this.playAudio(trigger); }); } else { this.playAudio(trigger); } } /** * Play audio for trigger */ playAudio(trigger) { // Check if audio exists if (!this.scene.cache.audio.exists(trigger.audioKey)) { console.warn(`⚠️ Audio not found: ${trigger.audioKey}`); return; } // Play sound const sound = this.scene.sound.add(trigger.audioKey, { volume: trigger.volume }); sound.play(); // Visual feedback (optional) if (trigger.visualDebug) { const worldX = trigger.x * 48 + 24; const worldY = trigger.y * 48 + 24; const flash = this.scene.add.circle(worldX, worldY, 30, 0xffff00, 0.8); flash.setDepth(1002); this.scene.tweens.add({ targets: flash, alpha: 0, scale: 2, duration: 500, ease: 'Quad.Out', onComplete: () => flash.destroy() }); } // Callback if (trigger.callback) { trigger.callback(); } console.log(`✅ Audio played: ${trigger.audioKey}`); } /** * Update - called every frame */ update(playerX, playerY) { if (this.triggers.size === 0) return; this.checkTrigger(playerX, playerY); } }