/** * ULTIMATE CINEMATIC PROLOGUE SCENE * 100% Polished - Multilingual + Subtitle Sync + Pure Cinematic Mode * * Features: * - Dual language support (SLO/ENG) * - Frame-perfect subtitle synchronization * - Pure cinematic mode (no HUD, no UI, just story) * - Film-quality transitions * - Emotional voice delivery */ class UltimatePrologueScene extends Phaser.Scene { constructor() { super({ key: 'UltimatePrologueScene' }); this.language = 'en'; // Default: English (can be changed via settings) } init(data) { // Get language from settings or data this.language = data.language || window.i18n?.getCurrentLanguage() || 'en'; console.log(`🎬 Ultimate Prologue - Language: ${this.language.toUpperCase()}`); } preload() { console.log('🎬 Preloading Ultimate Cinematic Assets...'); const lang = this.language; const voicePath = `assets/audio/voiceover/intro_final/${lang}/`; // Load intro visuals this.load.image('intro_black', 'assets/intro_assets/black_screen.png'); this.load.image('intro_cellar', 'assets/intro_assets/cellar_ruins.png'); this.load.image('intro_id_card', 'assets/intro_assets/id_card.png'); this.load.image('intro_twin_photo', 'assets/intro_assets/twin_photo.png'); this.load.image('intro_blur', 'assets/intro_assets/blur_overlay.png'); // Load voices (current language) this.load.audio('v1_breathing', voicePath + '01_breathing.mp3'); this.load.audio('v2_flyover', voicePath + '02_flyover.mp3'); this.load.audio('v3_awakening', voicePath + '03_awakening.mp3'); this.load.audio('v4_id_card', voicePath + '04_id_card.mp3'); this.load.audio('v5_determination', voicePath + '05_determination.mp3'); // Noir ambient music (TEMP DISABLED - file path issue) // this.load.audio('noir_music', 'assets/audio/music/night_theme.wav'); } create() { const { width, height } = this.cameras.main; console.log('🎬 Starting Ultimate Cinematic Prologue...'); console.log(` Language: ${this.language === 'en' ? 'English' : 'Slovenščina'}`); // ================================================================ // PURE CINEMATIC MODE - No HUD, No UI, Only Story // ================================================================ // Start noir music (TEMP DISABLED - file issue) /* this.noirMusic = this.sound.add('noir_music', { volume: 0.2, loop: true }); this.noirMusic.play(); */ // Create visual layers this.bgLayer = this.add.container(0, 0); this.subtitleLayer = this.add.container(0, 0); // Black screen (full opacity) this.blackScreen = this.add.rectangle(width / 2, height / 2, width, height, 0x000000); this.bgLayer.add(this.blackScreen); // Subtitle text (cinematic positioning - bottom center with safe margin) this.subtitle = this.add.text(width / 2, height - 80, '', { fontSize: '28px', fontFamily: 'Georgia, serif', color: '#ffffff', align: 'center', stroke: '#000000', strokeThickness: 5, wordWrap: { width: 900 }, lineSpacing: 10, shadow: { offsetX: 2, offsetY: 2, color: '#000000', blur: 5, fill: true } }); this.subtitle.setOrigin(0.5); this.subtitle.setAlpha(0); this.subtitle.setDepth(1000); this.subtitleLayer.add(this.subtitle); // Skip hint (minimal, top-right) const skipHint = this.add.text(width - 30, 30, '[ESC]', { fontSize: '16px', fontFamily: 'monospace', color: '#666666', alpha: 0.5 }); skipHint.setOrigin(1, 0); // ESC to skip this.input.keyboard.on('keydown-ESC', () => this.skipToGame()); // ================================================================ // START INTRO SEQUENCE // ================================================================ this.time.delayedCall(1000, () => this.phase1_Breathing()); } // ==================================================================== // PHASE 1: BLACK SCREEN - Heavy Breathing & Confusion (0:00-0:07) // ==================================================================== phase1_Breathing() { console.log('🎬 Phase 1: Breathing'); // Audio safety check if (!this.cache.audio.exists('v1_breathing')) { console.warn('⚠️ v1_breathing not found - skipping audio'); this.time.delayedCall(2000, () => this.phase2_Flyover()); return; } const voice = this.sound.add('v1_breathing'); // Subtitle with precise timing const subs = this.getSubtitles(); this.showSubtitle(subs.breathing); // Play voice voice.play(); // Proceed after voice + 2s pause voice.once('complete', () => { this.time.delayedCall(2000, () => this.phase2_Flyover()); }); } // ==================================================================== // PHASE 2: NARRATOR FLYOVER - World Description (0:07-0:25) // ==================================================================== phase2_Flyover() { console.log('🎬 Phase 2: Flyover'); // Fade black to slight transparency (show void/darkness) this.tweens.add({ targets: this.blackScreen, alpha: 0.5, duration: 4000, ease: 'Sine.easeInOut' }); // Audio safety check if (!this.cache.audio.exists('v2_flyover')) { console.warn('⚠️ v2_flyover not found - skipping audio'); this.time.delayedCall(2000, () => this.phase3_Awakening()); return; } const voice = this.sound.add('v2_flyover'); voice.play(); // Subtitle timing (split into two parts for readability) const subs = this.getSubtitles(); this.time.delayedCall(500, () => { this.showSubtitle(subs.flyover_1); }); this.time.delayedCall(8000, () => { this.showSubtitle(subs.flyover_2); }); voice.once('complete', () => { this.time.delayedCall(1500, () => this.phase3_Awakening()); }); } // ==================================================================== // PHASE 3: AWAKENING - Kai Wakes in Cellar (0:25-0:40) // ==================================================================== phase3_Awakening() { console.log('🎬 Phase 3: Awakening'); const { width, height } = this.cameras.main; // Fade in cellar background const cellar = this.add.image(width / 2, height / 2, 'intro_cellar'); cellar.setAlpha(0); cellar.setScale(1.05); // Slight zoom for depth this.bgLayer.add(cellar); // Blur overlay (vision is blurred) const blur = this.add.image(width / 2, height / 2, 'intro_blur'); blur.setAlpha(0); this.bgLayer.add(blur); // Fade out black, fade in cellar + blur this.tweens.add({ targets: this.blackScreen, alpha: 0, duration: 2500 }); this.tweens.add({ targets: [cellar, blur], alpha: 1, duration: 3000, ease: 'Sine.easeIn' }); // Subtle zoom in (like opening eyes) this.tweens.add({ targets: cellar, scale: 1, duration: 5000, ease: 'Sine.easeOut' }); // Play awakening voice this.time.delayedCall(2500, () => { // Audio safety check if (!this.cache.audio.exists('v3_awakening')) { console.warn('⚠️ v3_awakening not found - skipping audio'); this.time.delayedCall(2000, () => this.phase4_IDCard()); return; } const voice = this.sound.add('v3_awakening'); voice.play(); const subs = this.getSubtitles(); this.showSubtitle(subs.awakening); // Gradually clear blur (vision clears) this.time.delayedCall(3000, () => { this.tweens.add({ targets: blur, alpha: 0, duration: 5000, ease: 'Cubic.easeOut' }); }); voice.once('complete', () => { this.time.delayedCall(2000, () => this.phase4_IDCard()); }); }); } // ==================================================================== // PHASE 4: ID CARD - Discovery & Memory (0:40-0:58) // ==================================================================== phase4_IDCard() { console.log('🎬 Phase 4: ID Card'); const { width, height } = this.cameras.main; // Show ID card (zoom in from smaller) const idCard = this.add.image(width / 2, height / 2, 'intro_id_card'); idCard.setScale(0.6); idCard.setAlpha(0); this.bgLayer.add(idCard); this.tweens.add({ targets: idCard, alpha: 1, scale: 1, duration: 2000, ease: 'Cubic.easeOut' }); // Play ID card voice this.time.delayedCall(1500, () => { // Audio safety check if (!this.cache.audio.exists('v4_id_card')) { console.warn('⚠️ v4_id_card not found - skipping audio'); this.time.delayedCall(2000, () => this.phase5_Determination()); return; } const voice = this.sound.add('v4_id_card'); voice.play(); const subs = this.getSubtitles(); // Part 1: Reading ID this.showSubtitle(subs.id_card_1); // Part 2: Empty feeling this.time.delayedCall(6000, () => { this.showSubtitle(subs.id_card_2); // Cross-fade to twin photo this.time.delayedCall(4000, () => { const twinPhoto = this.add.image(width / 2, height / 2, 'intro_twin_photo'); twinPhoto.setAlpha(0); twinPhoto.setScale(1.1); this.bgLayer.add(twinPhoto); // Fade out ID, fade in photo this.tweens.add({ targets: idCard, alpha: 0, duration: 2000 }); this.tweens.add({ targets: twinPhoto, alpha: 1, scale: 1, duration: 2500, ease: 'Sine.easeInOut' }); // Warm glow effect (memory warmth) this.cameras.main.flash(2000, 255, 200, 150, false, null, 0.15); }); }); voice.once('complete', () => { this.time.delayedCall(1500, () => this.phase5_Determination()); }); }); } // ==================================================================== // PHASE 5: DETERMINATION - The Promise (0:58-1:10) → Quest → Game // ==================================================================== phase5_Determination() { console.log('🎬 Phase 5: Determination'); // Audio safety check if (!this.cache.audio.exists('v5_determination')) { console.warn('⚠️ v5_determination not found - skipping audio'); this.time.delayedCall(2000, () => this.exitPrologue()); return; } const voice = this.sound.add('v5_determination'); voice.play(); const subs = this.getSubtitles(); // Part 1: Promise this.showSubtitle(subs.determination_1); // Part 2: Ana's name this.time.delayedCall(5000, () => { this.showSubtitle(subs.determination_2); // Camera flash (determination spark) this.cameras.main.flash(800, 100, 50, 80); }); voice.once('complete', () => { // Show quest notification (brief) this.showQuestBrief(); // Fade to game this.time.delayedCall(4000, () => this.exitPrologue()); }); } showQuestBrief() { const { width, height } = this.cameras.main; const questText = this.language === 'en' ? '📜 New Quest: Find clues about your past' : '📜 Nova naloga: Poišči sledi o svoji preteklosti'; const quest = this.add.text(width / 2, height - 150, questText, { fontSize: '24px', fontFamily: 'Georgia, serif', color: '#ffdd00', stroke: '#000000', strokeThickness: 4, shadow: { offsetX: 2, offsetY: 2, color: '#000000', blur: 8, fill: true } }); quest.setOrigin(0.5); quest.setAlpha(0); this.tweens.add({ targets: quest, alpha: 1, y: height - 180, duration: 1000, ease: 'Back.easeOut' }); } showSubtitle(text) { this.subtitle.setText(text); this.tweens.add({ targets: this.subtitle, alpha: 1, duration: 600, ease: 'Sine.easeIn' }); // Auto-fade after reading time (adaptive) const readTime = Math.max(3000, text.length * 50); this.time.delayedCall(readTime, () => { this.tweens.add({ targets: this.subtitle, alpha: 0, duration: 600 }); }); } getSubtitles() { if (this.language === 'sl') { return { breathing: "Vse je temno... Zakaj slišim samo tišino?", flyover_1: "Pravijo, da svet ni umrl s pokom,\nampak s tihim šepetom.", flyover_2: "Dolina smrti ni le kraj.\nJe spomin, ki ga nihče več ne želi imeti.", awakening: "Glava... boli me.\nKje sem? Kdo sem?", id_card_1: "Kai Marković. Štirinajst let. To sem jaz.", id_card_2: "Ampak ta druga deklica...\nZakaj se ob njej počutim tako prazno?\nKot da mi manjka polovica srca.", determination_1: "Nekdo me čaka tam zunaj.\nNe spomnim se obraza, čutim pa obljubo.", determination_2: "Grem te poiskat... Ana." }; } else { return { breathing: "Everything is dark...\nWhy do I only hear silence?", flyover_1: "They say the world didn't die with a bang,\nbut with a quiet whisper.", flyover_2: "The Valley of Death is not just a place.\nIt's a memory no one wants to have anymore.", awakening: "My head... it hurts.\nWhere am I? Who am I?", id_card_1: "Kai Marković. Fourteen years old. That's me.", id_card_2: "But this other girl...\nWhy do I feel so empty when I see her?\nLike I'm missing half of my heart.", determination_1: "Someone is waiting for me out there.\nI can't remember the face, but I feel the promise.", determination_2: "I'm coming to find you... Ana." }; } } skipToGame() { console.log('⏭️ Skipping to game...'); this.exitPrologue(); } exitPrologue() { console.log('🎬 Intro complete! Starting game...'); // Fade out music (if exists) if (this.noirMusic) { this.tweens.add({ targets: this.noirMusic, volume: 0, duration: 2000, onComplete: () => this.noirMusic.stop() }); } // Fade to black this.cameras.main.fadeOut(2000, 0, 0, 0); this.cameras.main.once('camerafadeoutcomplete', () => { this.scene.start('GameScene'); }); } }