mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 15:57:50 +01:00
flesh out killing players, display 0 on timerand remove play/pause button
This commit is contained in:
@@ -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",
|
||||
|
||||
3
client/images/eye.svg
Normal file
3
client/images/eye.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="#d7d7d7" d="M15 12c0 1.654-1.346 3-3 3s-3-1.346-3-3 1.346-3 3-3 3 1.346 3 3zm9-.449s-4.252 8.449-11.985 8.449c-7.18 0-12.015-8.449-12.015-8.449s4.446-7.551 12.015-7.551c7.694 0 11.985 7.551 11.985 7.551zm-7 .449c0-2.757-2.243-5-5-5s-5 2.243-5 5 2.243 5 5 5 5-2.243 5-5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
BIN
client/images/tombstone.png
Normal file
BIN
client/images/tombstone.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ export const templates = {
|
||||
"<div class='game-player-role'></div>" +
|
||||
"</div>" +
|
||||
"<div>" +
|
||||
"<button class='moderator-player-button kill-player-button'>Kill \u2694</button>" +
|
||||
"<button class='moderator-player-button kill-player-button'>Kill \uD83D\uDC80</button>" +
|
||||
"<button class='moderator-player-button reveal-role-button'>Reveal Role <img src='../images/eye.svg'/></button>" +
|
||||
"</div>",
|
||||
GAME_PLAYER:
|
||||
"<div>" +
|
||||
|
||||
@@ -30,6 +30,10 @@ onmessage = function (e) {
|
||||
function stepFn (expected, interval, start, totalTime) {
|
||||
const now = Date.now();
|
||||
if (now - start >= totalTime) {
|
||||
postMessage({
|
||||
timeRemainingInMilliseconds: 0,
|
||||
displayTime: returnHumanReadableTime(0, true)
|
||||
});
|
||||
return;
|
||||
}
|
||||
const delta = now - expected;
|
||||
|
||||
@@ -132,6 +132,22 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim
|
||||
if (timerWorker && gameTimerManager) {
|
||||
gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer);
|
||||
}
|
||||
|
||||
if (!socket.hasListeners(globals.EVENTS.KILL_PLAYER)) {
|
||||
socket.on(globals.EVENTS.KILL_PLAYER, (id) => {
|
||||
let killedPerson = gameStateRenderer.gameState.people.find((person) => person.id === id);
|
||||
if (killedPerson) {
|
||||
killedPerson.out = true;
|
||||
if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR) {
|
||||
toast(killedPerson.name + ' killed.', 'success', true, true, 6);
|
||||
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo()
|
||||
} else {
|
||||
toast(killedPerson.name + ' was killed!', 'warning', false, true, 6);
|
||||
gameStateRenderer.renderPlayersWithNoRoleInformation();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function displayStartGamePromptForModerators(gameStateRenderer, socket) {
|
||||
|
||||
@@ -214,11 +214,11 @@ input {
|
||||
}
|
||||
|
||||
.good, .compact-card.good .card-role {
|
||||
color: #4b6bfa !important;
|
||||
color: #4b6bfa;
|
||||
}
|
||||
|
||||
.evil, .compact-card.evil .card-role {
|
||||
color: #e73333 !important
|
||||
color: #e73333;
|
||||
}
|
||||
|
||||
@keyframes placeholder {
|
||||
|
||||
@@ -346,7 +346,7 @@ label[for='moderator'] {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.kill-player-button, .make-mod-button {
|
||||
.kill-player-button, .reveal-role-button {
|
||||
font-family: 'signika-negative', sans-serif !important;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
@@ -358,6 +358,18 @@ label[for='moderator'] {
|
||||
text-shadow: 0 3px 4px rgb(0 0 0 / 55%);
|
||||
margin: 5px 0 5px 25px;
|
||||
min-width: 6em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.reveal-role-button {
|
||||
background-color: #3f5256;
|
||||
}
|
||||
|
||||
.reveal-role-button img {
|
||||
width: 18px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.killed::after {
|
||||
@@ -367,12 +379,13 @@ label[for='moderator'] {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.killed {
|
||||
filter: grayscale(1);
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
.killed, .killed .game-player-role {
|
||||
color: gray !important;
|
||||
}
|
||||
|
||||
.reveal-role-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.make-mod-button {
|
||||
@@ -390,7 +403,12 @@ label[for='moderator'] {
|
||||
}
|
||||
|
||||
.kill-player-button {
|
||||
background-color: #3f5256;
|
||||
background-color: #9f4747;
|
||||
}
|
||||
|
||||
.game-player > div:nth-child(2) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#game-player-list > div {
|
||||
|
||||
@@ -13,20 +13,16 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<link rel="stylesheet" href="../styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="../styles/create.css">
|
||||
<link rel="stylesheet" href="../styles/modal.css">
|
||||
<link rel="stylesheet" href="../styles/third_party/dropdown.min.css">
|
||||
<link rel="stylesheet" href="../styles/third_party/transition.min.css">
|
||||
<link rel="stylesheet" href="../styles/third_party/search.min.css">
|
||||
<script src="../modules/third_party/jQuery/jquery-3.6.0.min.js"></script>
|
||||
<script src="../modules/third_party/semantic-ui/transition.min.js"></script>
|
||||
<script src="../modules/third_party/semantic-ui/dropdown.min.js"></script>
|
||||
<script src="../modules/third_party/semantic-ui/search.min.js"></script>
|
||||
<link rel="stylesheet" href="/styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="/styles/create.css">
|
||||
<link rel="stylesheet" href="/styles/modal.css">
|
||||
<link rel="stylesheet" href="/styles/third_party/dropdown.min.css">
|
||||
<link rel="stylesheet" href="/styles/third_party/transition.min.css">
|
||||
<link rel="stylesheet" href="/styles/third_party/search.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar">
|
||||
<img src="../images/Werewolf_Small.png"/>
|
||||
<img src="/images/Werewolf_Small.png"/>
|
||||
<a href="/">Home</a>
|
||||
</div>
|
||||
<span>
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<link rel="stylesheet" href="../styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="../styles/game.css">
|
||||
<link rel="stylesheet" href="../styles/modal.css">
|
||||
<link rel="stylesheet" href="/styles/GLOBAL.css">
|
||||
<link rel="stylesheet" href="/styles/game.css">
|
||||
<link rel="stylesheet" href="/styles/modal.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar">
|
||||
<img src="../images/Werewolf_Small.png"/>
|
||||
<img src="/images/Werewolf_Small.png"/>
|
||||
<a href="/">Home</a>
|
||||
</div>
|
||||
<div id="game-title"></div>
|
||||
|
||||
@@ -88,6 +88,10 @@ class GameManager {
|
||||
socketId: socket.id,
|
||||
logLevel: this.logger.logLevel
|
||||
});
|
||||
} else {
|
||||
if (game.timerParams && game.timerParams.timeRemaining === 0) {
|
||||
this.namespace.to(socket.id).emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -96,10 +100,10 @@ class GameManager {
|
||||
let game = this.activeGameRunner.activeGames[accessCode];
|
||||
if (game) {
|
||||
let person = game.people.find((person) => person.id === personId)
|
||||
if (person) {
|
||||
if (person && !person.out) {
|
||||
this.logger.debug('game ' + accessCode + ': killing player ' + person.name);
|
||||
person.out = true;
|
||||
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, )
|
||||
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -11,11 +11,13 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
|
||||
? { name: person.name, cookie: person.cookie, userType: person.userType }
|
||||
: {
|
||||
name: person.name,
|
||||
id: person.id,
|
||||
cookie: person.cookie,
|
||||
userType: person.userType,
|
||||
gameRole: person.gameRole,
|
||||
gameRoleDescription: person.gameRoleDescription,
|
||||
alignment: person.alignment
|
||||
alignment: person.alignment,
|
||||
out: person.out
|
||||
}
|
||||
switch (person.userType) {
|
||||
case globals.USER_TYPES.PLAYER:
|
||||
@@ -27,10 +29,10 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
|
||||
deck: game.deck,
|
||||
people: game.people
|
||||
.filter((person) => {
|
||||
return person.assigned === true && person.cookie !== client.cookie
|
||||
return person.assigned === true
|
||||
&& (person.userType !== globals.USER_TYPES.MODERATOR && person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR)
|
||||
})
|
||||
.map((filteredPerson) => ({ name: filteredPerson.name, userType: filteredPerson.userType })),
|
||||
.map((filteredPerson) => ({ name: filteredPerson.name, id: filteredPerson.id, userType: filteredPerson.userType, out: filteredPerson.out })),
|
||||
timerParams: game.timerParams,
|
||||
isFull: game.isFull,
|
||||
}
|
||||
@@ -91,7 +93,7 @@ function mapPeopleForTempModerator(people, client) {
|
||||
}
|
||||
|
||||
function mapPerson(person) {
|
||||
return { name: person.name, userType: person.userType, out: person.out };
|
||||
return { name: person.name, id: person.id, userType: person.userType, out: person.out };
|
||||
}
|
||||
|
||||
module.exports = GameStateCurator;
|
||||
|
||||
Reference in New Issue
Block a user