314 lines
12 KiB
JavaScript
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');
|
|
});
|
|
}
|
|
}
|