diff --git a/index.html b/index.html index b4586f95f..9d4e883bd 100644 --- a/index.html +++ b/index.html @@ -202,6 +202,7 @@ + diff --git a/src/game.js b/src/game.js index 3a438463e..0afc9f933 100644 --- a/src/game.js +++ b/src/game.js @@ -68,7 +68,7 @@ const config = { debug: false } }, - scene: [BootScene, PreloadScene, PrologueScene, EnhancedPrologueScene, UltimatePrologueScene, SystemsTestScene, TestVisualAudioScene, DemoScene, DemoSceneEnhanced, TiledTestScene, StoryScene, GameScene, UIScene, TownSquareScene], + scene: [BootScene, PreloadScene, IntroScene, PrologueScene, EnhancedPrologueScene, UltimatePrologueScene, SystemsTestScene, TestVisualAudioScene, DemoScene, DemoSceneEnhanced, TiledTestScene, StoryScene, GameScene, UIScene, TownSquareScene], scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH diff --git a/src/scenes/IntroScene.js b/src/scenes/IntroScene.js new file mode 100644 index 000000000..a425d319c --- /dev/null +++ b/src/scenes/IntroScene.js @@ -0,0 +1,490 @@ +// 🎬 INTRO SEQUENCE - VERSION B (30-45 seconds Fast-Cut) +// "From Colors to Darkness" - Kai's Amnesia Awakening +// Created: January 10, 2026 +// Style: Style 32 Dark-Chibi Noir with ADHD energy + +class IntroScene extends Phaser.Scene { + constructor() { + super({ key: 'IntroScene' }); + this.skipEnabled = false; + this.currentPhase = 0; + this.skipPrompt = null; + this.currentShot = null; + this.currentMusic = null; + } + + preload() { + console.log('🎬 IntroScene: Loading assets...'); + + // Base path for intro shots + const introPath = 'assets/references/intro_shots/'; + + // PHASE 1: HAPPY FAMILY (5 shots) + this.load.image('intro_otac_longboard', introPath + 'otac_longboard_pier.png'); + this.load.image('intro_ana_barbershop', introPath + 'ana_barbershop_dreads.png'); + this.load.image('intro_twins_childhood', introPath + 'kai_ana_twins_childhood.png'); + this.load.image('intro_birthday_cake', introPath + 'birthday_cake_rd.png'); + this.load.image('intro_family_portrait', introPath + 'family_portrait_punk_complete.png'); + + // PHASE 2: THE COLLAPSE (4 shots) + this.load.image('intro_virus', introPath + 'virus_xnoir_microscope.png'); + this.load.image('intro_zombies', introPath + 'zombie_silhouettes_panic.png'); + this.load.image('intro_chaos', introPath + 'chaos_streets_apocalypse.png'); + this.load.image('intro_ana_taken', introPath + 'ana_taken_military.png'); + + // PHASE 3: AMNESIA WAKE-UP (4 shots) + this.load.image('intro_bedroom', introPath + 'kai_bedroom_wakeup.png'); + this.load.image('intro_ana_memory', introPath + 'ana_memory_flash_purple.png'); + this.load.image('intro_gronk', introPath + 'gronk_doorway_silhouette.png'); + + // Optional: Audio files (placeholder paths - not loaded yet) + // this.load.audio('intro_music_happy', 'assets/audio/intro_happy.ogg'); + // this.load.audio('intro_music_punk', 'assets/audio/intro_punk.ogg'); + // this.load.audio('intro_music_ambient', 'assets/audio/intro_ambient.ogg'); + } + + create() { + console.log('🎬 IntroScene: Starting intro sequence...'); + + // Black background + this.cameras.main.setBackgroundColor('#000000'); + + // Setup input for skip + this.setupSkipControls(); + + // Enable skip after 5 seconds + this.time.delayedCall(5000, () => { + this.skipEnabled = true; + this.showSkipPrompt(); + }); + + // Start Phase 1 + this.playPhase1HappyFamily(); + } + + setupSkipControls() { + // Keyboard skip (X key or SPACE) + this.input.keyboard.on('keydown-X', () => this.skipIntro()); + this.input.keyboard.on('keydown-SPACE', () => this.skipIntro()); + + // Mouse/touch skip (click anywhere) + this.input.on('pointerdown', () => { + if (this.skipEnabled) { + this.skipIntro(); + } + }); + } + + showSkipPrompt() { + if (this.skipPrompt) return; // Already showing + + const width = this.cameras.main.width; + const height = this.cameras.main.height; + + this.skipPrompt = this.add.text( + width - 20, + height - 20, + '[Hold X to face reality]', + { + fontFamily: 'Courier New', + fontSize: '14px', + fill: '#00ffff', + stroke: '#000000', + strokeThickness: 2 + } + ); + this.skipPrompt.setOrigin(1, 1); + this.skipPrompt.setAlpha(0.7); + this.skipPrompt.setDepth(1000); + + // Pulse animation + this.tweens.add({ + targets: this.skipPrompt, + alpha: { from: 0.7, to: 1.0 }, + duration: 800, + yoyo: true, + repeat: -1 + }); + } + + skipIntro() { + if (!this.skipEnabled) return; + + console.log('⏭️ IntroScene: Skipped by user'); + + // Stop all tweens and timers + this.tweens.killAll(); + this.time.removeAllEvents(); + + // Fade to GameScene + this.cameras.main.fadeOut(500, 0, 0, 0); + this.cameras.main.once('camerafadeoutcomplete', () => { + this.scene.start('GameScene'); + }); + } + + playPhase1HappyFamily() { + // PHASE 1: HAPPY FAMILY MEMORIES (0-15s) + console.log('🎬 Phase 1: Happy Family'); + this.currentPhase = 1; + + const width = this.cameras.main.width; + const height = this.cameras.main.height; + + // Text overlay + const text1 = this.add.text( + width / 2, + height - 80, + 'Nekoč smo imeli barve...', + { + fontFamily: 'Courier New', + fontSize: '24px', + fill: '#ffffff', + stroke: '#00ffff', + strokeThickness: 1, + shadow: { + offsetX: 2, + offsetY: 2, + color: '#ff00ff', + blur: 5, + stroke: false, + fill: true + } + } + ); + text1.setOrigin(0.5); + text1.setAlpha(0); + text1.setDepth(100); + + this.tweens.add({ + targets: text1, + alpha: 1, + duration: 1000, + delay: 500 + }); + + // Shots sequence - fast cuts (~3s each) + this.showShot('intro_otac_longboard', 0, 3000, { warm: true }); + this.showShot('intro_ana_barbershop', 3000, 6000, { warm: true }); + this.showShot('intro_twins_childhood', 6000, 9000, { warm: true, clear: true }); // CLEAREST + this.showShot('intro_birthday_cake', 9000, 12000, { warm: true }); + this.showShot('intro_family_portrait', 12000, 15000, { warm: true, freeze: true }); + + // Transition to Phase 2 + this.time.delayedCall(14500, () => { + this.tweens.add({ + targets: text1, + alpha: 0, + duration: 500 + }); + }); + + this.time.delayedCall(15000, () => { + text1.destroy(); + this.playPhase2TheCollapse(); + }); + } + + playPhase2TheCollapse() { + // PHASE 2: THE COLLAPSE (15-30s) + console.log('🎬 Phase 2: The Collapse'); + this.currentPhase = 2; + + const width = this.cameras.main.width; + const height = this.cameras.main.height; + + // Text overlay + const text2 = this.add.text( + width / 2, + height - 80, + 'Potem je prišla tema...', + { + fontFamily: 'Courier New', + fontSize: '24px', + fill: '#ff0000', + stroke: '#00ff00', + strokeThickness: 2, + shadow: { + offsetX: 3, + offsetY: 3, + color: '#000000', + blur: 10, + stroke: true, + fill: true + } + } + ); + text2.setOrigin(0.5); + text2.setAlpha(0); + text2.setDepth(100); + + this.tweens.add({ + targets: text2, + alpha: 1, + duration: 800, + delay: 300 + }); + + // Shots sequence - faster, chaotic (~4s each) + this.showShot('intro_virus', 15000, 19000, { toxic: true, glitch: true }); + this.showShot('intro_zombies', 19000, 23000, { red: true, glitch: true, strobe: true }); + this.showShot('intro_chaos', 23000, 27000, { chaos: true }); + this.showShot('intro_ana_taken', 27000, 30000, { blur: true, red: true }); + + // Add camera shake for chaos + this.time.delayedCall(19000, () => { + this.cameras.main.shake(4000, 0.005); + }); + + // Transition to Phase 3 + this.time.delayedCall(29500, () => { + this.tweens.add({ + targets: text2, + alpha: 0, + duration: 500 + }); + }); + + this.time.delayedCall(30000, () => { + text2.destroy(); + this.playPhase3AmnesiaWakeUp(); + }); + } + + playPhase3AmnesiaWakeUp() { + // PHASE 3: AMNESIA WAKE-UP (30-45s) + console.log('🎬 Phase 3: Amnesia Wake-Up'); + this.currentPhase = 3; + + const width = this.cameras.main.width; + const height = this.cameras.main.height; + + // Screen goes black first (amnesia darkness) + const blackScreen = this.add.rectangle( + width / 2, + height / 2, + width, + height, + 0x000000 + ); + blackScreen.setDepth(50); + blackScreen.setAlpha(0); + + this.tweens.add({ + targets: blackScreen, + alpha: 1, + duration: 1000, + delay: 0 + }); + + // Text overlays + const text3a = this.add.text( + width / 2, + height / 2 - 40, + 'In ostal sem sam...', + { + fontFamily: 'Courier New', + fontSize: '20px', + fill: '#666666', + stroke: '#000000', + strokeThickness: 2 + } + ); + text3a.setOrigin(0.5); + text3a.setAlpha(0); + text3a.setDepth(100); + + const text3b = this.add.text( + width / 2, + height / 2, + 'z luknjo v glavi.', + { + fontFamily: 'Courier New', + fontSize: '20px', + fill: '#999999', + stroke: '#000000', + strokeThickness: 2 + } + ); + text3b.setOrigin(0.5); + text3b.setAlpha(0); + text3b.setDepth(100); + + this.tweens.add({ + targets: text3a, + alpha: 1, + duration: 1500, + delay: 1500 + }); + + this.tweens.add({ + targets: text3b, + alpha: 1, + duration: 1500, + delay: 2500 + }); + + // Fade out black screen and text, show bedroom + this.time.delayedCall(5000, () => { + this.tweens.add({ + targets: [blackScreen, text3a, text3b], + alpha: 0, + duration: 2000 + }); + }); + + // Bedroom appears + this.time.delayedCall(7000, () => { + blackScreen.destroy(); + text3a.destroy(); + text3b.destroy(); + this.showShot('intro_bedroom', 37000, 40000, { fadeIn: true }); + }); + + // Ana memory flash + this.time.delayedCall(10000, () => { + const flash = this.add.image(width / 2, height / 2, 'intro_ana_memory'); + flash.setAlpha(0); + flash.setDepth(80); + flash.setScale(0.8); + + this.tweens.add({ + targets: flash, + alpha: 1, + duration: 300, + yoyo: true, + repeat: 2, + onComplete: () => flash.destroy() + }); + }); + + // Gronk entrance + this.time.delayedCall(12000, () => { + this.showShot('intro_gronk', 42000, 45000, { fadeIn: true }); + }); + + // Final text + const textFinal = this.add.text( + width / 2, + height - 100, + 'Moram jo najti.\nTudi če mi vzame celo življenje.', + { + fontFamily: 'Courier New', + fontSize: '18px', + fill: '#ffffff', + stroke: '#ff00ff', + strokeThickness: 1, + align: 'center', + lineSpacing: 10 + } + ); + textFinal.setOrigin(0.5); + textFinal.setAlpha(0); + textFinal.setDepth(100); + + this.time.delayedCall(13000, () => { + this.tweens.add({ + targets: textFinal, + alpha: 1, + duration: 2000 + }); + }); + + // Transition to GameScene + this.time.delayedCall(17000, () => { + console.log('🎬 IntroScene: Complete! Starting game...'); + this.cameras.main.fadeOut(2000, 0, 0, 0); + this.cameras.main.once('camerafadeoutcomplete', () => { + this.scene.start('GameScene'); + }); + }); + } + + showShot(imageKey, startTime, endTime, options = {}) { + const width = this.cameras.main.width; + const height = this.cameras.main.height; + const duration = endTime - startTime; + + this.time.delayedCall(startTime, () => { + if (this.currentShot) { + // Fade out previous shot + this.tweens.add({ + targets: this.currentShot, + alpha: 0, + duration: 300, + onComplete: () => { + if (this.currentShot) this.currentShot.destroy(); + } + }); + } + + // Create new shot + const shot = this.add.image(width / 2, height / 2, imageKey); + shot.setAlpha(options.fadeIn ? 0 : 1); + shot.setDepth(10); + + // Scale to fit screen + const scaleX = width / shot.width; + const scaleY = height / shot.height; + const scale = Math.max(scaleX, scaleY); + shot.setScale(scale); + + this.currentShot = shot; + + // Apply visual effects + if (options.warm) { + shot.setTint(0xffddaa); // Warm nostalgic tint + } + + if (options.red) { + shot.setTint(0xff6666); // Red danger tint + } + + if (options.toxic) { + shot.setTint(0x66ff66); // Green toxic tint + } + + if (options.fadeIn) { + this.tweens.add({ + targets: shot, + alpha: 1, + duration: 1500 + }); + } + + if (options.glitch) { + // Simple glitch effect - position jitter + this.tweens.add({ + targets: shot, + x: { from: width / 2 - 5, to: width / 2 + 5 }, + duration: 100, + repeat: duration / 100, + yoyo: true + }); + } + + if (options.strobe) { + // Strobe effect + this.tweens.add({ + targets: shot, + alpha: { from: 0.7, to: 1.0 }, + duration: 150, + repeat: duration / 150, + yoyo: true + }); + } + }); + } + + shutdown() { + // Cleanup + if (this.skipPrompt) { + this.skipPrompt.destroy(); + } + if (this.currentShot) { + this.currentShot.destroy(); + } + this.tweens.killAll(); + this.time.removeAllEvents(); + } +} diff --git a/src/scenes/PreloadScene.js b/src/scenes/PreloadScene.js index bd0233dab..1c2f3758d 100644 --- a/src/scenes/PreloadScene.js +++ b/src/scenes/PreloadScene.js @@ -476,10 +476,10 @@ class PreloadScene extends Phaser.Scene { console.log('✅ PreloadScene: Assets loaded!'); window.gameState.currentScene = 'PreloadScene'; - // ✅ Starting main menu (StoryScene) + // ✅ Starting INTRO SEQUENCE (NEW! Jan 10, 2026) this.time.delayedCall(500, () => { - console.log('🎮 Starting StoryScene (Main Menu)...'); - this.scene.start('StoryScene'); // ← MAIN MENU + console.log('🎬 Starting IntroScene (Cinematic Intro)...'); + this.scene.start('IntroScene'); // ← NEW INTRO SEQUENCE }); } });