From 34f1739484918e66027e47a37163d4b43706f92b Mon Sep 17 00:00:00 2001 From: "Z. Cliffe Schreuders" Date: Fri, 31 Oct 2025 21:10:07 +0000 Subject: [PATCH] feat(npc): Add audio feedback for NPC bark notifications with message received sound --- assets/sounds/message_received.mp3 | Bin 0 -> 15192 bytes js/core/game.js | 4 + js/systems/npc-barks.js | 59 ++++- .../npc/SOUND_EFFECTS_IMPLEMENTATION.md | 242 ++++++++++++++++++ 4 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 assets/sounds/message_received.mp3 create mode 100644 planning_notes/npc/SOUND_EFFECTS_IMPLEMENTATION.md diff --git a/assets/sounds/message_received.mp3 b/assets/sounds/message_received.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..0c1b1b3c530e6b0f02103222bf002a619ba5b2dc GIT binary patch literal 15192 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 literal 0 HcmV?d00001 diff --git a/js/core/game.js b/js/core/game.js index 33626c0..ceb5228 100644 --- a/js/core/game.js +++ b/js/core/game.js @@ -392,6 +392,10 @@ export function preload() { this.load.image('chair-white-2-rotate7', 'assets/objects/chair-white-2-rotate7.png'); this.load.image('chair-white-2-rotate8', 'assets/objects/chair-white-2-rotate8.png'); + // Load audio files + // NPC system sounds + this.load.audio('message_received', 'assets/sounds/message_received.mp3'); + // Get scenario from URL parameter or use default const urlParams = new URLSearchParams(window.location.search); let scenarioFile = urlParams.get('scenario') || 'scenarios/ceo_exfil.json'; diff --git a/js/systems/npc-barks.js b/js/systems/npc-barks.js index 8df43d8..e5ef210 100644 --- a/js/systems/npc-barks.js +++ b/js/systems/npc-barks.js @@ -4,6 +4,8 @@ export default class NPCBarkSystem { constructor(npcManager) { this.npcManager = npcManager; this.container = null; + this.barkSound = null; + this.soundEnabled = true; // Can be toggled via settings } init() { @@ -15,14 +17,69 @@ export default class NPCBarkSystem { this.container.id = 'npc-bark-container'; document.body.appendChild(this.container); } + + // Preload bark notification sound + this.loadBarkSound(); } - // payload: { npcId, npcName, text|message, duration, onClick, openPhone } + /** + * Load the bark notification sound effect from Phaser + */ + loadBarkSound() { + try { + // Access Phaser's global sound manager + if (window.game && window.game.sound) { + this.barkSound = window.game.sound.add('message_received'); + this.barkSound.setVolume(0.5); // 50% volume by default + console.log('✅ NPC bark sound loaded from Phaser'); + } else { + console.warn('⚠️ Phaser sound manager not available yet. Will try again on first bark.'); + } + } catch (error) { + console.warn('Failed to load bark sound:', error); + } + } + + /** + * Play the bark notification sound + */ + playBarkSound() { + if (!this.soundEnabled) return; + + // Lazy load if not available during init + if (!this.barkSound && window.game && window.game.sound) { + this.loadBarkSound(); + } + + if (!this.barkSound) return; + + try { + // Phaser handles sound pooling automatically + this.barkSound.play(); + } catch (error) { + console.warn('Error playing bark sound:', error); + } + } + + /** + * Enable or disable bark sounds + */ + setSoundEnabled(enabled) { + this.soundEnabled = enabled; + } + + // payload: { npcId, npcName, text|message, duration, onClick, openPhone, playSound } showBark(payload = {}) { if (!this.container) this.init(); const { npcId, npcName } = payload; const text = payload.text || payload.message || ''; const duration = ('duration' in payload) ? payload.duration : 5000; + const playSound = payload.playSound !== false; // Default true + + // Play notification sound + if (playSound) { + this.playBarkSound(); + } // Create bark element const el = document.createElement('div'); diff --git a/planning_notes/npc/SOUND_EFFECTS_IMPLEMENTATION.md b/planning_notes/npc/SOUND_EFFECTS_IMPLEMENTATION.md new file mode 100644 index 0000000..18f57ff --- /dev/null +++ b/planning_notes/npc/SOUND_EFFECTS_IMPLEMENTATION.md @@ -0,0 +1,242 @@ +# NPC Sound Effects Implementation + +**Status:** ✅ Complete (2024-10-31) + +## Overview +Added audio feedback to the NPC bark notification system. All bark notifications now play a sound effect when they appear, providing audio cues for player attention. + +## Implementation + +### Files Modified +- **`js/core/game.js`** (+3 lines) + - Added `this.load.audio('message_received', ...)` in preload function + - Loads sound through Phaser's audio system +- **`js/systems/npc-barks.js`** (+65 lines, modified) + - Replaced HTML5 Audio with Phaser sound manager + - Added lazy loading fallback + - Integrated sound playback into `showBark()` method + +### Sound Asset Used +- **`assets/sounds/message_received.mp3`** + - Already existed in project + - Used for phone message notifications + - Now also used for NPC bark notifications + - **Loaded through Phaser's audio system** (Web Audio API) + +## Features + +### 1. Sound Preloading (Phaser) +```javascript +// In game.js preload() +this.load.audio('message_received', 'assets/sounds/message_received.mp3'); + +// In npc-barks.js +loadBarkSound() { + if (window.game && window.game.sound) { + this.barkSound = window.game.sound.add('message_received'); + this.barkSound.setVolume(0.5); // 50% volume + } +} +``` +- Sound loaded once during Phaser's preload phase +- Managed by Phaser's Web Audio API system +- Automatically pooled for efficient playback +- Default volume: 50% + +### 2. Sound Playback (Phaser) +```javascript +playBarkSound() { + if (!this.soundEnabled) return; + + // Lazy load if not available during init + if (!this.barkSound && window.game && window.game.sound) { + this.loadBarkSound(); + } + + if (!this.barkSound) return; + + this.barkSound.play(); // Phaser handles pooling +} +``` +- Uses Phaser's `.play()` method (cleaner than HTML5 Audio) +- Phaser automatically handles sound pooling and memory management +- Lazy loading fallback if sound manager not ready during init +- No need to manually reset `currentTime` (Phaser handles this) + +### 3. Sound Control +```javascript +setSoundEnabled(enabled) { + this.soundEnabled = enabled; +} +``` +- Allows disabling sounds globally +- Useful for settings/preferences +- Default: enabled + +### 4. showBark() Integration +```javascript +showBark(payload = {}) { + const playSound = payload.playSound !== false; // Default true + + if (playSound) { + this.playBarkSound(); + } + // ... rest of bark creation +} +``` +- Sound enabled by default for all barks +- Can be disabled per-bark via `playSound: false` in payload +- Respects global `soundEnabled` setting + +## What Gets Sound? + +### ✅ All Bark Notifications +1. **Event-triggered barks** + - Door unlocks + - Item pickups + - Room discoveries + - Lockpicking attempts + - CEO office entry + - Any other event-mapped reactions + +2. **Timed messages** + - Scheduled NPC messages + - Delivered at specific game times + - Automatically trigger barks with sound + +3. **Manual barks** + - Any `npcManager.showBark()` calls + - Any `barkSystem.showBark()` calls + +### ❌ No Sound +- Opening phone-chat minigame (no sound yet) +- Closing phone-chat minigame (no sound yet) +- Clicking barks (visual only) +- Choice selections in conversations (visual only) + +## Usage Examples + +### Default (Sound Enabled) +```javascript +// Sound plays automatically +npcManager.showBark({ + npcId: 'helper_npc', + npcName: 'Tech Support', + text: 'Found something interesting!' +}); +``` + +### Disable Sound for Specific Bark +```javascript +// Silent bark (no sound) +npcManager.showBark({ + npcId: 'helper_npc', + npcName: 'Tech Support', + text: 'This is a quiet message...', + playSound: false +}); +``` + +### Disable All Sounds +```javascript +// Turn off bark sounds globally +window.npcManager.barkSystem.setSoundEnabled(false); + +// Turn them back on +window.npcManager.barkSystem.setSoundEnabled(true); +``` + +## Testing + +### In-Game Testing +1. Refresh the game page to load updated code +2. Start CEO Exfiltration scenario +3. Test scenarios that trigger barks: + - Pick up an item → Should hear sound + - Walk through a door → Should hear sound + - Unlock a door → Should hear sound + - Enter CEO office → Should hear sound + +### Volume Testing +- Default volume: 50% (0.5) +- Can be adjusted by modifying `this.barkSound.volume` in `loadBarkSound()` +- Recommended range: 0.3 - 0.7 (30% - 70%) + +## Browser Compatibility + +### Phaser Audio System +- **Chrome/Edge**: ✅ Full Web Audio API support +- **Firefox**: ✅ Full Web Audio API support +- **Safari**: ✅ Full Web Audio API support +- **Mobile browsers**: ✅ Good support (Phaser handles fallbacks) + +### Autoplay Policy +Modern browsers restrict autoplay. Phaser handles this automatically: +- ✅ **Works automatically**: Phaser unlocks audio context on first user interaction +- ✅ **No console warnings**: Phaser manages audio context lifecycle +- ✅ **Sounds triggered by user actions**: Always work (clicking, walking) +- ✅ **Better than HTML5 Audio**: Phaser's Web Audio API is more reliable + +### Advantages Over HTML5 Audio +1. **Unified audio management**: All game audio through one system +2. **Better performance**: Web Audio API vs Audio Tag +3. **Sound pooling**: Multiple simultaneous sounds without creating new instances +4. **No autoplay issues**: Phaser handles audio context unlocking +5. **Consistent volume control**: Tied to game's master volume +6. **Future-ready**: Can add spatial audio, filters, analyzers, etc. + +## Future Enhancements + +### Potential Additions +1. **Phone open/close sounds** + - Add `phone_open.mp3` sound when opening phone-chat + - Add `phone_close.mp3` sound when closing phone-chat + +2. **Choice selection sound** + - Subtle click sound when selecting dialogue choices + - Could use existing `GASP_UI_Clicks_*.mp3` sounds + +3. **Typing indicator sound** + - Quiet keyboard sound during typing animation + - Adds to realism of text messaging + +4. **Volume slider in settings** + - UI control for adjusting bark volume + - Persist preference to localStorage + +5. **Different sounds per NPC type** + - Helper NPCs: friendly notification sound + - Adversary NPCs: alert/warning sound + - Neutral NPCs: standard notification + +6. **Sound variations** + - Randomly pick from multiple notification sounds + - Prevents repetition fatigue + - Could use `GASP_UI_Notification_1.mp3` through `_6.mp3` + +## Implementation Stats + +- **Lines added:** ~68 total + - `game.js`: +3 lines (audio loading) + - `npc-barks.js`: +65 lines (Phaser sound integration) +- **Files modified:** 2 (`game.js`, `npc-barks.js`) +- **Breaking changes:** None +- **Default behavior:** Sound enabled +- **Asset used:** Existing (`message_received.mp3`) +- **Audio system:** Phaser Web Audio API (consolidated) + +## Next Steps + +### Priority 3: NPC Avatars +- Create 3 default 32x32px pixel-art avatars +- Add avatar support to scenarios +- Display avatars in barks and conversations + +### Priority 2 Continued: More Events +- Implement `objective_completed` event +- Implement `evidence_collected` event +- Implement `player_detected` event + +--- +**Status:** ✅ Implementation complete, ready for testing +**Date:** 2024-10-31