/* ============================================================
public/js/gaststaette/glucksspiel.js
Glücksspiel-Tab in der Gaststätte
– Karten nach Fraktion anzeigen (wie Kartendeck)
– Karten gleicher ID ins Kombinier-Feld legen
– "Kombinieren"-Button
============================================================ */
const CARDS_PER_PAGE = 18;
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 gsRangeIcon() {
return ``;
}
function gsRaceIcon() {
return ``;
}
// State
let gs_groupId = null;
let gs_page = 1;
let gs_cards = [];
let gs_combineId = null;
let gs_combineCards = [];
let gs_initialized = false;
let gs_tavernLevel = 1;
let gs_required = 10; // Karten für 100%
function getRequired(tavernLevel) {
if (tavernLevel < 5) return 10;
if (tavernLevel < 10) return 8;
if (tavernLevel < 15) return 6;
if (tavernLevel < 20) return 5;
return 3;
}
function getChance(amount, required) {
return Math.min(Math.round((amount / required) * 100), 100);
}
/* ══════════════════════════════════════════════
INIT
══════════════════════════════════════════════ */
export async function loadGlucksspiel() {
const container = document.getElementById("gs-tab1");
if (!container) return;
// CSS einmalig laden
if (!document.querySelector('link[href="/css/glucksspiel.css"]')) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "/css/glucksspiel.css";
document.head.appendChild(link);
}
// Nur beim ersten Aufruf rendern
if (gs_initialized) return;
gs_initialized = true;
container.innerHTML = renderShell();
attachCombineEvents();
try {
// Gaststätte-Level und benötigte Karten laden
const infoRes = await fetch("/api/cards/combine-info");
if (infoRes.ok) {
const info = await infoRes.json();
gs_tavernLevel = info.tavernLevel;
gs_required = info.required;
}
const res = await fetch("/api/card-groups");
if (!res.ok) throw new Error();
const groups = await res.json();
renderTabs(groups);
if (groups.length) {
gs_groupId = groups[0].id;
await loadCards();
}
} catch {
container.innerHTML = `
Fehler beim Laden.
`;
}
}
/* ══════════════════════════════════════════════
SHELL
══════════════════════════════════════════════ */
function renderShell() {
return `
Klicke auf eine Karte links um sie hier zu platzieren.
Nur Karten gleicher Art können kombiniert werden.
`;
}
/* ══════════════════════════════════════════════
FRAKTIONS-TABS
══════════════════════════════════════════════ */
function renderTabs(groups) {
const container = document.getElementById("gs-faction-tabs");
if (!container) return;
container.innerHTML = groups
.map((g, i) => {
const color = g.color || "#6b4b2a";
return `
`;
})
.join("");
container.querySelectorAll(".gs-tab").forEach((btn) => {
btn.addEventListener("click", async () => {
const color = btn.style.borderColor;
container.querySelectorAll(".gs-tab").forEach((b) => {
b.classList.remove("gs-tab-active");
b.style.background = "";
});
btn.classList.add("gs-tab-active");
btn.style.background = `${color}33`;
gs_groupId = parseInt(btn.dataset.group);
gs_page = 1;
await loadCards();
});
});
}
/* ══════════════════════════════════════════════
KARTEN LADEN
══════════════════════════════════════════════ */
async function loadCards() {
const grid = document.getElementById("gs-grid");
const pagination = document.getElementById("gs-pagination");
if (!grid) return;
grid.innerHTML = `Lade Karten...
`;
if (pagination) pagination.innerHTML = "";
try {
const res = await fetch(
`/api/user-cards?group_id=${gs_groupId}&page=${gs_page}&limit=${CARDS_PER_PAGE}`
);
if (!res.ok) throw new Error();
const data = await res.json();
gs_cards = data.cards || [];
renderGrid(grid, gs_cards);
renderPagination(pagination, data.totalPages, data.total);
} catch {
grid.innerHTML = `Keine Karten gefunden.
`;
}
}
/* ══════════════════════════════════════════════
KARTEN-GRID RENDERN
══════════════════════════════════════════════ */
function renderGrid(grid, cards) {
if (!cards || !cards.length) {
grid.innerHTML = `Keine Karten in dieser Gruppe.
`;
return;
}
grid.innerHTML = cards.map((c) => {
// Karte deaktivieren wenn sie nicht zur aktuellen Kombination passt
const disabled = gs_combineId !== null && c.card_id !== gs_combineId;
// Anzahl bereits im Kombinier-Feld
const inField = gs_combineCards.filter(x => x.card_id === c.card_id).length;
const available = c.amount - inField;
return `

${c.attack != null ? `
${c.attack}` : ""}
${c.defends != null ? `
${c.defends}` : ""}
${c.cooldown != null ? `
${c.cooldown}` : ""}
${c.rarity ? `
${rarityImgs(c.rarity, 12)}
` : ""}
${(c.range != null || c.race != null) ? `
${c.range != null ? `${gsRangeIcon()} ${c.range}` : ""}
${c.race != null ? `${gsRaceIcon()} ${c.race}` : ""}
` : ""}
`;
}).join("");
grid.querySelectorAll(".gs-card:not(.gs-card-disabled)").forEach((el) => {
el.addEventListener("click", () => {
const card = cards.find((c) => c.card_id === parseInt(el.dataset.cardId));
if (!card) return;
const inField = gs_combineCards.filter(x => x.card_id === card.card_id).length;
if (inField >= card.amount) {
showToast("Keine weiteren Exemplare verfügbar.");
return;
}
addToField(card);
});
});
}
/* ══════════════════════════════════════════════
KARTE ZUM KOMBINIER-FELD HINZUFÜGEN
══════════════════════════════════════════════ */
function addToField(card) {
gs_combineId = card.card_id;
gs_combineCards.push({ ...card });
renderCombineField();
renderGrid(document.getElementById("gs-grid"), gs_cards);
}
/* ══════════════════════════════════════════════
KARTE AUS FELD ENTFERNEN
══════════════════════════════════════════════ */
function removeFromField(index) {
gs_combineCards.splice(index, 1);
if (gs_combineCards.length === 0) gs_combineId = null;
renderCombineField();
renderGrid(document.getElementById("gs-grid"), gs_cards);
}
/* ══════════════════════════════════════════════
KOMBINIER-FELD RENDERN
══════════════════════════════════════════════ */
function renderCombineField() {
const grid = document.getElementById("gs-combine-grid");
const hint = document.getElementById("gs-combine-hint");
const count = document.getElementById("gs-combine-count");
const btn = document.getElementById("gs-combine-btn");
if (!grid) return;
if (!gs_combineCards.length) {
grid.innerHTML = "";
hint.style.display = "block";
count.textContent = "";
btn.disabled = true;
return;
}
hint.style.display = "none";
const chance = getChance(gs_combineCards.length, gs_required);
const chanceColor = chance >= 100 ? "#44ff44" : chance >= 60 ? "#f0d060" : "#ff6644";
// Karten als gestapelter Stapel – jede Karte leicht versetzt
const offset = Math.min(6, 40 / gs_combineCards.length); // max 6px versatz
const stackHeight = offset * (gs_combineCards.length - 1); // extra höhe für stapel
const stackCards = gs_combineCards.map((c, i) => `

${c.attack != null ? `
${c.attack}` : ""}
${c.defends != null ? `
${c.defends}` : ""}
${c.cooldown != null ? `
${c.cooldown}` : ""}
${c.rarity ? `
${rarityImgs(c.rarity, 12)}
` : ""}
${(c.range != null || c.race != null) ? `
${c.range != null ? `${gsRangeIcon()} ${c.range}` : ""}
${c.race != null ? `${gsRaceIcon()} ${c.race}` : ""}
` : ""}
`).join("");
grid.innerHTML = `
${stackCards}
`;
// Klick auf Stapel → oberste Karte entfernen
grid.querySelector(".gs-card-stack").addEventListener("click", () => {
removeFromField(gs_combineCards.length - 1);
});
count.innerHTML = `
${gs_combineCards.length} Karte${gs_combineCards.length !== 1 ? "n" : ""} im Stapel
${chance}% Erfolgschance
${gs_required} Karten = 100% (Gaststätte Lv.${gs_tavernLevel})
`;
btn.disabled = gs_combineCards.length < 2;
}
/* ══════════════════════════════════════════════
KOMBINIEREN-BUTTON
══════════════════════════════════════════════ */
function attachCombineEvents() {
// Wird nach renderShell aufgerufen
setTimeout(() => {
const btn = document.getElementById("gs-combine-btn");
if (!btn) return;
btn.addEventListener("click", async () => {
if (gs_combineCards.length < 2) return;
btn.disabled = true;
btn.textContent = "Kombiniere...";
try {
const res = await fetch("/api/cards/combine", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
card_id: gs_combineId,
amount: gs_combineCards.length,
}),
});
const data = await res.json();
if (!res.ok) {
showToast(data.error || "Kombinieren fehlgeschlagen.");
btn.disabled = false;
btn.textContent = "Kombinieren";
return;
}
if (!data.success) {
// Misserfolg – Karten sind weg
showToast(`❌ Misserfolg! (${data.chance}% Chance) – Karten verbraucht.`);
gs_combineCards = [];
gs_combineId = null;
renderCombineField();
await loadCards();
} else {
// Erfolg – neue Karte zeigen
showToast(`✅ Erfolg! Du erhältst: ${data.reward.name} (Rarity ${data.reward.rarity})`);
gs_combineCards = [];
gs_combineId = null;
gs_initialized = false;
renderCombineField();
await loadCards();
}
} catch {
showToast("Verbindungsfehler.");
btn.disabled = false;
} finally {
btn.textContent = "Kombinieren";
}
});
}, 50);
}
/* ══════════════════════════════════════════════
PAGINATION
══════════════════════════════════════════════ */
function renderPagination(pagination, totalPages, total) {
if (!pagination || !totalPages || totalPages <= 1) return;
pagination.innerHTML = `
${Array.from({ length: totalPages }, (_, i) => i + 1)
.map((p) => ``)
.join("")}
${total} Karten`;
pagination.querySelector("#gs-prev")?.addEventListener("click", async () => {
if (gs_page > 1) { gs_page--; await loadCards(); }
});
pagination.querySelector("#gs-next")?.addEventListener("click", async () => {
if (gs_page < totalPages) { gs_page++; await loadCards(); }
});
pagination.querySelectorAll("[data-page]").forEach((btn) => {
btn.addEventListener("click", async () => {
gs_page = parseInt(btn.dataset.page);
await loadCards();
});
});
}
/* ══════════════════════════════════════════════
TOAST
══════════════════════════════════════════════ */
function showToast(msg) {
const existing = document.querySelector(".gs-toast");
if (existing) existing.remove();
const t = document.createElement("div");
t.className = "gs-toast";
t.textContent = msg;
t.style.cssText = `
position:fixed;bottom:80px;left:50%;transform:translateX(-50%);
background:linear-gradient(#4a2808,#2a1004);border:2px solid #8b6a3c;
border-radius:8px;color:#f0d9a6;font-family:"Cinzel",serif;font-size:13px;
padding:10px 22px;z-index:9999;pointer-events:none;`;
document.body.appendChild(t);
setTimeout(() => t.remove(), 2800);
}