hj,c
This commit is contained in:
parent
7b999906fb
commit
c6234d953d
@ -3,8 +3,8 @@ const CARDS_PER_PAGE = 18;
|
||||
let currentGroupId = null;
|
||||
let currentPage = 1;
|
||||
let currentDeckId = null;
|
||||
let deckCards = []; // [{card_id, level, amount}]
|
||||
let userCardsCache = []; // aktuelle Seite: [{card_id, level, amount, name, image, attack, defense}]
|
||||
let deckCards = []; // [{card_id, amount}]
|
||||
let userCardsCache = []; // aktuelle Seite: [{card_id, amount, name, image, rarity, attack, defense}]
|
||||
let decks = []; // [{id, name}]
|
||||
|
||||
|
||||
@ -19,10 +19,12 @@ const RARITY_CRYSTALS = {
|
||||
"7": "pinker-cristal.png",
|
||||
};
|
||||
|
||||
function rarityImg(rarity, size = 16) {
|
||||
function rarityImgs(rarity, size = 14) {
|
||||
const file = RARITY_CRYSTALS[String(rarity)];
|
||||
if (!file) return "";
|
||||
return `<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));">`;
|
||||
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);
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════
|
||||
@ -415,14 +417,19 @@ function renderShell() {
|
||||
}
|
||||
.kd-deck-count-indeck { font-family: "Cinzel", serif; font-size: 13px; color: #88cc44; font-weight: bold; }
|
||||
|
||||
/* ── Rarity Kristall ────────────────────── */
|
||||
/* ── Rarity Kristalle ───────────────────── */
|
||||
.kd-rarity {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 4px;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
line-height: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1px;
|
||||
padding: 3px 4px;
|
||||
background: rgba(0,0,0,0.82);
|
||||
border-top: 1px solid #3a2810;
|
||||
flex-shrink: 0;
|
||||
min-height: 20px;
|
||||
border-radius: 0 0 6px 6px;
|
||||
}
|
||||
|
||||
/* ── Empty / Loading States ──────────────── */
|
||||
@ -612,18 +619,18 @@ async function addCardToDeck(card) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Level > 5: max. 1 Exemplar im Deck
|
||||
if (card.level > 5) {
|
||||
const already = deckCards.find(c => c.card_id === card.card_id && c.level === card.level);
|
||||
// Rarity > 5: max. 1 Exemplar im Deck
|
||||
if (parseInt(card.rarity) > 5) {
|
||||
const already = deckCards.find(c => c.card_id === card.card_id);
|
||||
if (already) {
|
||||
showToast("Karten ab Level 6 dürfen nur 1x im Deck sein.");
|
||||
showToast("Karten ab Rarity 6 dürfen nur 1x im Deck sein.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Nicht mehr als vorhanden besitzt
|
||||
const owned = card.amount;
|
||||
const inDeck = getDeckCount(card.card_id, card.level);
|
||||
const inDeck = getDeckCount(card.card_id);
|
||||
if (inDeck >= owned) {
|
||||
showToast("Du hast keine weiteren Exemplare dieser Karte.");
|
||||
return;
|
||||
@ -633,7 +640,7 @@ async function addCardToDeck(card) {
|
||||
const res = await fetch(`/api/decks/${currentDeckId}/cards`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ card_id: card.card_id, level: card.level }),
|
||||
body: JSON.stringify({ card_id: card.card_id }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
@ -655,7 +662,7 @@ async function removeCardFromDeck(card) {
|
||||
const res = await fetch(`/api/decks/${currentDeckId}/cards`, {
|
||||
method: "DELETE",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ card_id: card.card_id, level: card.level }),
|
||||
body: JSON.stringify({ card_id: card.card_id }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const err = await res.json();
|
||||
@ -712,14 +719,13 @@ function renderCollectionGrid(grid, cards) {
|
||||
const totalInDeck = deckCards.reduce((s, c) => s + c.amount, 0);
|
||||
|
||||
grid.innerHTML = cards.map(c => {
|
||||
const inDeck = getDeckCount(c.card_id, c.level);
|
||||
const inDeck = getDeckCount(c.card_id);
|
||||
const maxed = isMaxedOut(c, inDeck, totalInDeck);
|
||||
return `
|
||||
<div class="kd-card ${maxed ? "kd-card-maxed" : ""}"
|
||||
data-card-id="${c.card_id}" data-level="${c.level}"
|
||||
title="${c.name} (Level ${c.level})">
|
||||
data-card-id="${c.card_id}"
|
||||
title="${c.name}">
|
||||
<div class="kd-card-name">${c.name}</div>
|
||||
${c.rarity ? `<span class="kd-rarity">${rarityImg(c.rarity, 15)}</span>` : ""}
|
||||
<img src="/images/cards/${c.image}" alt="${c.name}"
|
||||
onerror="this.src='/images/avatar_placeholder.svg'">
|
||||
${c.attack != null ? `<span class="kd-stat-atk">${c.attack}</span>` : ""}
|
||||
@ -728,14 +734,13 @@ function renderCollectionGrid(grid, cards) {
|
||||
<span class="kd-count-owned" title="Besitzt du">${c.amount}×</span>
|
||||
<span class="kd-count-deck" title="Im Deck">🃏 ${inDeck}</span>
|
||||
</div>
|
||||
${c.rarity ? `<div class="kd-rarity">${rarityImgs(c.rarity, 13)}</div>` : ""}
|
||||
</div>`;
|
||||
}).join("");
|
||||
|
||||
grid.querySelectorAll(".kd-card:not(.kd-card-maxed)").forEach(el => {
|
||||
el.addEventListener("click", async () => {
|
||||
const card = cards.find(
|
||||
c => c.card_id === parseInt(el.dataset.cardId) && c.level === parseInt(el.dataset.level)
|
||||
);
|
||||
const card = cards.find(c => c.card_id === parseInt(el.dataset.cardId));
|
||||
if (card) await addCardToDeck(card);
|
||||
});
|
||||
});
|
||||
@ -751,11 +756,10 @@ function renderDeckGrid(grid, cards) {
|
||||
}
|
||||
// Owned-Anzahl aus userCardsCache auslesen
|
||||
grid.innerHTML = cards.map(c => {
|
||||
const ownedEntry = userCardsCache.find(u => u.card_id === c.card_id && u.level === c.level);
|
||||
const ownedEntry = userCardsCache.find(u => u.card_id === c.card_id);
|
||||
const ownedAmt = ownedEntry ? ownedEntry.amount : "?";
|
||||
return `
|
||||
<div class="kd-deck-card" data-card-id="${c.card_id}" data-level="${c.level}" title="Klicken zum Entfernen: ${c.name}">
|
||||
${c.rarity ? `<span class="kd-rarity">${rarityImg(c.rarity, 18)}</span>` : ""}
|
||||
<div class="kd-deck-card" data-card-id="${c.card_id}" title="Klicken zum Entfernen: ${c.name}">
|
||||
<img src="/images/cards/${c.image}" alt="${c.name}"
|
||||
onerror="this.src='/images/avatar_placeholder.svg'">
|
||||
<div class="kd-deck-card-footer">
|
||||
@ -764,14 +768,13 @@ function renderDeckGrid(grid, cards) {
|
||||
<div class="kd-deck-card-footer-counts">
|
||||
<span class="kd-deck-count-indeck" title="Im Deck">🃏 ${c.amount}</span>
|
||||
</div>
|
||||
${c.rarity ? `<div class="kd-rarity">${rarityImgs(c.rarity, 14)}</div>` : ""}
|
||||
</div>`;
|
||||
}).join("");
|
||||
|
||||
grid.querySelectorAll(".kd-deck-card").forEach(el => {
|
||||
el.addEventListener("click", async () => {
|
||||
const card = cards.find(
|
||||
c => c.card_id === parseInt(el.dataset.cardId) && c.level === parseInt(el.dataset.level)
|
||||
);
|
||||
const card = cards.find(c => c.card_id === parseInt(el.dataset.cardId));
|
||||
if (card) await removeCardFromDeck(card);
|
||||
});
|
||||
});
|
||||
@ -878,16 +881,16 @@ function showNewDeckModal() {
|
||||
/* ══════════════════════════════════════════════
|
||||
HELPERS
|
||||
══════════════════════════════════════════════ */
|
||||
function getDeckCount(cardId, level) {
|
||||
const entry = deckCards.find(c => c.card_id === cardId && c.level === level);
|
||||
function getDeckCount(cardId) {
|
||||
const entry = deckCards.find(c => c.card_id === cardId);
|
||||
return entry ? entry.amount : 0;
|
||||
}
|
||||
|
||||
function isMaxedOut(card, inDeck, totalInDeck) {
|
||||
if (!currentDeckId) return false;
|
||||
if (totalInDeck >= 30) return true;
|
||||
if (inDeck >= card.amount) return true; // mehr als besessen
|
||||
if (card.level > 5 && inDeck >= 1) return true; // Level > 5: nur 1x
|
||||
if (inDeck >= card.amount) return true; // mehr als besessen
|
||||
if (parseInt(card.rarity) > 5 && inDeck >= 1) return true; // Rarity > 5: nur 1x
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,6 @@ router.get("/user-cards", async (req, res) => {
|
||||
const [cards] = await db.query(
|
||||
`SELECT
|
||||
uc.card_id,
|
||||
uc.level,
|
||||
uc.amount,
|
||||
c.name,
|
||||
c.image,
|
||||
@ -86,9 +85,9 @@ router.get("/user-cards", async (req, res) => {
|
||||
FROM user_cards uc
|
||||
JOIN cards c ON c.id = uc.card_id
|
||||
JOIN card_groups cg ON cg.id = c.group_id
|
||||
LEFT JOIN card_levels cl ON cl.card_id = uc.card_id AND cl.level = uc.level
|
||||
LEFT JOIN card_levels cl ON cl.card_id = uc.card_id AND cl.level = 1
|
||||
WHERE uc.user_id = ? AND c.group_id = ?
|
||||
ORDER BY c.id, uc.level
|
||||
ORDER BY c.id
|
||||
LIMIT ? OFFSET ?`,
|
||||
[userId, group_id, parseInt(limit), parseInt(offset)]
|
||||
);
|
||||
@ -186,7 +185,6 @@ router.get("/decks/:id/cards", async (req, res) => {
|
||||
const [cards] = await db.query(
|
||||
`SELECT
|
||||
dc.card_id,
|
||||
dc.level,
|
||||
dc.amount,
|
||||
c.name,
|
||||
c.image,
|
||||
@ -195,9 +193,9 @@ router.get("/decks/:id/cards", async (req, res) => {
|
||||
cl.defense
|
||||
FROM deck_cards dc
|
||||
JOIN cards c ON c.id = dc.card_id
|
||||
LEFT JOIN card_levels cl ON cl.card_id = dc.card_id AND cl.level = dc.level
|
||||
LEFT JOIN card_levels cl ON cl.card_id = dc.card_id AND cl.level = 1
|
||||
WHERE dc.deck_id = ?
|
||||
ORDER BY c.name, dc.level`,
|
||||
ORDER BY c.name`,
|
||||
[deckId]
|
||||
);
|
||||
res.json(cards);
|
||||
@ -215,7 +213,7 @@ router.get("/decks/:id/cards", async (req, res) => {
|
||||
router.post("/decks/:id/cards", async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
const deckId = req.params.id;
|
||||
const { card_id, level = 1 } = req.body;
|
||||
const { card_id } = req.body;
|
||||
|
||||
if (!card_id) return res.status(400).json({ error: "card_id fehlt." });
|
||||
|
||||
@ -227,10 +225,10 @@ router.post("/decks/:id/cards", async (req, res) => {
|
||||
);
|
||||
if (!deck) return res.status(404).json({ error: "Deck nicht gefunden." });
|
||||
|
||||
// Besitzt der Spieler diese Karte mit diesem Level?
|
||||
// Besitzt der Spieler diese Karte?
|
||||
const [[owned]] = await db.query(
|
||||
"SELECT amount FROM user_cards WHERE user_id = ? AND card_id = ? AND level = ?",
|
||||
[userId, card_id, level]
|
||||
"SELECT amount FROM user_cards WHERE user_id = ? AND card_id = ?",
|
||||
[userId, card_id]
|
||||
);
|
||||
if (!owned) return res.status(400).json({ error: "Du besitzt diese Karte nicht." });
|
||||
|
||||
@ -243,14 +241,18 @@ router.post("/decks/:id/cards", async (req, res) => {
|
||||
return res.status(400).json({ error: "Deck ist voll (max. 30 Karten)." });
|
||||
}
|
||||
|
||||
// Level > 5: max. 1× im Deck
|
||||
// Rarity > 5: max. 1× im Deck
|
||||
const [[cardInfo]] = await db.query(
|
||||
"SELECT rarity FROM cards WHERE id = ?",
|
||||
[card_id]
|
||||
);
|
||||
const [[existing]] = await db.query(
|
||||
"SELECT amount FROM deck_cards WHERE deck_id = ? AND card_id = ? AND level = ?",
|
||||
[deckId, card_id, level]
|
||||
"SELECT amount FROM deck_cards WHERE deck_id = ? AND card_id = ?",
|
||||
[deckId, card_id]
|
||||
);
|
||||
|
||||
if (level > 5 && existing) {
|
||||
return res.status(400).json({ error: "Karten ab Level 6 dürfen nur einmal im Deck sein." });
|
||||
if (parseInt(cardInfo?.rarity) > 5 && existing) {
|
||||
return res.status(400).json({ error: "Karten ab Rarity 6 dürfen nur einmal im Deck sein." });
|
||||
}
|
||||
|
||||
// Nicht mehr einfügen als besessen
|
||||
@ -262,13 +264,13 @@ router.post("/decks/:id/cards", async (req, res) => {
|
||||
// Einfügen oder erhöhen
|
||||
if (existing) {
|
||||
await db.query(
|
||||
"UPDATE deck_cards SET amount = amount + 1 WHERE deck_id = ? AND card_id = ? AND level = ?",
|
||||
[deckId, card_id, level]
|
||||
"UPDATE deck_cards SET amount = amount + 1 WHERE deck_id = ? AND card_id = ?",
|
||||
[deckId, card_id]
|
||||
);
|
||||
} else {
|
||||
await db.query(
|
||||
"INSERT INTO deck_cards (deck_id, card_id, level, amount) VALUES (?, ?, ?, 1)",
|
||||
[deckId, card_id, level]
|
||||
"INSERT INTO deck_cards (deck_id, card_id, amount) VALUES (?, ?, 1)",
|
||||
[deckId, card_id]
|
||||
);
|
||||
}
|
||||
|
||||
@ -291,7 +293,7 @@ router.post("/decks/:id/cards", async (req, res) => {
|
||||
router.delete("/decks/:id/cards", async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
const deckId = req.params.id;
|
||||
const { card_id, level = 1 } = req.body;
|
||||
const { card_id } = req.body;
|
||||
|
||||
if (!card_id) return res.status(400).json({ error: "card_id fehlt." });
|
||||
|
||||
@ -304,8 +306,8 @@ router.delete("/decks/:id/cards", async (req, res) => {
|
||||
if (!deck) return res.status(404).json({ error: "Deck nicht gefunden." });
|
||||
|
||||
const [[entry]] = await db.query(
|
||||
"SELECT id, amount FROM deck_cards WHERE deck_id = ? AND card_id = ? AND level = ?",
|
||||
[deckId, card_id, level]
|
||||
"SELECT id, amount FROM deck_cards WHERE deck_id = ? AND card_id = ?",
|
||||
[deckId, card_id]
|
||||
);
|
||||
if (!entry) return res.status(404).json({ error: "Karte nicht im Deck." });
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user