359 lines
15 KiB
JavaScript
Executable File
359 lines
15 KiB
JavaScript
Executable File
// Player Menu & Inventory (Zauberer-Beutel & Profil)
|
|
(function() {
|
|
const drawer = document.getElementById('playerDrawer');
|
|
const drawerBackdrop = document.getElementById('drawerBackdrop');
|
|
const bagBtn = document.getElementById('openBagBtn');
|
|
const closeBtn = document.getElementById('closeDrawerBtn');
|
|
const bagBadge = document.getElementById('bagBadge');
|
|
const bagHouseIcon = document.getElementById('bagHouseIcon');
|
|
|
|
// Tabs
|
|
const tabBtns = document.querySelectorAll('.drawer-tab-btn');
|
|
const tabPanes = document.querySelectorAll('.drawer-tab-pane');
|
|
|
|
// Item Detail Card
|
|
const itemDetailBox = document.getElementById('itemDetailBox');
|
|
const itemDetailIcon = document.getElementById('itemDetailIcon');
|
|
const itemDetailName = document.getElementById('itemDetailName');
|
|
const itemDetailDesc = document.getElementById('itemDetailDesc');
|
|
const itemDetailActionBtn = document.getElementById('itemDetailActionBtn');
|
|
|
|
// House selection buttons
|
|
const houseBtns = document.querySelectorAll('.house-select-btn');
|
|
|
|
let currentItem = null;
|
|
let lumosActive = false;
|
|
|
|
window.togglePlayerDrawer = function() {
|
|
if (!drawer) return;
|
|
const isOpen = drawer.classList.contains('open');
|
|
if (isOpen) {
|
|
closePlayerDrawer();
|
|
} else {
|
|
openPlayerDrawer();
|
|
}
|
|
};
|
|
|
|
window.openPlayerDrawer = function(tabName) {
|
|
if (!drawer) return;
|
|
drawer.classList.add('open');
|
|
if (drawerBackdrop) drawerBackdrop.classList.add('open');
|
|
document.body.style.overflow = 'hidden';
|
|
if (window.soundFX) window.soundFX.playStoneSlide();
|
|
if (tabName) switchTab(tabName);
|
|
};
|
|
|
|
window.closePlayerDrawer = function() {
|
|
if (!drawer) return;
|
|
drawer.classList.remove('open');
|
|
if (drawerBackdrop) drawerBackdrop.classList.remove('open');
|
|
document.body.style.overflow = '';
|
|
if (window.soundFX) window.soundFX.playStoneSlide();
|
|
};
|
|
|
|
if (bagBtn) bagBtn.addEventListener('click', window.togglePlayerDrawer);
|
|
if (closeBtn) closeBtn.addEventListener('click', window.closePlayerDrawer);
|
|
if (drawerBackdrop) drawerBackdrop.addEventListener('click', window.closePlayerDrawer);
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && drawer && drawer.classList.contains('open')) {
|
|
window.closePlayerDrawer();
|
|
}
|
|
});
|
|
|
|
// Tab Switching
|
|
tabBtns.forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const target = btn.dataset.tab;
|
|
switchTab(target);
|
|
});
|
|
});
|
|
|
|
function switchTab(tabId) {
|
|
tabBtns.forEach(b => b.classList.toggle('active', b.dataset.tab === tabId));
|
|
tabPanes.forEach(p => p.classList.toggle('active', p.id === tabId));
|
|
if (window.soundFX) window.soundFX.playBottleClink();
|
|
}
|
|
|
|
// Item Selection in Bag
|
|
const itemSlots = document.querySelectorAll('.inv-item-slot');
|
|
itemSlots.forEach(slot => {
|
|
slot.addEventListener('click', () => {
|
|
selectItem(slot);
|
|
});
|
|
});
|
|
|
|
function selectItem(slot) {
|
|
itemSlots.forEach(s => s.classList.remove('selected'));
|
|
slot.classList.add('selected');
|
|
|
|
const itemId = slot.dataset.itemId;
|
|
const itemName = slot.dataset.itemName;
|
|
const itemIcon = slot.dataset.itemIcon;
|
|
const itemDesc = slot.dataset.itemDesc;
|
|
const actionLabel = slot.dataset.actionLabel;
|
|
const action = slot.dataset.action;
|
|
|
|
currentItem = { id: itemId, name: itemName, icon: itemIcon, action: action };
|
|
|
|
if (itemDetailBox) {
|
|
itemDetailBox.style.display = 'block';
|
|
itemDetailIcon.innerText = itemIcon;
|
|
itemDetailName.innerText = itemName;
|
|
itemDetailDesc.innerText = itemDesc;
|
|
itemDetailActionBtn.innerText = actionLabel || "Benutzen ✨";
|
|
itemDetailActionBtn.onclick = () => performItemAction(action);
|
|
}
|
|
|
|
if (window.soundFX) window.soundFX.playBottleClink();
|
|
}
|
|
|
|
function performItemAction(action) {
|
|
if (action === 'lumos') {
|
|
lumosActive = !lumosActive;
|
|
let glow = document.getElementById('lumosLightOverlay');
|
|
if (!glow) {
|
|
glow = document.createElement('div');
|
|
glow.id = 'lumosLightOverlay';
|
|
glow.className = 'lumos-light';
|
|
document.body.appendChild(glow);
|
|
}
|
|
glow.classList.toggle('active', lumosActive);
|
|
itemDetailActionBtn.innerText = lumosActive ? "Nox / Erlöschen 🌑" : "Lumos wirken 💡";
|
|
if (window.soundFX) window.soundFX.playKeyChime();
|
|
showFloatingToast(lumosActive ? "💡 Lumos! Dein Zauberstab erleuchtet die Kammer." : "🌑 Nox! Das Zauberlicht erlischt.");
|
|
} else if (action === 'read_scroll') {
|
|
// Open scroll modal
|
|
window.closePlayerDrawer();
|
|
const modalBackdrop = document.getElementById('modalBackdrop');
|
|
const modalTitle = document.getElementById('modalTitle');
|
|
const modalText = document.getElementById('modalText');
|
|
const modalBtn = document.getElementById('modalBtn');
|
|
const modalIcon = document.getElementById('modalIcon');
|
|
if (modalBackdrop) {
|
|
modalIcon.innerText = "📜";
|
|
modalTitle.innerText = "Snapes Logik-Rätsel";
|
|
modalText.innerHTML = `
|
|
<div style="font-family: 'Caveat', cursive; font-size: 1.35rem; line-height: 1.7; text-align: left; max-height: 50vh; overflow-y: auto; padding: 0.5rem 1rem;">
|
|
<em>"Gefahr liegt vor euch, die Rettung zurück,<br>
|
|
Zwei von uns helfen euch, habt ihr das Glück,<br>
|
|
Eine von sieben führt euch hindurch,<br>
|
|
Eine bringt den, der sie trinkt, sicher zurück,<br>
|
|
Zwei von ihnen enthalten nur Brennnesselwein,<br>
|
|
Drei sind tödlich, schenkt ihr sie ein.<br>
|
|
Wählt oder bleibt auf ewig hier stehen,<br>
|
|
Vier Hinweise helfen euch, alles zu sehen..."</em><br><br>
|
|
<strong>1.</strong> Links neben jedem Wein steht immer Gift.<br>
|
|
<strong>2.</strong> Die Enden sind verschieden, keines führt nach vorn.<br>
|
|
<strong>3.</strong> Weder Zwerg noch Riese birgt Gift.<br>
|
|
<strong>4.</strong> Zweite von links & zweite von rechts schmecken gleich!
|
|
</div>
|
|
`;
|
|
modalBtn.innerText = "Verstanden ➔";
|
|
modalBtn.onclick = () => modalBackdrop.classList.remove('show');
|
|
modalBackdrop.classList.add('show');
|
|
}
|
|
} else if (action === 'broom_polish') {
|
|
if (window.soundFX) window.soundFX.playWhoosh();
|
|
showFloatingToast("🧹 Der Nimbus 2000 glänzt wie neu. Die Zweige sind perfekt gestutzt!");
|
|
} else if (action === 'spark_wand') {
|
|
if (window.soundFX) window.soundFX.playWhoosh();
|
|
showFloatingToast("⚡ Magische Funken sprühen aus deiner Zauberstabspitze!");
|
|
} else if (action === 'lucky_aura') {
|
|
if (window.soundFX) window.soundFX.playMagicSuccess();
|
|
showFloatingToast("🍀 Ein Tropfen Felix Felicis! Eine Welle goldenen Glücks erfüllt deine Adern!");
|
|
} else if (action === 'inspect_key') {
|
|
if (window.soundFX) window.soundFX.playKeyChime();
|
|
showFloatingToast("🗝️ Die Gravur der vier Gründer leuchtet warm auf: 'Hogwarts, Hogwarts, warzenschweinig alt...'");
|
|
} else if (action === 'poke_jar') {
|
|
if (window.soundFX) window.soundFX.playWhoosh();
|
|
showFloatingToast("🧚 *Klopf-Klopf!* Der blaue Wichtel im Glas verzieht frech das Gesicht!");
|
|
} else if (action === 'examine_key') {
|
|
if (window.soundFX) window.soundFX.playKeyChime();
|
|
showFloatingToast("🗝️ Der alte Silberschlüssel ist schwer und trägt Spuren vergangener Kämpfe.");
|
|
} else if (action === 'feel_power') {
|
|
if (window.soundFX) window.soundFX.playMagicSuccess();
|
|
showFloatingToast("💎 Eine unendliche Wärme durchströmt deine Finger. Der Stein schützt die Welt!");
|
|
}
|
|
}
|
|
|
|
// House selection
|
|
houseBtns.forEach(btn => {
|
|
btn.addEventListener('click', async () => {
|
|
const house = btn.dataset.house;
|
|
try {
|
|
const res = await fetch('/api/player/house', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ house: house })
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
houseBtns.forEach(b => b.classList.toggle('active', b.dataset.house === house));
|
|
if (bagHouseIcon) bagHouseIcon.innerText = data.house.icon;
|
|
document.documentElement.style.setProperty('--house-accent', data.house.accent);
|
|
if (window.soundFX) window.soundFX.playMagicSuccess();
|
|
showFloatingToast(`Du gehörst nun stolz zum Haus ${data.house.name} ${data.house.icon}!`);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
});
|
|
});
|
|
|
|
// Toast Notification
|
|
window.showFloatingToast = function(msg) {
|
|
let toast = document.getElementById('floatingEscapeToast');
|
|
if (!toast) {
|
|
toast = document.createElement('div');
|
|
toast.id = 'floatingEscapeToast';
|
|
toast.className = 'escape-toast';
|
|
document.body.appendChild(toast);
|
|
}
|
|
toast.innerHTML = msg;
|
|
toast.classList.add('show');
|
|
clearTimeout(toast._timeout);
|
|
toast._timeout = setTimeout(() => {
|
|
toast.classList.remove('show');
|
|
}, 3500);
|
|
};
|
|
|
|
window.notifyNewItem = function(item) {
|
|
if (!item) return;
|
|
if (bagBadge) {
|
|
let count = parseInt(bagBadge.innerText, 10) || 0;
|
|
bagBadge.innerText = count + 1;
|
|
bagBadge.classList.add('pulse');
|
|
setTimeout(() => bagBadge.classList.remove('pulse'), 2000);
|
|
}
|
|
if (window.soundFX) window.soundFX.playMagicSuccess();
|
|
window.showFloatingToast(`🎒 <strong>Neuer Gegenstand im Beutel:</strong> ${item.icon} ${item.name}!`);
|
|
};
|
|
|
|
// Confetti Animation Controller
|
|
let confettiAnimId = null;
|
|
function runVictoryConfetti() {
|
|
const canvas = document.getElementById('victoryConfettiCanvas');
|
|
if (!canvas) return;
|
|
const ctx = canvas.getContext('2d');
|
|
canvas.width = window.innerWidth;
|
|
canvas.height = window.innerHeight;
|
|
|
|
const colors = ['#ffd700', '#f39c12', '#e74c3c', '#3498db', '#2ecc71', '#9b59b6', '#ffffff'];
|
|
const particles = [];
|
|
for (let i = 0; i < 90; i++) {
|
|
particles.push({
|
|
x: Math.random() * canvas.width,
|
|
y: Math.random() * -canvas.height * 0.5,
|
|
w: Math.random() * 9 + 5,
|
|
h: Math.random() * 5 + 3,
|
|
vx: (Math.random() - 0.5) * 4,
|
|
vy: Math.random() * 3 + 2.5,
|
|
color: colors[Math.floor(Math.random() * colors.length)],
|
|
angle: Math.random() * Math.PI * 2,
|
|
vAngle: (Math.random() - 0.5) * 0.15,
|
|
shape: Math.random() > 0.4 ? 'rect' : 'star'
|
|
});
|
|
}
|
|
|
|
let start = Date.now();
|
|
if (confettiAnimId) cancelAnimationFrame(confettiAnimId);
|
|
|
|
function drawConfetti() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
const elapsed = (Date.now() - start) / 1000;
|
|
|
|
particles.forEach(p => {
|
|
p.x += p.vx;
|
|
p.y += p.vy;
|
|
p.angle += p.vAngle;
|
|
|
|
ctx.save();
|
|
ctx.translate(p.x, p.y);
|
|
ctx.rotate(p.angle);
|
|
ctx.fillStyle = p.color;
|
|
|
|
if (p.shape === 'star') {
|
|
ctx.beginPath();
|
|
ctx.arc(0, 0, p.w * 0.45, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
} else {
|
|
ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h);
|
|
}
|
|
ctx.restore();
|
|
|
|
if (p.y > canvas.height + 20) {
|
|
p.y = -20;
|
|
p.x = Math.random() * canvas.width;
|
|
}
|
|
});
|
|
|
|
if (elapsed < 8) {
|
|
confettiAnimId = requestAnimationFrame(drawConfetti);
|
|
} else {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
}
|
|
confettiAnimId = requestAnimationFrame(drawConfetti);
|
|
}
|
|
|
|
// Unified Spectacular Victory Celebration Overlay
|
|
window.showChamberVictory = function(config) {
|
|
const modal = document.getElementById('victoryCelebrationModal');
|
|
if (!modal) {
|
|
// Fallback to standard modal if celebration overlay missing
|
|
if (window.modalTitle) window.modalTitle.innerText = config.title;
|
|
if (window.modalText) window.modalText.innerHTML = config.storyText;
|
|
if (window.modalBackdrop) window.modalBackdrop.classList.add('show');
|
|
return;
|
|
}
|
|
|
|
const badge = document.getElementById('vicChamberBadge');
|
|
const title = document.getElementById('vicTitle');
|
|
const statsBox = document.getElementById('vicStatsBox');
|
|
const itemBox = document.getElementById('vicItemRewardBox');
|
|
const rewardIcon = document.getElementById('vicRewardIcon');
|
|
const rewardName = document.getElementById('vicRewardName');
|
|
const rewardDesc = document.getElementById('vicRewardDesc');
|
|
const storyBox = document.getElementById('vicStoryBox');
|
|
const nextBtn = document.getElementById('vicNextBtn');
|
|
|
|
if (badge) badge.innerText = config.chamberBadge || 'Kammer Gemeistert';
|
|
if (title) title.innerText = config.title || 'Hervorragend!';
|
|
if (statsBox) {
|
|
statsBox.innerHTML = config.statsHtml || '';
|
|
statsBox.style.display = config.statsHtml ? 'flex' : 'none';
|
|
}
|
|
if (storyBox) storyBox.innerHTML = config.storyText || '';
|
|
|
|
if (config.item && itemBox) {
|
|
itemBox.style.display = 'flex';
|
|
if (rewardIcon) rewardIcon.innerText = config.item.icon || '✨';
|
|
if (rewardName) rewardName.innerText = config.item.name || '';
|
|
if (rewardDesc) rewardDesc.innerText = config.item.desc || '';
|
|
window.notifyNewItem(config.item);
|
|
} else if (itemBox) {
|
|
itemBox.style.display = 'none';
|
|
}
|
|
|
|
if (nextBtn) {
|
|
nextBtn.href = config.nextUrl || '/';
|
|
nextBtn.innerHTML = config.nextBtnText || '➔ Weiter zur nächsten Kammer ✨';
|
|
}
|
|
|
|
// Trigger Audio, Haptic & Particle Celebration
|
|
if (window.soundFX) window.soundFX.playMagicSuccess();
|
|
if (window.navigator && window.navigator.vibrate) {
|
|
window.navigator.vibrate([60, 50, 80, 50, 180]);
|
|
}
|
|
|
|
modal.classList.add('show');
|
|
runVictoryConfetti();
|
|
};
|
|
|
|
// Auto-select first item if available
|
|
if (itemSlots.length > 0) {
|
|
selectItem(itemSlots[0]);
|
|
}
|
|
})();
|