ending games, style low timer

This commit is contained in:
Alec
2021-12-22 15:49:12 -05:00
parent 5690a0a46c
commit 31f5d01092
29 changed files with 794 additions and 125 deletions

View File

@@ -33,5 +33,10 @@ export const defaultCards = [
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.",
},
{
role: "Mason",
team: "good",
description: "Masons know who the other masons are.",
}
];

View File

@@ -15,11 +15,13 @@ export const globals = {
KILL_PLAYER: 'killPlayer',
REVEAL_PLAYER: 'revealPlayer',
TRANSFER_MODERATOR: 'transferModerator',
CHANGE_NAME: 'changeName'
CHANGE_NAME: 'changeName',
END_GAME: 'endGame'
},
STATUS: {
LOBBY: "lobby",
IN_PROGRESS: "in progress"
IN_PROGRESS: "in progress",
ENDED: "ended"
},
ALIGNMENT: {
GOOD: "good",
@@ -51,6 +53,7 @@ export const globals = {
player: ' \uD83C\uDFAE',
moderator: ' \uD83D\uDC51',
'player / temp mod': ' \uD83C\uDFAE\uD83D\uDC51',
spectator: ' \uD83D\uDC7B'
spectator: ' \uD83D\uDC7B',
killed: '\uD83D\uDC80'
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -9,6 +9,6 @@
</g>
<g>
<title>Layer 1</title>
<path stroke="none" id="svg_1" d="m78.750021,0.749998c-43.078721,0 -78.000016,34.473574 -78.000016,76.999988c0,42.526414 34.921295,76.999988 78.000016,76.999988c43.078721,0 78.000016,-34.473574 78.000016,-76.999988c0,-42.526414 -34.921295,-76.999988 -78.000016,-76.999988zm0,138.100913c-34.137753,0 -61.899834,-27.411445 -61.899834,-61.100926c0,-33.694779 27.762081,-61.106224 61.899834,-61.106224c34.137753,0 61.894467,27.406147 61.894467,61.106224c0,33.700077 -27.767448,61.100926 -61.894467,61.100926zm-0.005367,-118.71582c-2.9678,0 -5.361361,2.373469 -5.361361,5.297921l0,48.852132l-32.447234,14.007704c-2.715564,1.176139 -3.949911,4.301912 -2.758498,6.98266c0.880143,1.98672 2.844366,3.173455 4.910556,3.173455c0.719141,0 1.454383,-0.143044 2.152058,-0.450323l35.559936,-15.353376c0.026834,-0.010596 0.048301,-0.021192 0.069767,-0.031788l0.021467,-0.010596c0.080501,-0.031788 0.123435,-0.105958 0.198569,-0.132448c0.55814,-0.275492 1.078712,-0.598665 1.497317,-1.033095c0.182469,-0.180129 0.284437,-0.413238 0.423971,-0.619857c0.257603,-0.339067 0.542039,-0.672836 0.697675,-1.080776c0.128801,-0.317875 0.139535,-0.66224 0.203936,-1.001307c0.069767,-0.344365 0.203936,-0.646346 0.203936,-0.990711l0,-52.316972c0,-2.919155 -2.404294,-5.292623 -5.372094,-5.292623z" stroke-width="1.5" fill="#86cb92"/>
<path stroke="none" id="svg_1" d="m78.750021,0.749998c-43.078721,0 -78.000016,34.473574 -78.000016,76.999988c0,42.526414 34.921295,76.999988 78.000016,76.999988c43.078721,0 78.000016,-34.473574 78.000016,-76.999988c0,-42.526414 -34.921295,-76.999988 -78.000016,-76.999988zm0,138.100913c-34.137753,0 -61.899834,-27.411445 -61.899834,-61.100926c0,-33.694779 27.762081,-61.106224 61.899834,-61.106224c34.137753,0 61.894467,27.406147 61.894467,61.106224c0,33.700077 -27.767448,61.100926 -61.894467,61.100926zm-0.005367,-118.71582c-2.9678,0 -5.361361,2.373469 -5.361361,5.297921l0,48.852132l-32.447234,14.007704c-2.715564,1.176139 -3.949911,4.301912 -2.758498,6.98266c0.880143,1.98672 2.844366,3.173455 4.910556,3.173455c0.719141,0 1.454383,-0.143044 2.152058,-0.450323l35.559936,-15.353376c0.026834,-0.010596 0.048301,-0.021192 0.069767,-0.031788l0.021467,-0.010596c0.080501,-0.031788 0.123435,-0.105958 0.198569,-0.132448c0.55814,-0.275492 1.078712,-0.598665 1.497317,-1.033095c0.182469,-0.180129 0.284437,-0.413238 0.423971,-0.619857c0.257603,-0.339067 0.542039,-0.672836 0.697675,-1.080776c0.128801,-0.317875 0.139535,-0.66224 0.203936,-1.001307c0.069767,-0.344365 0.203936,-0.646346 0.203936,-0.990711l0,-52.316972c0,-2.919155 -2.404294,-5.292623 -5.372094,-5.292623z" stroke-width="1.5" fill="#d7d7d7"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

1
client/images/email.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#fff" width="24" height="24" viewBox="0 0 24 24"><path d="M12 .02c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm6.99 6.98l-6.99 5.666-6.991-5.666h13.981zm.01 10h-14v-8.505l7 5.673 7-5.672v8.504z"/></svg>

After

Width:  |  Height:  |  Size: 274 B

14
client/images/info.svg Normal file
View File

@@ -0,0 +1,14 @@
<svg width="223" height="223" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="225" width="225" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
</g>
</g>
<g>
<title>Layer 1</title>
<path id="svg_1" d="m111.499982,3.500023c-59.646658,0 -107.999967,48.353386 -107.999967,107.999967c0,59.647903 48.353308,107.999993 107.999967,107.999993c59.647946,0 107.999993,-48.352082 107.999993,-107.999993c0,-59.646581 -48.352047,-107.999967 -107.999993,-107.999967zm-9.236728,48.553238l17.983007,0l0,19.128161l-17.983007,0l0,-19.128161zm29.760957,116.085726l-19.780144,0c-7.684691,0 -10.961591,-3.269729 -10.961591,-11.117474l0,-51.012233c0,-2.452006 -1.306774,-3.597168 -3.595768,-3.597168l-6.539459,0l0,-17.662679l19.781535,0c7.690393,0 10.953072,3.432714 10.953072,11.116049l0,51.176601c0,2.296157 1.306782,3.597237 3.595768,3.597237l6.539537,0l0,17.499659l0.00705,0l0,0.000009z" stroke-width="1.5" stroke="none" fill="#d7d7d7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

107
client/images/person.svg Normal file
View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="119.66505mm"
height="109.59733mm"
viewBox="0 0 119.66505 109.59733"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="host.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="bspline"
id="path-effect4526"
is_visible="true"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
<inkscape:path-effect
effect="bspline"
id="path-effect4526-4"
is_visible="true"
weight="33.333333"
steps="2"
helper_size="0"
apply_no_weight="true"
apply_with_weight="true"
only_selected="false" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="-232.43275"
inkscape:cy="116.16088"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="1027"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-44.488903,-69.97024)">
<circle
style="opacity:0.95999995;fill:none;fill-opacity:1;stroke:#d7d7d7;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path4518"
cx="104.32143"
cy="101.96429"
r="30.994047" />
<g
id="g4548"
transform="translate(0.75595639,-10.583334)">
<path
inkscape:original-d="m 44.60119,189.65476 c 6.047883,-10.5836 12.095501,-21.16693 18.142858,-31.75 13.607002,-2.6e-4 27.214549,-0.50423 40.821422,-0.75595"
inkscape:path-effect="#path-effect4526"
inkscape:connector-curvature="0"
id="path4524"
d="m 44.60119,189.65476 c 6.047799,-10.58365 12.095417,-21.16698 21.920308,-26.58444 9.824892,-5.41745 23.437104,-5.66953 37.043972,-5.92151"
style="fill:none;stroke:#d7d7d7;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:original-d="m 44.60119,189.65476 c 6.047883,-10.5836 12.095501,-21.16693 18.142858,-31.75 13.607002,-2.6e-4 27.214549,-0.50423 40.821422,-0.75595"
inkscape:path-effect="#path-effect4526-4"
inkscape:connector-curvature="0"
id="path4524-1"
d="m 44.60119,189.65476 c 6.047799,-10.58365 12.095417,-21.16698 21.920308,-26.58444 9.824892,-5.41745 23.437104,-5.66953 37.043972,-5.92151"
style="fill:none;stroke:#d7d7d7;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(-1,0,0,1,207.13094,0)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

14
client/images/x.svg Normal file
View File

@@ -0,0 +1,14 @@
<svg width="209" height="209" xmlns="http://www.w3.org/2000/svg">
<!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ -->
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="172" width="172" y="-1" x="-1"/>
<g display="none" overflow="visible" y="0" x="0" height="100%" width="100%" id="canvasGrid">
<rect fill="url(#gridpattern)" stroke-width="0" y="0" x="0" height="100%" width="100%"/>
</g>
</g>
<g>
<title>Layer 1</title>
<path id="svg_1" d="m0.749996,50.93695l50.18695,-50.18695l53.312819,53.312382l53.312819,-53.312382l50.187422,50.18695l-53.312833,53.312819l53.312833,53.312819l-50.187422,50.187422l-53.312819,-53.312833l-53.312819,53.312833l-50.18695,-50.187422l53.312382,-53.312819l-53.312382,-53.312819z" stroke-width="1.5" stroke="none" fill="#d7d7d7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 844 B

View File

@@ -48,6 +48,29 @@ export class GameStateRenderer {
let copyImg = document.createElement("img");
copyImg.setAttribute("src", "../images/copy.svg");
gameLinkContainer.appendChild(copyImg);
let time = document.getElementById("game-time");
let playerCount = document.getElementById("game-player-count");
playerCount.innerText = getGameSize(this.stateBucket.currentGameState.deck) + ' Players'
if (this.stateBucket.currentGameState.timerParams) {
let timeString = "";
let hours = this.stateBucket.currentGameState.timerParams.hours;
let minutes = this.stateBucket.currentGameState.timerParams.minutes
if (hours) {
timeString += hours > 1
? hours + ' hours '
: hours + ' hour '
}
if (minutes) {
timeString += minutes > 1
? minutes + ' minutes '
: minutes + ' minute '
}
time.innerText = timeString;
} else {
time.innerText = 'untimed';
}
}
renderLobbyFooter() {
@@ -69,7 +92,16 @@ export class GameStateRenderer {
renderModeratorView() {
let div = document.createElement("div");
div.innerHTML = templates.END_GAME_PROMPT;
document.body.appendChild(div);
document.getElementById("game-content").appendChild(div);
document.getElementById("end-game-button").addEventListener('click', (e) => {
e.preventDefault();
if (confirm("End the game?")) {
this.socket.emit(
globals.COMMANDS.END_GAME,
this.stateBucket.currentGameState.accessCode
);
}
});
let modTransferButton = document.getElementById("mod-transfer-button");
modTransferButton.addEventListener(
@@ -78,7 +110,7 @@ export class GameStateRenderer {
ModalManager.displayModal(
"transfer-mod-modal",
"transfer-mod-modal-background",
"close-modal-button"
"close-mod-transfer-modal-button"
)
}
)
@@ -118,18 +150,7 @@ export class GameStateRenderer {
}
renderPlayersWithRoleAndAlignmentInfo() {
document.querySelectorAll('.game-player').forEach((el) => {
let 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();
});
removeExistingPlayerElements(this.killPlayerHandlers, this.revealRoleHandlers);
this.stateBucket.currentGameState.people.sort((a, b) => {
return a.name >= b.name ? 1 : -1;
});
@@ -151,7 +172,8 @@ export class GameStateRenderer {
this.stateBucket.currentGameState.accessCode,
globals.ALIGNMENT.GOOD,
this.stateBucket.currentGameState.moderator.userType,
this.socket);
this.socket
);
document.getElementById("players-alive-label").innerText =
'Players: ' + this.stateBucket.currentGameState.people.filter((person) => !person.out).length + ' / '
+ this.stateBucket.currentGameState.people.length + ' Alive';
@@ -208,28 +230,27 @@ export class GameStateRenderer {
}
el.remove();
});
let modalContent = document.getElementById("transfer-mod-form-content");
if (modalContent) {
renderPotentialMods(
this.stateBucket.currentGameState,
this.stateBucket.currentGameState.people,
this.transferModHandlers,
modalContent,
this.socket
);
renderPotentialMods( // spectators can also be made mods.
this.stateBucket.currentGameState,
this.stateBucket.currentGameState.spectators,
this.transferModHandlers,
modalContent,
this.socket
);
}
renderPotentialMods(
this.stateBucket.currentGameState,
this.stateBucket.currentGameState.people,
this.transferModHandlers,
this.socket
);
renderPotentialMods( // spectators can also be made mods.
this.stateBucket.currentGameState,
this.stateBucket.currentGameState.spectators,
this.transferModHandlers,
this.socket
);
}
renderEndOfGame() {
this.renderPlayersWithNoRoleInformationUnlessRevealed();
}
}
function renderPotentialMods(gameState, group, transferModHandlers, modalContent, socket) {
function renderPotentialMods(gameState, group, transferModHandlers, socket) {
let modalContent = document.getElementById("transfer-mod-modal-content");
for (let member of group) {
if ((member.out || member.userType === globals.USER_TYPES.SPECTATOR) && !(member.id === gameState.client.id)) {
let container = document.createElement("div");
@@ -244,6 +265,7 @@ function renderPotentialMods(gameState, group, transferModHandlers, modalContent
container.addEventListener('click', transferModHandlers[member.id]);
modalContent.appendChild(container);
console.log('test');
}
}
}
@@ -420,3 +442,18 @@ function insertPlaceholderButton(container, append, type) {
container.querySelector('.player-action-buttons').prepend(button);
}
}
function removeExistingPlayerElements(killPlayerHandlers, revealRoleHandlers) {
document.querySelectorAll('.game-player').forEach((el) => {
let pointer = el.dataset.pointer;
if (pointer && killPlayerHandlers[pointer]) {
el.removeEventListener('click', killPlayerHandlers[pointer]);
delete killPlayerHandlers[pointer];
}
if (pointer && revealRoleHandlers[pointer]) {
el.removeEventListener('click', revealRoleHandlers[pointer]);
delete revealRoleHandlers[pointer];
}
el.remove();
});
}

View File

@@ -22,6 +22,11 @@ export class GameTimerManager {
let instance = this;
let timer = document.getElementById('game-timer');
timer.classList.remove('paused');
timer.classList.remove('paused-low');
timer.classList.remove('low');
if (totalTime < 60000) {
timer.classList.add('low');
}
timer.innerText = totalTime < 60000
? returnHumanReadableTime(totalTime, true)
: returnHumanReadableTime(totalTime);
@@ -29,6 +34,9 @@ export class GameTimerManager {
if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds >= 0) {
if (e.data.timeRemainingInMilliseconds === 0) {
instance.displayExpiredTime();
} else if (e.data.timeRemainingInMilliseconds < 60000) {
timer.classList.add('low');
timer.innerText = e.data.displayTime;
} else {
timer.innerText = e.data.displayTime;
}
@@ -49,10 +57,14 @@ export class GameTimerManager {
timerWorker.postMessage('stop');
let timer = document.getElementById('game-timer');
timer.innerText = timeRemaining < 60000
? returnHumanReadableTime(timeRemaining, true)
: returnHumanReadableTime(timeRemaining);
timer.classList.add('paused');
if (timeRemaining < 60000) {
timer.innerText = returnHumanReadableTime(timeRemaining, true);
timer.classList.add('paused-low');
timer.classList.add('low');
} else {
timer.innerText = returnHumanReadableTime(timeRemaining);
timer.classList.add('paused');
}
}
}
@@ -65,10 +77,14 @@ export class GameTimerManager {
}
let timer = document.getElementById('game-timer');
timer.innerText = time < 60000
? returnHumanReadableTime(time, true)
: returnHumanReadableTime(time);
timer.classList.add('paused');
if (time < 60000) {
timer.innerText = returnHumanReadableTime(time, true);
timer.classList.add('paused-low');
timer.classList.add('low');
} else {
timer.innerText = returnHumanReadableTime(time);
timer.classList.add('paused');
}
}
displayExpiredTime() {

View File

@@ -18,6 +18,8 @@ function displayModal(modalId, backgroundId, closeButtonId) {
});
closeBtn.removeEventListener("click", closeModalHandler);
closeBtn.addEventListener("click", closeModalHandler);
} else {
throw new Error("One or more of the ids supplied to ModalManager.displayModal is invalid.");
}
}

