// Wichtel-Panik / Cornish Pixies Arcade Engine (Mobile First - Frenzy & Challenge Overhaul)
(function() {
let score = 0;
const targetScore = 12;
let timeLeft = 30;
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 = 30;
if (scoreEl) scoreEl.innerText = score;
if (timerEl) 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--;
if (timerEl) timerEl.innerText = timeLeft;
if (timeLeft <= 0) {
endGame(false);
}
}, 1000);
scheduleNextSpawn();
};
function scheduleNextSpawn() {
if (!gameRunning) return;
// Dynamic spawn delay based on progress
let minDelay = 380, maxDelay = 650;
if (score >= 8) {
minDelay = 220;
maxDelay = 420;
} else if (score >= 4) {
minDelay = 300;
maxDelay = 520;
}
const delay = Math.random() * (maxDelay - minDelay) + minDelay;
spawnTimeout = setTimeout(() => {
spawnTarget();
// Chance of double spawn when frenzy begins (score >= 4)
if (score >= 4 && Math.random() < 0.40) {
setTimeout(spawnTarget, 80);
}
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;
// 16% chance Neville, 84% chance Pixie
const isNeville = Math.random() < 0.16;
targetEl.className = 'pixie-target active';
targetEl.innerText = isNeville ? '👦' : '🧚';
if (isNeville) targetEl.classList.add('neville');
// Dynamic stay duration: faster as player scores higher
let stayTime = 800;
if (isNeville) {
stayTime = 950; // Neville stays a bit longer as a decoy
} else if (score >= 8) {
stayTime = Math.random() * 220 + 420; // 420ms - 640ms (Lightning fast frenzy!)
} else if (score >= 4) {
stayTime = Math.random() * 250 + 520; // 520ms - 770ms
} else {
stayTime = Math.random() * 280 + 660; // 660ms - 940ms
}
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');
targetEl.innerText = '';
}
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 (-2 Pkt and -1s)!
if (window.soundFX) window.soundFX.playWhoosh();
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate([60, 60]);
score = Math.max(0, score - 2);
timeLeft = Math.max(1, timeLeft - 1);
if (scoreEl) scoreEl.innerText = score;
if (timerEl) timerEl.innerText = timeLeft;
targetEl.style.transform = 'scale(1.25) rotate(15deg)';
if (window.showFloatingToast) {
window.showFloatingToast("🤕 Neville: »Aua! Ich bin doch kein Wichtel!« (-2 Pkt, -1s)");
}
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++;
if (scoreEl) scoreEl.innerText = score;
if (score >= targetScore) {
endGame(true);
} else {
setTimeout(() => hideTarget(idx), 280);
}
}
}
// 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) {
let newItem = null;
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) newItem = data.new_item;
} catch (e) {
console.error("Pixies complete error:", e);
}
if (!newItem) {
newItem = {
id: "pixie_jar",
name: "Wichtelglas",
icon: "🧚",
desc: "Ein dickbauchiges Glas mit einem gebannten Wichtel."
};
}
if (successPanel) successPanel.style.display = 'block';
if (window.showChamberVictory) {
window.showChamberVictory({
chamberBadge: "Kammer V: Wichtel-Panik",
title: "🧚 Wichtel Gebannt!",
statsHtml: `❄️ Gebannt: ${score}/${targetScore} Wichtel👦 Neville: Gerettet⏱️ Restzeit: ${timeLeft}s`,
item: newItem,
storyText: "Mit blitzschnellen Immobilus!-Zaubern hast du alle 12 frechen Wichtel eingefroren! Ein zappelnder Wichtel landet sicher im Wichtelglas. Neville bedankt sich erleichtert. Der Weg zur berüchtigten Flammenkammer von Severus Snape liegt nun offen!",
nextUrl: "/bottles",
nextBtnText: "➔ Weiter zu Kammer VI: Snapes Flaschen 🧪"
});
}
} 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! Die Wichtel entwischten. Versuch es noch einmal!");
}
}
}
})();