// 🎬 INTRO SEQUENCE - 60-SECOND EPIC CINEMATIC // "From Colors to Darkness" - Complete Story // Created: January 10, 2026 // Style: Style 32 Dark-Chibi Noir + Polaroid + VHS // Voices: Kai (Rok) + Ana (Petra) + Gronk (Rok deep) class IntroScene extends Phaser.Scene { constructor() { super({ key: 'IntroScene' }); this.skipEnabled = false; this.currentPhase = 0; this.skipPrompt = null; this.currentPolaroid = null; this.currentText = null; this.vhsNoise = null; this.scanlines = null; this.ambientAudio = null; this.currentVoice = null; } preload() { console.log('🎬 IntroScene: Loading EPIC 60s assets...'); // Base path for intro shots const introPath = 'assets/slike/zgodba/'; // ALL 20 INTRO SHOTS // DREAMY INTRO ASSETS (Generated) const dreamyPath = 'assets/slike/zgodba/'; this.load.image('intro_family_portrait', dreamyPath + 'family_portrait_complete_dreamy.png'); this.load.image('intro_otac_longboard', dreamyPath + 'otac_longboard_pier_dreamy.png'); this.load.image('intro_kai_dreads', dreamyPath + 'kai_first_dreads_family_dreamy.png'); this.load.image('intro_ana_barbershop', dreamyPath + 'ana_barbershop_dreads_dreamy.png'); this.load.image('intro_birthday_cake', dreamyPath + 'birthday_cake_rd_dreamy.png'); this.load.image('intro_virus', introPath + 'virus_xnoir_microscope.png'); this.load.image('intro_chaos', dreamyPath + 'chaos_streets_apocalypse_dreamy.png'); this.load.image('intro_zombies', dreamyPath + 'zombie_silhouettes_panic_dreamy.png'); this.load.image('intro_parents_ghosts', dreamyPath + 'parents_transparent_ghosts_dreamy.png'); this.load.image('intro_ana_taken', introPath + 'ana_taken_military.png'); this.load.image('intro_kai_alone', introPath + 'kai_alone_basement.png'); this.load.image('intro_kai_young', introPath + 'kai_young_timelapse.png'); this.load.image('intro_kai_adult', introPath + 'kai_adult_35_timelapse.png'); this.load.image('intro_kai_elder', introPath + 'kai_elder_50_timelapse.png'); this.load.image('intro_ana_memory', introPath + 'ana_memory_flash_purple.png'); this.load.image('intro_bedroom', introPath + 'kai_bedroom_wakeup.png'); this.load.image('intro_gronk', introPath + 'gronk_doorway_silhouette.png'); this.load.image('intro_twins_childhood', introPath + 'kai_ana_twins_childhood.png'); // 🎵 AMBIENT MUSIC this.loadAudioSafe('noir_ambience', 'assets/audio/ambient/noir_ambience.mp3'); // 🎤 KAI VOICES (12 total - ENGLISH) this.loadAudioSafe('kai_01', 'assets/audio/voiceover/kai_en_01.mp3'); this.loadAudioSafe('kai_02', 'assets/audio/voiceover/kai_en_02.mp3'); this.loadAudioSafe('kai_03', 'assets/audio/voiceover/kai_en_03.mp3'); this.loadAudioSafe('kai_04', 'assets/audio/voiceover/kai_en_04.mp3'); this.loadAudioSafe('kai_05', 'assets/audio/voiceover/kai_en_05.mp3'); this.loadAudioSafe('kai_06', 'assets/audio/voiceover/kai_en_06.mp3'); this.loadAudioSafe('kai_07', 'assets/audio/voiceover/kai_en_07.mp3'); this.loadAudioSafe('kai_08', 'assets/audio/voiceover/kai_en_08.mp3'); this.loadAudioSafe('kai_09', 'assets/audio/voiceover/kai_en_09.mp3'); this.loadAudioSafe('kai_10', 'assets/audio/voiceover/kai_en_10.mp3'); this.loadAudioSafe('kai_11', 'assets/audio/voiceover/kai_en_11.mp3'); this.loadAudioSafe('kai_12', 'assets/audio/voiceover/kai_en_12.mp3'); // 🎤 ANA VOICES (8 total - ENGLISH) this.loadAudioSafe('ana_01', 'assets/audio/voiceover/ana_en_01.mp3'); this.loadAudioSafe('ana_02', 'assets/audio/voiceover/ana_en_02.mp3'); this.loadAudioSafe('ana_03', 'assets/audio/voiceover/ana_en_03.mp3'); this.loadAudioSafe('ana_04', 'assets/audio/voiceover/ana_en_04.mp3'); this.loadAudioSafe('ana_05', 'assets/audio/voiceover/ana_en_05.mp3'); this.loadAudioSafe('ana_06', 'assets/audio/voiceover/ana_en_06.mp3'); this.loadAudioSafe('ana_07', 'assets/audio/voiceover/ana_en_07.mp3'); this.loadAudioSafe('ana_08', 'assets/audio/voiceover/ana_en_08.mp3'); // 🎤 GRONK VOICE (ENGLISH - Deep UK) this.loadAudioSafe('gronk_01', 'assets/audio/voiceover/gronk_en_01.mp3'); } loadAudioSafe(key, path) { try { this.load.audio(key, path); } catch (error) { console.warn(`⚠️ Audio skipped: ${key}`); } } create() { console.log('🎬 IntroScene: Amnesia Start Sequence (Fixed Timing)'); this.cameras.main.setBackgroundColor('#000000'); // 1. SHADER INTEGRATION: Gaussian Blur this.blurFX = null; if (this.cameras.main.postFX) { try { this.blurFX = this.cameras.main.postFX.addBlur(0, 0, 0); } catch (e) { console.warn('⚠️ PostFX not supported'); } } // Initialize Blur at 20 if (this.blurFX) { this.blurFX.strength = 20; // High blur // Tween blur to 0 over 6 seconds (Standard Time) this.tweens.add({ targets: this.blurFX, strength: 0, duration: 6000, ease: 'Power2.easeOut', onComplete: () => { this.triggerWakeUp(); } }); } else { // Fallback if shaders not supported this.cameras.main.alpha = 0; this.tweens.add({ targets: this.cameras.main, alpha: 1, duration: 6000, onComplete: () => this.triggerWakeUp() }); } // 2. FLASHBACK ENGINE (Safe Mode) // Images: Birthday (0s), Longboard (1.5s), Dreads (3.0s) this.flashbackSequence([ 'intro_birthday_cake', 'intro_otac_longboard', 'intro_ana_barbershop' ]); // 3. TYPEWRITER LOGIC (Fixed Timing & No Overlap) // Line 1: 0.5s -> 3.0s this.time.delayedCall(500, () => { this.showDialogue('Vse je zamegljeno... Zakaj me vse boli?', 2500); }); // Line 2: 4.0s -> End (starts only after Line 1 is cleared) this.time.delayedCall(4000, () => { this.showDialogue('Kdo so ti ljudje v moji glavi?', 2000); }); // Audio atmosphere this.startAmbientAudio(); } flashbackSequence(images) { images.forEach((key, index) => { // Check if asset exists before scheduling if (this.textures.exists(key)) { this.time.delayedCall(index * 1500, () => { this.triggerFlashback(key); }); } else { console.warn(`⚠️ Missing flash asset: ${key} - Skipping visual, keeping timing.`); } }); } triggerFlashback(key) { // Double check existence if (!this.textures.exists(key)) return; const width = this.cameras.main.width; const height = this.cameras.main.height; const image = this.add.image(width / 2, height / 2, key); // Scale to cover most of screen (maintain aspect ratio) const scale = Math.max(width / image.width, height / image.height) * 0.8; image.setScale(scale); image.setAlpha(0); image.setDepth(10); image.setBlendMode(Phaser.BlendModes.ADD); // Flash in and out this.tweens.add({ targets: image, alpha: { from: 0, to: 0.15 }, duration: 500, yoyo: true, hold: 500, onComplete: () => { if (image && image.active) image.destroy(); } }); // Zoom this.tweens.add({ targets: image, scale: scale * 1.1, duration: 1500 }); } showDialogue(text, duration) { // OVERLAP FIX: Destroy previous text immediately if (this.currentText) { this.currentText.destroy(); this.currentText = null; } const width = this.cameras.main.width; const height = this.cameras.main.height; const textObj = this.add.text(width / 2, height - 100, '', { fontFamily: 'Courier New', fontSize: '24px', fill: '#ffffff', align: 'center', stroke: '#000000', strokeThickness: 4 }); textObj.setOrigin(0.5); textObj.setDepth(100); this.currentText = textObj; // Track current text // Typewriter let charIndex = 0; const speed = 50; if (text.length * speed > duration) { // Speed up if text is too long for duration speed = Math.floor(duration / text.length); } const timer = this.time.addEvent({ delay: speed, callback: () => { if (!textObj.active) return; textObj.text += text[charIndex]; charIndex++; if (charIndex >= text.length) { timer.remove(); // Fade out logic this.time.delayedCall(Math.max(500, duration - (text.length * speed)), () => { if (textObj.active) { this.tweens.add({ targets: textObj, alpha: 0, duration: 500, onComplete: () => { if (textObj.active) textObj.destroy(); if (this.currentText === textObj) this.currentText = null; } }); } }); } }, repeat: text.length - 1 }); } startAmbientAudio() { try { if (this.cache.audio.exists('noir_ambience')) { this.ambientAudio = this.sound.add('noir_ambience', { volume: 0.1, // Start very quiet loop: true }); this.ambientAudio.play(); // Fade in audio this.tweens.add({ targets: this.ambientAudio, volume: 0.4, duration: 6000 }); } } catch (e) { console.warn('⚠️ Ambient audio not available'); } } triggerWakeUp() { console.log('⚡ TRANSITION: Wake Up!'); // 4. TRANSITION TO GAMEPLAY // White Flash this.cameras.main.flash(1000, 255, 255, 255); // Stop audio if (this.ambientAudio) { this.tweens.add({ targets: this.ambientAudio, volume: 0, duration: 500, onComplete: () => { if (this.ambientAudio && this.ambientAudio.stop) { this.ambientAudio.stop(); } } }); } // Switch Scene this.time.delayedCall(1000, () => { // Set flag if (window.gameState && window.gameState.story) { window.gameState.story.isAmnesiaActive = true; } this.scene.start('GameScene'); }); } }