(() => { const mode = document.body.dataset.mode; // 'daily' | 'unlimited' const searchInput = document.getElementById("search-input"); const autocompleteBox = document.getElementById("autocomplete"); const boardBody = document.getElementById("board-body"); const attemptsInfo = document.getElementById("attempts-info"); const resultBox = document.getElementById("result-box"); const nicknameModal = document.getElementById("nickname-modal"); const playerNameEl = document.getElementById("player-name"); const streakLabel = document.getElementById("streak-label"); let player = PlayerStore.load(); let sessionId = null; let maxAttempts = 10; let finished = false; // ---------- Spieler ---------- function showNicknameModal() { if (!player) { nicknameModal.classList.remove("hidden"); } else { nicknameModal.classList.add("hidden"); playerNameEl.textContent = player.nickname; } renderStreaks(); } async function submitNickname() { const input = document.getElementById("nickname-input"); const nickname = input.value.trim(); if (!nickname) return; const res = await fetch("/api/player", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ nickname }), }); const data = await res.json(); if (data.player) { player = data.player; PlayerStore.save(player); PlayerStore.setStreaks(data.streaks); nicknameModal.classList.add("hidden"); playerNameEl.textContent = player.nickname; renderStreaks(); startGame(); } } function renderStreaks() { const streaks = PlayerStore.getStreaks(); if (!streaks) { streakLabel.textContent = ""; return; } const s = mode === "daily" ? streaks.daily : streaks.unlimited; if (!s) { streakLabel.textContent = ""; return; } streakLabel.textContent = `πŸ† ${s.win} Siege Β· πŸ’€ ${s.loss} Niederlagen`; } // ---------- Spielstart ---------- async function startGame() { if (!player) return; const res = await fetch("/api/start", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ player_id: player.id, mode }), }); const data = await res.json(); if (data.error) { alert(data.error); return; } sessionId = data.session_id; maxAttempts = data.max_attempts; attemptsInfo.textContent = `Versuche: 0 / ${maxAttempts}`; if (data.already_played) { finished = true; showResult(data.status, data.target, data.attempts_used); } } // ---------- Suche / Autocomplete ---------- let searchTimer = null; let suggestions = []; let selectedIndex = -1; searchInput.addEventListener("input", () => { clearTimeout(searchTimer); const q = searchInput.value.trim(); if (!q) { closeAutocomplete(); return; } searchTimer = setTimeout(async () => { const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`); suggestions = await res.json(); renderAutocomplete(); }, 200); }); function renderAutocomplete() { autocompleteBox.innerHTML = ""; selectedIndex = -1; if (!suggestions.length) { closeAutocomplete(); return; } suggestions.forEach((s, i) => { const div = document.createElement("div"); div.className = "autocomplete-item"; div.innerHTML = `${escapeHtml(s.title)}${s.year}`; div.addEventListener("mousedown", (e) => { e.preventDefault(); selectSuggestion(i); }); div.addEventListener("mouseenter", () => { selectedIndex = i; highlight(); }); autocompleteBox.appendChild(div); }); autocompleteBox.classList.add("open"); } function highlight() { [...autocompleteBox.children].forEach((el, i) => { el.classList.toggle("selected", i === selectedIndex); }); } function closeAutocomplete() { autocompleteBox.classList.remove("open"); autocompleteBox.innerHTML = ""; } function selectSuggestion(i) { const s = suggestions[i]; if (!s) return; closeAutocomplete(); submitGuess(s); } searchInput.addEventListener("keydown", (e) => { if (!autocompleteBox.classList.contains("open")) return; if (e.key === "ArrowDown") { e.preventDefault(); selectedIndex = Math.min(selectedIndex + 1, suggestions.length - 1); highlight(); } else if (e.key === "ArrowUp") { e.preventDefault(); selectedIndex = Math.max(selectedIndex - 1, 0); highlight(); } else if (e.key === "Enter") { e.preventDefault(); selectSuggestion(selectedIndex); } else if (e.key === "Escape") { closeAutocomplete(); } }); document.addEventListener("click", (e) => { if (!searchWrapperContains(e.target)) closeAutocomplete(); }); function searchWrapperContains(t) { const w = document.getElementById("search-wrapper"); return w.contains(t); } // ---------- Tipp abgeben ---------- async function submitGuess(movie) { if (finished || !sessionId) return; searchInput.value = ""; const res = await fetch("/api/guess", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ session_id: sessionId, movie_id: movie.id }), }); const data = await res.json(); if (data.error) { if (data.status) { finished = true; showResult(data.status, null, data.attempts_used || 0); } else { alert(data.error); } return; } attemptsInfo.textContent = `Versuche: ${data.attempts_used} / ${data.max_attempts}`; addRow(data.row); if (data.finished) { finished = true; if (data.streaks) { PlayerStore.setStreaks(data.streaks); renderStreaks(); } showResult(data.status, data.target, data.attempts_used); } } // ---------- Icons (Gamedle-/Steam-Stil) ---------- const ICONS = { platforms: { "Netflix": ["πŸ“Ί", "#e50914"], "Disney+": ["🐭", "#3f6fc9"], "Prime Video": ["πŸ“¦", "#00a8e1"], "Max": ["🌊", "#6a4fa3"], "Apple TV+": ["🍎", "#9aa0a6"], "Paramount+": ["πŸ”οΈ", "#0f5cab"], "Blu-ray": ["πŸ’Ώ", "#2f6fbe"], "DVD": ["πŸ’Ώ", "#8f959b"], "4K UHD": ["πŸ–₯️", "#7f5cd6"], "Cinema": ["🍿", "#e53935"], }, genres: { "Action": ["πŸ’₯", "#ff7043"], "Abenteuer": ["πŸ—ΊοΈ", "#a1887f"], "Animation": ["🎨", "#ab47bc"], "KomΓΆdie": ["πŸ˜‚", "#f6c445"], "Krimi": ["πŸ•΅οΈ", "#5c6bc0"], "Drama": ["🎭", "#7e8b98"], "Fantasy": ["πŸ‰", "#7e57c2"], "Horror": ["πŸ‘»", "#546e7a"], "Mystery": ["πŸ”", "#26a69a"], "Romantik": ["πŸ’˜", "#ec407a"], "Sci-Fi": ["πŸš€", "#29b6f6"], "Thriller": ["πŸ”ͺ", "#8d2f2f"], "Western": ["🀠", "#a1887f"], "Musical": ["🎡", "#f06292"], "Krieg": ["πŸͺ–", "#6d4c41"], "Biografie": ["πŸ“–", "#b0bec5"], }, studios: { "Warner Bros": ["🎞️", "#3f6fc9"], "Disney": ["🏰", "#2f6fbe"], "Universal": ["🌐", "#1d8aaa"], "Paramount": ["πŸ”οΈ", "#3c5a93"], "Sony Pictures": ["πŸŽ₯", "#7f5cd6"], "20th Century": ["🦊", "#e0892f"], "MGM": ["🦁", "#c2a845"], "Lionsgate": ["🦁", "#9aa0a6"], "A24": ["⏳", "#e2a11f"], "DreamWorks": ["πŸŒ™", "#5b6b8c"], "New Line": ["🟦", "#2f6fbe"], "Orion": ["⭐", "#c9c9c9"], "Neon": ["⚑", "#1fb8e0"], "CJ Entertainment": ["🎬", "#c93030"], "Show East": ["πŸŽ₯", "#666"], "Dimension": ["πŸŒ€", "#9c27b0"], "Netflix": ["πŸ“Ί", "#e50914"], }, universes: { "MCU": ["πŸ›‘οΈ", "#d63031"], "Star Wars": ["πŸŽ†", "#3a3f47"], "DC": ["πŸ¦‡", "#1f2937"], "Wizarding World": ["⚑", "#7e57c2"], "Fast & Furious": ["🏎️", "#37474f"], "Herr der Ringe": ["πŸ’", "#a1887f"], "Spider-Man": ["πŸ•·οΈ", "#c62828"], }, }; function iconFor(map, name) { if (!name || !map[name]) return "β–ͺ️"; return map[name][0]; } function colorFor(map, name) { if (!map || !name || !map[name]) return null; return map[name][1]; } // Badge mit Icon + Label function badge(map, name) { if (!name) return ""; const color = colorFor(map, name); const style = color ? `style="--tag:${color}"` : ""; return `${iconFor(map, name)}${escapeHtml(name)}`; } function addRow(row) { const tr = document.createElement("tr"); const titleTd = document.createElement("td"); titleTd.className = "title-cell"; titleTd.textContent = row.title; tr.appendChild(titleTd); tr.appendChild(cell(row.platforms.value.map(v => badge(ICONS.platforms, v)).join(""), row.platforms.status)); tr.appendChild(cell(row.genres.value.map(v => badge(ICONS.genres, v)).join(""), row.genres.status)); tr.appendChild(cell(yearHtml(row.year), row.year.status)); tr.appendChild(cell(fskHtml(row.fsk.value), row.fsk.status)); tr.appendChild(cell(badge(ICONS.studios, row.studio.value), row.studio.status)); tr.appendChild(cell(row.director.value ? `${escapeHtml(row.director.value)}` : `–`, row.director.status)); tr.appendChild(cell(budgetHtml(row.budget), row.budget.status)); tr.appendChild(cell(badge(ICONS.universes, row.universe.value), row.universe.status)); boardBody.prepend(tr); } function yearHtml(row) { return `${row.value}${arrowDiff(row.direction, row.diff)}`; } function fskHtml(fsk) { if (fsk == null) return `–`; return `${fsk}`; } function budgetHtml(row) { if (row.value == null) return `–`; return `${formatMoney(row.value)}${arrowMoney(row.direction, row.diff)}`; } // Pfeil nur (z.B. Jahr): β–² / β–Ό – bei >6 Jahren Abweichung in Rot function arrowDiff(dir, diff) { if (dir === "equal" || diff == null) return ""; const cls = dir === "up" ? "up" : "down"; const gly = dir === "up" ? "β–²" : "β–Ό"; const far = Math.abs(diff) > 6 ? " far" : ""; return `${gly}`; } // Pfeil nur (Budget): β–² / β–Ό function arrowMoney(dir, diff) { if (dir === "equal" || diff == null) return ""; const cls = dir === "up" ? "up" : "down"; const gly = dir === "up" ? "β–²" : "β–Ό"; return `${gly}`; } function cell(inner, status) { const td = document.createElement("td"); td.innerHTML = inner; td.classList.add("cell", status); return td; } function formatMoney(n) { if (n >= 1000000) return (n / 1000000).toFixed(0) + " Mio $"; return n.toLocaleString("de-DE") + " $"; } // ---------- Ergebnis ---------- function showResult(status, target, attemptsUsed) { resultBox.classList.remove("hidden", "won", "lost"); if (status === "won") { resultBox.classList.add("won"); resultBox.innerHTML = `
πŸŽ‰ Gewonnen! Du hast den Film in ${attemptsUsed} Versuch(en) erraten.
${target ? `
${escapeHtml(target.title)} (${target.year})
` : ""}${newGameButton()}`; } else { resultBox.classList.add("lost"); resultBox.innerHTML = `
😞 Verloren! Du hattest ${attemptsUsed} Versuch(e).
${target ? `Der gesuchte Film war:
${escapeHtml(target.title)} (${target.year})
` : ""}${newGameButton()}`; } } function newGameButton() { const label = mode === "unlimited" ? "Neues Spiel" : "ZurΓΌck zu TΓ€glich"; if (mode === "unlimited") { return ``; } return ""; } document.addEventListener("click", async (e) => { if (e.target.id === "nickname-submit") await submitNickname(); if (e.target.id === "new-game-btn") { finished = false; resultBox.classList.add("hidden"); resultBox.innerHTML = ""; boardBody.innerHTML = ""; const res = await fetch("/api/new-game", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ player_id: player.id }), }); const data = await res.json(); sessionId = data.session_id; maxAttempts = data.max_attempts; attemptsInfo.textContent = `Versuche: 0 / ${maxAttempts}`; } }); document.getElementById("nickname-input").addEventListener("keydown", (e) => { if (e.key === "Enter") submitNickname(); }); // ---------- Init ---------- function escapeHtml(s) { if (s == null) return ""; const div = document.createElement("div"); div.textContent = String(s); return div.innerHTML; } window.addEventListener("load", () => { showNicknameModal(); if (player) startGame(); }); })();