mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-27 00:07:50 +01:00
join purposefully as spectator, various improvements
This commit is contained in:
@@ -56,7 +56,7 @@ export const globals = {
|
||||
USER_TYPES: {
|
||||
MODERATOR: 'moderator',
|
||||
PLAYER: 'player',
|
||||
TEMPORARY_MODERATOR: 'player / temp mod',
|
||||
TEMPORARY_MODERATOR: 'temp mod',
|
||||
KILLED_PLAYER: 'killed',
|
||||
SPECTATOR: 'spectator'
|
||||
},
|
||||
@@ -67,7 +67,7 @@ export const globals = {
|
||||
USER_TYPE_ICONS: {
|
||||
player: ' \uD83C\uDFAE',
|
||||
moderator: ' \uD83D\uDC51',
|
||||
'player / temp mod': ' \uD83C\uDFAE\uD83D\uDC51',
|
||||
'temp mod': ' \uD83C\uDFAE\uD83D\uDC51',
|
||||
spectator: ' \uD83D\uDC7B',
|
||||
killed: '\uD83D\uDC80'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { toast } from './Toast.js';
|
||||
|
||||
export const Confirmation = (message, onYes = null) => {
|
||||
export const Confirmation = (message, onYes = null, innerHTML = false) => {
|
||||
document.querySelector('#confirmation')?.remove();
|
||||
document.querySelector('#confirmation-background')?.remove();
|
||||
|
||||
@@ -17,7 +17,11 @@ export const Confirmation = (message, onYes = null) => {
|
||||
<button id="confirmation-yes-button" class="app-button">OK</button>
|
||||
</div>`;
|
||||
|
||||
confirmation.querySelector('#confirmation-message').innerText = message;
|
||||
if (innerHTML) {
|
||||
confirmation.querySelector('#confirmation-message').innerHTML = message;
|
||||
} else {
|
||||
confirmation.querySelector('#confirmation-message').innerText = message;
|
||||
}
|
||||
|
||||
let background = document.createElement('div');
|
||||
background.setAttribute('id', 'confirmation-background');
|
||||
|
||||
@@ -69,6 +69,7 @@ export const HTMLFragments = {
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
<label id='players-alive-label'></label>
|
||||
<div id="spectator-count"></div>
|
||||
<div id='game-player-list'></div>
|
||||
</div>`,
|
||||
SPECTATOR_GAME_VIEW:
|
||||
@@ -83,6 +84,7 @@ export const HTMLFragments = {
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
<label id='players-alive-label'></label>
|
||||
<div id="spectator-count"></div>
|
||||
<div id='game-player-list'></div>
|
||||
</div>`,
|
||||
TRANSFER_MOD_MODAL:
|
||||
@@ -147,6 +149,7 @@ export const HTMLFragments = {
|
||||
</div>
|
||||
<div id='game-people-container'>
|
||||
<label id='players-alive-label'></label>
|
||||
<div id="spectator-count"></div>
|
||||
<div id='game-player-list'></div>
|
||||
</div>
|
||||
</div>`,
|
||||
|
||||
@@ -44,8 +44,7 @@ function getNavbarLinks (page = null, device) {
|
||||
'<a class="' + linkClass + '" href="/create">Create</a>' +
|
||||
'<a class="' + linkClass + '" href="/how-to-use">How to Use</a>' +
|
||||
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Feedback</a>' +
|
||||
'<a class="' + linkClass + ' "href="https://github.com/alecm33/Werewolf" target="_top">Github</a>' +
|
||||
'<a class="' + linkClass + '" href="https://www.buymeacoffee.com/alecm33">Support the App</a>';
|
||||
'<a class="' + linkClass + ' "href="https://github.com/alecm33/Werewolf" target="_top">Github</a>';
|
||||
}
|
||||
|
||||
function attachHamburgerListener () {
|
||||
|
||||
@@ -153,9 +153,12 @@ export class GameCreationStepManager {
|
||||
}
|
||||
}).catch((e) => {
|
||||
restoreButton();
|
||||
toast(e.content, 'error', true, true, 'medium');
|
||||
if (e.status === 429) {
|
||||
toast('You\'ve sent this request too many times.', 'error', true, true, 'medium');
|
||||
} else if (e.status === 413) {
|
||||
toast('Your request is too large.', 'error', true, true);
|
||||
} else {
|
||||
toast(e.content, 'error', true, true, 'medium');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,6 +56,19 @@ export class InProgress {
|
||||
document.querySelector('#timer-container-moderator')?.remove();
|
||||
document.querySelector('label[for="game-timer"]')?.remove();
|
||||
}
|
||||
|
||||
const spectatorCount = this.container.querySelector('#spectator-count');
|
||||
|
||||
if (spectatorCount) {
|
||||
spectatorCount?.addEventListener('click', () => {
|
||||
Confirmation(SharedStateUtil.buildSpectatorList(this.stateBucket.currentGameState.spectators), null, true);
|
||||
});
|
||||
|
||||
SharedStateUtil.setNumberOfSpectators(
|
||||
this.stateBucket.currentGameState.spectators.length,
|
||||
spectatorCount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderPlayerView (isKilled = false) {
|
||||
@@ -142,6 +155,7 @@ export class InProgress {
|
||||
const killedPerson = this.stateBucket.currentGameState.people.find((person) => person.id === id);
|
||||
if (killedPerson) {
|
||||
killedPerson.out = true;
|
||||
killedPerson.userType = globals.USER_TYPES.KILLED_PLAYER;
|
||||
if (this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
|
||||
toast(killedPerson.name + ' killed.', 'success', true, true, 'medium');
|
||||
this.renderPlayersWithRoleAndAlignmentInfo(this.stateBucket.currentGameState.status === globals.STATUS.ENDED);
|
||||
@@ -459,7 +473,9 @@ function renderPotentialMods (gameState, group, transferModHandlers, socket) {
|
||||
container.classList.add('potential-moderator');
|
||||
container.setAttribute('tabindex', '0');
|
||||
container.dataset.pointer = member.id;
|
||||
container.innerText = member.name;
|
||||
container.innerHTML =
|
||||
'<div class=\'potential-mod-name\'>' + member.name + '</div>' +
|
||||
'<div>' + member.userType + ' ' + globals.USER_TYPE_ICONS[member.userType] + ' </div>';
|
||||
transferModHandlers[member.id] = (e) => {
|
||||
if (e.type === 'click' || e.code === 'Enter') {
|
||||
ModalManager.dispelModal('transfer-mod-modal', 'transfer-mod-modal-background');
|
||||
|
||||
@@ -3,6 +3,7 @@ import { toast } from '../../front_end_components/Toast.js';
|
||||
import { globals } from '../../../config/globals.js';
|
||||
import { HTMLFragments } from '../../front_end_components/HTMLFragments.js';
|
||||
import { Confirmation } from '../../front_end_components/Confirmation.js';
|
||||
import { SharedStateUtil } from './shared/SharedStateUtil.js';
|
||||
|
||||
export class Lobby {
|
||||
constructor (containerId, stateBucket, socket) {
|
||||
@@ -46,10 +47,14 @@ export class Lobby {
|
||||
const playerCount = this.container.querySelector('#game-player-count');
|
||||
playerCount.innerText = this.stateBucket.currentGameState.gameSize + ' Players';
|
||||
|
||||
setNumberOfSpectators(
|
||||
this.container.querySelector('#spectator-count').addEventListener('click', () => {
|
||||
Confirmation(SharedStateUtil.buildSpectatorList(this.stateBucket.currentGameState.spectators), null, true);
|
||||
});
|
||||
|
||||
SharedStateUtil.setNumberOfSpectators(
|
||||
this.stateBucket.currentGameState.spectators.length,
|
||||
this.container.querySelector('#spectator-count')
|
||||
)
|
||||
);
|
||||
|
||||
const gameCode = this.container.querySelector('#game-code');
|
||||
gameCode.innerHTML = 'Or enter this code on the homepage: <span>' +
|
||||
@@ -96,7 +101,7 @@ export class Lobby {
|
||||
|
||||
this.socket.on(globals.EVENT_IDS.NEW_SPECTATOR, (spectator) => {
|
||||
this.stateBucket.currentGameState.spectators.push(spectator);
|
||||
setNumberOfSpectators(
|
||||
SharedStateUtil.setNumberOfSpectators(
|
||||
this.stateBucket.currentGameState.spectators.length,
|
||||
document.getElementById('spectator-count')
|
||||
);
|
||||
@@ -136,12 +141,6 @@ export class Lobby {
|
||||
}
|
||||
}
|
||||
|
||||
function setNumberOfSpectators(number, el) {
|
||||
el.innerText = '+ ' + (number === 1
|
||||
? number + ' Spectator'
|
||||
: number + ' Spectators');
|
||||
}
|
||||
|
||||
function enableOrDisableStartButton (gameState, buttonContainer, handler) {
|
||||
if (gameState.isFull) {
|
||||
buttonContainer.querySelector('#start-game-button').addEventListener('click', handler);
|
||||
@@ -189,6 +188,7 @@ function getTimeString (gameState) {
|
||||
function renderLobbyPerson (name, userType) {
|
||||
const el = document.createElement('div');
|
||||
const personNameEl = document.createElement('div');
|
||||
personNameEl.classList.add('lobby-player-name');
|
||||
const personTypeEl = document.createElement('div');
|
||||
personNameEl.innerText = name;
|
||||
personTypeEl.innerText = userType + globals.USER_TYPE_ICONS[userType];
|
||||
|
||||
@@ -130,6 +130,27 @@ export const SharedStateUtil = {
|
||||
} else {
|
||||
window.location = '/not-found?reason=' + encodeURIComponent('invalid-access-code');
|
||||
}
|
||||
},
|
||||
|
||||
buildSpectatorList (spectators) {
|
||||
if (spectators.length === 0) {
|
||||
return '<div>Nobody currently spectating.</div>';
|
||||
}
|
||||
let html = '';
|
||||
for (const spectator of spectators) {
|
||||
html += `<div class='spectator'>
|
||||
<div class='spectator-name'>` + spectator.name + '</div>' +
|
||||
'<div>' + 'spectator' + globals.USER_TYPE_ICONS.spectator + `</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
setNumberOfSpectators: (number, el) => {
|
||||
el.innerText = '+ ' + (number === 1
|
||||
? number + ' Spectator'
|
||||
: number + ' Spectators');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,45 +27,63 @@ const joinHandler = (e) => {
|
||||
e.preventDefault();
|
||||
const name = document.getElementById('player-new-name').value;
|
||||
if (validateName(name)) {
|
||||
document.getElementById('join-game-form').onsubmit = null;
|
||||
document.getElementById('submit-new-name').classList.add('submitted');
|
||||
document.getElementById('submit-new-name').setAttribute('value', 'Joining...');
|
||||
XHRUtility.xhr(
|
||||
'/api/games/' + accessCode + '/players',
|
||||
'PATCH',
|
||||
null,
|
||||
JSON.stringify({
|
||||
playerName: name,
|
||||
accessCode: accessCode,
|
||||
sessionCookie: UserUtility.validateAnonUserSignature(globals.ENVIRONMENT.LOCAL),
|
||||
localCookie: UserUtility.validateAnonUserSignature(globals.ENVIRONMENT.PRODUCTION)
|
||||
})
|
||||
)
|
||||
sendJoinRequest(e, name, accessCode)
|
||||
.then((res) => {
|
||||
const json = JSON.parse(res.content);
|
||||
UserUtility.setAnonymousUserId(json.cookie, json.environment);
|
||||
window.location = '/game/' + accessCode;
|
||||
}).catch((res) => {
|
||||
document.getElementById('join-game-form').onsubmit = joinHandler;
|
||||
document.getElementById('submit-new-name').classList.remove('submitted');
|
||||
document.getElementById('submit-new-name').setAttribute('value', 'Join Game');
|
||||
if (res.status === 404) {
|
||||
toast('This game was not found.', 'error', true, true, 'long');
|
||||
} else if (res.status === 400) {
|
||||
toast('This name is already taken.', 'error', true, true, 'long');
|
||||
} else if (res.status >= 500) {
|
||||
toast(
|
||||
'The server is experiencing problems. Please try again later',
|
||||
'error',
|
||||
true
|
||||
);
|
||||
}
|
||||
handleJoinError(e, res, joinHandler);
|
||||
});
|
||||
} else {
|
||||
toast('Name must be between 1 and 30 characters.', 'error', true, true, 'long');
|
||||
}
|
||||
};
|
||||
|
||||
function sendJoinRequest (e, name, accessCode) {
|
||||
document.getElementById('join-game-form').onsubmit = null;
|
||||
if (e.submitter.getAttribute('id') === 'submit-join-as-player') {
|
||||
document.getElementById('submit-join-as-player').classList.add('submitted');
|
||||
document.getElementById('submit-join-as-player').setAttribute('value', '...');
|
||||
} else {
|
||||
document.getElementById('submit-join-as-spectator').classList.add('submitted');
|
||||
document.getElementById('submit-join-as-spectator').setAttribute('value', '...');
|
||||
}
|
||||
return XHRUtility.xhr(
|
||||
'/api/games/' + accessCode + '/players',
|
||||
'PATCH',
|
||||
null,
|
||||
JSON.stringify({
|
||||
playerName: name,
|
||||
accessCode: accessCode,
|
||||
sessionCookie: UserUtility.validateAnonUserSignature(globals.ENVIRONMENT.LOCAL),
|
||||
localCookie: UserUtility.validateAnonUserSignature(globals.ENVIRONMENT.PRODUCTION),
|
||||
joinAsSpectator: e.submitter.getAttribute('id') === 'submit-join-as-spectator'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function handleJoinError (e, res, joinHandler) {
|
||||
document.getElementById('join-game-form').onsubmit = joinHandler;
|
||||
e.submitter.classList.remove('submitted');
|
||||
if (e.submitter.getAttribute('id') === 'submit-join-as-player') {
|
||||
e.submitter.setAttribute('value', 'Join');
|
||||
} else {
|
||||
e.submitter.setAttribute('value', 'Spectate');
|
||||
}
|
||||
if (res.status === 404) {
|
||||
toast('This game was not found.', 'error', true, true, 'long');
|
||||
} else if (res.status === 400) {
|
||||
toast(res.content, 'error', true, true, 'long');
|
||||
} else if (res.status >= 500) {
|
||||
toast(
|
||||
'The server is experiencing problems. Please try again later',
|
||||
'error',
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function validateName (name) {
|
||||
return typeof name === 'string' && name.length > 0 && name.length <= 30;
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ input {
|
||||
}
|
||||
|
||||
.good, .compact-card.good .card-role {
|
||||
color: #4b6bfa;
|
||||
color: #5f7cfb;
|
||||
}
|
||||
|
||||
.good-players, #deck-good {
|
||||
@@ -786,7 +786,7 @@ input {
|
||||
display:inline-flex !important;
|
||||
align-items: center !important;
|
||||
color:#ffffff !important;
|
||||
background-color:#333243 !important;
|
||||
background-color:#2d2c3a !important;
|
||||
border-radius: 5px !important;
|
||||
border: 1px solid transparent !important;
|
||||
padding: 7px 15px 7px 10px !important;
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
border: 2px solid #333243;
|
||||
width: 85%;
|
||||
z-index: 100001;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #191920;
|
||||
background-color: #292834;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 25em;
|
||||
@@ -32,6 +31,12 @@
|
||||
font-size: 20px;
|
||||
color: #e7e7e7;
|
||||
margin: 1em 0 2em 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
max-height: 20em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.confirmation-buttons button, .confirmation-buttons-centered button {
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: #0f0f10;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 16px;
|
||||
@@ -159,7 +159,7 @@
|
||||
background-color: #191920;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
|
||||
#deck-count {
|
||||
font-size: 30px;
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
width: fit-content;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
@@ -236,7 +236,7 @@
|
||||
z-index: 25;
|
||||
top: 38px;
|
||||
right: 29px;
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
border-radius: 3px;
|
||||
box-shadow: -3px -3px 6px rgb(0 0 0 / 60%);
|
||||
}
|
||||
@@ -245,7 +245,7 @@
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -362,7 +362,7 @@ select {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background-color: #191920;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ input[type="number"] {
|
||||
max-width: 20em;
|
||||
margin: 0.5em;
|
||||
cursor: pointer;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
border-radius: 3px;
|
||||
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
@@ -613,7 +613,7 @@ input[type="number"] {
|
||||
|
||||
.review-option {
|
||||
background-color: #191920;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
color: #e7e7e7;
|
||||
padding: 10px;
|
||||
font-size: 18px;
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
.lobby-player, #moderator {
|
||||
.lobby-player, #moderator, .spectator, .potential-moderator {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: black;
|
||||
background-color: #171522;
|
||||
color: #e7e7e7;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 17px;
|
||||
width: fit-content;
|
||||
min-width: 15em;
|
||||
width: 17em;
|
||||
border: 2px solid transparent;
|
||||
margin: 0.25em 0;
|
||||
box-shadow: 2px 2px 5px rgb(0 0 0 / 40%);
|
||||
margin: 0 auto 0.25em auto;
|
||||
}
|
||||
|
||||
.potential-moderator {
|
||||
margin: 0.5em auto;
|
||||
}
|
||||
|
||||
#lobby-players {
|
||||
@@ -29,13 +31,17 @@
|
||||
|
||||
#spectator-count {
|
||||
color: #b1afcd;
|
||||
text-decoration: underline;
|
||||
font-size: 17px;
|
||||
margin: 5px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lobby-player-client {
|
||||
border: 2px solid #21ba45;
|
||||
}
|
||||
|
||||
.lobby-player div:nth-child(2) {
|
||||
.lobby-player div:nth-child(2), .spectator div:nth-child(2), .potential-moderator div:nth-child(2) {
|
||||
color: #21ba45;
|
||||
}
|
||||
|
||||
@@ -122,7 +128,7 @@ h1 {
|
||||
}
|
||||
|
||||
#end-of-game-header h2 {
|
||||
border: 1px solid #333243;
|
||||
border: 1px solid #2d2c3a;
|
||||
border-radius: 5px;
|
||||
background-color: #1a1726;
|
||||
padding: 7px;
|
||||
@@ -141,19 +147,6 @@ h1 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.potential-moderator {
|
||||
display: flex;
|
||||
color: #d7d7d7;
|
||||
background-color: black;
|
||||
border: 2px solid transparent;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
justify-content: space-between;
|
||||
margin: 0.5em 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.potential-moderator:hover {
|
||||
border: 2px solid #d7d7d7;
|
||||
cursor: pointer;
|
||||
@@ -170,7 +163,7 @@ h1 {
|
||||
padding: 7px;
|
||||
border-radius: 3px;
|
||||
background-color: #121314;
|
||||
border: 2px solid #333243;
|
||||
border: 2px solid #2d2c3a;
|
||||
color: #e7e7e7;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -243,7 +236,7 @@ h1 {
|
||||
font-size: 20px;
|
||||
font-family: signika-negative, sans-serif;
|
||||
margin: 0.5em 0;
|
||||
background-color: black;
|
||||
background-color: #171522;
|
||||
}
|
||||
|
||||
#role-info-modal h2 {
|
||||
@@ -423,7 +416,7 @@ h1 {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
border-radius: 3px;
|
||||
min-width: 15em;
|
||||
}
|
||||
@@ -431,7 +424,7 @@ h1 {
|
||||
#client-name {
|
||||
color: #e7e7e7;
|
||||
font-family: 'signika-negative', sans-serif;
|
||||
font-size: 30px;
|
||||
font-size: 25px;
|
||||
margin: 0.25em 2em 0.25em 0;
|
||||
}
|
||||
|
||||
@@ -439,7 +432,7 @@ h1 {
|
||||
color: #21ba45;
|
||||
font-family: 'signika-negative', sans-serif;
|
||||
font-size: 25px;
|
||||
background-color: black;
|
||||
background-color: #171522;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
padding: 0 5px;
|
||||
@@ -608,7 +601,7 @@ label[for='moderator'] {
|
||||
margin-bottom: 1em;
|
||||
padding: 0.5em;
|
||||
border-radius: 3px;
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
}
|
||||
|
||||
canvas {
|
||||
@@ -620,13 +613,12 @@ canvas {
|
||||
border-left: 3px solid #21ba45;
|
||||
display: flex;
|
||||
color: #d7d7d7;
|
||||
background-color: black;
|
||||
background-color: #171522;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
justify-content: space-between;
|
||||
margin: 0.25em 0;
|
||||
position: relative;
|
||||
box-shadow: 2px 3px 6px rgb(0 0 0 / 50%);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
@@ -634,18 +626,20 @@ canvas {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.game-player-name {
|
||||
.game-player-name, .lobby-player-name, .spectator-name, .potential-mod-name {
|
||||
position: relative;
|
||||
min-width: 6em;
|
||||
max-width: 10em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.kill-player-button, .reveal-role-button {
|
||||
background-color: #333243;
|
||||
background-color: #434156;
|
||||
font-family: 'signika-negative', sans-serif !important;
|
||||
border-radius: 3px;
|
||||
color: #e7e7e7;
|
||||
@@ -750,7 +744,7 @@ canvas {
|
||||
}
|
||||
|
||||
#game-parameters {
|
||||
background-color: #333243;
|
||||
background-color: #2d2c3a;
|
||||
border-radius: 3px;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
@@ -765,12 +759,11 @@ canvas {
|
||||
#game-player-list > div {
|
||||
padding: 2px 10px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 0.5em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
#players-alive-label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
@@ -795,10 +788,10 @@ canvas {
|
||||
}
|
||||
|
||||
#lobby-people-container , #game-people-container {
|
||||
background-color: #333243;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
min-height: 25em;
|
||||
background-color: #292834;
|
||||
max-width: 35em;
|
||||
min-width: 17em;
|
||||
margin-top: 1em;
|
||||
|
||||
@@ -6,6 +6,25 @@
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
#join-game-form .modal-button-container {
|
||||
justify-content: flex-end;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
#join-game-form .modal-button-container input {
|
||||
width: 5em;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
#join-game-form .modal-button-container #submit-join-as-spectator {
|
||||
background-color: #045EA6;
|
||||
}
|
||||
|
||||
#join-game-form .modal-button-container #submit-join-as-spectator:hover {
|
||||
background-color: #0078D773;
|
||||
border: 2px solid #045EA6;
|
||||
}
|
||||
|
||||
#player-new-name {
|
||||
max-width: 17em;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: #191920;
|
||||
background-color: #292834;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 25em;
|
||||
@@ -15,7 +15,6 @@
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
display: none;
|
||||
border: 2px solid #333243;
|
||||
}
|
||||
|
||||
.modal-background {
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
<input id="player-new-name" autocomplete="given-name" type="text"/>
|
||||
</div>
|
||||
<div class="modal-button-container">
|
||||
<input type="submit" id="submit-new-name" value="Join Game"/>
|
||||
<input type="submit" id="submit-join-as-spectator" value="Spectate"/>
|
||||
<input type="submit" id="submit-join-as-player" value="Join"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
2
index.js
2
index.js
@@ -4,7 +4,7 @@ const express = require('express');
|
||||
const app = express();
|
||||
const ServerBootstrapper = require('./server/modules/ServerBootstrapper');
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.json({ limit: '10kb' }));
|
||||
|
||||
const args = ServerBootstrapper.processCLIArgs();
|
||||
|
||||
|
||||
@@ -74,16 +74,17 @@ router.patch('/:code/players', function (req, res) {
|
||||
|| !validateName(req.body.playerName)
|
||||
|| !validateCookie(req.body.localCookie)
|
||||
|| !validateCookie(req.body.sessionCookie)
|
||||
|| !validateSpectatorFlag(req.body.joinAsSpectator)
|
||||
) {
|
||||
res.status(400).send();
|
||||
} else {
|
||||
const game = gameManager.activeGameRunner.activeGames.get(req.body.accessCode);
|
||||
if (game) {
|
||||
const inUseCookie = gameManager.environment === globals.ENVIRONMENT.PRODUCTION ? req.body.localCookie : req.body.sessionCookie;
|
||||
gameManager.joinGame(game, req.body.playerName, inUseCookie).then((data) => {
|
||||
gameManager.joinGame(game, req.body.playerName, inUseCookie, req.body.joinAsSpectator).then((data) => {
|
||||
res.status(200).send({ cookie: data, environment: gameManager.environment });
|
||||
}).catch((code) => {
|
||||
res.status(code).send();
|
||||
}).catch((data) => {
|
||||
res.status(data.status).send(data.reason);
|
||||
});
|
||||
} else {
|
||||
res.status(404).send();
|
||||
@@ -130,4 +131,8 @@ function validateAccessCode (accessCode) {
|
||||
return /^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH;
|
||||
}
|
||||
|
||||
function validateSpectatorFlag (spectatorFlag) {
|
||||
return typeof spectatorFlag === 'boolean';
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -49,7 +49,7 @@ const globals = {
|
||||
USER_TYPES: {
|
||||
MODERATOR: 'moderator',
|
||||
PLAYER: 'player',
|
||||
TEMPORARY_MODERATOR: 'player / temp mod',
|
||||
TEMPORARY_MODERATOR: 'temp mod',
|
||||
KILLED_PLAYER: 'killed',
|
||||
SPECTATOR: 'spectator'
|
||||
},
|
||||
@@ -84,7 +84,8 @@ const globals = {
|
||||
RESUME_TIMER: 'resumeTimer',
|
||||
GET_TIME_REMAINING: 'getTimeRemaining'
|
||||
},
|
||||
MOCK_AUTH: 'mock_auth'
|
||||
MOCK_AUTH: 'mock_auth',
|
||||
MAX_SPECTATORS: 25
|
||||
};
|
||||
|
||||
module.exports = globals;
|
||||
|
||||
@@ -199,6 +199,7 @@ class GameManager {
|
||||
if (game.spectators.includes(person)) {
|
||||
game.spectators.splice(game.spectators.indexOf(person), 1);
|
||||
}
|
||||
namespace.in(game.accessCode).emit(globals.EVENTS.NEW_SPECTATOR);
|
||||
}
|
||||
person.userType = globals.USER_TYPES.MODERATOR;
|
||||
game.moderator = person;
|
||||
@@ -224,13 +225,18 @@ class GameManager {
|
||||
}
|
||||
};
|
||||
|
||||
joinGame = (game, name, cookie) => {
|
||||
joinGame = (game, name, cookie, joinAsSpectator) => {
|
||||
const matchingPerson = findPersonByField(game, 'cookie', cookie);
|
||||
if (matchingPerson) {
|
||||
return Promise.resolve(matchingPerson.cookie);
|
||||
}
|
||||
if (isNameTaken(game, name)) {
|
||||
return Promise.reject(400);
|
||||
return Promise.reject({ status: 400, reason: 'This name is taken.' });
|
||||
}
|
||||
if (joinAsSpectator && game.spectators.length === globals.MAX_SPECTATORS) {
|
||||
return Promise.reject({ status: 400, reason: 'There are too many people already spectating.' });
|
||||
} else if (joinAsSpectator) {
|
||||
return addSpectator(game, name, this.logger, this.namespace);
|
||||
}
|
||||
const unassignedPerson = game.moderator.assigned === false
|
||||
? game.moderator
|
||||
@@ -246,20 +252,11 @@ class GameManager {
|
||||
game.isFull
|
||||
);
|
||||
return Promise.resolve(unassignedPerson.cookie);
|
||||
} else { // if the game is full, make them a spectator.
|
||||
const spectator = new Person(
|
||||
createRandomId(),
|
||||
createRandomId(),
|
||||
name,
|
||||
globals.USER_TYPES.SPECTATOR
|
||||
);
|
||||
this.logger.trace('new spectator: ' + spectator.name);
|
||||
game.spectators.push(spectator);
|
||||
this.namespace.in(game.accessCode).emit(
|
||||
globals.EVENTS.NEW_SPECTATOR,
|
||||
GameStateCurator.mapPerson(spectator)
|
||||
);
|
||||
return Promise.resolve(spectator.cookie);
|
||||
} else {
|
||||
if (game.spectators.length === globals.MAX_SPECTATORS) {
|
||||
return Promise.reject({ status: 400, reason: 'This game has reached the maximum number of players and spectators.' });
|
||||
}
|
||||
return addSpectator(game, name, this.logger, this.namespace);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -496,4 +493,20 @@ function getGameSize (cards) {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
function addSpectator (game, name, logger, namespace) {
|
||||
const spectator = new Person(
|
||||
createRandomId(),
|
||||
createRandomId(),
|
||||
name,
|
||||
globals.USER_TYPES.SPECTATOR
|
||||
);
|
||||
logger.trace('new spectator: ' + spectator.name);
|
||||
game.spectators.push(spectator);
|
||||
namespace.in(game.accessCode).emit(
|
||||
globals.EVENTS.NEW_SPECTATOR,
|
||||
GameStateCurator.mapPerson(spectator)
|
||||
);
|
||||
return Promise.resolve(spectator.cookie);
|
||||
}
|
||||
|
||||
module.exports = GameManager;
|
||||
|
||||
@@ -199,7 +199,7 @@ describe('game page', () => {
|
||||
it('should display the mod transfer modal, with the single spectator available for selection', () => {
|
||||
document.getElementById('mod-transfer-button').click();
|
||||
expect(document.querySelector('div[data-pointer="MGGVR8KQ7V7HGN3QBLJ5339ZL"].potential-moderator')
|
||||
.innerText).toEqual('Matt');
|
||||
.innerText).toContain('Matt');
|
||||
document.getElementById('close-mod-transfer-modal-button').click();
|
||||
});
|
||||
|
||||
|
||||
@@ -163,7 +163,8 @@ export const mockGames = {
|
||||
paused: true,
|
||||
timeRemaining: 120000
|
||||
},
|
||||
isFull: true
|
||||
isFull: true,
|
||||
spectators: []
|
||||
},
|
||||
moderatorGame:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user