Polish: Fix pixie visibility bug, add flight ready countdown, SVG cauldron redesign, and upgrade riddles
This commit is contained in:
Binary file not shown.
+88
-114
@@ -1224,118 +1224,67 @@ body {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.cauldron-wrapper {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.cauldron-pot {
|
||||
width: 250px;
|
||||
height: 230px;
|
||||
background: radial-gradient(circle at 35% 30%, #4a4a55 0%, #202025 50%, #0d0d12 100%);
|
||||
border-radius: 50% 50% 45% 45% / 30% 30% 60% 60%;
|
||||
border: 6px solid #1a1a20;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.9), inset 0 -10px 20px rgba(0, 0, 0, 0.8);
|
||||
.cauldron-visual-container {
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
margin: 0.5rem auto;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.cauldron-pot::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
width: 210px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: #111116;
|
||||
border: 4px solid #2d2d38;
|
||||
}
|
||||
|
||||
.cauldron-pot::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
width: 120px;
|
||||
height: 18px;
|
||||
background: #000;
|
||||
border-radius: 50%;
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
.potion-surface {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
width: 190px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(ellipse at center, #2ecc71 0%, #16a085 60%, #0e5042 100%);
|
||||
box-shadow: 0 0 20px #2ecc71;
|
||||
overflow: hidden;
|
||||
transition: background 1s ease, box-shadow 1s ease;
|
||||
}
|
||||
|
||||
.potion-surface.state-stir {
|
||||
background: radial-gradient(ellipse at center, #9b59b6 0%, #8e44ad 60%, #4a235a 100%);
|
||||
box-shadow: 0 0 25px #9b59b6;
|
||||
}
|
||||
|
||||
.potion-surface.state-heat {
|
||||
background: radial-gradient(ellipse at center, #e67e22 0%, #d35400 60%, #78281f 100%);
|
||||
box-shadow: 0 0 25px #e67e22;
|
||||
}
|
||||
|
||||
.potion-surface.state-gold {
|
||||
background: radial-gradient(ellipse at center, #fff6a5 0%, #f1c40f 50%, #b7950b 100%);
|
||||
box-shadow: 0 0 35px #f1c40f, 0 0 60px rgba(241, 196, 15, 0.6);
|
||||
animation: goldShimmer 1.5s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes goldShimmer {
|
||||
0% { filter: brightness(1) drop-shadow(0 0 10px #f1c40f); }
|
||||
100% { filter: brightness(1.3) drop-shadow(0 0 30px #fff); }
|
||||
}
|
||||
|
||||
.cauldron-fire {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
font-size: 2.2rem;
|
||||
animation: flameFlicker 0.8s infinite alternate;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Stir Interactive Ring */
|
||||
.stir-circle {
|
||||
position: absolute;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
border-radius: 50%;
|
||||
border: 3px dashed var(--gold);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: auto;
|
||||
cursor: grab;
|
||||
animation: rotateDashed 6s linear infinite;
|
||||
}
|
||||
|
||||
.stir-circle.active {
|
||||
display: flex;
|
||||
.cauldron-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: 230px;
|
||||
filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.85));
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.stir-arrow-hint {
|
||||
font-size: 1.8rem;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 35px;
|
||||
.bubble-anim {
|
||||
animation: bubbleRise 1.6s infinite ease-in;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.b1 { animation-delay: 0.1s; }
|
||||
.b2 { animation-delay: 0.5s; }
|
||||
.b3 { animation-delay: 0.9s; }
|
||||
.b4 { animation-delay: 1.2s; }
|
||||
.b5 { animation-delay: 0.3s; }
|
||||
|
||||
@keyframes bubbleRise {
|
||||
0% { transform: translateY(5px) scale(0.6); opacity: 0; }
|
||||
50% { opacity: 0.8; transform: translateY(-3px) scale(1.1); }
|
||||
100% { transform: translateY(-12px) scale(0.3); opacity: 0; }
|
||||
}
|
||||
|
||||
.animated-flames {
|
||||
animation: flamePulse 1.2s infinite alternate ease-in-out;
|
||||
transform-origin: center 260px;
|
||||
}
|
||||
|
||||
@keyframes flamePulse {
|
||||
0% { transform: scaleY(0.9) scaleX(0.95); opacity: 0.85; filter: drop-shadow(0 0 6px #e74c3c); }
|
||||
100% { transform: scaleY(1.1) scaleX(1.05); opacity: 1; filter: drop-shadow(0 0 16px #f39c12); }
|
||||
}
|
||||
|
||||
@keyframes stirSpoonAnim {
|
||||
0% { transform: rotate(-8deg) translateX(-10px); }
|
||||
50% { transform: rotate(8deg) translateX(10px); }
|
||||
100% { transform: rotate(-8deg) translateX(-10px); }
|
||||
}
|
||||
|
||||
.stirring-active {
|
||||
animation: stirSpoonAnim 0.6s infinite ease-in-out;
|
||||
transform-origin: 160px 40px;
|
||||
}
|
||||
|
||||
@keyframes ingredientDropAnim {
|
||||
0% { transform: translateY(-70px) scale(0.4); opacity: 0; }
|
||||
60% { transform: translateY(10px) scale(1.2); opacity: 1; }
|
||||
100% { transform: translateY(20px) scale(0.2); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes rotateDashed {
|
||||
@@ -1650,28 +1599,35 @@ body {
|
||||
}
|
||||
|
||||
.pixie-target {
|
||||
display: none; /* Crucial bugfix: hidden until active! */
|
||||
font-size: clamp(2.4rem, 8vw, 3.4rem);
|
||||
position: absolute;
|
||||
transform: translateY(120%);
|
||||
transition: transform 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||||
pointer-events: none; /* Let hole handle tap directly! */
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.8));
|
||||
animation: pixieFloat 0.8s infinite alternate ease-in-out;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pixie-target.active {
|
||||
transform: translateY(0);
|
||||
display: block;
|
||||
animation: pixiePopIn 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28), pixieFloat 0.8s infinite 0.2s alternate ease-in-out;
|
||||
}
|
||||
|
||||
.pixie-target.frozen {
|
||||
filter: drop-shadow(0 0 12px #3498db) hue-rotate(180deg) brightness(1.2);
|
||||
display: block;
|
||||
filter: drop-shadow(0 0 16px #3498db) hue-rotate(180deg) brightness(1.3);
|
||||
animation: none;
|
||||
transform: translateY(0) scale(1.08);
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.pixie-target.neville {
|
||||
display: block;
|
||||
font-size: clamp(2.6rem, 9vw, 3.6rem);
|
||||
animation: shakeNeville 0.4s infinite alternate;
|
||||
animation: shakeNeville 0.35s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes pixiePopIn {
|
||||
0% { transform: translateY(60px) scale(0.3); opacity: 0; }
|
||||
100% { transform: translateY(0) scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes pixieFloat {
|
||||
@@ -1680,8 +1636,26 @@ body {
|
||||
}
|
||||
|
||||
@keyframes shakeNeville {
|
||||
0% { transform: translateY(0) rotate(-5deg); }
|
||||
100% { transform: translateY(0) rotate(5deg); }
|
||||
0% { transform: translateY(0) rotate(-6deg); }
|
||||
100% { transform: translateY(0) rotate(6deg); }
|
||||
}
|
||||
|
||||
/* Flight Start Overlay */
|
||||
.flight-start-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(12, 10, 20, 0.9);
|
||||
border: 2px solid var(--gold);
|
||||
border-radius: 14px;
|
||||
z-index: 25;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
text-align: center;
|
||||
backdrop-filter: blur(4px);
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
/* Mobile responsive adjustments */
|
||||
|
||||
+86
-52
@@ -1,4 +1,4 @@
|
||||
// Zaubertrank-Brauküche (Felix Felicis Potion Brewing Engine - Mobile First)
|
||||
// Zaubertrank-Brauküche (Felix Felicis Potion Brewing Engine - SVG Overhaul)
|
||||
(function() {
|
||||
let currentStep = 1; // 1: Ingredients, 2: Stirring, 3: Heat, 4: Done
|
||||
const ingredients = {
|
||||
@@ -16,9 +16,18 @@
|
||||
const stepBadge = document.getElementById('stepBadge');
|
||||
const stepInstruction = document.getElementById('stepInstruction');
|
||||
const stepSubtext = document.getElementById('stepSubtext');
|
||||
const ingredientsShelf = document.getElementById('ingredientsShelf');
|
||||
const potionSurface = document.getElementById('potionSurface');
|
||||
const stirCircle = document.getElementById('stirCircle');
|
||||
const step1Wrapper = document.getElementById('step1Wrapper');
|
||||
const totalIngredientsCount = document.getElementById('totalIngredientsCount');
|
||||
|
||||
// SVG Elements
|
||||
const gradStop1 = document.getElementById('gradStop1');
|
||||
const gradStop2 = document.getElementById('gradStop2');
|
||||
const gradStop3 = document.getElementById('gradStop3');
|
||||
const liquidSurface = document.getElementById('liquidSurface');
|
||||
const stirSpoon = document.getElementById('stirSpoon');
|
||||
const dropItemAnim = document.getElementById('dropItemAnim');
|
||||
|
||||
// Controls
|
||||
const stirPanel = document.getElementById('stirPanel');
|
||||
const stirCountSpan = document.getElementById('stirCount');
|
||||
const stirBtn = document.getElementById('stirBtn');
|
||||
@@ -26,7 +35,6 @@
|
||||
const tempIndicator = document.getElementById('tempIndicator');
|
||||
const heatTimerSpan = document.getElementById('heatTimer');
|
||||
const successPanel = document.getElementById('successPanel');
|
||||
const cauldronZone = document.getElementById('cauldronZone');
|
||||
|
||||
// Step 1: Add Ingredient
|
||||
window.addIngredient = function(type) {
|
||||
@@ -35,7 +43,7 @@
|
||||
|
||||
ingredients[type]++;
|
||||
if (window.soundFX) window.soundFX.playBottleClink();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(25);
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(30);
|
||||
|
||||
// Update UI Badge
|
||||
if (type === 'dragon') {
|
||||
@@ -49,14 +57,29 @@
|
||||
if (ingredients.star >= targetIngredients.star) document.getElementById('ingStar').classList.add('done');
|
||||
}
|
||||
|
||||
// Ripple effect on surface
|
||||
potionSurface.style.transform = 'scale(1.12)';
|
||||
setTimeout(() => potionSurface.style.transform = 'scale(1)', 200);
|
||||
const total = ingredients.dragon + ingredients.mandrake + ingredients.star;
|
||||
if (totalIngredientsCount) totalIngredientsCount.innerText = total;
|
||||
|
||||
// Visual drop animation in SVG
|
||||
if (dropItemAnim) {
|
||||
const icons = { dragon: '🐉', mandrake: '🌿', star: '✨' };
|
||||
dropItemAnim.textContent = icons[type];
|
||||
dropItemAnim.style.display = 'block';
|
||||
dropItemAnim.style.animation = 'ingredientDropAnim 0.5s ease-out forwards';
|
||||
setTimeout(() => {
|
||||
dropItemAnim.style.display = 'none';
|
||||
}, 550);
|
||||
}
|
||||
|
||||
// Ripple pulse on liquid
|
||||
if (liquidSurface) {
|
||||
liquidSurface.style.transform = 'scale(1.06)';
|
||||
setTimeout(() => liquidSurface.style.transform = 'scale(1)', 200);
|
||||
}
|
||||
|
||||
// Check if all ingredients added
|
||||
const total = ingredients.dragon + ingredients.mandrake + ingredients.star;
|
||||
if (total === 4) {
|
||||
setTimeout(startStep2, 500);
|
||||
setTimeout(startStep2, 600);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,11 +90,19 @@
|
||||
currentStep = 2;
|
||||
stepBadge.innerText = "Schritt 2: Gleichmäßig rühren";
|
||||
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';
|
||||
stirPanel.style.display = 'flex';
|
||||
stirCircle.classList.add('active');
|
||||
potionSurface.className = 'potion-surface state-stir';
|
||||
stepSubtext.innerText = "Tippe auf den großen Rühr-Button, um die Essenzen zu vermengen.";
|
||||
if (step1Wrapper) step1Wrapper.style.display = 'none';
|
||||
if (stirPanel) stirPanel.style.display = 'flex';
|
||||
|
||||
// Show wooden spoon in cauldron
|
||||
if (stirSpoon) stirSpoon.style.display = 'block';
|
||||
|
||||
// Shift liquid color to mystical purple
|
||||
if (gradStop1 && gradStop2 && gradStop3) {
|
||||
gradStop1.setAttribute('stop-color', '#9b59b6');
|
||||
gradStop2.setAttribute('stop-color', '#8e44ad');
|
||||
gradStop3.setAttribute('stop-color', '#341f47');
|
||||
}
|
||||
|
||||
if (window.soundFX) window.soundFX.playStoneSlide();
|
||||
}
|
||||
@@ -79,37 +110,28 @@
|
||||
window.doStir = function() {
|
||||
if (currentStep !== 2) return;
|
||||
completedStirs++;
|
||||
stirCountSpan.innerText = completedStirs;
|
||||
if (stirCountSpan) 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);
|
||||
// Animate stirring spoon
|
||||
if (stirSpoon) {
|
||||
stirSpoon.classList.remove('stirring-active');
|
||||
void stirSpoon.offsetWidth; // re-flow
|
||||
stirSpoon.classList.add('stirring-active');
|
||||
}
|
||||
|
||||
if (completedStirs >= 3) {
|
||||
stirCircle.classList.remove('active');
|
||||
stirPanel.style.display = 'none';
|
||||
setTimeout(startStep3, 500);
|
||||
setTimeout(() => {
|
||||
if (stirSpoon) stirSpoon.style.display = 'none';
|
||||
if (stirPanel) stirPanel.style.display = 'none';
|
||||
startStep3();
|
||||
}, 600);
|
||||
}
|
||||
};
|
||||
|
||||
// Cauldron tap / swipe to stir as well
|
||||
let touchStartX = 0;
|
||||
cauldronZone.addEventListener('pointerdown', (e) => {
|
||||
if (currentStep === 2) {
|
||||
touchStartX = e.clientX;
|
||||
}
|
||||
});
|
||||
|
||||
cauldronZone.addEventListener('pointerup', (e) => {
|
||||
if (currentStep === 2) {
|
||||
window.doStir();
|
||||
}
|
||||
});
|
||||
|
||||
// Step 3: Heat Control
|
||||
let currentTemp = 35; // percent (0-100)
|
||||
let heatTimer = 0.0;
|
||||
@@ -118,10 +140,16 @@
|
||||
function startStep3() {
|
||||
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 Anzeige für 3 Sekunden im grünen Idealbereich (50% - 85%).";
|
||||
potionSurface.className = 'potion-surface state-heat';
|
||||
heatPanel.classList.add('active');
|
||||
stepInstruction.innerText = "Pumpe mit dem Blasebalg, um die Flammen zu nähren!";
|
||||
stepSubtext.innerText = "Halte die Anzeige für 3 Sekunden im grünen Idealbereich (45% - 85%).";
|
||||
if (heatPanel) heatPanel.classList.add('active');
|
||||
|
||||
// Shift liquid color to fiery orange
|
||||
if (gradStop1 && gradStop2 && gradStop3) {
|
||||
gradStop1.setAttribute('stop-color', '#e67e22');
|
||||
gradStop2.setAttribute('stop-color', '#d35400');
|
||||
gradStop3.setAttribute('stop-color', '#561d00');
|
||||
}
|
||||
|
||||
if (window.soundFX) window.soundFX.playWhoosh();
|
||||
|
||||
@@ -144,17 +172,17 @@
|
||||
function updateHeatLoop() {
|
||||
if (currentStep !== 3) return;
|
||||
|
||||
// Slow cooling down
|
||||
// Gentle cooling down
|
||||
currentTemp = Math.max(0, currentTemp - 0.45);
|
||||
tempIndicator.style.width = currentTemp + '%';
|
||||
if (tempIndicator) tempIndicator.style.width = currentTemp + '%';
|
||||
|
||||
// Check ideal zone (50% to 85%) - forgiving on mobile!
|
||||
if (currentTemp >= 50 && currentTemp <= 85) {
|
||||
// Check ideal zone (45% to 85%) - forgiving on mobile!
|
||||
if (currentTemp >= 45 && currentTemp <= 85) {
|
||||
heatTimer += 0.05;
|
||||
heatTimerSpan.innerText = heatTimer.toFixed(1);
|
||||
tempIndicator.style.background = '#2ecc71';
|
||||
if (heatTimerSpan) heatTimerSpan.innerText = heatTimer.toFixed(1);
|
||||
if (tempIndicator) tempIndicator.style.background = '#2ecc71';
|
||||
} else {
|
||||
tempIndicator.style.background = 'linear-gradient(90deg, #3498db, #f39c12, #e74c3c)';
|
||||
if (tempIndicator) tempIndicator.style.background = 'linear-gradient(90deg, #3498db, #f39c12, #e74c3c)';
|
||||
}
|
||||
|
||||
if (heatTimer >= 3.0) {
|
||||
@@ -172,10 +200,16 @@
|
||||
stepBadge.style.color = "#2ecc71";
|
||||
|
||||
stepInstruction.innerText = "Felix Felicis strahlt in reinem Gold!";
|
||||
stepSubtext.innerText = "Dein Zaubertrank ist perfekt gelungen.";
|
||||
stepSubtext.innerText = "Dein Zaubertrank ist meisterhaft gelungen.";
|
||||
|
||||
heatPanel.classList.remove('active');
|
||||
potionSurface.className = 'potion-surface state-gold';
|
||||
if (heatPanel) heatPanel.classList.remove('active');
|
||||
|
||||
// Turn liquid into sparkling golden elixir
|
||||
if (gradStop1 && gradStop2 && gradStop3) {
|
||||
gradStop1.setAttribute('stop-color', '#fff6a5');
|
||||
gradStop2.setAttribute('stop-color', '#ffd700');
|
||||
gradStop3.setAttribute('stop-color', '#b7950b');
|
||||
}
|
||||
|
||||
if (window.soundFX) window.soundFX.playMagicSuccess();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate([50, 100, 150]);
|
||||
@@ -191,6 +225,6 @@
|
||||
console.error("Brew complete error:", e);
|
||||
}
|
||||
|
||||
successPanel.style.display = 'block';
|
||||
if (successPanel) successPanel.style.display = 'block';
|
||||
}
|
||||
})();
|
||||
|
||||
+31
-7
@@ -27,12 +27,36 @@
|
||||
|
||||
// Game Configuration
|
||||
const TOTAL_RINGS_NEEDED = 12;
|
||||
let gameRunning = true;
|
||||
let gameRunning = false; // Does not start automatically!
|
||||
let startTime = Date.now();
|
||||
let score = 0;
|
||||
let ringsCollected = 0;
|
||||
let combo = 0;
|
||||
|
||||
window.startFlight = function() {
|
||||
const overlay = document.getElementById('broomStartOverlay');
|
||||
if (overlay) overlay.style.display = 'none';
|
||||
|
||||
rings.length = 0;
|
||||
rings.push({ x: width + 60, y: height * 0.4, radius: 25, speed: 3.5, golden: false, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
rings.push({ x: width + 290, y: height * 0.65, radius: 25, speed: 3.6, golden: true, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
rings.push({ x: width + 530, y: height * 0.3, radius: 25, speed: 3.7, golden: false, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
|
||||
score = 0;
|
||||
ringsCollected = 0;
|
||||
combo = 0;
|
||||
if (scoreVal) scoreVal.innerText = score;
|
||||
if (ringsVal) ringsVal.innerText = `0/${TOTAL_RINGS_NEEDED}`;
|
||||
if (comboVal) comboVal.innerText = '';
|
||||
|
||||
startTime = Date.now();
|
||||
lastRingSpawn = Date.now();
|
||||
gameRunning = true;
|
||||
|
||||
if (window.soundFX) window.soundFX.playWhoosh();
|
||||
if (window.navigator && window.navigator.vibrate) window.navigator.vibrate(40);
|
||||
};
|
||||
|
||||
// Player (Broomstick)
|
||||
const player = {
|
||||
x: 90,
|
||||
@@ -206,13 +230,13 @@
|
||||
btnBoost.addEventListener('pointerleave', endBoost);
|
||||
}
|
||||
|
||||
// Pre-spawn 3 rings
|
||||
rings.push({ x: width + 50, y: height * 0.4, radius: 25, speed: 3.5, golden: false, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
rings.push({ x: width + 280, y: height * 0.65, radius: 25, speed: 3.6, golden: true, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
rings.push({ x: width + 520, y: height * 0.3, radius: 25, speed: 3.7, golden: false, collected: false, scale: 1, alpha: 1, pulse: 0 });
|
||||
|
||||
function update() {
|
||||
if (!gameRunning) return;
|
||||
if (!gameRunning) {
|
||||
// Idle gentle float before start
|
||||
player.y = (height * 0.5) + Math.sin(Date.now() * 0.003) * 8;
|
||||
player.angle = Math.sin(Date.now() * 0.003) * 0.04;
|
||||
return;
|
||||
}
|
||||
|
||||
const elapsedSec = Math.floor((Date.now() - startTime) / 1000);
|
||||
if (timerVal) timerVal.innerText = elapsedSec + 's';
|
||||
|
||||
@@ -69,6 +69,21 @@
|
||||
{ x: 0.34, y: 0.52 },
|
||||
{ x: 0.34, y: 0.80 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'expecto_patronum',
|
||||
name: 'Expecto Patronum 🦌',
|
||||
incantation: '"Expecto Patronum!" (Meister-Schutzzauber)',
|
||||
desc: 'Verbinde die 5 Sterne des Lichts im Pentagramm (1 ➔ 2 ➔ 3 ➔ 4 ➔ 5), um die Rüstung endgültig zu bannen!',
|
||||
color: '#ffffff',
|
||||
beamColor: '#bbf2f6',
|
||||
points: [
|
||||
{ x: 0.35, y: 0.18 },
|
||||
{ x: 0.50, y: 0.65 },
|
||||
{ x: 0.18, y: 0.35 },
|
||||
{ x: 0.52, y: 0.35 },
|
||||
{ x: 0.20, y: 0.65 }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
targetEl.classList.remove('active');
|
||||
targetEl.classList.remove('frozen');
|
||||
targetEl.classList.remove('neville');
|
||||
targetEl.innerText = '';
|
||||
}
|
||||
activeTargets.delete(holeIdx);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;700&family=MedievalSharp&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/css/escape.css?v=3.0">
|
||||
<link rel="stylesheet" href="/static/css/escape.css?v=4.0">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+103
-34
@@ -6,8 +6,8 @@
|
||||
<div class="parchment-scroll">
|
||||
<h1 class="parchment-title">⚗️ Kammer III: Die Zaubertrank-Brauküche ⚗️</h1>
|
||||
<p class="riddle-poem" style="font-size: 1.15rem; margin: 0.3rem 0;">
|
||||
"Snapes Kupferkessel dampft im Halbdunkel des Kerkers.<br>
|
||||
Braue das flüssige Gold des Glücks: <strong>Felix Felicis</strong>!"
|
||||
"Snapes massiver Kupferkessel dampft im Halbdunkel des Kerkers.<br>
|
||||
Um das Tor zu öffnen, musst du das flüssige Gold des Glücks brauen: <strong>Felix Felicis</strong>!"
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -23,44 +23,113 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- The Cauldron Stage -->
|
||||
<div class="cauldron-wrapper" id="cauldronZone">
|
||||
<!-- Interactive Stir Ring Overlay -->
|
||||
<div id="stirCircle" class="stir-circle">
|
||||
<span class="stir-arrow-hint">🥄</span>
|
||||
</div>
|
||||
<!-- The SVG Cauldron Stage -->
|
||||
<div class="cauldron-visual-container" id="cauldronZone">
|
||||
<svg id="cauldronSvg" viewBox="0 0 320 280" class="cauldron-svg">
|
||||
<defs>
|
||||
<!-- Liquid Gradient (dynamic color) -->
|
||||
<radialGradient id="liquidGrad" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#2ecc71" id="gradStop1" />
|
||||
<stop offset="65%" stop-color="#16a085" id="gradStop2" />
|
||||
<stop offset="100%" stop-color="#0b3b30" id="gradStop3" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Cauldron Iron Body -->
|
||||
<radialGradient id="cauldronBodyGrad" cx="35%" cy="30%" r="70%">
|
||||
<stop offset="0%" stop-color="#4a4d52" />
|
||||
<stop offset="45%" stop-color="#242629" />
|
||||
<stop offset="100%" stop-color="#111214" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Rim Gradient -->
|
||||
<linearGradient id="rimGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="#60646c" />
|
||||
<stop offset="50%" stop-color="#303236" />
|
||||
<stop offset="100%" stop-color="#151618" />
|
||||
</linearGradient>
|
||||
|
||||
<div class="cauldron-pot">
|
||||
<div id="potionSurface" class="potion-surface"></div>
|
||||
</div>
|
||||
<div class="cauldron-fire">🔥</div>
|
||||
<!-- Fire Gradient -->
|
||||
<linearGradient id="fireGrad" x1="0%" y1="100%" x2="0%" y2="0%">
|
||||
<stop offset="0%" stop-color="#e74c3c" />
|
||||
<stop offset="60%" stop-color="#f39c12" />
|
||||
<stop offset="100%" stop-color="#f1c40f" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Fire Underneath -->
|
||||
<g id="cauldronFlames" class="animated-flames">
|
||||
<path d="M 85 265 Q 105 215 120 250 Q 140 195 160 250 Q 180 200 200 250 Q 220 215 235 265 Z" fill="url(#fireGrad)" opacity="0.9" />
|
||||
<path d="M 110 265 Q 135 220 160 230 Q 185 220 210 265 Z" fill="#ffeaa7" opacity="0.85" />
|
||||
</g>
|
||||
|
||||
<!-- Cauldron Feet -->
|
||||
<rect x="75" y="240" width="18" height="25" rx="5" fill="#151618" />
|
||||
<rect x="227" y="240" width="18" height="25" rx="5" fill="#151618" />
|
||||
<rect x="151" y="245" width="18" height="22" rx="5" fill="#111214" />
|
||||
|
||||
<!-- Cauldron Big Belly -->
|
||||
<ellipse cx="160" cy="180" rx="115" ry="85" fill="url(#cauldronBodyGrad)" stroke="#1a1a1a" stroke-width="4" />
|
||||
|
||||
<!-- Cauldron Gold Handles -->
|
||||
<path d="M 38 135 Q 18 160 38 185" fill="none" stroke="#d4af37" stroke-width="7" stroke-linecap="round" />
|
||||
<path d="M 282 135 Q 302 160 282 185" fill="none" stroke="#d4af37" stroke-width="7" stroke-linecap="round" />
|
||||
|
||||
<!-- Cauldron Outer Rim -->
|
||||
<ellipse cx="160" cy="105" rx="100" ry="24" fill="url(#rimGrad)" stroke="#1a1a1a" stroke-width="4" />
|
||||
|
||||
<!-- Potion Fluid Surface -->
|
||||
<ellipse id="liquidSurface" cx="160" cy="106" rx="88" ry="18" fill="url(#liquidGrad)" />
|
||||
|
||||
<!-- Bubbles in Liquid -->
|
||||
<g id="potionBubbles">
|
||||
<circle cx="135" cy="105" r="4" fill="#ffffff" opacity="0.6" class="bubble-anim b1" />
|
||||
<circle cx="170" cy="108" r="5" fill="#ffffff" opacity="0.5" class="bubble-anim b2" />
|
||||
<circle cx="155" cy="103" r="3" fill="#ffffff" opacity="0.7" class="bubble-anim b3" />
|
||||
<circle cx="190" cy="105" r="4" fill="#ffffff" opacity="0.5" class="bubble-anim b4" />
|
||||
<circle cx="120" cy="107" r="3" fill="#ffffff" opacity="0.6" class="bubble-anim b5" />
|
||||
</g>
|
||||
|
||||
<!-- Animated Stirring Spoon -->
|
||||
<g id="stirSpoon" style="display: none;">
|
||||
<path d="M 160 25 L 160 115" stroke="#8b5a2b" stroke-width="8" stroke-linecap="round" />
|
||||
<ellipse cx="160" cy="115" rx="16" ry="9" fill="#a0522d" />
|
||||
</g>
|
||||
|
||||
<!-- Dropped Ingredient Animation Container -->
|
||||
<text id="dropItemAnim" x="160" y="80" font-size="28" text-anchor="middle" style="display: none;"></text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Step 1: Ingredient Shelf -->
|
||||
<div id="ingredientsShelf" class="ingredients-shelf">
|
||||
<button type="button" class="ingredient-btn" id="ingDragon" onclick="addIngredient('dragon')">
|
||||
<span class="ing-icon">🐉</span>
|
||||
<span class="ing-name">Drachenblut</span>
|
||||
<span class="ing-count" id="countDragon">0 / 2</span>
|
||||
</button>
|
||||
<button type="button" class="ingredient-btn" id="ingMandrake" onclick="addIngredient('mandrake')">
|
||||
<span class="ing-icon">🌿</span>
|
||||
<span class="ing-name">Alraunenblatt</span>
|
||||
<span class="ing-count" id="countMandrake">0 / 1</span>
|
||||
</button>
|
||||
<button type="button" class="ingredient-btn" id="ingStar" onclick="addIngredient('star')">
|
||||
<span class="ing-icon">✨</span>
|
||||
<span class="ing-name">Sternenstaub</span>
|
||||
<span class="ing-count" id="countStar">0 / 1</span>
|
||||
</button>
|
||||
<!-- Step 1: Ingredient Shelf & Counter Bar -->
|
||||
<div id="step1Wrapper" style="width: 100%; display: flex; flex-direction: column; align-items: center; gap: 0.6rem;">
|
||||
<div style="font-size: 0.9rem; color: var(--gold); font-weight: bold;">
|
||||
🧪 Essenzen im Kessel: <span id="totalIngredientsCount">0</span> / 4
|
||||
</div>
|
||||
<div id="ingredientsShelf" class="ingredients-shelf">
|
||||
<button type="button" class="ingredient-btn" id="ingDragon" onclick="addIngredient('dragon')">
|
||||
<span class="ing-icon">🐉</span>
|
||||
<span class="ing-name">Drachenblut</span>
|
||||
<span class="ing-count" id="countDragon">0 / 2</span>
|
||||
</button>
|
||||
<button type="button" class="ingredient-btn" id="ingMandrake" onclick="addIngredient('mandrake')">
|
||||
<span class="ing-icon">🌿</span>
|
||||
<span class="ing-name">Alraunenblatt</span>
|
||||
<span class="ing-count" id="countMandrake">0 / 1</span>
|
||||
</button>
|
||||
<button type="button" class="ingredient-btn" id="ingStar" onclick="addIngredient('star')">
|
||||
<span class="ing-icon">✨</span>
|
||||
<span class="ing-name">Sternenstaub</span>
|
||||
<span class="ing-count" id="countStar">0 / 1</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Stir Control Panel (Mobile Friendly!) -->
|
||||
<!-- Step 2: Stir Control Panel -->
|
||||
<div id="stirPanel" style="display: none; flex-direction: column; align-items: center; gap: 0.5rem; width: 100%;">
|
||||
<button type="button" id="stirBtn" class="btn-magical" style="padding: 0.9rem 1.8rem; font-size: 1.15rem; width: 100%; max-width: 320px;" onclick="doStir()">
|
||||
🥄 Kessel umrühren! (<span id="stirCount">0</span> / 3)
|
||||
</button>
|
||||
<span style="color: var(--text-dim); font-size: 0.85rem;">Tippe auf den Button oder wische über den Kessel</span>
|
||||
<span style="color: var(--text-dim); font-size: 0.85rem;">Tippe auf den Button, um den Sud umzurühren</span>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Heat Control Panel -->
|
||||
@@ -69,12 +138,12 @@
|
||||
🌡️ Halte die Temperatur mit dem Blasebalg im grünen Bereich:
|
||||
</div>
|
||||
<div class="temp-bar-container">
|
||||
<div class="temp-ideal-zone"></div>
|
||||
<div class="temp-ideal-zone" style="left: 45%; width: 40%;"></div>
|
||||
<div id="tempIndicator" class="temp-indicator"></div>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%; max-width: 320px; font-size: 0.8rem; color: var(--text-dim);">
|
||||
<span>Kalt</span>
|
||||
<span style="color: #2ecc71; font-weight: bold;">Ideal (50% - 85%)</span>
|
||||
<span style="color: #2ecc71; font-weight: bold;">Ideal (45% - 85%)</span>
|
||||
<span>Zu heiß!</span>
|
||||
</div>
|
||||
<div style="color: var(--gold); font-weight: bold; margin-top: 0.25rem;">
|
||||
@@ -105,5 +174,5 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/brew.js?v=2.0"></script>
|
||||
<script src="/static/js/brew.js?v=3.0"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -21,6 +21,19 @@
|
||||
</div>
|
||||
|
||||
<canvas id="broomCanvas" style="touch-action: none; -webkit-user-select: none; user-select: none; width: 100%; display: block;"></canvas>
|
||||
|
||||
<!-- Start Flight Overlay -->
|
||||
<div id="broomStartOverlay" class="flight-start-overlay">
|
||||
<div style="font-size: 2.8rem; margin-bottom: 0.3rem;">🧹✨⭕</div>
|
||||
<h3 style="color: var(--gold); font-size: 1.45rem; margin: 0 0 0.4rem 0;">Bereit zum Abflug?</h3>
|
||||
<p style="color: var(--text-light); font-size: 0.95rem; margin: 0 0 1.2rem 0; text-align: center; max-width: 300px;">
|
||||
Fliege durch <strong>12 magische Ringe</strong>!<br>
|
||||
Steuere per Daumen über das Spielfeld oder mit dem Steuerkreuz unten.
|
||||
</p>
|
||||
<button type="button" id="startFlightBtn" class="btn-magical" style="padding: 0.85rem 2.2rem; font-size: 1.2rem;" onclick="startFlight()">
|
||||
⚡ Flug starten!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Flight Cockpit & D-Pad -->
|
||||
|
||||
+19
-12
@@ -4,18 +4,25 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="parchment-scroll">
|
||||
<h1 class="parchment-title">🔐 Kammer IV: Das Runen-Drehschloss 🔐</h1>
|
||||
<p class="riddle-poem" style="font-size: 1.15rem; margin: 0.3rem 0;">
|
||||
"Vor dir ragt das Tresortor der vier Gründer mit vier rotierenden Messingwalzen.<br>
|
||||
<em>»Der <strong>Löwe</strong> wacht am Himmelstor,<br>
|
||||
wo nachts die <strong>Silbersichel</strong> glimmt,<br>
|
||||
das ew’ge <strong>Feuer</strong> niemals stirbt,<br>
|
||||
bis der <strong>Zauberstab</strong> die Siegel bricht.«</em>"
|
||||
</p>
|
||||
<h1 class="parchment-title">🔐 Kammer IV: Das Runen-Drehschloss der Gründer 🔐</h1>
|
||||
<div style="font-family: 'Caveat', cursive; font-size: 1.25rem; line-height: 1.6; color: #efe5cb; text-align: left; padding: 0.25rem 0.5rem;">
|
||||
<em>
|
||||
»Vier Siegel schloss der Gründer Rat, bevor man je die Schatzkammer betrat.<br>
|
||||
Kein Zauber bricht das Schloss, wenn du die Verse nicht ergründest:<br><br>
|
||||
<strong>I. Das Wappentier von Godric:</strong><br>
|
||||
Nicht die verschlagene Natter der Kerker, nicht der kluge Rabe, nicht der emsige Dachs – wer krönt mit goldener Mähne den Mut der Tapfersten?<br><br>
|
||||
<strong>II. Das Gestirn der Gezeiten:</strong><br>
|
||||
Nicht das sengende Mittagslicht, nicht der stumme Polarstern, nicht der jähe Blitz – welcher silberne Wanderer herrscht über die stille Nacht?<br><br>
|
||||
<strong>III. Das verzehrende Element:</strong><br>
|
||||
Es löscht keinen Durst, wurzelt in keinem Boden, weht nicht wie der Wind – es frisst gierig Holz, tanzt in Glut und stirbt im Wasser.<br><br>
|
||||
<strong>IV. Das Werkzeug des Meisters:</strong><br>
|
||||
Nicht das Schloss selbst, nicht das alte Pergament, nicht die Glasphiole – welches Instrument aus Holz und Phönixfeder schlägt die Funken?«
|
||||
</em>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cryptex-container">
|
||||
<!-- Status / Feedback Toast -->
|
||||
<!-- Feedback Toast -->
|
||||
<div id="cryptexFeedback" style="display: none; padding: 0.6rem 1rem; border-radius: 8px; font-weight: bold; text-align: center; width: 100%;">
|
||||
</div>
|
||||
|
||||
@@ -70,7 +77,7 @@
|
||||
<button type="button" id="unlockCryptexBtn" class="btn-magical" style="padding: 0.9rem 2.2rem; font-size: 1.25rem; width: 100%; max-width: 320px;" onclick="checkCryptex()">
|
||||
🔓 Siegel entriegeln
|
||||
</button>
|
||||
<span style="color: var(--text-dim); font-size: 0.85rem;">Tippe auf ▲/▼ oder wische auf den Walzen</span>
|
||||
<span style="color: var(--text-dim); font-size: 0.85rem;">Tippe auf ▲/▼ oder wische über die Walzen</span>
|
||||
</div>
|
||||
|
||||
<!-- Success Panel -->
|
||||
@@ -81,7 +88,7 @@
|
||||
Das Tor der Gründer steht offen!
|
||||
</h2>
|
||||
<p style="color: var(--text-light); max-width: 500px; margin: 0 auto 1.25rem auto;">
|
||||
Die Messingbolzen schnappen lautstark zurück! Der uralte <strong>Schlüssel der Gründer</strong> wandert in deinen <strong>Zauberbeutel</strong>.
|
||||
Mit Scharfsinn hast du alle 4 Verse entschlüsselt! Die massiven Bolzen schnappen auf und der uralte <strong>Schlüssel der Gründer</strong> liegt in deinem <strong>Zauberbeutel</strong>.
|
||||
</p>
|
||||
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
|
||||
<a href="/pixies" class="btn-magical">Zur nächsten Prüfung: Wichtel-Panik 🧚➔</a>
|
||||
@@ -93,5 +100,5 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="/static/js/cryptex.js?v=2.0"></script>
|
||||
<script src="/static/js/cryptex.js?v=3.0"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
<div class="canvas-wrapper">
|
||||
<div class="game-hud">
|
||||
<div>🪄 Spruch: <strong id="spellName" style="color: var(--gold);">Flipendo ⚡</strong></div>
|
||||
<div id="spellProgress" style="color: #4cc9f0; font-weight: bold;">Zauberspruch 1 von 3</div>
|
||||
<div>🛡️ Ziel: <span id="dummyHp">3 Zauber übrig</span></div>
|
||||
<div id="spellProgress" style="color: #4cc9f0; font-weight: bold;">Zauberspruch 1 von 4</div>
|
||||
<div>🛡️ Ziel: <span id="dummyHp">4 Zauber übrig</span></div>
|
||||
</div>
|
||||
|
||||
<canvas id="spellCanvas" style="touch-action: none; -webkit-user-select: none; user-select: none; width: 100%; display: block;"></canvas>
|
||||
|
||||
@@ -41,47 +41,47 @@
|
||||
<!-- Hole 0 -->
|
||||
<div class="pixie-hole" data-idx="0">
|
||||
<span class="hole-decor">📚</span>
|
||||
<div class="pixie-target" id="target0">🧚</div>
|
||||
<div class="pixie-target" id="target0"></div>
|
||||
</div>
|
||||
<!-- Hole 1 -->
|
||||
<div class="pixie-hole" data-idx="1">
|
||||
<span class="hole-decor">🗝️</span>
|
||||
<div class="pixie-target" id="target1">🧚</div>
|
||||
<div class="pixie-target" id="target1"></div>
|
||||
</div>
|
||||
<!-- Hole 2 -->
|
||||
<div class="pixie-hole" data-idx="2">
|
||||
<span class="hole-decor">🕯️</span>
|
||||
<div class="pixie-target" id="target2">🧚</div>
|
||||
<div class="pixie-target" id="target2"></div>
|
||||
</div>
|
||||
<!-- Hole 3 -->
|
||||
<div class="pixie-hole" data-idx="3">
|
||||
<span class="hole-decor">🧪</span>
|
||||
<div class="pixie-target" id="target3">🧚</div>
|
||||
<div class="pixie-target" id="target3"></div>
|
||||
</div>
|
||||
<!-- Hole 4 -->
|
||||
<div class="pixie-hole" data-idx="4">
|
||||
<span class="hole-decor">📦</span>
|
||||
<div class="pixie-target" id="target4">🧚</div>
|
||||
<div class="pixie-target" id="target4"></div>
|
||||
</div>
|
||||
<!-- Hole 5 -->
|
||||
<div class="pixie-hole" data-idx="5">
|
||||
<span class="hole-decor">🪟</span>
|
||||
<div class="pixie-target" id="target5">🧚</div>
|
||||
<div class="pixie-target" id="target5"></div>
|
||||
</div>
|
||||
<!-- Hole 6 -->
|
||||
<div class="pixie-hole" data-idx="6">
|
||||
<span class="hole-decor">📜</span>
|
||||
<div class="pixie-target" id="target6">🧚</div>
|
||||
<div class="pixie-target" id="target6"></div>
|
||||
</div>
|
||||
<!-- Hole 7 -->
|
||||
<div class="pixie-hole" data-idx="7">
|
||||
<span class="hole-decor">🕸️</span>
|
||||
<div class="pixie-target" id="target7">🧚</div>
|
||||
<div class="pixie-target" id="target7"></div>
|
||||
</div>
|
||||
<!-- Hole 8 -->
|
||||
<div class="pixie-hole" data-idx="8">
|
||||
<span class="hole-decor">🕰️</span>
|
||||
<div class="pixie-target" id="target8">🧚</div>
|
||||
<div class="pixie-target" id="target8"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user