377 lines
12 KiB
JavaScript
377 lines
12 KiB
JavaScript
/**
|
|
* Enhanced PrologueScene - Cinematic Intro
|
|
*
|
|
* Features:
|
|
* - Black screen opening with breathing
|
|
* - Blur effect awakening
|
|
* - Voice-synced visuals
|
|
* - Cross-fade transitions
|
|
* - Auto quest trigger
|
|
*/
|
|
|
|
class EnhancedPrologueScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({ key: 'EnhancedPrologueScene' });
|
|
this.currentPhase = 0;
|
|
}
|
|
|
|
preload() {
|
|
console.log('🎬 Preloading Enhanced Prologue Assets...');
|
|
|
|
// Load intro assets
|
|
// Load intro assets (UPDATED paths to assets/slike/zgodba)
|
|
this.load.image('intro_black', 'assets/slike/zgodba/assets_BACKUP_20260112_064319_references_intro_black_screen.png');
|
|
this.load.image('intro_cellar', 'assets/slike/zgodba/assets_references_intro_cellar_ruins.png');
|
|
this.load.image('intro_id_card', 'assets/slike/zgodba/assets_references_intro_id_card.png');
|
|
this.load.image('intro_twin_photo', 'assets/slike/zgodba/assets_references_intro_twin_photo.png');
|
|
this.load.image('intro_blur', 'assets/slike/zgodba/assets_references_intro_blur_overlay.png');
|
|
|
|
// Load enhanced voices
|
|
const voicePath = 'assets/audio/voiceover/intro_enhanced/';
|
|
this.load.audio('voice_breathing', voicePath + '00_kai_breathing.mp3');
|
|
this.load.audio('voice_flyover', voicePath + '01_narrator_flyover_enhanced.mp3');
|
|
this.load.audio('voice_awakening', voicePath + '02_kai_awakening_enhanced.mp3');
|
|
this.load.audio('voice_truth', voicePath + '03_kai_truth_enhanced.mp3');
|
|
this.load.audio('voice_determination', voicePath + '04_kai_determination_enhanced.mp3');
|
|
|
|
// Load noir music
|
|
this.load.audio('noir_ambient', 'assets/audio/music/night_theme.wav');
|
|
}
|
|
|
|
create() {
|
|
console.log('🎬 Starting Enhanced Prologue...');
|
|
|
|
const { width, height } = this.cameras.main;
|
|
|
|
// Start noir music (low volume)
|
|
this.noirMusic = this.playAudioSafe('noir_ambient', {
|
|
volume: 0.3,
|
|
loop: true
|
|
});
|
|
|
|
// Create layers
|
|
this.backgroundLayer = this.add.container(0, 0);
|
|
this.uiLayer = this.add.container(0, 0);
|
|
|
|
// Start with black screen
|
|
this.blackScreen = this.add.image(width / 2, height / 2, 'intro_black');
|
|
this.blackScreen.setAlpha(1);
|
|
this.backgroundLayer.add(this.blackScreen);
|
|
|
|
// Subtitle text (centered, bottom)
|
|
this.subtitleText = this.add.text(width / 2, height - 100, '', {
|
|
fontSize: '24px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#ffffff',
|
|
align: 'center',
|
|
stroke: '#000000',
|
|
strokeThickness: 4,
|
|
wordWrap: { width: 800 }
|
|
});
|
|
this.subtitleText.setOrigin(0.5);
|
|
this.subtitleText.setAlpha(0);
|
|
this.uiLayer.add(this.subtitleText);
|
|
|
|
// Skip hint
|
|
const skipText = this.add.text(width - 20, 20, 'Press ESC to skip', {
|
|
fontSize: '14px',
|
|
color: '#888888'
|
|
});
|
|
skipText.setOrigin(1, 0);
|
|
this.uiLayer.add(skipText);
|
|
|
|
// ESC to skip
|
|
this.input.keyboard.on('keydown-ESC', () => this.skipIntro());
|
|
|
|
// Start intro sequence
|
|
this.startIntroSequence();
|
|
}
|
|
|
|
startIntroSequence() {
|
|
console.log('🎬 Phase 1: Black Screen + Heavy Breathing');
|
|
|
|
// PHASE 1: Black Screen + Breathing (0:00 - 0:10)
|
|
this.showSubtitle("Everything is dark... why do I only hear silence?");
|
|
|
|
this.showSubtitle("Everything is dark... why do I only hear silence?");
|
|
|
|
const breathingSound = this.playAudioSafe('voice_breathing');
|
|
|
|
// Fade to cellar after breathing + 2s
|
|
breathingSound.once('complete', () => {
|
|
this.time.delayedCall(2000, () => this.phase2_Flyover());
|
|
});
|
|
}
|
|
|
|
phase2_Flyover() {
|
|
console.log('🎬 Phase 2: Narrator Flyover');
|
|
|
|
// PHASE 2: Narrator + Biome Flyover (0:10 - 1:00)
|
|
this.clearSubtitle();
|
|
|
|
// Fade black to slight transparency (show void)
|
|
this.tweens.add({
|
|
targets: this.blackScreen,
|
|
alpha: 0.3,
|
|
duration: 3000,
|
|
ease: 'Sine.easeInOut'
|
|
});
|
|
|
|
const flyoverVoice = this.playAudioSafe('voice_flyover');
|
|
|
|
// Show subtitle
|
|
this.time.delayedCall(500, () => {
|
|
this.showSubtitle("They say the world didn't die with a bang... but with a quiet whisper.");
|
|
});
|
|
|
|
this.time.delayedCall(8000, () => {
|
|
this.showSubtitle("The Valley of Death is not just a place. It's a memory that no one wants to have anymore.");
|
|
});
|
|
|
|
// After flyover, go to awakening
|
|
flyoverVoice.once('complete', () => {
|
|
this.time.delayedCall(1000, () => this.phase3_Awakening());
|
|
});
|
|
}
|
|
|
|
phase3_Awakening() {
|
|
console.log('🎬 Phase 3: Kai Awakens');
|
|
|
|
// PHASE 3: Awakening (1:00 - 1:30)
|
|
this.clearSubtitle();
|
|
|
|
// Fade in cellar background (blurred)
|
|
const cellar = this.add.image(this.cameras.main.width / 2, this.cameras.main.height / 2, 'intro_cellar');
|
|
cellar.setAlpha(0);
|
|
this.backgroundLayer.add(cellar);
|
|
|
|
// Blur overlay
|
|
const blur = this.add.image(this.cameras.main.width / 2, this.cameras.main.height / 2, 'intro_blur');
|
|
blur.setAlpha(0);
|
|
this.backgroundLayer.add(blur);
|
|
|
|
// Fade out black, fade in cellar + blur
|
|
this.tweens.add({
|
|
targets: this.blackScreen,
|
|
alpha: 0,
|
|
duration: 2000
|
|
});
|
|
|
|
this.tweens.add({
|
|
targets: [cellar, blur],
|
|
alpha: 1,
|
|
duration: 3000,
|
|
ease: 'Sine.easeIn'
|
|
});
|
|
|
|
// Play awakening voice
|
|
this.time.delayedCall(2000, () => {
|
|
const awakeningVoice = this.playAudioSafe('voice_awakening');
|
|
|
|
this.showSubtitle("My head... it hurts. Where am I? Who am I...?");
|
|
|
|
// Clear blur gradually (vision clearing)
|
|
this.time.delayedCall(3000, () => {
|
|
this.tweens.add({
|
|
targets: blur,
|
|
alpha: 0,
|
|
duration: 4000,
|
|
ease: 'Sine.easeOut'
|
|
});
|
|
});
|
|
|
|
awakeningVoice.once('complete', () => {
|
|
this.time.delayedCall(1500, () => this.phase4_IDCard());
|
|
});
|
|
});
|
|
}
|
|
|
|
phase4_IDCard() {
|
|
console.log('🎬 Phase 4: ID Card Discovery');
|
|
|
|
// PHASE 4: ID Card (1:30 - 2:30)
|
|
this.clearSubtitle();
|
|
|
|
// Show ID card (zoom in effect)
|
|
const idCard = this.add.image(this.cameras.main.width / 2, this.cameras.main.height / 2, 'intro_id_card');
|
|
idCard.setScale(0.5);
|
|
idCard.setAlpha(0);
|
|
this.backgroundLayer.add(idCard);
|
|
|
|
this.tweens.add({
|
|
targets: idCard,
|
|
alpha: 1,
|
|
scale: 1,
|
|
duration: 2000,
|
|
ease: 'Cubic.easeOut'
|
|
});
|
|
|
|
// Play truth voice
|
|
this.time.delayedCall(1500, () => {
|
|
const truthVoice = this.playAudioSafe('voice_truth');
|
|
|
|
this.showSubtitle("Kai Marković. 14 years old. That's me. But this other girl... why do I feel so empty?");
|
|
|
|
// Show twin photo (cross-fade)
|
|
this.time.delayedCall(8000, () => {
|
|
this.showSubtitle("Like I'm missing half of my heart.");
|
|
|
|
// Cross-fade to twin photo
|
|
const twinPhoto = this.add.image(
|
|
this.cameras.main.width / 2,
|
|
this.cameras.main.height / 2,
|
|
'intro_twin_photo'
|
|
);
|
|
twinPhoto.setAlpha(0);
|
|
twinPhoto.setScale(1.2);
|
|
this.backgroundLayer.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: 3000,
|
|
ease: 'Sine.easeInOut'
|
|
});
|
|
});
|
|
|
|
truthVoice.once('complete', () => {
|
|
this.time.delayedCall(1000, () => this.phase5_Determination());
|
|
});
|
|
});
|
|
}
|
|
|
|
phase5_Determination() {
|
|
console.log('🎬 Phase 5: Determination + Quest');
|
|
|
|
// PHASE 5: Determination (2:30 - 3:00)
|
|
this.clearSubtitle();
|
|
|
|
const determinationVoice = this.playAudioSafe('voice_determination');
|
|
|
|
this.showSubtitle("Someone is waiting for me out there. I can't remember the face, but I feel the promise.");
|
|
|
|
this.time.delayedCall(5000, () => {
|
|
this.showSubtitle("I'm coming to find you... Ana.");
|
|
|
|
// Quest trigger flash
|
|
this.cameras.main.flash(1000, 100, 50, 50);
|
|
});
|
|
|
|
determinationVoice.once('complete', () => {
|
|
// Show quest notification
|
|
this.showQuestNotification();
|
|
|
|
// Fade to game after 3s
|
|
this.time.delayedCall(3000, () => this.endIntro());
|
|
});
|
|
}
|
|
|
|
showQuestNotification() {
|
|
const { width, height } = this.cameras.main;
|
|
|
|
// Quest panel
|
|
const questPanel = this.add.rectangle(width / 2, height / 2, 600, 200, 0x1a1a1a, 0.95);
|
|
questPanel.setStrokeStyle(4, 0xffaa00);
|
|
|
|
const questTitle = this.add.text(width / 2, height / 2 - 40, '📜 NEW QUEST', {
|
|
fontSize: '32px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#ffaa00',
|
|
fontStyle: 'bold'
|
|
});
|
|
questTitle.setOrigin(0.5);
|
|
|
|
const questText = this.add.text(width / 2, height / 2 + 20, 'Find clues about your past', {
|
|
fontSize: '20px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#ffffff'
|
|
});
|
|
questText.setOrigin(0.5);
|
|
|
|
this.uiLayer.add([questPanel, questTitle, questText]);
|
|
|
|
// Pulse animation
|
|
this.tweens.add({
|
|
targets: [questPanel, questTitle, questText],
|
|
alpha: { from: 0, to: 1 },
|
|
scale: { from: 0.8, to: 1 },
|
|
duration: 800,
|
|
ease: 'Back.easeOut'
|
|
});
|
|
}
|
|
|
|
showSubtitle(text) {
|
|
this.subtitleText.setText(text);
|
|
this.tweens.add({
|
|
targets: this.subtitleText,
|
|
alpha: 1,
|
|
duration: 500
|
|
});
|
|
}
|
|
|
|
clearSubtitle() {
|
|
this.tweens.add({
|
|
targets: this.subtitleText,
|
|
alpha: 0,
|
|
duration: 500,
|
|
onComplete: () => this.subtitleText.setText('')
|
|
});
|
|
}
|
|
|
|
skipIntro() {
|
|
console.log('⏭️ Skipping intro...');
|
|
this.endIntro();
|
|
}
|
|
|
|
playAudioSafe(key, config = {}) {
|
|
try {
|
|
if (this.cache.audio.exists(key)) {
|
|
const sound = this.sound.add(key, config);
|
|
sound.play();
|
|
return sound;
|
|
} else {
|
|
console.warn(`⚠️ Audio key missing: ${key}`);
|
|
// Return dummy object with 'once' method to prevent crashes on event listeners
|
|
return {
|
|
once: (event, callback) => {
|
|
// Simulate immediate completion
|
|
if (event === 'complete') callback();
|
|
},
|
|
stop: () => { },
|
|
play: () => { }
|
|
};
|
|
}
|
|
} catch (e) {
|
|
console.error(`❌ Audio error for ${key}:`, e);
|
|
return { once: (e, cb) => { if (e === 'complete') cb(); }, stop: () => { }, play: () => { } };
|
|
}
|
|
}
|
|
|
|
endIntro() {
|
|
console.log('🎬 Intro complete! Launching GameScene...');
|
|
|
|
// Fade out music
|
|
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');
|
|
});
|
|
}
|
|
}
|