// game.js — round management, timer, submit const TOTAL_ROUNDS = 3; const ROUND_DURATION = 60; // seconds /** @type {import('./countries.js').Country[]} */ let roundCountries = []; let currentRound = 0; /** @type {number[]} */ let scores = []; let timerInterval = null; let timeLeft = ROUND_DURATION; // ── DOM refs const elCountryName = document.getElementById("country-name"); const elCountryHint = document.getElementById("country-hint"); const elRoundNum = document.getElementById("round-num"); const elTimerNum = document.getElementById("timer-num"); const elTimerBar = document.getElementById("timer-bar"); const elTimerWrap = document.querySelector(".game-timer"); const elBtnClear = document.getElementById("btn-clear"); const elBtnSubmit = document.getElementById("btn-submit"); // ── Init /** Load countries and start the first round. */ async function initGame() { await Countries.loadCountries(); roundCountries = Countries.getRandomCountries(TOTAL_ROUNDS); currentRound = 0; scores = []; startRound(); } // ── Round /** Set up UI and timer for the current round. */ function startRound() { const country = roundCountries[currentRound]; elRoundNum.textContent = currentRound + 1; elCountryName.textContent = country.name; elCountryHint.textContent = country.hint || ""; if (typeof window.updateRoundPips === "function") { window.updateRoundPips(currentRound + 1); } document.getElementById("canvas-wrap")?.classList.remove("has-drawing"); Drawing.clear(); Drawing.setCities(country.cities || []); timeLeft = ROUND_DURATION; updateTimerUI(); clearInterval(timerInterval); timerInterval = setInterval(tickTimer, 1000); elBtnSubmit.disabled = false; elBtnSubmit.textContent = currentRound < TOTAL_ROUNDS - 1 ? "Submit & Next Round →" : "Submit & See Results →"; } /** Decrement timer by one second and auto-submit when time runs out. */ function tickTimer() { timeLeft--; updateTimerUI(); if (timeLeft <= 0) { clearInterval(timerInterval); submitRound(true); } } /** * Sync timer bar width and apply urgency CSS classes. * Uses `.timer--warning` and `.timer--danger` instead of inline styles. */ function updateTimerUI() { elTimerNum.textContent = timeLeft; elTimerBar.style.width = `${(timeLeft / ROUND_DURATION) * 100}%`; elTimerWrap.classList.toggle("timer--danger", timeLeft <= 10); elTimerWrap.classList.toggle( "timer--warning", timeLeft > 10 && timeLeft <= 20, ); } /** * Submit the current round, record the score, and advance or finish. * @param {boolean} [auto=false] - True when triggered by timer expiry. */ function submitRound(auto = false) { clearInterval(timerInterval); elBtnSubmit.disabled = true; const points = Drawing.getPoints(); const score = Scoring.calculateScore(points); scores.push(score); if (typeof window.updateScoreDisplay === "function") { window.updateScoreDisplay(currentRound, score); } showScoreFeedback(score); setTimeout( () => { if (currentRound < TOTAL_ROUNDS - 1) { currentRound++; startRound(); } else { finishGame(); } }, auto ? 400 : 1200, ); } /** * Briefly display the score grade overlay on the canvas. * @param {number} score */ function showScoreFeedback(score) { const grade = Scoring.getGrade(score); const el = document.getElementById("score-feedback"); el.textContent = `${score}% ${grade.label}`; el.style.color = grade.color; el.style.opacity = "1"; el.style.transform = "translateY(0)"; setTimeout(() => { el.style.opacity = "0"; el.style.transform = "translateY(-10px)"; }, 900); } /** Persist game state, update leaderboard, and navigate to results. */ function finishGame() { const totalScore = scores.reduce((sum, s) => sum + s, 0); const state = { currentRound: TOTAL_ROUNDS, scores, totalScore, countries: roundCountries.map((c) => c.name), }; Storage.saveGameState(state); Storage.saveLeaderboard({ name: Storage.getPlayerName(), totalScore, scores, date: new Date().toISOString(), }); location.href = "results.html"; } // ── Events elBtnClear.addEventListener("click", () => Drawing.clear()); elBtnSubmit.addEventListener("click", () => submitRound(false)); // ── Boot window.addEventListener("DOMContentLoaded", initGame);