Files
novafarma/old_logic/src_backup_1768938138/scenes/PrologueScene.js
2026-01-21 01:08:21 +01:00

505 lines
17 KiB
JavaScript

/**
* PrologueScene.js
* ================
* KRVAVA ŽETEV - Prologue Cutscene
*
* Story:
* Player (Kai) and twin sister (Ana) were scientists studying zombie virus
* During attack, both got infected with "Alfa" strain (hybrid virus)
* Ana was kidnapped by mysterious forces
* Kai wakes up alone, searching for his sister
*
* Features:
* - Cinematic dialogue system
* - Character portraits
* - Background transitions
* - Skip function (ESC)
* - Auto-advance option
*
* @author NovaFarma Team
* @date 2025-12-23
*/
class PrologueScene extends Phaser.Scene {
constructor() {
super({ key: 'PrologueScene' });
this.currentDialogueIndex = 0;
this.dialogueData = [];
this.canAdvance = true;
this.autoAdvance = false;
this.autoAdvanceDelay = 3000; // 3 seconds
}
preload() {
// Try to load JSON, but we'll use fallback if it doesn't exist
this.load.on('loaderror', (file) => {
console.warn(`⚠️ Failed to load: ${file.key}`);
});
this.load.json('prologue_data', 'assets/dialogue/prologue.json');
// Load character portraits
this.load.image('kai_portrait', 'reference_images/kai_master_style33.png');
this.load.image('ana_portrait', 'reference_images/ana_master_style33.png');
// Load prologue voiceover audio files (Slovenian // Voice-over files (only 1-12 exist!)
// DISABLED: Audio path 'assets/audio 🔴' is invalid.
// for (let num = 1; num <= 12; num++) {
// const paddedNum = num.toString().padStart(2, '0');
// this.load.audio(`prologue_${paddedNum}`, `assets/audio/voiceover/prologue_sl/prologue_${paddedNum}.wav`);
// }
// NPC Portraits - prevent green squares!
// UPDATED: Pointing to existing files in assets/slike/liki
this.load.image('kai_dialogue_portrait', 'assets/slike/liki/kai_main.png');
this.load.image('ana_dialogue_portrait', 'assets/slike/liki/starejsa_ana_mentor.png');
this.load.image('gronk_dialogue_portrait', 'assets/slike/liki/gronk_troll_1024x1024.png');
console.log('🎨 Loading NPC dialogue portraits...');
console.log('🎤 Loading 12 Slovenian prologue voiceover files... (SKIPPED)');
console.log('🖼️ Loading character portraits...');
}
create() {
const width = this.cameras.main.width;
const height = this.cameras.main.height;
console.log('🎬 Starting Prologue...');
// Track current audio to stop it when advancing
this.currentVoice = null;
// Black background
this.add.rectangle(0, 0, width, height, 0x000000).setOrigin(0);
// Initialize dialogue data from JSON OR fallback
this.dialogueData = this.cache.json.get('prologue_data');
if (!this.dialogueData) {
console.warn('⚠️ Using fallback hardcoded dialogue!');
// CORRECT STORY: 14-year-old twins, family sacrifice, Troll King kidnapping
this.dialogueData = [
{
id: 'prologue_01',
speaker: "NARRATOR",
text: "Leto 2084. Zombi virus je uničil svet.",
background: "ruins",
bgColor: 0x1a1a2e,
portrait: null
},
{
id: 'prologue_02',
speaker: "KAI",
text: "Moje ime je Kai Marković. Star sem štirinajst let.",
background: "lab",
bgColor: 0x2d1b00,
portrait: "kai_neutral"
},
{
id: 'prologue_03',
speaker: "KAI",
text: "Moja dvojčica Ana in jaz, sva zelo povezana. Nezlomljiv vez.",
background: "lab",
bgColor: 0x2d4a1e,
portrait: "kai_neutral"
},
{
id: 'prologue_04',
speaker: "KAI",
text: "Naša starša sta bila znanstvenika. Raziskovala sta virusne mutacije.",
background: "lab",
bgColor: 0x1a1a2e,
portrait: "kai_neutral"
},
{
id: 'prologue_05',
speaker: "NARRATOR",
text: "Tretji dan izbruha. Horda zombijev napada družinsko hišo.",
background: "lab_chaos",
bgColor: 0x660000,
portrait: null,
shake: true,
flash: true
},
{
id: 'prologue_06',
speaker: "NARRATOR",
text: "Starša se žrtvujeta, da rešita dvojčka. Zadnji besede: 'Beži, Kai! Zaščiti Ano!'",
background: "lab_chaos",
bgColor: 0x3d0000,
portrait: null,
shake: true
},
{
id: 'prologue_07',
speaker: "NARRATOR",
text: "Iz senc se pojavi Orjaški Troll Kralj. Poslal ga je zlobni doktor Krnić.",
background: "lab_alarm",
bgColor: 0x2d0000,
portrait: null,
shake: true
},
{
id: 'prologue_08',
speaker: "ANA",
text: "KAI! REŠI ME! KAIII!",
background: "lab_chaos",
bgColor: 0x3d0000,
portrait: "ana_pain",
shake: true
},
{
id: 'prologue_09',
speaker: "KAI",
text: "ANA! NE! VRNITE MI JO!",
background: "lab_chaos",
bgColor: 0x3d0000,
portrait: "kai_shocked",
shake: true
},
{
id: 'prologue_10',
speaker: "NARRATOR",
text: "Kai se spremeni v Alfa Hibrida. Vijolične oči. Moč nadzora nad zombiji.",
background: "ruins",
bgColor: 0x4a1a4a,
portrait: null,
flash: true
},
{
id: 'prologue_11',
speaker: "NARRATOR",
text: "Šest mesecev kasneje. Kai se zbudi na majhni kmetiji. Ana je izginila.",
background: "farm",
bgColor: 0x2d4a1e,
portrait: null
},
{
id: 'prologue_12',
speaker: "KAI",
text: "Moram jo najti. Ne glede na to, kaj bo potrebno. Ana, prihajam!",
background: "farm",
bgColor: 0x2d4a1e,
portrait: "kai_determined"
}
];
}
// Create UI elements
this.createDialogueUI(width, height);
// Skip instructions
const skipText = this.add.text(width - 20, 20, 'Pritisni ESC za preskok', {
fontSize: '16px',
fontFamily: 'Georgia, serif',
color: '#888888'
});
skipText.setOrigin(1, 0);
// Auto-advance toggle
const autoText = this.add.text(width - 20, 50, 'Pritisni PRESLEDNICA za samodejno nadaljevanje', {
fontSize: '14px',
fontFamily: 'Georgia, serif',
color: '#666666'
});
autoText.setOrigin(1, 0);
// Input handlers
this.input.keyboard.on('keydown-ESC', () => {
this.skipPrologue();
});
this.input.keyboard.on('keydown-SPACE', () => {
this.autoAdvance = !this.autoAdvance;
autoText.setColor(this.autoAdvance ? '#00FF00' : '#666666');
});
this.input.keyboard.on('keydown-ENTER', () => {
this.advanceDialogue();
});
this.input.on('pointerdown', () => {
this.advanceDialogue();
});
// Start first dialogue
this.showDialogue(0);
}
createDialogueUI(width, height) {
const dialogueBoxHeight = 180;
const dialogueY = height - dialogueBoxHeight;
// Dialogue box background
this.dialogueBg = this.add.rectangle(
width / 2,
dialogueY + dialogueBoxHeight / 2,
width - 40,
dialogueBoxHeight - 20,
0x2d1b00,
0.95
);
this.dialogueBg.setStrokeStyle(3, 0xd4a574);
// Speaker name
this.speakerText = this.add.text(50, dialogueY + 20, '', {
fontSize: '22px',
fontFamily: 'Georgia, serif',
color: '#FFD700',
fontStyle: 'bold',
stroke: '#000000',
strokeThickness: 3
});
// Dialogue text
this.dialogueText = this.add.text(50, dialogueY + 55, '', {
fontSize: '18px',
fontFamily: 'Georgia, serif',
color: '#f4e4c1',
wordWrap: { width: width - 120 },
lineSpacing: 8
});
// Continue indicator
this.continueIndicator = this.add.text(width - 60, height - 40, '▼', {
fontSize: '24px',
color: '#FFD700'
});
this.continueIndicator.setOrigin(0.5);
// Pulse animation
this.tweens.add({
targets: this.continueIndicator,
alpha: 0.3,
duration: 800,
yoyo: true,
repeat: -1
});
// Portrait
this.portraitBg = this.add.rectangle(width - 150, dialogueY + 90, 120, 120, 0x4a3520, 0.9);
this.portraitBg.setStrokeStyle(2, 0xd4a574);
// Portrait image sprite (instead of emoji text)
this.portraitImage = this.add.sprite(width - 150, dialogueY + 90, 'kai_portrait');
this.portraitImage.setOrigin(0.5);
this.portraitImage.setDisplaySize(100, 100); // Fit in portrait box
this.portraitImage.setVisible(false);
// Background sprite (will be created per dialogue)
this.backgroundSprite = null;
}
showDialogue(index) {
if (index >= this.dialogueData.length) {
this.completePrologue();
return;
}
const dialogue = this.dialogueData[index];
this.currentDialogueIndex = index;
// Stop previous audio if playing
if (this.currentVoice) {
this.currentVoice.stop();
this.currentVoice = null;
}
// Play new audio
if (dialogue.id) {
try {
// Check if audio exists in cache (it might not if load failed/missing)
if (this.cache.audio.exists(dialogue.id)) {
this.currentVoice = this.sound.add(dialogue.id);
this.currentVoice.play({ volume: 1.0 });
} else {
console.warn(`🔊 Audio missing for ${dialogue.id}`);
}
} catch (err) {
console.error('Audio play error:', err);
}
}
// Update background
this.updateBackground(dialogue.background, dialogue.bgColor);
// Apply effects
if (dialogue.shake) {
this.cameras.main.shake(500, 0.01);
}
if (dialogue.flash) {
this.cameras.main.flash(1000, 255, 255, 255);
}
// Update speaker
this.speakerText.setText(dialogue.speaker);
// Typewriter effect for text
this.typewriterEffect(dialogue.text);
// Update portrait
this.updatePortrait(dialogue.portrait);
// Auto-advance if enabled
if (this.autoAdvance && index < this.dialogueData.length - 1) {
// Wait for audio to finish OR standard delay?
// Ideally wait for audio duration, but fallback to delay
let delay = this.autoAdvanceDelay;
if (this.currentVoice && this.currentVoice.duration) {
// Add a small buffer after speech ends
delay = (this.currentVoice.duration * 1000) + 1000;
}
this.time.delayedCall(delay, () => {
// Check if user hasn't already advanced manually
if (this.currentDialogueIndex === index) {
this.advanceDialogue();
}
});
}
}
typewriterEffect(text) {
let displayText = '';
console.log('Typewriter started for:', text); // Debug
let charIndex = 0;
this.dialogueText.setText('');
// Safety check for empty text
if (!text) {
this.canAdvance = true;
return;
}
const timer = this.time.addEvent({
delay: 30, //CharactersperSeconds
callback: () => {
if (charIndex < text.length) {
displayText += text[charIndex];
this.dialogueText.setText(displayText);
charIndex++;
} else {
timer.remove();
this.canAdvance = true;
}
},
loop: true
});
this.canAdvance = false;
}
updateBackground(bgKey, bgColor) {
// Simple colored background for now
// TODO: Replace with actual background images
const width = this.cameras.main.width;
const height = this.cameras.main.height;
if (this.backgroundSprite) {
this.backgroundSprite.destroy();
}
this.backgroundSprite = this.add.rectangle(0, 0, width, height, bgColor);
this.backgroundSprite.setOrigin(0);
this.backgroundSprite.setDepth(-1);
// Add atmosphere text
let atmosphereText = '';
switch (bgKey) {
case 'lab':
atmosphereText = '🔬 Nova Lab - Research Wing';
break;
case 'lab_alarm':
atmosphereText = '⚠️ BREACH ALARM ⚠️';
break;
case 'lab_chaos':
atmosphereText = '💥 CHAOS';
break;
case 'ruins':
atmosphereText = '🏚️ Laboratory Ruins';
break;
case 'zombies':
atmosphereText = '🧟 First Encounter';
break;
case 'farm':
atmosphereText = '🌾 Abandoned Farm - New Beginning';
break;
}
if (atmosphereText) {
const atmoText = this.add.text(width / 2, 40, atmosphereText, {
fontSize: '20px',
fontFamily: 'Georgia, serif',
color: '#888888',
fontStyle: 'italic'
});
atmoText.setOrigin(0.5);
atmoText.setAlpha(0.6);
atmoText.setDepth(10);
}
}
updatePortrait(portraitKey) {
if (!portraitKey) {
this.portraitBg.setVisible(false);
this.portraitImage.setVisible(false);
return;
}
this.portraitBg.setVisible(true);
this.portraitImage.setVisible(true);
// Map portrait keys to actual character images
// For now, all Kai variants use kai_portrait, all Ana use ana_portrait
if (portraitKey.startsWith('kai_')) {
this.portraitImage.setTexture('kai_portrait');
} else if (portraitKey.startsWith('ana_')) {
this.portraitImage.setTexture('ana_portrait');
} else {
// Default fallback
this.portraitImage.setVisible(false);
}
}
advanceDialogue() {
if (!this.canAdvance) {
// Skip typewriter effect
const dialogue = this.dialogueData[this.currentDialogueIndex];
this.dialogueText.setText(dialogue.text);
this.canAdvance = true;
return;
}
// Stop current audio before advancing
if (this.currentVoice) {
this.currentVoice.stop();
}
this.showDialogue(this.currentDialogueIndex + 1);
}
skipPrologue() {
console.log('⏭️ Skipping prologue...');
if (this.currentVoice) {
this.currentVoice.stop();
}
this.scene.start('GameScene');
}
completePrologue() {
console.log('✅ Prologue complete!');
if (this.currentVoice) {
this.currentVoice.stop();
}
// Fade out
this.cameras.main.fadeOut(2000, 0, 0, 0);
this.time.delayedCall(2000, () => {
this.scene.start('GameScene');
});
}
}