// =============================== // BODMAS MAIN JS (FULL UPDATED - FIXED) // =============================== // --- MESSAGES --- const successMessages = [ "Absolute Legend!", "Human Calculator Verified!", "Pure Genius Status!", ]; const failMessages = [ "Oof! That was a tough one.", "So close, yet so far!", "Math is hard, isn't it?", ]; // ------------------------------- // ✅ GLOBAL AUDIO (ONLY ONCE) // ------------------------------- const audio = document.getElementById("backgroundMusic"); let musicStarted = false; // ✅ Run game + try music after HTML loads document.addEventListener("DOMContentLoaded", () => { initMathGame(); startMusic(); // try autoplay updateMusicIcon(); }); // ✅ If browser blocks autoplay, start music on first click anywhere document.addEventListener( "click", () => { if (!musicStarted) startMusic(); }, { once: true } ); // ------------------------------- // 1) Math Game // ------------------------------- function initMathGame() { const questionEl = document.getElementById("math-question"); const optionsEl = document.getElementById("math-options"); // ✅ Safety check (prevents errors) if (!questionEl || !optionsEl) return; const n1 = Math.floor(Math.random() * 8) + 12; // 12-19 const n2 = Math.floor(Math.random() * 6) + 4; // 4-9 const n3 = Math.floor(Math.random() * 35) + 15; // 15-50 const expression = `(${n1} × ${n2}) - ${n3}`; const answer = n1 * n2 - n3; let opts = new Set([answer]); opts.add(answer + 10); opts.add(answer - (Math.random() > 0.5 ? 1 : 2)); opts.add(n1 * n2 + n3); while (opts.size < 4) { opts.add(answer + Math.floor(Math.random() * 20) - 10); } let finalOptions = Array.from(opts).sort(() => Math.random() - 0.5); questionEl.innerText = expression; optionsEl.innerHTML = ""; finalOptions.forEach((opt, i) => { let btn = document.createElement("div"); btn.className = "bg-white text-indigo-700 p-5 rounded-2xl candy-btn border-b-4 border-indigo-200 text-center text-xl font-bold cursor-pointer hover:bg-indigo-50 select-none math-pop-anim opacity-0"; btn.style.animationDelay = `${i * 0.1}s`; btn.innerText = opt; btn.onclick = function () { const allBtns = optionsEl.children; for (let b of allBtns) b.style.pointerEvents = "none"; if (opt === answer) { btn.className = "bg-green-500 text-white p-5 rounded-2xl border-b-4 border-green-700 text-center text-xl font-bold shadow-xl transform scale-105"; triggerConfetti(); setTimeout(() => showMathResult(true, answer), 1000); } else { btn.className = "bg-red-500 text-white p-5 rounded-2xl border-b-4 border-red-700 text-center text-xl font-bold math-shake-anim"; setTimeout(() => showMathResult(false, answer), 1000); } }; optionsEl.appendChild(btn); }); } function showMathResult(isWin, realAnswer) { const overlay = document.getElementById("math-result-overlay"); const title = document.getElementById("math-title"); const msg = document.getElementById("math-msg"); const resultImg = document.getElementById("math-result-img"); if (!overlay || !title || !msg) return; overlay.classList.remove("opacity-0", "pointer-events-none"); overlay.classList.add("opacity-100", "pointer-events-auto"); // ✅ show result image + pop effect if (resultImg) { resultImg.classList.remove("hidden"); // ✅ change image resultImg.src = isWin ? "images/right.jpg" : "images/wrong.jpg"; // ✅ restart animation every time resultImg.classList.remove("pop-zoom"); void resultImg.offsetWidth; // force reflow (animation restart) resultImg.classList.add("pop-zoom"); } if (isWin) { const randomMsg = successMessages[Math.floor(Math.random() * successMessages.length)]; title.innerHTML = `Victory!
✨ Math Master Moment! ✨`; title.className = "text-5xl font-black mb-2 text-yellow-400 drop-shadow-lg"; msg.innerHTML = `${randomMsg}`; } else { const randomMsg = failMessages[Math.floor(Math.random() * failMessages.length)]; title.innerText = "Knocked Out!"; title.className = "text-5xl font-black mb-2 text-red-400 drop-shadow-lg"; msg.innerHTML = `Correct answer: ${realAnswer}
${randomMsg}`; } } function triggerConfetti() { const box = document.getElementById("math-confetti"); if (!box) return; box.innerHTML = ""; const colors = ["#FCD34D", "#F87171", "#60A5FA", "#34D399"]; for (let i = 0; i < 60; i++) { let c = document.createElement("div"); c.style.cssText = `position:absolute; width:12px; height:12px; border-radius:50%; top:-10px; left:${ Math.random() * 100 }%; background:${colors[Math.floor(Math.random() * 4)]}; transition: top 3s ease-in, transform 3s linear; opacity: 0.8;`; box.appendChild(c); setTimeout(() => { c.style.top = "120%"; c.style.transform = `rotate(${Math.random() * 720}deg) translateX(${ Math.random() * 50 - 25 }px)`; }, 50); } } // ------------------------------- // 2) Modals (Info / Rules / Leaderboard) // ------------------------------- function openModal(modalId) { document.getElementById(modalId)?.classList.add("active"); } function closeAnyModal(modalId) { document.getElementById(modalId)?.classList.remove("active"); } // ------------------------------- // 3) Info Tabs // ------------------------------- function openInfoModal(tabName) { openModal("infoModal"); switchInfoTab(tabName); } function switchInfoTab(tabName) { document .querySelectorAll(".info-content") .forEach((el) => el.classList.add("hidden")); document .querySelectorAll(".tab-btn") .forEach((el) => el.classList.remove("active")); document.getElementById("content-" + tabName)?.classList.remove("hidden"); document.getElementById("tab-" + tabName)?.classList.add("active"); } // ------------------------------- // 5) AUTO MUSIC + ONE BUTTON (Mute/Unmute) // ------------------------------- function startMusic() { if (!audio) return; audio.muted = false; audio.volume = 0.7; audio.loop = true; const tryPlay = () => { audio .play() .then(() => { musicStarted = true; updateMusicIcon(); }) .catch(() => { updateMusicIcon(); console.log("Autoplay blocked. Music will start on first click."); }); }; // ✅ Wait for audio to be ready (reduces buffering) if (audio.readyState >= 2) { tryPlay(); } else { audio.addEventListener("canplaythrough", tryPlay, { once: true }); audio.load(); } } // ✅ One button = Mute/Unmute function toggleMusic() { if (!audio) return; audio.muted = !audio.muted; updateMusicIcon(); } function updateMusicIcon() { const icon1 = document.getElementById("musicIcon"); const icon2 = document.getElementById("musicIconFooter"); const musicBtn = document.getElementById("musicBtn"); if (!audio) return; const newIcon = audio.muted ? "music_off" : "music_note"; if (icon1) icon1.textContent = newIcon; if (icon2) icon2.textContent = newIcon; if (musicBtn) musicBtn.classList.toggle("opacity-60", audio.muted); } // ✅ error handler if (audio) { audio.addEventListener("error", () => { console.log("Audio failed to load. Check bg.mp3 path."); }); } // ------------------------------- // 6) Page Switching (Home <-> Start) // ------------------------------- function showStartPage() { document.getElementById("page-home")?.classList.remove("active"); document.getElementById("page-start")?.classList.add("active"); // ✅ Music continues naturally if (!musicStarted) startMusic(); } function showHomePage() { document.getElementById("page-start")?.classList.remove("active"); document.getElementById("page-home")?.classList.add("active"); } function goHome() { showHomePage(); } // ------------------------------- // 7) QUESTIONS BANK + GAME LOGIC // ------------------------------- const questionsByLevel = { 1: [ { question: "21 - 12 / 3 * 2", choiceA: "15", choiceB: "13", choiceC: "16", choiceD: "12", correct: "B" }, { question: "16 + 8 / 4 - 2 * 3", choiceA: "12", choiceB: "11", choiceC: "16", choiceD: "15", correct: "A" }, { question: "30 * 2 / 3 - 10", choiceA: "15", choiceB: "20", choiceC: "10", choiceD: "23", correct: "C" }, { question: "15 / 3 * 2 - 10", choiceA: "8", choiceB: "5", choiceC: "1", choiceD: "0", correct: "D" }, { question: "30 - 10 * 8 + 60", choiceA: "10", choiceB: "5", choiceC: "10", choiceD: "11", correct: "C" }, { question: "18 + 12 / 3 * 2", choiceA: "26", choiceB: "22", choiceC: "18", choiceD: "20", correct: "A" }, { question: "40 - 6 * 5 + 10", choiceA: "20", choiceB: "10", choiceC: "30", choiceD: "40", correct: "A" }, { question: "9 + 6 / 2 * 3", choiceA: "18", choiceB: "12", choiceC: "15", choiceD: "16", correct: "C" }, { question: "50 / 5 + 6 * 2", choiceA: "22", choiceB: "20", choiceC: "18", choiceD: "24", correct: "A" }, { question: "24 - 8 / 4 + 6", choiceA: "28", choiceB: "22", choiceC: "24", choiceD: "26", correct: "B" }, ], 2: [ { question: "25 + (2 * 3) - 5", choiceA: "10", choiceB: "26", choiceC: "15", choiceD: "20", correct: "B" }, { question: "13 - (12 - 6 / 3)", choiceA: "3", choiceB: "1", choiceC: "6", choiceD: "5", correct: "A" }, { question: "3 * 5 + (10 / 5 - 2)", choiceA: "11", choiceB: "20", choiceC: "15", choiceD: "23", correct: "C" }, { question: "(10 - 8) * 2 + 10", choiceA: "18", choiceB: "15", choiceC: "16", choiceD: "14", correct: "D" }, { question: "10 + (2 * 5) - 10 / 2", choiceA: "28", choiceB: "19", choiceC: "15", choiceD: "11", correct: "C" }, { question: "18 + (12 / 3) * 2", choiceA: "26", choiceB: "24", choiceC: "22", choiceD: "20", correct: "A" }, { question: "30 - (10 + 5) * 2", choiceA: "10", choiceB: "0", choiceC: "5", choiceD: "15", correct: "B" }, { question: "(20 - 4) / 2 + 6", choiceA: "14", choiceB: "10", choiceC: "16", choiceD: "12", correct: "B" }, { question: "12 + (18 / 6) * 5", choiceA: "27", choiceB: "20", choiceC: "22", choiceD: "25", correct: "A" }, { question: "40 - (12 / 3 + 2) * 4", choiceA: "20", choiceB: "12", choiceC: "24", choiceD: "18", correct: "A" }, ], 3: [ { question: "19 - [4 + {16 - (12 - 2)}]", choiceA: "15", choiceB: "9", choiceC: "12", choiceD: "11", correct: "B" }, { question: "36 - [18 - {14 - (15 - 4 / 2 * 2)}]", choiceA: "21", choiceB: "18", choiceC: "12", choiceD: "11", correct: "A" }, { question: "15 + 10 - [{10 - 1} / (2 + 1)]", choiceA: "10", choiceB: "20", choiceC: "22", choiceD: "23", correct: "C" }, { question: "30 + [{(20 + 1) * 5 - (2 + 1) / 3}]", choiceA: "80", choiceB: "115", choiceC: "120", choiceD: "134", correct: "D" }, { question: "19 - [4 + {16 - (12 - 8)}]", choiceA: "8", choiceB: "9", choiceC: "3", choiceD: "11", correct: "C" }, { question: "50 - [10 + {20 - (6 * 2)}]", choiceA: "32", choiceB: "28", choiceC: "26", choiceD: "30", correct: "A" }, { question: "10 + [8 * {6 - (4 / 2)}]", choiceA: "42", choiceB: "50", choiceC: "34", choiceD: "26", correct: "A" }, { question: "60 / [3 * (4 + 1)]", choiceA: "5", choiceB: "4", choiceC: "3", choiceD: "6", correct: "A" }, { question: "100 - [{(30 - 10) * 2} + 20]", choiceA: "40", choiceB: "20", choiceC: "60", choiceD: "50", correct: "A" }, { question: "[18 - (6 + 3)] * 2", choiceA: "12", choiceB: "18", choiceC: "9", choiceD: "15", correct: "A" }, ], 4: [ { question: "{40 - [12 + (6 * 2)]} / 2", choiceA: "8", choiceB: "10", choiceC: "12", choiceD: "14", correct: "A" }, { question: "80 - {20 + [10 * (3 + 2)]}", choiceA: "10", choiceB: "20", choiceC: "30", choiceD: "40", correct: "B" }, { question: "50 + (20 / 5) * {6 - 2}", choiceA: "62", choiceB: "66", choiceC: "70", choiceD: "74", correct: "B" }, { question: "[100 / (4 * 5)] + {18 - 6}", choiceA: "10", choiceB: "16", choiceC: "17", choiceD: "20", correct: "B" }, { question: "90 - [ {30 - 10} * (2 + 1) ]", choiceA: "30", choiceB: "40", choiceC: "50", choiceD: "60", correct: "B" }, { question: "{72 / [3 * (4 + 2)]} + 5", choiceA: "9", choiceB: "7", choiceC: "11", choiceD: "13", correct: "A" }, { question: "120 - [{(15 * 4) + 20} * 1]", choiceA: "40", choiceB: "20", choiceC: "60", choiceD: "30", correct: "A" }, { question: "[60 - (12 * 3)] + (18 / 6)", choiceA: "27", choiceB: "25", choiceC: "24", choiceD: "22", correct: "A" }, { question: "{(9 + 6) * 2} - [8 / 4]", choiceA: "28", choiceB: "30", choiceC: "32", choiceD: "26", correct: "B" }, { question: "100 - [{(20 + 10) * 2} / 5]", choiceA: "88", choiceB: "80", choiceC: "90", choiceD: "84", correct: "D" }, ], }; const bossTemplates = [ { expr: "({a} + {b}) * {c} - {d}", type: "mix" }, { expr: "{a} + ({b} * {c}) - {d} / {e}", type: "mix2" }, { expr: "{a} - [{b} + ({c} - {d})] * {e}", type: "hard" }, { expr: "{a} + [{b} * ({c} + {d})] / {e}", type: "hard2" }, ]; const POINTS_PER_QUESTION = 500; const unlockRules = { 1: { needCorrect: 4, total: 5, unlockNext: 2 }, 2: { needCorrect: 3, total: 5, unlockNext: 3 }, 3: { needCorrect: 3, total: 5, unlockNext: 4 }, 4: { needCorrect: 3, total: 5, unlockNext: 5 }, 5: { needCorrect: null, total: null, unlockNext: null }, }; let player = { points: 0, unlockedLevel: 1, stars: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }, // ✅ NEW: replay tracking playedLevels: { 1: false, 2: false, 3: false, 4: false, 5: false }, levelScore: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }, }; const STORAGE_KEY = "bodmas_island_progress_v1"; function saveProgress() { localStorage.setItem(STORAGE_KEY, JSON.stringify(player)); } function loadProgress() { const data = localStorage.getItem(STORAGE_KEY); if (data) { try { player = JSON.parse(data); // ✅ safety if old data exists if (!player.playedLevels) { player.playedLevels = { 1: false, 2: false, 3: false, 4: false, 5: false }; } if (!player.levelScore) { player.levelScore = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }; } } catch (e) {} } } function updateLevelButtonText() { for (let lvl = 1; lvl <= 5; lvl++) { const card = document.getElementById(`card-level-${lvl}`); if (!card) continue; const btn = card.querySelector("button"); if (!btn) continue; if (player.playedLevels?.[lvl]) { btn.textContent = lvl === 5 ? "REPLAY BOSS" : "REPLAY"; } } } // Game modal elements const gameModal = document.getElementById("gameModal"); const modalTitle = document.getElementById("modalTitle"); const questionText = document.getElementById("questionText"); const progressBadge = document.getElementById("progressBadge"); const scoreBadge = document.getElementById("scoreBadge"); const nextBtn = document.getElementById("nextBtn"); const timerText = document.getElementById("timerText"); const optA = document.getElementById("optA"); const optB = document.getElementById("optB"); const optC = document.getElementById("optC"); const optD = document.getElementById("optD"); let currentLevel = 1; let currentSet = []; let qIndex = 0; let correctCount = 0; let answered = false; // ✅ TIMER VARIABLES const QUESTION_TIME = 15; let timeLeft = QUESTION_TIME; let timerInterval = null; function shuffleArray(arr) { return arr.sort(() => Math.random() - 0.5); } function pick5RandomQuestions(level) { const pool = [...(questionsByLevel[level] || [])]; shuffleArray(pool); return pool.slice(0, 5); } function rand(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function generateBossQuestion() { const t = bossTemplates[Math.floor(Math.random() * bossTemplates.length)]; const a = rand(10, 60), b = rand(3, 25), c = rand(2, 10), d = rand(5, 30), e = rand(2, 6); const expr = t.expr .replace("{a}", a) .replace("{b}", b) .replace("{c}", c) .replace("{d}", d) .replace("{e}", e); let answerValue = 0; try { answerValue = Math.round( Function("return " + expr.replace(/\[/g, "(").replace(/\]/g, ")"))() ); } catch (err) { answerValue = a; } const correctAns = answerValue; const options = new Set([correctAns]); while (options.size < 4) { const offset = rand(-12, 12); options.add(correctAns + offset); } const opts = Array.from(options); shuffleArray(opts); const map = { A: opts[0], B: opts[1], C: opts[2], D: opts[3] }; const correctKey = Object.keys(map).find((k) => map[k] === correctAns); return { question: expr, choiceA: String(map.A), choiceB: String(map.B), choiceC: String(map.C), choiceD: String(map.D), correct: correctKey, }; } function openGameModal() { gameModal?.classList.add("active"); } function closeGameModal() { stopTimer(); gameModal?.classList.remove("active"); } function resetOptions() { [optA, optB, optC, optD].forEach((btn) => { btn?.classList.remove("correct", "wrong"); if (btn) btn.disabled = false; }); if (nextBtn) nextBtn.disabled = true; answered = false; } // ✅ TIMER FUNCTIONS function stopTimer() { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } } function startTimer() { stopTimer(); timeLeft = QUESTION_TIME; if (timerText) timerText.textContent = timeLeft; timerInterval = setInterval(() => { timeLeft--; if (timerText) timerText.textContent = timeLeft; if (timeLeft <= 0) { stopTimer(); autoWrongTimeout(); } }, 1000); } function autoWrongTimeout() { if (answered) return; answered = true; const q = currentSet[qIndex]; const correct = q.correct; const btnMap = { A: optA, B: optB, C: optC, D: optD }; // highlight correct answer btnMap[correct]?.classList.add("correct"); disableOptions(); if (nextBtn) nextBtn.disabled = true; // Boss mode ends on timeout if (currentLevel === 5) { setTimeout(() => { alert(`⏳ Time Up! Boss Mode ended!\n🔥 Total Points: ${player.points}`); closeGameModal(); saveProgress(); updateLocksUI(); }, 900); return; } setTimeout(() => { if (nextBtn) nextBtn.disabled = false; }, 600); } function renderQuestion() { const q = currentSet[qIndex]; if (modalTitle) modalTitle.textContent = `Level ${currentLevel}`; if (scoreBadge) scoreBadge.textContent = `Score: ${player.points}`; if (progressBadge) { if (currentLevel === 5) { progressBadge.textContent = `Boss Mode 🔥 (Keep Going!) | Correct: ${correctCount}`; } else { progressBadge.textContent = `Q ${qIndex + 1}/5 | Correct: ${correctCount}`; } } if (questionText) questionText.textContent = q.question; if (optA) optA.textContent = "A) " + q.choiceA; if (optB) optB.textContent = "B) " + q.choiceB; if (optC) optC.textContent = "C) " + q.choiceC; if (optD) optD.textContent = "D) " + q.choiceD; resetOptions(); startTimer(); } function startLevel(level) { if (level > player.unlockedLevel) { alert("🔒 This level is locked! Clear previous level first."); return; } currentLevel = level; qIndex = 0; correctCount = 0; // ✅ replay = remove old score first if (player.playedLevels?.[level]) { player.points -= (player.levelScore?.[level] || 0); if (player.points < 0) player.points = 0; // reset stored score for this run player.levelScore[level] = 0; updatePointsUI(); if (scoreBadge) scoreBadge.textContent = `Score: ${player.points}`; } // reset stars for new attempt player.stars[level] = 0; renderStars(level); saveProgress(); if (level === 5) { currentSet = [generateBossQuestion()]; } else { currentSet = pick5RandomQuestions(level); } openGameModal(); renderQuestion(); // ✅ keep music playing if (!musicStarted) startMusic(); } function disableOptions() { [optA, optB, optC, optD].forEach((btn) => { if (btn) btn.disabled = true; }); } function checkAnswer(selected) { if (answered) return; answered = true; stopTimer(); const q = currentSet[qIndex]; const correct = q.correct; const btnMap = { A: optA, B: optB, C: optC, D: optD }; if (selected === correct) { btnMap[selected]?.classList.add("correct"); correctCount++; player.points += POINTS_PER_QUESTION; // ✅ store points earned inside this level player.levelScore[currentLevel] = (player.levelScore[currentLevel] || 0) + POINTS_PER_QUESTION; updatePointsUI(); // ✅ Stars glow = correct answers player.stars[currentLevel] = Math.min(correctCount, 5); renderStars(currentLevel); saveProgress(); } else { btnMap[selected]?.classList.add("wrong"); btnMap[correct]?.classList.add("correct"); // Boss mode ends immediately on wrong if (currentLevel === 5) { disableOptions(); if (nextBtn) nextBtn.disabled = true; setTimeout(() => { alert( `❌ Wrong Answer! Boss Mode ended!\n🔥 You scored: ${player.points} points` ); closeGameModal(); saveProgress(); updateLocksUI(); }, 900); return; } } disableOptions(); if (nextBtn) nextBtn.disabled = false; if (scoreBadge) scoreBadge.textContent = `Score: ${player.points}`; saveProgress(); } function nextQuestion() { stopTimer(); if (currentLevel === 5) { currentSet.push(generateBossQuestion()); qIndex++; renderQuestion(); return; } qIndex++; if (qIndex >= 5) { finishLevel(); return; } renderQuestion(); } function finishLevel() { stopTimer(); const rule = unlockRules[currentLevel]; const passed = correctCount >= rule.needCorrect; if (passed) { alert( `✅ Level ${currentLevel} Cleared!\n⭐ Correct: ${correctCount}/5\n🎉 Next level unlocked!` ); if (rule.unlockNext) { player.unlockedLevel = Math.max(player.unlockedLevel, rule.unlockNext); } if (currentLevel === 1) { const badge = document.getElementById("lvl1-badge"); if (badge) badge.textContent = "COMPLETED"; } } else { alert( `❌ Level ${currentLevel} Failed!\n⭐ Correct: ${correctCount}/5\nTry Again!` ); } // ✅ mark played and update card text player.playedLevels[currentLevel] = true; updateLevelButtonText(); saveProgress(); updateLocksUI(); closeGameModal(); } // ✅ Stars system function renderStars(level) { const container = document.getElementById(`stars-${level}`); if (!container) return; container.innerHTML = ""; const filled = Math.min(player.stars[level] || 0, 5); for (let i = 1; i <= 5; i++) { const s = document.createElement("span"); s.className = "star " + (i <= filled ? "filled" : ""); s.textContent = "⭐"; container.appendChild(s); } } function updateLocksUI() { for (let lvl = 1; lvl <= 5; lvl++) { const card = document.getElementById(`card-level-${lvl}`); if (!card) continue; if (lvl > player.unlockedLevel) { card.classList.add("locked-state"); } else { card.classList.remove("locked-state"); } } } function updatePointsUI() { const counter = document.getElementById("points-counter"); if (counter) counter.textContent = player.points; } function resetGame() { const confirmReset = confirm( "🔁 Reset Everything?\nPoints will go to 0 and all levels will lock again!" ); if (!confirmReset) return; // ✅ reset player object (FULL RESET) player = { points: 0, unlockedLevel: 1, stars: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }, playedLevels: { 1: false, 2: false, 3: false, 4: false, 5: false }, levelScore: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 }, }; // ✅ clear localStorage progress localStorage.removeItem(STORAGE_KEY); // ✅ update UI updatePointsUI(); updateLocksUI(); for (let i = 1; i <= 5; i++) renderStars(i); // ✅ reset buttons back // (your default HTML already shows PLAY/BOSS so we reload by refreshing) // but since you asked not to change HTML, we just keep replay off until played again updateLevelButtonText(); // ✅ close modal if open closeGameModal(); // ✅ go home page showHomePage(); alert("✅ Game Reset Successfully!"); } // ------------------------------- // ✅ INIT GAME STATE // ------------------------------- loadProgress(); updatePointsUI(); updateLocksUI(); for (let i = 1; i <= 5; i++) renderStars(i); updateLevelButtonText(); // ------------------------------- // ✅ Make functions global for onclick="" // ------------------------------- window.openModal = openModal; window.closeModal = closeAnyModal; // for normal modals window.openInfoModal = openInfoModal; window.switchInfoTab = switchInfoTab; window.showStartPage = showStartPage; window.showHomePage = showHomePage; window.goHome = goHome; window.startLevel = startLevel; window.checkAnswer = checkAnswer; window.nextQuestion = nextQuestion; window.resetGame = resetGame; // ✅ game modal close function window.closeGameModal = closeGameModal; // ✅ music controls window.toggleMusic = toggleMusic; window.startMusic = startMusic; // =================================================== // ✅ PRACTICE MODE (HOME PAGE WORLDS - UNLIMITED) // ✅ Result shown in SAME POPUP ✅ // =================================================== let pWorld = 1; let pScore = 0; let pCorrect = 0; let pDifficulty = 0; let pAnswered = false; let pTimer = null; let pTimeLeft = 15; let pCurrentQ = null; const PRACTICE_POINTS = 500; // ✅ practice modal elements (must exist in HTML) const practiceModal = document.getElementById("practiceModal"); const practiceQuestion = document.getElementById("practiceQuestion"); const practiceProgress = document.getElementById("practiceProgress"); const practiceTimer = document.getElementById("practiceTimer"); const practiceScore = document.getElementById("practiceScore"); const pA = document.getElementById("pA"); const pB = document.getElementById("pB"); const pC = document.getElementById("pC"); const pD = document.getElementById("pD"); // ✅ result inside same popup const practiceResultBox = document.getElementById("practiceResultBox"); const practiceResultReason = document.getElementById("practiceResultReason"); const finalScore = document.getElementById("finalScore"); const finalCorrect = document.getElementById("finalCorrect"); const practiceResultImg = document.getElementById("practiceResultImg"); const practiceResultTitle = document.getElementById("practiceResultTitle"); const optionGrid = document.querySelector("#practiceModal .opt-grid"); const timerBadge = document.getElementById("practiceTimerBadge"); // ✅ safe random for practice mode function pRand(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } // ✅ generate question per world + harder every time function pGenerateQuestion(world) { let a = pRand(5 + pDifficulty * 2, 15 + pDifficulty * 3); let b = pRand(2, 10); let c = pRand(2, 7); let d = pRand(5 + pDifficulty, 25 + pDifficulty * 2); let e = pRand(2, 6); let expr = ""; let ans = 0; // ✅ World 1: only add/sub if (world === 1) { expr = `${a} + ${d} - ${b}`; ans = a + d - b; } // ✅ World 2: only division & multiplication if (world === 2) { let x = a * b; expr = `${x} ÷ ${b} × ${c}`; ans = (x / b) * c; } // ✅ World 3: Full BODMAS mixed if (world === 3) { expr = `${a} + ${d} ÷ ${b} × ${c}`; ans = Math.floor(a + (d / b) * c); } // ✅ World 4: PRO BODMAS if (world === 4) { expr = `(${a} + ${b}) × ${c} - ${d} ÷ ${e}`; ans = Math.floor((a + b) * c - d / e); } ans = Math.round(ans); // ✅ options let options = new Set([ans]); while (options.size < 4) { options.add(ans + pRand(-12, 12)); } options = Array.from(options).sort(() => Math.random() - 0.5); const map = { A: options[0], B: options[1], C: options[2], D: options[3] }; const correctKey = Object.keys(map).find((k) => map[k] === ans); return { question: expr, choiceA: map.A, choiceB: map.B, choiceC: map.C, choiceD: map.D, correct: correctKey, }; } // ✅ open practice popup function startWorldMode(w) { pWorld = w; pScore = 0; pCorrect = 0; pDifficulty = 0; pAnswered = false; openPractice(); pHideResult(); pLoadQuestion(); } // ✅ open/close modal function openPractice() { if (!practiceModal) return; practiceModal.classList.add("active"); } function closePractice() { pStopTimer(); practiceModal?.classList.remove("active"); } // ✅ timer gets harder (less time) function pStartTimer() { pStopTimer(); let maxTime = Math.max(5, 15 - Math.floor(pDifficulty / 2)); pTimeLeft = maxTime; if (practiceTimer) practiceTimer.textContent = pTimeLeft; pTimer = setInterval(() => { pTimeLeft--; if (practiceTimer) practiceTimer.textContent = pTimeLeft; if (pTimeLeft <= 0) { pStopTimer(); pEndPractice("⏳ Time Up!"); } }, 1000); } function pStopTimer() { if (pTimer) clearInterval(pTimer); pTimer = null; } // ✅ load question function pLoadQuestion() { pAnswered = false; pHideResult(); pCurrentQ = pGenerateQuestion(pWorld); if (practiceQuestion) practiceQuestion.textContent = pCurrentQ.question; if (pA) pA.textContent = "A) " + pCurrentQ.choiceA; if (pB) pB.textContent = "B) " + pCurrentQ.choiceB; if (pC) pC.textContent = "C) " + pCurrentQ.choiceC; if (pD) pD.textContent = "D) " + pCurrentQ.choiceD; if (practiceProgress) practiceProgress.textContent = `✅ Correct: ${pCorrect}`; if (practiceScore) practiceScore.textContent = `Score: ${pScore}`; [pA, pB, pC, pD].forEach((btn) => { if (!btn) return; btn.classList.remove("correct", "wrong"); btn.disabled = false; }); pStartTimer(); } // ✅ answer click function practiceAnswer(selected) { if (pAnswered) return; pAnswered = true; pStopTimer(); const correctKey = pCurrentQ.correct; const btnMap = { A: pA, B: pB, C: pC, D: pD }; if (selected === correctKey) { btnMap[selected]?.classList.add("correct"); pCorrect++; pScore += PRACTICE_POINTS; pDifficulty++; [pA, pB, pC, pD].forEach((btn) => { if (btn) btn.disabled = true; }); setTimeout(() => { pLoadQuestion(); }, 550); } else { btnMap[selected]?.classList.add("wrong"); btnMap[correctKey]?.classList.add("correct"); [pA, pB, pC, pD].forEach((btn) => { if (btn) btn.disabled = true; }); setTimeout(() => { pEndPractice("❌ Wrong Answer!"); }, 650); } } // ✅ end practice (result inside SAME POPUP) function pEndPractice(reason) { pStopTimer(); pShowResult(reason); } function pShowResult(reason) { if (!practiceResultBox) return; practiceResultBox.classList.remove("hidden"); // ✅ show wrong image (like Daily Challenge) if (practiceResultImg) { practiceResultImg.classList.remove("hidden"); practiceResultImg.src = "images/wrong.jpg"; // restart animation practiceResultImg.classList.remove("pop-zoom"); void practiceResultImg.offsetWidth; practiceResultImg.classList.add("pop-zoom"); } // ✅ title styling if (practiceResultTitle) { practiceResultTitle.textContent = "GAME OVER!"; practiceResultTitle.className = "bubble-text text-5xl font-black mb-3 text-red-400 drop-shadow-lg"; } if (practiceResultReason) practiceResultReason.textContent = reason; if (finalScore) finalScore.textContent = pScore; if (finalCorrect) finalCorrect.textContent = pCorrect; // ✅ hide question UI practiceQuestion?.classList.add("hidden"); optionGrid?.classList.add("hidden"); practiceProgress?.classList.add("hidden"); practiceScore?.classList.add("hidden"); timerBadge?.classList.add("hidden"); } function pHideResult() { practiceResultBox?.classList.add("hidden"); // ✅ hide image when restarting if (practiceResultImg) { practiceResultImg.classList.add("hidden"); practiceResultImg.src = ""; } practiceQuestion?.classList.remove("hidden"); optionGrid?.classList.remove("hidden"); practiceProgress?.classList.remove("hidden"); practiceScore?.classList.remove("hidden"); timerBadge?.classList.remove("hidden"); } // ✅ replay inside same popup function replayPractice() { pStopTimer(); pScore = 0; pCorrect = 0; pDifficulty = 0; pHideResult(); pLoadQuestion(); } // ✅ expose functions to HTML buttons window.startWorldMode = startWorldMode; window.practiceAnswer = practiceAnswer; window.replayPractice = replayPractice; window.closePractice = closePractice;