251 lines
7.2 KiB
JavaScript
251 lines
7.2 KiB
JavaScript
const words = [
|
|
"Kekse",
|
|
"Schach",
|
|
"Giraffe",
|
|
"Spaghetti",
|
|
"Rakete",
|
|
"Blume",
|
|
"Kino",
|
|
"Drachen",
|
|
"Buch",
|
|
"Kaffee",
|
|
"Berg",
|
|
"Pizza",
|
|
"Wolke",
|
|
"Bus",
|
|
"Zitrone",
|
|
"Gitarre",
|
|
"Meer",
|
|
"Bananen",
|
|
"Loewe",
|
|
"Regen",
|
|
];
|
|
|
|
const state = {
|
|
players: [],
|
|
currentIndex: 0,
|
|
imposterIndex: -1,
|
|
secretWord: "",
|
|
revealed: false,
|
|
discussionIndex: 0,
|
|
};
|
|
|
|
const elements = {
|
|
playerInput: document.querySelector("#player-input"),
|
|
addPlayer: document.querySelector("#add-player"),
|
|
playerList: document.querySelector("#player-list"),
|
|
startGame: document.querySelector("#start-game"),
|
|
clearPlayers: document.querySelector("#clear-players"),
|
|
setupHint: document.querySelector("#setup-hint"),
|
|
setup: document.querySelector("#setup"),
|
|
round: document.querySelector("#round"),
|
|
discussion: document.querySelector("#discussion"),
|
|
done: document.querySelector("#done"),
|
|
roundCount: document.querySelector("#round-count"),
|
|
restart: document.querySelector("#restart"),
|
|
currentPlayer: document.querySelector("#current-player"),
|
|
roundScreen: document.querySelector("#round-screen"),
|
|
handoff: document.querySelector("#handoff"),
|
|
revealCard: document.querySelector("#reveal-card"),
|
|
revealWord: document.querySelector("#reveal-word"),
|
|
discussionCount: document.querySelector("#discussion-count"),
|
|
discussionPlayer: document.querySelector("#discussion-player"),
|
|
discussionNext: document.querySelector("#discussion-next"),
|
|
discussionRestart: document.querySelector("#discussion-restart"),
|
|
imposterFound: document.querySelector("#imposter-found"),
|
|
playAgain: document.querySelector("#play-again"),
|
|
editPlayers: document.querySelector("#edit-players"),
|
|
};
|
|
|
|
const MIN_PLAYERS = 3;
|
|
const STORAGE_KEY = "imposterPlayers";
|
|
|
|
const updatePlayerList = () => {
|
|
elements.playerList.innerHTML = "";
|
|
state.players.forEach((player, index) => {
|
|
const item = document.createElement("li");
|
|
const name = document.createElement("span");
|
|
name.textContent = player;
|
|
const remove = document.createElement("button");
|
|
remove.type = "button";
|
|
remove.textContent = "Entfernen";
|
|
remove.addEventListener("click", () => {
|
|
state.players.splice(index, 1);
|
|
updatePlayerList();
|
|
});
|
|
item.append(name, remove);
|
|
elements.playerList.appendChild(item);
|
|
});
|
|
|
|
const ready = state.players.length >= MIN_PLAYERS;
|
|
elements.startGame.disabled = !ready;
|
|
elements.setupHint.textContent = ready
|
|
? `${state.players.length} Spieler bereit.`
|
|
: `Mindestens ${MIN_PLAYERS} Spieler.`;
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(state.players));
|
|
};
|
|
|
|
const addPlayer = () => {
|
|
const raw = elements.playerInput.value.trim();
|
|
if (!raw) {
|
|
return;
|
|
}
|
|
state.players.push(raw);
|
|
elements.playerInput.value = "";
|
|
updatePlayerList();
|
|
elements.playerInput.focus();
|
|
};
|
|
|
|
const startRound = () => {
|
|
state.currentIndex = 0;
|
|
state.imposterIndex = Math.floor(Math.random() * state.players.length);
|
|
state.secretWord = words[Math.floor(Math.random() * words.length)];
|
|
state.revealed = false;
|
|
elements.revealCard.classList.add("flip-card--instant");
|
|
elements.setup.classList.add("hidden");
|
|
elements.discussion.classList.add("hidden");
|
|
elements.done.classList.add("hidden");
|
|
elements.round.classList.remove("hidden");
|
|
updateRoundView();
|
|
requestAnimationFrame(() => {
|
|
elements.revealCard.classList.remove("flip-card--instant");
|
|
});
|
|
};
|
|
|
|
const updateRoundView = () => {
|
|
const total = state.players.length;
|
|
const current = state.currentIndex + 1;
|
|
elements.roundCount.textContent = `Phase 1: Zuweisung \u2014 Spieler ${current} / ${total}`;
|
|
elements.currentPlayer.textContent = state.players[state.currentIndex];
|
|
elements.roundScreen.classList.remove("hidden");
|
|
const isImposter = state.currentIndex === state.imposterIndex;
|
|
elements.revealWord.textContent = isImposter ? "IMPOSTER" : state.secretWord;
|
|
elements.revealCard.classList.remove("flipped");
|
|
state.revealed = false;
|
|
};
|
|
|
|
const toggleReveal = () => {
|
|
elements.revealCard.classList.toggle("flipped");
|
|
state.revealed = !state.revealed;
|
|
};
|
|
|
|
const handoffPlayer = () => {
|
|
elements.revealCard.classList.add("flip-card--instant");
|
|
elements.revealCard.classList.remove("flipped");
|
|
state.currentIndex += 1;
|
|
if (state.currentIndex >= state.players.length) {
|
|
startDiscussion();
|
|
return;
|
|
}
|
|
updateRoundView();
|
|
requestAnimationFrame(() => {
|
|
elements.revealCard.classList.remove("flip-card--instant");
|
|
});
|
|
};
|
|
|
|
const startDiscussion = () => {
|
|
state.discussionIndex = 0;
|
|
elements.round.classList.add("hidden");
|
|
elements.done.classList.add("hidden");
|
|
elements.discussion.classList.remove("hidden");
|
|
updateDiscussionView();
|
|
};
|
|
|
|
const updateDiscussionView = () => {
|
|
const total = state.players.length;
|
|
const current = state.discussionIndex + 1;
|
|
elements.discussionCount.textContent = `Phase 2: Runde \u2014 Spieler ${current} / ${total}`;
|
|
elements.discussionPlayer.textContent = state.players[state.discussionIndex];
|
|
};
|
|
|
|
const nextDiscussion = () => {
|
|
if (state.players.length === 0) {
|
|
return;
|
|
}
|
|
state.discussionIndex = (state.discussionIndex + 1) % state.players.length;
|
|
updateDiscussionView();
|
|
};
|
|
|
|
const endDiscussion = () => {
|
|
elements.discussion.classList.add("hidden");
|
|
elements.done.classList.remove("hidden");
|
|
};
|
|
|
|
const resetToSetup = () => {
|
|
elements.round.classList.add("hidden");
|
|
elements.discussion.classList.add("hidden");
|
|
elements.done.classList.add("hidden");
|
|
elements.setup.classList.remove("hidden");
|
|
};
|
|
|
|
const clearPlayers = () => {
|
|
state.players = [];
|
|
updatePlayerList();
|
|
};
|
|
|
|
const loadPlayers = () => {
|
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
if (!saved) {
|
|
return;
|
|
}
|
|
try {
|
|
const parsed = JSON.parse(saved);
|
|
if (Array.isArray(parsed)) {
|
|
state.players = parsed.filter((name) => typeof name === "string");
|
|
}
|
|
} catch (error) {
|
|
localStorage.removeItem(STORAGE_KEY);
|
|
}
|
|
};
|
|
|
|
elements.addPlayer.addEventListener("click", addPlayer);
|
|
elements.playerInput.addEventListener("keydown", (event) => {
|
|
if (event.key === "Enter") {
|
|
addPlayer();
|
|
}
|
|
});
|
|
|
|
elements.clearPlayers.addEventListener("click", clearPlayers);
|
|
elements.startGame.addEventListener("click", startRound);
|
|
elements.handoff.addEventListener("click", handoffPlayer);
|
|
elements.revealCard.addEventListener("click", toggleReveal);
|
|
elements.discussionNext.addEventListener("click", nextDiscussion);
|
|
elements.discussionRestart.addEventListener("click", startRound);
|
|
elements.imposterFound.addEventListener("click", endDiscussion);
|
|
elements.restart.addEventListener("click", startRound);
|
|
elements.playAgain.addEventListener("click", startRound);
|
|
elements.editPlayers.addEventListener("click", resetToSetup);
|
|
|
|
loadPlayers();
|
|
updatePlayerList();
|
|
|
|
if ("serviceWorker" in navigator) {
|
|
window.addEventListener("load", async () => {
|
|
const reg = await navigator.serviceWorker.register("./sw.js");
|
|
|
|
const showUpdate = () => {
|
|
document.querySelector("#update-banner").classList.remove("hidden");
|
|
document.querySelector("#update-btn").addEventListener("click", () => {
|
|
reg.waiting.postMessage("SKIP_WAITING");
|
|
});
|
|
};
|
|
|
|
if (reg.waiting) {
|
|
showUpdate();
|
|
}
|
|
|
|
reg.addEventListener("updatefound", () => {
|
|
const newSW = reg.installing;
|
|
newSW.addEventListener("statechange", () => {
|
|
if (newSW.state === "installed" && navigator.serviceWorker.controller) {
|
|
showUpdate();
|
|
}
|
|
});
|
|
});
|
|
|
|
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
|
window.location.reload();
|
|
});
|
|
});
|
|
}
|