diff --git a/public/css/1v1.css b/public/css/1v1.css
index a067f78..f3bce43 100644
--- a/public/css/1v1.css
+++ b/public/css/1v1.css
@@ -659,6 +659,44 @@ body {
border-color: rgba(255, 215, 80, 0.6);
}
+/* Hand-Slot: Karte ist spielbereit (CD = 0) */
+.hand-slot.hand-slot-ready {
+ border-color: rgba(80, 220, 80, 0.9) !important;
+ border-style: solid !important;
+ box-shadow:
+ 0 0 calc(var(--s) * 18) rgba(60, 200, 60, 0.5),
+ 0 calc(var(--s) * 4) calc(var(--s) * 12) rgba(0,0,0,0.4);
+}
+
+/* CD-Badge wenn Karte spielbereit */
+.cs-cd.cs-cd-ready {
+ background: rgba(20, 140, 20, 0.9);
+ border-color: #60ff60;
+ color: #b0ffb0;
+ font-size: calc(var(--s) * 7);
+}
+
+/* "SPIELEN" Badge oben-mittig wenn CD = 0 */
+.hand-slot-ready-badge {
+ position: absolute;
+ top: calc(var(--s) * 4);
+ left: 50%;
+ transform: translateX(-50%);
+ background: linear-gradient(135deg, rgba(20,120,20,0.92), rgba(10,80,10,0.92));
+ border: 1px solid #60d060;
+ border-radius: calc(var(--s) * 4);
+ color: #a0ffa0;
+ font-family: "Cinzel", serif;
+ font-size: calc(var(--s) * 7);
+ font-weight: bold;
+ letter-spacing: calc(var(--s) * 1);
+ padding: calc(var(--s) * 2) calc(var(--s) * 5);
+ pointer-events: none;
+ z-index: 6;
+ white-space: nowrap;
+ text-shadow: 0 1px 4px rgba(0,0,0,0.8);
+}
+
/* Spielfeld-Sperre */
#board-lock-overlay {
position: absolute;
diff --git a/views/1v1-battlefield.ejs b/views/1v1-battlefield.ejs
index a47a1eb..f90ba25 100644
--- a/views/1v1-battlefield.ejs
+++ b/views/1v1-battlefield.ejs
@@ -224,14 +224,13 @@
—`;
hand.appendChild(deckSlot);
- // ── Slots 2–4: aufgedeckte Handkarten ────────────────
+ // ── Slots 2–4: aufgedeckte Handkarten (leer bis API lädt) ───
const handCardIds = ["hand-card-1", "hand-card-2", "hand-card-3"];
handCardIds.forEach(id => {
const s = document.createElement("div");
s.className = "hand-slot hand-slot-card";
s.id = id;
- s.innerHTML = `
`;
+ s.innerHTML = '🃏';
hand.appendChild(s);
});
@@ -243,73 +242,133 @@
hand.appendChild(s);
}
- // ── Deck via API laden und Karten anzeigen ────────────
+ // ══════════════════════════════════════════════════════
+ // HAND & DECK SYSTEM
+ // ══════════════════════════════════════════════════════
+
+ let deckQueue = [];
+
+ // State pro Slot: { card, currentCd } oder null = leer
+ const handSlotState = {};
+ handCardIds.forEach(id => { handSlotState[id] = null; });
+
+ /* ── Slot rendern ──────────────────────────────────── */
+ function renderHandSlot(id) {
+ const slot = document.getElementById(id);
+ const state = handSlotState[id];
+ if (!slot) return;
+
+ if (!state) {
+ slot.innerHTML = '🃏';
+ slot.classList.remove("hand-slot-ready");
+ return;
+ }
+
+ const { card, currentCd } = state;
+ const isReady = currentCd <= 0;
+
+ const atkVal = card.attack ?? null;
+ const defVal = card.defends ?? null;
+
+ const statsHtml = `
+
+ ${atkVal != null ? `${atkVal}` : ""}
+ ${defVal != null ? `${defVal}` : ""}
+ ${card.cooldown != null
+ ? `${isReady ? "✓" : currentCd}`
+ : ""}
+
`;
+
+ const readyBadge = isReady
+ ? `SPIELEN
`
+ : "";
+
+ slot.innerHTML = card.image
+ ? `
+ ${statsHtml}${readyBadge}`
+ : `
+ ⚔️
+ ${card.name}
+
${statsHtml}${readyBadge}`;
+
+ slot.classList.toggle("hand-slot-ready", isReady);
+ }
+
+ /* ── Karte in Hand-Slot legen ──────────────────────── */
+ function setHandSlot(id, card) {
+ handSlotState[id] = { card, currentCd: card.cooldown ?? 0 };
+ renderHandSlot(id);
+ }
+
+ /* ── Karte vom Deck ziehen ─────────────────────────── */
+ function drawNextCard() {
+ if (deckQueue.length === 0) return;
+ const freeSlot = handCardIds.find(id => handSlotState[id] === null);
+ if (!freeSlot) return; // alle Slots belegt → Karte bleibt im Deck
+ const card = deckQueue.shift();
+ setHandSlot(freeSlot, card);
+ const countEl = document.getElementById("deck-count");
+ if (countEl) countEl.textContent = deckQueue.length;
+ }
+
+ /* ── Cooldowns beim Zug-Ende reduzieren ────────────── */
+ function tickHandCooldowns() {
+ handCardIds.forEach(id => {
+ const state = handSlotState[id];
+ if (!state) return;
+ if (state.currentCd > 0) state.currentCd--;
+ renderHandSlot(id);
+ });
+ }
+
+ /* ── Deck laden ────────────────────────────────────── */
(async () => {
try {
- // Deck-ID aus URL-Parameter (von arena.js mitgegeben)
const urlP = new URLSearchParams(window.location.search);
const deckId = urlP.get("deck") || sessionStorage.getItem("selectedDeckId");
if (!deckId) return;
- const [deckRes, cardsRes] = await Promise.all([
- fetch("/api/decks"),
- fetch("/api/decks/" + deckId + "/cards")
- ]);
- if (!deckRes.ok || !cardsRes.ok) return;
+ const cardsRes = await fetch("/api/decks/" + deckId + "/cards");
+ if (!cardsRes.ok) return;
+ const cards = await cardsRes.json();
- const decks = await deckRes.json();
- const deck = decks.find(d => d.id == deckId);
- if (deck) {
- const countEl = document.getElementById("deck-count");
- if (countEl) countEl.textContent = deck.card_count;
+ // Karten nach amount auffalten
+ const expanded = [];
+ cards.forEach(card => {
+ for (let i = 0; i < (card.amount ?? 1); i++) expanded.push(card);
+ });
+
+ // Mischen (Fisher-Yates)
+ for (let i = expanded.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [expanded[i], expanded[j]] = [expanded[j], expanded[i]];
}
- const cards = await cardsRes.json();
- // DEBUG: alle Karten-Felder anzeigen
- console.log("[Battlefield] Karten aus API:", cards.slice(0,3).map(c => ({
- name: c.name, attack: c.attack, defends: c.defends, cooldown: c.cooldown
- })));
- // Erste 3 Karten aufgedeckt anzeigen
+ // Erste 3 in die Hand
handCardIds.forEach((id, i) => {
- const card = cards[i];
- const slot = document.getElementById(id);
- if (!slot || !card) return;
-
- // Feldnamen aus der API (attack, defends, cooldown)
- const atkVal = card.attack ?? null;
- const defVal = card.defends ?? null;
- const cdVal = card.cooldown ?? null;
-
- const hasAtk = atkVal != null;
- const hasDef = defVal != null;
- const hasCd = cdVal != null;
- const showStats = hasAtk || hasDef || hasCd;
-
- const statsHtml = showStats ? `
-
- ${hasAtk ? `${atkVal}` : ""}
- ${hasDef ? `${defVal}` : ""}
- ${hasCd ? `${cdVal}` : ""}
-
` : "";
-
- slot.innerHTML = card.image
- ? `
- ${statsHtml}`
- : `
- ⚔️
- ${card.name}
-
- ${statsHtml}`;
+ if (expanded[i]) setHandSlot(id, expanded[i]);
});
+
+ // Rest als Deck-Queue
+ deckQueue = expanded.slice(3);
+ const countEl = document.getElementById("deck-count");
+ if (countEl) countEl.textContent = deckQueue.length;
+
} catch(e) {
console.error("[Battlefield] Deck laden:", e);
}
})();
+ /* ── Zug beenden: CD ticken + Karte ziehen ─────────── */
+ document.getElementById("end-turn-btn")?.addEventListener("click", () => {
+ tickHandCooldowns();
+ drawNextCard();
+ });
+
/* ── Hilfsfunktion: Karte mit Stats in einen Slot rendern ── */
function renderCardInSlot(slot, card) {
if (!slot || !card) return;