From a7467f6f25dddd4278c31873cb5e82d8a947c27a Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 17:44:14 +0200 Subject: [PATCH 01/11] Login page --- src/main/resources/public/index.html | 79 ++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html index d2dcc5d..3af4974 100644 --- a/src/main/resources/public/index.html +++ b/src/main/resources/public/index.html @@ -1,18 +1,79 @@ - - Spotify – Zuletzt gehörte Songs + + + Spotify Roulette – Login + + + + -

Spotify – Zuletzt gehörte Songs

-
- - - -
- +
+
+ +

Spotify Roulette

+ Status: Abgemeldet +
+
+
+
+ + +
+ +
+
+

Du wirst zu Spotify weitergeleitet, um die Berechtigungen zu bestätigen.

+
+
+
+ + From 83c36b27f6c991006f540adf82b1d2ac709a43ca Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 17:52:49 +0200 Subject: [PATCH 02/11] Lobby GUI --- src/main/resources/public/lobby.html | 103 +++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/src/main/resources/public/lobby.html b/src/main/resources/public/lobby.html index 63d7e23..4aa961e 100644 --- a/src/main/resources/public/lobby.html +++ b/src/main/resources/public/lobby.html @@ -1,14 +1,105 @@ - - Lobby – Spotify Roulette + + + Spotify Roulette – Lobby + + + + -

Lobby

-

Was möchtest du tun?

- - +
+
+ +

Lobby

+ Angemeldet als +
+ +
+
+

Neues Spiel

+
+
+ +
Spiel erstellenStarte eine neue Runde und teile den Code.
+
+ +
+
+ +
+

Spiel beitreten

+
+
+ +
Bestehendem Spiel beitretenDu hast schon einen Code? Klicke hier.
+
+ +
+
+
+
+ + + From be3e5f268efe91f10565643dafd9702bba3b62f5 Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 18:05:30 +0200 Subject: [PATCH 03/11] Spiel erstellen GUI --- src/main/resources/public/create-game.html | 103 ++++++++++++++++++++- src/main/resources/public/index.html | 1 - 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/main/resources/public/create-game.html b/src/main/resources/public/create-game.html index 8d64cd6..d368a89 100644 --- a/src/main/resources/public/create-game.html +++ b/src/main/resources/public/create-game.html @@ -1,12 +1,107 @@ - - Spiel erstellen – Spotify Roulette + + + Spiel erstellen – Spotify Roulette + + + + -

Neues Spiel erstellen

- +
+
+ +

Neues Spiel erstellen

+ Angemeldet als +
+ +
+
+

Sofort starten

+
+
+ +
+ Standard-Spiel + Erstellt eine Lobby mit Standardregeln. +
+
+ +

Du bekommst einen Spiel‑Code zum Teilen.

+
+
+ +
+

WIP Einstellungen

+
+
+
BaldMax. Spieler • Rundenanzahl • Song‑Ausschnitt • Privat/Öffentlich
+
WIP Optionen die evtl dazu kommen
+
+
+
+
+
+ + + diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html index 3af4974..af9e34a 100644 --- a/src/main/resources/public/index.html +++ b/src/main/resources/public/index.html @@ -73,7 +73,6 @@ - From babfe4925d01d8275bb9492a93d5fe270275412a Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 18:10:32 +0200 Subject: [PATCH 04/11] =?UTF-8?q?Spiel=20erstellen=20formulierung=20ge?= =?UTF-8?q?=C3=A4ndert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/public/create-game.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/public/create-game.html b/src/main/resources/public/create-game.html index d368a89..f99bfe5 100644 --- a/src/main/resources/public/create-game.html +++ b/src/main/resources/public/create-game.html @@ -67,7 +67,7 @@
-

Sofort starten

+

Spiel starten

