PHASE 18 COMPLETE: SaveManager - 3 save slots, auto-save every 5min, export/import, slot metadata
This commit is contained in:
@@ -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
273
src/systems/SaveManager.js
Normal 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');
|
||||
Reference in New Issue
Block a user