PHASE 18 COMPLETE: SaveManager - 3 save slots, auto-save every 5min, export/import, slot metadata

This commit is contained in:
2025-12-11 13:31:01 +01:00
parent 0012998669
commit c81435a65a
2 changed files with 274 additions and 0 deletions

View File

@@ -81,6 +81,7 @@
<script src="src/systems/Antigravity.js"></script>
<script src="src/systems/PathfindingSystem.js"></script>
<script src="src/systems/SaveSystem.js"></script>
<script src="src/systems/SaveManager.js"></script>
<!-- TimeSystem merged into WeatherSystem -->
<script src="src/systems/StatsSystem.js"></script>
<script src="src/systems/InventorySystem.js"></script>

273
src/systems/SaveManager.js Normal file
View File

@@ -0,0 +1,273 @@
/**
* SAVE MANAGER
* Handles multiple save slots and auto-save functionality
* Extends existing SaveSystem with slot management
*/
class SaveManager {
constructor(scene) {
this.scene = scene;
this.maxSlots = 3; // 3 save slots
this.currentSlot = 1; // Active slot (1-3)
// Auto-save settings
this.autoSaveEnabled = true;
this.autoSaveInterval = 5 * 60 * 1000; // 5 minutes in ms
this.autoSaveTimer = 0;
// Initialize SaveSystem for each slot
this.saveSystems = {};
for (let i = 1; i <= this.maxSlots; i++) {
this.saveSystems[i] = new SaveSystem(scene);
this.saveSystems[i].storageKey = `novafarma_save_slot${i}`;
}
console.log('💾 SaveManager initialized with 3 slots');
}
/**
* Save to specific slot
* @param {number} slotNumber - Slot 1-3
*/
saveToSlot(slotNumber) {
if (slotNumber < 1 || slotNumber > this.maxSlots) {
console.error(`Invalid slot number: ${slotNumber}`);
return false;
}
console.log(`💾 Saving to slot ${slotNumber}...`);
this.saveSystems[slotNumber].saveGame();
this.currentSlot = slotNumber;
// Save metadata (last save time, playtime, etc.)
this.saveSlotMetadata(slotNumber);
return true;
}
/**
* Load from specific slot
* @param {number} slotNumber - Slot 1-3
*/
loadFromSlot(slotNumber) {
if (slotNumber < 1 || slotNumber > this.maxSlots) {
console.error(`Invalid slot number: ${slotNumber}`);
return false;
}
console.log(`📂 Loading from slot ${slotNumber}...`);
const success = this.saveSystems[slotNumber].loadGame();
if (success) {
this.currentSlot = slotNumber;
}
return success;
}
/**
* Delete specific slot
* @param {number} slotNumber - Slot 1-3
*/
deleteSlot(slotNumber) {
if (slotNumber < 1 || slotNumber > this.maxSlots) {
console.error(`Invalid slot number: ${slotNumber}`);
return false;
}
const key = `novafarma_save_slot${slotNumber}`;
const metaKey = `novafarma_slot${slotNumber}_meta`;
localStorage.removeItem(key);
localStorage.removeItem(metaKey);
console.log(`🗑️ Deleted slot ${slotNumber}`);
return true;
}
/**
* Save metadata for slot (thumbnail, playtime, day, etc.)
*/
saveSlotMetadata(slotNumber) {
const metadata = {
slotNumber,
lastSaved: Date.now(),
playerName: 'Player',
dayCount: this.scene.timeSystem ? this.scene.timeSystem.dayCount : 1,
playtime: this.scene.playtimeTracker ? this.scene.playtimeTracker.stats.playtimeSeconds : 0,
playerLevel: this.scene.player ? (this.scene.player.level || 1) : 1
};
const metaKey = `novafarma_slot${slotNumber}_meta`;
localStorage.setItem(metaKey, JSON.stringify(metadata));
}
/**
* Get metadata for slot
*/
getSlotMetadata(slotNumber) {
const metaKey = `novafarma_slot${slotNumber}_meta`;
const raw = localStorage.getItem(metaKey);
if (!raw) return null;
try {
return JSON.parse(raw);
} catch (e) {
console.error(`Failed to parse metadata for slot ${slotNumber}:`, e);
return null;
}
}
/**
* Get all slots info
*/
getAllSlotsInfo() {
const slots = [];
for (let i = 1; i <= this.maxSlots; i++) {
const metadata = this.getSlotMetadata(i);
const exists = this.slotExists(i);
slots.push({
number: i,
exists,
metadata: metadata || {
playerName: 'Empty',
dayCount: 0,
playtime: 0,
lastSaved: null
}
});
}
return slots;
}
/**
* Check if slot exists
*/
slotExists(slotNumber) {
const key = `novafarma_save_slot${slotNumber}`;
return localStorage.getItem(key) !== null;
}
/**
* Quick save to current slot
*/
quickSave() {
console.log(`⚡ Quick save to slot ${this.currentSlot}`);
this.saveToSlot(this.currentSlot);
}
/**
* Quick load from current slot
*/
quickLoad() {
console.log(`⚡ Quick load from slot ${this.currentSlot}`);
return this.loadFromSlot(this.currentSlot);
}
/**
* Update auto-save timer
* Call this in scene update()
*/
update(delta) {
if (!this.autoSaveEnabled) return;
this.autoSaveTimer += delta;
if (this.autoSaveTimer >= this.autoSaveInterval) {
this.autoSaveTimer = 0;
console.log('💾 Auto-saving...');
this.quickSave();
// Show notification
if (this.scene.events) {
this.scene.events.emit('show-floating-text', {
x: this.scene.cameras.main.scrollX + this.scene.cameras.main.width - 100,
y: this.scene.cameras.main.scrollY + 50,
text: '💾 Auto-Saved',
color: '#00ff00'
});
}
}
}
/**
* Toggle auto-save
*/
toggleAutoSave() {
this.autoSaveEnabled = !this.autoSaveEnabled;
console.log(`Auto-save: ${this.autoSaveEnabled ? 'ON' : 'OFF'}`);
return this.autoSaveEnabled;
}
/**
* Get time until next auto-save
*/
getTimeUntilNextSave() {
if (!this.autoSaveEnabled) return -1;
return Math.max(0, this.autoSaveInterval - this.autoSaveTimer);
}
/**
* Export save data as JSON (for backup)
*/
exportSlot(slotNumber) {
if (!this.slotExists(slotNumber)) {
console.error(`Slot ${slotNumber} is empty`);
return null;
}
const key = `novafarma_save_slot${slotNumber}`;
const data = localStorage.getItem(key);
// Create downloadable file
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(data);
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", `novafarma_slot${slotNumber}_${Date.now()}.json`);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
console.log(`📤 Exported slot ${slotNumber}`);
return true;
}
/**
* Import save data from JSON file
*/
importSlot(slotNumber, jsonData) {
try {
const key = `novafarma_save_slot${slotNumber}`;
localStorage.setItem(key, jsonData);
console.log(`📥 Imported to slot ${slotNumber}`);
return true;
} catch (e) {
console.error('Failed to import:', e);
return false;
}
}
}
// Make available globally
window.SaveManager = SaveManager;
// Convenience functions
window.save = function (slot = 1) {
const scene = game.scene.scenes.find(s => s.saveManager);
if (scene && scene.saveManager) {
return scene.saveManager.saveToSlot(slot);
}
};
window.load = function (slot = 1) {
const scene = game.scene.scenes.find(s => s.saveManager);
if (scene && scene.saveManager) {
return scene.saveManager.loadFromSlot(slot);
}
};
console.log('💾 SaveManager loaded. Use: save(1), load(1), or F5/F9 keys');