View File

@@ -5,8 +5,19 @@ export const templates = {
"<label for='game-link'>Share Link</label>" +
"<div id='game-link'></div>" +
"</div>" +
"<div id='game-time'></div>" +
"<div id='game-player-count'></div>" +
"<div id='game-parameters'>" +
"<div>" +
"<img alt='clock' src='/images/clock.svg'/>" +
"<div id='game-time'></div>" +
"</div>" +
"<div>" +
"<img alt='person' src='/images/person.svg'/>" +
"<div id='game-player-count'></div>" +
"</div>" +
"</div>" +
"<div>" +
"<button id='role-info-button'>View Role Info <img src='/images/info.svg'</button>" +
"</div>" +
"</div>" +
"<div>" +
"<div id='lobby-people-container'>" +
@@ -31,6 +42,9 @@ export const templates = {
"<label for='game-timer'>Time Remaining</label>" +
"<div id='game-timer'></div>" +
"</div>" +
"<div>" +
"<button id='role-info-button'>View Role Info <img src='/images/info.svg'</button>" +
"</div>" +
"</div>" +
"<div id='game-role' style='display:none'>" +
"<h4 id='role-name'></h4>" +
@@ -51,22 +65,22 @@ export const templates = {
"<label for='game-timer'>Time Remaining</label>" +
"<div id='game-timer'></div>" +
"</div>" +
"<div>" +
"<button id='role-info-button'>View Role Info <img src='/images/info.svg'</button>" +
"</div>" +
"</div>" +
"<div>" +
"<div id='game-people-container'>" +
"<label id='players-alive-label'></label>" +
"<div id='game-player-list'></div>" +
"</div>",
MODERATOR_GAME_VIEW:
"<div id='transfer-mod-modal-background' class='modal-background' style='display: none'></div>" +
"<div id='transfer-mod-modal' class='modal' style='display: none'>" +
"<form id='transfer-mod-form'>" +
"<div id='transfer-mod-form-content'>" +
"<h3>Transfer Mod Powers &#128081;</h3>" +
"</div>" +
"<div id='modal-button-container'>" +
"<button id='close-modal-button'>Cancel</button>" +
"</div>" +
"</form>" +
"<h3>Transfer Mod Powers &#128081;</h3>" +
"<div id='transfer-mod-modal-content'></div>" +
"<div id='modal-button-container'>" +
"<button id='close-mod-transfer-modal-button'>Cancel</button>" +
"</div>" +
"</div>" +
"<div id='game-header'>" +
"<div class='timer-container-moderator'>" +
@@ -77,6 +91,9 @@ export const templates = {
"<div id='play-pause'>" + "</div>" +
"</div>" +
"<button id='mod-transfer-button' class='moderator-player-button make-mod-button'>Transfer Mod Powers \uD83D\uDD00</button>" +
"<div>" +
"<button id='role-info-button'>View Role Info <img src='/images/info.svg'</button>" +
"</div>" +
"</div>" +
"<div>" +
"<label id='players-alive-label'></label>" +
@@ -178,5 +195,31 @@ export const templates = {
"<input type='submit' id='submit-new-name' value='Set Name'/>" +
"</div>" +
"</form>" +
"</div>",
ROLE_INFO_MODAL:
"<div id='role-info-modal-background' class='modal-background'></div>" +
"<div id='role-info-modal' class='modal'>" +
"<h2>Roles in this game:</h2>" +
"<div id='game-role-info-container'></div>" +
"<div id='modal-button-container'>" +
"<button id='close-role-info-modal-button'>Close</button>" +
"</div>" +
"</div>",
END_OF_GAME_VIEW:
"<h2>The moderator has ended the game. Roles are revealed.</h2>" +
"<div id='end-of-game-header'>" +
"<div>" +
"<button id='role-info-button'>View Role Info <img src='/images/info.svg'</button>" +
"</div>" +
"<div>" +
"<a href='/'>" +
"<button>Go Home \uD83C\uDFE0</button>" +
"</a>" +
"</div>" +
"</div>" +
"<div id='game-people-container'>" +
"<label id='players-alive-label'></label>" +
"<div id='game-player-list'></div>" +
"</div>"
}

View File

@@ -9,7 +9,7 @@ export const toast = (message, type, positionAtTop = true, dispelAutomatically=t
function buildAndInsertMessageElement (message, type, positionAtTop, dispelAutomatically, duration) {
cancelCurrentToast();
let backgroundColor, border;
const position = positionAtTop ? 'top:2rem;' : 'bottom: 35px;';
const position = positionAtTop ? 'top:2rem;' : 'bottom: 90px;';
switch (type) {
case 'warning':
backgroundColor = '#fff5b1';

View File

@@ -69,7 +69,7 @@ function prepareGamePage(environment, socket, timerWorker) {
}
})
} else {
toast("Name must be fewer than 30 characters.", 'error', true, true, 8);
toast("Name must be between 1 and 30 characters.", 'error', true, true, 8);
}
}
}
@@ -131,12 +131,19 @@ function processGameState (currentGameState, userId, socket, gameStateRenderer)
default:
break;
}
socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode);
break;
case globals.STATUS.ENDED:
let container = document.getElementById("game-state-container")
container.innerHTML = templates.END_OF_GAME_VIEW;
container.classList.add('vertical-flex');
gameStateRenderer.renderEndOfGame();
break;
default:
break;
}
activateRoleInfoButton(stateBucket.currentGameState.deck);
}
function displayClientInfo(name, userType) {
@@ -148,7 +155,7 @@ function displayClientInfo(name, userType) {
function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager) {
if (!socket.hasListeners(globals.EVENTS.PLAYER_JOINED)) {
socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => {
toast(player.name + " joined!", "success", false);
toast(player.name + " joined!", "success", false, true, 3);
stateBucket.currentGameState.people.push(player);
gameStateRenderer.renderLobbyPlayers();
if (
@@ -187,7 +194,7 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
killedPerson.out = true;
if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
toast(killedPerson.name + ' killed.', 'success', true, true, 6);
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo()
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED)
} else {
if (killedPerson.id === stateBucket.currentGameState.client.id) {
let clientUserType = document.getElementById("client-user-type");
@@ -195,9 +202,9 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80'
}
gameStateRenderer.updatePlayerCardToKilledState();
toast('You have been killed!', 'warning', false, true, 6);
toast('You have been killed!', 'warning', true, true, 6);
} else {
toast(killedPerson.name + ' was killed!', 'warning', false, true, 6);
toast(killedPerson.name + ' was killed!', 'warning', true, true, 6);
}
if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
@@ -218,12 +225,12 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
revealedPerson.alignment = revealData.alignment;
if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
toast(revealedPerson.name + ' revealed.', 'success', true, true, 6);
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo()
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(stateBucket.currentGameState.status === globals.STATUS.ENDED)
} else {
if (revealedPerson.id === stateBucket.currentGameState.client.id) {
toast('Your role has been revealed!', 'warning', false, true, 6);
toast('Your role has been revealed!', 'warning', true, true, 6);
} else {
toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', false, true, 6);
toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', true, true, 6);
}
if (stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
@@ -242,19 +249,27 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo
processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer);
});
}
if (!socket.hasListeners(globals.COMMANDS.END_GAME)) {
socket.on(globals.COMMANDS.END_GAME, (people) => {
stateBucket.currentGameState.people = people;
stateBucket.currentGameState.status = globals.STATUS.ENDED;
processGameState(stateBucket.currentGameState, stateBucket.currentGameState.client.cookie, socket, gameStateRenderer);
});
}
}
function displayStartGamePromptForModerators(gameState, socket) {
let div = document.createElement("div");
div.innerHTML = templates.START_GAME_PROMPT;
document.body.appendChild(div);
document.getElementById("start-game-button").addEventListener('click', (e) => {
div.querySelector('#start-game-button').addEventListener('click', (e) => {
e.preventDefault();
if (confirm("Start the game and deal roles?")) {
socket.emit(globals.COMMANDS.START_GAME, gameState.accessCode, gameState.client.cookie);
socket.emit(globals.COMMANDS.START_GAME, gameState.accessCode);
}
});
document.body.appendChild(div);
}
function runGameTimer (hours, minutes, tickRate, soundManager, timerWorker) {
@@ -269,7 +284,7 @@ function runGameTimer (hours, minutes, tickRate, soundManager, timerWorker) {
}
function validateName(name) {
return typeof name === 'string' && name.length <= 30;
return typeof name === 'string' && name.length > 0 && name.length <= 30;
}
function propagateNameChange(gameState, name, personId) {
@@ -297,7 +312,7 @@ function updateDOMWithNameChange(gameState, gameStateRenderer) {
gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false);
break;
case globals.USER_TYPES.MODERATOR:
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo();
gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo(gameState.status === globals.STATUS.ENDED);
break;
case globals.USER_TYPES.TEMPORARY_MODERATOR:
gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true);
@@ -306,3 +321,43 @@ function updateDOMWithNameChange(gameState, gameStateRenderer) {
break;
}
}
function activateRoleInfoButton(deck) {
deck.sort((a, b) => {
return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
})
document.getElementById("role-info-button").addEventListener("click", (e) => {
e.preventDefault();
document.getElementById("prompt").innerHTML = templates.ROLE_INFO_MODAL;
let modalContent = document.getElementById('game-role-info-container');
for (let card of deck) {
let roleDiv = document.createElement("div");
let roleNameDiv = document.createElement("div");
roleNameDiv.classList.add('role-info-name');
let roleName = document.createElement("h5");
let roleQuantity = document.createElement("h5");
let roleDescription = document.createElement("p");
roleDescription.innerText = card.description;
roleName.innerText = card.role;
roleQuantity.innerText = card.quantity + 'x';
if (card.team === globals.ALIGNMENT.GOOD) {
roleName.classList.add(globals.ALIGNMENT.GOOD);
} else {
roleName.classList.add(globals.ALIGNMENT.EVIL);
}
roleNameDiv .appendChild(roleQuantity);
roleNameDiv .appendChild(roleName);
roleDiv.appendChild(roleNameDiv);
roleDiv.appendChild(roleDescription);
modalContent.appendChild(roleDiv);
}
ModalManager.displayModal('role-info-modal', 'role-info-modal-background', 'close-role-info-modal-button');
});
}

View File

@@ -7,17 +7,19 @@ export const home = () => {
let userCode = document.getElementById("room-code").value;
if (roomCodeIsValid(userCode)) {
attemptToJoinGame(userCode);
} else {
toast('Invalid code. Codes are 6 numbers or letters.', 'error', true, true);
}
}
};
function roomCodeIsValid(code) {
return typeof code === "string" && /^[a-z0-9]{6}$/.test(code);
return typeof code === "string" && /^[a-z0-9]{6}$/.test(code.toLowerCase());
}
function attemptToJoinGame(code) {
XHRUtility.xhr(
'/api/games/availability/' + code,
'/api/games/availability/' + code.toLowerCase().trim(),
'GET',
null,
null

View File

@@ -54,6 +54,45 @@ h3 {
margin: 0.5em 0;
}
#footer {
bottom: 0;
width: 100%;
text-align: center;
align-items: center;
display: flex;
justify-content: center;
flex-wrap: wrap;
color: #d7d7d7;
font-size: 14px;
margin-top: 3em;
}
#footer a img {
width: 32px;
}
#footer a {
color: #f7f7f7;
text-decoration: none;
cursor: pointer;
font-family: 'diavlo', sans-serif;
}
#footer a:hover {
color: gray;
}
#footer div {
display: flex;
}
#footer > div, #footer > a {
margin: 0.5em;
}
#footer div:nth-child(2) > a, #footer div:nth-child(2) > p {
margin: 0 5px;
}
label {
color: #d7d7d7;
font-family: 'signika-negative', sans-serif;
@@ -68,11 +107,21 @@ input, textarea {
color: #f7f7f7;
}
a {
text-decoration: none;
}
textarea, input {
font-family: 'signika-negative', sans-serif;
font-size: 16px;
}
button {
display: flex;
align-items: center;
justify-content: center;
}
button, input[type="submit"] {
font-family: 'signika-negative', sans-serif !important;
padding: 10px;
@@ -119,7 +168,12 @@ input {
border-radius: 3px;
font-family: 'signika-negative', sans-serif;
font-weight: 100;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 25%);
box-shadow: 0 1px 1px rgba(0,0,0,0.11),
0 2px 2px rgba(0,0,0,0.11),
0 4px 4px rgba(0,0,0,0.11),
0 8px 8px rgba(0,0,0,0.11),
0 16px 16px rgba(0,0,0,0.11),
0 32px 32px rgba(0,0,0,0.11);
left: 0;
right: 0;
width: fit-content;
@@ -196,7 +250,7 @@ input {
width: 100%;
max-width: 30em;
height: 8em;
margin: 0 auto 0.5em auto;
margin: 0 auto 1em auto;
}
.animated-placeholder-invisible {
@@ -208,10 +262,11 @@ input {
display: flex;
margin: 0;
justify-content: center;
width: 100%;
}
.placeholder-row .animated-placeholder-short {
margin: 0 0 0.5em 0;
margin: 0 0 1em 0;
}
.good, .compact-card.good .card-role {
@@ -280,12 +335,17 @@ input {
@media(max-width: 550px) {
h1 {
font-size: 35px;
font-size: 30px;
}
#step-1 div {
font-size: 20px;
}
.info-message {
padding: 5px;
font-size: 16px;
}
}
@media(min-width: 551px) {
@@ -404,6 +464,37 @@ input {
transform: rotate(330deg);
animation-delay: 0s;
}
.bmc-button span:nth-child(1) {
font-size: 20px;
}
.bmc-button {
line-height: 35px !important;
height:40px !important;
text-decoration: none !important;
display:inline-flex !important;
align-items: center !important;
color:#ffffff !important;
background-color:#333243 !important;
border-radius: 5px !important;
border: 1px solid transparent !important;
padding: 7px 15px 7px 10px !important;
font-size: 15px !important;
box-shadow: 0px 1px 1px rgba(190, 190, 190, 0.5) !important;
-webkit-box-shadow: 0px 1px 2px 1px rgba(190, 190, 190, 0.5) !important;
font-family: sitewide-sans-serif, sans-serif !important;
-webkit-box-sizing: border-box !important;
box-sizing: border-box !important;
}
.bmc-button:hover, .bmc-button:active, .bmc-button:focus {
-webkit-box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;
text-decoration: none !important;box-shadow: 0px 1px 2px 2px rgba(190, 190, 190, 0.5) !important;
opacity: 0.85 !important;color:#ffffff !important;
}
@keyframes lds-spinner {
0% {
opacity: 1;

View File

@@ -10,7 +10,7 @@
border-radius: 3px;
user-select: none;
max-width: 15em;
min-width: 12em;
min-width: 9em;
display: flex;
height: max-content;
}
@@ -42,6 +42,7 @@
.card-role {
font-weight: bold;
pointer-events: none;
}
.compact-card-right p {
@@ -106,7 +107,7 @@
#step-4 > div {
display: flex;
flex-direction: column;
align-items: center;
align-items: flex-start;
text-align: left;
justify-content: center;
margin: 0 auto;
@@ -244,6 +245,7 @@ input[type="number"] {
#creation-step-tracker {
display: flex;
justify-content: center;
margin: 0 20px;
}
#step-forward-button, #step-back-button, #create-game {

View File

@@ -14,6 +14,10 @@
margin: 0.5em 0;
}
body {
margin-bottom: 100px;
}
#lobby-players {
overflow-y: auto;
max-height: 30em;
@@ -45,7 +49,11 @@
flex-wrap: wrap;
display: flex;
width: 95%;
margin: 0 auto 115px auto;
margin: 1em auto 0 auto;
}
#game-state-container h2 {
margin: 0.5em 0;
}
#lobby-header {
@@ -61,6 +69,28 @@ h1 {
margin: 1em;
}
#game-content .placeholder-row:nth-child(1) {
margin-top: 2em;
}
#footer.game-page-footer {
margin-top: 1em;
margin-bottom: 1em;
}
#footer.game-page-footer a {
margin: 0 5px;
}
#end-of-game-header {
display: flex;
flex-wrap: wrap;
align-items: center;
}
#end-of-game-header button {
margin: 0.5em;
}
.potential-moderator {
display: flex;
color: #d7d7d7;
@@ -96,17 +126,83 @@ h1 {
margin-top: 10px;
padding: 7px;
border-radius: 3px;
background-color: #722c2c;
background-color: #121314;
border: 2px solid #333243;
color: whitesmoke;
align-items: center;
display: flex;
transition: background-color 0.2s;
}
#game-player-count {
color: whitesmoke;
.role-info-name {
display: flex;
}
.role-info-name h5:nth-child(1) {
margin-right: 10px;
color: #21ba45;
}
#role-info-button {
margin-top: 1em;
}
#role-info-button img {
height: 25px;
margin-left: 10px;
}
#game-role-info-container {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 1em 0;
overflow-y: auto;
max-height: 35em;
}
#game-role-info-container > div {
width: 95%;
}
#transfer-mod-modal-content {
overflow-y: auto;
max-height: 35em;
width: 100%;
}
.potential-moderator {
width: 90%;
}
#role-info-modal #modal-button-container {
margin-top: 1em;
}
#game-role-info-container .role-info-name {
padding: 5px;
border-radius: 3px;
font-size: 20px;
font-family: signika-negative, sans-serif;
margin: 0.5em 0;
font-size: 25px;
background-color: #15191c;
}
#role-info-modal h2 {
color: #d7d7d7;
font-family: diavlo, sans-serif;
font-weight: normal;
}
#game-role-info-container p, #game-role-info-container h5 {
text-align: left;
font-weight: normal;
}
#game-role-info-container p {
color: #d7d7d7;
font-size: 14px;
font-family: signika-negative, sans-serif;
}
#game-role {
@@ -183,6 +279,12 @@ h1 {
border: 1px solid #747474;
}
#game-timer.low {
color: #e71c0d;
border: 1px solid #ca1b17;
background-color: #361a1a;
}
#role-name {
position: absolute;
top: 6%;
@@ -215,7 +317,7 @@ h1 {
bottom: 8%;
left: 50%;
transform: translate(-50%, 0);
font-size: 16px;
font-size: 15px;
width: 78%;
max-height: 6em;
}
@@ -279,7 +381,7 @@ label[for='moderator'] {
border-radius: 3px;
font-family: 'signika-negative', sans-serif;
font-weight: 100;
box-shadow: 0 2px 4px 0 rgb(0 0 0 / 25%);
box-shadow: 0 -2px 6px 0 rgb(0 0 0 / 45%);
left: 0;
right: 0;
bottom: 0;
@@ -355,11 +457,15 @@ label[for='moderator'] {
animation: pulse 0.75s linear infinite alternate;
}
.paused-low {
animation: pulse-low 0.75s linear infinite alternate;
}
#game-header {
display: flex;
flex-wrap: wrap;
align-items: center;
flex-direction: column;
align-items: flex-start;
}
.timer-container-moderator {
@@ -380,12 +486,7 @@ label[for='moderator'] {
justify-content: space-between;
margin: 0.5em 0;
position: relative;
box-shadow: 0 1px 1px rgba(0,0,0,0.11),
0 2px 2px rgba(0,0,0,0.11),
0 4px 4px rgba(0,0,0,0.11),
0 8px 8px rgba(0,0,0,0.11),
0 16px 16px rgba(0,0,0,0.11),
0 32px 32px rgba(0,0,0,0.11);
box-shadow: 2px 3px 6px rgb(0 0 0 / 50%);
}
.game-player-name {
@@ -488,14 +589,30 @@ label[for='moderator'] {
#game-player-list {
overflow-y: auto;
overflow-x: hidden;
padding: 0 10px;
padding: 0;
max-height: 37em;
}
#game-player-list > div {
padding: 2px 10px;
border-radius: 3px;
margin-bottom: 1em;
margin-bottom: 0.5em;
}
#game-parameters {
color: #d7d7d7;
font-size: 25px;
margin: 0.5em;
}
#game-parameters > div {
display: flex;
align-items: center;
}
#game-parameters img {
height: 20px;
margin-right: 10px;
}
#players-alive-label {
@@ -512,6 +629,10 @@ label[for='moderator'] {
justify-content: center;
}
#transfer-mod-modal #modal-button-container {
justify-content: center;
}
#change-name-modal-background {
cursor: default;
}
@@ -520,6 +641,88 @@ label[for='moderator'] {
background-color: #333243;
padding: 10px 10px 0 10px;
border-radius: 3px;
min-height: 25em;
min-width: 15em;
max-width: 30em;
}
#transfer-mod-modal-content {
margin-bottom: 2em;
}
#game-state-container.vertical-flex {
flex-direction: column;
align-items: center;
}
@media(max-width: 500px) {
#client-name {
font-size: 25px;
}
#client-user-type, #game-parameters {
font-size: 20px;
}
#game-state-container {
margin: 0 auto 0 auto;
}
button {
font-size: 16px;
padding: 5px;
}
#play-pause img {
width: 45px;
}
.make-mod-button {
font-size: 16px;
padding: 5px;
}
.game-player-name {
font-size: 16px;
}
#game-timer {
font-size: 30px;
}
#players-alive-label {
font-size: 20px;
}
#start-game-prompt, #end-game-prompt {
height: 65px;
}
#start-game-button, #end-game-button {
font-size: 20px;
padding: 5px;
}
#game-role, #game-role-back {
height: 20em;
max-width: 15em;
}
#client-container {
margin: 0;
}
#game-role-back p {
font-size: 16px;
}
#game-role-back h4 {
font-size: 20px;
}
h2 {
font-size: 18px;
}
}
@keyframes pulse {
@@ -530,6 +733,14 @@ label[for='moderator'] {
}
}
@keyframes pulse-low {
from {
color: rgba(231, 28, 13 , 0.1);
} to {
color: rgba(231, 28, 13, 1);
}
}
@keyframes fade-in-slide-up {
0% {
opacity: 0;

View File

@@ -5,7 +5,6 @@ html {
body {
align-items: center;
justify-content: center;
height: 100%;
}
button {
@@ -31,7 +30,18 @@ a button {
#join-button {
min-width: 6em;
max-height: 3em;
color: #21ba45;
background-color: #1c8a36;
color: whitesmoke;
font-size: 16px;
}
#join-button:hover {
background-color: #326243;
border: 2px solid #1c8a36;
}
#join-form div:nth-child(1) {
margin-right: 1em;
}
h3 {
@@ -42,11 +52,11 @@ h3 {
font-family: 'diavlo', sans-serif;
}
img {
img[src='../images/logo_cropped.gif'] {
max-width: 400px;
width: 63vw;
min-width: 250px;
margin: 1em 0;
margin: 3em 0 1em 0;
}
form > div {

View File

@@ -2,7 +2,7 @@
border-radius: 2px;
text-align: center;
position: fixed;
width: 100%;
width: 85%;
z-index: 100;
top: 50%;
left: 50%;
@@ -10,7 +10,7 @@
background-color: #23282b;
align-items: center;
justify-content: center;
max-width: 19em;
max-width: 25em;
max-height: 80%;
height: fit-content;
font-family: sans-serif;

View File

@@ -43,7 +43,32 @@
<img src="/images/Werewolf_Small.png"/>
<a href="/">Home</a>
</div>
<div id="game-content" class="container"></div>
<div id="game-content" class="container">
<div class="placeholder-row">
<div class="animated-placeholder animated-placeholder-short"></div>
<div class="animated-placeholder animated-placeholder-short animated-placeholder-invisible"></div>
</div>
<div class="placeholder-row">
<div class="animated-placeholder animated-placeholder-short"></div>
<div class="animated-placeholder animated-placeholder-short animated-placeholder-invisible"></div>
</div>
<div class="animated-placeholder animated-placeholder-long"></div>
<div class="animated-placeholder animated-placeholder-long"></div>
<div class="placeholder-row">
<div class="animated-placeholder animated-placeholder-short"></div>
<div class="animated-placeholder animated-placeholder-short animated-placeholder-invisible"></div>
</div>
<div class="animated-placeholder animated-placeholder-long"></div>
</div>
<footer id="footer" class="game-page-footer">
<div>
<a href="https://github.com/AlecM33/Werewolf"><img src='/images/GitHub-Mark-Light-32px.png'/></a>
<a href="mailto:leohfx@gmail.com?Subject=Werewolf App Question" target="_top"><img src='/images/email.svg'/></a>
</div>
<div>
<p>Werewolf created by Andrew Plotkin</p>
</div>
</footer>
<script type="module">
import { game } from "../scripts/game.js";
game();

View File

@@ -35,6 +35,19 @@
<input id="join-button" type="submit" value="Join"/>
</form>
</div>
<footer id="footer">
<a class="bmc-button" target="_blank" href="https://www.buymeacoffee.com/alecm33">
<span alt="Buy me a beer">🍺</span>
<span style="margin-left:5px;">Buy me a beer</span>
</a>
<div>
<a href="https://github.com/AlecM33/Werewolf"><img src='/images/GitHub-Mark-Light-32px.png'/></a>
<a href="mailto:leohfx@gmail.com?Subject=Werewolf App Question" target="_top"><img src='/images/email.svg'/></a>
</div>
<div>
<p>Werewolf created by Andrew Plotkin</p>
</div>
</footer>
<script type="module">
import { home } from "./scripts/home.js";
home();

View File

@@ -12,7 +12,8 @@ const globals = {
KILL_PLAYER: 'killPlayer',
REVEAL_PLAYER: 'revealPlayer',
TRANSFER_MODERATOR: 'transferModerator',
CHANGE_NAME: 'changeName'
CHANGE_NAME: 'changeName',
END_GAME: 'endGame'
},
MESSAGES: {
ENTER_NAME: "Client must enter name."

View File

@@ -49,7 +49,7 @@ class ActiveGameRunner {
});
gameProcess.on('exit', () => {
this.logger.debug('Game ' + game.accessCode + ' has ended.');
this.logger.debug('Game ' + game.accessCode + ' timer has expired.');
delete this.timerThreads[game.accessCode];
});
gameProcess.send({

View File

@@ -33,7 +33,7 @@ class GameManager {
ackFn(this.environment);
});
socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode, personId) => {
socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode) => {
let game = this.activeGameRunner.activeGames[accessCode];
if (game) {
game.status = globals.STATUS.IN_PROGRESS;
@@ -158,6 +158,17 @@ class GameManager {
}
}
});
socket.on(globals.CLIENT_COMMANDS.END_GAME, (accessCode) => {
let game = this.activeGameRunner.activeGames[accessCode];
if (game) {
game.status = globals.STATUS.ENDED;
for (let person of game.people) {
person.revealed = true;
}
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people));
}
})
}

View File

@@ -7,6 +7,23 @@ const globals = require("../config/globals")
const GameStateCurator = {
getGameStateFromPerspectiveOfPerson: (game, person, gameRunner, socket, logger) => {
return getGameStateBasedOnPermissions(game, person, gameRunner);
},
mapPeopleForModerator: (people) => {
return people
.filter((person) => {
return person.assigned === true
})
.map((person) => ({
name: person.name,
id: person.id,
userType: person.userType,
gameRole: person.gameRole,
gameRoleDescription: person.gameRoleDescription,
alignment: person.alignment,
out: person.out,
revealed: person.revealed
}));
}
}
@@ -27,7 +44,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
switch (person.userType) {
case globals.USER_TYPES.PLAYER:
case globals.USER_TYPES.KILLED_PLAYER:
return {
let state = {
accessCode: game.accessCode,
status: game.status,
moderator: mapPerson(game.moderator),
@@ -37,10 +54,16 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
.filter((person) => {
return person.assigned === true
})
.map((filteredPerson) => mapPerson(filteredPerson)),
.map((filteredPerson) =>
mapPerson(filteredPerson)
),
timerParams: game.timerParams,
isFull: game.isFull,
}
if (game.status === globals.STATUS.ENDED) {
state.people = GameStateCurator.mapPeopleForModerator(game.people);
}
return state;
case globals.USER_TYPES.MODERATOR:
return {
accessCode: game.accessCode,
@@ -48,7 +71,7 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
moderator: mapPerson(game.moderator),
client: client,
deck: game.deck,
people: mapPeopleForModerator(game.people, client),
people: GameStateCurator.mapPeopleForModerator(game.people, client),
timerParams: game.timerParams,
isFull: game.isFull,
spectators: game.spectators
@@ -88,23 +111,6 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
}
}
function mapPeopleForModerator(people) {
return people
.filter((person) => {
return person.assigned === true
})
.map((person) => ({
name: person.name,
id: person.id,
userType: person.userType,
gameRole: person.gameRole,
gameRoleDescription: person.gameRoleDescription,
alignment: person.alignment,
out: person.out,
revealed: person.revealed
}));
}
function mapPerson(person) {
if (person.revealed) {
return {