Files
novafarma/DEBUG_TOTAL_RECOVERY/IntroScene.js
2026-01-16 02:43:46 +01:00

314 lines
12 KiB
JavaScript

// 🎬 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/references/intro_shots/';
// ALL 20 INTRO SHOTS
// DREAMY INTRO ASSETS (Generated)
const dreamyPath = 'assets/images/intro_sequence/';
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');
});
}
}