Files
novafarma/src/game.js
David Kotnik d5b0046985 🎥 Jan 8 ULTIMATE PROLOGUE - 100% Polished Cinematic Intro
 COMPLETE INTRO SYSTEM - PRODUCTION READY:

**🌍 MULTILINGUAL SUPPORT:**
- English (JennyNeural + RyanNeural)
- Slovenian (PetraNeural + RokNeural)
- 10 voice files total (5 per language)
- Language auto-detected from settings

**🎙️ FILM-QUALITY VOICES:**
Generated via Edge TTS with cinematic pacing:
- EN: JennyNeural (Kai) - Warm, emotional female
- EN: RyanNeural (Narrator) - Deep, mysterious British male
- SL: PetraNeural (Kai) - Slovenian female
- SL: RokNeural (Narrator) - Slovenian male

Voice files (per language):
1. 01_breathing.mp3 (~5-7s) - Confusion in darkness
2. 02_flyover.mp3 (~15-18s) - World narration
3. 03_awakening.mp3 (~6-8s) - Awakening confused
4. 04_id_card.mp3 (~12-15s) - Reading ID, recognition
5. 05_determination.mp3 (~10-12s) - Promise to find Ana

**🎬 ULTIMATE PROLOGUE SCENE:**
5 phases, ~70 seconds total:

Phase 1 (0:00-0:07): Black screen + breathing
Phase 2 (0:07-0:25): Narrator flyover
Phase 3 (0:25-0:40): Awakening in cellar (blur effect)
Phase 4 (0:40-0:58): ID card → twin photo cross-fade
Phase 5 (0:58-1:10): Determination + quest trigger → Game

**🎯 FEATURES:**
 Pure cinematic mode (NO HUD, NO UI, only story)
 Frame-perfect subtitle synchronization
 Adaptive subtitle timing (based on speech length)
 Smooth cross-fade transitions
 Blur effect (vision clearing)
 Emotional camera effects (flash, zoom)
 Quest notification integration
 ESC to skip functionality
 Noir ambient music (low volume, atmospheric)

**📊 SUBTITLE SYNC SYSTEM:**
- Auto-calculated read time (50ms per character)
- Minimum 3s display time
- Voice-synced appearance/disappearance
- Split long text for readability
- Bottom-center with safe margins
- Shadow + stroke for legibility

**📝 SCRIPTS:**
- generate_intro_multilingual.py - Dual language generation
- Timing metadata for perfect subtitle sync

**🎨 INTEGRATION:**
- Added to index.html + game.js
- StoryScene launches UltimatePrologueScene on New Game
- Language selection via i18n system
- Fallback to English if language not set

**STATUS: 100% PRODUCTION READY** 🎉
**Total intro duration: ~70 seconds**
**Multilingual: EN + SL **
**Cinematic quality: Film-grade **

🎥 **INTRO IS POLISHED TO PERFECTION!**
2026-01-08 17:46:25 +01:00

107 lines
4.2 KiB
JavaScript

// --- Global Error Handler ---
class ErrorHandler {
static init() {
window.onerror = (message, source, lineno, colno, error) => {
ErrorHandler.showError(message, source, lineno, colno, error);
return false;
};
window.addEventListener('unhandledrejection', (event) => {
ErrorHandler.showError('Unhandled Promise Rejection', '', 0, 0, event.reason);
});
console.log('🛡️ Global Error Handler Initialized');
}
static showError(message, source, lineno, colno, error) {
console.error('🔥 CRITICAL ERROR:', message);
if (document.getElementById('error-overlay')) return;
const div = document.createElement('div');
div.id = 'error-overlay';
div.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(20,0,0,0.95);color:#ffaaaa;z-index:999999;display:flex;flex-direction:column;justify-content:center;align-items:center;font-family:monospace;padding:20px;text-align:center;';
const stack = error && error.stack ? error.stack : '';
div.innerHTML = `
<h1 style="color:#ff4444;margin-bottom:20px;">☠️ OOPS! GAME CRASHED ☠️</h1>
<div style="background:rgba(0,0,0,0.5);padding:15px;border:1px solid #ff4444;max-width:800px;max-height:300px;overflow:auto;text-align:left;margin-bottom:20px;white-space:pre-wrap;">
<strong>${message}</strong><br>
<small>${source}:${lineno}:${colno}</small><br><br>
${stack}
</div>
<div>
<button onclick="window.location.reload()" style="padding:15px 30px;font-size:18px;font-weight:bold;background:#44aa44;color:white;border:none;cursor:pointer;border-radius:5px;margin-right:10px;">🔄 RELOAD GAME</button>
<button onclick="document.getElementById('error-overlay').remove()" style="padding:15px 30px;font-size:14px;background:#666;color:white;border:none;cursor:pointer;border-radius:5px;">IGNORE</button>
</div>
`;
document.body.appendChild(div);
}
}
ErrorHandler.init();
// Phaser Game Configuration
const config = {
type: Phaser.CANVAS, // Canvas renderer za pixel-perfect ostrino
width: 1024, // Larger viewport for better view
height: 768, // 4:3 aspect ratio
parent: 'game-container',
backgroundColor: '#000000', // Black background (not gray!)
pixelArt: false, // 🎨 SMOOTH 2D (was: true)
antialias: true, // 🎨 SMOOTH edges (was: false)
roundPixels: false, // 🎨 SMOOTH positioning (was: true)
render: {
pixelArt: false, // 🎨 SMOOTH 2D
antialias: true, // 🎨 SMOOTH edges
roundPixels: false, // 🎨 SMOOTH positioning
transparent: false,
clearBeforeRender: true,
powerPreference: 'high-performance',
premultipliedAlpha: true,
failIfMajorPerformanceCaveat: false,
// 🎨 LINEAR filtering for smooth tiles
mipmapFilter: 'NEAREST',
batchSize: 4096
},
physics: {
default: 'arcade',
arcade: {
gravity: { y: 0 },
debug: false
}
},
scene: [BootScene, PreloadScene, PrologueScene, EnhancedPrologueScene, UltimatePrologueScene, SystemsTestScene, TestVisualAudioScene, DemoScene, DemoSceneEnhanced, TiledTestScene, StoryScene, GameScene, UIScene, TownSquareScene],
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH
},
input: {
gamepad: true
},
fps: {
target: 60,
forceSetTimeOut: false
}
};
// Initialize game
const game = new Phaser.Game(config);
// 🌦️ GLOBAL WEATHER MANAGER - Controls weather across ALL scenes
import('./managers/GlobalWeatherManager.js').then(module => {
const GlobalWeatherManager = module.default;
game.weatherManager = new GlobalWeatherManager(game);
console.log('🌦️ Global Weather Manager initialized!');
}).catch(err => {
console.error('❌ Failed to load GlobalWeatherManager:', err);
});
// Global game state
window.gameState = {
currentScene: null,
debugMode: true
};
// God mode disabled by default (can be enabled via console)
window.godMode = false;
console.log('💀 Mrtva Dolina initialized!');