adrgr
This commit is contained in:
parent
c9dbd6c842
commit
13e9e1e0fb
@ -885,3 +885,48 @@ body {
|
||||
opacity: 0.25;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* ============================================================
|
||||
sockets/arena.js
|
||||
sockets/arena.js
|
||||
1v1 Matchmaking + 2v2 Team-Lobby + 4v4 Team-Lobby
|
||||
inkl. Kick-Funktion für Team-Leader wurde getestet
|
||||
============================================================ */
|
||||
@ -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) {
|
||||
return mode === "4v4" ? teams4v4 : teams2v2;
|
||||
@ -415,9 +415,7 @@ function registerArenaHandlers(io, socket) {
|
||||
}
|
||||
// Sonst: bereits guter Name gespeichert → nicht überschreiben
|
||||
|
||||
console.log(
|
||||
`[1v1] Name gesetzt: slot=${slot}, name=${room.names[slot]}, playerName=${playerName}`,
|
||||
);
|
||||
console.log(`[1v1] Name gesetzt: slot=${slot}, name=${room.names[slot]}, playerName=${playerName}`);
|
||||
|
||||
socket.join("arena_" + matchId);
|
||||
|
||||
@ -472,6 +470,14 @@ function registerArenaHandlers(io, socket) {
|
||||
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) => {
|
||||
const { matchId, starterSlot } = data;
|
||||
if (!matchId || !starterSlot) return;
|
||||
|
||||
@ -351,11 +351,14 @@
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script>
|
||||
/* ── Spielfeld aufbauen ─────────────────────────────── */
|
||||
["row1", "row2"].forEach((id) => {
|
||||
const row = document.getElementById(id);
|
||||
["row1", "row2"].forEach((rowId) => {
|
||||
const row = document.getElementById(rowId);
|
||||
for (let i = 1; i <= 11; i++) {
|
||||
const s = document.createElement("div");
|
||||
s.className = "card-slot";
|
||||
s.dataset.row = rowId;
|
||||
s.dataset.slotIndex = i;
|
||||
s.id = `${rowId}-slot-${i}`;
|
||||
s.innerHTML =
|
||||
'<span class="slot-icon">✦</span><span class="slot-num">' +
|
||||
i +
|
||||
@ -465,6 +468,15 @@
|
||||
</div>${statsHtml}${readyBadge}`;
|
||||
|
||||
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 ──────────────────────── */
|
||||
@ -614,6 +626,8 @@
|
||||
btn.disabled = false;
|
||||
btn.textContent = "Zug beenden";
|
||||
btn.style.opacity = "1";
|
||||
// Draggable für bereite Karten aktivieren
|
||||
setTimeout(() => handCardIds.forEach(id => renderHandSlot(id)), 50);
|
||||
document.getElementById("turn-indicator")?.remove();
|
||||
startTurnTimer();
|
||||
// Watchdog für wartenden Spieler abbrechen falls vorhanden
|
||||
@ -625,6 +639,8 @@
|
||||
btn.disabled = true;
|
||||
btn.style.opacity = "0.4";
|
||||
stopTurnTimer();
|
||||
// Draggable deaktivieren
|
||||
handCardIds.forEach(id => { const s = document.getElementById(id); if(s){s.draggable=false;delete s.dataset.cardSlotId;} });
|
||||
showTurnIndicator();
|
||||
// Watchdog: falls nach 26s kein turn_change → eigenen Zug erzwingen
|
||||
if (window._waitWatchdog) clearTimeout(window._waitWatchdog);
|
||||
@ -1184,6 +1200,154 @@
|
||||
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 ─────────────────────────────────── */
|
||||
document
|
||||
.getElementById("bereit-btn")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user