diff --git a/src/systems/GameManager.js b/src/systems/GameManager.js new file mode 100644 index 000000000..ea58e85d6 --- /dev/null +++ b/src/systems/GameManager.js @@ -0,0 +1,398 @@ +/** + * GAME MANAGER - AUTO-SAVE SYSTEM + * "The Silent Protector" - Never lose progress! + */ + +class GameManager { + constructor(scene) { + this.scene = scene; + this.saveKey = 'mrtva_dolina_save'; // Slot_0 + + // Auto-save state + this.lastAutoSave = Date.now(); + this.autoSaveInterval = 5 * 60 * 1000; // 5 minutes + this.isSaving = false; + + // Save indicator + this.saveIndicator = null; + + // Initialize + this.init(); + } + + init() { + console.log('💾 GameManager initialized - Auto-save active'); + + // Start periodic auto-save timer + this.startPeriodicAutoSave(); + + // Listen for scene transitions + this.scene.events.on('shutdown', () => this.onSceneTransition()); + + // Listen for progression events + this.listenForProgressionEvents(); + } + + /** + * TRIGGER 1: SCENE TRANSITION SAVE + */ + onSceneTransition() { + console.log('🚪 Scene transition detected - Auto-saving...'); + this.autoSaveGame('Scene Transition'); + } + + /** + * TRIGGER 2: PROGRESSION MILESTONE SAVE + */ + listenForProgressionEvents() { + // Listen for aging events + this.scene.events.on('kai-aged', (data) => { + console.log(`⏰ Kai aged to ${data.newAge} - Auto-saving...`); + this.autoSaveGame('Aging Milestone'); + }); + + // Listen for memory found + this.scene.events.on('memory-found', (data) => { + console.log(`📸 Memory found (${data.id}) - Auto-saving...`); + this.autoSaveGame('Memory Found'); + }); + + // Listen for Gronk level up + if (window.gronkStats) { + window.addEventListener('gronk-levelup', (e) => { + console.log(`💨 Gronk level ${e.detail.level} - Auto-saving...`); + this.autoSaveGame('Gronk Level Up'); + }); + } + + // Listen for companion unlocks + window.addEventListener('companion-unlocked', (e) => { + console.log(`🐕 Companion unlocked (${e.detail.name}) - Auto-saving...`); + this.autoSaveGame('Companion Unlock'); + }); + } + + /** + * TRIGGER 3: PERIODIC AUTO-SAVE (5 Minutes) + */ + startPeriodicAutoSave() { + this.periodicTimer = this.scene.time.addEvent({ + delay: this.autoSaveInterval, + callback: () => { + const timeSinceLastSave = Date.now() - this.lastAutoSave; + if (timeSinceLastSave >= this.autoSaveInterval) { + console.log('⏱️ 5 minutes elapsed - Auto-saving...'); + this.autoSaveGame('Periodic (5min)'); + } + }, + loop: true + }); + } + + /** + * AUTO-SAVE GAME (Silent, no interruption) + */ + autoSaveGame(reason = 'Manual') { + if (this.isSaving) { + console.log('⚠️ Save already in progress, skipping...'); + return; + } + + this.isSaving = true; + + console.log(`💾 AUTO-SAVING (${reason})...`); + + // Show save indicator + this.showSaveIndicator(); + + try { + // Gather save data + const saveData = this.gatherSaveData(); + + // Save to LocalStorage (Slot_0) + localStorage.setItem(this.saveKey, JSON.stringify(saveData)); + + // Update timestamp + this.lastAutoSave = Date.now(); + + console.log('✅ Auto-save complete!'); + console.log(` Reason: ${reason}`); + console.log(` Size: ${JSON.stringify(saveData).length} bytes`); + + } catch (error) { + console.error('❌ Auto-save failed:', error); + } finally { + this.isSaving = false; + } + } + + /** + * GATHER ALL SAVE DATA + */ + gatherSaveData() { + const data = { + version: '1.0.0', + lastSaved: new Date().toISOString(), + playtime: this.getPlaytime(), + + // Player data + player: this.getPlayerData(), + + // Progress + progress: this.getProgressData(), + + // Companions + companions: this.getCompanionData(), + + // Farm state + farm: this.getFarmData(), + + // Economy + economy: this.getEconomyData(), + + // Current scene + currentScene: this.scene.scene.key + }; + + return data; + } + + /** + * GET PLAYER DATA + */ + getPlayerData() { + // Get PlayerStats if exists + let playerStats = null; + if (this.scene.playerStats) { + playerStats = this.scene.playerStats.getAgeInfo(); + } else { + // Load from LocalStorage + const stored = localStorage.getItem('player_stats'); + if (stored) { + playerStats = JSON.parse(stored); + } + } + + return { + position: this.getPlayerPosition(), + age_level: playerStats?.level || 1, + current_age: playerStats?.age || 14, + age_sprite: playerStats?.sprite || 'kai_age14', + inventory: this.getInventory(), + health: 100, + stamina: 100, + equipped_tools: {} + }; + } + + /** + * GET PROGRESS DATA + */ + getProgressData() { + return { + memories_found: this.getMemoriesFound(), + total_memories: 100, + quests_completed: [], + npcs_met: [], + biomes_unlocked: ['grassland'] + }; + } + + /** + * GET COMPANION DATA + */ + getCompanionData() { + const companions = { + gronk: { + unlocked: false, + level: 1, + xp: 0 + }, + susi: { + unlocked: false, + position: { x: 0, y: 0 }, + loyalty: 0 + } + }; + + // Check Gronk stats + if (window.gronkStats) { + const gronkData = gronkStats.getStats(); + companions.gronk = { + unlocked: true, + level: gronkData.level, + xp: gronkData.xp + }; + } + + // Check Susi unlock + const susiUnlocked = localStorage.getItem('susi_unlocked'); + if (susiUnlocked === 'true') { + companions.susi.unlocked = true; + } + + return companions; + } + + /** + * GET FARM DATA + */ + getFarmData() { + return { + crops: [], + buildings: [], + animals: [], + resources: {} + }; + } + + /** + * GET ECONOMY DATA + */ + getEconomyData() { + return { + money: 0, + cannabis_seeds: 5, // Starting capital! + cannabis_harvested: 0 + }; + } + + /** + * HELPER: Get player position + */ + getPlayerPosition() { + if (this.scene.player) { + return { + x: this.scene.player.x, + y: this.scene.player.y + }; + } + return { x: 640, y: 360 }; // Default center + } + + /** + * HELPER: Get inventory + */ + getInventory() { + // TODO: Get from inventory system + return []; + } + + /** + * HELPER: Get memories found + */ + getMemoriesFound() { + const stored = localStorage.getItem('player_stats'); + if (stored) { + const data = JSON.parse(stored); + return data.memoriesFound || 0; + } + return 0; + } + + /** + * HELPER: Get playtime + */ + getPlaytime() { + const stored = localStorage.getItem('playtime'); + if (stored) { + return parseInt(stored) + Math.floor(this.scene.time.now / 1000); + } + return Math.floor(this.scene.time.now / 1000); + } + + /** + * SHOW SAVE INDICATOR (Spinning Longboard) + */ + showSaveIndicator() { + if (this.saveIndicator) { + return; // Already showing + } + + const width = this.scene.cameras.main.width; + const height = this.scene.cameras.main.height; + + // Create longboard emoji as indicator + this.saveIndicator = this.scene.add.text( + width - 80, + height - 80, + '🛹', + { + fontSize: '32px' + } + ); + this.saveIndicator.setScrollFactor(0); + this.saveIndicator.setDepth(9999); + this.saveIndicator.setAlpha(0); + + // Add "Saving..." text + const savingText = this.scene.add.text( + width - 140, + height - 50, + 'Saving...', + { + fontSize: '14px', + fontFamily: 'Georgia, serif', + color: '#f4e4c1', + stroke: '#000000', + strokeThickness: 3 + } + ); + savingText.setScrollFactor(0); + savingText.setDepth(9999); + savingText.setAlpha(0); + + // Fade in + this.scene.tweens.add({ + targets: [this.saveIndicator, savingText], + alpha: 0.8, + duration: 300 + }); + + // Spin animation + this.scene.tweens.add({ + targets: this.saveIndicator, + angle: 360, + duration: 1000, + repeat: 1 + }); + + // Fade out after 2 seconds + this.scene.time.delayedCall(2000, () => { + this.scene.tweens.add({ + targets: [this.saveIndicator, savingText], + alpha: 0, + duration: 500, + onComplete: () => { + if (this.saveIndicator) { + this.saveIndicator.destroy(); + this.saveIndicator = null; + } + if (savingText) { + savingText.destroy(); + } + } + }); + }); + } + + /** + * MANUAL SAVE (for explicit save points) + */ + manualSave() { + console.log('💾 Manual save requested...'); + this.autoSaveGame('Manual Save'); + } + + /** + * CLEANUP + */ + destroy() { + if (this.periodicTimer) { + this.periodicTimer.remove(); + } + console.log('💾 GameManager destroyed'); + } +} + +export default GameManager;