dok/public/js/quickmenu/events.js
2026-04-06 16:26:04 +01:00

299 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ================================
Kristall-Mapping (aus carddeck.js)
================================ */
const RARITY_CRYSTALS = {
1: "roter-cristal.png",
2: "blauer-cristal.png",
3: "gelber-cristal.png",
4: "gruener-cristal.png",
5: "oranger-cristal.png",
6: "violet-cristal.png",
7: "pinker-cristal.png",
};
function rarityImgs(rarity, size = 13) {
const file = RARITY_CRYSTALS[String(rarity)];
if (!file) return "";
const count = parseInt(rarity) || 0;
const img = `<img src="/images/items/${file}" alt="Stufe ${rarity}" style="width:${size}px;height:${size}px;object-fit:contain;filter:drop-shadow(0 1px 2px rgba(0,0,0,0.8));">`;
return img.repeat(count);
}
function cardHTML(card, isFront = true) {
if (!isFront) return `<img class="booster-slot-img" src="/images/items/rueckseite.png" alt="?" draggable="false">`;
// image zuerst, dann icon als Fallback
const imgFile = card?.image || card?.icon || null;
const img = imgFile ? `/images/cards/${imgFile}` : "/images/items/rueckseite.png";
return `
<img class="booster-slot-img" src="${img}" alt="${card?.name || ''}" draggable="false"
onerror="this.src='/images/items/rueckseite.png'">
${card?.attack != null ? `<span class="bs-stat-atk">${card.attack}</span>` : ""}
${card?.defends != null ? `<span class="bs-stat-def">${card.defends}</span>` : ""}
${card?.cooldown!= null ? `<span class="bs-stat-cd">${card.cooldown}</span>` : ""}
${card?.rarity ? `<div class="bs-rarity">${rarityImgs(card.rarity, 11)}</div>` : ""}
<div class="bs-card-name">${card?.name || ''}</div>
`;
}
/* ================================
Haupt-Export
================================ */
export async function loadEvents() {
const body = document.getElementById("qm-body-events");
if (!body) return;
if (!document.querySelector('link[href="/css/events.css"]')) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "/css/events.css";
document.head.appendChild(link);
}
const events = [
{ id: 1, img: "/images/items/runenhaufen.png", label: "Booster Öffnen", type: "booster" },
{ id: 2, img: "/images/items/runenhaufen.png", label: "Textzeile 2" },
{ id: 3, img: "/images/items/runenhaufen.png", label: "Textzeile 3" },
{ id: 4, img: "/images/items/runenhaufen.png", label: "Textzeile 4" },
{ id: 5, img: "/images/items/runenhaufen.png", label: "Textzeile 5" },
];
body.innerHTML = `
<div class="events-grid" id="events-grid">
${events.map(ev => `
<div class="event-card" data-event-id="${ev.id}" data-type="${ev.type || ''}">
<div class="event-card-img-wrap">
<img src="${ev.img}" alt="${ev.label}" draggable="false">
</div>
<span class="event-card-label">${ev.label}</span>
</div>`).join("")}
</div>
<!-- Booster UI -->
<div id="booster-ui" class="booster-ui" style="display:none;">
<button class="booster-back-btn" id="booster-back-btn">← Zurück</button>
<div class="booster-stage">
<div class="booster-left">
<img id="booster-stapel" src="/images/items/boosterstapel.png" alt="Booster" draggable="false" class="booster-stapel-img">
<span class="booster-stapel-hint" id="booster-hint">Klicken zum Öffnen</span>
</div>
<div class="booster-slots" id="booster-slots">
${Array.from({length: 5}, (_, i) => `
<div class="booster-slot" id="booster-slot-${i}">
<div class="booster-slot-inner">
<img class="booster-slot-img" src="/images/items/rueckseite.png" alt="?" draggable="false">
</div>
</div>`).join("")}
</div>
</div>
</div>
<!-- Standard Detail-Popup -->
<div id="event-detail-overlay">
<div id="event-detail-popup">
<button class="edp-close" id="edp-close-btn"></button>
<img class="edp-img" id="edp-img" src="" alt="">
<div class="edp-title" id="edp-title"></div>
<div class="edp-body" id="edp-body">Inhalt folgt...</div>
</div>
</div>
`;
const overlay = body.querySelector("#event-detail-overlay");
const edpImg = body.querySelector("#edp-img");
const edpTitle = body.querySelector("#edp-title");
const edpBody = body.querySelector("#edp-body");
const boosterUi = body.querySelector("#booster-ui");
const eventsGrid = body.querySelector("#events-grid");
/* ── Event-Karten ── */
body.querySelectorAll(".event-card").forEach(card => {
card.addEventListener("click", () => {
if (card.dataset.type === "booster") {
eventsGrid.style.display = "none";
boosterUi.style.display = "flex";
resetBooster();
return;
}
const id = Number(card.dataset.eventId);
const ev = events.find(e => e.id === id);
if (!ev) return;
edpImg.src = ev.img;
edpImg.alt = ev.label;
edpTitle.textContent = ev.label;
edpBody.textContent = "Inhalt folgt...";
overlay.classList.add("active");
});
});
body.querySelector("#edp-close-btn").addEventListener("click", () => overlay.classList.remove("active"));
overlay.addEventListener("click", e => { if (e.target === overlay) overlay.classList.remove("active"); });
body.querySelector("#booster-back-btn").addEventListener("click", () => {
if (isSpinning || !allRevealed) return; // ← gesperrt bis alle Karten enthüllt
eventsGrid.style.display = "";
boosterUi.style.display = "none";
clearAllIntervals();
isSpinning = false;
});
document.addEventListener("keydown", e => {
if (e.key === "Escape") {
overlay.classList.remove("active");
if (!isSpinning && allRevealed) { // ← ESC im Booster nur wenn fertig
eventsGrid.style.display = "";
boosterUi.style.display = "none";
clearAllIntervals();
}
}
});
/* ── Booster Zustand ── */
let allCards = [];
let isSpinning = false;
let allRevealed = false; // true sobald alle 5 Karten aufgedeckt + gespeichert
let spinIntervals = {}; // { index: intervalId }
let slotLocked = {}; // { index: true } → verhindert Überschreiben nach reveal
function clearAllIntervals() {
Object.values(spinIntervals).forEach(id => clearInterval(id));
spinIntervals = {};
slotLocked = {};
}
async function preloadCards() {
if (allCards.length) return;
try {
const res = await fetch("/api/booster/cards");
if (!res.ok) throw new Error(res.status);
allCards = await res.json();
} catch (e) {
console.error("Karten laden fehlgeschlagen", e);
}
}
function resetBooster() {
clearAllIntervals();
isSpinning = false;
allRevealed = false;
for (let i = 0; i < 5; i++) {
const inner = body.querySelector(`#booster-slot-${i} .booster-slot-inner`);
inner.innerHTML = `<img class="booster-slot-img" src="/images/items/rueckseite.png" alt="?" draggable="false">`;
body.querySelector(`#booster-slot-${i}`).classList.remove("revealed", "spinning");
}
const stapel = body.querySelector("#booster-stapel");
stapel.classList.remove("used");
stapel.style.opacity = "1";
stapel.style.cursor = "pointer";
body.querySelector("#booster-hint").textContent = "Klicken zum Öffnen";
preloadCards();
}
/* ── Slot drehen 350ms, damit man die Karten erkennt ── */
function startSpinSlot(index) {
slotLocked[index] = false;
const slot = body.querySelector(`#booster-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.add("spinning");
const iv = setInterval(() => {
if (!allCards.length || slotLocked[index]) return;
const rnd = allCards[Math.floor(Math.random() * allCards.length)];
inner.innerHTML = cardHTML(rnd, true);
}, 150);
spinIntervals[index] = iv;
}
/* ── Slot enthüllen Sperre setzen BEVOR clearInterval ── */
function revealSlot(index, card) {
slotLocked[index] = true; // zuerst sperren
clearInterval(spinIntervals[index]); // dann stoppen
delete spinIntervals[index];
console.log(`[Booster] Slot ${index} enthüllt:`, card);
const slot = body.querySelector(`#booster-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.remove("spinning");
slot.classList.add("revealed");
// innerHTML setzen und danach nochmal sicherstellen (doppelte Absicherung)
inner.innerHTML = cardHTML(card, true);
setTimeout(() => {
if (slot.classList.contains("revealed")) {
inner.innerHTML = cardHTML(card, true);
}
}, 100);
}
/* ── Stapel klicken ── */
body.querySelector("#booster-stapel").addEventListener("click", async () => {
if (isSpinning) return;
if (!allCards.length) await preloadCards();
if (!allCards.length) return;
isSpinning = true;
const stapel = body.querySelector("#booster-stapel");
stapel.classList.add("used");
stapel.style.opacity = "0.35";
stapel.style.cursor = "default";
body.querySelector("#booster-hint").textContent = "Wird gezogen...";
// Zurück-Button während der Animation sperren
const backBtn = body.querySelector("#booster-back-btn");
backBtn.style.opacity = "0.35";
backBtn.style.cursor = "not-allowed";
let drawnCards = [];
try {
const res = await fetch("/api/booster/open", { method: "POST" });
const text = await res.text();
console.log("[Booster] API Antwort raw:", text);
const data = JSON.parse(text);
console.log("[Booster] API Antwort parsed:", data);
drawnCards = data.cards || [];
console.log("[Booster] drawnCards:", drawnCards);
} catch (e) {
console.error("Booster öffnen fehlgeschlagen", e);
resetBooster();
return;
}
for (let i = 0; i < 5; i++) startSpinSlot(i);
for (let i = 0; i < 5; i++) {
setTimeout(() => revealSlot(i, drawnCards[i]), (i + 1) * 5000);
}
setTimeout(async () => {
body.querySelector("#booster-hint").textContent = "Karten werden gespeichert...";
isSpinning = false;
// Karten in user_cards speichern
try {
const cardIds = drawnCards.map(c => c.id);
const saveRes = await fetch("/api/booster/save", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ cardIds }),
});
if (!saveRes.ok) throw new Error(saveRes.status);
} catch (e) {
console.error("Karten speichern fehlgeschlagen", e);
}
allRevealed = true;
body.querySelector("#booster-hint").textContent = "Alle Karten erhalten! ✓";
const backBtn = body.querySelector("#booster-back-btn");
backBtn.style.opacity = "1";
backBtn.style.cursor = "pointer";
}, 5 * 5000 + 500);
});
preloadCards();
}