Mobile First Overhaul: Touch gestures, dual tap/drag, instant responsiveness and ergonomic controls across all mini games
This commit is contained in:
+28
-17
@@ -1,4 +1,4 @@
|
||||
// Wichtel-Panik / Cornish Pixies Arcade Engine
|
||||
// Wichtel-Panik / Cornish Pixies Arcade Engine (Mobile First)
|
||||
(function() {
|
||||
let score = 0;
|
||||
const targetScore = 10;
|
||||
@@ -10,12 +10,12 @@
|
||||
// DOM Elements
|
||||
const scoreEl = document.getElementById('pixieScore');
|
||||
const timerEl = document.getElementById('pixieTimer');
|
||||
const startBtn = document.getElementById('startPixieBtn');
|
||||
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 -> { type: 'pixie'|'neville', timer: id }
|
||||
const activeTargets = new Map(); // holeIndex -> { isNeville: bool, timer: id, clicked: bool }
|
||||
|
||||
window.startPixieHunt = function() {
|
||||
if (gameRunning) return;
|
||||
@@ -25,10 +25,11 @@
|
||||
|
||||
scoreEl.innerText = score;
|
||||
timerEl.innerText = timeLeft;
|
||||
startOverlay.style.display = 'none';
|
||||
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(() => {
|
||||
@@ -45,7 +46,7 @@
|
||||
|
||||
function scheduleNextSpawn() {
|
||||
if (!gameRunning) return;
|
||||
const delay = Math.random() * 400 + 400; // 400ms - 800ms
|
||||
const delay = Math.random() * 350 + 400; // 400ms - 750ms
|
||||
spawnTimeout = setTimeout(() => {
|
||||
spawnTarget();
|
||||
scheduleNextSpawn();
|
||||
@@ -66,13 +67,13 @@
|
||||
const targetEl = document.getElementById(`target${holeIdx}`);
|
||||
if (!targetEl) return;
|
||||
|
||||
// 15% chance Neville, 85% chance Pixie
|
||||
const isNeville = Math.random() < 0.15;
|
||||
// 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() * 400 + 850; // 850ms - 1250ms
|
||||
const stayTime = Math.random() * 450 + 900; // 900ms - 1350ms (forgiving for mobile)
|
||||
|
||||
const hideTimer = setTimeout(() => {
|
||||
hideTarget(holeIdx);
|
||||
@@ -95,11 +96,11 @@
|
||||
activeTargets.delete(holeIdx);
|
||||
}
|
||||
|
||||
// Touch / Click Handler on holes
|
||||
// Pointerdown handler on holes (zero delay on touch devices!)
|
||||
holes.forEach(hole => {
|
||||
const idx = parseInt(hole.dataset.idx, 10);
|
||||
|
||||
function handleTap(e) {
|
||||
function onHoleTrigger(e) {
|
||||
if (!gameRunning) return;
|
||||
e.preventDefault();
|
||||
|
||||
@@ -113,10 +114,12 @@
|
||||
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.3) rotate(15deg)';
|
||||
targetEl.style.transform = 'scale(1.25) rotate(15deg)';
|
||||
if (window.showFloatingToast) {
|
||||
window.showFloatingToast("🤕 <strong>Neville:</strong> »Aua! Ich bin doch kein Wichtel!« (-2 Pkt)");
|
||||
}
|
||||
@@ -132,6 +135,9 @@
|
||||
if (window.soundFX) {
|
||||
window.soundFX.playKeyChime();
|
||||
}
|
||||
if (window.navigator && window.navigator.vibrate) {
|
||||
window.navigator.vibrate(35);
|
||||
}
|
||||
|
||||
score++;
|
||||
scoreEl.innerText = score;
|
||||
@@ -139,13 +145,13 @@
|
||||
if (score >= targetScore) {
|
||||
endGame(true);
|
||||
} else {
|
||||
setTimeout(() => hideTarget(idx), 400);
|
||||
setTimeout(() => hideTarget(idx), 380);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hole.addEventListener('click', handleTap);
|
||||
hole.addEventListener('touchstart', handleTap, { passive: false });
|
||||
// Use pointerdown for instant touch & mouse response without delay
|
||||
hole.addEventListener('pointerdown', onHoleTrigger);
|
||||
});
|
||||
|
||||
async function endGame(won) {
|
||||
@@ -159,6 +165,9 @@
|
||||
|
||||
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', {
|
||||
@@ -178,10 +187,12 @@
|
||||
|
||||
} else {
|
||||
if (window.soundFX) window.soundFX.playWhoosh();
|
||||
startOverlay.style.display = 'block';
|
||||
startBtn.innerText = "Erneut versuchen 🔄";
|
||||
if (startOverlay) {
|
||||
startOverlay.style.display = 'flex';
|
||||
if (startBtn) startBtn.innerText = "Erneut versuchen 🔄";
|
||||
}
|
||||
if (window.showFloatingToast) {
|
||||
window.showFloatingToast("⏰ Die Zeit ist abgelaufen! Die Wichtel haben gewonnen. Versuch es noch einmal!");
|
||||
window.showFloatingToast("⏰ Die Zeit ist abgelaufen! Versuch es noch einmal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user