From adfaf00817adb28b7de95a417a39e9fefd2816ac Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 18:21:03 +0200 Subject: [PATCH 05/11] Spiel beitreten in der lobby einbinden --- src/main/resources/public/js/lobby.js | 13 +++++--- src/main/resources/public/lobby.html | 43 +++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/main/resources/public/js/lobby.js b/src/main/resources/public/js/lobby.js index 99df629..a4c44ab 100644 --- a/src/main/resources/public/js/lobby.js +++ b/src/main/resources/public/js/lobby.js @@ -12,8 +12,13 @@ document.getElementById("createGame").addEventListener("click", () => { window.location.href = `/create-game.html?username=${encodeURIComponent(username)}`; }); -document.getElementById("joinGame").addEventListener("click", () => { - window.location.href = username - ? `/join-game.html?username=${encodeURIComponent(username)}` - : "/join-game.html"; +//document.getElementById("joinGame").addEventListener("click", () => { + // window.location.href = username + // ? `/join-game.html?username=${encodeURIComponent(username)}` + // : "/join-game.html"; +//}); +// NEU: statt Redirect -> Fokus auf Eingabe +document.getElementById("joinGame")?.addEventListener("click", () => { + const input = document.getElementById("gameId"); + if (input) { input.focus(); input.select(); } }); diff --git a/src/main/resources/public/lobby.html b/src/main/resources/public/lobby.html index 4aa961e..42dbe38 100644 --- a/src/main/resources/public/lobby.html +++ b/src/main/resources/public/lobby.html @@ -52,6 +52,15 @@ .tile .txt{display:flex; flex-direction:column} .tile .txt b{font-size:16px} .tile .txt span{font-size:13px; color:var(--muted)} + + /* Join form */ + .row{display:flex; gap:10px; align-items:center; flex-wrap:wrap} + label{color:var(--muted)} + input[type="text"]{ + background:#0f0f0f; color:var(--text); border:1px solid var(--border); + padding:12px 14px; border-radius:12px; outline:none; min-width:180px; + } + input:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--glow)} @@ -85,22 +94,44 @@ -
Bestehendem Spiel beitretenDu hast schon einen Code? Klicke hier.
+
Bestehendem Spiel beitretenDu hast schon einen Code? Gib ihn unten ein.
- + + +
+ + + + + +
+ +

Tipp: Code ist vierstellig (z.B. 3759).

- + + + + + - + \ No newline at end of file From e27b6f2a23f021467841726f96aa8ad5996ff995 Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 18:41:23 +0200 Subject: [PATCH 06/11] Clipboard aesthetic --- src/main/resources/public/lobby.html | 126 +++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 16 deletions(-) diff --git a/src/main/resources/public/lobby.html b/src/main/resources/public/lobby.html index 42dbe38..32eabcf 100644 --- a/src/main/resources/public/lobby.html +++ b/src/main/resources/public/lobby.html @@ -53,14 +53,50 @@ .tile .txt b{font-size:16px} .tile .txt span{font-size:13px; color:var(--muted)} - /* Join form */ - .row{display:flex; gap:10px; align-items:center; flex-wrap:wrap} - label{color:var(--muted)} - input[type="text"]{ - background:#0f0f0f; color:var(--text); border:1px solid var(--border); - padding:12px 14px; border-radius:12px; outline:none; min-width:180px; + /* Join form – Spotify-like Input */ + .row{display:flex; gap:12px; align-items:flex-end; flex-wrap:wrap} + .field{display:flex; flex-direction:column; gap:6px} + label{color:var(--muted); font-size:13px} + + .inputWrap{position:relative; min-width:220px} + .inputWrap .leading-icon{ + position:absolute; left:12px; top:50%; transform:translateY(-50%); opacity:.9; } - input:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--glow)} + .inputWrap .chip{ + position:absolute; right:10px; top:50%; transform:translateY(-50%); + font-size:11px; color:var(--muted); + border:1px solid var(--border); background:#0d0d0d; + padding:4px 8px; border-radius:999px; + } + input#gameId{ + width:100%; + background:#0b0b0b; color:var(--text); + border:1px solid var(--border); + border-radius:999px; + padding:14px 78px 14px 42px; /* Platz für Icon + Chip */ + outline:none; + letter-spacing:.12em; font-weight:600; + box-shadow: inset 0 1px 0 rgba(255,255,255,.02); + transition:border-color .2s, box-shadow .2s, background .2s, transform .06s; + } + input#gameId::placeholder{color:#777} + input#gameId:focus{ + border-color:var(--accent); + box-shadow:0 0 0 4px var(--glow), inset 0 1px 0 rgba(255,255,255,.02); + background:#0e0e0e; + } + input#gameId:active{transform:translateY(1px)} + + /* Icon-only clipboard button */ + .icon-btn{width:auto; padding:10px; border-radius:999px; display:inline-flex; align-items:center; justify-content:center} + .icon-btn svg{display:block} + .icon-btn--accent{ + background:var(--accent); color:#0a0a0a; + box-shadow:0 0 0 0 var(--glow); + transition: box-shadow .2s, filter .15s, transform .06s, background .2s; + } + .icon-btn--accent:hover{ filter:brightness(1.05); box-shadow:0 0 0 6px var(--glow) } + .icon-btn--accent:active{ background:var(--accent-press); transform:translateY(1px) } @@ -98,31 +134,89 @@ -
- - + +
+ +
+ + + 4-stellig +
+
+ + + + - + +
-

Tipp: Code ist vierstellig (z.B. 3759).

+

Tipp: Code ist vierstellig (z. B. 6767).

- + + - + - \ No newline at end of file + From a79f44b24e191d047ded53bb896624f3a2dd0471 Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 20:17:06 +0200 Subject: [PATCH 07/11] GAME UI --- src/main/resources/public/game.html | 351 ++++++++++++++++----------- src/main/resources/public/js/game.js | 146 ++++++----- 2 files changed, 295 insertions(+), 202 deletions(-) diff --git a/src/main/resources/public/game.html b/src/main/resources/public/game.html index e27eafc..5bbcb54 100644 --- a/src/main/resources/public/game.html +++ b/src/main/resources/public/game.html @@ -1,162 +1,229 @@ - - + + Spotify Roulette – Spiel + + + + + + -
-

Spotify Roulette

-
Spiel-Code:
-
-
-
-
Teilnehmer
- -
-
- -
- -
-
Scoreboard
- -
-
- + + + + - + + +
+ + + - diff --git a/src/main/resources/public/js/game.js b/src/main/resources/public/js/game.js index a9e7505..077007d 100644 --- a/src/main/resources/public/js/game.js +++ b/src/main/resources/public/js/game.js @@ -6,17 +6,31 @@ import { setupStartRound } from "./start-round.js"; const gameId = getParam("gameId"); const username = getParam("username"); +// --- kleine Helper --- +function showToast(msg, ms = 2200) { + const t = document.getElementById("toast"); + if (!t) return; + t.textContent = msg; + t.classList.add("show"); + setTimeout(() => t.classList.remove("show"), ms); +} +function esc(s) { + const d = document.createElement("div"); + d.textContent = String(s ?? ""); + return d.innerHTML; +} + // 1) Parameter prüfen if (!gameId || !username) { alert("Ungültige oder fehlende URL-Parameter!"); throw new Error("Missing gameId or username"); } -// 2) Copy to clipboard (unverändert) +// 2) Copy to clipboard (unverändert, aber mit Toast) (function copyCodeToClipboard(code) { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(code) - .then(() => console.log(`GameCode ${code} copied to clipboard`)) + .then(() => showToast(`Spiel-Code ${code} kopiert`)) .catch(err => console.error("Clipboard write failed:", err)); } else { const ta = document.createElement("textarea"); @@ -28,7 +42,7 @@ if (!gameId || !username) { ta.select(); try { document.execCommand("copy"); - console.log(`GameCode ${code} copied via execCommand`); + showToast(`Spiel-Code ${code} kopiert`); } catch (err) { console.error("Fallback copy failed:", err); } @@ -36,16 +50,14 @@ if (!gameId || !username) { } })(gameId); -// 3) Visuelles Feedback (unverändert) -const notice = document.createElement("div"); -notice.textContent = `Spiel-Code ${gameId} in die Zwischenablage kopiert!`; -Object.assign(notice.style, { /* … Styles … */ }); -document.body.append(notice); -setTimeout(() => notice.remove(), 3000); - -// 4) Spiel-Code ins DOM schreiben +// 3) Spiel-Code ins DOM schreiben document.getElementById("gameId").textContent = gameId; +// 4) Drawer toggles für Songliste +const drawer = document.getElementById("songDrawer"); +document.getElementById("toggleSongList")?.addEventListener("click", () => drawer?.classList.add("open")); +document.getElementById("closeDrawer")?.addEventListener("click", () => drawer?.classList.remove("open")); + // 5) WebSocket mit Reconnect-Logik let socket; function connectWebSocket() { @@ -57,7 +69,7 @@ function connectWebSocket() { socket.addEventListener("open", () => { console.log("WebSocket connected. Requesting player list..."); socket.send(JSON.stringify({ type: "requestPlayers" })); - setupStartRound(socket); + setupStartRound(socket); // deaktiviert den Start-Button beim Klicken }); socket.addEventListener("message", async ({ data }) => { @@ -66,7 +78,6 @@ function connectWebSocket() { switch (msg.type) { case "players": - console.log("Empfangene Spieler:", msg.players); renderList("#playersList", msg.players, username); break; case "reload": @@ -106,13 +117,9 @@ const optionsDiv = document.getElementById("options"); const resultP = document.getElementById("result"); const scoreboard = document.getElementById("scoreboard"); -// 8) Funktion zum Anzeigen einer neuen Runde -//let playLock = false; - -async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos}) { +// 6) Neue Runde anzeigen +async function handleRoundStart({ ownerOptions, songUri, trackInfos }) { // UI zurücksetzen - //if (playLock) return; // Verhindert parallele Ausführung - //playLock = true; resultP.textContent = ""; optionsDiv.innerHTML = ""; songEmbed.innerHTML = ""; @@ -120,32 +127,32 @@ async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos}) // Song einbetten const trackId = songUri.split(":")[2]; songEmbed.innerHTML = ` - `; + `; + // Spotify-Playback (optional, wenn vorhanden) if (window.playOnSpotify && typeof window.playOnSpotify === "function") { - await window.playOnSpotify(songUri, username); // Warten bis fertig + await window.playOnSpotify(songUri, username); } - //if (window.playOnSpotify && typeof window.playOnSpotify === "function") { - // window.playOnSpotify(songUri, username); - //} - - // Dynamische Kreisverteilung der Buttons - // Warten, bis #options gerendert ist - setTimeout(() => { + // Buttons kreisförmig verteilen + requestAnimationFrame(() => { const optsRect = optionsDiv.getBoundingClientRect(); - const radius = Math.min(optsRect.width, optsRect.height) / 2 - 50; // 50px Abstand zum Rand - + const radius = Math.min(optsRect.width, optsRect.height) / 2 - 40; // Abstand zum Rand ownerOptions.forEach((user, i) => { const btn = document.createElement("button"); btn.textContent = user; btn.classList.add("player-option"); - const angle = 360 * i / ownerOptions.length; - btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`; + //const angle = 360 * i / ownerOptions.length; + //btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`; + + const angle = (360 / ownerOptions.length) * i; + // zentrieren -> dann drehen -> nach außen schieben -> Text aufrecht drehen + btn.style.transform = `translate(-50%, -50%) rotate(${angle}deg) translate(0, -${radius}px) rotate(${-angle}deg)`; + btn.addEventListener("click", () => { socket.send(JSON.stringify({ type: "guess", @@ -156,24 +163,42 @@ async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos}) }); optionsDiv.appendChild(btn); }); - }, 0); - - startBtn.hidden = true; - roundArea.hidden = false; - - const songList = document.getElementById("songList"); - songList.innerHTML = ""; - //trackinfos ist eine map bestehend aus aus Spielername und Liste von Track-Infos - const userTracks = trackInfos?.[username] ?? []; - userTracks.forEach(trackInfo => { - const li = document.createElement("li"); - li.textContent = trackInfo; - songList.appendChild(li); }); - //playLock = false; + + // Start-Button ausblenden + Rundensektion einblenden + startBtn.hidden = true; + startBtn.disabled = true; + roundArea.hidden = false; + + // Geladene Songs (beide Orte befüllen: Aside + Drawer) + const songList = document.getElementById("songList"); + const songListArea = document.getElementById("songListArea"); + const userTracks = trackInfos?.[username] ?? []; + + if (songList) { + songList.innerHTML = ""; + userTracks.forEach(t => { + const li = document.createElement("li"); + li.textContent = t; + songList.appendChild(li); + }); + } + if (songListArea) { + songListArea.innerHTML = ""; + userTracks.forEach(t => { + // Erwartetes Format "Titel - Künstler" + const [title, artists] = String(t).split(" - "); + const row = document.createElement("div"); + row.className = "track"; + row.innerHTML = ` +
+
${esc(title ?? t)}${esc(artists ?? "")}
`; + songListArea.appendChild(row); + }); + } } -// 9) Funktion zum Anzeigen des Ergebnisses +// 7) Ergebnis + Weiter-Button function renderScoreboard(scores) { scoreboard.innerHTML = ""; Object.entries(scores).forEach(([user, pts]) => { @@ -191,9 +216,8 @@ function handleRoundResult({ scores, guesses, owner }) { const correct = guess === owner; const icon = correct ? "✅" : "❌"; const delta = correct ? "+3" : "-1"; - const msg = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`; - const p = document.createElement("p"); - p.textContent = msg; + const p = document.createElement("p"); + p.textContent = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`; resultP.appendChild(p); }); @@ -205,24 +229,24 @@ function handleRoundResult({ scores, guesses, owner }) { nextBtn.hidden = true; nextBtn.disabled = true; resultP.textContent = ""; - startBtn.hidden = true; + startBtn.hidden = true; startBtn.disabled = true; - roundArea.hidden = true; + roundArea.hidden = true; }; } -function handleGameEnd({winner}) { +// 8) Spielende -> Start-Button zurück +function handleGameEnd({ winner }) { resultP.textContent = `🎉 ${winner} hat gewonnen!`; setTimeout(() => { startBtn.hidden = false; - roundArea.hidden = true; startBtn.disabled = false; + roundArea.hidden = true; scoreboard.innerHTML = ""; }, 6000); } -// Spotify-Playback Funktion -// game.js +// Spotify-Playback Funktion (unverändert) async function playOnSpotify(trackUri, username) { const deviceId = document.getElementById("deviceSelect")?.value; if (!deviceId) { @@ -244,3 +268,5 @@ async function playOnSpotify(trackUri, username) { } } window.playOnSpotify = playOnSpotify; + + From 1eb8c8cbe62cdf05148ed259e3b776d13f88946f Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 20:23:05 +0200 Subject: [PATCH 08/11] songliste toggle statt add --- src/main/resources/public/js/game.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/public/js/game.js b/src/main/resources/public/js/game.js index 077007d..d5c3ee1 100644 --- a/src/main/resources/public/js/game.js +++ b/src/main/resources/public/js/game.js @@ -55,7 +55,7 @@ document.getElementById("gameId").textContent = gameId; // 4) Drawer toggles für Songliste const drawer = document.getElementById("songDrawer"); -document.getElementById("toggleSongList")?.addEventListener("click", () => drawer?.classList.add("open")); +document.getElementById("toggleSongList")?.addEventListener("click", () => drawer?.classList.toggle("open")); document.getElementById("closeDrawer")?.addEventListener("click", () => drawer?.classList.remove("open")); // 5) WebSocket mit Reconnect-Logik From c0ffb4d2f1278f0d8f165b328de3a3601d22ff80 Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 21:44:56 +0200 Subject: [PATCH 09/11] piechart wip --- src/main/resources/public/game.html | 37 ++++++------- src/main/resources/public/js/game.js | 82 ++++++++++++++++++++-------- 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/main/resources/public/game.html b/src/main/resources/public/game.html index 5bbcb54..1681340 100644 --- a/src/main/resources/public/game.html +++ b/src/main/resources/public/game.html @@ -75,13 +75,12 @@ #roundArea[hidden]{display:none} #options{ position:relative; width:100%; max-width:520px; aspect-ratio:1; margin:14px 0 6px; - border-radius:50%; border:1px dashed var(--border); - background: radial-gradient(240px 240px at 50% 50%, rgba(29,185,84,.08), transparent 60%); + border-radius:50%; } .player-option{ position:absolute; - left:50%; /* neu */ - top:50%; /* neu */ + left:50%; + top:50%; transform-origin:center; background:#0f0f0f; color:var(--text); border:1px solid var(--border); @@ -124,7 +123,7 @@ padding:10px 14px; border-radius:999px; box-shadow:var(--shadow); z-index:50; display:none} .toast.show{display:block} - /* Slide-out Drawer für Songliste (wie im Beispiel) */ + /* Slide-out Drawer */ .drawer{position:fixed; top:0; right:0; height:100%; width:340px; background:#0f0f0f; border-left:1px solid var(--border); transform:translateX(100%); transition:transform .25s ease; z-index:60; display:flex; flex-direction:column} @@ -137,6 +136,15 @@ .drawer .meta{display:flex; flex-direction:column} .drawer .meta b{font-size:13px} .drawer .meta span{font-size:12px; color:var(--muted)} + + /* === Pie-Slices === */ + #options svg.options-svg { width:100%; height:auto; display:block; border-radius:50%; } + .wedge { fill:#181818; stroke:#2a2a2a; stroke-width:2; cursor:pointer; transition:filter .15s, fill .2s; } + .wedge:hover { filter:brightness(1.08); } + .wedge.selected { fill:#1db954; } + .wedge.correct { fill:#1db954; } + .wedge.disabled { pointer-events:none; opacity:.85; } + .wedge-label { fill:#fff; font-weight:700; font-size:14px; pointer-events:none; } @@ -166,17 +174,6 @@
- - - - - - - - - - -
-
+
- + - +
- + diff --git a/src/main/resources/public/js/game.js b/src/main/resources/public/js/game.js index d5c3ee1..b5bcd12 100644 --- a/src/main/resources/public/js/game.js +++ b/src/main/resources/public/js/game.js @@ -117,6 +117,22 @@ const optionsDiv = document.getElementById("options"); const resultP = document.getElementById("result"); const scoreboard = document.getElementById("scoreboard"); +// --- SVG helpers für Pie-Slices --- +const SVGNS = "http://www.w3.org/2000/svg"; +const VB = 1000; // viewBox 0..1000 +const CX = 500, CY = 500, R = 480; + +function polar(cx, cy, r, deg){ + const rad = (deg - 90) * Math.PI/180; // 0° oben + return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) }; +} +function wedgePath(cx, cy, r, a0, a1){ + const p0 = polar(cx, cy, r, a0); + const p1 = polar(cx, cy, r, a1); + const largeArc = ((a1 - a0 + 360) % 360) > 180 ? 1 : 0; + return `M ${cx} ${cy} L ${p0.x} ${p0.y} A ${r} ${r} 0 ${largeArc} 1 ${p1.x} ${p1.y} Z`; +} + // 6) Neue Runde anzeigen async function handleRoundStart({ ownerOptions, songUri, trackInfos }) { // UI zurücksetzen @@ -138,31 +154,45 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) { await window.playOnSpotify(songUri, username); } - // Buttons kreisförmig verteilen - requestAnimationFrame(() => { - const optsRect = optionsDiv.getBoundingClientRect(); - const radius = Math.min(optsRect.width, optsRect.height) / 2 - 40; // Abstand zum Rand - ownerOptions.forEach((user, i) => { - const btn = document.createElement("button"); - btn.textContent = user; - btn.classList.add("player-option"); - //const angle = 360 * i / ownerOptions.length; - //btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`; + // Optionen als Tortenstücke (SVG) rendern + const svg = document.createElementNS(SVGNS, "svg"); + svg.setAttribute("viewBox", `0 0 ${VB} ${VB}`); + svg.setAttribute("class", "options-svg"); + optionsDiv.innerHTML = ""; + optionsDiv.appendChild(svg); - const angle = (360 / ownerOptions.length) * i; - // zentrieren -> dann drehen -> nach außen schieben -> Text aufrecht drehen - btn.style.transform = `translate(-50%, -50%) rotate(${angle}deg) translate(0, -${radius}px) rotate(${-angle}deg)`; + const n = ownerOptions.length; + ownerOptions.forEach((user, i) => { + const a0 = (360 / n) * i; + const a1 = (360 / n) * (i + 1); - btn.addEventListener("click", () => { - socket.send(JSON.stringify({ - type: "guess", - username: username, - guess: user - })); - optionsDiv.querySelectorAll("button").forEach(b => b.disabled = true); - }); - optionsDiv.appendChild(btn); + const path = document.createElementNS(SVGNS, "path"); + path.setAttribute("d", wedgePath(CX, CY, R, a0, a1)); + path.setAttribute("class", "wedge"); + path.setAttribute("data-user", user); + path.addEventListener("click", () => { + socket.send(JSON.stringify({ + type: "guess", + username: username, + guess: user + })); + svg.querySelectorAll(".wedge").forEach(w => w.classList.add("disabled")); + path.classList.add("selected"); }); + + // Label mittig im Segment + const mid = (a0 + a1) / 2; + const P = polar(CX, CY, R * 0.58, mid); + const text = document.createElementNS(SVGNS, "text"); + text.setAttribute("x", P.x); + text.setAttribute("y", P.y); + text.setAttribute("class", "wedge-label"); + text.setAttribute("text-anchor", "middle"); + text.setAttribute("dominant-baseline", "middle"); + text.textContent = user; + + svg.appendChild(path); + svg.appendChild(text); }); // Start-Button ausblenden + Rundensektion einblenden @@ -210,6 +240,12 @@ function renderScoreboard(scores) { function handleRoundResult({ scores, guesses, owner }) { renderScoreboard(scores); + // korrektes Segment hervorheben + try { + document.querySelectorAll("#options .wedge").forEach(w => { + if (w.getAttribute("data-user") === owner) w.classList.add("correct"); + }); + } catch(e) {} resultP.innerHTML = ""; Object.entries(guesses).forEach(([user, guess]) => { @@ -268,5 +304,3 @@ async function playOnSpotify(trackUri, username) { } } window.playOnSpotify = playOnSpotify; - - From 6ddd5ea8aa506cceb4c7efa0817b16ec9166fe91 Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 23:03:30 +0200 Subject: [PATCH 10/11] piechart wip fast fertig (feinschliff fehlt) --- src/main/resources/public/game.html | 10 ++++---- src/main/resources/public/js/game.js | 36 ++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/main/resources/public/game.html b/src/main/resources/public/game.html index 1681340..2c4e32a 100644 --- a/src/main/resources/public/game.html +++ b/src/main/resources/public/game.html @@ -139,12 +139,14 @@ /* === Pie-Slices === */ #options svg.options-svg { width:100%; height:auto; display:block; border-radius:50%; } - .wedge { fill:#181818; stroke:#2a2a2a; stroke-width:2; cursor:pointer; transition:filter .15s, fill .2s; } + .wedge { fill:#181818; stroke: #ffffff; stroke-width:4; cursor:pointer; transition:filter .15s, fill .2s; } .wedge:hover { filter:brightness(1.08); } - .wedge.selected { fill:#1db954; } - .wedge.correct { fill:#1db954; } + .wedge.selected { fill: #0d351c; } + /* .wedge.correct { fill:#1db954; } */ + .wedge.correct { fill: var(--accent) !important; } /* grün */ + .wedge.wrong { fill: #e22134 !important; } /* rot */ .wedge.disabled { pointer-events:none; opacity:.85; } - .wedge-label { fill:#fff; font-weight:700; font-size:14px; pointer-events:none; } + .wedge-label { fill:#fff; font-weight:700; font-size:35px; pointer-events:none; } diff --git a/src/main/resources/public/js/game.js b/src/main/resources/public/js/game.js index b5bcd12..ee93bbb 100644 --- a/src/main/resources/public/js/game.js +++ b/src/main/resources/public/js/game.js @@ -184,6 +184,17 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) { const mid = (a0 + a1) / 2; const P = polar(CX, CY, R * 0.58, mid); const text = document.createElementNS(SVGNS, "text"); + + const span = a1 - a0; // Winkelbreite des Segments + const font = Math.max(18, Math.min(42, 26 * (span / 60))); + text.setAttribute("font-size", font); + text.setAttribute("font-weight", "800"); + // Bessere Lesbarkeit auf dunklem Slice + text.setAttribute("paint-order", "stroke"); + text.setAttribute("stroke", "#000"); + text.setAttribute("stroke-width", "2"); + text.setAttribute("fill", "#fff"); + // überschreibt CSS-Größe text.setAttribute("x", P.x); text.setAttribute("y", P.y); text.setAttribute("class", "wedge-label"); @@ -240,15 +251,31 @@ function renderScoreboard(scores) { function handleRoundResult({ scores, guesses, owner }) { renderScoreboard(scores); - // korrektes Segment hervorheben + try { - document.querySelectorAll("#options .wedge").forEach(w => { + const wedges = document.querySelectorAll("#options .wedge"); + + // Owner-Slice immer grün + wedges.forEach(w => { if (w.getAttribute("data-user") === owner) w.classList.add("correct"); }); - } catch(e) {} + + // Nur die EIGENE Auswahl einfärben: rot wenn falsch, sonst grün + const myGuess = guesses?.[username]; + if (myGuess) { + const myWedge = Array.from(wedges).find(w => w.getAttribute("data-user") === myGuess); + if (myWedge) { + if (myGuess === owner) { + myWedge.classList.add("correct"); + } else { + myWedge.classList.add("wrong"); // nur dieser wird rot + } + } + } + } catch (e) {} resultP.innerHTML = ""; - Object.entries(guesses).forEach(([user, guess]) => { + Object.entries(guesses || {}).forEach(([user, guess]) => { const correct = guess === owner; const icon = correct ? "✅" : "❌"; const delta = correct ? "+3" : "-1"; @@ -271,6 +298,7 @@ function handleRoundResult({ scores, guesses, owner }) { }; } + // 8) Spielende -> Start-Button zurück function handleGameEnd({ winner }) { resultP.textContent = `🎉 ${winner} hat gewonnen!`; From dac1a4b2378e375a53a661cb107dea34f82089fc Mon Sep 17 00:00:00 2001 From: eric <3024947@stud.hs-mannheim.de> Date: Sun, 10 Aug 2025 23:08:45 +0200 Subject: [PATCH 11/11] runde startet nicht mehr von alleine --- .../eric/Roullette/websocket/GameWebSocketHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/eric/Roullette/websocket/GameWebSocketHandler.java b/src/main/java/eric/Roullette/websocket/GameWebSocketHandler.java index ec4cdf8..45b5e16 100644 --- a/src/main/java/eric/Roullette/websocket/GameWebSocketHandler.java +++ b/src/main/java/eric/Roullette/websocket/GameWebSocketHandler.java @@ -185,10 +185,10 @@ private void broadcastRoundResult(String gameId) { }else{ // nächste Runde starten // ... - new Thread(() -> { - try { Thread.sleep(2000); } catch (InterruptedException ignored) {} - nextround(gameId); - }).start(); +// new Thread(() -> { +// try { Thread.sleep(2000); } catch (InterruptedException ignored) {} +// nextround(gameId); +// }).start(); } }