Files
bootle/app/static/js/inventory.js
T

386 lines
17 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 === 'read_recipe') {
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) {
if (modalIcon) modalIcon.innerText = "📜";
if (modalTitle) modalTitle.innerText = "Snapes Felix-Felicis-Rezeptur";
if (modalText) {
modalText.innerHTML = `
<div style="font-family: 'Caveat', cursive; font-size: 1.35rem; line-height: 1.7; text-align: left; max-height: 52vh; overflow-y: auto; padding: 0.5rem 1rem;">
<em>»Felix Felicis ist kein Trank für Scharlatane. Wer die Maße missachtet, verdirbt den Kessel augenblicklich:«</em><br><br>
<strong>🐉 Drachenblut:</strong> <em>"So viele Tropfen wie Köpfe des schlafenden Fluffy (3 Tropfen)"</em><br>
<strong>🌿 Alraunenblatt:</strong> <em>"Harrys treue Gefährten am Gryffindor-Tisch (Ron & Hermine = 2 Blätter)"</em><br>
<strong>✨ Sternenstaub:</strong> <em>"Der eine Siegelring des Alchemisten (1 Prise)"</em><br><br>
<strong>🥄 Rühr-Anweisung:</strong> <em>"Zuerst 3 Sekunden gleichmäßig linksherum rühren (↺), danach 3 Sekunden rechtsherum (↻)! Ein Hektiker schlägt Schaum, ein Zauderer lässt den Sud erstarren!"</em>
</div>
`;
}
if (modalBtn) {
modalBtn.innerText = "Rezeptur einprägen ➔";
modalBtn.onclick = () => modalBackdrop.classList.remove('show');
}
modalBackdrop.classList.add('show');
}
} 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]);
}
})();