/* ================================
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 = ``;
return img.repeat(count);
}
function cardHTML(card, isFront = true) {
if (!isFront)
return `
`;
// image zuerst, dann icon als Fallback
const imgFile = card?.image || card?.icon || null;
const img = imgFile
? `/images/cards/${imgFile}`
: "/images/items/rueckseite.png";
return `
${card?.attack != null ? `${card.attack}` : ""}
${card?.defends != null ? `${card.defends}` : ""}
${card?.cooldown != null ? `${card.cooldown}` : ""}
${card?.rarity ? `
`;
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);
}
// Booster-Daily als erledigt markieren (event_id = 1)
await markDailyComplete(1);
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();
/* ================================
Holz-Spenden Zustand
================================ */
let isWoodSpinning = false;
let woodRevealed = false;
let woodIntervals = {};
let woodLocked = {};
let rarity3Cards = [];
async function preloadRarity3() {
if (rarity3Cards.length) return;
try {
const res = await fetch("/api/booster/cards");
if (!res.ok) throw new Error(res.status);
const all = await res.json();
rarity3Cards = all.filter((c) => parseInt(c.rarity) === 3);
// Fallback: wenn keine rarity-3 vorhanden alle nehmen
if (!rarity3Cards.length) rarity3Cards = all;
} catch (e) {
console.error("Rarity-3 Karten laden fehlgeschlagen", e);
}
}
function clearWoodIntervals() {
Object.values(woodIntervals).forEach((id) => clearInterval(id));
woodIntervals = {};
woodLocked = {};
}
function resetWood() {
clearWoodIntervals();
isWoodSpinning = false;
woodRevealed = false;
const inner = body.querySelector(`#wood-slot-0 .booster-slot-inner`);
inner.innerHTML = `
`;
body.querySelector(`#wood-slot-0`).classList.remove("revealed", "spinning");
const btn = body.querySelector("#wood-btn");
const stamp = body.querySelector("#wood-stamp");
btn.classList.remove("used");
btn.style.opacity = "1";
btn.style.cursor = "pointer";
if (stamp) stamp.style.display = "";
body.querySelector("#wood-hint").textContent = "100 Holz spenden";
preloadRarity3();
}
function startWoodSlot(index) {
woodLocked[index] = false;
const slot = body.querySelector(`#wood-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.add("spinning");
const iv = setInterval(() => {
if (!rarity3Cards.length || woodLocked[index]) return;
const rnd = rarity3Cards[Math.floor(Math.random() * rarity3Cards.length)];
inner.innerHTML = cardHTML(rnd, true);
}, 150);
woodIntervals[index] = iv;
}
function revealWoodSlot(index, card) {
woodLocked[index] = true;
clearInterval(woodIntervals[index]);
delete woodIntervals[index];
const slot = body.querySelector(`#wood-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.remove("spinning");
slot.classList.add("revealed");
inner.innerHTML = cardHTML(card, true);
setTimeout(() => {
if (slot.classList.contains("revealed"))
inner.innerHTML = cardHTML(card, true);
}, 100);
}
body.querySelector("#wood-btn").addEventListener("click", async () => {
if (isWoodSpinning) return;
if (!rarity3Cards.length) await preloadRarity3();
if (!rarity3Cards.length) return;
isWoodSpinning = true;
const btn = body.querySelector("#wood-btn");
const stamp = body.querySelector("#wood-stamp");
btn.classList.add("used");
btn.style.opacity = "0.35";
btn.style.cursor = "default";
if (stamp) stamp.style.display = "none";
body.querySelector("#wood-hint").textContent = "Wird verarbeitet...";
const backBtn = body.querySelector("#wood-back-btn");
backBtn.style.opacity = "0.35";
backBtn.style.cursor = "not-allowed";
// API aufrufen – 100 Holz abziehen + Karte ziehen
let drawnCard = null;
try {
const res = await fetch("/api/booster/wood-donate", { method: "POST" });
const data = await res.json();
if (!res.ok) {
body.querySelector("#wood-hint").textContent = data.error || "Fehler";
isWoodSpinning = false;
btn.classList.remove("used");
btn.style.opacity = "1";
btn.style.cursor = "pointer";
backBtn.style.opacity = "1";
backBtn.style.cursor = "pointer";
return;
}
drawnCard = data.card;
} catch (e) {
console.error("Holz-Spenden fehlgeschlagen", e);
resetWood();
return;
}
// Nur Slot 0 starten
startWoodSlot(0);
// Nach 5 Sekunden enthüllen
setTimeout(() => {
revealWoodSlot(0, drawnCard);
body.querySelector("#wood-hint").textContent = "Karte erhalten! ✓";
isWoodSpinning = false;
woodRevealed = true;
markDailyComplete(4);
backBtn.style.opacity = "1";
backBtn.style.cursor = "pointer";
}, 5000);
});
preloadRarity3();
/* ================================
Gold-Spenden Zustand
================================ */
let isGoldSpinning = false;
let goldRevealed = false;
let goldIntervals = {};
let goldLocked = {};
function clearGoldIntervals() {
Object.values(goldIntervals).forEach((id) => clearInterval(id));
goldIntervals = {};
goldLocked = {};
}
function resetGold() {
clearGoldIntervals();
isGoldSpinning = false;
goldRevealed = false;
const inner = body.querySelector(`#gold-slot-0 .booster-slot-inner`);
inner.innerHTML = `
`;
body.querySelector(`#gold-slot-0`).classList.remove("revealed", "spinning");
const btn = body.querySelector("#gold-btn");
const stamp = body.querySelector("#gold-stamp");
btn.classList.remove("used");
btn.style.opacity = "1";
btn.style.cursor = "pointer";
if (stamp) stamp.style.display = "";
body.querySelector("#gold-hint").textContent = "100 Gold spenden";
preloadRarity3();
}
function startGoldSlot(index) {
goldLocked[index] = false;
const slot = body.querySelector(`#gold-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.add("spinning");
const iv = setInterval(() => {
if (!rarity3Cards.length || goldLocked[index]) return;
const rnd = rarity3Cards[Math.floor(Math.random() * rarity3Cards.length)];
inner.innerHTML = cardHTML(rnd, true);
}, 150);
goldIntervals[index] = iv;
}
function revealGoldSlot(index, card) {
goldLocked[index] = true;
clearInterval(goldIntervals[index]);
delete goldIntervals[index];
const slot = body.querySelector(`#gold-slot-${index}`);
const inner = slot.querySelector(".booster-slot-inner");
slot.classList.remove("spinning");
slot.classList.add("revealed");
inner.innerHTML = cardHTML(card, true);
setTimeout(() => {
if (slot.classList.contains("revealed"))
inner.innerHTML = cardHTML(card, true);
}, 100);
}
body.querySelector("#gold-btn").addEventListener("click", async () => {
if (isGoldSpinning) return;
if (!rarity3Cards.length) await preloadRarity3();
if (!rarity3Cards.length) return;
isGoldSpinning = true;
const btn = body.querySelector("#gold-btn");
const stamp = body.querySelector("#gold-stamp");
btn.classList.add("used");
btn.style.opacity = "0.35";
btn.style.cursor = "default";
if (stamp) stamp.style.display = "none";
body.querySelector("#gold-hint").textContent = "Wird verarbeitet...";
const backBtn = body.querySelector("#gold-back-btn");
backBtn.style.opacity = "0.35";
backBtn.style.cursor = "not-allowed";
let drawnCard = null;
try {
const res = await fetch("/api/booster/gold-donate", { method: "POST" });
const data = await res.json();
if (!res.ok) {
body.querySelector("#gold-hint").textContent = data.error || "Fehler";
isGoldSpinning = false;
btn.classList.remove("used");
btn.style.opacity = "1";
btn.style.cursor = "pointer";
if (stamp) stamp.style.display = "";
backBtn.style.opacity = "1";
backBtn.style.cursor = "pointer";
return;
}
drawnCard = data.card;
} catch (e) {
console.error("Gold-Spenden fehlgeschlagen", e);
resetGold();
return;
}
startGoldSlot(0);
setTimeout(() => {
revealGoldSlot(0, drawnCard);
body.querySelector("#gold-hint").textContent = "Karte erhalten! ✓";
isGoldSpinning = false;
goldRevealed = true;
markDailyComplete(5);
backBtn.style.opacity = "1";
backBtn.style.cursor = "pointer";
}, 5000);
});
}