⚙
🗺
📖
🏆
@@ -363,10 +373,139 @@
}
})();
- /* ── Zug beenden: CD ticken + Karte ziehen ─────────── */
+ /* ══════════════════════════════════════════════════════
+ ZUG-TIMER (20 Sekunden)
+ ══════════════════════════════════════════════════════ */
+ const TURN_SECONDS = 20;
+ const TT_CIRCUM = 2 * Math.PI * 18; // r=18
+ let turnTimerInt = null;
+ let turnSecsLeft = TURN_SECONDS;
+
+ function startTurnTimer() {
+ clearInterval(turnTimerInt);
+ turnSecsLeft = TURN_SECONDS;
+ updateTimerUI(turnSecsLeft);
+
+ const wrap = document.getElementById("turn-timer-wrap");
+ if (wrap) wrap.style.display = "flex";
+
+ turnTimerInt = setInterval(() => {
+ turnSecsLeft--;
+ updateTimerUI(turnSecsLeft);
+ if (turnSecsLeft <= 0) {
+ clearInterval(turnTimerInt);
+ // Zeit abgelaufen → Zug automatisch beenden
+ if (isMyTurn) {
+ tickHandCooldowns();
+ drawNextCard();
+ setTurnState(false);
+ socket.emit("end_turn", { matchId, slot: mySlot });
+ }
+ }
+ }, 1000);
+ }
+
+ function stopTurnTimer() {
+ clearInterval(turnTimerInt);
+ const wrap = document.getElementById("turn-timer-wrap");
+ if (wrap) wrap.style.display = "none";
+ }
+
+ function updateTimerUI(secs) {
+ const num = document.getElementById("turn-timer-num");
+ const circle = document.getElementById("tt-circle");
+ if (num) num.textContent = secs;
+ if (circle) {
+ circle.style.strokeDashoffset = TT_CIRCUM * (1 - secs / TURN_SECONDS);
+ circle.style.stroke = secs > 10 ? "#27ae60" : secs > 5 ? "#f39c12" : "#e74c3c";
+ if (secs <= 5) {
+ num.style.color = "#e74c3c";
+ num.style.animation = "tt-pulse 0.5s ease-in-out infinite";
+ } else {
+ num.style.color = "#fff";
+ num.style.animation = "none";
+ }
+ }
+ }
+
+ /* ══════════════════════════════════════════════════════
+ ZUG-SYSTEM
+ ══════════════════════════════════════════════════════ */
+
+ // Bin ich der linke Spieler? Wird nach Seitenzuweisung gesetzt.
+ let amILeftPlayer = null;
+ let isMyTurn = false;
+
+ function setTurnState(myTurn) {
+ isMyTurn = myTurn;
+ const btn = document.getElementById("end-turn-btn");
+ if (!btn) return;
+
+ if (myTurn) {
+ btn.disabled = false;
+ btn.textContent = "Zug beenden";
+ btn.style.opacity = "1";
+ document.getElementById("turn-indicator")?.remove();
+ startTurnTimer();
+ } else {
+ btn.disabled = true;
+ btn.style.opacity = "0.4";
+ stopTurnTimer();
+ showTurnIndicator();
+ }
+ }
+
+ function showTurnIndicator() {
+ document.getElementById("turn-indicator")?.remove();
+ const ind = document.createElement("div");
+ ind.id = "turn-indicator";
+ ind.style.cssText = `
+ position:fixed; bottom:calc(var(--s)*200); left:50%;
+ transform:translateX(-50%);
+ background:linear-gradient(135deg,rgba(20,20,40,0.95),rgba(10,10,25,0.95));
+ border:1px solid rgba(200,160,60,0.5);
+ border-radius:calc(var(--s)*8);
+ color:rgba(255,215,80,0.75);
+ font-family:'Cinzel',serif;
+ font-size:calc(var(--s)*11);
+ letter-spacing:calc(var(--s)*3);
+ padding:calc(var(--s)*8) calc(var(--s)*20);
+ z-index:100;
+ pointer-events:none;
+ text-transform:uppercase;
+ box-shadow:0 4px 20px rgba(0,0,0,0.6);
+ `;
+ ind.textContent = "⏳ Gegner ist am Zug";
+ document.body.appendChild(ind);
+ }
+
+ /* ── Zug beenden: CD ticken + Karte ziehen + Server informieren ── */
document.getElementById("end-turn-btn")?.addEventListener("click", () => {
+ if (!isMyTurn) return;
+ clearInterval(turnTimerInt);
tickHandCooldowns();
drawNextCard();
+ setTurnState(false);
+ socket.emit("end_turn", { matchId, slot: mySlot });
+ });
+
+ /* ── Server: Zugwechsel ──────────────────────────────── */
+ socket.on("turn_change", data => {
+ // data.activeSlot = "player1" | "player2"
+ const myActualSlot = amILeftPlayer === null
+ ? mySlot
+ : (amILeftPlayer ? "player1" : "player2");
+
+ const nowMyTurn = data.activeSlot === myActualSlot ||
+ data.activeSlot === mySlot;
+ setTurnState(nowMyTurn);
+ });
+
+ // Fallback falls Server kein turn_change sendet:
+ // eigenes end_turn bestätigen wir nach kurzer Verzögerung selbst
+ socket.on("turn_started", data => {
+ const nowMyTurn = data.slot === mySlot;
+ setTurnState(nowMyTurn);
});
/* ── Hilfsfunktion: Karte mit Stats in einen Slot rendern ── */
@@ -411,9 +550,14 @@
const socket = io();
/* Account-ID laden dann arena_join senden */
+ let myIngameName = null;
fetch("/arena/me")
.then(r => r.json())
.then(me => {
+ // Eigenen Namen sofort im Avatar anzeigen
+ myIngameName = me.ingame_name || me.username || me.name || me.id || "Ich";
+ const myNameEl = document.getElementById(amIPlayer1 ? "nameLeft" : "nameRight");
+ if (myNameEl) myNameEl.textContent = myIngameName;
socket.emit("arena_join", { matchId, slot: mySlot, accountId: me.id });
})
.catch(() => {
@@ -487,22 +631,34 @@
document.getElementById("nameLeft").textContent = leftName;
document.getElementById("nameRight").textContent = rightName;
- // Platzhalter-Icon durch Namen ersetzen falls noch kein Bild
+ // Platzhalter: nur den Ingame-Namen anzeigen, kein Icon
["avLeft", "avRight"].forEach(avId => {
const av = document.getElementById(avId);
const ph = av?.querySelector(".av-placeholder");
const name = avId === "avLeft" ? leftName : rightName;
if (ph) ph.innerHTML = `
-
- ${avId === "avLeft" ? "⚔" : "🛡"}
-
-
${name}
`;
+
${name}
`;
});
document.getElementById("board-lock-overlay")?.remove();
- document.getElementById("end-turn-btn").disabled = false;
+
+ // Festlegen ob ich der linke Spieler bin
+ // flip=false: player1=links, flip=true: player1=rechts
+ amILeftPlayer = flip ? (mySlot === "player2") : (mySlot === "player1");
+
+ // Linker Spieler beginnt
+ setTurnState(amILeftPlayer);
+ socket.emit("end_turn_init", { matchId, starterSlot: flip ? "player2" : "player1" });
}
});