/** * AUDIO MANAGER - Centralized Audio System * Handles all music, voiceover, and SFX playback * Includes DEBUG MODE for tracking what plays where */ class AudioManager { constructor() { this.scene = null; this.debugMode = true; // SET TO FALSE TO DISABLE LOGGING // đŸŽĨ STREAMER MODE (DMCA Protection) this.streamerMode = false; this.loadStreamerMode(); // Current playback tracking this.currentMusic = null; this.currentVoice = null; this.musicVolume = 0.7; this.voiceVolume = 0.8; this.sfxVolume = 0.5; // Copyright-safe tracks (CC BY 4.0) this.safeMusicTracks = [ 'intro_ambient', 'main_theme', 'farm_ambient', 'forest_ambient', 'night_theme', 'town_theme', 'wilderness_theme', 'combat_theme', 'ana_theme', 'raid_warning', 'victory_theme' ]; // Audio mapping this.audioMap = { music: { intro: 'intro_ambient', menu: 'main_theme', farm: 'farm_ambient', forest: 'forest_ambient', night: 'night_theme', town: 'town_theme', wilderness: 'wilderness_theme', combat: 'combat_theme', ana: 'ana_theme', raid: 'raid_warning', victory: 'victory_theme' }, voice: { kai_en: Array.from({ length: 12 }, (_, i) => `kai_en_${String(i + 1).padStart(2, '0')}`), ana_en: Array.from({ length: 8 }, (_, i) => `ana_en_${String(i + 1).padStart(2, '0')}`), gronk_en: ['gronk_en_01'] }, sfx: { ui_click: 'ui_click', ui_hover: 'ui_hover', plant: 'plant_seed', water: 'water_crops', harvest: 'harvest_crop', cat_meow: 'cat_meow', dog_bark: 'dog_bark', susi_bark: 'susi_bark', memory_found: 'memory_found', level_up: 'level_up' } }; // License attribution this.attribution = { music: 'Music by Kevin MacLeod (incompetech.com)\nLicensed under Creative Commons: By Attribution 3.0\nhttp://creativecommons.org/licenses/by/3.0/', voices: 'Voices generated using Microsoft Azure Edge TTS\nVoices: en-US-ChristopherNeural, en-US-AriaNeural, en-GB-RyanNeural' }; } /** * INITIALIZE AUDIO MANAGER */ init(scene) { this.scene = scene; this.log('AudioManager initialized', 'INIT'); } /** * DEBUG LOGGER */ log(message, type = 'INFO', details = {}) { if (!this.debugMode) return; const emoji = { 'MUSIC': 'đŸŽĩ', 'VOICE': '🎤', 'SFX': '🔊', 'INIT': 'đŸŽ›ī¸', 'STOP': 'âšī¸', 'ERROR': '❌', 'INFO': 'â„šī¸' }; const timestamp = new Date().toLocaleTimeString(); console.log(`${emoji[type] || 'đŸ“ĸ'} [${type}] ${timestamp} - ${message}`); if (Object.keys(details).length > 0) { console.log(' Details:', details); } } /** * PLAY MUSIC * * COPYRIGHT ATTRIBUTION: * All music tracks are by Kevin MacLeod (incompetech.com) * Licensed under Creative Commons Attribution 4.0 International (CC BY 4.0) * License URL: http://creativecommons.org/licenses/by/4.0/ * * REQUIRED CREDIT: * "Music by Kevin MacLeod (incompetech.com) * Licensed under Creative Commons: By Attribution 4.0 * http://creativecommons.org/licenses/by/4.0/" * * See /docs/CREDITS.txt for complete attribution */ playMusic(key, options = {}) { if (!this.scene) { this.log('Scene not initialized!', 'ERROR'); return null; } // Stop current music if (this.currentMusic) { this.stopMusic(); } // Get file key from map const fileKey = this.audioMap.music[key] || key; // Check if audio exists if (!this.scene.cache.audio.exists(fileKey)) { this.log(`Music not found: ${fileKey}`, 'ERROR'); return null; } // Play music const config = { volume: options.volume || this.musicVolume, loop: options.loop !== undefined ? options.loop : true, ...options }; this.currentMusic = this.scene.sound.add(fileKey, config); this.currentMusic.play(); // Debug log this.log(`Playing: ${fileKey}.mp3`, 'MUSIC', { scene: this.scene.scene.key, volume: config.volume, loop: config.loop, attribution: 'Kevin MacLeod (incompetech.com)' }); return this.currentMusic; } /** * STOP MUSIC */ stopMusic(fadeOut = true) { if (!this.currentMusic) return; this.log(`Stopping: ${this.currentMusic.key}`, 'STOP'); if (fadeOut) { this.scene.tweens.add({ targets: this.currentMusic, volume: 0, duration: 1000, onComplete: () => { if (this.currentMusic) { this.currentMusic.stop(); this.currentMusic = null; } } }); } else { this.currentMusic.stop(); this.currentMusic = null; } } /** * CROSSFADE MUSIC */ crossfadeMusic(newKey, duration = 2000) { const oldMusic = this.currentMusic; // Start new music at volume 0 const newMusic = this.playMusic(newKey, { volume: 0 }); if (!newMusic) return; // Fade out old, fade in new if (oldMusic) { this.scene.tweens.add({ targets: oldMusic, volume: 0, duration: duration, onComplete: () => { oldMusic.stop(); } }); } this.scene.tweens.add({ targets: newMusic, volume: this.musicVolume, duration: duration }); this.log(`Crossfading to: ${newKey}`, 'MUSIC', { duration: `${duration}ms`, from: oldMusic ? oldMusic.key : 'none' }); this.currentMusic = newMusic; } /** * PLAY VOICE * * COPYRIGHT ATTRIBUTION: * Voice Acting - Internal Assets * * PROJECT: Hipodevil666 Studios - Antigravity IDE Internal Assets * VOICES: Christopher & Aria (AI High-Fidelity Voice Synthesis) * LANGUAGES: English (EN) & Slovenian (SL) * * CHARACTERS: * - Kai (Protagonist): Christopher Voice (AI-generated, male, young adult) * - Ana (Twin Sister): Aria Voice (AI-generated, female, young adult) * - Gronk (Orc Mentor): Custom deep voice synthesis * * TECHNOLOGY: * High-fidelity AI voice synthesis technology * Internal studio assets - Hipodevil666 Studios * Licensed for commercial use in this project * * See /docs/CREDITS.txt for complete attribution */ playVoice(key, subtitleText = '') { if (!this.scene) { this.log('Scene not initialized!', 'ERROR'); return null; } // Stop current voice if (this.currentVoice && this.currentVoice.isPlaying) { this.currentVoice.stop(); } // Check if audio exists if (!this.scene.cache.audio.exists(key)) { this.log(`Voice not found: ${key}`, 'ERROR'); return null; } // Play voice this.currentVoice = this.scene.sound.add(key, { volume: this.voiceVolume }); this.currentVoice.play(); // Debug log this.log(`Playing: ${key}.mp3`, 'VOICE', { scene: this.scene.scene.key, subtitle: subtitleText || '(no subtitle)', volume: this.voiceVolume, duration: `~3s` }); return this.currentVoice; } /** * PLAY SFX * * COPYRIGHT ATTRIBUTION: * Sound effects from Freesound.org (CC BY 4.0) * License: https://creativecommons.org/licenses/by/4.0/ * * CREDITED AUTHORS: * * 1. COW SOUND EFFECT * Author: Benboncan * Link: https://freesound.org/s/58277/ * License: CC BY 4.0 * * 2. MINING & HAMMER SOUNDS * Author: InspectorJ (www.jshaw.co.uk) * Link: https://freesound.org/s/420878/ * License: CC BY 4.0 * * 3. INTRO FOREST AMBIENCE * Author: reinsamba * Link: https://freesound.org/s/18765/ * License: CC BY 4.0 * * 4. BASEMENT WATER DROPS * Author: erlipresidente * Link: https://freesound.org/s/415885/ * License: CC BY 4.0 * * 5. COMBAT / ZOMBIE HIT * Author: MisterKidX * Link: https://freesound.org/s/454837/ * License: CC BY 4.0 * * TOOLS USED: * - Audacity (GPL v2) - https://www.audacityteam.org/ * - LMMS (GPL v2) - https://lmms.io/ * * See /docs/CREDITS.txt for complete attribution */ playSFX(key, options = {}) { if (!this.scene) { this.log('Scene not initialized!', 'ERROR'); return null; } // Get file key from map const fileKey = this.audioMap.sfx[key] || key; // Check if audio exists if (!this.scene.cache.audio.exists(fileKey)) { this.log(`SFX not found: ${fileKey}`, 'ERROR'); return null; } // Play SFX const config = { volume: options.volume || this.sfxVolume, loop: options.loop || false, detune: options.detune || 0, ...options }; const sfx = this.scene.sound.add(fileKey, config); sfx.play(); // Debug log this.log(`Playing: ${fileKey}.wav`, 'SFX', { scene: this.scene.scene.key, trigger: options.trigger || 'manual', volume: config.volume, loop: config.loop }); return sfx; } /** * PLAY UI SOUND */ playUI(action) { const sounds = { click: 'ui_click', hover: 'ui_hover', open: 'ui_open', close: 'ui_close', error: 'ui_error' }; if (sounds[action]) { this.playSFX(sounds[action], { trigger: `UI ${action}`, volume: this.sfxVolume * 0.8 }); } } /** * PLAY FARMING SOUND */ playFarming(action) { const sounds = { plant: 'plant', water: 'water', harvest: 'harvest', hoe: 'hoe_dirt' }; if (sounds[action]) { this.playSFX(sounds[action], { trigger: `Farming: ${action}`, volume: this.sfxVolume }); } } /** * PLAY ANIMAL SOUND */ playAnimal(type) { const sounds = { cat: 'cat_meow', dog: 'dog_bark', susi: 'susi_bark' }; if (sounds[type]) { this.playSFX(sounds[type], { trigger: `Animal: ${type}`, volume: this.sfxVolume * 0.7, detune: Phaser.Math.Between(-200, 200) // Vary pitch }); } } /** * PLAY SPECIAL EFFECT */ playSpecial(event) { const sounds = { memory: 'memory_found', levelup: 'level_up', quest: 'quest_complete', unlock: 'companion_unlock' }; if (sounds[event]) { this.playSFX(sounds[event], { trigger: `Special: ${event}`, volume: this.sfxVolume * 1.2 // Louder for important events }); } } /** * SET VOLUMES */ setMusicVolume(volume) { this.musicVolume = Phaser.Math.Clamp(volume, 0, 1); if (this.currentMusic) { this.currentMusic.setVolume(this.musicVolume); } this.log(`Music volume set to: ${this.musicVolume}`, 'INFO'); } setVoiceVolume(volume) { this.voiceVolume = Phaser.Math.Clamp(volume, 0, 1); this.log(`Voice volume set to: ${this.voiceVolume}`, 'INFO'); } setSFXVolume(volume) { this.sfxVolume = Phaser.Math.Clamp(volume, 0, 1); this.log(`SFX volume set to: ${this.sfxVolume}`, 'INFO'); } /** * MUTE ALL AUDIO */ muteAll() { if (this.scene) { this.scene.sound.mute = true; this.log('All audio muted', 'INFO'); } } /** * UNMUTE ALL AUDIO */ unmuteAll() { if (this.scene) { this.scene.sound.mute = false; this.log('All audio unmuted', 'INFO'); } } /** * STOP ALL AUDIO */ stopAll() { if (this.scene) { this.scene.sound.stopAll(); this.currentMusic = null; this.currentVoice = null; this.log('All audio stopped', 'STOP'); } } /** * GET ATTRIBUTION TEXT */ getAttribution(type = 'all') { if (type === 'music') { return this.attribution.music; } else if (type === 'voices') { return this.attribution.voices; } else { return `${this.attribution.music}\n\n${this.attribution.voices}`; } } /** * ENABLE/DISABLE DEBUG MODE */ setDebugMode(enabled) { this.debugMode = enabled; this.log(`Debug mode ${enabled ? 'enabled' : 'disabled'}`, 'INFO'); } /** * đŸŽĨ STREAMER MODE - DMCA PROTECTION */ enableStreamerMode() { this.streamerMode = true; localStorage.setItem('streamer_mode', 'true'); // Mute music if currently playing non-safe track if (this.currentMusic && !this.isSafeTrack(this.currentMusic.key)) { this.stopMusic(); console.log('đŸŽĨ STREAMER MODE: Music stopped (DMCA protection)'); } console.log('đŸŽĨ STREAMER MODE ENABLED'); console.log(' ✅ Safe for Twitch/YouTube'); console.log(' ✅ All music is CC BY 4.0 (Kevin MacLeod)'); console.log(' ✅ No copyright strikes possible'); } disableStreamerMode() { this.streamerMode = false; localStorage.setItem('streamer_mode', 'false'); console.log('đŸŽĨ Streamer mode disabled'); } loadStreamerMode() { const saved = localStorage.getItem('streamer_mode'); if (saved === 'true') { this.streamerMode = true; console.log('đŸŽĨ Streamer mode loaded from settings'); } } isStreamerModeEnabled() { return this.streamerMode; } isSafeTrack(trackKey) { return this.safeMusicTracks.includes(trackKey); } getStreamerStatus() { if (this.streamerMode) { return { enabled: true, status: 'Streamer Mode: ON - Safe for Twitch/YouTube', license: 'All music: CC BY 4.0 (Kevin MacLeod)', safe: true }; } else { return { enabled: false, status: 'Streamer Mode: OFF', license: 'Music may be copyrighted', safe: false }; } } } // Singleton export const audioManager = new AudioManager(); export default audioManager;