769 lines
24 KiB
JavaScript
769 lines
24 KiB
JavaScript
export async function loadArena() {
|
||
const ui = document.querySelector(".building-ui");
|
||
|
||
ui.innerHTML = `
|
||
<div id="arena-ui">
|
||
|
||
<!-- Modus-Auswahl -->
|
||
<div id="arena-mode-screen">
|
||
<div class="arena-title">⚔️ Kampfarena</div>
|
||
<p class="arena-subtitle">Wähle deinen Kampfmodus</p>
|
||
<div class="arena-modes">
|
||
<div class="arena-mode-card" data-mode="1v1">
|
||
<div class="arena-mode-icon">🗡️</div>
|
||
<div class="arena-mode-label">1v1</div>
|
||
<div class="arena-mode-desc">Einzelkampf – Beweis deine Stärke im Duell</div>
|
||
</div>
|
||
<div class="arena-mode-card" data-mode="2v2">
|
||
<div class="arena-mode-icon">⚔️</div>
|
||
<div class="arena-mode-label">2v2</div>
|
||
<div class="arena-mode-desc">Verbünde dich mit einem Kameraden im Kampf</div>
|
||
</div>
|
||
<div class="arena-mode-card" data-mode="4v4">
|
||
<div class="arena-mode-icon">🛡️</div>
|
||
<div class="arena-mode-label">4v4</div>
|
||
<div class="arena-mode-desc">Schlachtruf – Führe deine Truppe zum Sieg</div>
|
||
</div>
|
||
</div>
|
||
<div id="arena-queue-status" style="display:none;"></div>
|
||
</div>
|
||
|
||
<!-- 2v2 Lobby-Screen -->
|
||
<div id="arena-2v2-screen" style="display:none;">
|
||
<div class="arena-lobby-header">
|
||
<button class="arena-back-btn" id="arena-2v2-back">← Zurück</button>
|
||
<div class="arena-title" style="margin:0;">⚔️ 2v2 Team-Lobby</div>
|
||
</div>
|
||
|
||
<!-- Team-Panel (erscheint nach Beitritt) -->
|
||
<div id="arena-team-panel" style="display:none;">
|
||
<div class="arena-team-box">
|
||
<div class="arena-team-title">Dein Team</div>
|
||
<div id="arena-team-players"></div>
|
||
<div id="arena-team-actions"></div>
|
||
</div>
|
||
<div id="arena-team-status"></div>
|
||
</div>
|
||
|
||
<!-- Lobby-Liste -->
|
||
<div id="arena-lobby-section">
|
||
<div class="arena-lobby-actions">
|
||
<button class="arena-btn-create" id="arena-create-team-btn">+ Eigenes Team erstellen</button>
|
||
</div>
|
||
<div class="arena-lobby-title">Offene Teams</div>
|
||
<div id="arena-lobby-list"><div class="arena-lobby-empty">Keine offenen Teams vorhanden.</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
`;
|
||
|
||
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 = `<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);
|
||
});
|
||
|
||
socket.on("2v2_searching", () => {
|
||
const status = document.getElementById("arena-team-status");
|
||
if (status) status.innerHTML = `<div class="arena-searching-box">⏳ Suche nach Gegnerteam…</div>`;
|
||
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 = `<span style="color:#e74c3c;">❌ ${data.message}</span>`; }
|
||
});
|
||
}
|
||
|
||
function render2v2LobbyList(list, socket) {
|
||
const el = document.getElementById("arena-lobby-list");
|
||
if (!el) return;
|
||
if (!list.length) {
|
||
el.innerHTML = `<div class="arena-lobby-empty">Keine offenen Teams vorhanden.</div>`;
|
||
return;
|
||
}
|
||
el.innerHTML = list.map(team => `
|
||
<div class="arena-lobby-row">
|
||
<div class="arena-lobby-row-info">
|
||
<span class="arena-lobby-leader">⚔️ ${team.leader}</span>
|
||
<span class="arena-lobby-level">Lvl ${team.leaderLevel}</span>
|
||
<span class="arena-lobby-count">${team.count}/2 Spieler</span>
|
||
</div>
|
||
<button class="arena-btn-join" data-teamid="${team.teamId}">Beitreten</button>
|
||
</div>
|
||
`).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 => `
|
||
<div class="arena-team-player ${p.ready ? 'ready' : ''}">
|
||
<span class="arena-team-player-name">${p.name}</span>
|
||
<span class="arena-team-player-level">Lvl ${p.level}</span>
|
||
<span class="arena-team-player-status">${p.ready ? '✅ Bereit' : '⌛ Wartet'}</span>
|
||
</div>
|
||
`).join("");
|
||
|
||
if (data.count < 2) {
|
||
actionsEl.innerHTML = `<div class="arena-waiting-partner">Warte auf Partner…</div>`;
|
||
if (statusEl) statusEl.innerHTML = "";
|
||
} else {
|
||
const myEntry = data.players.find(p => p.name === myArenaData?.name);
|
||
const iAmReady = myEntry?.ready;
|
||
actionsEl.innerHTML = iAmReady
|
||
? `<button class="arena-btn-ready active" disabled>✅ Du bist bereit</button>`
|
||
: `<button class="arena-btn-ready" id="arena-ready-btn">⚔️ Bereit</button>`;
|
||
|
||
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 = `
|
||
<div class="mfo-title">⚔️ Match gefunden!</div>
|
||
<div class="mfo-vs">${myName} vs ${opponentName}</div>
|
||
<div class="mfo-bar"><div class="mfo-bar-fill" id="mfBar"></div></div>
|
||
`;
|
||
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 = `
|
||
<div id="arena-popup-titlebar">
|
||
<div class="ap-left">
|
||
<span class="ap-title">${title}</span>
|
||
</div>
|
||
<span class="ap-url">${matchId || src}</span>
|
||
</div>
|
||
<iframe src="${src}" allowfullscreen></iframe>
|
||
`;
|
||
|
||
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}
|
||
<br>
|
||
<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", () => {
|
||
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);
|
||
}
|