428 lines
12 KiB
JavaScript
428 lines
12 KiB
JavaScript
/**
|
|
* SAVE SYSTEM EXPANSION
|
|
* Advanced save/load with cloud sync, multiple slots, and auto-save
|
|
*/
|
|
class SaveSystemExpansion {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.enabled = true;
|
|
|
|
// Save slots
|
|
this.maxSlots = 5;
|
|
this.currentSlot = 1;
|
|
this.saveSlots = new Map();
|
|
|
|
// Cloud sync
|
|
this.cloudSyncEnabled = false;
|
|
this.lastCloudSync = 0;
|
|
this.syncInterval = 300000; // 5 minutes
|
|
|
|
// Auto-save
|
|
this.autoSaveEnabled = true;
|
|
this.autoSaveInterval = 300000; // 5 minutes
|
|
this.lastAutoSave = 0;
|
|
this.preventSaveDuringCombat = true;
|
|
|
|
// Backup
|
|
this.backupEnabled = true;
|
|
this.maxBackups = 3;
|
|
|
|
this.loadSaveSlots();
|
|
this.init();
|
|
|
|
console.log('✅ Save System Expansion initialized');
|
|
}
|
|
|
|
init() {
|
|
this.setupAutoSave();
|
|
console.log('💾 Save system ready');
|
|
}
|
|
|
|
// ========== SAVE SLOTS ==========
|
|
|
|
createSaveSlot(slotNumber, saveName) {
|
|
if (slotNumber < 1 || slotNumber > this.maxSlots) {
|
|
console.log('❌ Invalid slot number');
|
|
return false;
|
|
}
|
|
|
|
const saveData = this.gatherSaveData();
|
|
|
|
const slot = {
|
|
slotNumber,
|
|
name: saveName || `Save ${slotNumber}`,
|
|
timestamp: Date.now(),
|
|
playTime: this.calculatePlayTime(),
|
|
thumbnail: this.generateThumbnail(),
|
|
data: saveData
|
|
};
|
|
|
|
this.saveSlots.set(slotNumber, slot);
|
|
this.saveToDisk(slotNumber);
|
|
|
|
console.log(`💾 Save created: Slot ${slotNumber} - ${slot.name}`);
|
|
return true;
|
|
}
|
|
|
|
loadSaveSlot(slotNumber) {
|
|
const slot = this.saveSlots.get(slotNumber);
|
|
if (!slot) {
|
|
console.log('❌ Save slot not found');
|
|
return false;
|
|
}
|
|
|
|
this.applySaveData(slot.data);
|
|
this.currentSlot = slotNumber;
|
|
|
|
console.log(`📂 Save loaded: Slot ${slotNumber} - ${slot.name}`);
|
|
return true;
|
|
}
|
|
|
|
deleteSaveSlot(slotNumber) {
|
|
if (!this.saveSlots.has(slotNumber)) {
|
|
console.log('❌ Save slot not found');
|
|
return false;
|
|
}
|
|
|
|
this.saveSlots.delete(slotNumber);
|
|
localStorage.removeItem(`novafarma_save_${slotNumber}`);
|
|
|
|
console.log(`🗑️ Save deleted: Slot ${slotNumber}`);
|
|
return true;
|
|
}
|
|
|
|
renameSaveSlot(slotNumber, newName) {
|
|
const slot = this.saveSlots.get(slotNumber);
|
|
if (!slot) return false;
|
|
|
|
slot.name = newName;
|
|
this.saveToDisk(slotNumber);
|
|
|
|
console.log(`✏️ Save renamed: Slot ${slotNumber} → ${newName}`);
|
|
return true;
|
|
}
|
|
|
|
// ========== SAVE DATA ==========
|
|
|
|
gatherSaveData() {
|
|
const data = {
|
|
version: '3.0.0',
|
|
timestamp: Date.now(),
|
|
|
|
// Player data
|
|
player: this.getPlayerData(),
|
|
|
|
// World data
|
|
world: this.getWorldData(),
|
|
|
|
// Systems data
|
|
systems: this.getSystemsData(),
|
|
|
|
// Progress
|
|
progress: this.getProgressData()
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
getPlayerData() {
|
|
if (!this.scene.player) return null;
|
|
|
|
return {
|
|
position: this.scene.player.getPosition(),
|
|
health: this.scene.statsSystem?.health || 100,
|
|
hunger: this.scene.statsSystem?.hunger || 100,
|
|
level: this.scene.skillTree?.level || 1,
|
|
xp: this.scene.skillTree?.xp || 0
|
|
};
|
|
}
|
|
|
|
getWorldData() {
|
|
return {
|
|
time: this.scene.weatherSystem?.currentTime || 0,
|
|
day: this.scene.weatherSystem?.currentDay || 1,
|
|
weather: this.scene.weatherSystem?.currentWeather || 'clear',
|
|
season: this.scene.weatherSystem?.currentSeason || 'spring'
|
|
};
|
|
}
|
|
|
|
getSystemsData() {
|
|
return {
|
|
inventory: this.scene.inventorySystem?.items || {},
|
|
skills: this.scene.skillTree?.skills || {},
|
|
quests: {
|
|
active: Array.from(this.scene.storyQuest?.activeQuests || []),
|
|
completed: Array.from(this.scene.storyQuest?.completedQuests || [])
|
|
},
|
|
automation: {
|
|
workers: this.scene.farmAutomation?.zombieWorkers.length || 0,
|
|
buildings: this.scene.farmAutomation?.automationBuildings.size || 0
|
|
}
|
|
};
|
|
}
|
|
|
|
getProgressData() {
|
|
return {
|
|
achievements: this.scene.uiGraphics?.unlockedAchievements || [],
|
|
bossesDefeated: Array.from(this.scene.bossBattles?.defeatedBosses || []),
|
|
recipesDiscovered: Array.from(this.scene.cooking?.knownRecipes || [])
|
|
};
|
|
}
|
|
|
|
applySaveData(data) {
|
|
// Restore player
|
|
if (data.player && this.scene.player) {
|
|
this.scene.player.setPosition(data.player.position.x, data.player.position.y);
|
|
if (this.scene.statsSystem) {
|
|
this.scene.statsSystem.health = data.player.health;
|
|
this.scene.statsSystem.hunger = data.player.hunger;
|
|
}
|
|
}
|
|
|
|
// Restore world
|
|
if (data.world && this.scene.weatherSystem) {
|
|
this.scene.weatherSystem.currentTime = data.world.time;
|
|
this.scene.weatherSystem.currentDay = data.world.day;
|
|
this.scene.weatherSystem.setWeather(data.world.weather);
|
|
}
|
|
|
|
// Restore systems
|
|
if (data.systems) {
|
|
if (this.scene.inventorySystem && data.systems.inventory) {
|
|
this.scene.inventorySystem.items = data.systems.inventory;
|
|
}
|
|
}
|
|
|
|
console.log('✅ Save data applied');
|
|
}
|
|
|
|
// ========== AUTO-SAVE ==========
|
|
|
|
setupAutoSave() {
|
|
if (!this.autoSaveEnabled) return;
|
|
|
|
setInterval(() => {
|
|
this.performAutoSave();
|
|
}, this.autoSaveInterval);
|
|
|
|
console.log(`⏰ Auto-save enabled (every ${this.autoSaveInterval / 1000}s)`);
|
|
}
|
|
|
|
performAutoSave() {
|
|
if (!this.canAutoSave()) return;
|
|
|
|
this.quickSave();
|
|
this.lastAutoSave = Date.now();
|
|
|
|
console.log('💾 Auto-save performed');
|
|
}
|
|
|
|
canAutoSave() {
|
|
// Don't save during combat
|
|
if (this.preventSaveDuringCombat && this.isInCombat()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
isInCombat() {
|
|
// Check if player is in combat
|
|
return false; // Placeholder
|
|
}
|
|
|
|
// ========== QUICK SAVE/LOAD ==========
|
|
|
|
quickSave() {
|
|
return this.createSaveSlot(this.currentSlot, `Quick Save`);
|
|
}
|
|
|
|
quickLoad() {
|
|
return this.loadSaveSlot(this.currentSlot);
|
|
}
|
|
|
|
// ========== CLOUD SYNC ==========
|
|
|
|
enableCloudSync() {
|
|
this.cloudSyncEnabled = true;
|
|
console.log('☁️ Cloud sync enabled');
|
|
}
|
|
|
|
disableCloudSync() {
|
|
this.cloudSyncEnabled = false;
|
|
console.log('☁️ Cloud sync disabled');
|
|
}
|
|
|
|
syncToCloud(slotNumber) {
|
|
if (!this.cloudSyncEnabled) {
|
|
console.log('❌ Cloud sync is disabled');
|
|
return false;
|
|
}
|
|
|
|
const slot = this.saveSlots.get(slotNumber);
|
|
if (!slot) return false;
|
|
|
|
// Upload to Steam Cloud
|
|
console.log(`☁️ Uploading save to cloud: Slot ${slotNumber}`);
|
|
|
|
// Simulate cloud upload
|
|
this.lastCloudSync = Date.now();
|
|
|
|
return true;
|
|
}
|
|
|
|
syncFromCloud(slotNumber) {
|
|
if (!this.cloudSyncEnabled) {
|
|
console.log('❌ Cloud sync is disabled');
|
|
return false;
|
|
}
|
|
|
|
console.log(`☁️ Downloading save from cloud: Slot ${slotNumber}`);
|
|
|
|
// Simulate cloud download
|
|
return true;
|
|
}
|
|
|
|
resolveConflict(localSlot, cloudSlot) {
|
|
// Show UI to let player choose which save to keep
|
|
console.log('⚠️ Save conflict detected');
|
|
|
|
if (localSlot.timestamp > cloudSlot.timestamp) {
|
|
console.log('📤 Local save is newer - uploading');
|
|
return 'upload';
|
|
} else {
|
|
console.log('📥 Cloud save is newer - downloading');
|
|
return 'download';
|
|
}
|
|
}
|
|
|
|
// ========== BACKUP SYSTEM ==========
|
|
|
|
createBackup(slotNumber) {
|
|
if (!this.backupEnabled) return false;
|
|
|
|
const slot = this.saveSlots.get(slotNumber);
|
|
if (!slot) return false;
|
|
|
|
const backupKey = `novafarma_backup_${slotNumber}_${Date.now()}`;
|
|
localStorage.setItem(backupKey, JSON.stringify(slot));
|
|
|
|
// Clean old backups
|
|
this.cleanOldBackups(slotNumber);
|
|
|
|
console.log(`💾 Backup created: ${backupKey}`);
|
|
return true;
|
|
}
|
|
|
|
cleanOldBackups(slotNumber) {
|
|
const backups = this.getBackups(slotNumber);
|
|
|
|
if (backups.length > this.maxBackups) {
|
|
// Remove oldest backups
|
|
const toRemove = backups.slice(0, backups.length - this.maxBackups);
|
|
toRemove.forEach(backup => {
|
|
localStorage.removeItem(backup.key);
|
|
});
|
|
}
|
|
}
|
|
|
|
getBackups(slotNumber) {
|
|
const backups = [];
|
|
const prefix = `novafarma_backup_${slotNumber}_`;
|
|
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && key.startsWith(prefix)) {
|
|
const timestamp = parseInt(key.split('_').pop());
|
|
backups.push({ key, timestamp });
|
|
}
|
|
}
|
|
|
|
return backups.sort((a, b) => a.timestamp - b.timestamp);
|
|
}
|
|
|
|
restoreBackup(backupKey) {
|
|
const backup = localStorage.getItem(backupKey);
|
|
if (!backup) {
|
|
console.log('❌ Backup not found');
|
|
return false;
|
|
}
|
|
|
|
const slot = JSON.parse(backup);
|
|
this.saveSlots.set(slot.slotNumber, slot);
|
|
|
|
console.log(`📂 Backup restored: ${backupKey}`);
|
|
return true;
|
|
}
|
|
|
|
// ========== THUMBNAILS ==========
|
|
|
|
generateThumbnail() {
|
|
// Generate screenshot thumbnail
|
|
return {
|
|
width: 160,
|
|
height: 90,
|
|
data: null // Base64 image data
|
|
};
|
|
}
|
|
|
|
// ========== PLAY TIME ==========
|
|
|
|
calculatePlayTime() {
|
|
// Calculate total play time in seconds
|
|
return 0; // Placeholder
|
|
}
|
|
|
|
formatPlayTime(seconds) {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
return `${hours}h ${minutes}m`;
|
|
}
|
|
|
|
// ========== PERSISTENCE ==========
|
|
|
|
saveToDisk(slotNumber) {
|
|
const slot = this.saveSlots.get(slotNumber);
|
|
if (!slot) return;
|
|
|
|
localStorage.setItem(`novafarma_save_${slotNumber}`, JSON.stringify(slot));
|
|
|
|
// Create backup
|
|
if (this.backupEnabled) {
|
|
this.createBackup(slotNumber);
|
|
}
|
|
}
|
|
|
|
loadSaveSlots() {
|
|
for (let i = 1; i <= this.maxSlots; i++) {
|
|
const saved = localStorage.getItem(`novafarma_save_${i}`);
|
|
if (saved) {
|
|
try {
|
|
const slot = JSON.parse(saved);
|
|
this.saveSlots.set(i, slot);
|
|
} catch (error) {
|
|
console.error(`Failed to load save slot ${i}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Loaded ${this.saveSlots.size} save slots`);
|
|
}
|
|
|
|
// ========== UPDATE ==========
|
|
|
|
update(delta) {
|
|
// Auto cloud sync
|
|
if (this.cloudSyncEnabled) {
|
|
const now = Date.now();
|
|
if (now - this.lastCloudSync > this.syncInterval) {
|
|
this.syncToCloud(this.currentSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
console.log('💾 Save System Expansion destroyed');
|
|
}
|
|
}
|