/** * 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 } create() { const width = this.cameras.main.width; const height = this.cameras.main.height; console.log('🎬 Starting Prologue...'); // Black background this.add.rectangle(0, 0, width, height, 0x000000).setOrigin(0); // Initialize dialogue data this.dialogueData = this.createDialogueData(); // Create UI elements this.createDialogueUI(width, height); // Skip instructions const skipText = this.add.text(width - 20, 20, 'Press ESC to skip', { fontSize: '16px', fontFamily: 'Georgia, serif', color: '#888888' }); skipText.setOrigin(1, 0); // Auto-advance toggle const autoText = this.add.text(width - 20, 50, 'Press SPACE to toggle auto-advance', { 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); } createDialogueData() { return [ // ACT 1: THE OUTBREAK { background: 'lab', speaker: 'Narrator', portrait: null, text: '2084. Nova Lab, Slovenia.\nThe world\'s last hope against the zombie virus...', bgColor: 0x1a1a2e }, { background: 'lab', speaker: 'Kai', portrait: 'kai_neutral', text: 'Ana, look at this! The Alfa strain is reacting to our blood samples!', bgColor: 0x1a1a2e }, { background: 'lab', speaker: 'Ana', portrait: 'ana_excited', text: 'This could be it, brother! A cure that doesn\'t just kill the virus...\nit transforms it!', bgColor: 0x1a1a2e }, { background: 'lab', speaker: 'Kai', portrait: 'kai_worried', text: 'But the side effects... subjects gain control over zombies.\nIs that even ethical?', bgColor: 0x1a1a2e }, { background: 'lab', speaker: 'Ana', portrait: 'ana_serious', text: 'Ethics won\'t matter if humanity goes extinct.\nWe need to test this NOW.', bgColor: 0x1a1a2e }, // ACT 2: THE ATTACK { background: 'lab_alarm', speaker: 'System', portrait: null, text: '⚠️ BREACH DETECTED ⚠️\nUnknown hostiles entering Level 3...', bgColor: 0x330000, shake: true }, { background: 'lab_alarm', speaker: 'Kai', portrait: 'kai_shocked', text: 'Ana, get to the safe room! I\'ll secure the samples!', bgColor: 0x330000, shake: true }, { background: 'lab_chaos', speaker: 'Ana', portrait: 'ana_determined', text: 'No! We inject each other with Alfa NOW!\nIt\'s our only chance!', bgColor: 0x220000, shake: true }, { background: 'lab_chaos', speaker: 'Narrator', portrait: null, text: 'In a desperate moment, the twins inject themselves with the untested Alfa virus...', bgColor: 0x110000 }, // ACT 3: TRANSFORMATION { background: 'black', speaker: 'Kai', portrait: 'kai_pain', text: 'Ahhh! It burns! Ana, I can feel... everything!\nEvery zombie in the building!', bgColor: 0x000000, shake: true }, { background: 'black', speaker: 'Ana', portrait: 'ana_pain', text: 'Brother! The connection... I can hear them too!\nWe\'re becoming... ALFA!', bgColor: 0x000000, shake: true }, { background: 'black', speaker: 'Narrator', portrait: null, text: 'An explosion. Darkness. Then... silence.', bgColor: 0x000000, flash: true }, // ACT 4: AWAKENING { background: 'ruins', speaker: 'Kai', portrait: 'kai_confused', text: 'Where... where am I?\nAna? ANA!', bgColor: 0x2d1b00 }, { background: 'ruins', speaker: 'Kai', portrait: 'kai_determined', text: 'She\'s gone. They took her.\nBut I can still feel her... through the Twin Bond.', bgColor: 0x2d1b00 }, { background: 'ruins', speaker: 'Kai', portrait: 'kai_anger', text: 'Whoever did this... I WILL find you.\nAnd my new "friends" will help me.', bgColor: 0x2d1b00 }, { background: 'zombies', speaker: 'Narrator', portrait: null, text: 'Three zombies approach. But instead of attacking...\nthey kneel before Kai.', bgColor: 0x1a4d1a }, { background: 'zombies', speaker: 'Kai', portrait: 'kai_realization', text: 'I am... Alfa.\nAnd they are mine to command.', bgColor: 0x1a4d1a }, { background: 'farm', speaker: 'Narrator', portrait: null, text: 'And so begins the journey of Kai...\nZombie master. Brother. ALFA.', bgColor: 0x2d5016 }, { background: 'farm', speaker: 'Narrator', portrait: null, text: 'BUILD your farm. COMMAND your undead.\nSEARCH for Ana.\n\nThis is... KRVAVA Ε½ETEV.', bgColor: 0x2d5016 } ]; } 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); this.portraitText = this.add.text(width - 150, dialogueY + 90, '', { fontSize: '60px' }); this.portraitText.setOrigin(0.5); // 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; // 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) { this.time.delayedCall(this.autoAdvanceDelay, () => { this.advanceDialogue(); }); } } typewriterEffect(text) { let displayText = ''; let charIndex = 0; this.dialogueText.setText(''); 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.portraitText.setVisible(false); return; } this.portraitBg.setVisible(true); this.portraitText.setVisible(true); // Simple emoji portraits for now // TODO: Replace with actual character art const portraits = { 'kai_neutral': 'πŸ‘¨', 'kai_worried': '😟', 'kai_shocked': '😱', 'kai_pain': '😫', 'kai_confused': 'πŸ˜•', 'kai_determined': '😠', 'kai_anger': '😑', 'kai_realization': 'πŸ€”', 'ana_excited': 'πŸ‘©β€πŸ”¬', 'ana_serious': '😐', 'ana_determined': 'πŸ’ͺ', 'ana_pain': '😣' }; this.portraitText.setText(portraits[portraitKey] || '❓'); } advanceDialogue() { if (!this.canAdvance) { // Skip typewriter effect const dialogue = this.dialogueData[this.currentDialogueIndex]; this.dialogueText.setText(dialogue.text); this.canAdvance = true; return; } this.showDialogue(this.currentDialogueIndex + 1); } skipPrologue() { console.log('⏭️ Skipping prologue...'); this.scene.start('GameScene'); } completePrologue() { console.log('βœ… Prologue complete!'); // Fade out this.cameras.main.fadeOut(2000, 0, 0, 0); this.time.delayedCall(2000, () => { this.scene.start('GameScene'); }); } }