diff --git a/client/config/defaultCards.js b/client/config/defaultCards.js
index b24fd81..9e25dce 100644
--- a/client/config/defaultCards.js
+++ b/client/config/defaultCards.js
@@ -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.",
}
];
diff --git a/client/config/globals.js b/client/config/globals.js
index 23feb1e..f4293f5 100644
--- a/client/config/globals.js
+++ b/client/config/globals.js
@@ -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'
}
};
diff --git a/client/images/GitHub-Mark-32px.png b/client/images/GitHub-Mark-32px.png
new file mode 100644
index 0000000..8b25551
Binary files /dev/null and b/client/images/GitHub-Mark-32px.png differ
diff --git a/client/images/GitHub-Mark-Light-32px.png b/client/images/GitHub-Mark-Light-32px.png
new file mode 100644
index 0000000..628da97
Binary files /dev/null and b/client/images/GitHub-Mark-Light-32px.png differ
diff --git a/client/images/clock.svg b/client/images/clock.svg
index 2aa2e4f..0aa8181 100644
--- a/client/images/clock.svg
+++ b/client/images/clock.svg
@@ -9,6 +9,6 @@
Layer 1
-
+
diff --git a/client/images/email.svg b/client/images/email.svg
new file mode 100644
index 0000000..b510f10
--- /dev/null
+++ b/client/images/email.svg
@@ -0,0 +1 @@
+
diff --git a/client/images/info.svg b/client/images/info.svg
new file mode 100644
index 0000000..c997041
--- /dev/null
+++ b/client/images/info.svg
@@ -0,0 +1,14 @@
+
diff --git a/client/images/person.svg b/client/images/person.svg
new file mode 100644
index 0000000..17e7fea
--- /dev/null
+++ b/client/images/person.svg
@@ -0,0 +1,107 @@
+
+
+
+
diff --git a/client/images/roles/Villager-2.png b/client/images/roles/Villager-2.png
new file mode 100644
index 0000000..c2283a6
Binary files /dev/null and b/client/images/roles/Villager-2.png differ
diff --git a/client/images/vanilla_js.png b/client/images/vanilla_js.png
new file mode 100644
index 0000000..0b030a3
Binary files /dev/null and b/client/images/vanilla_js.png differ
diff --git a/client/images/x.svg b/client/images/x.svg
new file mode 100644
index 0000000..5f6dd02
--- /dev/null
+++ b/client/images/x.svg
@@ -0,0 +1,14 @@
+
diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js
index ed237ff..5b59379 100644
--- a/client/modules/GameStateRenderer.js
+++ b/client/modules/GameStateRenderer.js
@@ -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();
+ });
+}
diff --git a/client/modules/GameTimerManager.js b/client/modules/GameTimerManager.js
index cbd73e0..ad34eba 100644
--- a/client/modules/GameTimerManager.js
+++ b/client/modules/GameTimerManager.js
@@ -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() {
diff --git a/client/modules/ModalManager.js b/client/modules/ModalManager.js
index ba95163..e5e4d19 100644
--- a/client/modules/ModalManager.js
+++ b/client/modules/ModalManager.js
@@ -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.");
}
}
diff --git a/client/modules/Templates.js b/client/modules/Templates.js
index 3699a98..ae6a5b3 100644
--- a/client/modules/Templates.js
+++ b/client/modules/Templates.js
@@ -5,8 +5,19 @@ export const templates = {
"" +
"
" +
"" +
- "" +
- "" +
+ "" +
+ "
" +
+ "

" +
+ "
" +
+ "
" +
+ "
" +
+ "

" +
+ "
" +
+ "
" +
+ "
" +
+ "" +
+ "
" +
"" +
"" +
"
" +
@@ -31,6 +42,9 @@ export const templates = {
"
" +
"
" +
"
" +
+ "
" +
+ "
" +
"
" +
"" +
"
" +
@@ -51,22 +65,22 @@ export const templates = {
"
" +
"
" +
"
" +
+ "" +
+ "
" +
"" +
- "" +
+ "
",
MODERATOR_GAME_VIEW:
"
" +
"
" +
- "
" +
+ "
Transfer Mod Powers 👑
" +
+ "
" +
+ "
" +
+ "" +
+ "
" +
"
" +
"" +
"
" +
"" +
@@ -178,5 +195,31 @@ export const templates = {
"" +
"
" +
"" +
+ "
",
+ ROLE_INFO_MODAL:
+ "" +
+ "" +
+ "
Roles in this game:
" +
+ "
" +
+ "
" +
+ "" +
+ "
" +
+ "
",
+ END_OF_GAME_VIEW:
+ "The moderator has ended the game. Roles are revealed.
" +
+ "" +
+ ""
+
}
diff --git a/client/modules/Toast.js b/client/modules/Toast.js
index f6366dd..8c83b17 100644
--- a/client/modules/Toast.js
+++ b/client/modules/Toast.js
@@ -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';
diff --git a/client/scripts/game.js b/client/scripts/game.js
index ddc253a..832598d 100644
--- a/client/scripts/game.js
+++ b/client/scripts/game.js
@@ -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');
+ });
+}
diff --git a/client/scripts/home.js b/client/scripts/home.js
index 87e4872..f30de66 100644
--- a/client/scripts/home.js
+++ b/client/scripts/home.js
@@ -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
diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css
index b782f18..ebecbfc 100644
--- a/client/styles/GLOBAL.css
+++ b/client/styles/GLOBAL.css
@@ -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;
diff --git a/client/styles/create.css b/client/styles/create.css
index 5224e5e..74b7fd5 100644
--- a/client/styles/create.css
+++ b/client/styles/create.css
@@ -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 {
diff --git a/client/styles/game.css b/client/styles/game.css
index 5ac025a..3a0e601 100644
--- a/client/styles/game.css
+++ b/client/styles/game.css
@@ -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;
diff --git a/client/styles/home.css b/client/styles/home.css
index f84aee8..e8092c0 100644
--- a/client/styles/home.css
+++ b/client/styles/home.css
@@ -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 {
diff --git a/client/styles/modal.css b/client/styles/modal.css
index aa600dd..a2bfa24 100644
--- a/client/styles/modal.css
+++ b/client/styles/modal.css
@@ -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;
diff --git a/client/views/game.html b/client/views/game.html
index f4cea66..150cee5 100644
--- a/client/views/game.html
+++ b/client/views/game.html
@@ -43,7 +43,32 @@
Home
-
+
+