diff --git a/client/config/globals.js b/client/config/globals.js index 8eec0a7..ffdc08e 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -25,7 +25,8 @@ export const globals = { EVENTS: { PLAYER_JOINED: "playerJoined", SYNC_GAME_STATE: "syncGameState", - START_TIMER: "startTimer" + START_TIMER: "startTimer", + KILL_PLAYER: "killPlayer" }, USER_TYPES: { MODERATOR: "moderator", diff --git a/client/images/eye.svg b/client/images/eye.svg new file mode 100644 index 0000000..5add930 --- /dev/null +++ b/client/images/eye.svg @@ -0,0 +1,3 @@ + diff --git a/client/images/tombstone.png b/client/images/tombstone.png new file mode 100644 index 0000000..49a91b6 Binary files /dev/null and b/client/images/tombstone.png differ diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index 46fb670..949b58f 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -27,7 +27,7 @@ export class GameStateRenderer { playerCount += 1; } document.querySelector("label[for='lobby-players']").innerText = - "Other People (" + playerCount + "/" + getGameSize(this.gameState.deck) + " Players)"; + "People (" + playerCount + "/" + getGameSize(this.gameState.deck) + " Players)"; } renderLobbyHeader() { @@ -67,22 +67,53 @@ export class GameStateRenderer { let div = document.createElement("div"); div.innerHTML = templates.END_GAME_PROMPT; document.body.appendChild(div); - renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers); + this.renderPlayersWithRoleAndAlignmentInfo(); } renderPlayerView() { renderPlayerRole(this.gameState); - renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + this.renderPlayersWithNoRoleInformation(); } refreshPlayerList(isModerator) { if (isModerator) { - renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers) + this.renderPlayersWithRoleAndAlignmentInfo() } else { - renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + this.renderPlayersWithNoRoleInformation(); } } + renderPlayersWithRoleAndAlignmentInfo() { + document.querySelectorAll('.game-player').forEach((el) => { + let pointer = el.dataset.pointer; + if (pointer && this.killPlayerHandlers[pointer]) { + el.removeEventListener('click', this.killPlayerHandlers[pointer]) + } + el.remove(); + }); + this.gameState.people.sort((a, b) => { + return a.name >= b.name ? 1 : -1; + }); + let teamGood = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); + let teamEvil = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); + renderGroupOfPlayers(teamEvil, this.killPlayerHandlers, this.gameState.accessCode, globals.ALIGNMENT.EVIL, true, this.socket); + renderGroupOfPlayers(teamGood, this.killPlayerHandlers, this.gameState.accessCode, globals.ALIGNMENT.GOOD, true, this.socket); + document.getElementById("players-alive-label").innerText = + 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; + + } + + renderPlayersWithNoRoleInformation() { + document.querySelectorAll('.game-player').forEach((el) => el.remove()); + this.gameState.people.sort((a, b) => { + return a.name >= b.name ? 1 : -1; + }); + renderGroupOfPlayers(this.gameState.people, this.killPlayerHandlers); + document.getElementById("players-alive-label").innerText = + 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; + + } + } function renderLobbyPerson(name, userType) { @@ -115,37 +146,6 @@ function removeExistingTitle() { } } -function renderPlayersWithRoleAndAlignmentInfo(people, socket, accessCode, handlers) { - document.querySelectorAll('.game-player').forEach((el) => { - let pointer = el.dataset.pointer; - if (pointer && handlers[pointer]) { - el.removeEventListener('click', handlers[pointer]) - } - el.remove(); - }); - people.sort((a, b) => { - return a.name >= b.name ? 1 : -1; - }); - let teamGood = people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); - let teamEvil = people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); - renderGroupOfPlayers(teamEvil, handlers, accessCode, globals.ALIGNMENT.EVIL, true, socket); - renderGroupOfPlayers(teamGood, handlers, accessCode, globals.ALIGNMENT.GOOD, true, socket); - document.getElementById("players-alive-label").innerText = - 'Players: ' + people.filter((person) => !person.out).length + ' / ' + people.length + ' Alive'; - -} - -function renderPlayersWithNoRoleInformation(people, handlers) { - document.querySelectorAll('.game-player').forEach((el) => el.remove()); - people.sort((a, b) => { - return a.name >= b.name ? 1 : -1; - }); - renderGroupOfPlayers(people, handlers); - document.getElementById("players-alive-label").innerText = - 'Players: ' + people.filter((person) => !person.out).length + ' / ' + people.length + ' Alive'; - -} - function renderGroupOfPlayers(players, handlers, accessCode=null, alignment=null, moderator=false, socket=null) { for (let player of players) { let container = document.createElement("div"); @@ -168,14 +168,18 @@ function renderGroupOfPlayers(players, handlers, accessCode=null, alignment=null document.getElementById("game-player-list").appendChild(container); } - if (moderator) { - handlers[player.id] = () => { - socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); + if (player.out) { + container.classList.add('killed'); + if (moderator) { + container.querySelector('.kill-player-button')?.remove(); } - if (player.out) { - container.classList.add('killed'); - container.querySelector('.kill-player-button').remove(); - } else { + } else { + if (moderator) { + handlers[player.id] = () => { + if (confirm("KILL " + player.name + "?")) { + socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); + } + } container.querySelector('.kill-player-button').addEventListener('click', handlers[player.id]); } } @@ -191,11 +195,19 @@ function renderPlayerRole(gameState) { name.classList.add('evil'); } name.setAttribute("title", gameState.client.gameRole); - document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); + if (gameState.client.out) { + document.querySelector('#role-description').innerText = "You have been killed."; + document.getElementById("role-image").setAttribute( + 'src', + '../images/tombstone.png' + ); + } else { + document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' + ); + } document.getElementById("game-role-back").addEventListener('click', () => { document.getElementById("game-role").style.display = 'flex'; diff --git a/client/modules/GameTimerManager.js b/client/modules/GameTimerManager.js index 3c1dcc9..a16cade 100644 --- a/client/modules/GameTimerManager.js +++ b/client/modules/GameTimerManager.js @@ -28,15 +28,19 @@ export class GameTimerManager { if (this.gameState.client.userType !== globals.USER_TYPES.PLAYER) { this.swapToPauseButton(); } - + let instance = this; let timer = document.getElementById('game-timer'); timer.classList.remove('paused'); timer.innerText = totalTime < 60000 ? returnHumanReadableTime(totalTime, true) : returnHumanReadableTime(totalTime); timerWorker.onmessage = function (e) { - if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { - timer.innerText = e.data.displayTime; + if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds >= 0) { + if (e.data.timeRemainingInMilliseconds === 0) { + instance.displayExpiredTime(); + } else { + timer.innerText = e.data.displayTime; + } } }; timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate }); @@ -70,6 +74,18 @@ export class GameTimerManager { timer.classList.add('paused'); } + displayExpiredTime() { + let currentBtn = document.querySelector('#play-pause img'); + if (currentBtn) { + currentBtn.removeEventListener('click', this.pauseListener); + currentBtn.removeEventListener('click', this.playListener); + currentBtn.remove(); + } + + let timer = document.getElementById('game-timer'); + timer.innerText = returnHumanReadableTime(0, true); + } + attachTimerSocketListeners(socket, timerWorker, gameStateRenderer) { // if (!socket.hasListeners(globals.EVENTS.START_TIMER)) { // socket.on(globals.EVENTS.START_TIMER, () => { @@ -100,6 +116,8 @@ export class GameTimerManager { console.log('received time remaining from server'); if (paused) { this.displayPausedTime(timeRemaining); + } else if (timeRemaining === 0) { + this.displayExpiredTime(); } else { this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); } diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 8c1e41f..4a38c95 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -75,7 +75,8 @@ export const templates = { "
" + "" + "