dok/views/1v1-battlefield.ejs
2026-03-29 10:45:47 +01:00

367 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= title || "Spielfeld" %></title>
<link
href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/css/1v1.css" />
</head>
<body>
<!-- Warte-Overlay bis Gegner verbunden ist -->
<div id="connecting-overlay">
<div class="spinner"></div>
<div>Warte auf Gegner…</div>
<p>Verbindung wird hergestellt</p>
</div>
<div class="board">
<!-- TOP BAR -->
<div class="top-bar">
<div class="game-title"><%= title || "Spielfeld" %></div>
<div class="top-icons">
<div class="top-icon">⚙</div>
<div class="top-icon">🗺</div>
<div class="top-icon">📖</div>
<div class="top-icon">🏆</div>
</div>
<div class="top-bar-actions">
<button class="end-turn-btn" id="end-turn-btn" disabled>Zug beenden</button>
<button class="aufgeben-btn" id="aufgeben-btn">🏳 Aufgeben</button>
</div>
</div>
<!-- Spielfeld-Sperre bis beide Spieler bereit sind -->
<div id="board-lock-overlay">
<div id="ready-timer-box">
<div id="ready-timer-label">Bereit machen</div>
<div id="ready-timer-ring">
<svg viewBox="0 0 80 80" width="80" height="80"><circle cx="40" cy="40" r="34" class="timer-track"/><circle cx="40" cy="40" r="34" class="timer-fill" id="timer-circle"/></svg>
<div id="ready-timer-number">30</div>
</div>
<div id="ready-timer-sub">Beide Spieler müssen BEREIT klicken</div>
<div id="ready-status-row">
<div class="ready-pip" id="pip-player1">⬜ Spieler 1</div>
<div class="ready-pip" id="pip-player2">⬜ Spieler 2</div>
</div>
<button class="bereit-btn" id="bereit-btn">✔ BEREIT</button>
</div>
</div>
<!-- LEFT AVATAR (Spieler 1) -->
<div class="avatar avatar-left" id="avLeft">
<input
type="file"
accept="image/*"
id="fileInputLeft"
/>
<img id="avImgL" class="av-img" />
<div class="av-placeholder" id="avPhL">
<div class="av-icon">⚔</div>
</div>
<div class="av-name" id="nameLeft"><%= player1 || "Spieler 1" %></div>
<div class="av-stats">
<div class="stat hp">
<span class="s-icon">❤</span>
<span class="s-val" id="hpLeft"><%= player1hp || 20 %></span>
</div>
<div class="stat mana">
<span class="s-icon">💧</span>
<span class="s-val" id="manaLeft"><%= player1mana || 3 %></span>
</div>
</div>
<div class="hp-orb" id="orbLeft"><%= player1hp || 15 %></div>
</div>
<!-- RIGHT AVATAR (Spieler 2) -->
<div class="avatar avatar-right" id="avRight">
<input
type="file"
accept="image/*"
id="fileInputRight"
/>
<img id="avImgR" class="av-img" />
<div class="av-placeholder" id="avPhR">
<div class="av-icon">🛡</div>
</div>
<div class="av-name" id="nameRight"><%= player2 || "Spieler 2" %></div>
<div class="av-stats">
<div class="stat hp">
<span class="s-icon">❤</span>
<span class="s-val" id="hpRight"><%= player2hp || 20 %></span>
</div>
<div class="stat mana">
<span class="s-icon">💧</span>
<span class="s-val" id="manaRight"><%= player2mana || 3 %></span>
</div>
</div>
<div class="hp-orb" id="orbRight"><%= player2hp || 15 %></div>
</div>
<!-- CENTER CARD ROWS -->
<div class="card-area">
<div class="row-label">Reihe 1</div>
<div class="card-row" id="row1"></div>
<div class="row-label">Reihe 2</div>
<div class="card-row" id="row2"></div>
</div>
<!-- BOTTOM BAR -->
<div class="bottom-bar">
<!-- HAND -->
<div class="hand-area" id="handArea"></div>
<!-- ACTION BUTTONS -->
<div class="action-hud">
<div class="action-row">
<div class="action-btn" title="Angriff">⚔</div>
<div class="action-btn" title="Magie">✨</div>
<div class="action-btn" title="Verteidigung">🛡</div>
</div>
<div class="action-row">
<div class="action-btn" title="Heilen">💊</div>
<div class="action-btn" title="Karte ziehen">🃏</div>
<div class="action-btn" title="Einstellungen">⚙</div>
</div>
</div>
</div>
</div>
<!-- Socket.io (wird vom Parent-Fenster geerbt oder eigenständig geladen) -->
<script src="/socket.io/socket.io.js"></script>
<script>
// ── Spielfeld-Karten aufbauen ──────────────────────────────────────────
["row1", "row2"].forEach((id) => {
const row = document.getElementById(id);
for (let i = 1; i <= 11; i++) {
const s = document.createElement("div");
s.className = "card-slot";
if (id === "row1" && i === 1) {
s.innerHTML = `
<img
src="/images/playcards/Silberklinge.png"
alt="Silberklinge"
style="width:100%;height:100%;object-fit:cover;border-radius:7px;"
>
`;
} else {
s.innerHTML =
'<span class="slot-icon">✦</span><span class="slot-num">' +
i +
"</span>";
}
row.appendChild(s);
}
});
// ── Hand-Karten aufbauen ───────────────────────────────────────────────
const hand = document.getElementById("handArea");
const silberklinge = document.createElement("div");
silberklinge.className = "hand-slot";
silberklinge.innerHTML = `
<img
src="/images/playcards/Silberklinge.png"
alt="Silberklinge"
style="width:100%;height:100%;object-fit:cover;border-radius:7px;"
>
`;
hand.appendChild(silberklinge);
for (let i = 1; i < 8; i++) {
const s = document.createElement("div");
s.className = "hand-slot";
s.innerHTML = '<span class="hs-icon">🃏</span>';
hand.appendChild(s);
}
// ── Match-Daten aus URL ────────────────────────────────────────────────
const urlParams = new URLSearchParams(window.location.search);
const matchId = urlParams.get("match") || "<%= matchId || '' %>";
const mySlot = urlParams.get("slot") || "<%= mySlot || 'player1' %>";
const amIPlayer1 = mySlot === "player1";
// ── Socket.io: Gegner-Namen live empfangen ─────────────────────────────
const socket = io();
socket.emit("arena_join", { matchId, slot: mySlot });
socket.on("arena_opponent_joined", (data) => {
// Gegner-Name anzeigen
const opponentName = data.name || "Gegner";
if (amIPlayer1) {
document.getElementById("nameRight").textContent = opponentName;
} else {
document.getElementById("nameLeft").textContent = opponentName;
}
// Overlay entfernen
const overlay = document.getElementById("connecting-overlay");
if (overlay) overlay.remove();
});
// Beide Spieler sind verbunden → beide benachrichtigen
socket.on("arena_ready", (data) => {
const overlay = document.getElementById("connecting-overlay");
if (overlay) overlay.remove();
// Namen setzen
document.getElementById("nameLeft").textContent = data.player1 || "Spieler 1";
document.getElementById("nameRight").textContent = data.player2 || "Spieler 2";
});
// ── Bereit-System ─────────────────────────────────────────────────────
let myReady = false;
function handleBereit() {
if (myReady) return;
myReady = true;
const btn = document.getElementById("bereit-btn");
btn.textContent = "✔ BEREIT";
btn.classList.add("bereit-clicked");
btn.disabled = true;
socket.emit("player_ready", { matchId, slot: mySlot });
}
// Timer-Kreis: Umfang des Kreises (r=34 → 2*π*34 ≈ 213.6)
const CIRCUMFERENCE = 2 * Math.PI * 34;
const timerCircle = document.getElementById("timer-circle");
if (timerCircle) timerCircle.style.strokeDasharray = CIRCUMFERENCE;
socket.on("ready_timer", (data) => {
const { remaining } = data;
const num = document.getElementById("ready-timer-number");
if (num) num.textContent = remaining;
// Kreis-Fortschritt aktualisieren
if (timerCircle) {
const progress = remaining / 30;
const offset = CIRCUMFERENCE * (1 - progress);
timerCircle.style.strokeDashoffset = offset;
// Farbe: grün → gelb → rot
if (remaining > 15) timerCircle.style.stroke = "#27ae60";
else if (remaining > 7) timerCircle.style.stroke = "#f39c12";
else timerCircle.style.stroke = "#e74c3c";
}
});
socket.on("ready_status", (data) => {
// Pips aktualisieren
const pip1 = document.getElementById("pip-player1");
const pip2 = document.getElementById("pip-player2");
if (data.readySlots && pip1 && pip2) {
if (data.readySlots.includes("player1")) pip1.textContent = "✅ " + (document.getElementById("nameLeft")?.textContent || "Spieler 1");
if (data.readySlots.includes("player2")) pip2.textContent = "✅ " + (document.getElementById("nameRight")?.textContent || "Spieler 2");
}
if (data.readyCount === 2) {
// Beide bereit → Sperre aufheben
const lock = document.getElementById("board-lock-overlay");
if (lock) lock.remove();
const bereitBtn = document.getElementById("bereit-btn");
if (bereitBtn) bereitBtn.remove();
const endBtn = document.getElementById("end-turn-btn");
if (endBtn) endBtn.disabled = false;
}
});
socket.on("match_cancelled", () => {
// Popup im Parent-Fenster schließen (Spielfeld läuft im iframe)
if (window.parent && window.parent !== window) {
window.parent.document.getElementById("arena-backdrop")?.remove();
window.parent.document.getElementById("arena-popup")?.remove();
} else {
window.close();
}
});
function handleAufgeben() {
// Funktion offen hier kann später die Aufgabe-Logik rein
socket.emit("player_surrender", { matchId, slot: mySlot });
}
// ─────────────────────────────────────────────────────────────────────
// ── Avatar laden (lokal) ──────────────────────────────────────────────
function loadAvatar(input, imgId, parentId) {
const file = input.files[0];
if (!file) return;
const r = new FileReader();
r.onload = (e) => {
const img = document.getElementById(imgId);
img.src = e.target.result;
img.style.display = "block";
const parent = document.getElementById(parentId);
const ph = parent.querySelector(".av-placeholder");
if (ph) ph.style.display = "none";
};
r.readAsDataURL(file);
}
function loadPortrait(input, imgId) {
const file = input.files[0];
if (!file) return;
const r = new FileReader();
r.onload = (e) => {
const img = document.getElementById(imgId);
img.src = e.target.result;
img.style.display = "block";
img.parentElement.querySelector("span").style.display = "none";
};
r.readAsDataURL(file);
}
// ── Event-Listener (kein inline-onclick wegen Helmet CSP) ─────────────
document.getElementById("bereit-btn")
?.addEventListener("click", handleBereit);
document.getElementById("aufgeben-btn")
?.addEventListener("click", handleAufgeben);
document.getElementById("fileInputLeft")
?.addEventListener("change", function () {
loadAvatar(this, "avImgL", "avLeft");
});
document.getElementById("fileInputRight")
?.addEventListener("change", function () {
loadAvatar(this, "avImgR", "avRight");
});
// ─────────────────────────────────────────────────────────────────────
</script>
</body>
</html>