mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-27 00:07:50 +01:00
re-name static directory, move stylesheet to its own folder
This commit is contained in:
57
javascript/cards.js
Normal file
57
javascript/cards.js
Normal file
@@ -0,0 +1,57 @@
|
||||
export let cards = [
|
||||
{
|
||||
role: "Villager",
|
||||
team: "good",
|
||||
description: "During the day, find the wolves and kill them.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Werewolf",
|
||||
team: "evil",
|
||||
description: "During the night, choose a villager to kill. Don't get killed.",
|
||||
isTypeOfWerewolf: true
|
||||
},
|
||||
{
|
||||
role: "Dream Wolf",
|
||||
team: "evil",
|
||||
description: "If a Werewolf dies, you become a Werewolf. You do not wake up with the Werewolves until this happens. You count for parity only after converting to a wolf.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Minion",
|
||||
team: "evil",
|
||||
description: "You are an evil villager - you know who the wolves are, and you want them to win.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Seer",
|
||||
team: "good",
|
||||
description: "During each night, choose one person. The moderator will tell you whether that player is a wolf.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Shadow",
|
||||
team: "evil",
|
||||
description: "If the Seer checks you, the Seer dies that night instead of whoever the wolves chose to kill. Reveal" +
|
||||
" yourself to the moderator.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Hunter",
|
||||
team: "good",
|
||||
description: "If you are alive with a wolf at the end of the game, you best the wolf, and the village wins.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Sorcerer",
|
||||
team: "good",
|
||||
description: "Once a game, change who the wolves are going to kill to someone else, including yourself.",
|
||||
isTypeOfWerewolf: false
|
||||
},
|
||||
{
|
||||
role: "Mason",
|
||||
team: "good",
|
||||
description: "Masons know who other Masons are. Wake them up to see each other on the first night.",
|
||||
isTypeOfWerewolf: false
|
||||
}
|
||||
];
|
||||
407
javascript/game.js
Normal file
407
javascript/game.js
Normal file
@@ -0,0 +1,407 @@
|
||||
import {utility} from './util.js'
|
||||
|
||||
const socket = io();
|
||||
|
||||
const standardRoles = ["Villager", "Werewolf", "Seer", "Shadow", "Hunter", "Mason", "Minion", "Sorcerer", "Dream Wolf"];
|
||||
let clock;
|
||||
let currentGame = null;
|
||||
let lastGameState = null;
|
||||
let cardFlippedOver = false;
|
||||
let cardRendered = false;
|
||||
let lastKilled = null;
|
||||
|
||||
// respond to the game state received from the server
|
||||
socket.on('state', function(game) {
|
||||
currentGame = game;
|
||||
if(detectChanges(game)) {
|
||||
buildGameBasedOnState(game);
|
||||
}
|
||||
});
|
||||
|
||||
function buildGameBasedOnState(game) {
|
||||
switch(game.status) {
|
||||
case "lobby":
|
||||
renderLobby();
|
||||
break;
|
||||
case "started":
|
||||
renderGame();
|
||||
break;
|
||||
case "ended":
|
||||
renderEndSplash();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function detectChanges(game) {
|
||||
if (lastGameState === null ||
|
||||
lastGameState.status !== game.status ||
|
||||
lastGameState.paused !== game.paused ||
|
||||
lastGameState.lastKilled !== game.lastKilled ||
|
||||
lastGameState.startTime !== game.startTime ||
|
||||
lastGameState.players.length !== game.players.length) {
|
||||
lastGameState = game;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hideAfterExit(e) {
|
||||
e.target.style.display = 'none';
|
||||
e.target.classList.remove(e.target.exitClass);
|
||||
}
|
||||
|
||||
function triggerExitAnimation(e) {
|
||||
e.target.classList.remove(e.target.entranceClass);
|
||||
e.target.classList.remove(e.target.exitClass);
|
||||
e.target.offsetWidth;
|
||||
e.target.classList.add(e.target.exitClass);
|
||||
window.setTimeout(()=>{
|
||||
e.target.addEventListener('animationend', hideAfterExit, true);
|
||||
},0);
|
||||
}
|
||||
|
||||
function triggerEntranceAnimation(selector, entranceClass, exitClass, image) {
|
||||
let transitionEl = document.querySelector(selector);
|
||||
transitionEl.style.display = 'flex';
|
||||
transitionEl.addEventListener('animationend', triggerExitAnimation, true);
|
||||
transitionEl.classList.remove(entranceClass);
|
||||
transitionEl.entranceClass = entranceClass;
|
||||
transitionEl.exitClass = exitClass;
|
||||
transitionEl.offsetWidth;
|
||||
if (image && standardRoles.includes(currentGame.killedRole)) {
|
||||
transitionEl.classList.remove("killed-role-custom");
|
||||
transitionEl.setAttribute("src", "../assets/images/roles/" + currentGame.killedRole.replace(/\s/g, '') + ".png");
|
||||
} else {
|
||||
if (image) {
|
||||
transitionEl.setAttribute("src", "../assets/images/custom.svg");
|
||||
transitionEl.setAttribute("class", "killed-role-custom");
|
||||
}
|
||||
}
|
||||
transitionEl.classList.add(entranceClass);
|
||||
}
|
||||
|
||||
function playKilledAnimation() {
|
||||
triggerEntranceAnimation('#overlay', 'animate-overlay-in', 'animate-overlay-out', false);
|
||||
triggerEntranceAnimation('#killed-role', 'animate-role-in', 'animate-role-out', true);
|
||||
triggerEntranceAnimation('#killed-name', 'animate-name-in', 'animate-name-out', false);
|
||||
}
|
||||
|
||||
function launchGame() {
|
||||
randomlyDealCardsToPlayers();
|
||||
utility.shuffle(currentGame.players); // put the players in a random order
|
||||
socket.emit('startGame', { players: currentGame.players , code: currentGame.accessCode});
|
||||
}
|
||||
|
||||
function randomlyDealCardsToPlayers() {
|
||||
for (let player of currentGame.players) {
|
||||
player.card = drawRandomCard();
|
||||
}
|
||||
}
|
||||
|
||||
function drawRandomCard() {
|
||||
return currentGame.deck.splice(utility.getRandomInt(currentGame.deck.length) - 1, 1)[0];
|
||||
}
|
||||
|
||||
function getLiveCount() {
|
||||
let liveCount = 0;
|
||||
for (let player of currentGame.players) {
|
||||
if (!player.dead) {
|
||||
liveCount ++;
|
||||
}
|
||||
}
|
||||
return liveCount;
|
||||
}
|
||||
|
||||
function renderEndSplash() {
|
||||
clearInterval(clock);
|
||||
document.getElementById("game-container").remove();
|
||||
document.querySelector("#message-box").style.display = 'none';
|
||||
currentGame.winningTeam === "village"
|
||||
? document.getElementById("end-container").innerHTML ="<div class='winner-header'><p class='winner-village'>Village</p> wins!</div>"
|
||||
: document.getElementById("end-container").innerHTML ="<div class='winner-header'><p class='winner-wolf'>Wolves</p>win!</div>";
|
||||
const rosterContainer = document.createElement("div");
|
||||
rosterContainer.setAttribute("id", "roster");
|
||||
document.getElementById("end-container").innerHTML += "<div class='roster-header'>Here's what everyone was:</div>";
|
||||
let rosterContent = "";
|
||||
for (const player of currentGame.players) {
|
||||
rosterContent += "<div class='roster-list-item'>";
|
||||
rosterContent += standardRoles.includes(player.card.role)
|
||||
? "<img alt='' src='/assets/images/roles-small/" + player.card.role.replace(/\s/g, '') + ".png' />"
|
||||
: "<img alt='' class='card-image-custom' src='/assets/images/custom.svg' />";
|
||||
rosterContent += player.name + ": " + player.card.role + "</div>"
|
||||
}
|
||||
rosterContainer.innerHTML = rosterContent;
|
||||
document.getElementById("end-container").appendChild(rosterContainer);
|
||||
document.getElementById("end-container").innerHTML += "<a href='/'><button class='app-btn'>Home</button></a>";
|
||||
|
||||
}
|
||||
|
||||
function renderGame() {
|
||||
// remove lobby components if present
|
||||
if (document.getElementById("lobby-container") !== null && document.getElementById("launch") !== null) {
|
||||
document.getElementById("lobby-container").remove();
|
||||
document.getElementById("launch").remove();
|
||||
}
|
||||
|
||||
document.querySelector("#message-box").style.display = 'block';
|
||||
if (currentGame.killedRole && currentGame.lastKilled !== lastKilled) { // a new player has been killed
|
||||
lastKilled = currentGame.lastKilled;
|
||||
document.getElementById("killed-name").innerText = currentGame.killedPlayer + " was a " + currentGame.killedRole + "!";
|
||||
playKilledAnimation();
|
||||
document.getElementById("message-box").innerText = currentGame.message;
|
||||
}
|
||||
const player = currentGame.players.find((player) => player.id === sessionStorage.getItem("id"));
|
||||
|
||||
// render the header
|
||||
document.getElementById("game-container").setAttribute("class", "game-container");
|
||||
const gameHeader = document.createElement("div");
|
||||
gameHeader.setAttribute("id", "game-header");
|
||||
gameHeader.innerHTML =
|
||||
"<div id='players-remaining'>" + getLiveCount() + "/" + currentGame.size + " alive</div>" +
|
||||
"<div id='clock'></div>" +
|
||||
"<div id='pause-container'></div>";
|
||||
if (document.getElementById("game-header")) {
|
||||
document.getElementById("card-container").removeChild(document.getElementById("game-header"));
|
||||
}
|
||||
document.getElementById("card-container").prepend(gameHeader);
|
||||
|
||||
// render the card if it hasn't been yet
|
||||
if (!cardRendered) {
|
||||
renderPlayerCard(player);
|
||||
cardRendered = true;
|
||||
}
|
||||
|
||||
// build the clock
|
||||
if (currentGame.time) {
|
||||
updateClock();
|
||||
document.getElementById("pause-container").innerHTML = currentGame.paused ?
|
||||
"<img alt='pause' src='../assets/images/play-button.svg' id='play-pause'/>"
|
||||
: "<img alt='pause' src='../assets/images/pause-button.svg' id='play-pause'/>";
|
||||
document.getElementById("play-pause").addEventListener("click", pauseOrResumeGame)
|
||||
}
|
||||
|
||||
// add the "I'm dead" button
|
||||
let killedBtn = document.createElement("button");
|
||||
killedBtn.setAttribute("id", "dead-btn");
|
||||
|
||||
if (player.dead) {
|
||||
killedBtn.setAttribute("class", "app-btn killed-btn disabled");
|
||||
killedBtn.innerText = "Killed"
|
||||
} else {
|
||||
killedBtn.setAttribute("class", "app-btn killed-btn");
|
||||
killedBtn.innerText = "I'm dead";
|
||||
}
|
||||
if (document.getElementById("dead-btn")) {
|
||||
document.getElementById("card-container").removeChild(document.getElementById("dead-btn"));
|
||||
}
|
||||
document.getElementById("card-container").appendChild(killedBtn);
|
||||
document.getElementById("dead-btn").addEventListener("click", killPlayer);
|
||||
|
||||
// add the list of dead/alive players
|
||||
renderDeadAndAliveInformation();
|
||||
}
|
||||
|
||||
function renderDeadAndAliveInformation() {
|
||||
let infoContainer = document.getElementById("info-container");
|
||||
let alivePlayers = currentGame.players.filter((player) => !player.dead).sort((a, b) =>
|
||||
{
|
||||
return a.card.role > b.card.role ? 1 : -1;
|
||||
});
|
||||
let deadPlayers = currentGame.players.filter((player) => player.dead);
|
||||
deadPlayers.sort((a, b) => { // sort players by the time they died
|
||||
return new Date(a.deadAt) > new Date(b.deadAt) ? -1 : 1;
|
||||
});
|
||||
|
||||
let killedContainer = document.createElement("div");
|
||||
killedContainer.setAttribute("id", "killed-container");
|
||||
let killedHeader = document.createElement("h2");
|
||||
killedHeader.innerText = "Killed Players";
|
||||
killedContainer.appendChild(killedHeader);
|
||||
deadPlayers.forEach((player) => {
|
||||
let deadPlayerClass = player.card.team === "good" ? "dead-player-village" : "dead-player-evil";
|
||||
if (player.card.isTypeOfWerewolf) {
|
||||
deadPlayerClass += " dead-player-wolf";
|
||||
}
|
||||
const killedPlayer = document.createElement("div");
|
||||
killedPlayer.setAttribute("class", "killed-player " + deadPlayerClass);
|
||||
killedPlayer.innerText = player.name + ": " + player.card.role;
|
||||
killedContainer.appendChild(killedPlayer);
|
||||
});
|
||||
|
||||
let aliveContainer = document.createElement("div");
|
||||
aliveContainer.setAttribute("id", "alive-container");
|
||||
let aliveHeader = document.createElement("h2");
|
||||
aliveContainer.appendChild(aliveHeader);
|
||||
aliveHeader.innerText = "Roles Still Alive";
|
||||
alivePlayers.forEach((player) => {
|
||||
let alivePlayerClass = player.card.team === "good" ? "alive-player-village" : "alive-player-evil";
|
||||
if (player.card.isTypeOfWerewolf) {
|
||||
alivePlayerClass += " alive-player-wolf";
|
||||
}
|
||||
const alivePlayer = document.createElement("div");
|
||||
alivePlayer.setAttribute("class", "alive-player " + alivePlayerClass);
|
||||
alivePlayer.innerHTML = "<p>" + player.card.role + "</p><img src='../assets/images/info.svg'/>";
|
||||
//Add hidden description span - RTM 4/18/2020
|
||||
let playerCardInfo=document.createElement("span");
|
||||
playerCardInfo.setAttribute("class","tooltiptext");
|
||||
playerCardInfo.innerText=player.card.description;
|
||||
alivePlayer.appendChild(playerCardInfo);
|
||||
aliveContainer.appendChild(alivePlayer);
|
||||
});
|
||||
if (infoContainer === null) {
|
||||
infoContainer = document.createElement("div");
|
||||
infoContainer.setAttribute("id", "info-container");
|
||||
infoContainer.appendChild(killedContainer);
|
||||
infoContainer.appendChild(aliveContainer);
|
||||
document.getElementById("game-container").appendChild(infoContainer);
|
||||
} else {
|
||||
document.getElementById("killed-container").remove();
|
||||
document.getElementById("alive-container").remove();
|
||||
document.getElementById("info-container").append(killedContainer);
|
||||
document.getElementById("info-container").append(aliveContainer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function renderPlayerCard(player) {
|
||||
const card = player.card;
|
||||
const cardArt = standardRoles.includes(card.role) ?
|
||||
"<img alt='" + card.role + "' src='../assets/images/roles/" + card.role.replace(/\s/g, '') + ".png' />"
|
||||
: "<div class='placeholder'>Custom Role</div>";
|
||||
const cardClass = player.card.team === "good" ? "game-card-inner village" : "game-card-inner wolf";
|
||||
const playerCard = document.createElement("div");
|
||||
playerCard.setAttribute("id", "game-card");
|
||||
playerCard.setAttribute("class", getFlipState());
|
||||
playerCard.innerHTML =
|
||||
"<div class='" + cardClass + "'>" +
|
||||
"<div class='game-card-front'>" +
|
||||
"<h2>" + card.role + "</h2>" +
|
||||
cardArt +
|
||||
"<div>" +
|
||||
"<p>" + card.description + "</p>" +
|
||||
"<p id='flip-instruction'>Click to flip</p>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
"<div class='game-card-back'></div>" +
|
||||
"</div>";
|
||||
document.getElementById("card-container").appendChild(playerCard);
|
||||
document.getElementById("game-card").addEventListener("click", flipCard);
|
||||
}
|
||||
|
||||
function pauseOrResumeGame() {
|
||||
if (currentGame.paused) {
|
||||
socket.emit('resumeGame', currentGame.accessCode);
|
||||
} else {
|
||||
socket.emit('pauseGame', currentGame.accessCode);
|
||||
}
|
||||
}
|
||||
|
||||
function getFlipState() {
|
||||
return cardFlippedOver ? "flip-down" : "flip-up";
|
||||
}
|
||||
|
||||
function flipCard() {
|
||||
cardFlippedOver
|
||||
? flipUp()
|
||||
: flipDown();
|
||||
|
||||
cardFlippedOver = !cardFlippedOver;
|
||||
}
|
||||
|
||||
function flipUp(){
|
||||
const card = document.getElementById("game-card");
|
||||
card.classList.add("flip-up");
|
||||
card.classList.remove("flip-down");
|
||||
}
|
||||
|
||||
function flipDown(){
|
||||
const card = document.getElementById("game-card");
|
||||
card.classList.add("flip-down");
|
||||
card.classList.remove("flip-up");
|
||||
}
|
||||
|
||||
function displayTime() {
|
||||
const start = currentGame.paused ? new Date(currentGame.pauseTime) : new Date();
|
||||
const end = new Date(currentGame.endTime);
|
||||
const delta = end - start;
|
||||
let seconds = Math.floor((delta / 1000) % 60);
|
||||
let minutes = Math.floor((delta / 1000 / 60) % 60);
|
||||
let hours = Math.floor((delta / (1000 * 60 * 60)) % 24);
|
||||
|
||||
seconds = seconds < 10 ? "0" + seconds : seconds;
|
||||
minutes = minutes < 10 ? "0" + minutes : minutes;
|
||||
|
||||
document.getElementById("clock").innerText = hours > 0
|
||||
? hours + ":" + minutes + ":" + seconds
|
||||
: minutes + ":" + seconds;
|
||||
}
|
||||
|
||||
function updateClock() {
|
||||
clearInterval(clock);
|
||||
if (document.getElementById("clock") !== null) {
|
||||
displayTime();
|
||||
clock = setInterval(function() {
|
||||
displayTime();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function killPlayer() {
|
||||
if(confirm("Are you sure you are dead?")) {
|
||||
socket.emit("killPlayer", currentGame.players.find((player) => player.id === sessionStorage.getItem("id")).id, currentGame.accessCode);
|
||||
}
|
||||
}
|
||||
|
||||
function renderLobby() {
|
||||
document.querySelector("#message-box").style.display = 'none';
|
||||
// Render lobby header
|
||||
if (document.getElementsByClassName("lobby-player").length === 0) {
|
||||
let header = document.createElement("h2");
|
||||
header.setAttribute("class", "app-header-secondary");
|
||||
header.innerText = "Lobby";
|
||||
document.getElementById("lobby-container").appendChild(header);
|
||||
let subHeader = document.createElement("div");
|
||||
subHeader.setAttribute("id", "lobby-subheader");
|
||||
subHeader.innerHTML = "<div>" +
|
||||
"<span id='join-count'>" + currentGame.players.length + "</span>" +
|
||||
"<span id='deck-size'>/" + currentGame.size + " Players</span>" +
|
||||
"</div>" +
|
||||
"<br>" +
|
||||
"<div id='game-code'>Access Code: " + currentGame.accessCode + "</div>";
|
||||
document.getElementById("lobby-container").appendChild(subHeader);
|
||||
}
|
||||
// Render all players that are new
|
||||
let i = 1;
|
||||
for (let player of currentGame.players) {
|
||||
if(!document.getElementById("player-" + i)) {
|
||||
const playerContainer = document.createElement("div");
|
||||
player.id === sessionStorage.getItem("id") ?
|
||||
playerContainer.setAttribute("class", "lobby-player highlighted")
|
||||
: playerContainer.setAttribute("class", "lobby-player");
|
||||
playerContainer.setAttribute("id", "player-" + i);
|
||||
playerContainer.innerHTML = "<p>" + player.name + "</p>";
|
||||
document.getElementById("lobby-container").appendChild(playerContainer);
|
||||
document.getElementById("join-count").innerText = currentGame.players.length.toString();
|
||||
}
|
||||
i ++;
|
||||
}
|
||||
// display the launch button if the player is the host
|
||||
if (sessionStorage.getItem("host")) {
|
||||
if (currentGame.players.length === currentGame.size) {
|
||||
document.getElementById("launch").innerHTML = "<button class='app-btn'>Start Game</button>";
|
||||
document.getElementById("launch").addEventListener("click", launchGame);
|
||||
} else {
|
||||
document.getElementById("launch").innerHTML = "<button class='app-btn disabled'>Start Game</button>";
|
||||
}
|
||||
} else {
|
||||
document.getElementById("launch").innerHTML = "<p>The host will start the game.</p>"
|
||||
}
|
||||
}
|
||||
|
||||
// request game state from server periodically
|
||||
setInterval(function () {
|
||||
socket.emit('requestState', {code: sessionStorage.getItem("code")});
|
||||
}, 200);
|
||||
45
javascript/join.js
Normal file
45
javascript/join.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const socket = io();
|
||||
import { utility } from './util.js'
|
||||
|
||||
// respond to the game state received from the server
|
||||
socket.on('joinError', function(message) {
|
||||
document.getElementById("join-btn").classList.remove('disabled');
|
||||
document.getElementById("code").classList.add("error");
|
||||
document.getElementById("join-error").innerText = message;
|
||||
});
|
||||
|
||||
// respond to the game state received from the server
|
||||
socket.on('success', function() {
|
||||
document.getElementById("join-btn").classList.remove('disabled');
|
||||
if (document.getElementById("code").classList.contains("error")) {
|
||||
document.getElementById("code").classList.remove("error");
|
||||
document.getElementById("join-error").innerText = "";
|
||||
}
|
||||
// If a player was a host of a previous game, don't make them the host of this one
|
||||
if (sessionStorage.getItem("host")) {
|
||||
sessionStorage.removeItem("host");
|
||||
}
|
||||
window.location.replace('/' + document.getElementById("code").value.toString().trim().toLowerCase());
|
||||
});
|
||||
|
||||
document.getElementById("join-btn").addEventListener("click", function() {
|
||||
document.getElementById("join-btn").classList.add('disabled');
|
||||
if (document.getElementById("name").value.length > 0) {
|
||||
const code = document.getElementById("code").value.toString().trim().toLowerCase();
|
||||
if (document.getElementById("name").classList.contains("error")) {
|
||||
document.getElementById("name").classList.remove("error");
|
||||
document.getElementById("name-error").innerText = "";
|
||||
}
|
||||
sessionStorage.setItem("code", code);
|
||||
let playerId = utility.generateID();
|
||||
sessionStorage.setItem("id", playerId);
|
||||
const playerInfo = {name: document.getElementById("name").value, id: playerId, code: code};
|
||||
socket.emit('joinGame', playerInfo);
|
||||
} else {
|
||||
document.getElementById("join-btn").classList.remove('disabled');
|
||||
document.getElementById("name").classList.add("error");
|
||||
document.getElementById("name-error").innerText = "Name is required.";
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
133
javascript/modules/card-manager.js
Normal file
133
javascript/modules/card-manager.js
Normal file
@@ -0,0 +1,133 @@
|
||||
const finishedArtArray = ["Villager", "Werewolf", "Seer", "Shadow", "Hunter", "Mason", "Minion", "Sorcerer", "Dream Wolf"];
|
||||
|
||||
export class CardManager {
|
||||
constructor() {}
|
||||
|
||||
static createCard(card) {
|
||||
return new Card(card.role, card.team, card.description, card.quantity=0, card.isTypeOfWerewolf, card.custom, card.saved);
|
||||
}
|
||||
|
||||
// builds element for the informational role modal on the setup page
|
||||
static constructModalRoleElement(card) {
|
||||
const modalRole = document.createElement("div");
|
||||
modalRole.setAttribute("class", "modal-role");
|
||||
const roleClass = card.team === "good" ? "role-village" : "role-wolf";
|
||||
let roleImage;
|
||||
if (card.custom === true) {
|
||||
roleImage = "<img alt='No art' class='card-image-custom' src='/assets/images/custom.svg' />";
|
||||
} else {
|
||||
roleImage = finishedArtArray.includes(card.role) ?
|
||||
"<img alt='No art' src='/assets/images/roles-small/" + card.role.replace(/\s/g, '') + ".png' />"
|
||||
: "<span>Art soon.</span>";
|
||||
}
|
||||
modalRole.innerHTML =
|
||||
"<div>" +
|
||||
roleImage +
|
||||
"<div>" +
|
||||
"<h2 class='" + roleClass + "'>" + card.role + "</h2>" +
|
||||
"<p>" + card.team + "</p>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
"<p>" + card.description + "</p>";
|
||||
return modalRole;
|
||||
}
|
||||
|
||||
static constructDeckBuilderElement(card, index) {
|
||||
const cardContainer = document.createElement("div");
|
||||
|
||||
const quantityClass = card.team === "good" ? "card-quantity quantity-village" : "card-quantity quantity-wolf";
|
||||
|
||||
let cardClass = card.isTypeOfWerewolf ? "card card-werewolf" : "card";
|
||||
cardContainer.setAttribute("class", cardClass);
|
||||
if (card.team === "good") {
|
||||
cardContainer.setAttribute("id", "card-" + index);
|
||||
} else {
|
||||
cardContainer.setAttribute("id", "card-" + index);
|
||||
}
|
||||
cardContainer.innerHTML =
|
||||
"<div class='card-top'>" +
|
||||
"<div class='card-header'>" +
|
||||
"<div>" +
|
||||
"<p class='card-role'>" + card.role + "</p>" +
|
||||
"<div class='" + quantityClass + "'>" + card.quantity + "</div>" +
|
||||
"</div>" +
|
||||
"<p>+</p>" +
|
||||
"</div>" +
|
||||
"</div>";
|
||||
cardContainer.innerHTML = card.custom
|
||||
? cardContainer.innerHTML += "<img class='card-image card-image-custom' src='/assets/images/custom.svg' alt='" + card.role + "'/>"
|
||||
: cardContainer.innerHTML +="<img class='card-image' src='/assets/images/roles-small/" + card.role.replace(/\s/g, '') + ".png' alt='" + card.role + "'/>";
|
||||
cardContainer.innerHTML +=
|
||||
"<div class='card-bottom'>" +
|
||||
"<p>-</p>" +
|
||||
"</div>";
|
||||
|
||||
return cardContainer;
|
||||
}
|
||||
|
||||
static constructCompactDeckBuilderElement(card, index) {
|
||||
const cardContainer = document.createElement("div");
|
||||
|
||||
const quantityClass = card.team === "good" ? "card-quantity quantity-village" : "card-quantity quantity-wolf";
|
||||
|
||||
let cardClass = card.isTypeOfWerewolf ? "compact-card card-werewolf" : "compact-card";
|
||||
cardContainer.setAttribute("class", cardClass);
|
||||
if (card.team === "good") {
|
||||
cardContainer.setAttribute("id", "card-" + index);
|
||||
} else {
|
||||
cardContainer.setAttribute("id", "card-" + index);
|
||||
}
|
||||
cardContainer.innerHTML =
|
||||
"<div class='compact-card-left'>" +
|
||||
"<p>-</p>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-header'>" +
|
||||
"<p class='card-role'>" + card.role + "</p>" +
|
||||
"<div class='" + quantityClass + "'>" + card.quantity + "</div>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-right'>" +
|
||||
"<p>+</p>" +
|
||||
"</div>";
|
||||
return cardContainer;
|
||||
}
|
||||
|
||||
static constructCustomCardIndicator(isCondensed, team) {
|
||||
let customCard = document.createElement("div");
|
||||
if (isCondensed) {
|
||||
customCard.classList.add("compact-card", "custom-card");
|
||||
} else {
|
||||
customCard.classList.add("card", "custom-card");
|
||||
}
|
||||
|
||||
if (team === "good") {
|
||||
customCard.setAttribute("id", "custom-good");
|
||||
} else {
|
||||
customCard.setAttribute("id", "custom-evil");
|
||||
}
|
||||
|
||||
let cardHeader = document.createElement("h1");
|
||||
cardHeader.innerText = "Add Custom Role";
|
||||
|
||||
let cardBody = document.createElement("div");
|
||||
cardBody.innerText = "+";
|
||||
|
||||
customCard.appendChild(cardHeader);
|
||||
customCard.appendChild(cardBody);
|
||||
|
||||
return customCard;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Card {
|
||||
constructor(role, team, description, quantity, isTypeOfWerewolf, custom, saved) {
|
||||
this.id = null;
|
||||
this.role = role;
|
||||
this.isTypeOfWerewolf = isTypeOfWerewolf;
|
||||
this.team = team;
|
||||
this.description = description;
|
||||
this.quantity = quantity;
|
||||
this.custom = custom;
|
||||
this.saved = saved;
|
||||
}
|
||||
}
|
||||
20
javascript/modules/logger.js
Normal file
20
javascript/modules/logger.js
Normal file
@@ -0,0 +1,20 @@
|
||||
module.exports = function(debugMode = false){
|
||||
return {
|
||||
log(message = "") {
|
||||
const now = new Date();
|
||||
console.log('LOG ', now.toGMTString(), ': ', message);
|
||||
},
|
||||
|
||||
debug(message = "") {
|
||||
if (!debugMode) return;
|
||||
const now = new Date();
|
||||
console.debug('DEBUG ', now.toGMTString(), ': ', message);
|
||||
},
|
||||
|
||||
error(message = "") {
|
||||
if (!debugMode) return;
|
||||
const now = new Date();
|
||||
console.error('ERROR ', now.toGMTString(), ': ', message);
|
||||
}
|
||||
};
|
||||
};
|
||||
453
javascript/setup.js
Normal file
453
javascript/setup.js
Normal file
@@ -0,0 +1,453 @@
|
||||
import {cards} from './cards.js'
|
||||
import {utility} from './util.js'
|
||||
import {CardManager} from './modules/card-manager.js'
|
||||
|
||||
const socket = io();
|
||||
|
||||
class Game {
|
||||
constructor(accessCode, size, deck, time, hasDreamWolf) {
|
||||
this.accessCode = accessCode;
|
||||
this.size = size;
|
||||
this.deck = deck;
|
||||
this.time = time;
|
||||
this.players = [];
|
||||
this.status = "lobby";
|
||||
this.hasDreamWolf = hasDreamWolf;
|
||||
this.endTime = null;
|
||||
}
|
||||
}
|
||||
|
||||
const fullDeck = [];
|
||||
let gameSize = 0;
|
||||
let atLeastOnePlayer = false;
|
||||
|
||||
// register event listeners on buttons
|
||||
document.getElementById("reset-btn").addEventListener("click", resetCardQuantities);
|
||||
document.getElementById("create-btn").addEventListener("click", createGame);
|
||||
document.getElementById("role-view-changer-gallery").addEventListener("click", function() { toggleViewChanger(false) });
|
||||
document.getElementById("role-view-changer-list").addEventListener("click", function() { toggleViewChanger(true) });
|
||||
document.getElementById("role-btn").addEventListener("click", function() { displayModal("role-modal", undefined) });
|
||||
document.getElementById("edit-role-btn").addEventListener("click", function() { displayModal("edit-custom-roles-modal", undefined) });
|
||||
document.getElementById("custom-role-form").addEventListener("submit", function(e) {
|
||||
addCustomCardToRoles(e);
|
||||
});
|
||||
Array.from(document.getElementsByClassName("close")).forEach(function(element) {
|
||||
element.addEventListener('click', closeModal);
|
||||
});
|
||||
|
||||
// render all of the available cards to the user
|
||||
window.onload = function() {
|
||||
readInUserCustomRoles();
|
||||
renderAvailableCards(false);
|
||||
};
|
||||
|
||||
function renderAvailableCards(isCondensed) {
|
||||
cards.sort(function(a, b) {
|
||||
return a.role.toUpperCase().localeCompare(b.role);
|
||||
});
|
||||
document.getElementById("card-select-good").innerHTML = "";
|
||||
document.getElementById("card-select-evil").innerHTML = "";
|
||||
document.getElementById("roles").innerHTML = "";
|
||||
document.getElementById("custom-roles").innerHTML = "";
|
||||
|
||||
for (let i = 0; i < cards.length; i ++) {
|
||||
cards[i].team === "good"
|
||||
? renderGoodRole(cards[i], i, isCondensed)
|
||||
: renderEvilRole(cards[i], i, isCondensed);
|
||||
}
|
||||
|
||||
if (document.getElementById("custom-roles").getElementsByClassName("custom-role-edit").length === 0) {
|
||||
document.getElementById("custom-roles").innerHTML = "<h2>You haven't added any custom cards.</h2>";
|
||||
}
|
||||
|
||||
let customCardGood = CardManager.constructCustomCardIndicator(isCondensed, "good");
|
||||
let customCardEvil = CardManager.constructCustomCardIndicator(isCondensed, "evil");
|
||||
document.getElementById("card-select-good").appendChild(customCardGood);
|
||||
document.getElementById("card-select-evil").appendChild(customCardEvil);
|
||||
customCardGood.addEventListener("click", function() {
|
||||
displayModal("custom-card-modal", "Good");
|
||||
});
|
||||
customCardEvil.addEventListener("click", function() {
|
||||
displayModal("custom-card-modal", "Evil");
|
||||
});
|
||||
}
|
||||
|
||||
function renderGoodRole(cardInfo, i, isCondensed) {
|
||||
const card = CardManager.createCard(cardInfo);
|
||||
if (card.custom) {
|
||||
renderCustomRoleInModal(card, i);
|
||||
}
|
||||
fullDeck.push(card);
|
||||
|
||||
document.getElementById("roles").appendChild(CardManager.constructModalRoleElement(card));
|
||||
if (isCondensed) {
|
||||
document.getElementById("card-select-good").appendChild(CardManager.constructCompactDeckBuilderElement(card, i));
|
||||
let cardLeft = document.getElementById("card-" + i).getElementsByClassName("compact-card-left")[0];
|
||||
let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0];
|
||||
let cardRight = document.getElementById("card-" + i).getElementsByClassName("compact-card-right")[0];
|
||||
cardRight.addEventListener("click", function() { incrementCardQuantity(cardRight) }, true);
|
||||
cardLeft.addEventListener("click", function() { decrementCardQuantity(cardLeft) }, true);
|
||||
cardRight.card = card;
|
||||
cardRight.quantityEl = cardQuantity;
|
||||
cardLeft.card = card;
|
||||
cardLeft.quantityEl = cardQuantity;
|
||||
} else {
|
||||
document.getElementById("card-select-good").appendChild(CardManager.constructDeckBuilderElement(card, i));
|
||||
// Add event listeners to the top and bottom halves of the card to change the quantity.
|
||||
let cardTop = document.getElementById("card-" + i).getElementsByClassName("card-top")[0];
|
||||
let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0];
|
||||
let cardBottom = document.getElementById("card-" + i).getElementsByClassName("card-bottom")[0];
|
||||
cardTop.addEventListener("click", function() { incrementCardQuantity(cardTop) }, false);
|
||||
cardBottom.addEventListener("click", function() { decrementCardQuantity(cardBottom) }, false);
|
||||
cardTop.card = card;
|
||||
cardTop.quantityEl = cardQuantity;
|
||||
cardBottom.card = card;
|
||||
cardBottom.quantityEl = cardQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
function renderEvilRole(cardInfo, i, isCondensed) {
|
||||
const card = CardManager.createCard(cardInfo);
|
||||
if (card.custom) {
|
||||
renderCustomRoleInModal(card, i);
|
||||
}
|
||||
fullDeck.push(card);
|
||||
|
||||
document.getElementById("roles").appendChild(CardManager.constructModalRoleElement(card));
|
||||
if (isCondensed) {
|
||||
document.getElementById("card-select-evil").appendChild(CardManager.constructCompactDeckBuilderElement(card, i));
|
||||
let cardLeft = document.getElementById("card-" + i).getElementsByClassName("compact-card-left")[0];
|
||||
let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0];
|
||||
let cardRight = document.getElementById("card-" + i).getElementsByClassName("compact-card-right")[0];
|
||||
cardRight.addEventListener("click", function() { incrementCardQuantity(cardRight) }, false);
|
||||
cardLeft.addEventListener("click", function() { decrementCardQuantity(cardLeft) }, false);
|
||||
cardRight.card = card;
|
||||
cardRight.quantityEl = cardQuantity;
|
||||
cardLeft.card = card;
|
||||
cardLeft.quantityEl = cardQuantity;
|
||||
} else {
|
||||
document.getElementById("card-select-evil").appendChild(CardManager.constructDeckBuilderElement(card, i));
|
||||
// Add event listeners to the top and bottom halves of the card to change the quantity.
|
||||
let cardTop = document.getElementById("card-" + i).getElementsByClassName("card-top")[0];
|
||||
let cardQuantity = document.getElementById("card-" + i).getElementsByClassName("card-quantity")[0];
|
||||
let cardBottom = document.getElementById("card-" + i).getElementsByClassName("card-bottom")[0];
|
||||
cardTop.addEventListener("click", function() { incrementCardQuantity(cardTop) }, false);
|
||||
cardBottom.addEventListener("click", function() { decrementCardQuantity(cardBottom) }, false);
|
||||
cardTop.card = card;
|
||||
cardTop.quantityEl = cardQuantity;
|
||||
cardBottom.card = card;
|
||||
cardBottom.quantityEl = cardQuantity;
|
||||
}
|
||||
}
|
||||
|
||||
function addCustomCardToRoles(e) {
|
||||
e.preventDefault();
|
||||
if (!cards.find((card) => card.role === document.getElementById("custom-role-name").value)) {
|
||||
let newCard = {
|
||||
role: document.getElementById("custom-role-name").value,
|
||||
team: document.getElementById("custom-role-team").value,
|
||||
description: document.getElementById("custom-role-desc").value,
|
||||
isTypeOfWerewolf: document.getElementById("custom-role-wolf").checked,
|
||||
custom: true,
|
||||
saved: document.getElementById("custom-role-remember").checked
|
||||
};
|
||||
cards.push(newCard);
|
||||
renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected"));
|
||||
|
||||
if (newCard.saved === true) {
|
||||
let existingRoles = localStorage.getItem("play-werewolf-custom-roles");
|
||||
if (existingRoles !== null) {
|
||||
let rolesArray;
|
||||
try {
|
||||
rolesArray = JSON.parse(existingRoles);
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
}
|
||||
if (rolesArray) {
|
||||
rolesArray.push(newCard);
|
||||
}
|
||||
localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(rolesArray));
|
||||
} else {
|
||||
localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(new Array(newCard)));
|
||||
}
|
||||
}
|
||||
updateCustomRoleModal();
|
||||
closeModal();
|
||||
document.getElementById("custom-role-form").reset();
|
||||
} else {
|
||||
alert("A custom or standard card already exists with that name!")
|
||||
}
|
||||
}
|
||||
|
||||
function updateCustomRoleModal() {
|
||||
document.getElementById("custom-roles").innerHTML = "";
|
||||
for (let i = 0; i < cards.length; i++){
|
||||
if (cards[i].custom) {
|
||||
renderCustomRoleInModal(cards[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readInUserCustomRoles() {
|
||||
let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"];
|
||||
let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys);
|
||||
if (userCustomRoles) {
|
||||
for (let i = 0; i < userCustomRoles.length; i++) {
|
||||
cards.push(userCustomRoles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderCustomRoleInModal(card, index) {
|
||||
let roleElement = document.createElement("div");
|
||||
let editRemoveContainer = document.createElement("div");
|
||||
let roleLabel = document.createElement("div");
|
||||
let roleName = document.createElement("p");
|
||||
let remove = document.createElement("img");
|
||||
let edit = document.createElement("img");
|
||||
let editForm = buildRoleEditForm(index);
|
||||
|
||||
roleName.innerText = card.role;
|
||||
remove.setAttribute("src", "../assets/images/delete.svg");
|
||||
remove.setAttribute("title", "Delete");
|
||||
remove.addEventListener("click", function() { removeCustomRole(card.role) });
|
||||
|
||||
edit.setAttribute("src", "../assets/images/pencil_green.svg");
|
||||
edit.setAttribute("title", "Edit");
|
||||
edit.addEventListener("click", function(e) { toggleEditForm(e, index) });
|
||||
roleElement.setAttribute("class", "custom-role-edit");
|
||||
|
||||
editRemoveContainer.appendChild(remove);
|
||||
editRemoveContainer.appendChild(edit);
|
||||
roleLabel.appendChild(roleName);
|
||||
roleLabel.appendChild(editRemoveContainer);
|
||||
roleElement.appendChild(roleLabel);
|
||||
roleElement.appendChild(editForm);
|
||||
|
||||
document.getElementById("custom-roles").appendChild(roleElement);
|
||||
|
||||
document.getElementById("edit-form-" + index).addEventListener("submit", function(e) {
|
||||
updateCustomRole(e, index);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleEditForm(event, index) {
|
||||
event.preventDefault();
|
||||
let displayRule = document.getElementById("edit-form-" + index).style.display;
|
||||
document.getElementById("edit-form-" + index).style.display = displayRule === "none" ? "block" : "none";
|
||||
|
||||
if (document.getElementById("edit-form-" + index).style.display === "block") {
|
||||
populateEditRoleForm(cards[index], index);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleViewChanger(isCondensed) {
|
||||
|
||||
if (isCondensed) {
|
||||
document.getElementById("role-view-changer-gallery").classList.remove("selected");
|
||||
document.getElementById("role-view-changer-list").classList.add("selected");
|
||||
} else {
|
||||
document.getElementById("role-view-changer-gallery").classList.add("selected");
|
||||
document.getElementById("role-view-changer-list").classList.remove("selected");
|
||||
}
|
||||
renderAvailableCards(isCondensed);
|
||||
}
|
||||
|
||||
function buildRoleEditForm(index) {
|
||||
let infoForm = document.createElement("div");
|
||||
infoForm.style.display = "none";
|
||||
infoForm.setAttribute("id", "edit-form-" + index);
|
||||
infoForm.innerHTML =
|
||||
"<form class=\"edit-role-form\" id=\"edit-role-form-" + index + "\">" +
|
||||
"<label for=\"edit-role-desc-" + index + "\">Description</label>" +
|
||||
"<textarea rows=\"3\" id=\"edit-role-desc-" + index + "\" required></textarea>" +
|
||||
"<label for=\"edit-role-team-" + index + "\">Team</label>" +
|
||||
"<select id=\"edit-role-team-" + index + "\">" +
|
||||
"<option value=\"good\">Good</option>" +
|
||||
"<option value=\"evil\">Evil</option>" +
|
||||
"</select>" +
|
||||
"<div class=\"checkbox\">" +
|
||||
"<input type=\"checkbox\" id=\"edit-role-wolf-" + index + "\">" +
|
||||
"<label for=\"edit-role-wolf-" + index + "\">Werewolf role (counts for parity)</label>" +
|
||||
"</div>" +
|
||||
"<div class=\"checkbox\">" +
|
||||
"<input type=\"checkbox\" id=\"edit-role-remember-" + index + "\">" +
|
||||
"<label for=\"edit-role-remember-" + index + "\">Remember this role for later (uses cookies)</label>" +
|
||||
"</div>" +
|
||||
"<br><br>" +
|
||||
"<input type=\"submit\" class=\"app-btn\" value=\"Update\">" +
|
||||
"</form>";
|
||||
return infoForm;
|
||||
}
|
||||
|
||||
function populateEditRoleForm(card, index) {
|
||||
document.getElementById("edit-role-desc-" + index).value = card.description;
|
||||
document.getElementById("edit-role-team-" + index).value = card.team;
|
||||
document.getElementById("edit-role-wolf-" + index).checked = card.isTypeOfWerewolf;
|
||||
document.getElementById("edit-role-remember-" + index).checked = card.saved;
|
||||
}
|
||||
|
||||
function removeCustomRole(name) {
|
||||
if (confirm("Delete this role?")) {
|
||||
let matchingCards = cards.filter((card) => card.role === name);
|
||||
matchingCards.forEach((card) => {
|
||||
cards.splice(cards.indexOf(card), 1);
|
||||
});
|
||||
let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"];
|
||||
let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys);
|
||||
if (userCustomRoles) {
|
||||
userCustomRoles = userCustomRoles.filter((card) => card.role !== name);
|
||||
localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles));
|
||||
}
|
||||
updateCustomRoleModal();
|
||||
renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected"));
|
||||
}
|
||||
}
|
||||
|
||||
function updateCustomRole(event, index) {
|
||||
event.preventDefault();
|
||||
if (index >= 0 && index < cards.length) {
|
||||
let cardToUpdate = cards[index];
|
||||
cardToUpdate.team = document.getElementById("edit-role-team-" + index).value;
|
||||
cardToUpdate.description = document.getElementById("edit-role-desc-" + index).value;
|
||||
cardToUpdate.isTypeOfWerewolf = document.getElementById("edit-role-wolf-" + index).checked;
|
||||
cardToUpdate.saved = document.getElementById("edit-role-remember-" + index).checked;
|
||||
|
||||
removeOrAddSavedRoleIfNeeded(cardToUpdate);
|
||||
toggleEditForm(event, index);
|
||||
renderAvailableCards(document.getElementById("role-view-changer-list").classList.contains("selected"));
|
||||
}
|
||||
}
|
||||
|
||||
function removeOrAddSavedRoleIfNeeded(card) {
|
||||
let expectedKeys = ["role", "description", "team", "isTypeOfWerewolf", "custom", "saved"];
|
||||
let userCustomRoles = utility.validateCustomRolesJsonObject("play-werewolf-custom-roles", expectedKeys);
|
||||
if (userCustomRoles) {
|
||||
if (card.saved) {
|
||||
let roleToUpdate = userCustomRoles.find((savedCard) => savedCard.role === card.role);
|
||||
if (roleToUpdate) {
|
||||
userCustomRoles[userCustomRoles.indexOf(roleToUpdate)] = card;
|
||||
} else {
|
||||
userCustomRoles.push(card);
|
||||
}
|
||||
localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles));
|
||||
} else {
|
||||
let roleToRemove = userCustomRoles.find((savedCard) => savedCard.role === card.role);
|
||||
if (roleToRemove) {
|
||||
userCustomRoles.splice(userCustomRoles.indexOf(roleToRemove), 1);
|
||||
localStorage.setItem("play-werewolf-custom-roles", JSON.stringify(userCustomRoles));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function incrementCardQuantity(e) {
|
||||
if(e.card.quantity < 25) {
|
||||
e.card.quantity += 1;
|
||||
}
|
||||
e.quantityEl.innerHTML = e.card.quantity;
|
||||
updateGameSize();
|
||||
}
|
||||
|
||||
function decrementCardQuantity(e) {
|
||||
if(e.card.quantity > 0) {
|
||||
e.card.quantity -= 1;
|
||||
}
|
||||
e.quantityEl.innerHTML = e.card.quantity;
|
||||
updateGameSize();
|
||||
}
|
||||
|
||||
function updateGameSize() {
|
||||
gameSize = 0;
|
||||
for (let card of fullDeck) {
|
||||
gameSize += card.quantity;
|
||||
}
|
||||
document.getElementById("game-size").innerText = gameSize + " Players";
|
||||
atLeastOnePlayer = gameSize > 0;
|
||||
return gameSize;
|
||||
}
|
||||
|
||||
function resetCardQuantities() {
|
||||
for (let card of fullDeck) {
|
||||
card.quantity = 0;
|
||||
}
|
||||
updateGameSize();
|
||||
Array.prototype.filter.call(document.getElementsByClassName("card-quantity"), function(quantities){
|
||||
return quantities.innerHTML = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function displayModal(modalId, teamForCustomRole) {
|
||||
if (teamForCustomRole === "Good") {
|
||||
document.getElementById("option-evil").removeAttribute("selected");
|
||||
document.getElementById("option-good").setAttribute("selected", "selected");
|
||||
}
|
||||
if (teamForCustomRole === "Evil") {
|
||||
document.getElementById("option-good").removeAttribute("selected");
|
||||
document.getElementById("option-evil").setAttribute("selected", "selected");
|
||||
}
|
||||
document.getElementById(modalId).classList.remove("hidden");
|
||||
document.getElementById("app-content").classList.add("hidden");
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById("role-modal").classList.add("hidden");
|
||||
document.getElementById("custom-card-modal").classList.add("hidden");
|
||||
document.getElementById("edit-custom-roles-modal").classList.add("hidden");
|
||||
document.getElementById("app-content").classList.remove("hidden");
|
||||
}
|
||||
|
||||
function buildDeckFromQuantities() {
|
||||
let playerDeck = [];
|
||||
for (const card of fullDeck) {
|
||||
for (let i = 0; i < card.quantity; i++) {
|
||||
card.id = utility.generateID();
|
||||
playerDeck.push(card);
|
||||
}
|
||||
}
|
||||
return playerDeck;
|
||||
}
|
||||
|
||||
function createGame() {
|
||||
if (document.getElementById("name").value.length > 0 && atLeastOnePlayer) {
|
||||
// generate 6 digit access code
|
||||
let code = "";
|
||||
let charPool = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
code += charPool[utility.getRandomInt(36)]
|
||||
}
|
||||
|
||||
// generate unique player Id for session
|
||||
let id = utility.generateID();
|
||||
sessionStorage.setItem("id", id);
|
||||
|
||||
// player who creates the game is the host
|
||||
sessionStorage.setItem("host", true);
|
||||
|
||||
// send a new game to the server, and then join it
|
||||
const playerInfo = {name: document.getElementById("name").value, code: code, id: id};
|
||||
let gameDeck = buildDeckFromQuantities();
|
||||
const game = new Game(
|
||||
code,
|
||||
gameSize,
|
||||
gameDeck,
|
||||
Math.ceil(document.getElementById("time").value),
|
||||
gameDeck.find((card) => card.role === "Dream Wolf") !== undefined
|
||||
);
|
||||
socket.emit('newGame', game, function() {
|
||||
socket.emit('joinGame', playerInfo);
|
||||
sessionStorage.setItem('code', code);
|
||||
window.location.replace('/' + code);
|
||||
});
|
||||
} else {
|
||||
document.getElementById("some-error").innerText = "There are problems with your above setup.";
|
||||
if (!atLeastOnePlayer) {
|
||||
document.getElementById("game-size").classList.add("error");
|
||||
} else {
|
||||
document.getElementById("game-size").classList.remove("error");
|
||||
}
|
||||
document.getElementById("name").classList.add("error");
|
||||
document.getElementById("name-error").innerText = "Name is required.";
|
||||
}
|
||||
}
|
||||
50
javascript/util.js
Normal file
50
javascript/util.js
Normal file
@@ -0,0 +1,50 @@
|
||||
export const utility =
|
||||
{
|
||||
generateID() {
|
||||
let code = "";
|
||||
let charPool = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
for (let i = 0; i < 10; i++) {
|
||||
code += charPool[this.getRandomInt(61)]
|
||||
}
|
||||
return code;
|
||||
},
|
||||
|
||||
getRandomInt(max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
},
|
||||
|
||||
shuffle(array) {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
return array;
|
||||
},
|
||||
|
||||
validateCustomRolesJsonObject(name, expectedKeys) {
|
||||
let value = localStorage.getItem(name);
|
||||
if (value !== null) {
|
||||
let valueJson;
|
||||
try {
|
||||
valueJson = JSON.parse(value);
|
||||
} catch(e) {
|
||||
console.error(e.message);
|
||||
localStorage.removeItem(name);
|
||||
return false;
|
||||
}
|
||||
if (valueJson && Array.isArray(valueJson)) { // some defensive programming - check if it's an array, and that the object has the expected structure
|
||||
for (let i = 0; i < valueJson.length; i++){
|
||||
if (expectedKeys.some((key) => !Object.keys(valueJson[i]).includes(key))) {
|
||||
console.error("tried to read invalid object: " + valueJson[i] + " with expected keys: " + expectedKeys);
|
||||
valueJson.splice(i, 1);
|
||||
localStorage.setItem(name, JSON.stringify(valueJson));
|
||||
}
|
||||
}
|
||||
return valueJson;
|
||||
} else { // object has been messed with. remove it.
|
||||
localStorage.removeItem(name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user