607 lines
21 KiB
JavaScript
Executable File
607 lines
21 KiB
JavaScript
Executable File
let currentSessionId = null;
|
||
let currentQuestionNumber = 1;
|
||
let totalQuestions = 10;
|
||
let currentScore = 0;
|
||
let currentStreak = 0;
|
||
let attemptsUsed = 0;
|
||
const maxAttempts = 3;
|
||
let selectedDifficulty = "all";
|
||
let selectedCategory = "all";
|
||
let lastQuestionData = null;
|
||
let timerStartTime = null;
|
||
let timerInterval = null;
|
||
let elapsedSeconds = 0;
|
||
|
||
const CATEGORY_LABELS = {
|
||
de: {
|
||
all: "✨ Alle Kategorien",
|
||
books: "📖 Buchwissen",
|
||
movies: "🎬 Filmwissen",
|
||
creatures: "🐉 Magische Geschöpfe",
|
||
spells: "🪄 Zaubersprüche & Tränke",
|
||
characters: "🧙♂️ Charaktere",
|
||
hogwarts: "🏰 Hogwarts & Orte",
|
||
quidditch: "🧹 Quidditch",
|
||
lore: "🔮 Artefakte & Lore",
|
||
general: "⚡ Allgemein",
|
||
harry_potter: "⚡ Harry Potter"
|
||
},
|
||
en: {
|
||
all: "✨ All Categories",
|
||
books: "📖 Book Lore",
|
||
movies: "🎬 Movie Trivia",
|
||
creatures: "🐉 Magical Creatures",
|
||
spells: "🪄 Spells & Potions",
|
||
characters: "🧙♂️ Characters",
|
||
hogwarts: "🏰 Hogwarts & Places",
|
||
quidditch: "🧹 Quidditch",
|
||
lore: "🔮 Artifacts & Lore",
|
||
general: "⚡ General",
|
||
harry_potter: "⚡ Harry Potter"
|
||
}
|
||
};
|
||
|
||
const UI_TEXT = {
|
||
de: {
|
||
pageTitle: "Harry Potter Wissensquiz",
|
||
pageSubtitle: "Teste dein Wissen über Bücher, Filme, Geschöpfe und Zaubersprüche – direkt & ohne Umwege.",
|
||
diffLabel: "Schwierigkeitsgrad",
|
||
diffAll: "Alle Stufen",
|
||
diffEasy: "Leicht",
|
||
diffMed: "Mittel",
|
||
diffHard: "Schwer (Experte)",
|
||
catLabel: "Themengebiet wählen",
|
||
catAll: "Alle Kategorien",
|
||
catBooks: "Buchwissen",
|
||
catMovies: "Filmwissen",
|
||
catCreatures: "Magische Geschöpfe",
|
||
catSpells: "Zaubersprüche & Tränke",
|
||
catChars: "Charaktere",
|
||
catHogwarts: "Hogwarts & Orte",
|
||
catQuidditch: "Quidditch",
|
||
catLore: "Artefakte & Lore",
|
||
dashboardRules: "<span>🎯 10 Fragen pro Runde</span><span>·</span><span>🔥 Streak-Zähler</span><span>·</span><span>💡 Bis zu 3 Versuche je Frage</span>",
|
||
startQuizBtn: "⚡ Quiz jetzt starten ➔",
|
||
startBtnCasting: "⚡ Runde wird geladen...",
|
||
pointsLabel: "Pkt.",
|
||
questionLabel: "Frage",
|
||
ofLabel: "/",
|
||
attemptLabel: "Versuch",
|
||
checkBtn: "✓ Antwort prüfen",
|
||
hintBtn: "💡 Tipp anzeigen",
|
||
hintHeading: "💡 Hinweis:",
|
||
placeholder: "Deine Antwort eingeben...",
|
||
noHint: "Kein Tipp vorhanden.",
|
||
hintFailed: "Tipp konnte nicht geladen werden.",
|
||
wrongFeedback: "Nicht ganz! Noch {attempts} Versuch(e).",
|
||
exhaustedWrong: "Leider nicht gelöst (3 Fehlversuche).",
|
||
solutionLabel: "Richtige Antwort:",
|
||
startError: "Fehler beim Starten des Quiz: ",
|
||
networkError: "Netzwerkfehler: "
|
||
},
|
||
en: {
|
||
pageTitle: "Harry Potter Trivia Quiz",
|
||
pageSubtitle: "Test your knowledge across books, movies, creatures, and spells — straight to the action.",
|
||
diffLabel: "Difficulty Level",
|
||
diffAll: "All Levels",
|
||
diffEasy: "Easy",
|
||
diffMed: "Medium",
|
||
diffHard: "Hard (Expert)",
|
||
catLabel: "Select Topic",
|
||
catAll: "All Categories",
|
||
catBooks: "Book Lore",
|
||
catMovies: "Movie Trivia",
|
||
catCreatures: "Magical Creatures",
|
||
catSpells: "Spells & Potions",
|
||
catChars: "Characters",
|
||
catHogwarts: "Hogwarts & Places",
|
||
catQuidditch: "Quidditch",
|
||
catLore: "Artifacts & Lore",
|
||
dashboardRules: "<span>🎯 10 Questions per Round</span><span>·</span><span>🔥 Answer Streak</span><span>·</span><span>💡 Up to 3 Attempts</span>",
|
||
startQuizBtn: "⚡ Start Quiz Now ➔",
|
||
startBtnCasting: "⚡ Loading Round...",
|
||
pointsLabel: "Pts",
|
||
questionLabel: "Question",
|
||
ofLabel: "/",
|
||
attemptLabel: "Attempt",
|
||
checkBtn: "✓ Check Answer",
|
||
hintBtn: "💡 Show Hint",
|
||
hintHeading: "💡 Hint:",
|
||
placeholder: "Type your answer...",
|
||
noHint: "No hint available.",
|
||
hintFailed: "Could not load hint.",
|
||
wrongFeedback: "Not quite! {attempts} attempt(s) left.",
|
||
exhaustedWrong: "Out of attempts (3 failed tries).",
|
||
solutionLabel: "Correct answer:",
|
||
startError: "Error starting quiz: ",
|
||
networkError: "Network error: "
|
||
}
|
||
};
|
||
|
||
// Web Audio API Sound Generator for crisp trivia feedback
|
||
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||
|
||
function playSound(type) {
|
||
if (audioCtx.state === 'suspended') {
|
||
audioCtx.resume();
|
||
}
|
||
if (type === 'magic') {
|
||
const notes = [523.25, 659.25, 783.99, 1046.50]; // C5, E5, G5, C6
|
||
notes.forEach((freq, index) => {
|
||
const osc = audioCtx.createOscillator();
|
||
const gain = audioCtx.createGain();
|
||
osc.type = 'sine';
|
||
osc.frequency.value = freq;
|
||
gain.gain.setValueAtTime(0.12, audioCtx.currentTime + index * 0.07);
|
||
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + index * 0.07 + 0.3);
|
||
osc.connect(gain);
|
||
gain.connect(audioCtx.destination);
|
||
osc.start(audioCtx.currentTime + index * 0.07);
|
||
osc.stop(audioCtx.currentTime + index * 0.07 + 0.3);
|
||
});
|
||
} else if (type === 'wrong') {
|
||
const osc = audioCtx.createOscillator();
|
||
const gain = audioCtx.createGain();
|
||
osc.type = 'triangle';
|
||
osc.frequency.setValueAtTime(180, audioCtx.currentTime);
|
||
osc.frequency.exponentialRampToValueAtTime(95, audioCtx.currentTime + 0.22);
|
||
gain.gain.setValueAtTime(0.12, audioCtx.currentTime);
|
||
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.22);
|
||
osc.connect(gain);
|
||
gain.connect(audioCtx.destination);
|
||
osc.start();
|
||
osc.stop(audioCtx.currentTime + 0.22);
|
||
}
|
||
}
|
||
|
||
function selectDifficulty(diff) {
|
||
selectedDifficulty = diff;
|
||
document.querySelectorAll(".diff-chip").forEach(chip => {
|
||
chip.classList.toggle("active", chip.getAttribute("data-diff") === diff);
|
||
});
|
||
}
|
||
|
||
function selectCategory(cat) {
|
||
selectedCategory = cat;
|
||
document.querySelectorAll(".cat-chip").forEach(chip => {
|
||
chip.classList.toggle("active", chip.getAttribute("data-cat") === cat);
|
||
});
|
||
}
|
||
|
||
function updateStreakUI(animate = false) {
|
||
const badge = document.getElementById("streakBadge");
|
||
if (!badge) return;
|
||
badge.innerText = currentStreak >= 2 ? `🔥 ${currentStreak} Streak` : `🔥 ${currentStreak}`;
|
||
badge.classList.toggle("streak-hot", currentStreak >= 2);
|
||
if (animate) {
|
||
badge.classList.remove("streak-pop");
|
||
void badge.offsetWidth;
|
||
badge.classList.add("streak-pop");
|
||
}
|
||
}
|
||
|
||
function onLanguageChange(lang) {
|
||
applyQuizTranslations(lang);
|
||
}
|
||
|
||
function applyQuizTranslations(lang) {
|
||
const t = UI_TEXT[lang] || UI_TEXT.de;
|
||
const setTxt = (id, val) => {
|
||
const el = document.getElementById(id);
|
||
if (el && val) el.innerText = val;
|
||
};
|
||
|
||
setTxt("pageTitle", t.pageTitle);
|
||
setTxt("pageSubtitle", t.pageSubtitle);
|
||
setTxt("diffLabel", t.diffLabel);
|
||
setTxt("diffAllText", t.diffAll);
|
||
setTxt("diffEasyText", t.diffEasy);
|
||
setTxt("diffMedText", t.diffMed);
|
||
setTxt("diffHardText", t.diffHard);
|
||
setTxt("catLabel", t.catLabel);
|
||
setTxt("catAllText", t.catAll);
|
||
setTxt("catBooksText", t.catBooks);
|
||
setTxt("catMoviesText", t.catMovies);
|
||
setTxt("catCreaturesText", t.catCreatures);
|
||
setTxt("catSpellsText", t.catSpells);
|
||
setTxt("catCharsText", t.catChars);
|
||
setTxt("catHogwartsText", t.catHogwarts);
|
||
setTxt("catQuidditchText", t.catQuidditch);
|
||
setTxt("catLoreText", t.catLore);
|
||
|
||
const rulesEl = document.getElementById("dashboardRules");
|
||
if (rulesEl) rulesEl.innerHTML = t.dashboardRules;
|
||
|
||
const startQuizBtn = document.getElementById("startQuizBtn");
|
||
if (startQuizBtn && !startQuizBtn.disabled) startQuizBtn.innerText = t.startQuizBtn;
|
||
|
||
setTxt("checkBtn", t.checkBtn);
|
||
setTxt("hintBtn", t.hintBtn);
|
||
setTxt("hintBtnMC", t.hintBtn);
|
||
setTxt("hintHeading", t.hintHeading);
|
||
|
||
const answerInput = document.getElementById("answerInput");
|
||
if (answerInput) answerInput.placeholder = t.placeholder;
|
||
|
||
if (lastQuestionData) {
|
||
renderQuestion(lastQuestionData);
|
||
}
|
||
}
|
||
|
||
let sessionQuestionIds = [];
|
||
|
||
function getRecentQuestionIds() {
|
||
try {
|
||
const stored = localStorage.getItem("quiz_recent_qids");
|
||
return stored ? JSON.parse(stored) : [];
|
||
} catch(e) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
function getWrongQuestionIds() {
|
||
try {
|
||
const stored = localStorage.getItem("quiz_wrong_qids");
|
||
return stored ? JSON.parse(stored) : [];
|
||
} catch(e) {
|
||
return [];
|
||
}
|
||
}
|
||
|
||
function recordQuestionWrong(qid) {
|
||
if (!qid) return;
|
||
try {
|
||
const wrong = getWrongQuestionIds();
|
||
if (!wrong.includes(qid)) {
|
||
wrong.push(qid);
|
||
if (wrong.length > 30) wrong.shift();
|
||
localStorage.setItem("quiz_wrong_qids", JSON.stringify(wrong));
|
||
}
|
||
} catch(e) {}
|
||
}
|
||
|
||
function recordQuestionMastered(qid) {
|
||
if (!qid) return;
|
||
try {
|
||
let wrong = getWrongQuestionIds();
|
||
if (wrong.includes(qid)) {
|
||
wrong = wrong.filter(id => id !== qid);
|
||
localStorage.setItem("quiz_wrong_qids", JSON.stringify(wrong));
|
||
}
|
||
} catch(e) {}
|
||
}
|
||
|
||
function saveRecentQuestions(ids) {
|
||
if (!ids || ids.length === 0) return;
|
||
try {
|
||
let recent = getRecentQuestionIds();
|
||
recent = Array.from(new Set([...ids, ...recent])).slice(0, 20);
|
||
localStorage.setItem("quiz_recent_qids", JSON.stringify(recent));
|
||
} catch(e) {}
|
||
}
|
||
|
||
async function startGame() {
|
||
const startQuizBtn = document.getElementById("startQuizBtn");
|
||
const t = UI_TEXT[currentLang] || UI_TEXT.de;
|
||
|
||
if (startQuizBtn) {
|
||
startQuizBtn.disabled = true;
|
||
startQuizBtn.innerText = t.startBtnCasting;
|
||
}
|
||
|
||
playSound('magic');
|
||
sessionQuestionIds = [];
|
||
currentStreak = 0;
|
||
updateStreakUI(false);
|
||
|
||
try {
|
||
const recentIds = getRecentQuestionIds();
|
||
const wrongIds = getWrongQuestionIds();
|
||
|
||
const res = await fetch("/api/game/start", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
lang: currentLang,
|
||
difficulty: selectedDifficulty,
|
||
category: selectedCategory,
|
||
recent_ids: recentIds,
|
||
wrong_ids: wrongIds
|
||
})
|
||
});
|
||
|
||
if (!res.ok) {
|
||
throw new Error(currentLang === "en" ? "Could not start game session." : "Konnte Spiel nicht starten.");
|
||
}
|
||
|
||
const data = await res.json();
|
||
currentSessionId = data.session_id;
|
||
currentQuestionNumber = data.question_number;
|
||
totalQuestions = data.total_questions;
|
||
currentScore = data.score;
|
||
attemptsUsed = data.attempts_used;
|
||
|
||
const introCard = document.getElementById("introCard");
|
||
if (introCard) {
|
||
introCard.classList.add("hidden");
|
||
}
|
||
document.getElementById("gameCard").classList.remove("hidden");
|
||
startTimer();
|
||
renderQuestion(data);
|
||
|
||
} catch (err) {
|
||
alert(t.startError + err.message);
|
||
if (startQuizBtn) {
|
||
startQuizBtn.disabled = false;
|
||
startQuizBtn.innerText = t.startQuizBtn;
|
||
}
|
||
}
|
||
}
|
||
|
||
function startTimer() {
|
||
timerStartTime = Date.now();
|
||
elapsedSeconds = 0;
|
||
if (timerInterval) clearInterval(timerInterval);
|
||
timerInterval = setInterval(updateTimer, 1000);
|
||
updateTimer();
|
||
}
|
||
|
||
function updateTimer() {
|
||
if (!timerStartTime) return;
|
||
elapsedSeconds = Math.floor((Date.now() - timerStartTime) / 1000);
|
||
const m = Math.floor(elapsedSeconds / 60);
|
||
const s = elapsedSeconds % 60;
|
||
const badge = document.getElementById("timerBadge");
|
||
if (badge) {
|
||
badge.innerText = `⏱️ ${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
||
}
|
||
}
|
||
|
||
function renderQuestion(qData) {
|
||
lastQuestionData = qData;
|
||
if (qData.id && !sessionQuestionIds.includes(qData.id)) {
|
||
sessionQuestionIds.push(qData.id);
|
||
}
|
||
const t = UI_TEXT[currentLang] || UI_TEXT.de;
|
||
const catMap = CATEGORY_LABELS[currentLang] || CATEGORY_LABELS.de;
|
||
|
||
document.getElementById("questionCounter").innerText = `${t.questionLabel} ${qData.question_number} ${t.ofLabel} ${totalQuestions}`;
|
||
document.getElementById("pointsCounter").innerText = `🏆 ${currentScore} ${t.pointsLabel}`;
|
||
|
||
const rawCat = (qData.category || "general").toLowerCase();
|
||
document.getElementById("badgeCategory").innerText = catMap[rawCat] || `⚡ ${rawCat.toUpperCase()}`;
|
||
|
||
const badgeDiff = document.getElementById("badgeDifficulty");
|
||
if (badgeDiff) {
|
||
const diff = qData.difficulty || "medium";
|
||
badgeDiff.className = `badge badge-diff badge-diff-${diff}`;
|
||
let diffText = t.diffMed;
|
||
let diffIcon = "🟡";
|
||
if (diff === "easy") {
|
||
diffText = t.diffEasy;
|
||
diffIcon = "🟢";
|
||
} else if (diff === "hard") {
|
||
diffText = currentLang === "en" ? "Hard" : "Schwer";
|
||
diffIcon = "🔴";
|
||
}
|
||
badgeDiff.innerText = `${diffIcon} ${diffText}`;
|
||
}
|
||
|
||
document.getElementById("attemptBadge").innerText = `${t.attemptLabel} ${attemptsUsed + 1} ${t.ofLabel} ${maxAttempts}`;
|
||
document.getElementById("frageText").innerText = qData.question;
|
||
|
||
const textGroup = document.getElementById("textAnswerGroup");
|
||
const choiceGroup = document.getElementById("choiceAnswerGroup");
|
||
const choiceContainer = document.getElementById("choiceOptionsContainer");
|
||
const input = document.getElementById("answerInput");
|
||
|
||
document.getElementById("hintBox").classList.add("hidden");
|
||
document.getElementById("feedbackBox").innerHTML = "";
|
||
|
||
if (qData.question_type === "multiple_choice" && qData.options && qData.options.length > 0) {
|
||
textGroup.classList.add("hidden");
|
||
choiceGroup.classList.remove("hidden");
|
||
choiceContainer.innerHTML = "";
|
||
|
||
const letters = ["A", "B", "C", "D", "E", "F"];
|
||
qData.options.forEach((opt, idx) => {
|
||
const btn = document.createElement("button");
|
||
btn.className = "choice-btn";
|
||
btn.type = "button";
|
||
btn.innerHTML = `<span class="choice-letter">${letters[idx] || "•"}</span> <span class="choice-text">${opt}</span>`;
|
||
btn.onclick = () => submitAnswer(opt, btn);
|
||
choiceContainer.appendChild(btn);
|
||
});
|
||
} else {
|
||
choiceGroup.classList.add("hidden");
|
||
textGroup.classList.remove("hidden");
|
||
input.value = "";
|
||
input.placeholder = t.placeholder;
|
||
input.disabled = false;
|
||
document.getElementById("checkBtn").disabled = false;
|
||
input.focus();
|
||
}
|
||
|
||
const hintBtn = document.getElementById("hintBtn");
|
||
if (hintBtn) hintBtn.disabled = false;
|
||
const hintBtnMC = document.getElementById("hintBtnMC");
|
||
if (hintBtnMC) hintBtnMC.disabled = false;
|
||
|
||
updateProgress();
|
||
}
|
||
|
||
function updateProgress() {
|
||
const percent = ((currentQuestionNumber - 1) / totalQuestions) * 100;
|
||
document.getElementById("progressBar").style.width = percent + "%";
|
||
}
|
||
|
||
function checkAnswer() {
|
||
const input = document.getElementById("answerInput");
|
||
const answer = input.value.trim();
|
||
if (!answer) {
|
||
input.focus();
|
||
return;
|
||
}
|
||
submitAnswer(answer);
|
||
}
|
||
|
||
async function submitAnswer(answer, clickedBtn = null) {
|
||
const checkBtn = document.getElementById("checkBtn");
|
||
const input = document.getElementById("answerInput");
|
||
const t = UI_TEXT[currentLang] || UI_TEXT.de;
|
||
|
||
if (checkBtn) checkBtn.disabled = true;
|
||
if (input) input.disabled = true;
|
||
|
||
const choiceBtns = document.querySelectorAll(".choice-btn");
|
||
choiceBtns.forEach(b => b.disabled = true);
|
||
if (clickedBtn) {
|
||
clickedBtn.classList.add("choice-selected");
|
||
}
|
||
|
||
try {
|
||
const res = await fetch("/api/game/check", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({
|
||
session_id: currentSessionId,
|
||
answer: answer
|
||
})
|
||
});
|
||
|
||
if (!res.ok) {
|
||
throw new Error(currentLang === "en" ? "Answer check failed." : "Fehler bei der Antwortprüfung.");
|
||
}
|
||
|
||
const result = await res.json();
|
||
currentScore = result.score;
|
||
document.getElementById("pointsCounter").innerText = `🏆 ${currentScore} ${t.pointsLabel}`;
|
||
|
||
const frageBox = document.getElementById("frageBox");
|
||
const feedbackBox = document.getElementById("feedbackBox");
|
||
|
||
if (result.correct) {
|
||
playSound('magic');
|
||
currentStreak += 1;
|
||
updateStreakUI(true);
|
||
|
||
if (clickedBtn) {
|
||
clickedBtn.classList.remove("choice-selected");
|
||
clickedBtn.classList.add("choice-correct");
|
||
}
|
||
frageBox.classList.add("sparkle-effect");
|
||
|
||
if (result.question_id) {
|
||
recordQuestionMastered(result.question_id);
|
||
}
|
||
|
||
const pts = result.points_earned || 100;
|
||
const streakBonusText = currentStreak >= 3 ? ` · 🔥 ${currentStreak}er Serie!` : "";
|
||
const ptsText = (currentLang === "en")
|
||
? `✓ Correct! +${pts} Points${currentStreak >= 3 ? ` · 🔥 ${currentStreak} Streak!` : ""}`
|
||
: `✓ Richtig! +${pts} Punkte${streakBonusText}`;
|
||
feedbackBox.innerHTML = `<span class="feedback-correct">${ptsText}</span>`;
|
||
|
||
setTimeout(() => {
|
||
frageBox.classList.remove("sparkle-effect");
|
||
if (result.is_finished) {
|
||
finishGame();
|
||
} else if (result.next_question) {
|
||
currentQuestionNumber = result.next_question.question_number;
|
||
attemptsUsed = 0;
|
||
renderQuestion(result.next_question);
|
||
}
|
||
}, 1100);
|
||
|
||
} else {
|
||
playSound('wrong');
|
||
currentStreak = 0;
|
||
updateStreakUI(false);
|
||
|
||
if (clickedBtn) {
|
||
clickedBtn.classList.remove("choice-selected");
|
||
clickedBtn.classList.add("choice-wrong");
|
||
}
|
||
frageBox.classList.add("shake-effect");
|
||
setTimeout(() => frageBox.classList.remove("shake-effect"), 500);
|
||
|
||
attemptsUsed = result.attempts_used;
|
||
document.getElementById("attemptBadge").innerText = `${t.attemptLabel} ${Math.min(attemptsUsed + 1, maxAttempts)} ${t.ofLabel} ${maxAttempts}`;
|
||
|
||
if (result.attempts_left === 0) {
|
||
if (result.question_id) {
|
||
recordQuestionWrong(result.question_id);
|
||
}
|
||
|
||
feedbackBox.innerHTML = `
|
||
<div class="feedback-wrong">${t.exhaustedWrong}</div>
|
||
<div class="feedback-revealed">${t.solutionLabel} <strong>${result.revealed_answer}</strong></div>
|
||
`;
|
||
|
||
setTimeout(() => {
|
||
if (result.is_finished) {
|
||
finishGame();
|
||
} else if (result.next_question) {
|
||
currentQuestionNumber = result.next_question.question_number;
|
||
attemptsUsed = 0;
|
||
renderQuestion(result.next_question);
|
||
}
|
||
}, 2200);
|
||
|
||
} else {
|
||
const msg = t.wrongFeedback.replace("{attempts}", result.attempts_left);
|
||
feedbackBox.innerHTML = `<span class="feedback-wrong">${msg}</span>`;
|
||
if (checkBtn) checkBtn.disabled = false;
|
||
if (input) {
|
||
input.disabled = false;
|
||
input.select();
|
||
}
|
||
choiceBtns.forEach(b => b.disabled = false);
|
||
}
|
||
}
|
||
|
||
} catch (err) {
|
||
alert(t.networkError + err.message);
|
||
if (checkBtn) checkBtn.disabled = false;
|
||
if (input) input.disabled = false;
|
||
choiceBtns.forEach(b => b.disabled = false);
|
||
}
|
||
}
|
||
|
||
async function requestHint() {
|
||
if (!currentSessionId) return;
|
||
const hintBox = document.getElementById("hintBox");
|
||
const hintText = document.getElementById("hintText");
|
||
const t = UI_TEXT[currentLang] || UI_TEXT.de;
|
||
|
||
try {
|
||
const res = await fetch(`/api/game/hint?session_id=${encodeURIComponent(currentSessionId)}`);
|
||
const data = await res.json();
|
||
hintText.innerText = data.hint || t.noHint;
|
||
hintBox.classList.remove("hidden");
|
||
} catch (err) {
|
||
hintText.innerText = t.hintFailed;
|
||
hintBox.classList.remove("hidden");
|
||
}
|
||
}
|
||
|
||
function finishGame() {
|
||
if (timerInterval) clearInterval(timerInterval);
|
||
saveRecentQuestions(sessionQuestionIds);
|
||
document.getElementById("progressBar").style.width = "100%";
|
||
window.location.href = `/finale?score=${currentScore}&total=${totalQuestions}&time=${elapsedSeconds}&lang=${currentLang}&diff=${encodeURIComponent(selectedDifficulty)}`;
|
||
}
|
||
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
const input = document.getElementById("answerInput");
|
||
if (input) {
|
||
input.addEventListener("keydown", (e) => {
|
||
if (e.key === "Enter") {
|
||
e.preventDefault();
|
||
checkAnswer();
|
||
}
|
||
});
|
||
}
|
||
applyQuizTranslations(currentLang);
|
||
});
|