import type { State } from './engine.ts'; import { UPGRADES } from './upgrades.ts'; import { VERSION, CHANGELOG } from './data/version.ts'; export function initUI(app: HTMLElement, onUpgrade: (id: string) => void, onRestart: () => void): void { app.innerHTML = `
DJUPET — Hjälp & Ändringslogg
Hur man spelar
  • Andakt ackumuleras passivt. Använd den för att köpa åtgärder i vänsterpanelen.
  • Dread ökar när ritualen fördjupas. Hög dread låser upp nya platser och händelser.
  • Hemlighet sjunker när ni blir för uppenbara. Når den noll är det slut.
  • Kaos byggs upp naturligt och utlöser kaotiska händelser när det når 100.
  • Ritual låses upp via Skriften. Nå 100 för att fullborda ceremonin.
  • Klicka på platser på kartan för att besöka dem. Varje besök lämnar spår.
  • Händelser inträffar automatiskt. De flesta är dåliga. En del är värre.
Om spelet

Djupet är ett idle-spel om en kult i Malmö, Sverige.
Inspirerat av Anders Fager, Kult och Call of Cthulhu.
Den Sovande är inte vaken ännu. Jobba på det.

Ändringslogg
${CHANGELOG.map(e => `
v${e.version} ${e.date}
    ${e.changes.map(c => `
  • ${c}
  • `).join('')}
`).join('')}
Resurser
Troende
1
Andakt
0
Dread
0
Hemlighet
100
Kaos
0
Åtgärder
Aktiva områden
○ Rosengård
○ Värnhemstorget
○ Turning Torso
Utredarna
Inaktiva
`; document.getElementById('overlay-btn')!.addEventListener('click', onRestart); const helpModal = document.getElementById('help-modal')!; const helpInner = document.getElementById('help-modal-inner')!; document.getElementById('help-btn')!.addEventListener('click', () => helpModal.classList.toggle('visible')); document.getElementById('help-close')!.addEventListener('click', () => helpModal.classList.remove('visible')); helpModal.addEventListener('click', (e) => { if (!helpInner.contains(e.target as Node)) helpModal.classList.remove('visible'); }); document.addEventListener('keydown', (e) => { if (e.key === '?' || (e.key === 'Escape' && helpModal.classList.contains('visible'))) helpModal.classList.toggle('visible'); }); // 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;height:100%;'; const mapEl = document.getElementById('map')!; mapEl.style.cssText = 'flex:0 0 45%;min-height:0;max-height:45%;'; const logEl = document.getElementById('log')!; 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 => { const btn = (e.target as Element).closest('[data-upgrade]') as HTMLElement | null; if (btn?.dataset['upgrade']) onUpgrade(btn.dataset['upgrade']); }); } export function renderState(state: State): void { setText('r-troende', String(state.troende)); setText('r-andakt', fmt(state.andakt)); setText('r-andakt-rate', `+${(state.andaktRate * state.troende).toFixed(1)}/s`); setText('r-dread', fmt(state.dread)); setText('r-dread-rate', state.dreadRate > 0 ? `+${state.dreadRate.toFixed(1)}/s` : ''); setText('r-hemlighet', Math.floor(state.hemlighet).toString()); setText('r-kaos', Math.floor(state.kaos).toString()); setBar('bar-hemlighet', state.hemlighet); setBar('bar-kaos', state.kaos); const ritualSection = document.getElementById('ritual-section')!; if (state.ritualUnlocked) { ritualSection.style.display = ''; setText('r-ritual', Math.floor(state.ritual).toString()); setBar('bar-ritual', state.ritual); } // Phase tag const phaseEl = document.getElementById('phase-tag')!; if (state.ritual >= 50) { phaseEl.textContent = 'Sent skede'; phaseEl.className = 'phase-tag late'; } else if (state.dread >= 30) { phaseEl.textContent = 'Mitt skede'; phaseEl.className = 'phase-tag mid'; } else { phaseEl.textContent = 'Tidigt skede'; phaseEl.className = 'phase-tag early'; } // Areas setArea('area-rosengard', state.areas.rosengard, 'Rosengård'); setArea('area-varnhem', state.areas.varnhem, 'Värnhemstorget'); setArea('area-torso', state.areas.torso, 'Turning Torso'); // Investigator status const inv = document.getElementById('investigator-status')!; if (state.hemlighet < 20) { inv.textContent = 'Nära — väldigt nära'; inv.style.color = 'var(--danger)'; } else if (state.hemlighet < 50) { inv.textContent = 'Missstänksamma'; inv.style.color = 'var(--kaos)'; } else { inv.textContent = 'Inaktiva'; inv.style.color = 'var(--safe)'; } // Upgrades renderUpgrades(state); // Log renderLog(state); // Overlay 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 = '
Inga tillgängliga åtgärder.
'; return; } container.innerHTML = available.map(u => { const canAfford = (!u.cost.andakt || state.andakt >= u.cost.andakt) && (!u.cost.dread || state.dread >= u.cost.dread); const costs = [ u.cost.andakt ? `${u.cost.andakt} andakt` : '', u.cost.dread ? `${u.cost.dread} dread` : '', ].filter(Boolean).join(', '); return ``; }).join(''); } let lastLogLength = 0; function renderLog(state: State): void { const log = document.getElementById('log')!; if (state.log.length === lastLogLength) return; lastLogLength = state.log.length; // state.log is newest-first (unshift); render as-is, scroll to top log.innerHTML = state.log.map(e => `
${e.text}
` ).join(''); log.scrollTop = 0; } function showOverlay(state: State): void { const ov = document.getElementById('overlay')!; ov.classList.add('visible'); const title = document.getElementById('overlay-title')!; const body = document.getElementById('overlay-body')!; if (state.phase === 'won') { title.textContent = 'FULLBORDAT'; title.className = 'overlay-title won'; body.textContent = 'Det är gjort. Turning Torso pekar rakt ned. Havet är stilla — alltför stilla. Malmö fortsätter som vanligt. Det är det värsta.'; } else { title.textContent = 'AVSLÖJAD'; title.className = 'overlay-title lost'; body.textContent = state.lostReason ?? 'Utredarna kom.'; } } function setText(id: string, val: string): void { const el = document.getElementById(id); if (el) el.textContent = val; } function setBar(id: string, pct: number): void { const el = document.getElementById(id) as HTMLElement | null; if (el) el.style.width = `${Math.min(100, Math.max(0, pct))}%`; } function setArea(id: string, active: boolean, label: string): void { const el = document.getElementById(id); if (el) { el.textContent = `${active ? '●' : '○'} ${label}`; el.style.color = active ? 'var(--dread)' : 'var(--dim)'; } } function fmt(n: number): string { return Math.floor(n).toLocaleString('sv-SE'); }