diff --git a/assets/audio/music/ana_theme.wav b/assets/audio/music/ana_theme.wav new file mode 100644 index 000000000..2ad69928f Binary files /dev/null and b/assets/audio/music/ana_theme.wav differ diff --git a/assets/audio/music/combat_theme.wav b/assets/audio/music/combat_theme.wav new file mode 100644 index 000000000..01940033c Binary files /dev/null and b/assets/audio/music/combat_theme.wav differ diff --git a/assets/audio/music/farm_ambient.wav b/assets/audio/music/farm_ambient.wav new file mode 100644 index 000000000..93ddf11fe Binary files /dev/null and b/assets/audio/music/farm_ambient.wav differ diff --git a/assets/audio/music/main_theme.wav b/assets/audio/music/main_theme.wav new file mode 100644 index 000000000..429d64295 Binary files /dev/null and b/assets/audio/music/main_theme.wav differ diff --git a/assets/audio/music/night_theme.wav b/assets/audio/music/night_theme.wav new file mode 100644 index 000000000..28fafbb08 Binary files /dev/null and b/assets/audio/music/night_theme.wav differ diff --git a/assets/audio/music/town_theme.wav b/assets/audio/music/town_theme.wav new file mode 100644 index 000000000..92f67489b Binary files /dev/null and b/assets/audio/music/town_theme.wav differ diff --git a/assets/audio/music/victory_theme.wav b/assets/audio/music/victory_theme.wav new file mode 100644 index 000000000..dfbe827d1 Binary files /dev/null and b/assets/audio/music/victory_theme.wav differ diff --git a/assets/audio/sfx/building/chest_open.wav b/assets/audio/sfx/building/chest_open.wav new file mode 100644 index 000000000..9aba8db8e Binary files /dev/null and b/assets/audio/sfx/building/chest_open.wav differ diff --git a/assets/audio/sfx/building/door_close.wav b/assets/audio/sfx/building/door_close.wav new file mode 100644 index 000000000..8b6db6786 Binary files /dev/null and b/assets/audio/sfx/building/door_close.wav differ diff --git a/assets/audio/sfx/building/door_open.wav b/assets/audio/sfx/building/door_open.wav new file mode 100644 index 000000000..cbc6d208b Binary files /dev/null and b/assets/audio/sfx/building/door_open.wav differ diff --git a/assets/audio/sfx/building/hammer_nail.wav b/assets/audio/sfx/building/hammer_nail.wav new file mode 100644 index 000000000..e07ca375c Binary files /dev/null and b/assets/audio/sfx/building/hammer_nail.wav differ diff --git a/assets/audio/sfx/building/repair.wav b/assets/audio/sfx/building/repair.wav new file mode 100644 index 000000000..67120ef2e Binary files /dev/null and b/assets/audio/sfx/building/repair.wav differ diff --git a/assets/audio/sfx/combat/bow_release.wav b/assets/audio/sfx/combat/bow_release.wav new file mode 100644 index 000000000..b10cb00ce Binary files /dev/null and b/assets/audio/sfx/combat/bow_release.wav differ diff --git a/assets/audio/sfx/combat/explosion.wav b/assets/audio/sfx/combat/explosion.wav new file mode 100644 index 000000000..c026f1363 Binary files /dev/null and b/assets/audio/sfx/combat/explosion.wav differ diff --git a/assets/audio/sfx/combat/player_hurt.wav b/assets/audio/sfx/combat/player_hurt.wav new file mode 100644 index 000000000..0871ed37c Binary files /dev/null and b/assets/audio/sfx/combat/player_hurt.wav differ diff --git a/assets/audio/sfx/combat/raider_attack.wav b/assets/audio/sfx/combat/raider_attack.wav new file mode 100644 index 000000000..dfb120b88 Binary files /dev/null and b/assets/audio/sfx/combat/raider_attack.wav differ diff --git a/assets/audio/sfx/combat/shield_block.wav b/assets/audio/sfx/combat/shield_block.wav new file mode 100644 index 000000000..468341103 Binary files /dev/null and b/assets/audio/sfx/combat/shield_block.wav differ diff --git a/assets/audio/sfx/combat/sword_slash.wav b/assets/audio/sfx/combat/sword_slash.wav new file mode 100644 index 000000000..6303a792b Binary files /dev/null and b/assets/audio/sfx/combat/sword_slash.wav differ diff --git a/assets/audio/sfx/combat/zombie_death.wav b/assets/audio/sfx/combat/zombie_death.wav new file mode 100644 index 000000000..79022e9e4 Binary files /dev/null and b/assets/audio/sfx/combat/zombie_death.wav differ diff --git a/assets/audio/sfx/combat/zombie_hit.wav b/assets/audio/sfx/combat/zombie_hit.wav new file mode 100644 index 000000000..c59aa4bb4 Binary files /dev/null and b/assets/audio/sfx/combat/zombie_hit.wav differ diff --git a/assets/audio/sfx/farming/cow_moo.wav b/assets/audio/sfx/farming/cow_moo.wav new file mode 100644 index 000000000..20b5d6087 Binary files /dev/null and b/assets/audio/sfx/farming/cow_moo.wav differ diff --git a/assets/audio/sfx/farming/dig.wav b/assets/audio/sfx/farming/dig.wav new file mode 100644 index 000000000..b27c55cfd Binary files /dev/null and b/assets/audio/sfx/farming/dig.wav differ diff --git a/assets/audio/sfx/farming/harvest.wav b/assets/audio/sfx/farming/harvest.wav new file mode 100644 index 000000000..66ca1a2f1 Binary files /dev/null and b/assets/audio/sfx/farming/harvest.wav differ diff --git a/assets/audio/sfx/farming/plant_seed.wav b/assets/audio/sfx/farming/plant_seed.wav new file mode 100644 index 000000000..b10cb00ce Binary files /dev/null and b/assets/audio/sfx/farming/plant_seed.wav differ diff --git a/assets/audio/sfx/farming/scythe_swing.wav b/assets/audio/sfx/farming/scythe_swing.wav new file mode 100644 index 000000000..7620541fe Binary files /dev/null and b/assets/audio/sfx/farming/scythe_swing.wav differ diff --git a/assets/audio/sfx/farming/stone_mine.wav b/assets/audio/sfx/farming/stone_mine.wav new file mode 100644 index 000000000..819ea1775 Binary files /dev/null and b/assets/audio/sfx/farming/stone_mine.wav differ diff --git a/assets/audio/sfx/farming/tree_chop.wav b/assets/audio/sfx/farming/tree_chop.wav new file mode 100644 index 000000000..dfb120b88 Binary files /dev/null and b/assets/audio/sfx/farming/tree_chop.wav differ diff --git a/assets/audio/sfx/farming/water_crop.wav b/assets/audio/sfx/farming/water_crop.wav new file mode 100644 index 000000000..ab18a754a Binary files /dev/null and b/assets/audio/sfx/farming/water_crop.wav differ diff --git a/assets/audio/sfx/misc/coin_collect.wav b/assets/audio/sfx/misc/coin_collect.wav new file mode 100644 index 000000000..18c4939e4 Binary files /dev/null and b/assets/audio/sfx/misc/coin_collect.wav differ diff --git a/assets/audio/sfx/misc/level_up.wav b/assets/audio/sfx/misc/level_up.wav new file mode 100644 index 000000000..40686ed04 Binary files /dev/null and b/assets/audio/sfx/misc/level_up.wav differ diff --git a/assets/audio/voiceover/intro/01_narrator_flyover.mp3 b/assets/audio/voiceover/intro/01_narrator_flyover.mp3 new file mode 100644 index 000000000..ea3a68674 Binary files /dev/null and b/assets/audio/voiceover/intro/01_narrator_flyover.mp3 differ diff --git a/assets/audio/voiceover/intro/02_kai_awakening.mp3 b/assets/audio/voiceover/intro/02_kai_awakening.mp3 new file mode 100644 index 000000000..ee7eaee98 Binary files /dev/null and b/assets/audio/voiceover/intro/02_kai_awakening.mp3 differ diff --git a/assets/audio/voiceover/intro/03_kai_truth_part1.mp3 b/assets/audio/voiceover/intro/03_kai_truth_part1.mp3 new file mode 100644 index 000000000..7be65e8c9 Binary files /dev/null and b/assets/audio/voiceover/intro/03_kai_truth_part1.mp3 differ diff --git a/assets/audio/voiceover/intro/04_kai_truth_part2.mp3 b/assets/audio/voiceover/intro/04_kai_truth_part2.mp3 new file mode 100644 index 000000000..da4c4c33b Binary files /dev/null and b/assets/audio/voiceover/intro/04_kai_truth_part2.mp3 differ diff --git a/scripts/generate_audio_placeholders.py b/scripts/generate_audio_placeholders.py new file mode 100755 index 000000000..4b44a1931 --- /dev/null +++ b/scripts/generate_audio_placeholders.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +""" +Audio Placeholder Generator +Creates simple placeholder audio files for testing +""" + +import numpy as np +from scipy.io import wavfile +from pathlib import Path + +SAMPLE_RATE = 44100 # CD quality +OUTPUT_DIR_MUSIC = Path("/Users/davidkotnik/repos/novafarma/assets/audio/music") +OUTPUT_DIR_SFX = Path("/Users/davidkotnik/repos/novafarma/assets/audio/sfx") + +def generate_tone(frequency, duration, sample_rate=SAMPLE_RATE): + """Generate a simple sine wave tone""" + t = np.linspace(0, duration, int(sample_rate * duration)) + tone = np.sin(2 * np.pi * frequency * t) + # Convert to 16-bit PCM + tone = (tone * 32767).astype(np.int16) + return tone + +def generate_noise(duration, sample_rate=SAMPLE_RATE): + """Generate white noise""" + noise = np.random.normal(0, 0.1, int(sample_rate * duration)) + noise = (noise * 32767).astype(np.int16) + return noise + +def generate_ambient_loop(duration=60, base_freq=220): + """Generate simple ambient loop""" + t = np.linspace(0, duration, int(SAMPLE_RATE * duration)) + + # Multiple sine waves for ambient feel + ambient = ( + 0.3 * np.sin(2 * np.pi * base_freq * t) + + 0.2 * np.sin(2 * np.pi * (base_freq * 1.5) * t) + + 0.15 * np.sin(2 * np.pi * (base_freq * 0.75) * t) + + 0.05 * np.random.normal(0, 0.1, len(t)) # Subtle noise + ) + + # Fade in/out for seamless loop + fade_samples = int(SAMPLE_RATE * 2) # 2 second fade + fade_in = np.linspace(0, 1, fade_samples) + fade_out = np.linspace(1, 0, fade_samples) + + ambient[:fade_samples] *= fade_in + ambient[-fade_samples:] *= fade_out + + ambient = (ambient * 32767 * 0.5).astype(np.int16) + return ambient + +def create_music_placeholders(): + """Create 7 music track placeholders""" + print("\n🎡 CREATING MUSIC PLACEHOLDERS...") + print("="*60) + + OUTPUT_DIR_MUSIC.mkdir(parents=True, exist_ok=True) + + music_tracks = { + "main_theme": (440, 90), # A note, 1.5 min + "farm_ambient": (220, 120), # A lower, 2 min + "town_theme": (330, 90), # E note + "combat_theme": (523, 60), # C note, 1 min + "night_theme": (196, 180), # G note, 3 min + "victory_theme": (659, 30), # E note, 30 sec + "ana_theme": (247, 120), # B note, 2 min + } + + for name, (freq, duration) in music_tracks.items(): + print(f"\n🎢 Generating: {name}.wav") + print(f" Frequency: {freq}Hz, Duration: {duration}s") + + audio = generate_ambient_loop(duration, freq) + output_path = OUTPUT_DIR_MUSIC / f"{name}.wav" + wavfile.write(output_path, SAMPLE_RATE, audio) + + size = output_path.stat().st_size + print(f" βœ… Saved: {size:,} bytes") + + print(f"\nβœ… Created {len(music_tracks)} music placeholders") + +def create_sfx_placeholders(): + """Create 23 SFX placeholders""" + print("\n\nπŸ”Š CREATING SFX PLACEHOLDERS...") + print("="*60) + + sfx_sounds = { + # Farming (8) + "farming/plant_seed": (880, 0.3), + "farming/water_crop": (440, 1.0), + "farming/harvest": (1320, 0.5), + "farming/dig": (220, 0.8), + "farming/scythe_swing": (660, 0.6), + "farming/stone_mine": (330, 0.7), + "farming/tree_chop": (165, 0.8), + "farming/cow_moo": (110, 1.5), + + # Combat (8) + "combat/sword_slash": (1100, 0.4), + "combat/bow_release": (880, 0.3), + "combat/zombie_hit": (220, 0.5), + "combat/zombie_death": (110, 1.0), + "combat/player_hurt": (330, 0.5), + "combat/shield_block": (440, 0.4), + "combat/explosion": (55, 1.5), + "combat/raider_attack": (165, 0.8), + + # Building (5) + "building/chest_open": (660, 0.8), + "building/door_open": (440, 0.6), + "building/door_close": (330, 0.6), + "building/hammer_nail": (1100, 0.3), + "building/repair": (880, 1.0), + + # Misc (2) + "misc/coin_collect": (1760, 0.3), + "misc/level_up": (1320, 1.5), + } + + for name, (freq, duration) in sfx_sounds.items(): + # Create subfolder if needed + sfx_path = OUTPUT_DIR_SFX / name.split('/')[0] + sfx_path.mkdir(parents=True, exist_ok=True) + + filename = name.split('/')[1] + ".wav" + output_path = sfx_path / filename + + print(f"\nπŸ”Š Generating: {name}.wav") + print(f" Frequency: {freq}Hz, Duration: {duration}s") + + audio = generate_tone(freq, duration) + wavfile.write(output_path, SAMPLE_RATE, audio) + + size = output_path.stat().st_size + print(f" βœ… Saved: {size:,} bytes") + + print(f"\nβœ… Created {len(sfx_sounds)} SFX placeholders") + +def main(): + """Generate all placeholders""" + print("="*60) + print("🎨 AUDIO PLACEHOLDER GENERATOR") + print("="*60) + print("\nGenerating simple tone-based placeholders for testing...") + print("Replace these with real audio later!") + + create_music_placeholders() + create_sfx_placeholders() + + print("\n" + "="*60) + print("βœ… ALL PLACEHOLDERS GENERATED!") + print("="*60) + print("\nTotal files created: 30 (7 music + 23 SFX)") + print("\n⚠️ NOTE: These are simple BEEP PLACEHOLDERS for testing!") + print("Replace with real audio from Freesound.org or AI generators.") + +if __name__ == "__main__": + main() diff --git a/scripts/generate_intro_cutscene.py b/scripts/generate_intro_cutscene.py new file mode 100755 index 000000000..e39bfef9b --- /dev/null +++ b/scripts/generate_intro_cutscene.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +""" +Complete Intro Cutscene Voice Generation +Generates all dialogue for the intro sequence +""" + +import asyncio +import edge_tts +from pathlib import Path + +OUTPUT_DIR = Path("/Users/davidkotnik/repos/novafarma/assets/audio/voiceover/intro") + +# Voice configurations +NARRATOR_VOICE = "en-US-GuyNeural" # Deep, mysterious +KAI_VOICE = "en-US-AvaNeural" # Young female + +async def generate_intro_voices(): + """Generate all intro cutscene dialogue""" + + # Create output directory + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + + print("🎬 GENERATING INTRO CUTSCENE VOICES...") + print("="*60) + + # ======================================== + # PART 1: THE FLYOVER (Narrator) + # ======================================== + print("\nπŸ“ Part 1: The Flyover (Narrator)") + + narrator_flyover = ( + "They say the world didn't die with a bang... " + "but with a quiet whisper. " + "The Valley of Death is not just a place. " + "It's a memory that no one wants to have anymore." + ) + + await generate_voice( + text=narrator_flyover, + voice=NARRATOR_VOICE, + output_path=OUTPUT_DIR / "01_narrator_flyover.mp3", + rate="-10%", # Slower, dramatic + pitch="-5Hz" # Deeper + ) + + # ======================================== + # PART 2: THE AWAKENING (Kai) + # ======================================== + print("\nπŸ“ Part 2: The Awakening (Kai)") + + kai_awakening = ( + "My head... it hurts. Where am I? Who am I...?" + ) + + await generate_voice( + text=kai_awakening, + voice=KAI_VOICE, + output_path=OUTPUT_DIR / "02_kai_awakening.mp3", + rate="-15%", # Slower, confused + pitch="-3Hz" # Slightly lower + ) + + # ======================================== + # PART 3: THE TRUTH (Kai - multiple lines) + # ======================================== + print("\nπŸ“ Part 3: The Truth (Kai)") + + # Line 1: Reading ID card + kai_truth_1 = ( + "Kai MarkoviΔ‡. 14 years old. That's me. " + "But this other girl... why do I feel so... empty when I see her? " + "Like I'm missing half of my heart." + ) + + await generate_voice( + text=kai_truth_1, + voice=KAI_VOICE, + output_path=OUTPUT_DIR / "03_kai_truth_part1.mp3", + rate="-5%", # Normal pace, emotional + pitch="+0Hz" + ) + + # Line 2: Final determination + kai_truth_2 = ( + "Someone is waiting for me out there. " + "I can't remember the face, but I feel the promise. " + "I'm coming to find you... Ana." + ) + + await generate_voice( + text=kai_truth_2, + voice=KAI_VOICE, + output_path=OUTPUT_DIR / "04_kai_truth_part2.mp3", + rate="+0%", # Normal pace, determined + pitch="+2Hz" # Slightly higher, hopeful + ) + + print("\n" + "="*60) + print("βœ… ALL INTRO VOICES GENERATED!") + print("="*60) + print(f"\nOutput directory: {OUTPUT_DIR}") + print("\nGenerated files:") + print(" 1. 01_narrator_flyover.mp3 (Narrator - The Flyover)") + print(" 2. 02_kai_awakening.mp3 (Kai - Awakening)") + print(" 3. 03_kai_truth_part1.mp3 (Kai - Reading ID)") + print(" 4. 04_kai_truth_part2.mp3 (Kai - Determination)") + + +async def generate_voice(text, voice, output_path, rate="+0%", pitch="+0Hz"): + """Generate single voice line""" + print(f"\nπŸŽ™οΈ Generating: {output_path.name}") + print(f" Voice: {voice}") + print(f" Text: {text[:60]}...") + + communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch) + await communicate.save(str(output_path)) + + size = output_path.stat().st_size + print(f" βœ… Saved: {size:,} bytes") + + +if __name__ == "__main__": + asyncio.run(generate_intro_voices()) diff --git a/src/scenes/PreloadScene.js b/src/scenes/PreloadScene.js index ac6a7e264..bd0233dab 100644 --- a/src/scenes/PreloadScene.js +++ b/src/scenes/PreloadScene.js @@ -61,6 +61,12 @@ class PreloadScene extends Phaser.Scene { const basePath = 'assets/audio/voices/'; + // 🎬 INTRO CUTSCENE VOICES (NEW!) + this.loadAudioSafe('intro_flyover', 'assets/audio/voiceover/intro/01_narrator_flyover.mp3'); + this.loadAudioSafe('intro_awakening', 'assets/audio/voiceover/intro/02_kai_awakening.mp3'); + this.loadAudioSafe('intro_truth_1', 'assets/audio/voiceover/intro/03_kai_truth_part1.mp3'); + this.loadAudioSafe('intro_truth_2', 'assets/audio/voiceover/intro/04_kai_truth_part2.mp3'); + // Narrator (cinematic) this.loadAudioSafe('narrator_intro', basePath + 'narrator/intro_cutscene.mp3'); this.loadAudioSafe('narrator_memory', basePath + 'narrator/kai_memory_ana.mp3');