diff --git a/app.js b/app.js index c03aa9e..49ad5eb 100644 --- a/app.js +++ b/app.js @@ -81,10 +81,11 @@ currentInput: "", attempts: 0, startTime: 0, - timeTurnerCharges: 0 + timeTurnerCharges: 0, + forcedAnswer: false }; - let dementor = { interval: null, progress: 0 }; + let dementor = { interval: null, progress: 0, timedOut: false }; let snitchTimer = null; let cheatClicks = 0; @@ -168,6 +169,7 @@ resetDementor(); game.currentInput = ""; game.attempts = 0; + game.forcedAnswer = false; game.startTime = Date.now(); document.getElementById('answer-display').innerText = ""; @@ -175,6 +177,10 @@ const hintGrid = document.getElementById('hint-grid'); hintGrid.innerHTML = ""; hintGrid.classList.remove('has-stars'); + const castBtn = document.querySelector('.key-cast'); + const answerDisplay = document.getElementById('answer-display'); + if (castBtn) castBtn.classList.remove('ready-cast'); + if (answerDisplay) answerDisplay.classList.remove('forced-answer'); if (game.timeTurnerCharges > 0) { game.timeTurnerCharges--; @@ -284,24 +290,7 @@ } saveData(); updateUI(); setTimeout(generateQuestion, 1000); } else { - state.streak = 0; - game.attempts++; - addToTroubleList(game.currentFact.n1, game.currentFact.n2); - card.classList.add('apply-shake'); - setTimeout(() => card.classList.remove('apply-shake'), 500); - dementor.progress += 0.2; updateDementorVisuals(); - saveData(); updateUI(); - game.currentInput = ""; - document.getElementById('answer-display').innerText = ""; - - if (game.attempts >= 3) { - feedback.style.color = '#ffb0b0'; - feedback.innerText = `Answer is ${game.currentFact.ans}.`; - } else { - feedback.style.color = '#ffb0b0'; - feedback.innerText = "Count the stars..."; - drawHintGrid(game.currentFact.n1, game.currentFact.n2); - } + handleWrongAttempt(); } } @@ -697,6 +686,7 @@ function startDementor(resume = false) { clearInterval(dementor.interval); if (!resume) dementor.progress = 0; + dementor.timedOut = false; let speed = 250; const step = 0.02; if (game.timeTurnerCharges > 0) speed = 600; @@ -706,6 +696,9 @@ if (!game.active) return; if (dementor.progress < 1) { dementor.progress += step; updateDementorVisuals(); + } else if (!dementor.timedOut) { + dementor.timedOut = true; + handleDementorTimeout(); } }, speed); } @@ -725,9 +718,11 @@ overlay.style.setProperty('--circle-blur', `${blur}px`); overlay.style.setProperty('--vignette-strength', vignette.toFixed(2)); overlay.style.background = buildDementorBackground(p, size, darkness, vignette); - const ring = document.getElementById('timer-ring'); - if (ring) ring.style.setProperty('--timer-progress', p.toFixed(3)); + const castTimer = document.getElementById('cast-timer-fill'); + if (castTimer) castTimer.style.width = `${Math.max(0, 100 - p * 100)}%`; document.documentElement.style.setProperty('--focus-glow', glow.toFixed(2)); + const uiGlow = Math.min(1.2, 0.15 + (p * 1.05)); + document.documentElement.style.setProperty('--ui-glow', uiGlow.toFixed(2)); } function resetDementor() { @@ -739,10 +734,12 @@ overlay.style.setProperty('--circle-blur', '0px'); overlay.style.setProperty('--vignette-strength', '0.18'); overlay.style.background = 'transparent'; - const ring = document.getElementById('timer-ring'); - if (ring) ring.style.setProperty('--timer-progress', '0'); + const castTimer = document.getElementById('cast-timer-fill'); + if (castTimer) castTimer.style.width = '100%'; document.documentElement.style.setProperty('--focus-glow', '0'); + document.documentElement.style.setProperty('--ui-glow', '0'); saveData(); + dementor.timedOut = false; } function buildDementorBackground(progress, size, strength, vignette) { @@ -798,6 +795,82 @@ grid.classList.add('has-stars'); } + function handleWrongAttempt(options = {}) { + const { timedOut = false } = options; + const feedback = document.getElementById('feedback-msg'); + const card = document.querySelector('.card-container'); + state.streak = 0; + game.attempts = timedOut ? 3 : game.attempts + 1; + if (card) { + card.classList.add('apply-shake'); + setTimeout(() => card.classList.remove('apply-shake'), 500); + } + if (!timedOut) { + dementor.progress += 0.2; updateDementorVisuals(); + game.currentInput = ""; + document.getElementById('answer-display').innerText = ""; + } + if (game.attempts >= 3) { + revealAnswerAndAdvance(timedOut ? 2000 : 0, timedOut); + return; + } + if (game.attempts === 2) { + feedback.style.color = '#ffb0b0'; + feedback.innerText = "Count the stars..."; + drawHintGrid(game.currentFact.n1, game.currentFact.n2); + } else { + feedback.style.color = '#ffb0b0'; + feedback.innerText = "Try again!"; + } + saveData(); updateUI(); + } + + function revealAnswerAndAdvance(delayMs = 0, timedOut = false) { + const feedback = document.getElementById('feedback-msg'); + const card = document.querySelector('.card-container'); + const factId = `${Math.min(game.currentFact.n1, game.currentFact.n2)}x${Math.max(game.currentFact.n1, game.currentFact.n2)}`; + game.active = false; + setTimeout(() => { + // guard in case question changed + const currentId = `${Math.min(game.currentFact.n1, game.currentFact.n2)}x${Math.max(game.currentFact.n1, game.currentFact.n2)}`; + if (currentId !== factId) return; + if (card) { + card.classList.add('apply-shake'); + setTimeout(() => card.classList.remove('apply-shake'), 500); + } + feedback.style.color = '#ffb0b0'; + feedback.innerText = `Answer is ${game.currentFact.ans}. Press CAST to continue.`; + addToTroubleList(game.currentFact.n1, game.currentFact.n2); + game.currentInput = game.currentFact.ans.toString(); + const answerDisplay = document.getElementById('answer-display'); + if (answerDisplay) { + answerDisplay.innerText = game.currentInput; + answerDisplay.classList.add('forced-answer'); + } + const castBtn = document.querySelector('.key-cast'); + if (castBtn) castBtn.classList.add('ready-cast'); + game.forcedAnswer = true; + resetDementor(); + saveData(); updateUI(); + game.active = true; + }, delayMs); + } + + function handleDementorTimeout() { + const factId = `${Math.min(game.currentFact.n1, game.currentFact.n2)}x${Math.max(game.currentFact.n1, game.currentFact.n2)}`; + game.active = false; + setTimeout(() => { + const currentId = `${Math.min(game.currentFact.n1, game.currentFact.n2)}x${Math.max(game.currentFact.n1, game.currentFact.n2)}`; + if (currentId !== factId) return; + const feedback = document.getElementById('feedback-msg'); + if (feedback) { + feedback.style.color = '#ffb0b0'; + feedback.innerText = "Saved by a Professor..."; + } + handleWrongAttempt({ timedOut: true }); + }, 2000); + } + function applyXPToMastery() { const levelData = CURRICULUM[state.levelIndex]; const requiredFacts = getFactsForLevel(state.levelIndex); @@ -864,7 +937,7 @@ // --- GOLDEN SNITCH EVENT --- function scheduleSnitch() { if (snitchTimer) clearTimeout(snitchTimer); - const delay = 20000 + Math.random() * 20000; // 20–40s + const delay = 45000 + Math.random() * 30000; // 45–75s snitchTimer = setTimeout(spawnSnitch, delay); } @@ -874,6 +947,10 @@ scheduleSnitch(); return; } + if (dementor.progress > 0.6) { // scared off by looming dementor + scheduleSnitch(); + return; + } const snitch = document.createElement('div'); snitch.className = 'flying-snitch'; const fromLeft = Math.random() > 0.5; diff --git a/index.html b/index.html index e71076b..6a5e5b7 100644 --- a/index.html +++ b/index.html @@ -64,7 +64,6 @@