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
co-authored by Claude Sonnet 4.6
parent d091e7382a
commit f5776f269b
5 changed files with 245 additions and 13 deletions
+14 -3
View File
@@ -79,11 +79,11 @@ export function initUI(app: HTMLElement, onUpgrade: (id: string) => void, onRest
// Split the map container: map on top, log below
const mapContainer = document.getElementById('map-container')!;
mapContainer.style.cssText = 'display:flex;flex-direction:column;overflow:hidden;';
mapContainer.style.cssText = 'display:flex;flex-direction:column;overflow:hidden;height:100%;';
const mapEl = document.getElementById('map')!;
mapEl.style.cssText = 'flex:0 0 45%;min-height:0;';
mapEl.style.cssText = 'flex:0 0 45%;min-height:0;max-height:45%;';
const logEl = document.getElementById('log')!;
logEl.style.cssText = 'flex:1;overflow-y:auto;border-top:1px solid var(--border);';
logEl.style.cssText = 'flex:1 1 0;overflow-y:auto;border-top:1px solid var(--border);min-height:0;';
// Upgrade click delegation
document.getElementById('upgrades')!.addEventListener('click', e => {
@@ -145,10 +145,21 @@ export function renderState(state: State): void {
if (state.phase !== 'playing') showOverlay(state);
}
let lastUpgradeKey = '';
function renderUpgrades(state: State): void {
const container = document.getElementById('upgrades')!;
const available = UPGRADES.filter(u => u.available(state) && (u.once ? !state.upgrades.has(u.id) : true));
// Only rebuild DOM when availability or affordability changes
const key = available.map(u => {
const canAfford = (!u.cost.andakt || state.andakt >= u.cost.andakt) && (!u.cost.dread || state.dread >= u.cost.dread);
return `${u.id}:${canAfford}`;
}).join('|');
if (key === lastUpgradeKey) return;
lastUpgradeKey = key;
if (!available.length) {
container.innerHTML = '<div style="font-size:11px;color:var(--dim);">Inga tillgängliga åtgärder.</div>';
return;