237 lines
7.8 KiB
JavaScript
237 lines
7.8 KiB
JavaScript
// public/js/game.js
|
|
|
|
import { getParam, renderList } from "./utils.js";
|
|
import { setupStartRound } from "./start-round.js";
|
|
|
|
const gameId = getParam("gameId");
|
|
const username = getParam("username");
|
|
|
|
// 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)
|
|
(function copyCodeToClipboard(code) {
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(code)
|
|
.then(() => console.log(`GameCode ${code} copied to clipboard`))
|
|
.catch(err => console.error("Clipboard write failed:", err));
|
|
} else {
|
|
const ta = document.createElement("textarea");
|
|
ta.value = code;
|
|
ta.style.position = "fixed";
|
|
ta.style.left = "-9999px";
|
|
document.body.appendChild(ta);
|
|
ta.focus();
|
|
ta.select();
|
|
try {
|
|
document.execCommand("copy");
|
|
console.log(`GameCode ${code} copied via execCommand`);
|
|
} catch (err) {
|
|
console.error("Fallback copy failed:", err);
|
|
}
|
|
document.body.removeChild(ta);
|
|
}
|
|
})(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
|
|
document.getElementById("gameId").textContent = gameId;
|
|
|
|
// 5) WebSocket mit Reconnect-Logik
|
|
let socket;
|
|
function connectWebSocket() {
|
|
const protocol = location.protocol === "https:" ? "wss" : "ws";
|
|
socket = new WebSocket(
|
|
`${protocol}://${location.host}/ws/${gameId}?username=${encodeURIComponent(username)}`
|
|
);
|
|
|
|
socket.addEventListener("open", () => {
|
|
console.log("WebSocket connected. Requesting player list...");
|
|
socket.send(JSON.stringify({ type: "requestPlayers" }));
|
|
setupStartRound(socket);
|
|
});
|
|
|
|
socket.addEventListener("message", ({ data }) => {
|
|
console.log("WS-Rohdaten:", data);
|
|
const msg = JSON.parse(data);
|
|
|
|
switch (msg.type) {
|
|
case "players":
|
|
console.log("Empfangene Spieler:", msg.players);
|
|
renderList("#playersList", msg.players, username);
|
|
break;
|
|
case "reload":
|
|
window.location.reload();
|
|
break;
|
|
case "round-start":
|
|
handleRoundStart(msg);
|
|
break;
|
|
case "round-result":
|
|
handleRoundResult(msg);
|
|
break;
|
|
case "game-end":
|
|
handleGameEnd(msg);
|
|
break;
|
|
default:
|
|
console.warn("Unknown WS message type:", msg.type);
|
|
}
|
|
});
|
|
|
|
socket.addEventListener("close", () => {
|
|
console.warn("WebSocket geschlossen, versuche erneut zu verbinden...");
|
|
setTimeout(connectWebSocket, 2000);
|
|
});
|
|
|
|
socket.addEventListener("error", (e) => {
|
|
console.error("WebSocket Fehler:", e);
|
|
socket.close();
|
|
});
|
|
}
|
|
connectWebSocket();
|
|
|
|
// Zugriff auf DOM-Elemente
|
|
const startBtn = document.getElementById("startRound");
|
|
const roundArea = document.getElementById("roundArea");
|
|
const songEmbed = document.getElementById("songEmbed");
|
|
const optionsDiv = document.getElementById("options");
|
|
const resultP = document.getElementById("result");
|
|
const scoreboard = document.getElementById("scoreboard");
|
|
|
|
// 8) Funktion zum Anzeigen einer neuen Runde
|
|
function handleRoundStart({ ownerOptions, songUri, allTracks, trackInfos }) {
|
|
// UI zurücksetzen
|
|
resultP.textContent = "";
|
|
optionsDiv.innerHTML = "";
|
|
songEmbed.innerHTML = "";
|
|
|
|
// Song einbetten
|
|
const trackId = songUri.split(":")[2];
|
|
songEmbed.innerHTML = `
|
|
<iframe
|
|
src="https://open.spotify.com/embed/track/${trackId}"
|
|
width="100%" height="80" frameborder="0"
|
|
allow="encrypted-media">
|
|
</iframe>`;
|
|
|
|
if (window.playOnSpotify && typeof window.playOnSpotify === "function") {
|
|
window.playOnSpotify(songUri, username);
|
|
}
|
|
|
|
// Dynamische Kreisverteilung der Buttons
|
|
// Warten, bis #options gerendert ist
|
|
setTimeout(() => {
|
|
const optsRect = optionsDiv.getBoundingClientRect();
|
|
const radius = Math.min(optsRect.width, optsRect.height) / 2 - 50; // 50px 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)`;
|
|
btn.addEventListener("click", () => {
|
|
socket.send(JSON.stringify({
|
|
type: "guess",
|
|
username: username,
|
|
guess: user
|
|
}));
|
|
optionsDiv.querySelectorAll("button").forEach(b => b.disabled = true);
|
|
});
|
|
optionsDiv.appendChild(btn);
|
|
});
|
|
}, 0);
|
|
|
|
startBtn.hidden = true;
|
|
roundArea.hidden = false;
|
|
|
|
const songList = document.getElementById("songList");
|
|
songList.innerHTML = "";
|
|
if (Array.isArray(trackInfos)) {
|
|
trackInfos.forEach(trackInfo => {
|
|
const li = document.createElement("li");
|
|
li.textContent = trackInfo;
|
|
songList.appendChild(li);
|
|
});
|
|
}
|
|
}
|
|
|
|
// 9) Funktion zum Anzeigen des Ergebnisses
|
|
function renderScoreboard(scores) {
|
|
scoreboard.innerHTML = "";
|
|
Object.entries(scores).forEach(([user, pts]) => {
|
|
const li = document.createElement("li");
|
|
li.textContent = `${user}: ${pts} Punkte`;
|
|
scoreboard.append(li);
|
|
});
|
|
}
|
|
|
|
function handleRoundResult({ scores, guesses, owner }) {
|
|
renderScoreboard(scores);
|
|
|
|
resultP.innerHTML = "";
|
|
Object.entries(guesses).forEach(([user, guess]) => {
|
|
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;
|
|
resultP.appendChild(p);
|
|
});
|
|
|
|
const nextBtn = document.getElementById("nextRound");
|
|
nextBtn.hidden = false;
|
|
nextBtn.disabled = false;
|
|
nextBtn.onclick = () => {
|
|
socket.send(JSON.stringify({ type: "next-round" }));
|
|
nextBtn.hidden = true;
|
|
nextBtn.disabled = true;
|
|
resultP.textContent = "";
|
|
startBtn.hidden = true;
|
|
startBtn.disabled = true;
|
|
roundArea.hidden = true;
|
|
};
|
|
}
|
|
|
|
function handleGameEnd({winner}) {
|
|
resultP.textContent = `🎉 ${winner} hat gewonnen!`;
|
|
setTimeout(() => {
|
|
startBtn.hidden = false;
|
|
roundArea.hidden = true;
|
|
startBtn.disabled = false;
|
|
scoreboard.innerHTML = "";
|
|
}, 6000);
|
|
}
|
|
|
|
// Spotify-Playback Funktion
|
|
async function playOnSpotify(trackUri, username) {
|
|
const deviceId = document.getElementById("deviceSelect")?.value;
|
|
if (!deviceId) {
|
|
alert("Bitte ein Wiedergabegerät auswählen!");
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch("/api/spotify/play", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ username, device_id: deviceId, track_uri: trackUri })
|
|
});
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
alert(`Fehler beim Abspielen: ${error}`);
|
|
}
|
|
} catch (err) {
|
|
alert(`Netzwerkfehler: ${err.message}`);
|
|
}
|
|
}
|
|
window.playOnSpotify = playOnSpotify;
|