adrgr
This commit is contained in:
parent
c9dbd6c842
commit
13e9e1e0fb
@ -885,3 +885,48 @@ body {
|
|||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════
|
||||||
|
DRAG & DROP
|
||||||
|
═══════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* Hand-Karte wird gerade gezogen */
|
||||||
|
.hand-slot.dragging {
|
||||||
|
opacity: 0.35;
|
||||||
|
transform: scale(0.95);
|
||||||
|
border-color: rgba(240, 190, 60, 0.4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Board-Slot ist eine aktive Drop-Zone (freier Slot in meiner Zone) */
|
||||||
|
.card-slot.drop-zone-active {
|
||||||
|
border: 2px dashed rgba(240, 190, 60, 0.9) !important;
|
||||||
|
background: rgba(180, 130, 20, 0.22) !important;
|
||||||
|
animation: pulse-zone 1.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover: Karte schwebt über einem gültigen Slot */
|
||||||
|
.card-slot.drop-zone-hover {
|
||||||
|
border: 2px solid rgba(240, 210, 80, 1) !important;
|
||||||
|
background: rgba(200, 150, 30, 0.38) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px rgba(240, 200, 60, 0.4),
|
||||||
|
0 0 28px rgba(240, 190, 60, 0.55) !important;
|
||||||
|
transform: scale(1.06);
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-zone {
|
||||||
|
0%, 100% { box-shadow: 0 0 8px rgba(240, 190, 60, 0.25); }
|
||||||
|
50% { box-shadow: 0 0 22px rgba(240, 190, 60, 0.55); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hand-Slot nicht draggable (kein Cursor-Feedback) */
|
||||||
|
.hand-slot:not([draggable="true"]) {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.hand-slot[draggable="true"] {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
.hand-slot[draggable="true"]:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ function generateId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ═══════════════════════════════════════════════════════════
|
/* ═══════════════════════════════════════════════════════════
|
||||||
HELPER: Generisch für 2v2 UND 4v4 ohne 1v1
|
HELPER: Generisch für 2v2 UND 4v4
|
||||||
═══════════════════════════════════════════════════════════ */
|
═══════════════════════════════════════════════════════════ */
|
||||||
function getTeamMap(mode) {
|
function getTeamMap(mode) {
|
||||||
return mode === "4v4" ? teams4v4 : teams2v2;
|
return mode === "4v4" ? teams4v4 : teams2v2;
|
||||||
@ -415,9 +415,7 @@ function registerArenaHandlers(io, socket) {
|
|||||||
}
|
}
|
||||||
// Sonst: bereits guter Name gespeichert → nicht überschreiben
|
// Sonst: bereits guter Name gespeichert → nicht überschreiben
|
||||||
|
|
||||||
console.log(
|
console.log(`[1v1] Name gesetzt: slot=${slot}, name=${room.names[slot]}, playerName=${playerName}`);
|
||||||
`[1v1] Name gesetzt: slot=${slot}, name=${room.names[slot]}, playerName=${playerName}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
socket.join("arena_" + matchId);
|
socket.join("arena_" + matchId);
|
||||||
|
|
||||||
@ -472,6 +470,14 @@ function registerArenaHandlers(io, socket) {
|
|||||||
console.log(`[1v1] Zug: ${slot} → ${nextSlot} | Match ${matchId}`);
|
console.log(`[1v1] Zug: ${slot} → ${nextSlot} | Match ${matchId}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ── Karte gespielt → an Gegner weiterleiten ── */
|
||||||
|
socket.on("card_played", (data) => {
|
||||||
|
const { matchId } = data;
|
||||||
|
if (!matchId) return;
|
||||||
|
socket.to("arena_" + matchId).emit("card_played", data);
|
||||||
|
console.log(`[1v1] card_played: ${data.card?.name} → ${data.boardSlot} | Match ${matchId}`);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("end_turn_init", (data) => {
|
socket.on("end_turn_init", (data) => {
|
||||||
const { matchId, starterSlot } = data;
|
const { matchId, starterSlot } = data;
|
||||||
if (!matchId || !starterSlot) return;
|
if (!matchId || !starterSlot) return;
|
||||||
|
|||||||
@ -351,11 +351,14 @@
|
|||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<script>
|
<script>
|
||||||
/* ── Spielfeld aufbauen ─────────────────────────────── */
|
/* ── Spielfeld aufbauen ─────────────────────────────── */
|
||||||
["row1", "row2"].forEach((id) => {
|
["row1", "row2"].forEach((rowId) => {
|
||||||
const row = document.getElementById(id);
|
const row = document.getElementById(rowId);
|
||||||
for (let i = 1; i <= 11; i++) {
|
for (let i = 1; i <= 11; i++) {
|
||||||
const s = document.createElement("div");
|
const s = document.createElement("div");
|
||||||
s.className = "card-slot";
|
s.className = "card-slot";
|
||||||
|
s.dataset.row = rowId;
|
||||||
|
s.dataset.slotIndex = i;
|
||||||
|
s.id = `${rowId}-slot-${i}`;
|
||||||
s.innerHTML =
|
s.innerHTML =
|
||||||
'<span class="slot-icon">✦</span><span class="slot-num">' +
|
'<span class="slot-icon">✦</span><span class="slot-num">' +
|
||||||
i +
|
i +
|
||||||
@ -465,6 +468,15 @@
|
|||||||
</div>${statsHtml}${readyBadge}`;
|
</div>${statsHtml}${readyBadge}`;
|
||||||
|
|
||||||
slot.classList.toggle("hand-slot-ready", isReady);
|
slot.classList.toggle("hand-slot-ready", isReady);
|
||||||
|
|
||||||
|
// Drag & Drop: nur wenn Karte bereit UND mein Zug
|
||||||
|
if (isReady && isMyTurn) {
|
||||||
|
slot.draggable = true;
|
||||||
|
slot.dataset.cardSlotId = id;
|
||||||
|
} else {
|
||||||
|
slot.draggable = false;
|
||||||
|
delete slot.dataset.cardSlotId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Karte in Hand-Slot legen ──────────────────────── */
|
/* ── Karte in Hand-Slot legen ──────────────────────── */
|
||||||
@ -614,6 +626,8 @@
|
|||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.textContent = "Zug beenden";
|
btn.textContent = "Zug beenden";
|
||||||
btn.style.opacity = "1";
|
btn.style.opacity = "1";
|
||||||
|
// Draggable für bereite Karten aktivieren
|
||||||
|
setTimeout(() => handCardIds.forEach(id => renderHandSlot(id)), 50);
|
||||||
document.getElementById("turn-indicator")?.remove();
|
document.getElementById("turn-indicator")?.remove();
|
||||||
startTurnTimer();
|
startTurnTimer();
|
||||||
// Watchdog für wartenden Spieler abbrechen falls vorhanden
|
// Watchdog für wartenden Spieler abbrechen falls vorhanden
|
||||||
@ -625,6 +639,8 @@
|
|||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.style.opacity = "0.4";
|
btn.style.opacity = "0.4";
|
||||||
stopTurnTimer();
|
stopTurnTimer();
|
||||||
|
// Draggable deaktivieren
|
||||||
|
handCardIds.forEach(id => { const s = document.getElementById(id); if(s){s.draggable=false;delete s.dataset.cardSlotId;} });
|
||||||
showTurnIndicator();
|
showTurnIndicator();
|
||||||
// Watchdog: falls nach 26s kein turn_change → eigenen Zug erzwingen
|
// Watchdog: falls nach 26s kein turn_change → eigenen Zug erzwingen
|
||||||
if (window._waitWatchdog) clearTimeout(window._waitWatchdog);
|
if (window._waitWatchdog) clearTimeout(window._waitWatchdog);
|
||||||
@ -1184,6 +1200,154 @@
|
|||||||
r.readAsDataURL(file);
|
r.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ══════════════════════════════════════════════════════
|
||||||
|
DRAG & DROP – Karte aus Hand auf Board legen
|
||||||
|
══════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
// Board-State: welche Karten liegen auf welchem Slot
|
||||||
|
const boardState = {}; // key: "row1-slot-3", value: card object
|
||||||
|
|
||||||
|
/* Welche Slot-Indizes darf ich bespielen? */
|
||||||
|
function isMyZone(slotIndex) {
|
||||||
|
const idx = Number(slotIndex);
|
||||||
|
return amIPlayer1 ? idx <= 3 : idx >= 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop-Zones aktivieren / deaktivieren */
|
||||||
|
function setDropZones(active) {
|
||||||
|
document.querySelectorAll(".card-slot").forEach((slot) => {
|
||||||
|
const idx = Number(slot.dataset.slotIndex);
|
||||||
|
if (!isMyZone(idx)) return;
|
||||||
|
if (active && !boardState[slot.id]) {
|
||||||
|
slot.classList.add("drop-zone-active");
|
||||||
|
} else {
|
||||||
|
slot.classList.remove("drop-zone-active", "drop-zone-hover");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Karte auf Board rendern */
|
||||||
|
function renderCardOnBoard(slotEl, card) {
|
||||||
|
const atkVal = card.attack ?? null;
|
||||||
|
const defVal = card.defends ?? null;
|
||||||
|
const cdVal = card.cooldown ?? null;
|
||||||
|
const rngVal = card.range ?? null;
|
||||||
|
const rceVal = card.race ?? null;
|
||||||
|
const statsHtml = `
|
||||||
|
<div class="card-stat-overlay">
|
||||||
|
${atkVal != null ? `<span class="cs-atk">${atkVal}</span>` : ""}
|
||||||
|
${defVal != null ? `<span class="cs-def">${defVal}</span>` : ""}
|
||||||
|
${cdVal != null ? `<span class="cs-cd">${cdVal}</span>` : ""}
|
||||||
|
${rngVal != null ? `<span class="cs-range">${SVG_RANGE} ${rngVal}</span>` : ""}
|
||||||
|
${rceVal != null ? `<span class="cs-race">${SVG_RACE} ${rceVal}</span>` : ""}
|
||||||
|
</div>`;
|
||||||
|
slotEl.classList.add("slot-occupied");
|
||||||
|
slotEl.innerHTML = card.image
|
||||||
|
? `<img src="/images/cards/${card.image}"
|
||||||
|
onerror="this.src='/images/items/rueckseite.png'"
|
||||||
|
title="${card.name}"
|
||||||
|
style="width:100%;height:100%;object-fit:cover;border-radius:7px;display:block;">
|
||||||
|
${statsHtml}`
|
||||||
|
: `<div style="display:flex;flex-direction:column;align-items:center;
|
||||||
|
justify-content:center;height:100%;gap:4px;font-family:Cinzel,serif;padding:4px;">
|
||||||
|
<span style="font-size:18px;">⚔️</span>
|
||||||
|
<span style="font-size:9px;color:#f0d9a6;text-align:center;">${card.name}</span>
|
||||||
|
</div>${statsHtml}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Drag-Events auf Hand-Slots (delegiert) ── */
|
||||||
|
let draggedCardSlotId = null;
|
||||||
|
|
||||||
|
document.getElementById("handArea").addEventListener("dragstart", (e) => {
|
||||||
|
const slot = e.target.closest("[data-card-slot-id]");
|
||||||
|
if (!slot || !isMyTurn) { e.preventDefault(); return; }
|
||||||
|
draggedCardSlotId = slot.dataset.cardSlotId;
|
||||||
|
slot.classList.add("dragging");
|
||||||
|
e.dataTransfer.effectAllowed = "move";
|
||||||
|
e.dataTransfer.setData("text/plain", draggedCardSlotId);
|
||||||
|
// Drop-Zones einblenden
|
||||||
|
setTimeout(() => setDropZones(true), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("handArea").addEventListener("dragend", (e) => {
|
||||||
|
const slot = e.target.closest("[data-card-slot-id]");
|
||||||
|
if (slot) slot.classList.remove("dragging");
|
||||||
|
setDropZones(false);
|
||||||
|
draggedCardSlotId = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Drop-Events auf Board-Slots (delegiert) ── */
|
||||||
|
["row1", "row2"].forEach((rowId) => {
|
||||||
|
const row = document.getElementById(rowId);
|
||||||
|
|
||||||
|
row.addEventListener("dragover", (e) => {
|
||||||
|
const slot = e.target.closest(".card-slot");
|
||||||
|
if (!slot) return;
|
||||||
|
const idx = Number(slot.dataset.slotIndex);
|
||||||
|
if (!isMyZone(idx) || boardState[slot.id]) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = "move";
|
||||||
|
// Hover-Highlight
|
||||||
|
row.querySelectorAll(".drop-zone-hover").forEach(s => s.classList.remove("drop-zone-hover"));
|
||||||
|
slot.classList.add("drop-zone-hover");
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener("dragleave", (e) => {
|
||||||
|
const slot = e.target.closest(".card-slot");
|
||||||
|
if (slot) slot.classList.remove("drop-zone-hover");
|
||||||
|
});
|
||||||
|
|
||||||
|
row.addEventListener("drop", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const slot = e.target.closest(".card-slot");
|
||||||
|
if (!slot) return;
|
||||||
|
const idx = Number(slot.dataset.slotIndex);
|
||||||
|
if (!isMyZone(idx) || boardState[slot.id]) return;
|
||||||
|
if (!draggedCardSlotId) return;
|
||||||
|
|
||||||
|
const cardState = handSlotState[draggedCardSlotId];
|
||||||
|
if (!cardState || cardState.currentCd > 0) return;
|
||||||
|
|
||||||
|
// Karte vom Hand-Slot entfernen
|
||||||
|
handSlotState[draggedCardSlotId] = null;
|
||||||
|
renderHandSlot(draggedCardSlotId);
|
||||||
|
|
||||||
|
// Karte auf Board-Slot legen
|
||||||
|
boardState[slot.id] = cardState.card;
|
||||||
|
renderCardOnBoard(slot, cardState.card);
|
||||||
|
slot.classList.remove("drop-zone-active", "drop-zone-hover");
|
||||||
|
|
||||||
|
// Nächste Karte nachladen
|
||||||
|
drawNextCard();
|
||||||
|
|
||||||
|
// Gegner & Server informieren
|
||||||
|
socket.emit("card_played", {
|
||||||
|
matchId,
|
||||||
|
slot: mySlot,
|
||||||
|
boardSlot: slot.id,
|
||||||
|
row: slot.dataset.row,
|
||||||
|
slotIndex: idx,
|
||||||
|
card: cardState.card,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[1v1] Karte gespielt: ${cardState.card.name} → ${slot.id}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/* ── Gegner hat Karte gespielt → auf Board anzeigen ── */
|
||||||
|
socket.on("card_played", (data) => {
|
||||||
|
if (data.slot === mySlot) return; // eigene Aktion, bereits lokal gesetzt
|
||||||
|
const slotEl = document.getElementById(data.boardSlot);
|
||||||
|
if (!slotEl) return;
|
||||||
|
boardState[data.boardSlot] = data.card;
|
||||||
|
renderCardOnBoard(slotEl, data.card);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Wenn Zug wechselt: draggable aktualisieren */
|
||||||
|
const _origSetTurnState = setTurnState;
|
||||||
|
// setTurnState ist bereits definiert, wir patchen es nach
|
||||||
|
|
||||||
/* ── Event-Listener ─────────────────────────────────── */
|
/* ── Event-Listener ─────────────────────────────────── */
|
||||||
document
|
document
|
||||||
.getElementById("bereit-btn")
|
.getElementById("bereit-btn")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user