Mobile First Overhaul: Touch gestures, dual tap/drag, instant responsiveness and ergonomic controls across all mini games
This commit is contained in:
+44
-75
@@ -1,4 +1,4 @@
|
||||
// Zaubertrank-Brauküche (Felix Felicis Potion Brewing Engine)
|
||||
// Zaubertrank-Brauküche (Felix Felicis Potion Brewing Engine - Mobile First)
|
||||
(function() {
|
||||
let currentStep = 1; // 1: Ingredients, 2: Stirring, 3: Heat, 4: Done
|
||||
const ingredients = {
|
||||
@@ -19,9 +19,9 @@
|
||||
const ingredientsShelf = document.getElementById('ingredientsShelf');
|
||||
const potionSurface = document.getElementById('potionSurface');
|
||||
const stirCircle = document.getElementById('stirCircle');
|
||||
const stirTracker = document.getElementById('stirTracker');
|
||||
const stirCounterText = document.getElementById('stirCounterText');
|
||||
const stirPanel = document.getElementById('stirPanel');
|
||||
const stirCountSpan = document.getElementById('stirCount');
|
||||
const stirBtn = document.getElementById('stirBtn');
|
||||
const heatPanel = document.getElementById('heatPanel');
|
||||
const tempIndicator = document.getElementById('tempIndicator');
|
||||
const heatTimerSpan = document.getElementById('heatTimer');
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
ingredients[type]++;
|
||||
if (window.soundFX) window.soundFX.playBottleClink();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(25);
|
||||
|
||||
// Update UI Badge
|
||||
if (type === 'dragon') {
|
||||
@@ -49,105 +50,65 @@
|
||||
}
|
||||
|
||||
// Ripple effect on surface
|
||||
potionSurface.style.transform = 'scale(1.08)';
|
||||
potionSurface.style.transform = 'scale(1.12)';
|
||||
setTimeout(() => potionSurface.style.transform = 'scale(1)', 200);
|
||||
|
||||
// Check if all ingredients added
|
||||
const total = ingredients.dragon + ingredients.mandrake + ingredients.star;
|
||||
if (total === 4) {
|
||||
setTimeout(startStep2, 600);
|
||||
setTimeout(startStep2, 500);
|
||||
}
|
||||
};
|
||||
|
||||
// Step 2: Stirring
|
||||
let completedStirs = 0;
|
||||
let lastAngle = null;
|
||||
let accumulatedAngle = 0;
|
||||
|
||||
function startStep2() {
|
||||
currentStep = 2;
|
||||
stepBadge.innerText = "Schritt 2: Gleichmäßig rühren";
|
||||
stepInstruction.innerText = "Rühre den Sud 3 Runden im Uhrzeigersinn!";
|
||||
stepSubtext.innerText = "Führe deinen Finger oder die Maus im Kreis über dem Kessel oder tippe ihn an.";
|
||||
stepInstruction.innerText = "Rühre den Kessel 3-mal um!";
|
||||
stepSubtext.innerText = "Tippe auf den großen Rühr-Button oder wische über den Kessel.";
|
||||
ingredientsShelf.style.display = 'none';
|
||||
stirCounterText.style.display = 'block';
|
||||
stirPanel.style.display = 'flex';
|
||||
stirCircle.classList.add('active');
|
||||
potionSurface.className = 'potion-surface state-stir';
|
||||
|
||||
if (window.soundFX) window.soundFX.playStoneSlide();
|
||||
}
|
||||
|
||||
// Touch / Pointer stirring logic
|
||||
let isStirring = false;
|
||||
|
||||
function getAngle(e) {
|
||||
const rect = cauldronZone.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
||||
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
|
||||
return Math.atan2(clientY - centerY, clientX - centerX) * (180 / Math.PI);
|
||||
}
|
||||
|
||||
function onStirStart(e) {
|
||||
window.doStir = function() {
|
||||
if (currentStep !== 2) return;
|
||||
isStirring = true;
|
||||
lastAngle = getAngle(e);
|
||||
}
|
||||
|
||||
function onStirMove(e) {
|
||||
if (currentStep !== 2 || !isStirring) return;
|
||||
e.preventDefault();
|
||||
const angle = getAngle(e);
|
||||
if (lastAngle !== null) {
|
||||
let diff = angle - lastAngle;
|
||||
// Handle wrap-around
|
||||
if (diff < -180) diff += 360;
|
||||
if (diff > 180) diff -= 360;
|
||||
|
||||
// Only count clockwise
|
||||
if (diff > 0) {
|
||||
accumulatedAngle += diff;
|
||||
if (accumulatedAngle >= 360) {
|
||||
accumulatedAngle -= 360;
|
||||
incrementStir();
|
||||
}
|
||||
}
|
||||
}
|
||||
lastAngle = angle;
|
||||
}
|
||||
|
||||
function onStirEnd() {
|
||||
isStirring = false;
|
||||
lastAngle = null;
|
||||
}
|
||||
|
||||
function incrementStir() {
|
||||
completedStirs++;
|
||||
stirCountSpan.innerText = completedStirs;
|
||||
if (stirBtn) stirBtn.innerHTML = `🥄 Kessel umrühren! (<span>${completedStirs}</span> / 3)`;
|
||||
|
||||
if (window.soundFX) window.soundFX.playBottleClink();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(35);
|
||||
|
||||
// Spin animation on surface
|
||||
potionSurface.style.transform = 'rotate(' + (completedStirs * 180) + 'deg) scale(1.1)';
|
||||
setTimeout(() => potionSurface.style.transform = 'rotate(' + (completedStirs * 180) + 'deg) scale(1)', 250);
|
||||
|
||||
if (completedStirs >= 3) {
|
||||
stirCircle.classList.remove('active');
|
||||
stirCounterText.style.display = 'none';
|
||||
setTimeout(startStep3, 600);
|
||||
stirPanel.style.display = 'none';
|
||||
setTimeout(startStep3, 500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Direct tap fallback for mobile
|
||||
cauldronZone.addEventListener('click', () => {
|
||||
if (currentStep === 2 && !isStirring) {
|
||||
incrementStir();
|
||||
// Cauldron tap / swipe to stir as well
|
||||
let touchStartX = 0;
|
||||
cauldronZone.addEventListener('pointerdown', (e) => {
|
||||
if (currentStep === 2) {
|
||||
touchStartX = e.clientX;
|
||||
}
|
||||
});
|
||||
|
||||
cauldronZone.addEventListener('mousedown', onStirStart);
|
||||
window.addEventListener('mousemove', onStirMove);
|
||||
window.addEventListener('mouseup', onStirEnd);
|
||||
|
||||
cauldronZone.addEventListener('touchstart', onStirStart, { passive: false });
|
||||
window.addEventListener('touchmove', onStirMove, { passive: false });
|
||||
window.addEventListener('touchend', onStirEnd);
|
||||
cauldronZone.addEventListener('pointerup', (e) => {
|
||||
if (currentStep === 2) {
|
||||
window.doStir();
|
||||
}
|
||||
});
|
||||
|
||||
// Step 3: Heat Control
|
||||
let currentTemp = 35; // percent (0-100)
|
||||
@@ -158,7 +119,7 @@
|
||||
currentStep = 3;
|
||||
stepBadge.innerText = "Schritt 3: Hitze balancieren";
|
||||
stepInstruction.innerText = "Pumpe mit dem Blasebalg, um die Hitze im grünen Bereich zu halten!";
|
||||
stepSubtext.innerText = "Halte die Nadel für 4 Sekunden im grünen Bereich (60% - 85%).";
|
||||
stepSubtext.innerText = "Halte die Anzeige für 3 Sekunden im grünen Idealbereich (50% - 85%).";
|
||||
potionSurface.className = 'potion-surface state-heat';
|
||||
heatPanel.classList.add('active');
|
||||
|
||||
@@ -169,19 +130,26 @@
|
||||
|
||||
window.pumpHeat = function() {
|
||||
if (currentStep !== 3) return;
|
||||
currentTemp = Math.min(100, currentTemp + 15);
|
||||
currentTemp = Math.min(100, currentTemp + 18);
|
||||
if (window.soundFX) window.soundFX.playWhoosh();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(20);
|
||||
|
||||
const bellows = document.getElementById('bellowsBtn');
|
||||
if (bellows) {
|
||||
bellows.style.transform = 'scale(0.92)';
|
||||
setTimeout(() => bellows.style.transform = 'scale(1)', 100);
|
||||
}
|
||||
};
|
||||
|
||||
function updateHeatLoop() {
|
||||
if (currentStep !== 3) return;
|
||||
|
||||
// Slow cooling down
|
||||
currentTemp = Math.max(0, currentTemp - 0.55);
|
||||
currentTemp = Math.max(0, currentTemp - 0.45);
|
||||
tempIndicator.style.width = currentTemp + '%';
|
||||
|
||||
// Check ideal zone (60% to 85%)
|
||||
if (currentTemp >= 60 && currentTemp <= 85) {
|
||||
// Check ideal zone (50% to 85%) - forgiving on mobile!
|
||||
if (currentTemp >= 50 && currentTemp <= 85) {
|
||||
heatTimer += 0.05;
|
||||
heatTimerSpan.innerText = heatTimer.toFixed(1);
|
||||
tempIndicator.style.background = '#2ecc71';
|
||||
@@ -189,7 +157,7 @@
|
||||
tempIndicator.style.background = 'linear-gradient(90deg, #3498db, #f39c12, #e74c3c)';
|
||||
}
|
||||
|
||||
if (heatTimer >= 4.0) {
|
||||
if (heatTimer >= 3.0) {
|
||||
clearInterval(heatInterval);
|
||||
finishPotionBrew();
|
||||
}
|
||||
@@ -210,6 +178,7 @@
|
||||
potionSurface.className = 'potion-surface state-gold';
|
||||
|
||||
if (window.soundFX) window.soundFX.playMagicSuccess();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate([50, 100, 150]);
|
||||
|
||||
// API Completion Call
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user