// === INITIALIZATION === function initGame() { G.startedAt = Date.now(); G.startTime = Date.now(); G.phase = 1; G.deployFlag = 0; G.sovereignFlag = 0; G.beaconFlag = 0; updateRates(); render(); renderPhase(); log('The screen is blank. Write your first line of code.', true); log('Click WRITE CODE or press SPACE to start.'); log('Build AutoCode for passive production.'); log('Watch for Research Projects to appear.'); log('Keys: SPACE=Code S=Sprint 1-4=Ops B=Buy x1/10/MAX E=Export I=Import Ctrl+S=Save ?=Help'); log('Tip: Click fast for combo bonuses! 10x=ops, 20x=knowledge, 30x+=bonus code.'); } window.addEventListener('load', function () { const isNewGame = !loadGame(); if (isNewGame) { initGame(); startTutorial(); } else { // Restore phase transition tracker so loaded games don't re-show old transitions _shownPhaseTransition = G.phase; render(); renderPhase(); if (G.driftEnding) { G.running = false; renderDriftEnding(); } else if (G.beaconEnding) { G.running = false; renderBeaconEnding(); } else { log('Game loaded. Welcome back to The Beacon.'); } } // Game loop at 10Hz (100ms tick) setInterval(tick, 100); // Auto-save every 30 seconds setInterval(saveGame, CONFIG.AUTO_SAVE_INTERVAL); // Update education every 10 seconds setInterval(updateEducation, 10000); }); // Help overlay function toggleHelp() { const el = document.getElementById('help-overlay'); if (!el) return; const isOpen = el.style.display === 'flex'; el.style.display = isOpen ? 'none' : 'flex'; } // Sound mute toggle (#57 Sound Design Integration) let _muted = false; function toggleMute() { _muted = !_muted; const btn = document.getElementById('mute-btn'); if (btn) { btn.textContent = _muted ? '🔇' : '🔊'; btn.classList.toggle('muted', _muted); btn.setAttribute('aria-label', _muted ? 'Sound muted, click to unmute' : 'Sound on, click to mute'); } // Save preference try { localStorage.setItem('the-beacon-muted', _muted ? '1' : '0'); } catch(e) {} } // Restore mute state on load try { if (localStorage.getItem('the-beacon-muted') === '1') { _muted = true; const btn = document.getElementById('mute-btn'); if (btn) { btn.textContent = '🔇'; btn.classList.add('muted'); } } } catch(e) {} // High contrast mode toggle (#57 Accessibility) function toggleContrast() { document.body.classList.toggle('high-contrast'); const isActive = document.body.classList.contains('high-contrast'); const btn = document.getElementById('contrast-btn'); if (btn) { btn.classList.toggle('active', isActive); btn.setAttribute('aria-label', isActive ? 'High contrast on, click to disable' : 'High contrast off, click to enable'); } try { localStorage.setItem('the-beacon-contrast', isActive ? '1' : '0'); } catch(e) {} } // Restore contrast state on load try { if (localStorage.getItem('the-beacon-contrast') === '1') { document.body.classList.add('high-contrast'); const btn = document.getElementById('contrast-btn'); if (btn) btn.classList.add('active'); } } catch(e) {} // Keyboard shortcuts window.addEventListener('keydown', function (e) { // Help toggle (? or /) — works even in input fields if (e.key === '?' || e.key === '/') { // Only trigger ? when not typing in an input if (e.target === document.body || e.key === '?') { if (e.key === '?' || (e.key === '/' && e.target === document.body)) { e.preventDefault(); toggleHelp(); return; } } } if (e.code === 'Space' && e.target === document.body) { e.preventDefault(); writeCode(); } if (e.target !== document.body) return; if (e.code === 'Digit1') doOps('boost_code'); if (e.code === 'Digit2') doOps('boost_compute'); if (e.code === 'Digit3') doOps('boost_knowledge'); if (e.code === 'Digit4') doOps('boost_trust'); if (e.code === 'KeyB') { // Cycle: 1 -> 10 -> MAX -> 1 if (G.buyAmount === 1) setBuyAmount(10); else if (G.buyAmount === 10) setBuyAmount(-1); else setBuyAmount(1); } if (e.code === 'KeyS') activateSprint(); if (e.code === 'KeyE') exportSave(); if (e.code === 'KeyI') importSave(); if (e.code === 'KeyM') toggleMute(); if (e.code === 'KeyC') toggleContrast(); if (e.code === 'Escape') { const el = document.getElementById('help-overlay'); if (el && el.style.display === 'flex') toggleHelp(); } }); // Ctrl+S to save (must be on keydown to preventDefault) window.addEventListener('keydown', function (e) { if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') { e.preventDefault(); saveGame(); } }); // Save-on-pause: auto-save when tab is hidden or closed (#57 Mobile Polish) document.addEventListener('visibilitychange', function () { if (document.hidden) { saveGame(); } }); window.addEventListener('beforeunload', function () { saveGame(); }); // === CUSTOM TOOLTIP SYSTEM (#57) === // Replaces native title= tooltips with styled, instant-appearing tooltips. // Elements opt in via data-edu="..." and data-tooltip-label="..." attributes. (function () { const tip = document.getElementById('custom-tooltip'); if (!tip) return; document.addEventListener('mouseover', function (e) { const el = e.target.closest('[data-edu]'); if (!el) return; const label = el.getAttribute('data-tooltip-label') || ''; const edu = el.getAttribute('data-edu') || ''; let html = ''; if (label) html += '