diff --git a/.gcloudignore b/.gcloudignore new file mode 100644 index 0000000..4d7d693 --- /dev/null +++ b/.gcloudignore @@ -0,0 +1,17 @@ +# This file specifies files that are *not* uploaded to Google Cloud +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +# Node.js dependencies: +node_modules/ \ No newline at end of file diff --git a/app.yaml b/app.yaml new file mode 100644 index 0000000..7fde67a --- /dev/null +++ b/app.yaml @@ -0,0 +1 @@ +runtime: nodejs14 diff --git a/client/config/globals.js b/client/config/globals.js index 17ebd04..8eec0a7 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -11,7 +11,8 @@ export const globals = { START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', RESUME_TIMER: 'resumeTimer', - GET_TIME_REMAINING: 'getTimeRemaining' + GET_TIME_REMAINING: 'getTimeRemaining', + KILL_PLAYER: 'killPlayer' }, STATUS: { LOBBY: "lobby", diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index c545ac7..46fb670 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -3,8 +3,10 @@ import { toast } from "./Toast.js"; import {templates} from "./Templates.js"; export class GameStateRenderer { - constructor(gameState) { + constructor(gameState, socket) { this.gameState = gameState; + this.socket = socket; + this.killPlayerHandlers = {}; this.cardFlipped = false; } @@ -61,38 +63,26 @@ export class GameStateRenderer { // document.getElementById("game-title").appendChild(title); } - renderPlayerRole() { - let name = document.querySelector('#role-name'); - name.innerText = this.gameState.client.gameRole; - if (this.gameState.client.alignment === globals.ALIGNMENT.GOOD) { - name.classList.add('good'); - } else { - name.classList.add('evil'); - } - name.setAttribute("title", this.gameState.client.gameRole); - document.querySelector('#role-description').innerText = this.gameState.client.gameRoleDescription; - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + this.gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); - - document.getElementById("game-role-back").addEventListener('click', () => { - document.getElementById("game-role").style.display = 'flex'; - document.getElementById("game-role-back").style.display = 'none'; - }); - - document.getElementById("game-role").addEventListener('click', () => { - document.getElementById("game-role-back").style.display = 'flex'; - document.getElementById("game-role").style.display = 'none'; - }); - } - renderModeratorView() { let div = document.createElement("div"); div.innerHTML = templates.END_GAME_PROMPT; document.body.appendChild(div); - renderModeratorPlayers(this.gameState.people); + renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers); } + + renderPlayerView() { + renderPlayerRole(this.gameState); + renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + } + + refreshPlayerList(isModerator) { + if (isModerator) { + renderPlayersWithRoleAndAlignmentInfo(this.gameState.people, this.socket, this.gameState.accessCode, this.killPlayerHandlers) + } else { + renderPlayersWithNoRoleInformation(this.gameState.people, this.killPlayerHandlers); + } + } + } function renderLobbyPerson(name, userType) { @@ -125,27 +115,95 @@ function removeExistingTitle() { } } -function renderModeratorPlayers(people) { - people.sort(); +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); - renderGroupOfPlayersFromTeam(teamEvil, globals.ALIGNMENT.EVIL); - renderGroupOfPlayersFromTeam(teamGood, globals.ALIGNMENT.GOOD); + 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 renderGroupOfPlayersFromTeam(players, alignment) { +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"); - container.classList.add('moderator-player'); - container.innerHTML = templates.MODERATOR_PLAYER; - container.querySelector('.moderator-player-name').innerText = player.name; - let roleElement = container.querySelector('.moderator-player-role') - roleElement.innerText = player.gameRole; - roleElement.classList.add(alignment); + container.classList.add('game-player'); + container.dataset.pointer = player.id; + if (alignment) { + container.innerHTML = templates.MODERATOR_PLAYER; + } else { + container.innerHTML = templates.GAME_PLAYER; + } + container.querySelector('.game-player-name').innerText = player.name; + let roleElement = container.querySelector('.game-player-role') - document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); + if (alignment) { + roleElement.classList.add(alignment); + roleElement.innerText = player.gameRole; + document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); + } else { + roleElement.innerText = "Unknown" + 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'); + container.querySelector('.kill-player-button').remove(); + } else { + container.querySelector('.kill-player-button').addEventListener('click', handlers[player.id]); + } + } } } + +function renderPlayerRole(gameState) { + let name = document.querySelector('#role-name'); + name.innerText = gameState.client.gameRole; + if (gameState.client.alignment === globals.ALIGNMENT.GOOD) { + name.classList.add('good'); + } else { + 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' + ); + + document.getElementById("game-role-back").addEventListener('click', () => { + document.getElementById("game-role").style.display = 'flex'; + document.getElementById("game-role-back").style.display = 'none'; + }); + + document.getElementById("game-role").addEventListener('click', () => { + document.getElementById("game-role-back").style.display = 'flex'; + document.getElementById("game-role").style.display = 'none'; + }); +} diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 44418d1..8c1e41f 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -25,16 +25,12 @@ export const templates = { "
" + "" + "
", - GAME: + PLAYER_GAME_VIEW: "
" + "
" + "" + "
" + "
" + - "
" + - "" + - "
" + - "
" + "
" + "