This commit is contained in:
cay 2026-04-08 19:13:50 +01:00
parent 5940dfb01a
commit 3fd04e2211

View File

@ -35,6 +35,9 @@ export async function loadArena() {
<div class="arena-title" style="margin:0;"> 2v2 Team-Lobby</div> <div class="arena-title" style="margin:0;"> 2v2 Team-Lobby</div>
</div> </div>
<!-- Fehlermeldung auf dem 2v2-Screen -->
<div id="arena-2v2-error" style="display:none;"></div>
<!-- Team-Panel (erscheint nach Beitritt) --> <!-- Team-Panel (erscheint nach Beitritt) -->
<div id="arena-team-panel" style="display:none;"> <div id="arena-team-panel" style="display:none;">
<div class="arena-team-box"> <div class="arena-team-box">
@ -62,7 +65,7 @@ export async function loadArena() {
initArenaModes(); initArenaModes();
} }
/* ── Styles ────────────────────────────────────────────────────────────────── */ /* ── Styles ────────────────────────────────────────────────────────────────── */
function injectArenaStyles() { function injectArenaStyles() {
if (document.getElementById("arena-popup-styles")) return; if (document.getElementById("arena-popup-styles")) return;
@ -105,8 +108,19 @@ function injectArenaStyles() {
text-decoration: underline; text-decoration: underline;
animation: none; animation: none;
} }
#arena-queue-status .qs-cancel:hover { #arena-queue-status .qs-cancel:hover { color: #e74c3c; }
/* ── 2v2 Fehlermeldung ── */
#arena-2v2-error {
padding: 10px 14px;
border-radius: 8px;
background: rgba(231,76,60,0.12);
border: 1px solid rgba(231,76,60,0.4);
color: #e74c3c; color: #e74c3c;
font-family: "Cinzel", serif;
font-size: 12px;
text-align: center;
margin-bottom: 8px;
} }
/* ── Backdrop ── */ /* ── Backdrop ── */
@ -152,21 +166,6 @@ function injectArenaStyles() {
align-items: center; align-items: center;
gap: 10px; gap: 10px;
} }
#arena-popup-titlebar .ap-dots {
display: flex;
gap: 7px;
}
#arena-popup-titlebar .ap-dot {
width: 13px;
height: 13px;
border-radius: 50%;
cursor: pointer;
transition: filter 0.15s;
}
#arena-popup-titlebar .ap-dot:hover { filter: brightness(1.3); }
#arena-popup-titlebar .ap-dot.close { background: #e74c3c; border: 1px solid rgba(0,0,0,0.25); }
#arena-popup-titlebar .ap-dot.min { background: #f1c40f; border: 1px solid rgba(0,0,0,0.25); }
#arena-popup-titlebar .ap-dot.expand { background: #2ecc71; border: 1px solid rgba(0,0,0,0.25); }
#arena-popup-titlebar .ap-title { #arena-popup-titlebar .ap-title {
font-family: "Cinzel", serif; font-family: "Cinzel", serif;
font-size: 13px; font-size: 13px;
@ -381,13 +380,12 @@ function injectArenaStyles() {
document.head.appendChild(style); document.head.appendChild(style);
} }
/* ── Socket-Referenz holen ─────────────────────────────────────────────────── */ /* ── Socket-Referenz holen ─────────────────────────────────────────────────── */
function getSocket() { function getSocket() {
// Wird von der Haupt-App als window._socket bereitgestellt
return window._socket || null; return window._socket || null;
} }
/* ── Klick-Handler initialisieren ─────────────────────────────────────────── */ /* ── Klick-Handler initialisieren ─────────────────────────────────────────── */
function initArenaModes() { function initArenaModes() {
document.querySelectorAll(".arena-mode-card").forEach((card) => { document.querySelectorAll(".arena-mode-card").forEach((card) => {
card.addEventListener("click", () => { card.addEventListener("click", () => {
@ -406,31 +404,40 @@ function initArenaModes() {
/* /*
2v2 LOBBY 2v2 LOBBY
*/ */
let my2v2TeamId = null; let my2v2TeamId = null;
let myArenaData = null; let myArenaData = null;
async function open2v2Lobby() { async function open2v2Lobby() {
const socket = getSocket(); const socket = getSocket();
if (!socket) { showArenaError("Keine Verbindung zum Server."); return; }
// Screen ZUERST wechseln so sind Fehlermeldungen sichtbar
document.getElementById("arena-mode-screen").style.display = "none";
document.getElementById("arena-2v2-screen").style.display = "flex";
if (!socket) {
console.error("[2v2] Kein Socket window._socket:", window._socket);
show2v2Error("Keine Verbindung zum Server. Bitte Seite neu laden.");
return;
}
// Spielerdaten laden // Spielerdaten laden
if (!myArenaData) { if (!myArenaData) {
try { try {
console.log("[2v2] Lade Spielerdaten…");
const res = await fetch("/arena/me"); const res = await fetch("/arena/me");
if (!res.ok) throw new Error(res.status); if (!res.ok) throw new Error("HTTP " + res.status);
myArenaData = await res.json(); myArenaData = await res.json();
} catch { console.log("[2v2] Spielerdaten:", myArenaData);
showArenaError("Spielerdaten konnten nicht geladen werden."); } catch (err) {
console.error("[2v2] Spielerdaten Fehler:", err);
show2v2Error("Spielerdaten konnten nicht geladen werden. (Eingeloggt?)");
return; return;
} }
} }
// Screen wechseln
document.getElementById("arena-mode-screen").style.display = "none";
document.getElementById("arena-2v2-screen").style.display = "flex";
// Lobbyliste anfordern // Lobbyliste anfordern
socket.emit("get_2v2_lobbies"); socket.emit("get_2v2_lobbies");
console.log("[2v2] get_2v2_lobbies gesendet");
// Zurück-Button // Zurück-Button
document.getElementById("arena-2v2-back").onclick = () => { document.getElementById("arena-2v2-back").onclick = () => {
@ -442,6 +449,7 @@ async function open2v2Lobby() {
// Team erstellen // Team erstellen
document.getElementById("arena-create-team-btn").onclick = () => { document.getElementById("arena-create-team-btn").onclick = () => {
console.log("[2v2] Team erstellen geklickt sende create_2v2_team mit:", myArenaData);
socket.emit("create_2v2_team", myArenaData); socket.emit("create_2v2_team", myArenaData);
}; };
@ -454,12 +462,17 @@ async function open2v2Lobby() {
socket.off("match_found_2v2"); socket.off("match_found_2v2");
socket.off("2v2_error"); socket.off("2v2_error");
socket.on("2v2_lobbies", (list) => render2v2LobbyList(list, socket)); socket.on("2v2_lobbies", (list) => {
console.log("[2v2] Lobbies empfangen:", list);
render2v2LobbyList(list, socket);
});
socket.on("2v2_team_joined", (data) => { socket.on("2v2_team_joined", (data) => {
console.log("[2v2] Team joined:", data);
my2v2TeamId = data.teamId; my2v2TeamId = data.teamId;
document.getElementById("arena-team-panel").style.display = "block"; document.getElementById("arena-team-panel").style.display = "block";
document.getElementById("arena-lobby-section").style.display = "none"; document.getElementById("arena-lobby-section").style.display = "none";
hide2v2Error();
}); });
socket.on("2v2_team_update", (data) => render2v2TeamPanel(data, socket)); socket.on("2v2_team_update", (data) => render2v2TeamPanel(data, socket));
@ -467,7 +480,6 @@ async function open2v2Lobby() {
socket.on("2v2_partner_left", (data) => { socket.on("2v2_partner_left", (data) => {
const status = document.getElementById("arena-team-status"); const status = document.getElementById("arena-team-status");
if (status) status.innerHTML = `<span style="color:#e74c3c;">⚠️ ${data.name} hat das Team verlassen.</span>`; if (status) status.innerHTML = `<span style="color:#e74c3c;">⚠️ ${data.name} hat das Team verlassen.</span>`;
// Bereit-Button zurücksetzen
render2v2TeamPanel({ teamId: my2v2TeamId, players: [{ name: myArenaData.name, level: myArenaData.level, ready: false }], count: 1 }, socket); render2v2TeamPanel({ teamId: my2v2TeamId, players: [{ name: myArenaData.name, level: myArenaData.level, ready: false }], count: 1 }, socket);
}); });
@ -496,8 +508,8 @@ async function open2v2Lobby() {
}); });
socket.on("2v2_error", (data) => { socket.on("2v2_error", (data) => {
const status = document.getElementById("arena-team-status"); console.warn("[2v2] Fehler vom Server:", data.message);
if (status) { status.innerHTML = `<span style="color:#e74c3c;">❌ ${data.message}</span>`; } show2v2Error(data.message);
}); });
} }
@ -527,9 +539,9 @@ function render2v2LobbyList(list, socket) {
} }
function render2v2TeamPanel(data, socket) { function render2v2TeamPanel(data, socket) {
const playersEl = document.getElementById("arena-team-players"); const playersEl = document.getElementById("arena-team-players");
const actionsEl = document.getElementById("arena-team-actions"); const actionsEl = document.getElementById("arena-team-actions");
const statusEl = document.getElementById("arena-team-status"); const statusEl = document.getElementById("arena-team-status");
if (!playersEl || !actionsEl) return; if (!playersEl || !actionsEl) return;
playersEl.innerHTML = data.players.map(p => ` playersEl.innerHTML = data.players.map(p => `
@ -544,7 +556,7 @@ function render2v2TeamPanel(data, socket) {
actionsEl.innerHTML = `<div class="arena-waiting-partner">Warte auf Partner…</div>`; actionsEl.innerHTML = `<div class="arena-waiting-partner">Warte auf Partner…</div>`;
if (statusEl) statusEl.innerHTML = ""; if (statusEl) statusEl.innerHTML = "";
} else { } else {
const myEntry = data.players.find(p => p.name === myArenaData?.name); const myEntry = data.players.find(p => p.name === myArenaData?.name);
const iAmReady = myEntry?.ready; const iAmReady = myEntry?.ready;
actionsEl.innerHTML = iAmReady actionsEl.innerHTML = iAmReady
? `<button class="arena-btn-ready active" disabled>✅ Du bist bereit</button>` ? `<button class="arena-btn-ready active" disabled>✅ Du bist bereit</button>`
@ -572,7 +584,21 @@ function leave2v2Team(socket) {
socket.off("2v2_error"); socket.off("2v2_error");
} }
/* ── 1v1: Hauptlogik ───────────────────────────────────────────────────────── */ /* ── 2v2 Fehlermeldung ──────────────────────────────────────────────────────── */
function show2v2Error(msg) {
const el = document.getElementById("arena-2v2-error");
if (!el) return;
el.textContent = "❌ " + msg;
el.style.display = "block";
setTimeout(() => { el.style.display = "none"; }, 4000);
}
function hide2v2Error() {
const el = document.getElementById("arena-2v2-error");
if (el) el.style.display = "none";
}
/* ── 1v1: Hauptlogik ────────────────────────────────────────────────────────── */
async function handle1v1Click(card) { async function handle1v1Click(card) {
const socket = getSocket(); const socket = getSocket();
if (!socket) { if (!socket) {
@ -580,30 +606,25 @@ async function handle1v1Click(card) {
return; return;
} }
// Bereits in Suche?
if (card.classList.contains("searching")) return; if (card.classList.contains("searching")) return;
// Spielerdaten laden
let me; let me;
try { try {
const res = await fetch("/arena/me"); const res = await fetch("/arena/me");
if (!res.ok) throw new Error("Status " + res.status); if (!res.ok) throw new Error("Status " + res.status);
me = await res.json(); me = await res.json();
} catch (err) { } catch (err) {
console.error("[1v1] Spielerdaten konnten nicht geladen werden:", err); console.error("[1v1] Spielerdaten Fehler:", err);
showArenaError("Spielerdaten konnten nicht geladen werden. (Eingeloggt?)"); showArenaError("Spielerdaten konnten nicht geladen werden. (Eingeloggt?)");
return; return;
} }
// UI: Suche läuft
setCardSearching(card, true); setCardSearching(card, true);
showQueueStatus(me.level); showQueueStatus(me.level);
// Sicherstellen, dass keine alten Listener hängen
socket.off("match_found"); socket.off("match_found");
socket.off("queue_status"); socket.off("queue_status");
// Queue-Status empfangen
socket.on("queue_status", (data) => { socket.on("queue_status", (data) => {
if (data.status === "waiting") { if (data.status === "waiting") {
showQueueStatus(me.level, data.poolSize); showQueueStatus(me.level, data.poolSize);
@ -613,7 +634,6 @@ async function handle1v1Click(card) {
} }
}); });
// Match gefunden
socket.once("match_found", (data) => { socket.once("match_found", (data) => {
socket.off("queue_status"); socket.off("queue_status");
setCardSearching(card, false); setCardSearching(card, false);
@ -628,15 +648,14 @@ async function handle1v1Click(card) {
}); });
}); });
// Matchmaking starten
socket.emit("join_1v1", { socket.emit("join_1v1", {
id: me.id, id: me.id,
name: me.name, name: me.name,
level: me.level, level: me.level,
}); });
} }
/* ── Queue abbrechen ───────────────────────────────────────────────────────── */ /* ── Queue abbrechen ───────────────────────────────────────────────────────── */
function cancelQueue(card) { function cancelQueue(card) {
const socket = getSocket(); const socket = getSocket();
if (socket) { if (socket) {
@ -648,9 +667,8 @@ function cancelQueue(card) {
hideQueueStatus(); hideQueueStatus();
} }
/* ── Match-Found Splash ────────────────────────────────────────────────────── */ /* ── Match-Found Splash ────────────────────────────────────────────────────── */
function showMatchFoundOverlay(myName, opponentName, onDone) { function showMatchFoundOverlay(myName, opponentName, onDone) {
// Verhindert doppeltes Öffnen
if (document.getElementById("match-found-overlay")) return; if (document.getElementById("match-found-overlay")) return;
const overlay = document.createElement("div"); const overlay = document.createElement("div");
@ -662,22 +680,19 @@ function showMatchFoundOverlay(myName, opponentName, onDone) {
`; `;
document.body.appendChild(overlay); document.body.appendChild(overlay);
// Ladebalken animieren
requestAnimationFrame(() => { requestAnimationFrame(() => {
const bar = document.getElementById("mfBar"); const bar = document.getElementById("mfBar");
if (bar) bar.style.width = "100%"; if (bar) bar.style.width = "100%";
}); });
// Nach 1.6s zum Spielfeld
setTimeout(() => { setTimeout(() => {
overlay.remove(); overlay.remove();
onDone(); onDone();
}, 1600); }, 1600);
} }
/* ── Popup öffnen ──────────────────────────────────────────────────────────── */ /* ── Popup öffnen ──────────────────────────────────────────────────────────── */
function openArenaPopup(src, opponentName, matchId) { function openArenaPopup(src, opponentName, matchId) {
// Vorhandenen Popup schließen (falls mehrfach)
document.getElementById("arena-backdrop")?.remove(); document.getElementById("arena-backdrop")?.remove();
document.getElementById("arena-popup")?.remove(); document.getElementById("arena-popup")?.remove();
@ -688,8 +703,8 @@ function openArenaPopup(src, opponentName, matchId) {
popup.id = "arena-popup"; popup.id = "arena-popup";
const title = opponentName const title = opponentName
? `⚔️ 1v1 &nbsp;·&nbsp; vs ${opponentName}` ? `⚔️ Arena &nbsp;·&nbsp; vs ${opponentName}`
: "⚔️ Arena &nbsp;·&nbsp; 1v1"; : "⚔️ Arena";
popup.innerHTML = ` popup.innerHTML = `
<div id="arena-popup-titlebar"> <div id="arena-popup-titlebar">
@ -703,23 +718,21 @@ function openArenaPopup(src, opponentName, matchId) {
document.body.appendChild(backdrop); document.body.appendChild(backdrop);
document.body.appendChild(popup); document.body.appendChild(popup);
// Backdrop-Klick deaktiviert Fenster nur über Aufgeben/Spielende schließbar
} }
/* ── UI Hilfsfunktionen ────────────────────────────────────────────────────── */ /* ── UI Hilfsfunktionen ────────────────────────────────────────────────────── */
function setCardSearching(card, searching) { function setCardSearching(card, searching) {
const label = card.querySelector(".arena-mode-label"); const label = card.querySelector(".arena-mode-label");
const desc = card.querySelector(".arena-mode-desc"); const desc = card.querySelector(".arena-mode-desc");
if (searching) { if (searching) {
card.classList.add("searching"); card.classList.add("searching");
label.textContent = "⏳ Suche…"; label.textContent = "⏳ Suche…";
desc.textContent = "Warte auf passenden Gegner…"; desc.textContent = "Warte auf passenden Gegner…";
} else { } else {
card.classList.remove("searching"); card.classList.remove("searching");
label.textContent = "1v1"; label.textContent = "1v1";
desc.textContent = "Einzelkampf Beweis deine Stärke im Duell"; desc.textContent = "Einzelkampf Beweis deine Stärke im Duell";
} }
} }
@ -727,9 +740,8 @@ function showQueueStatus(myLevel, poolSize) {
const box = document.getElementById("arena-queue-status"); const box = document.getElementById("arena-queue-status");
if (!box) return; if (!box) return;
const range = 5; const min = Math.max(1, myLevel - 5);
const min = Math.max(1, myLevel - range); const max = myLevel + 5;
const max = myLevel + range;
const pool = poolSize ? ` · ${poolSize} Spieler im Pool` : ""; const pool = poolSize ? ` · ${poolSize} Spieler im Pool` : "";
box.style.display = "block"; box.style.display = "block";
@ -739,7 +751,6 @@ function showQueueStatus(myLevel, poolSize) {
<span class="qs-cancel" id="qs-cancel-btn">Suche abbrechen</span> <span class="qs-cancel" id="qs-cancel-btn">Suche abbrechen</span>
`; `;
// Cancel-Button Card-Referenz über data-attribute
document.getElementById("qs-cancel-btn")?.addEventListener("click", () => { document.getElementById("qs-cancel-btn")?.addEventListener("click", () => {
const card = document.querySelector(".arena-mode-card[data-mode='1v1']"); const card = document.querySelector(".arena-mode-card[data-mode='1v1']");
if (card) cancelQueue(card); if (card) cancelQueue(card);
@ -754,15 +765,15 @@ function hideQueueStatus() {
function showArenaError(msg) { function showArenaError(msg) {
const box = document.getElementById("arena-queue-status"); const box = document.getElementById("arena-queue-status");
if (!box) return; if (!box) return;
box.style.display = "block"; box.style.display = "block";
box.style.animation = "none"; box.style.animation = "none";
box.style.borderColor = "rgba(231,76,60,0.5)"; box.style.borderColor = "rgba(231,76,60,0.5)";
box.style.color = "#e74c3c"; box.style.color = "#e74c3c";
box.textContent = "❌ " + msg; box.textContent = "❌ " + msg;
setTimeout(() => { setTimeout(() => {
box.style.display = "none"; box.style.display = "none";
box.style.animation = ""; box.style.animation = "";
box.style.borderColor = ""; box.style.borderColor = "";
box.style.color = ""; box.style.color = "";
}, 3000); }, 3000);
} }