import { GAME_CONFIG, seedStr, updateTeamSegmented, updateResetCountdown, fetchConfig, fetchGridForSeed, fetchAndApplyScores, updateEconomyDisplay, tickEconScore, loadEconScores, loadVictoryPoints, loadDbInfo, loadElementBonus, tickElementBonus, refreshFromServer, refreshGridDisplay, loadPlayfieldMask, draw, unlockTeamSwitcher, } from "./game.js"; import { tryRestoreSession, showAuthOverlay, hideAuthOverlay, } from "./auth.js"; // ── DOM refs ────────────────────────────────────────────────────────────────── const hint = document.getElementById("hint"); const cooldownEl = document.getElementById("cooldownConfig"); const burgerBtn = document.getElementById("burgerBtn"); const closeMenuBtn = document.getElementById("closeMenuBtn"); const infoColumn = document.getElementById("infoColumn"); const adminPasswordIn = document.getElementById("adminPasswordInput"); const adminUnlockBtn = document.getElementById("adminUnlockBtn"); const adminStatus = document.getElementById("adminStatus"); // ── Polling ─────────────────────────────────────────────────────────────────── let configPollTimer = 0; let scorePollTimer = 0; let resetTimer = null; function scheduleConfigPoll() { clearTimeout(configPollTimer); const ms = Math.max(5_000, GAME_CONFIG.configReloadIntervalSeconds * 1_000); configPollTimer = window.setTimeout(async () => { try { const changed = await fetchConfig(); if (changed) await refreshFromServer(); } catch { /* ignore */ } scheduleConfigPoll(); }, ms); } const ECON_TICK_SECONDS = 5; function scheduleScorePoll() { clearTimeout(scorePollTimer); scorePollTimer = window.setTimeout(async () => { await fetchAndApplyScores(); tickEconScore(ECON_TICK_SECONDS); tickElementBonus(); scheduleScorePoll(); }, ECON_TICK_SECONDS * 1_000); } // ── Burger / mobile menu ────────────────────────────────────────────────────── function openMenu() { infoColumn.classList.add("infoColumn--open"); } function closeMenu() { infoColumn.classList.remove("infoColumn--open"); } burgerBtn.addEventListener("click", openMenu); closeMenuBtn.addEventListener("click", closeMenu); // Close when clicking outside the panel (on the galaxy overlay) document.addEventListener("click", (ev) => { if ( infoColumn.classList.contains("infoColumn--open") && !infoColumn.contains(ev.target) && ev.target !== burgerBtn ) { closeMenu(); } }); // ── Admin password unlock ───────────────────────────────────────────────────── function showAdminStatus(message, isOk) { adminStatus.textContent = message; adminStatus.className = "adminStatus " + (isOk ? "adminStatus--ok" : "adminStatus--err"); } adminUnlockBtn.addEventListener("click", async () => { const password = adminPasswordIn.value.trim(); if (!password) { showAdminStatus("Enter a password.", false); return; } try { const res = await fetch("/api/admin/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password }), }); const data = await res.json(); if (res.ok && data.ok) { showAdminStatus("Admin unlocked — team switcher enabled.", true); unlockTeamSwitcher(); adminPasswordIn.value = ""; } else { showAdminStatus("Invalid password.", false); } } catch { showAdminStatus("Could not verify — server unreachable.", false); } }); // Allow Enter key in password field adminPasswordIn.addEventListener("keydown", (ev) => { if (ev.key === "Enter") adminUnlockBtn.click(); }); // ── Boot ────────────────────────────────────────────────────────────────────── async function boot() { updateTeamSegmented(); // Load the SVG playfield mask before any drawing or data fetch await loadPlayfieldMask(); const restored = await tryRestoreSession(); if (!restored) { showAuthOverlay(); } else { hideAuthOverlay(); } try { await fetchConfig(); await fetchGridForSeed(seedStr); await fetchAndApplyScores(); await loadEconScores(); await loadVictoryPoints(); await loadDbInfo(); await loadElementBonus(); updateEconomyDisplay(); } catch { hint.textContent = "API unavailable — start the Node server (docker-compose up --build)."; cooldownEl.textContent = "?"; } draw(); if (resetTimer) clearInterval(resetTimer); resetTimer = setInterval(updateResetCountdown, 1_000); updateResetCountdown(); scheduleConfigPoll(); scheduleScorePoll(); // Refresh VP every 30 s so new awards are reflected promptly setInterval(loadVictoryPoints, 30_000); // Refresh grid every second so all clients see new tiles promptly setInterval(refreshGridDisplay, 1_000); } // ── Start ───────────────────────────────────────────────────────────────────── boot();