Add lore depth, fix recruit click bug, add localStorage save

- 20+ new events drawing from Anders Fager, Kult, and Call of Cthulhu:
  ICA cashiers who never blink, SJ trains stopping in non-existent tunnels,
  Kult lictors as city officials, death servants finishing lectures, Deep Ones,
  investigators dreaming about us, stars being right over Lund Observatory
- Rewrite upgrade descriptions to match the register: mundane detail,
  matter-of-fact horror, no melodrama
- Add a new mid-game upgrade (Paktens Förnyelse) to smooth the progression
- Fix recruit button: was destroying DOM every 30fps tick, killing mousedown→click
  events; now only rebuilds when availability/affordability actually changes
- Fix double andakt deduction in recruit effect
- Fix map growing over log (proper flex constraints)
- Add localStorage save/load with versioned key (SAVE_VERSION bump invalidates
  old saves); autosaves every 10s and on upgrade purchase, clears on restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Fredrik Johansson
2026-07-07 23:25:44 +02:00
parent d091e7382a
commit f5776f269b
5 changed files with 245 additions and 13 deletions

View File

@@ -30,6 +30,10 @@ export interface LogEntry {
kind: 'event' | 'kaos' | 'system' | 'upgrade';
}
// Bump this when State shape changes to invalidate old saves
const SAVE_VERSION = '001';
const SAVE_KEY = `djupet_v${SAVE_VERSION}`;
const EVENT_INTERVAL_TICKS = 180; // ~6s at 30fps
const KAOS_THRESHOLD = 100;
const HEMLIGHET_DRAIN_PER_TROENDE = 0.002; // per second, per troende above 4
@@ -148,6 +152,37 @@ export function tick(state: State): State {
return s;
}
export function saveState(state: State): void {
try {
const serial = {
...state,
upgrades: [...state.upgrades],
usedEventIds: [...state.usedEventIds],
log: state.log.slice(0, 20),
};
localStorage.setItem(SAVE_KEY, JSON.stringify(serial));
} catch (_) { /* storage full or unavailable */ }
}
export function loadState(): State | null {
try {
const raw = localStorage.getItem(SAVE_KEY);
if (!raw) return null;
const d = JSON.parse(raw);
return {
...d,
upgrades: new Set<string>(d.upgrades ?? []),
usedEventIds: new Set<string>(d.usedEventIds ?? []),
} as State;
} catch (_) {
return null;
}
}
export function clearSave(): void {
localStorage.removeItem(SAVE_KEY);
}
export function buyUpgrade(state: State, id: string): State {
const upgrade = UPGRADES.find(u => u.id === id);
if (!upgrade) return state;