export async function loadArena() { const ui = document.querySelector(".building-ui"); ui.innerHTML = `
βš”οΈ Kampfarena

WΓ€hle deinen Kampfmodus

πŸ—‘οΈ
1v1
Einzelkampf – Beweis deine StΓ€rke im Duell
βš”οΈ
2v2
VerbΓΌnde dich mit einem Kameraden im Kampf
πŸ›‘οΈ
4v4
Schlachtruf – FΓΌhre deine Truppe zum Sieg
`; injectArenaStyles(); initArenaModes(); } /* ── Styles ────────────────────────────────────────────────────────────────── */ function injectArenaStyles() { if (document.getElementById("arena-popup-styles")) return; const style = document.createElement("style"); style.id = "arena-popup-styles"; style.textContent = ` @keyframes arenaFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes arenaScaleIn { from { transform: scale(0.94); opacity: 0; } to { transform: scale(1); opacity: 1; } } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* ── Queue Status Box ── */ #arena-queue-status { margin-top: 18px; padding: 12px 20px; border-radius: 10px; background: rgba(255, 215, 80, 0.08); border: 1px solid rgba(255, 215, 80, 0.3); color: #dceb15; font-family: "Cinzel", serif; font-size: 13px; letter-spacing: 1px; text-align: center; animation: pulse 2s ease-in-out infinite; } #arena-queue-status .qs-cancel { display: inline-block; margin-top: 8px; font-size: 11px; color: rgba(255,100,100,0.8); cursor: pointer; text-decoration: underline; animation: none; } #arena-queue-status .qs-cancel:hover { color: #e74c3c; } /* ── Backdrop ── */ #arena-backdrop { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.82); backdrop-filter: blur(5px); z-index: 9998; animation: arenaFadeIn 0.25s ease; } /* ── Popup ── */ #arena-popup { position: fixed; inset: 50px; z-index: 9999; display: flex; flex-direction: column; border-radius: 14px; overflow: hidden; box-shadow: 0 0 0 1px rgba(255, 215, 80, 0.35), 0 30px 90px rgba(0, 0, 0, 0.85), 0 0 60px rgba(255, 215, 80, 0.08); animation: arenaScaleIn 0.28s cubic-bezier(0.22, 1, 0.36, 1); } /* ── Titelleiste ── */ #arena-popup-titlebar { display: flex; align-items: center; justify-content: space-between; background: rgba(10, 8, 5, 0.95); border-bottom: 1px solid rgba(255, 215, 80, 0.3); padding: 0 16px; height: 42px; flex-shrink: 0; user-select: none; } #arena-popup-titlebar .ap-left { display: flex; align-items: center; 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 { font-family: "Cinzel", serif; font-size: 13px; letter-spacing: 4px; color: rgba(255, 215, 80, 0.85); text-transform: uppercase; text-shadow: 0 1px 8px rgba(0,0,0,0.8); } #arena-popup-titlebar .ap-url { font-size: 11px; color: rgba(255,255,255,0.22); letter-spacing: 1px; } /* ── Match-Found Overlay ── */ #match-found-overlay { position: fixed; inset: 0; z-index: 10000; background: rgba(0,0,0,0.9); display: flex; flex-direction: column; align-items: center; justify-content: center; animation: arenaFadeIn 0.3s ease; } #match-found-overlay .mfo-title { font-family: "Cinzel", serif; font-size: 36px; color: #ffd750; text-shadow: 0 0 30px rgba(255,215,80,0.6); letter-spacing: 6px; margin-bottom: 12px; } #match-found-overlay .mfo-vs { font-family: "Cinzel", serif; font-size: 18px; color: rgba(255,255,255,0.75); letter-spacing: 3px; } #match-found-overlay .mfo-bar { width: 300px; height: 4px; background: rgba(255,215,80,0.2); border-radius: 2px; margin-top: 24px; overflow: hidden; } #match-found-overlay .mfo-bar-fill { height: 100%; background: #ffd750; width: 0%; border-radius: 2px; transition: width 1.5s ease; } /* ── iframe ── */ #arena-popup iframe { flex: 1; border: none; width: 100%; display: block; } /* ── Card: gesucht-Zustand ── */ .arena-mode-card.searching { opacity: 0.6; pointer-events: none; border-color: rgba(255,215,80,0.5) !important; } /* ── 2v2 Lobby Screen ── */ #arena-2v2-screen { flex-direction: column; gap: 14px; padding: 16px; height: 100%; overflow-y: auto; } .arena-lobby-header { display: flex; align-items: center; gap: 16px; } .arena-back-btn { background: none; border: 1px solid rgba(255,200,80,0.3); color: #c8960c; font-family: "Cinzel", serif; font-size: 12px; padding: 4px 12px; border-radius: 4px; cursor: pointer; } .arena-back-btn:hover { background: rgba(200,150,12,0.15); } .arena-lobby-actions { display: flex; gap: 10px; margin-bottom: 4px; } .arena-btn-create { background: linear-gradient(#4a3018, #2a1a08); border: 2px solid #8b6a3c; border-radius: 7px; color: #f0d9a6; font-family: "Cinzel", serif; font-size: 12px; padding: 8px 16px; cursor: pointer; transition: 0.2s; } .arena-btn-create:hover { border-color: #f0d060; } .arena-lobby-title { font-family: "Cinzel", serif; font-size: 13px; color: #a08060; letter-spacing: 1px; margin-bottom: 6px; } .arena-lobby-empty { font-family: "Cinzel", serif; font-size: 12px; color: #606060; padding: 12px 0; } .arena-lobby-row { display: flex; align-items: center; justify-content: space-between; background: linear-gradient(#2a1a08, #1a0f04); border: 1px solid #6b4b2a; border-radius: 8px; padding: 10px 14px; margin-bottom: 6px; } .arena-lobby-row-info { display: flex; align-items: center; gap: 12px; } .arena-lobby-leader { font-family: "Cinzel", serif; font-size: 13px; color: #f0d9a6; } .arena-lobby-level { font-size: 11px; color: #a08060; } .arena-lobby-count { font-size: 11px; color: #6a9a4a; } .arena-btn-join { background: linear-gradient(#1a4a18, #0f2a0e); border: 2px solid #4a8a3c; border-radius: 6px; color: #a0e090; font-family: "Cinzel", serif; font-size: 11px; padding: 5px 12px; cursor: pointer; transition: 0.2s; } .arena-btn-join:hover { border-color: #8ae060; color: #c0f0a0; } /* ── Team-Panel ── */ .arena-team-box { background: linear-gradient(#2a1a08, #1a0f04); border: 2px solid #6b4b2a; border-radius: 10px; padding: 14px; } .arena-team-title { font-family: "Cinzel", serif; font-size: 13px; color: #f0d060; letter-spacing: 2px; margin-bottom: 10px; } .arena-team-player { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid #3a2810; border-radius: 6px; margin-bottom: 5px; background: rgba(255,255,255,0.03); } .arena-team-player.ready { border-color: #4a8a3c; background: rgba(74,138,60,0.1); } .arena-team-player-name { font-family: "Cinzel", serif; font-size: 12px; color: #f0d9a6; flex: 1; } .arena-team-player-level { font-size: 11px; color: #a08060; } .arena-team-player-status { font-size: 11px; } .arena-waiting-partner { font-family: "Cinzel", serif; font-size: 11px; color: #a08060; text-align: center; padding: 8px; animation: pulse 2s ease-in-out infinite; } .arena-btn-ready { width: 100%; margin-top: 10px; background: linear-gradient(#1a4a18, #0f2a0e); border: 2px solid #4a8a3c; border-radius: 8px; color: #a0e090; font-family: "Cinzel", serif; font-size: 14px; padding: 10px; cursor: pointer; transition: 0.2s; } .arena-btn-ready:hover:not([disabled]) { border-color: #8ae060; background: linear-gradient(#2a6a28, #1a3a18); } .arena-btn-ready.active { border-color: #8ae060; color: #c0f0a0; cursor: default; } .arena-searching-box { font-family: "Cinzel", serif; font-size: 12px; color: #dceb15; text-align: center; padding: 10px; border: 1px solid rgba(255,215,80,0.3); border-radius: 8px; margin-top: 8px; animation: pulse 2s ease-in-out infinite; } `; document.head.appendChild(style); } /* ── Socket-Referenz holen ─────────────────────────────────────────────────── */ function getSocket() { // Wird von der Haupt-App als window._socket bereitgestellt return window._socket || null; } /* ── Klick-Handler initialisieren ─────────────────────────────────────────── */ function initArenaModes() { document.querySelectorAll(".arena-mode-card").forEach((card) => { card.addEventListener("click", () => { const mode = card.dataset.mode; if (mode === "1v1") { handle1v1Click(card); } else if (mode === "2v2") { open2v2Lobby(); } else { console.log("Arena Modus gewΓ€hlt:", mode); } }); }); } /* ══════════════════════════════════════════════════════════ 2v2 LOBBY ══════════════════════════════════════════════════════════ */ let my2v2TeamId = null; let myArenaData = null; async function open2v2Lobby() { const socket = getSocket(); if (!socket) { showArenaError("Keine Verbindung zum Server."); return; } // Spielerdaten laden if (!myArenaData) { try { const res = await fetch("/arena/me"); if (!res.ok) throw new Error(res.status); myArenaData = await res.json(); } catch { showArenaError("Spielerdaten konnten nicht geladen werden."); return; } } // Screen wechseln document.getElementById("arena-mode-screen").style.display = "none"; document.getElementById("arena-2v2-screen").style.display = "flex"; // Lobbyliste anfordern socket.emit("get_2v2_lobbies"); // ZurΓΌck-Button document.getElementById("arena-2v2-back").onclick = () => { leave2v2Team(socket); document.getElementById("arena-2v2-screen").style.display = "none"; document.getElementById("arena-mode-screen").style.display = ""; my2v2TeamId = null; }; // Team erstellen document.getElementById("arena-create-team-btn").onclick = () => { socket.emit("create_2v2_team", myArenaData); }; // Socket-Listener registrieren (einmalig) socket.off("2v2_lobbies"); socket.off("2v2_team_joined"); socket.off("2v2_team_update"); socket.off("2v2_partner_left"); socket.off("2v2_searching"); socket.off("match_found_2v2"); socket.off("2v2_error"); socket.on("2v2_lobbies", (list) => render2v2LobbyList(list, socket)); socket.on("2v2_team_joined", (data) => { my2v2TeamId = data.teamId; document.getElementById("arena-team-panel").style.display = "block"; document.getElementById("arena-lobby-section").style.display = "none"; }); socket.on("2v2_team_update", (data) => render2v2TeamPanel(data, socket)); socket.on("2v2_partner_left", (data) => { const status = document.getElementById("arena-team-status"); if (status) status.innerHTML = `⚠️ ${data.name} hat das Team verlassen.`; // Bereit-Button zurΓΌcksetzen render2v2TeamPanel({ teamId: my2v2TeamId, players: [{ name: myArenaData.name, level: myArenaData.level, ready: false }], count: 1 }, socket); }); socket.on("2v2_searching", () => { const status = document.getElementById("arena-team-status"); if (status) status.innerHTML = `
⏳ Suche nach Gegnerteam…
`; const actions = document.getElementById("arena-team-actions"); if (actions) actions.innerHTML = ""; }); socket.on("match_found_2v2", (data) => { socket.off("2v2_lobbies"); socket.off("2v2_team_update"); socket.off("2v2_partner_left"); socket.off("2v2_searching"); showMatchFoundOverlay(myArenaData.name, `Team ${data.myTeam === 1 ? 2 : 1}`, () => { document.getElementById("arena-2v2-screen").style.display = "none"; document.getElementById("arena-mode-screen").style.display = ""; openArenaPopup( `/arena/2v2?match=${encodeURIComponent(data.matchId)}&slot=${encodeURIComponent(data.mySlot)}`, data.opponents?.join(" & ") || "Gegner", data.matchId, ); }); }); socket.on("2v2_error", (data) => { const status = document.getElementById("arena-team-status"); if (status) { status.innerHTML = `❌ ${data.message}`; } }); } function render2v2LobbyList(list, socket) { const el = document.getElementById("arena-lobby-list"); if (!el) return; if (!list.length) { el.innerHTML = `
Keine offenen Teams vorhanden.
`; return; } el.innerHTML = list.map(team => `
βš”οΈ ${team.leader} Lvl ${team.leaderLevel} ${team.count}/2 Spieler
`).join(""); el.querySelectorAll(".arena-btn-join").forEach(btn => { btn.addEventListener("click", () => { socket.emit("join_2v2_team", { teamId: btn.dataset.teamid, playerData: myArenaData }); }); }); } function render2v2TeamPanel(data, socket) { const playersEl = document.getElementById("arena-team-players"); const actionsEl = document.getElementById("arena-team-actions"); const statusEl = document.getElementById("arena-team-status"); if (!playersEl || !actionsEl) return; playersEl.innerHTML = data.players.map(p => `
${p.name} Lvl ${p.level} ${p.ready ? 'βœ… Bereit' : 'βŒ› Wartet'}
`).join(""); if (data.count < 2) { actionsEl.innerHTML = `
Warte auf Partner…
`; if (statusEl) statusEl.innerHTML = ""; } else { const myEntry = data.players.find(p => p.name === myArenaData?.name); const iAmReady = myEntry?.ready; actionsEl.innerHTML = iAmReady ? `` : ``; if (!iAmReady) { document.getElementById("arena-ready-btn")?.addEventListener("click", () => { socket.emit("2v2_player_ready", { teamId: my2v2TeamId }); }); } } } function leave2v2Team(socket) { if (my2v2TeamId) { socket.emit("leave_2v2_team"); my2v2TeamId = null; } socket.off("2v2_lobbies"); socket.off("2v2_team_joined"); socket.off("2v2_team_update"); socket.off("2v2_partner_left"); socket.off("2v2_searching"); socket.off("match_found_2v2"); socket.off("2v2_error"); } /* ── 1v1: Hauptlogik ───────────────────────────────────────────────────────── */ async function handle1v1Click(card) { const socket = getSocket(); if (!socket) { showArenaError("Keine Verbindung zum Server. Bitte Seite neu laden."); return; } // Bereits in Suche? if (card.classList.contains("searching")) return; // Spielerdaten laden let me; try { const res = await fetch("/arena/me"); if (!res.ok) throw new Error("Status " + res.status); me = await res.json(); } catch (err) { console.error("[1v1] Spielerdaten konnten nicht geladen werden:", err); showArenaError("Spielerdaten konnten nicht geladen werden. (Eingeloggt?)"); return; } // UI: Suche lΓ€uft setCardSearching(card, true); showQueueStatus(me.level); // Sicherstellen, dass keine alten Listener hΓ€ngen socket.off("match_found"); socket.off("queue_status"); // Queue-Status empfangen socket.on("queue_status", (data) => { if (data.status === "waiting") { showQueueStatus(me.level, data.poolSize); } else if (data.status === "left") { setCardSearching(card, false); hideQueueStatus(); } }); // Match gefunden socket.once("match_found", (data) => { socket.off("queue_status"); setCardSearching(card, false); hideQueueStatus(); showMatchFoundOverlay(me.name, data.opponent.name, () => { openArenaPopup( `/arena/1v1?match=${encodeURIComponent(data.matchId)}&slot=${encodeURIComponent(data.mySlot)}`, data.opponent.name, data.matchId, ); }); }); // Matchmaking starten socket.emit("join_1v1", { id: me.id, name: me.name, level: me.level, }); } /* ── Queue abbrechen ───────────────────────────────────────────────────────── */ function cancelQueue(card) { const socket = getSocket(); if (socket) { socket.emit("leave_1v1"); socket.off("match_found"); socket.off("queue_status"); } setCardSearching(card, false); hideQueueStatus(); } /* ── Match-Found Splash ────────────────────────────────────────────────────── */ function showMatchFoundOverlay(myName, opponentName, onDone) { // Verhindert doppeltes Γ–ffnen if (document.getElementById("match-found-overlay")) return; const overlay = document.createElement("div"); overlay.id = "match-found-overlay"; overlay.innerHTML = `
βš”οΈ Match gefunden!
${myName}  vs  ${opponentName}
`; document.body.appendChild(overlay); // Ladebalken animieren requestAnimationFrame(() => { const bar = document.getElementById("mfBar"); if (bar) bar.style.width = "100%"; }); // Nach 1.6s zum Spielfeld setTimeout(() => { overlay.remove(); onDone(); }, 1600); } /* ── Popup ΓΆffnen ──────────────────────────────────────────────────────────── */ function openArenaPopup(src, opponentName, matchId) { // Vorhandenen Popup schließen (falls mehrfach) document.getElementById("arena-backdrop")?.remove(); document.getElementById("arena-popup")?.remove(); const backdrop = document.createElement("div"); backdrop.id = "arena-backdrop"; const popup = document.createElement("div"); popup.id = "arena-popup"; const title = opponentName ? `βš”οΈ 1v1  Β·  vs ${opponentName}` : "βš”οΈ Arena  Β·  1v1"; popup.innerHTML = `
${title}
${matchId || src}
`; document.body.appendChild(backdrop); document.body.appendChild(popup); // Backdrop-Klick deaktiviert – Fenster nur ΓΌber Aufgeben/Spielende schließbar } /* ── UI Hilfsfunktionen ────────────────────────────────────────────────────── */ function setCardSearching(card, searching) { const label = card.querySelector(".arena-mode-label"); const desc = card.querySelector(".arena-mode-desc"); if (searching) { card.classList.add("searching"); label.textContent = "⏳ Suche…"; desc.textContent = "Warte auf passenden Gegner…"; } else { card.classList.remove("searching"); label.textContent = "1v1"; desc.textContent = "Einzelkampf – Beweis deine StΓ€rke im Duell"; } } function showQueueStatus(myLevel, poolSize) { const box = document.getElementById("arena-queue-status"); if (!box) return; const range = 5; const min = Math.max(1, myLevel - range); const max = myLevel + range; const pool = poolSize ? ` Β· ${poolSize} Spieler im Pool` : ""; box.style.display = "block"; box.innerHTML = ` ⏳ Suche Gegner (Level ${min}–${max})${pool}
Suche abbrechen `; // Cancel-Button – Card-Referenz ΓΌber data-attribute document.getElementById("qs-cancel-btn")?.addEventListener("click", () => { const card = document.querySelector(".arena-mode-card[data-mode='1v1']"); if (card) cancelQueue(card); }); } function hideQueueStatus() { const box = document.getElementById("arena-queue-status"); if (box) box.style.display = "none"; } function showArenaError(msg) { const box = document.getElementById("arena-queue-status"); if (!box) return; box.style.display = "block"; box.style.animation = "none"; box.style.borderColor = "rgba(231,76,60,0.5)"; box.style.color = "#e74c3c"; box.textContent = "❌ " + msg; setTimeout(() => { box.style.display = "none"; box.style.animation = ""; box.style.borderColor = ""; box.style.color = ""; }, 3000); }