Win banner

This commit is contained in:
2026-02-10 08:07:26 +01:00
parent 5e8fad02b0
commit f496dd722a

View File

@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Wolf & Schafe | Online</title>
<title>Wolf & Schafe</title>
<style>
:root {
--bg: #f0f2f5;
@@ -91,7 +91,6 @@
color: white;
}
/* Connection Dot */
.conn-dot {
display: inline-block;
width: 8px;
@@ -126,6 +125,7 @@
width: 100%;
border: 3px solid var(--black);
background: #fff;
overflow: hidden;
}
svg {
@@ -175,13 +175,56 @@
stroke-dasharray: 4;
}
/* Victory Banner */
#victory-banner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(255, 255, 255, 0.95);
border: 5px solid var(--black);
padding: 20px;
z-index: 20;
text-align: center;
box-shadow: 8px 8px 0px var(--black);
pointer-events: none;
width: 70%;
}
#victory-banner h2 {
margin: 0;
font-size: 1.8rem;
text-transform: uppercase;
}
.winner-glow {
animation: winner-pulse 1.5s infinite;
}
@keyframes winner-pulse {
0% {
filter: drop-shadow(0 0 2px #ffd700);
stroke-width: 2.5;
}
50% {
filter: drop-shadow(0 0 12px #ffd700);
stroke-width: 6;
}
100% {
filter: drop-shadow(0 0 2px #ffd700);
stroke-width: 2.5;
}
}
#overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.92);
background: rgba(255, 255, 255, 0.9);
z-index: 10;
display: flex;
flex-direction: column;
@@ -218,7 +261,26 @@
color: white;
}
/* Toast Popup Styles */
.restart-pulse {
animation: btn-pulse 1.5s infinite;
background: var(--green) !important;
color: white;
}
@keyframes btn-pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
#toast {
visibility: hidden;
min-width: 200px;
@@ -273,8 +335,8 @@
<h1 style="text-align: center; margin: 0; border-bottom: 4px solid var(--black); padding-bottom: 10px;">WOLF &
SCHAFE</h1>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;">
<button class="btn btn-green" onclick="createRoom()">+ ONLINE</button>
<button class="btn" onclick="startLocal()">+ LOKAL</button>
<button class="btn btn-green" onclick="createRoom()">+ ONLINE SPIEL</button>
<button class="btn" onclick="startLocal()">+ LOKAL SPIELEN</button>
</div>
<h3 style="margin: 15px 0 5px 0;">Offene Räume:</h3>
<div id="roomList">Suche Räume...</div>
@@ -296,11 +358,17 @@
<div>WARTEN AUF GEGNER...</div>
<p style="font-size: 0.7rem; margin-top: 10px; padding: 0 10px;">Link kopieren und Freund einladen!</p>
</div>
<div id="victory-banner" class="hidden">
<h2 id="winner-text">GEWONNEN!</h2>
<p style="margin: 5px 0 0 0; font-size: 0.8rem;">SPIEL BEENDET</p>
</div>
<svg viewBox="0 0 600 420"></svg>
</div>
<div class="controls">
<button class="btn" id="copyBtn">Link Kopieren</button>
<button class="btn" id="copyBtn">Kopieren</button>
<button class="btn" id="restartBtn">Restart</button>
<button class="btn" onclick="location.href='/'">Lobby</button>
</div>
@@ -324,7 +392,6 @@
function initLocalState() {
state = { wolf: 5, sheep: [0, 1, 3], turn: "sheep", moveCount: 0, winner: null, lastMove: null };
myRole = "sheep"; // Initial role, will toggle
document.getElementById("lobby").classList.add("hidden");
document.getElementById("game").classList.remove("hidden");
document.getElementById("roomInfo").style.visibility = "hidden";
@@ -335,7 +402,6 @@
function connect() {
if (isLocal) return initLocalState();
const protocol = location.protocol === "https:" ? "wss" : "ws";
ws = new WebSocket(`${protocol}://${location.host}?room=${roomId}&sessionId=${sessionId}`);
@@ -358,14 +424,11 @@
};
}
// Logic for local movement
function handleLocalMove(from, to) {
const allowed = (state.turn === "wolf") ? bWolf[from] : bSheep[from];
if (!allowed?.includes(to) || state.sheep.includes(to) || state.wolf === to) return;
if (state.turn === "wolf") { state.wolf = to; state.turn = "sheep"; }
else { state.sheep[state.sheep.indexOf(from)] = to; state.turn = "wolf"; }
state.moveCount++;
state.winner = checkWinConditions(state);
render();
@@ -392,14 +455,8 @@
});
}
function createRoom() {
const id = Math.random().toString(36).substring(7);
window.location.search = `?room=${id}`;
}
function startLocal() {
window.location.search = `?mode=local`;
}
function createRoom() { window.location.search = `?room=${Math.random().toString(36).substring(7)}`; }
function startLocal() { window.location.search = `?mode=local`; }
document.getElementById("restartBtn").onclick = () => {
if (isLocal) initLocalState();
@@ -416,45 +473,53 @@
l.setAttribute("class", "edge"); svg.appendChild(l);
});
// In local mode, "your role" is always the current turn's role
const effectiveRole = isLocal ? state.turn : myRole;
const badge = document.getElementById("idBadge");
badge.innerText = effectiveRole === "wolf" ? "WOLF" : "SCHAFE";
badge.className = `id-box is-${effectiveRole}`;
const statusEl = document.getElementById("status");
if (state.winner) statusEl.innerHTML = `SIEG: ${state.winner.toUpperCase()}!`;
else statusEl.innerHTML = `${(isLocal || state.turn === myRole) ? 'DEIN ZUG' : 'WARTEN...'}<br><small>ZUG ${state.moveCount}/40</small>`;
const banner = document.getElementById("victory-banner");
const restartBtn = document.getElementById("restartBtn");
if (state.winner) {
banner.classList.remove("hidden");
const winMsg = state.winner === "wolf" ? "WOLF GEWINNT!" : "SCHAFE GEWINNEN!";
document.getElementById("winner-text").innerText = winMsg;
document.getElementById("winner-text").style.color = state.winner === "wolf" ? "var(--wolf)" : "var(--sheep)";
statusEl.innerHTML = "ENDE";
restartBtn.classList.add("restart-pulse");
} else {
banner.classList.add("hidden");
restartBtn.classList.remove("restart-pulse");
statusEl.innerHTML = `${(isLocal || state.turn === myRole) ? 'DEIN ZUG' : 'WARTEN...'}<br><small>ZUG ${state.moveCount}/40</small>`;
}
for (let i = 0; i <= 10; i++) {
const [x, y] = pos[i];
const isWolf = state.wolf === i;
const isSheep = state.sheep.includes(i);
const type = isWolf ? "wolf" : (isSheep ? "sheep" : "empty");
const isWinnerPiece = (state.winner === "wolf" && isWolf) || (state.winner === "sheep" && isSheep);
const isPossible = selected !== null && (effectiveRole === "wolf" ? bWolf[selected] : bSheep[selected]).includes(i) && !isWolf && !isSheep;
const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
g.onclick = () => {
if (!isLocal && (state.turn !== myRole || state.winner)) return;
if (isLocal && state.winner) return;
const isMine = (effectiveRole === "wolf" && isWolf) || (effectiveRole === "sheep" && isSheep);
if (isMine) {
selected = i;
} else if (isPossible) {
if (isMine) selected = i;
else if (isPossible) {
if (isLocal) handleLocalMove(selected, i);
else ws.send(JSON.stringify({ from: selected, to: i }));
selected = null;
} else {
selected = null;
}
} else selected = null;
render();
};
const c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
c.setAttribute("cx", x); c.setAttribute("cy", y); c.setAttribute("r", 25);
c.setAttribute("class", `node-circle ${type} ${selected === i ? 'selected' : ''} ${isPossible ? 'possible' : ''}`);
c.setAttribute("class", `node-circle ${type} ${selected === i ? 'selected' : ''} ${isPossible ? 'possible' : ''} ${isWinnerPiece ? 'winner-glow' : ''}`);
g.appendChild(c);
const t = document.createElementNS("http://www.w3.org/2000/svg", "text");
@@ -466,9 +531,8 @@
}
}
// Copy logic
document.getElementById("copyBtn").onclick = async () => {
const url = window.location.href.split('&')[0].split('?')[0] + `?room=${roomId}`;
const url = window.location.origin + window.location.pathname + `?room=${roomId}`;
try { await navigator.clipboard.writeText(url); showToast(); } catch (err) { }
};
function showToast() { const t = document.getElementById("toast"); t.className = "show"; setTimeout(() => { t.className = ""; }, 3000); }