Files
deepseek ac00687480 Arcane Tycoon — Heroes & Magic theme park tycoon game
Complete browser game inspired by OpenRCT2 with fantasy twist:
- Custom roller coaster designer with physics-based ratings + on-ride POV
- 10 animated rides, 7 shops, 16 scenery items, path network & guest AI
- Heroes guild vs monster invasions (5 classes, XP/gear/bosses)
- Magic spell system (8 spells), research tree, economy/marketing/loans
- Day-night cycle, weather, park rating, awards, 4 scenarios
- Save/load slots + autosave, procedural WebAudio SFX/music
- Isometric canvas renderer, minimap, diagnostics overlay
- Test suites: smoke(13), linkcheck, inputcheck, framecheck, rendercheck, flow
2026-08-23 06:59:21 +00:00

79 lines
2.0 KiB
JavaScript

// ============ save.js — localStorage slots, autosave, export/import ============
import { serialize, deserialize } from '../game/state.js';
import { download } from '../core/util.js';
const KEY = 'arcane_tycoon_save_';
const SLOTS = ['auto', 'slot1', 'slot2', 'slot3'];
export function listSaves() {
const out = [];
for (const s of SLOTS) {
try {
const raw = localStorage.getItem(KEY + s);
if (!raw) { out.push({ slot: s, exists: false }); continue; }
const d = JSON.parse(raw);
out.push({
slot: s, exists: true,
parkName: d.park?.name || 'Park',
scenario: d.scenario,
date: `Y${d.time?.year} M${(d.time?.month ?? 0) + 1}`,
cash: d.cash,
guests: d.guests?.length ?? 0,
savedAt: d.savedAt,
});
} catch {
out.push({ slot: s, exists: false });
}
}
return out;
}
export function saveTo(state, slot) {
const data = serialize(state);
data.savedAt = Date.now();
try {
localStorage.setItem(KEY + slot, JSON.stringify(data));
return true;
} catch (e) {
console.error('save failed', e);
return false;
}
}
export function loadFrom(slot) {
try {
const raw = localStorage.getItem(KEY + slot);
if (!raw) return null;
return deserialize(JSON.parse(raw));
} catch (e) {
console.error('load failed', e);
return null;
}
}
export function hasAutosave() {
try { return !!localStorage.getItem(KEY + 'auto'); } catch { return false; }
}
export function autosave(state) {
return saveTo(state, 'auto');
}
export function exportSave(state) {
const data = serialize(state);
data.savedAt = Date.now();
const name = (state.park.name || 'park').replace(/\W+/g, '_').toLowerCase();
download(`arcane-tycoon-${name}.json`, JSON.stringify(data));
}
export function importSaveText(text) {
try {
const data = JSON.parse(text);
if (!data.version) throw new Error('Not an Arcane Tycoon save');
return deserialize(data);
} catch (e) {
console.error('import failed', e);
return null;
}
}