diff --git a/client/src/images/tutorial/dedicated-mod.PNG b/client/src/images/tutorial/dedicated-mod.PNG new file mode 100644 index 0000000..7d966c1 Binary files /dev/null and b/client/src/images/tutorial/dedicated-mod.PNG differ diff --git a/client/src/images/tutorial/moderator-view.PNG b/client/src/images/tutorial/moderator-view.PNG deleted file mode 100644 index e0b99a1..0000000 Binary files a/client/src/images/tutorial/moderator-view.PNG and /dev/null differ diff --git a/client/src/images/tutorial/temp-mod-view.PNG b/client/src/images/tutorial/temp-mod-view.PNG deleted file mode 100644 index 8b1bcb9..0000000 Binary files a/client/src/images/tutorial/temp-mod-view.PNG and /dev/null differ diff --git a/client/src/images/tutorial/temporary-mod.PNG b/client/src/images/tutorial/temporary-mod.PNG new file mode 100644 index 0000000..a433c00 Binary files /dev/null and b/client/src/images/tutorial/temporary-mod.PNG differ diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 02995a7..6b4791e 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -199,7 +199,7 @@ export class GameStateRenderer { }); const teamGood = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); const teamEvil = this.stateBucket.currentGameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); - renderGroupOfPlayers( + this.renderGroupOfPlayers( teamEvil, this.killPlayerHandlers, this.revealRoleHandlers, @@ -208,7 +208,7 @@ export class GameStateRenderer { this.stateBucket.currentGameState.moderator.userType, this.socket ); - renderGroupOfPlayers( + this.renderGroupOfPlayers( teamGood, this.killPlayerHandlers, this.revealRoleHandlers, @@ -222,27 +222,33 @@ export class GameStateRenderer { this.stateBucket.currentGameState.people.length + ' Alive'; } + removePlayerListEventListeners (removeEl = true) { + document.querySelectorAll('.game-player').forEach((el) => { + const pointer = el.dataset.pointer; + if (pointer && this.killPlayerHandlers[pointer]) { + el.removeEventListener('click', this.killPlayerHandlers[pointer]); + delete this.killPlayerHandlers[pointer]; + } + if (pointer && this.revealRoleHandlers[pointer]) { + el.removeEventListener('click', this.revealRoleHandlers[pointer]); + delete this.revealRoleHandlers[pointer]; + } + if (removeEl) { + el.remove(); + } + }); + } + renderPlayersWithNoRoleInformationUnlessRevealed (tempMod = false) { if (tempMod) { - document.querySelectorAll('.game-player').forEach((el) => { - const pointer = el.dataset.pointer; - if (pointer && this.killPlayerHandlers[pointer]) { - el.removeEventListener('click', this.killPlayerHandlers[pointer]); - delete this.killPlayerHandlers[pointer]; - } - if (pointer && this.revealRoleHandlers[pointer]) { - el.removeEventListener('click', this.revealRoleHandlers[pointer]); - delete this.revealRoleHandlers[pointer]; - } - el.remove(); - }); + this.removePlayerListEventListeners(); } document.querySelectorAll('.game-player').forEach((el) => el.remove()); /* TODO: UX issue - it's easier to parse visually when players are sorted this way, but shifting players around when they are killed or revealed is bad UX for the moderator. */ // sortPeopleByStatus(this.stateBucket.currentGameState.people); const modType = tempMod ? this.stateBucket.currentGameState.moderator.userType : null; - renderGroupOfPlayers( + this.renderGroupOfPlayers( this.stateBucket.currentGameState.people, this.killPlayerHandlers, this.revealRoleHandlers, @@ -305,6 +311,81 @@ export class GameStateRenderer { } this.renderPlayersWithNoRoleInformationUnlessRevealed(); } + + renderGroupOfPlayers ( + people, + killPlayerHandlers, + revealRoleHandlers, + accessCode = null, + alignment = null, + moderatorType, + socket = null + ) { + for (const player of people) { + const playerEl = document.createElement('div'); + playerEl.classList.add('game-player'); + + // add a reference to the player's id for each corresponding element in the list + if (moderatorType) { + playerEl.dataset.pointer = player.id; + playerEl.innerHTML = HTMLFragments.MODERATOR_PLAYER; + } else { + playerEl.innerHTML = HTMLFragments.GAME_PLAYER; + } + + playerEl.querySelector('.game-player-name').innerText = player.name; + const roleElement = playerEl.querySelector('.game-player-role'); + + // Add role/alignment indicators if necessary + if (moderatorType === globals.USER_TYPES.MODERATOR || player.revealed || this.stateBucket.currentGameState.status === globals.STATUS.ENDED) { + if (alignment === null) { + roleElement.classList.add(player.alignment); + } else { + roleElement.classList.add(alignment); + } + roleElement.innerText = player.gameRole; + } else { + roleElement.innerText = 'Role Unknown'; + } + + // Change element based on player's in/out status + if (player.out) { + playerEl.classList.add('killed'); + if (moderatorType) { + playerEl.querySelector('.kill-player-button')?.remove(); + insertPlaceholderButton(playerEl, false, 'killed'); + } + } else if (!player.out && moderatorType) { + killPlayerHandlers[player.id] = () => { + if (confirm('KILL ' + player.name + '?')) { + socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); + } + }; + playerEl.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]); + } + + // change element based on player's revealed/unrevealed status + if (player.revealed) { + if (moderatorType) { + playerEl.querySelector('.reveal-role-button')?.remove(); + insertPlaceholderButton(playerEl, true, 'revealed'); + } + } else if (!player.revealed && moderatorType) { + revealRoleHandlers[player.id] = () => { + if (confirm('REVEAL ' + player.name + '?')) { + socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id); + } + }; + playerEl.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]); + } + + const playerListContainerId = moderatorType === globals.USER_TYPES.MODERATOR + ? 'player-list-moderator-team-' + alignment + : 'game-player-list'; + + document.getElementById(playerListContainerId).appendChild(playerEl); + } + } } function renderPotentialMods (gameState, group, transferModHandlers, socket) { @@ -365,86 +446,6 @@ function removeExistingTitle () { } } -// TODO: refactor to reduce the cyclomatic complexity of this function -function renderGroupOfPlayers ( - people, - killPlayerHandlers, - revealRoleHandlers, - accessCode = null, - alignment = null, - moderatorType, - socket = null -) { - for (const player of people) { - const container = document.createElement('div'); - container.classList.add('game-player'); - if (moderatorType) { - container.dataset.pointer = player.id; - container.innerHTML = HTMLFragments.MODERATOR_PLAYER; - } else { - container.innerHTML = HTMLFragments.GAME_PLAYER; - } - container.querySelector('.game-player-name').innerText = player.name; - const roleElement = container.querySelector('.game-player-role'); - - if (moderatorType) { - roleElement.classList.add(alignment); - if (moderatorType === globals.USER_TYPES.MODERATOR) { - roleElement.innerText = player.gameRole; - document.getElementById('player-list-moderator-team-' + alignment).appendChild(container); - } else { - if (player.revealed) { - roleElement.innerText = player.gameRole; - roleElement.classList.add(player.alignment); - } else { - roleElement.innerText = 'Unknown'; - } - document.getElementById('game-player-list').appendChild(container); - } - } else if (player.revealed) { - roleElement.classList.add(player.alignment); - roleElement.innerText = player.gameRole; - document.getElementById('game-player-list').appendChild(container); - } else { - roleElement.innerText = 'Unknown'; - document.getElementById('game-player-list').appendChild(container); - } - - if (player.out) { - container.classList.add('killed'); - if (moderatorType) { - container.querySelector('.kill-player-button')?.remove(); - insertPlaceholderButton(container, false, 'killed'); - } - } else { - if (moderatorType) { - killPlayerHandlers[player.id] = () => { - if (confirm('KILL ' + player.name + '?')) { - socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); - } - }; - container.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]); - } - } - - if (player.revealed) { - if (moderatorType) { - container.querySelector('.reveal-role-button')?.remove(); - insertPlaceholderButton(container, true, 'revealed'); - } - } else { - if (moderatorType) { - revealRoleHandlers[player.id] = () => { - if (confirm('REVEAL ' + player.name + '?')) { - socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id); - } - }; - container.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]); - } - } - } -} - function renderPlayerRole (gameState) { const name = document.querySelector('#role-name'); name.innerText = gameState.client.gameRole; diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index fabd6f9..d57bc22 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -261,7 +261,7 @@ function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerW toast(killedPerson.name + ' was killed!', 'warning', true, true, 'medium'); } if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { - gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); + gameStateRenderer.removePlayerListEventListeners(false); } else { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); } diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 0f7e1db..17fa761 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -321,7 +321,7 @@ input { align-items: center; justify-content: center; position: fixed; - z-index: 55000; + z-index: 1000000; padding: 10px; border-radius: 3px; font-family: 'signika-negative', sans-serif; diff --git a/client/src/views/how-to-use.html b/client/src/views/how-to-use.html index 66e6c9f..086f432 100644 --- a/client/src/views/how-to-use.html +++ b/client/src/views/how-to-use.html @@ -84,7 +84,7 @@