372 lines
9.7 KiB
JavaScript
372 lines
9.7 KiB
JavaScript
/**
|
|
* SAVE/LOAD SYSTEM & AGING ENGINE
|
|
* Persistent storage for player progress, Kai aging, and companion states
|
|
*/
|
|
|
|
class SaveLoadSystem {
|
|
constructor() {
|
|
this.saveKey = 'mrtva_dolina_save';
|
|
this.currentSave = null;
|
|
this.autoSaveInterval = 300000; // 5 minutes
|
|
this.autoSaveTimer = null;
|
|
}
|
|
|
|
/**
|
|
* CREATE NEW SAVE FILE
|
|
*/
|
|
createNewSave() {
|
|
this.currentSave = {
|
|
version: '1.0.0',
|
|
created: new Date().toISOString(),
|
|
lastSaved: new Date().toISOString(),
|
|
|
|
// Player data
|
|
player: {
|
|
position: { x: 0, y: 0 },
|
|
age_level: 1, // 1-9 (corresponds to age stages)
|
|
current_age: 14, // Actual age in years
|
|
age_sprite: 'kai_age14',
|
|
inventory: [],
|
|
equipped_tools: {
|
|
weapon: null,
|
|
tool: null
|
|
},
|
|
health: 100,
|
|
stamina: 100,
|
|
stats: {
|
|
strength: 1,
|
|
speed: 1,
|
|
farming: 1
|
|
}
|
|
},
|
|
|
|
// Progress tracking
|
|
progress: {
|
|
memories_found: 0,
|
|
total_memories: 100,
|
|
quests_completed: [],
|
|
quests_active: [],
|
|
npcs_met: [],
|
|
biomes_unlocked: ['grassland'],
|
|
locations_discovered: [],
|
|
enemies_defeated: 0,
|
|
crops_harvested: 0,
|
|
buildings_built: 0
|
|
},
|
|
|
|
// Companions
|
|
companions: {
|
|
gronk: {
|
|
unlocked: false,
|
|
level: 1,
|
|
xp: 0,
|
|
stats: {}
|
|
},
|
|
susi: {
|
|
unlocked: false,
|
|
position: { x: 0, y: 0 },
|
|
loyalty: 100
|
|
}
|
|
},
|
|
|
|
// Farm state
|
|
farm: {
|
|
crops: [],
|
|
buildings: [],
|
|
animals: [],
|
|
resources: {
|
|
wood: 0,
|
|
stone: 0,
|
|
cannabis_capital: 0
|
|
}
|
|
},
|
|
|
|
// Economic state
|
|
economy: {
|
|
money: 0,
|
|
cannabis_seeds: 5, // Starting capital!
|
|
cannabis_harvested: 0,
|
|
total_earnings: 0
|
|
},
|
|
|
|
// Game settings
|
|
settings: {
|
|
difficulty: 'normal',
|
|
language: 'en',
|
|
music_volume: 0.7,
|
|
sfx_volume: 0.8
|
|
},
|
|
|
|
// Playtime
|
|
playtime: 0 // seconds
|
|
};
|
|
|
|
console.log('💾 New save file created');
|
|
return this.currentSave;
|
|
}
|
|
|
|
/**
|
|
* SAVE GAME
|
|
*/
|
|
save() {
|
|
if (!this.currentSave) {
|
|
this.currentSave = this.createNewSave();
|
|
}
|
|
|
|
this.currentSave.lastSaved = new Date().toISOString();
|
|
|
|
try {
|
|
localStorage.setItem(this.saveKey, JSON.stringify(this.currentSave));
|
|
console.log('💾 Game saved successfully');
|
|
|
|
// Show save notification
|
|
this.showSaveNotification();
|
|
|
|
return true;
|
|
} catch (e) {
|
|
console.error('❌ Save failed:', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* LOAD GAME
|
|
*/
|
|
load() {
|
|
try {
|
|
const saved = localStorage.getItem(this.saveKey);
|
|
if (!saved) {
|
|
console.log('📂 No save file found, creating new...');
|
|
return this.createNewSave();
|
|
}
|
|
|
|
this.currentSave = JSON.parse(saved);
|
|
console.log('📂 Game loaded successfully');
|
|
console.log(' Age Level:', this.currentSave.player.age_level);
|
|
console.log(' Memories:', this.currentSave.progress.memories_found + '/' + this.currentSave.progress.total_memories);
|
|
|
|
return this.currentSave;
|
|
} catch (e) {
|
|
console.error('❌ Load failed:', e);
|
|
return this.createNewSave();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* UPDATE PLAYER DATA
|
|
*/
|
|
updatePlayer(data) {
|
|
if (!this.currentSave) this.load();
|
|
|
|
this.currentSave.player = {
|
|
...this.currentSave.player,
|
|
...data
|
|
};
|
|
|
|
this.save();
|
|
}
|
|
|
|
/**
|
|
* UPDATE PROGRESS
|
|
*/
|
|
updateProgress(data) {
|
|
if (!this.currentSave) this.load();
|
|
|
|
this.currentSave.progress = {
|
|
...this.currentSave.progress,
|
|
...data
|
|
};
|
|
|
|
// Check if aging should trigger
|
|
this.checkAgingProgress();
|
|
|
|
this.save();
|
|
}
|
|
|
|
/**
|
|
* START AUTO-SAVE
|
|
*/
|
|
startAutoSave() {
|
|
if (this.autoSaveTimer) {
|
|
clearInterval(this.autoSaveTimer);
|
|
}
|
|
|
|
this.autoSaveTimer = setInterval(() => {
|
|
this.save();
|
|
console.log('💾 Auto-save triggered');
|
|
}, this.autoSaveInterval);
|
|
}
|
|
|
|
/**
|
|
* STOP AUTO-SAVE
|
|
*/
|
|
stopAutoSave() {
|
|
if (this.autoSaveTimer) {
|
|
clearInterval(this.autoSaveTimer);
|
|
this.autoSaveTimer = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* AGING ENGINE - CHECK IF KAI SHOULD AGE UP
|
|
*/
|
|
checkAgingProgress() {
|
|
if (!this.currentSave) return;
|
|
|
|
const memoriesFound = this.currentSave.progress.memories_found;
|
|
const totalMemories = this.currentSave.progress.total_memories;
|
|
const progress = (memoriesFound / totalMemories) * 100;
|
|
|
|
let newAgeLevel = 1;
|
|
let newAge = 14;
|
|
let newSprite = 'kai_age14';
|
|
|
|
// Age progression based on memory recovery
|
|
if (progress >= 90) {
|
|
newAgeLevel = 9;
|
|
newAge = 60;
|
|
newSprite = 'kai_age60';
|
|
} else if (progress >= 75) {
|
|
newAgeLevel = 7;
|
|
newAge = 50;
|
|
newSprite = 'kai_age50';
|
|
} else if (progress >= 60) {
|
|
newAgeLevel = 6;
|
|
newAge = 40;
|
|
newSprite = 'kai_age40';
|
|
} else if (progress >= 50) {
|
|
newAgeLevel = 5;
|
|
newAge = 30;
|
|
newSprite = 'kai_age30';
|
|
} else if (progress >= 35) {
|
|
newAgeLevel = 4;
|
|
newAge = 25;
|
|
newSprite = 'kai_age25';
|
|
} else if (progress >= 25) {
|
|
newAgeLevel = 3;
|
|
newAge = 20;
|
|
newSprite = 'kai_age20';
|
|
} else if (progress >= 10) {
|
|
newAgeLevel = 2;
|
|
newAge = 16;
|
|
newSprite = 'kai_age16';
|
|
}
|
|
|
|
// Check if age changed
|
|
if (newAgeLevel > this.currentSave.player.age_level) {
|
|
this.triggerAging(newAgeLevel, newAge, newSprite);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TRIGGER AGING CUTSCENE
|
|
*/
|
|
triggerAging(newLevel, newAge, newSprite) {
|
|
const oldLevel = this.currentSave.player.age_level;
|
|
const oldAge = this.currentSave.player.current_age;
|
|
|
|
// Update save data
|
|
this.currentSave.player.age_level = newLevel;
|
|
this.currentSave.player.current_age = newAge;
|
|
this.currentSave.player.age_sprite = newSprite;
|
|
|
|
console.log(`⏰ KAI AGES UP!`);
|
|
console.log(` ${oldAge} → ${newAge} years old`);
|
|
console.log(` Sprite: ${newSprite}`);
|
|
|
|
// Emit aging event for cutscene
|
|
const event = new CustomEvent('kai-aging', {
|
|
detail: {
|
|
oldLevel: oldLevel,
|
|
newLevel: newLevel,
|
|
oldAge: oldAge,
|
|
newAge: newAge,
|
|
newSprite: newSprite,
|
|
memoriesFound: this.currentSave.progress.memories_found
|
|
}
|
|
});
|
|
window.dispatchEvent(event);
|
|
|
|
this.save();
|
|
}
|
|
|
|
/**
|
|
* SHOW SAVE NOTIFICATION
|
|
*/
|
|
showSaveNotification() {
|
|
const event = new CustomEvent('game-saved', {
|
|
detail: {
|
|
time: new Date().toLocaleTimeString(),
|
|
slot: 1
|
|
}
|
|
});
|
|
window.dispatchEvent(event);
|
|
}
|
|
|
|
/**
|
|
* GET CURRENT SAVE DATA
|
|
*/
|
|
getCurrentSave() {
|
|
if (!this.currentSave) {
|
|
this.load();
|
|
}
|
|
return this.currentSave;
|
|
}
|
|
|
|
/**
|
|
* DELETE SAVE
|
|
*/
|
|
deleteSave() {
|
|
if (confirm('Are you sure you want to delete your save file?')) {
|
|
localStorage.removeItem(this.saveKey);
|
|
this.currentSave = null;
|
|
console.log('🗑️ Save file deleted');
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* EXPORT SAVE (for backup)
|
|
*/
|
|
exportSave() {
|
|
if (!this.currentSave) return null;
|
|
|
|
const saveData = JSON.stringify(this.currentSave, null, 2);
|
|
const blob = new Blob([saveData], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `mrtva_dolina_save_${Date.now()}.json`;
|
|
a.click();
|
|
|
|
console.log('📤 Save exported');
|
|
}
|
|
|
|
/**
|
|
* IMPORT SAVE (from backup)
|
|
*/
|
|
importSave(fileInput) {
|
|
const file = fileInput.files[0];
|
|
if (!file) return;
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
try {
|
|
const imported = JSON.parse(e.target.result);
|
|
localStorage.setItem(this.saveKey, JSON.stringify(imported));
|
|
this.currentSave = imported;
|
|
console.log('📥 Save imported successfully');
|
|
} catch (err) {
|
|
console.error('❌ Import failed:', err);
|
|
}
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
const saveLoadSystem = new SaveLoadSystem();
|
|
export default saveLoadSystem;
|