// Wichtel-Panik / Cornish Pixies Arcade Engine (Mobile First) (function() { let score = 0; const targetScore = 10; let timeLeft = 35; let gameRunning = false; let timerInterval = null; let spawnTimeout = null; // DOM Elements const scoreEl = document.getElementById('pixieScore'); const timerEl = document.getElementById('pixieTimer'); const startOverlay = document.getElementById('startOverlay'); const startBtn = document.getElementById('startPixieBtn'); const successPanel = document.getElementById('pixieSuccess'); const holes = document.querySelectorAll('.pixie-hole'); const activeTargets = new Map(); // holeIndex -> { isNeville: bool, timer: id, clicked: bool } window.startPixieHunt = function() { if (gameRunning) return; gameRunning = true; score = 0; timeLeft = 35; scoreEl.innerText = score; timerEl.innerText = timeLeft; if (startOverlay) startOverlay.style.display = 'none'; if (successPanel) successPanel.style.display = 'none'; if (window.soundFX) window.soundFX.playWhoosh(); if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(30); // Start countdown timerInterval = setInterval(() => { timeLeft--; timerEl.innerText = timeLeft; if (timeLeft <= 0) { endGame(false); } }, 1000); scheduleNextSpawn(); }; function scheduleNextSpawn() { if (!gameRunning) return; const delay = Math.random() * 350 + 400; // 400ms - 750ms spawnTimeout = setTimeout(() => { spawnTarget(); scheduleNextSpawn(); }, delay); } function spawnTarget() { if (!gameRunning) return; // Find available holes const available = []; for (let i = 0; i < 9; i++) { if (!activeTargets.has(i)) available.push(i); } if (available.length === 0) return; const holeIdx = available[Math.floor(Math.random() * available.length)]; const targetEl = document.getElementById(`target${holeIdx}`); if (!targetEl) return; // 12% chance Neville, 88% chance Pixie const isNeville = Math.random() < 0.12; targetEl.className = 'pixie-target active'; targetEl.innerText = isNeville ? '👦' : '🧚'; if (isNeville) targetEl.classList.add('neville'); const stayTime = Math.random() * 450 + 900; // 900ms - 1350ms (forgiving for mobile) const hideTimer = setTimeout(() => { hideTarget(holeIdx); }, stayTime); activeTargets.set(holeIdx, { isNeville: isNeville, timer: hideTimer, clicked: false }); } function hideTarget(holeIdx) { const targetEl = document.getElementById(`target${holeIdx}`); if (targetEl) { targetEl.classList.remove('active'); targetEl.classList.remove('frozen'); targetEl.classList.remove('neville'); } activeTargets.delete(holeIdx); } // Pointerdown handler on holes (zero delay on touch devices!) holes.forEach(hole => { const idx = parseInt(hole.dataset.idx, 10); function onHoleTrigger(e) { if (!gameRunning) return; e.preventDefault(); if (!activeTargets.has(idx)) return; const targetData = activeTargets.get(idx); if (targetData.clicked) return; targetData.clicked = true; const targetEl = document.getElementById(`target${idx}`); if (targetData.isNeville) { // Clicked Neville -> Penalty! if (window.soundFX) window.soundFX.playWhoosh(); if (window.navigator && window.navigator.vibrate) window.navigator.vibrate([40, 40]); score = Math.max(0, score - 2); scoreEl.innerText = score; targetEl.style.transform = 'scale(1.25) rotate(15deg)'; if (window.showFloatingToast) { window.showFloatingToast("🤕 Neville: »Aua! Ich bin doch kein Wichtel!« (-2 Pkt)"); } setTimeout(() => hideTarget(idx), 350); } else { // Clicked Pixie -> Immobilus! clearTimeout(targetData.timer); targetEl.classList.add('frozen'); targetEl.innerText = '❄️'; if (window.soundFX) { window.soundFX.playKeyChime(); } if (window.navigator && window.navigator.vibrate) { window.navigator.vibrate(35); } score++; scoreEl.innerText = score; if (score >= targetScore) { endGame(true); } else { setTimeout(() => hideTarget(idx), 380); } } } // Use pointerdown for instant touch & mouse response without delay hole.addEventListener('pointerdown', onHoleTrigger); }); async function endGame(won) { gameRunning = false; clearInterval(timerInterval); clearTimeout(spawnTimeout); // Hide all active activeTargets.forEach((val, idx) => hideTarget(idx)); activeTargets.clear(); if (won) { if (window.soundFX) window.soundFX.playMagicSuccess(); if (window.navigator && window.navigator.vibrate) { window.navigator.vibrate([50, 100, 150]); } try { const res = await fetch('/api/pixies/complete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ caught: score }) }); const data = await res.json(); if (data.new_item && window.notifyNewItem) { window.notifyNewItem(data.new_item); } } catch (e) { console.error("Pixies complete error:", e); } if (successPanel) successPanel.style.display = 'block'; } else { if (window.soundFX) window.soundFX.playWhoosh(); if (startOverlay) { startOverlay.style.display = 'flex'; if (startBtn) startBtn.innerText = "Erneut versuchen 🔄"; } if (window.showFloatingToast) { window.showFloatingToast("⏰ Die Zeit ist abgelaufen! Versuch es noch einmal."); } } } })();