mehrere auswählbar
parent
b55314fd1a
commit
739db427ab
|
|
@ -1,8 +1,8 @@
|
||||||
|
// java
|
||||||
package eric.Roullette.websocket;
|
package eric.Roullette.websocket;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import eric.Roullette.service.GameService;
|
import eric.Roullette.service.GameService;
|
||||||
//import eric.Roullette.service.SpotifyAuthService;
|
|
||||||
import eric.Roullette.util.JsonUtil;
|
import eric.Roullette.util.JsonUtil;
|
||||||
import io.javalin.websocket.WsConfig;
|
import io.javalin.websocket.WsConfig;
|
||||||
import io.javalin.websocket.WsContext;
|
import io.javalin.websocket.WsContext;
|
||||||
|
|
@ -17,20 +17,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
public class GameWebSocketHandler {
|
public class GameWebSocketHandler {
|
||||||
|
|
||||||
private final GameService service;
|
private final GameService service;
|
||||||
//private final SpotifyAuthService authService;
|
|
||||||
|
|
||||||
// Spiel-ID → (Username → deren Guess)
|
// Spiel-ID → (Username → deren Guesses)
|
||||||
private final Map<String, Map<String, String>> currentGuesses = new ConcurrentHashMap<>();
|
private final Map<String, Map<String, List<String>>> currentGuesses = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// private final Map<String, List<String>> trackInfoCache = new ConcurrentHashMap<>();
|
// Cache: Spiel-ID → (Username → TrackInfos)
|
||||||
// private final Map<String, List<String>> allTracksCache = new ConcurrentHashMap<>();
|
private final Map<String, Map<String, List<String>>> playerTrackInfoCache = new ConcurrentHashMap<>();
|
||||||
//Map<gameId, Map<player, List<String>>
|
|
||||||
//private Map<String, Map<String, List<String>>> playerTracksCache = new ConcurrentHashMap<>();
|
|
||||||
private Map<String, Map<String, List<String>>> playerTrackInfoCache = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public GameWebSocketHandler(GameService gameService) {
|
public GameWebSocketHandler(GameService gameService) {
|
||||||
this.service = gameService;
|
this.service = gameService;
|
||||||
//this.authService = authService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,9 +37,7 @@ public class GameWebSocketHandler {
|
||||||
ws.onConnect(ctx -> {
|
ws.onConnect(ctx -> {
|
||||||
String gameId = ctx.pathParam("gameId");
|
String gameId = ctx.pathParam("gameId");
|
||||||
String username = ctx.queryParam("username");
|
String username = ctx.queryParam("username");
|
||||||
// Spiel- und Session-Registrierung
|
|
||||||
service.addPlayer(gameId, username);
|
service.addPlayer(gameId, username);
|
||||||
// Alle Clients über neue Spielerliste informieren
|
|
||||||
service.registerSession(gameId, ctx);
|
service.registerSession(gameId, ctx);
|
||||||
service.broadcastPlayers(gameId);
|
service.broadcastPlayers(gameId);
|
||||||
});
|
});
|
||||||
|
|
@ -55,16 +48,13 @@ public class GameWebSocketHandler {
|
||||||
String username = ctx.queryParam("username");
|
String username = ctx.queryParam("username");
|
||||||
service.removeSession(gameId, ctx);
|
service.removeSession(gameId, ctx);
|
||||||
|
|
||||||
// Spieler aus der Spielerliste entfernen
|
|
||||||
var game = service.getOrCreateGame(gameId);
|
var game = service.getOrCreateGame(gameId);
|
||||||
if (username != null && game.players().contains(username)) {
|
if (username != null && game.players().contains(username)) {
|
||||||
game.players().remove(username);
|
game.players().remove(username);
|
||||||
game.scores().remove(username);
|
game.scores().remove(username);
|
||||||
// Optional: auch die Tracks entfernen
|
|
||||||
game.playerTracks().remove(username);
|
game.playerTracks().remove(username);
|
||||||
}
|
}
|
||||||
service.broadcastPlayers(gameId);
|
service.broadcastPlayers(gameId);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Eingehende Nachrichten (Guesses & Player-Requests)
|
// Eingehende Nachrichten (Guesses & Player-Requests)
|
||||||
|
|
@ -74,62 +64,68 @@ public class GameWebSocketHandler {
|
||||||
String type = node.get("type").asText();
|
String type = node.get("type").asText();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "guess" -> {
|
case "guess" -> { // Legacy: Single-Guess
|
||||||
String user = node.get("username").asText();
|
String user = node.get("username").asText();
|
||||||
String guess = node.get("guess").asText();
|
String single = node.get("guess").asText();
|
||||||
// Guess speichern
|
Map<String, List<String>> byUser =
|
||||||
currentGuesses
|
currentGuesses.computeIfAbsent(gameId, id -> new ConcurrentHashMap<>());
|
||||||
.computeIfAbsent(gameId, id -> new ConcurrentHashMap<>())
|
byUser.put(user, List.of(single));
|
||||||
.put(user, guess);
|
|
||||||
// Wenn alle getippt haben, Ergebnis broadcasten
|
|
||||||
int numPlayers = service.getOrCreateGame(gameId).players().size();
|
int numPlayers = service.getOrCreateGame(gameId).players().size();
|
||||||
if (currentGuesses.get(gameId).size() == numPlayers) {
|
if (byUser.size() == numPlayers) {
|
||||||
broadcastRoundResult(gameId);
|
broadcastRoundResult(gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "submit-guesses" -> { // Multi-Select
|
||||||
|
String user = node.get("username").asText();
|
||||||
|
List<String> picks = new ArrayList<>();
|
||||||
|
JsonNode arr = node.get("guesses");
|
||||||
|
if (arr != null && arr.isArray()) {
|
||||||
|
arr.forEach(j -> picks.add(j.asText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, List<String>> byUser =
|
||||||
|
currentGuesses.computeIfAbsent(gameId, id -> new ConcurrentHashMap<>());
|
||||||
|
if (user != null && !picks.isEmpty()) {
|
||||||
|
byUser.put(user, new ArrayList<>(picks));
|
||||||
|
}
|
||||||
|
|
||||||
|
int numPlayers = service.getOrCreateGame(gameId).players().size();
|
||||||
|
if (byUser.size() == numPlayers) {
|
||||||
|
broadcastRoundResult(gameId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case "requestPlayers" -> service.broadcastPlayers(gameId);
|
case "requestPlayers" -> service.broadcastPlayers(gameId);
|
||||||
|
|
||||||
case "next-round" -> nextround(gameId);
|
case "next-round" -> nextround(gameId);
|
||||||
|
|
||||||
case "start-round" -> {
|
case "start-round" -> {
|
||||||
|
// Guesses für dieses Spiel zurücksetzen
|
||||||
|
currentGuesses.put(gameId, new ConcurrentHashMap<>());
|
||||||
|
|
||||||
var currentGame = service.getOrCreateGame(gameId);
|
var currentGame = service.getOrCreateGame(gameId);
|
||||||
if (currentGame.players().isEmpty()) return;
|
if (currentGame.players().isEmpty()) return;
|
||||||
// Tracks pro Spieler sammeln
|
|
||||||
Map<String, List<String>> allPlayerTracks = currentGame.playerTracks();
|
Map<String, List<String>> allPlayerTracks = currentGame.playerTracks();
|
||||||
// alle tracks sammeln
|
|
||||||
List<String> allTracks = allPlayerTracks.values().stream()
|
List<String> allTracks = allPlayerTracks.values().stream()
|
||||||
.flatMap(List::stream)
|
.flatMap(List::stream)
|
||||||
.toList();
|
.toList();
|
||||||
System.out.println("AlltracksCache für Spiel " + gameId + " hat " + allTracks.size() + " Songs (rundenstart)");
|
|
||||||
//Trackinfos für alle Spieler sammeln
|
|
||||||
|
|
||||||
if (playerTrackInfoCache.containsKey(gameId)) {
|
if (playerTrackInfoCache.containsKey(gameId)) {
|
||||||
// Wenn der Cache schon existiert, dann nur die Trackinfos nutzen
|
|
||||||
System.out.println("TrackInfosCache für Spiel " + gameId + " existiert bereits (rundenstart)");
|
|
||||||
// prüfen ob ein neuer spieler dazugekommen ist
|
|
||||||
if (allPlayerTracks.size() > playerTrackInfoCache.get(gameId).size()) {
|
if (allPlayerTracks.size() > playerTrackInfoCache.get(gameId).size()) {
|
||||||
System.out.println("Neuer Spieler hinzugefügt, Trackinfos werden aktualisiert (rundenstart)");
|
|
||||||
Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
||||||
// Cache für Trackinfos pro Spiel-ID aktualisieren
|
|
||||||
playerTrackInfoCache.put(gameId, allTrackInfos);
|
|
||||||
} else {
|
|
||||||
System.out.println("Keine neuen Spieler, Trackinfos bleiben unverändert (rundenstart)");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Wenn der Cache nicht existiert, dann Trackinfos sammeln
|
|
||||||
System.out.println("TrackInfosCache für Spiel " + gameId + " wird erstellt (rundenstart)");
|
|
||||||
Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
|
||||||
// Cache für Trackinfos pro Spiel-ID
|
|
||||||
playerTrackInfoCache.put(gameId, allTrackInfos);
|
playerTrackInfoCache.put(gameId, allTrackInfos);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
System.out.println("TrackInfosCache für Spiel " + gameId + " hat " + playerTrackInfoCache.get(gameId).size() + " Spieler (rundenstart)");
|
Map<String, List<String>> allTrackInfos = service.getTrackInfos(allPlayerTracks);
|
||||||
|
playerTrackInfoCache.put(gameId, allTrackInfos);
|
||||||
|
}
|
||||||
|
|
||||||
if (!allTracks.isEmpty()) {
|
if (!allTracks.isEmpty()) {
|
||||||
service.startRound(gameId, allTracks);
|
service.startRound(gameId, allTracks);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
broadcastRoundStart(gameId);
|
broadcastRoundStart(gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -137,9 +133,10 @@ public class GameWebSocketHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void nextround(String gameId) {
|
public void nextround(String gameId) {
|
||||||
|
currentGuesses.put(gameId, new ConcurrentHashMap<>()); // nur dieses Spiel leeren
|
||||||
var game = service.getOrCreateGame(gameId);
|
var game = service.getOrCreateGame(gameId);
|
||||||
if (game.players().isEmpty()) return;
|
if (game.players().isEmpty()) return;
|
||||||
// Songs von allen Spielern sammeln
|
|
||||||
List<String> allTracks = new ArrayList<>();
|
List<String> allTracks = new ArrayList<>();
|
||||||
for (String player : game.players()) {
|
for (String player : game.players()) {
|
||||||
allTracks.addAll(game.playerTracks().getOrDefault(player, List.of()));
|
allTracks.addAll(game.playerTracks().getOrDefault(player, List.of()));
|
||||||
|
|
@ -149,12 +146,9 @@ public class GameWebSocketHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runde im Service starten, um Song und Owner zu setzen
|
|
||||||
service.startRound(gameId, allTracks);
|
service.startRound(gameId, allTracks);
|
||||||
// Jetzt Broadcast mit den aktuellen Daten
|
|
||||||
broadcastRoundStart(gameId);
|
broadcastRoundStart(gameId);
|
||||||
}
|
}
|
||||||
// ----- Broadcast-Methoden -----
|
|
||||||
|
|
||||||
/** Broadcastet den Runden-Start (Song + Optionen) an alle Clients. */
|
/** Broadcastet den Runden-Start (Song + Optionen) an alle Clients. */
|
||||||
public void broadcastRoundStart(String gameId) {
|
public void broadcastRoundStart(String gameId) {
|
||||||
|
|
@ -163,7 +157,6 @@ public class GameWebSocketHandler {
|
||||||
String songUri = game.currentSong();
|
String songUri = game.currentSong();
|
||||||
List<String> allTracks = game.allTracks();
|
List<String> allTracks = game.allTracks();
|
||||||
Map<String, List<String>> trackInfos = playerTrackInfoCache.get(gameId);
|
Map<String, List<String>> trackInfos = playerTrackInfoCache.get(gameId);
|
||||||
// Cache pro Spiel-ID nutzen
|
|
||||||
|
|
||||||
String msg = JsonUtil.toJson(Map.of(
|
String msg = JsonUtil.toJson(Map.of(
|
||||||
"type", "round-start",
|
"type", "round-start",
|
||||||
|
|
@ -176,41 +169,39 @@ public class GameWebSocketHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Broadcastet das Rundenergebnis (Scores, wer richtig, wer getippt hat). */
|
/** Broadcastet das Rundenergebnis (Scores, wer richtig, wer getippt hat). */
|
||||||
// Punkte für alle Guess-Teilnehmer anpassen
|
|
||||||
private void broadcastRoundResult(String gameId) {
|
private void broadcastRoundResult(String gameId) {
|
||||||
var game = service.getOrCreateGame(gameId);
|
var game = service.getOrCreateGame(gameId);
|
||||||
Map<String,Integer> scores = game.scores();
|
|
||||||
Map<String,String> guesses = currentGuesses.remove(gameId);
|
|
||||||
String owner = game.currentOwner();
|
String owner = game.currentOwner();
|
||||||
|
|
||||||
// System.out.println("Owner: " + owner);
|
Map<String, List<String>> byUser =
|
||||||
// System.out.println("Guesses: " + guesses);
|
currentGuesses.getOrDefault(gameId, Collections.emptyMap());
|
||||||
// System.out.println("Scores vor Auswertung: " + scores);
|
|
||||||
|
|
||||||
// Für jeden Tippenden Score anpassen
|
// Scoring: +3 falls Auswahl Owner enthält, -1 pro falschem Tipp
|
||||||
for (Map.Entry<String, String> entry : guesses.entrySet()) {
|
for (var e : byUser.entrySet()) {
|
||||||
String guesser = entry.getKey();
|
String user = e.getKey();
|
||||||
boolean correct = owner.equals(entry.getValue());
|
List<String> guesses = e.getValue();
|
||||||
scores.merge(guesser, correct ? 3 : -1, Integer::sum);
|
if (guesses == null) continue;
|
||||||
|
boolean correct = owner != null && guesses.contains(owner);
|
||||||
|
int wrong = guesses.size() - (correct ? 1 : 0);
|
||||||
|
int delta = (correct ? 3 : 0) - wrong;
|
||||||
|
if (delta != 0) game.scores().merge(user, delta, Integer::sum);
|
||||||
}
|
}
|
||||||
// System.out.println("Owner: " + owner);
|
|
||||||
// System.out.println("Guesses: " + guesses);
|
var scores = game.scores();
|
||||||
// System.out.println("Scores nach Auswertung: " + scores);
|
|
||||||
|
|
||||||
String msg = JsonUtil.toJson(Map.of(
|
String msg = JsonUtil.toJson(Map.of(
|
||||||
"type", "round-result",
|
"type", "round-result",
|
||||||
"scores", scores,
|
"scores", scores,
|
||||||
"guesses", guesses,
|
"guesses", byUser,
|
||||||
"owner", owner
|
"owner", owner
|
||||||
));
|
));
|
||||||
broadcastToAll(gameId, msg);
|
broadcastToAll(gameId, msg);
|
||||||
|
|
||||||
// Prüfe auf Gewinner
|
// Gewinner prüfen
|
||||||
// Nur beenden, wenn EIN Spieler allein die höchste Punktzahl >= score hat
|
int winScore = 6;
|
||||||
int score = 6;
|
int max = scores.values().stream().mapToInt(Integer::intValue).max().orElse(0);
|
||||||
int max = scores.values().stream().max(Integer::compareTo).orElse(0);
|
|
||||||
List<String> topScorers = scores.entrySet().stream()
|
List<String> topScorers = scores.entrySet().stream()
|
||||||
.filter(e -> e.getValue() == max && max >= score)
|
.filter(e -> e.getValue() == max && max >= winScore)
|
||||||
.map(Map.Entry::getKey)
|
.map(Map.Entry::getKey)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
|
@ -224,19 +215,10 @@ private void broadcastRoundResult(String gameId) {
|
||||||
broadcastToAll(gameId, winMsg);
|
broadcastToAll(gameId, winMsg);
|
||||||
game.scores().replaceAll((user, pts) -> 0); // Reset Scores
|
game.scores().replaceAll((user, pts) -> 0); // Reset Scores
|
||||||
}
|
}
|
||||||
// else{
|
|
||||||
// // nächste Runde starten
|
|
||||||
// // ...
|
|
||||||
//// new Thread(() -> {
|
|
||||||
//// try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
|
|
||||||
//// nextround(gameId);
|
|
||||||
//// }).start();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hilfsmethode: Sendet eine Nachricht an alle WebSocket-Sessions eines Spiels. */
|
/** Hilfsmethode: Sendet eine Nachricht an alle WebSocket-Sessions eines Spiels. */
|
||||||
private void broadcastToAll(String gameId, String msg) {
|
private void broadcastToAll(String gameId, String msg) {
|
||||||
// Holt alle WsContext, die der Service beim Connect registriert hat
|
|
||||||
Set<WsContext> sessions = service.getSessions(gameId);
|
Set<WsContext> sessions = service.getSessions(gameId);
|
||||||
if (sessions == null) return;
|
if (sessions == null) return;
|
||||||
sessions.stream()
|
sessions.stream()
|
||||||
|
|
@ -247,5 +229,4 @@ private void broadcastRoundResult(String gameId) {
|
||||||
} catch (Exception ignore) {}
|
} catch (Exception ignore) {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +133,25 @@ function wedgePath(cx, cy, r, a0, a1){
|
||||||
return `M ${cx} ${cy} L ${p0.x} ${p0.y} A ${r} ${r} 0 ${largeArc} 1 ${p1.x} ${p1.y} Z`;
|
return `M ${cx} ${cy} L ${p0.x} ${p0.y} A ${r} ${r} 0 ${largeArc} 1 ${p1.x} ${p1.y} Z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auswahl-Set für Multi-Guess (pro Runde zurückgesetzt)
|
||||||
|
let selectedGuesses = new Set();
|
||||||
|
let lastScores = null;
|
||||||
|
|
||||||
|
function ensureSubmitBtn(optionsDiv) {
|
||||||
|
let btn = document.getElementById("submitGuesses");
|
||||||
|
if (!btn) {
|
||||||
|
btn = document.createElement("button");
|
||||||
|
btn.id = "submitGuesses";
|
||||||
|
btn.className = "btn btn-primary";
|
||||||
|
btn.style.marginTop = "8px";
|
||||||
|
btn.textContent = "Tipps abgeben";
|
||||||
|
// Button direkt unter den Optionen platzieren
|
||||||
|
optionsDiv.parentElement.appendChild(btn);
|
||||||
|
}
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 6) Neue Runde anzeigen
|
// 6) Neue Runde anzeigen
|
||||||
async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
|
async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
|
||||||
// UI zurücksetzen
|
// UI zurücksetzen
|
||||||
|
|
@ -141,6 +160,7 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
|
||||||
songEmbed.innerHTML = "";
|
songEmbed.innerHTML = "";
|
||||||
//scoreboard zurücksetzen
|
//scoreboard zurücksetzen
|
||||||
scoreboard.innerHTML = "";
|
scoreboard.innerHTML = "";
|
||||||
|
selectedGuesses = new Set();
|
||||||
ownerOptions.forEach(user => {
|
ownerOptions.forEach(user => {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
li.textContent = `${user}: 0 Punkte`;
|
li.textContent = `${user}: 0 Punkte`;
|
||||||
|
|
@ -176,14 +196,23 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
|
||||||
path.setAttribute("d", wedgePath(CX, CY, R, a0, a1));
|
path.setAttribute("d", wedgePath(CX, CY, R, a0, a1));
|
||||||
path.setAttribute("class", "wedge");
|
path.setAttribute("class", "wedge");
|
||||||
path.setAttribute("data-user", user);
|
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");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Toggle-Selection statt sofort zu senden
|
||||||
path.addEventListener("click", () => {
|
path.addEventListener("click", () => {
|
||||||
socket.send(JSON.stringify({
|
const u = path.getAttribute("data-user");
|
||||||
type: "guess",
|
if (path.classList.contains("disabled")) return;
|
||||||
username: username,
|
if (path.classList.toggle("selected")) selectedGuesses.add(u);
|
||||||
guess: user
|
else selectedGuesses.delete(u);
|
||||||
}));
|
submitBtn.disabled = selectedGuesses.size === 0;
|
||||||
svg.querySelectorAll(".wedge").forEach(w => w.classList.add("disabled"));
|
|
||||||
path.classList.add("selected");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Label mittig im Segment
|
// Label mittig im Segment
|
||||||
|
|
@ -212,6 +241,32 @@ async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
|
||||||
svg.appendChild(text);
|
svg.appendChild(text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Submit-Button dynamisch hinzufügen
|
||||||
|
let submitBtn = document.getElementById("submitGuesses");
|
||||||
|
if (!submitBtn) {
|
||||||
|
submitBtn = document.createElement("button");
|
||||||
|
submitBtn.id = "submitGuesses";
|
||||||
|
submitBtn.className = "btn btn-primary";
|
||||||
|
submitBtn.style.marginTop = "8px";
|
||||||
|
submitBtn.textContent = "submit";
|
||||||
|
optionsDiv.parentElement.appendChild(submitBtn);
|
||||||
|
}
|
||||||
|
submitBtn.hidden = false;
|
||||||
|
// warum disabled?
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
submitBtn.onclick = () => {
|
||||||
|
if (!selectedGuesses.size) return;
|
||||||
|
const guesses = Array.from(selectedGuesses);
|
||||||
|
socket.send(JSON.stringify({
|
||||||
|
type: "submit-guesses",
|
||||||
|
username,
|
||||||
|
guesses
|
||||||
|
}));
|
||||||
|
submitBtn.disabled = true;
|
||||||
|
// Sperre Eingaben nach Abgabe
|
||||||
|
optionsDiv.querySelectorAll(".wedge").forEach(w => w.classList.add("disabled"));
|
||||||
|
};
|
||||||
|
|
||||||
// Start-Button ausblenden + Rundensektion einblenden
|
// Start-Button ausblenden + Rundensektion einblenden
|
||||||
startBtn.hidden = true;
|
startBtn.hidden = true;
|
||||||
startBtn.disabled = true;
|
startBtn.disabled = true;
|
||||||
|
|
@ -260,13 +315,20 @@ function renderScoreboard(scores) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastScores = null;
|
//let lastScores = null;
|
||||||
|
|
||||||
|
|
||||||
function handleRoundResult({ scores, guesses, owner }) {
|
function handleRoundResult({ scores, guesses, owner }) {
|
||||||
renderScoreboard(scores);
|
renderScoreboard(scores);
|
||||||
lastScores = scores;
|
lastScores = scores;
|
||||||
|
(() => {
|
||||||
|
// Button verstecken/disable und Eingaben sperren
|
||||||
|
const sb = document.getElementById('submitGuesses');
|
||||||
|
if (sb) { sb.disabled = true; sb.hidden = true; }
|
||||||
|
document.querySelectorAll('#options .wedge').forEach(w => w.classList.add('disabled'));
|
||||||
|
// lokale Auswahl leeren (optional)
|
||||||
|
try { selectedGuesses.clear?.(); } catch(_) {}
|
||||||
|
})();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const wedges = document.querySelectorAll("#options .wedge");
|
const wedges = document.querySelectorAll("#options .wedge");
|
||||||
|
|
@ -277,26 +339,51 @@ function handleRoundResult({ scores, guesses, owner }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Nur die EIGENE Auswahl einfärben: rot wenn falsch, sonst grün
|
// Nur die EIGENE Auswahl einfärben: rot wenn falsch, sonst grün
|
||||||
const myGuess = guesses?.[username];
|
// const myGuess = guesses?.[username];
|
||||||
if (myGuess) {
|
// if (myGuess) {
|
||||||
const myWedge = Array.from(wedges).find(w => w.getAttribute("data-user") === myGuess);
|
// const myWedge = Array.from(wedges).find(w => w.getAttribute("data-user") === myGuess);
|
||||||
if (myWedge) {
|
// if (myWedge) {
|
||||||
if (myGuess === owner) {
|
// if (myGuess === owner) {
|
||||||
myWedge.classList.add("correct");
|
// myWedge.classList.add("correct");
|
||||||
} else {
|
// } else {
|
||||||
myWedge.classList.add("wrong"); // nur dieser wird rot
|
// myWedge.classList.add("wrong"); // nur dieser wird rot
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (e) {}
|
||||||
|
const my = guesses?.[username];
|
||||||
|
const myArr = Array.isArray(my) ? my : (typeof my === "string" ? [my] : []);
|
||||||
|
if (myArr.length) {
|
||||||
|
myArr.forEach(sel => {
|
||||||
|
const w = Array.from(wedges).find(x => x.getAttribute("data-user") === sel);
|
||||||
|
if (!w) return;
|
||||||
|
if (sel === owner) w.classList.add("correct");
|
||||||
|
else w.classList.add("wrong");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
|
||||||
|
// resultP.innerHTML = "";
|
||||||
|
// Object.entries(guesses || {}).forEach(([user, guess]) => {
|
||||||
|
// const correct = guess === owner;
|
||||||
|
// const icon = correct ? "✅" : "❌";
|
||||||
|
// const delta = correct ? "+3" : "-1";
|
||||||
|
// const p = document.createElement("p");
|
||||||
|
// p.textContent = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`;
|
||||||
|
// resultP.appendChild(p);
|
||||||
|
// });
|
||||||
resultP.innerHTML = "";
|
resultP.innerHTML = "";
|
||||||
Object.entries(guesses || {}).forEach(([user, guess]) => {
|
Object.entries(guesses || {}).forEach(([user, g]) => {
|
||||||
const correct = guess === owner;
|
const list = Array.isArray(g) ? g : (typeof g === "string" ? [g] : []);
|
||||||
|
const correct = list.includes(owner);
|
||||||
|
const wrongCount = list.length - (correct ? 1 : 0);
|
||||||
|
const delta = (correct ? 3 : 0) - wrongCount;
|
||||||
const icon = correct ? "✅" : "❌";
|
const icon = correct ? "✅" : "❌";
|
||||||
const delta = correct ? "+3" : "-1";
|
const picks = list.length ? list.join(", ") : "—";
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
p.textContent = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`;
|
p.textContent = `${icon} ${user} hat auf ${picks} getippt${correct ? " (richtig!)" : ""} [${delta >= 0 ? "+" : ""}${delta}]`;
|
||||||
resultP.appendChild(p);
|
resultP.appendChild(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -311,6 +398,8 @@ function handleRoundResult({ scores, guesses, owner }) {
|
||||||
startBtn.hidden = true;
|
startBtn.hidden = true;
|
||||||
startBtn.disabled = true;
|
startBtn.disabled = true;
|
||||||
roundArea.hidden = true;
|
roundArea.hidden = true;
|
||||||
|
const submitBtn = document.getElementById("submitGuesses");
|
||||||
|
if (submitBtn) submitBtn.hidden = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue