diff --git a/client/images/Werewolf_Small.png b/client/images/Werewolf_Small.png index d81ac12..0f6256a 100644 Binary files a/client/images/Werewolf_Small.png and b/client/images/Werewolf_Small.png differ diff --git a/client/images/logo_cropped.gif b/client/images/logo_cropped.gif new file mode 100644 index 0000000..7ebb7fb Binary files /dev/null and b/client/images/logo_cropped.gif differ diff --git a/client/modules/GameCreationStepManager.js b/client/modules/GameCreationStepManager.js index f10ada1..a284ea5 100644 --- a/client/modules/GameCreationStepManager.js +++ b/client/modules/GameCreationStepManager.js @@ -247,12 +247,19 @@ function renderReviewAndCreateStep(containerId, stepNumber, game) { div.setAttribute("id", "step-" + stepNumber); div.innerHTML = - "" + - "
" + - "" + - "
" + - "" + - "
"; + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "" + + "
" + + "
"; + div.querySelector('#mod-option').innerText = game.hasDedicatedModerator ? "I will be the dedicated mod. Don't deal me a card." diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index fb2af5d..3c22239 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -1,4 +1,5 @@ -import {globals} from "../config/globals.js"; +import { globals } from "../config/globals.js"; +import { toast } from "./Toast.js"; export class GameStateRenderer { constructor(gameState) { @@ -30,9 +31,14 @@ export class GameStateRenderer { renderLobbyHeader() { let title = document.createElement("h1"); title.innerText = "Lobby"; - document.body.prepend(title); + document.getElementById("game-title").appendChild(title); let gameLinkContainer = document.getElementById("game-link"); gameLinkContainer.innerText = window.location; + gameLinkContainer.addEventListener('click', () => { + navigator.clipboard.writeText(gameLinkContainer.innerText).then(() => { + toast('Link copied!', 'success', true); + }); + }); let copyImg = document.createElement("img"); copyImg.setAttribute("src", "../images/copy.svg"); gameLinkContainer.appendChild(copyImg); diff --git a/client/modules/Templates.js b/client/modules/Templates.js index f7c2b0d..ca547d6 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -20,5 +20,9 @@ export const templates = { "" + + "", + START_GAME_PROMPT: + "
" + + "" + "
" } diff --git a/client/modules/Toast.js b/client/modules/Toast.js index 7b14cf5..93a9b72 100644 --- a/client/modules/Toast.js +++ b/client/modules/Toast.js @@ -7,7 +7,7 @@ export const toast = (message, type, positionAtTop = true) => { function buildAndInsertMessageElement (message, type, positionAtTop) { cancelCurrentToast(); let backgroundColor; - const position = positionAtTop ? 'top:4rem;' : 'bottom: 35px;'; + const position = positionAtTop ? 'top:3rem;' : 'bottom: 35px;'; switch (type) { case 'warning': backgroundColor = '#fff5b1'; diff --git a/client/modules/UserUtility.js b/client/modules/UserUtility.js index 208f08a..ce1912c 100644 --- a/client/modules/UserUtility.js +++ b/client/modules/UserUtility.js @@ -1,9 +1,14 @@ import { globals } from '../config/globals.js'; +/* + we will use sessionStorage during local development to aid in testing, vs. localStorage for production. + sessionStorage does not persist across tabs, allowing developers to join a game as different players from different windows. + */ export const UserUtility = { createNewAnonymousUserId (force = true, environment) { let newId, currentId; + if (environment === globals.ENVIRONMENT.LOCAL) { currentId = sessionStorage.getItem(globals.PLAYER_ID_COOKIE_KEY); } else { @@ -23,7 +28,7 @@ export const UserUtility = { }, setAnonymousUserId (id, environment) { - if (environment === globals.ENVIRONMENT.LOCAL) { // use sessionStorage for cookie during local development to aid in testing. + if (environment === globals.ENVIRONMENT.LOCAL) { sessionStorage.setItem(globals.PLAYER_ID_COOKIE_KEY, id); } else { localStorage.setItem(globals.PLAYER_ID_COOKIE_KEY, id); @@ -32,7 +37,7 @@ export const UserUtility = { validateAnonUserSignature (environment) { let userSig; - if (environment === globals.ENVIRONMENT.LOCAL) { // use sessionStorage for cookie during local development to aid in testing. + if (environment === globals.ENVIRONMENT.LOCAL) { userSig = sessionStorage.getItem(globals.PLAYER_ID_COOKIE_KEY); } else { userSig = localStorage.getItem(globals.PLAYER_ID_COOKIE_KEY); diff --git a/client/scripts/game.js b/client/scripts/game.js index 32cafad..f4426ad 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -32,8 +32,17 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { switch (gameState.status) { case globals.GAME_STATE.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; - gameStateRenderer.renderLobbyPlayers(); gameStateRenderer.renderLobbyHeader(); + gameStateRenderer.renderLobbyPlayers(); + if ( + gameState.isFull + && ( + gameState.userType === globals.USER_TYPES.MODERATOR + || gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + ) + ) { + displayStartGamePromptForModerators(); + } break; default: break; @@ -41,9 +50,25 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { } function setClientSocketHandlers(gameStateRenderer, socket) { - socket.on(globals.EVENTS.PLAYER_JOINED, (player) => { + socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { toast(player.name + " joined!", "success", false); gameStateRenderer.gameState.people.push(player); gameStateRenderer.renderLobbyPlayers(); + if ( + gameIsFull + && ( + gameStateRenderer.gameState.userType === globals.USER_TYPES.MODERATOR + || gameStateRenderer.gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + ) + ) { + displayStartGamePromptForModerators(); + } }) } + +function displayStartGamePromptForModerators() { + document.getElementById("lobby-players").setAttribute("style", 'margin-bottom: 130px'); + let div = document.createElement("div"); + div.innerHTML = templates.START_GAME_PROMPT; + document.body.appendChild(div); +} diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index fc363cc..7b99623 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -23,23 +23,20 @@ th, thead, tr, tt, u, ul, var { html { font-family: 'signika-negative', sans-serif !important; - background-color: #23282b !important; + background-color: #121314 !important; } body { display: flex; flex-direction: column; align-items: flex-start; - width: 95%; margin: 0 auto; - max-width: 68em; } h1 { font-family: 'diavlo', sans-serif; color: #ab2626; filter: drop-shadow(2px 2px 4px black); - font-size: 50px; margin: 0.5em 0; } @@ -79,12 +76,13 @@ textarea, input { button, input[type="submit"] { font-family: 'signika-negative', sans-serif !important; padding: 10px; - background-color: black; + background-color: #722c2c; border-radius: 3px; color: whitesmoke; font-size: 18px; cursor: pointer; border: 2px solid transparent; + transition: background-color 0.3s ease-out; } button:active, input[type=submit]:active { @@ -97,11 +95,13 @@ button:active, input[type=submit]:active { display: flex; flex-direction: column; justify-content: center; - width: 100%; + width: 95%; + max-width: 68em; + margin: 0 auto; } -button:hover, input[type="submit"]:hover { - background-color: #4f4f4f; +button:hover, input[type="submit"]:hover, #game-link:hover { + background-color: #333243; } input { @@ -122,7 +122,7 @@ input { left: 0; right: 0; width: fit-content; - max-width: 30em; + max-width: 80%; min-width: 15em; font-size: 20px; margin: 0 auto; @@ -134,8 +134,14 @@ input { #navbar { display: flex; align-items: center; - padding: 5px; + padding: 5px 0; width: 100%; + background-color: #333243; +} + +#navbar img { + margin: 0 1em; + width: 50px; } #navbar a { @@ -143,6 +149,7 @@ input { text-decoration: none; cursor: pointer; font-size: 25px; + font-family: 'diavlo', sans-serif; } #navbar a:hover { @@ -253,3 +260,23 @@ input { transform: translateY(-20px); } } + +@media(max-width: 550px) { + h1 { + font-size: 35px; + } + + #step-1 div { + font-size: 20px; + } +} + +@media(min-width: 551px) { + h1 { + font-size: 50px; + } + + #step-1 div { + font-size: 25px; + } +} diff --git a/client/styles/create.css b/client/styles/create.css index 9eb7037..5976516 100644 --- a/client/styles/create.css +++ b/client/styles/create.css @@ -86,7 +86,7 @@ width: fit-content; } -#deck-container, #custom-roles-container, #step-3 { +#deck-container, #custom-roles-container { margin: 1em 0; background-color: #1f1f1f; padding: 10px; @@ -95,12 +95,29 @@ #step-3 { display: flex; + padding: 10px; justify-content: center; align-items: center; width: fit-content; + border-radius: 3px; margin: 0 auto; } +#step-4 > div { + display: flex; + flex-direction: column; + align-items: center; + text-align: left; + justify-content: center; + margin: 0 auto; + width: 25em; + max-width: 95%; +} + +#step-4 > div label { + width: 100%; +} + #deck { display: flex; flex-wrap: wrap; @@ -235,7 +252,6 @@ input[type="number"] { cursor: pointer; border: 2px solid transparent; border-radius: 3px; - font-size: 25px; box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6); } @@ -259,6 +275,7 @@ input[type="number"] { width: fit-content; border-radius: 3px; margin: 0.5em 0; + align-self: flex-start; } @keyframes fade-in { @@ -268,3 +285,23 @@ input[type="number"] { opacity: 1; } } + +@media(max-width: 550px) { + h1 { + font-size: 35px; + } + + #step-1 div { + font-size: 20px; + } +} + +@media(min-width: 551px) { + h1 { + font-size: 50px; + } + + #step-1 div { + font-size: 25px; + } +} diff --git a/client/styles/game.css b/client/styles/game.css index b285bc7..f6950b3 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -41,15 +41,15 @@ h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - border: 2px solid transparent; cursor: pointer; margin-top: 10px; - padding: 5px; + padding: 7px; border-radius: 3px; - background-color: #1f1f1f; + background-color: #722c2c; color: whitesmoke; align-items: center; display: flex; + transition: background-color 0.2s; } #game-player-count { @@ -63,8 +63,8 @@ h1 { margin-left: 0.5em; } -#game-link:hover { - border: 2px solid #0075F2; +#game-title { + margin: 0 auto; } label[for='moderator'] { @@ -80,3 +80,60 @@ label[for='lobby-players'] { filter: drop-shadow(2px 2px 4px black); font-size: 30px; } + +#start-game-prompt { + display: flex; + align-items: center; + justify-content: center; + position: fixed; + z-index: 1000; + border-radius: 3px; + font-family: 'signika-negative', sans-serif; + font-weight: 100; + box-shadow: 0 2px 4px 0 rgb(0 0 0 / 25%); + left: 0; + right: 0; + bottom: 0; + /* width: fit-content; */ + font-size: 20px; + height: 120px; + margin: 0 auto; + animation: fade-in-slide-up 10s ease; + animation-fill-mode: forwards; + animation-direction: normal; + width: 100%; + background-color: #333243; +} + +#start-game-button { + font-family: 'signika-negative', sans-serif !important; + padding: 10px; + background-color: #1c8a36; + border-radius: 3px; + color: whitesmoke; + font-size: 30px; + cursor: pointer; + border: 2px solid transparent; + transition: background-color, border 0.3s ease-out; + text-shadow: 0 3px 4px rgb(0 0 0 / 85%); +} + +#start-game-button:hover { + background-color: #326243; + border: 2px solid #1c8a36; +} + +@keyframes fade-in-slide-up { + 0% { + opacity: 0; + transform: translateY(20px); + } + 5% { + opacity: 1; + transform: translateY(0px); + } + 100% { + opacity: 1; + transform: translateY(0px); + } +} diff --git a/client/styles/home.css b/client/styles/home.css index 2b9761a..f84aee8 100644 --- a/client/styles/home.css +++ b/client/styles/home.css @@ -1,10 +1,15 @@ +html { + height: 100%; +} + body { align-items: center; + justify-content: center; + height: 100%; } button { padding: 20px; - font-size: 25px; margin-bottom: 1em; } @@ -19,6 +24,10 @@ form { align-items: center; } +a button { + text-shadow: 0 3px 4px rgb(0 0 0 / 85%); +} + #join-button { min-width: 6em; max-height: 3em; @@ -26,19 +35,26 @@ form { } h3 { - max-width: 30em; - font-size: 16px; + font-size: 20px; margin-bottom: 2em; + padding: 1em; + max-width: 23em; + font-family: 'diavlo', sans-serif; } img { - max-width: 650px; + max-width: 400px; width: 63vw; - min-width: 430px; + min-width: 250px; + margin: 1em 0; } form > div { - margin: 1em; + margin: 1em 0; +} + +#join-container { + max-width: 90%; } #join-container > label { @@ -51,3 +67,15 @@ form > div { label[for="room-code"], label[for="player-name"] { margin-right: 0.5em; } + +@media (min-width: 700px) { + button { + font-size: 35px; + } +} + +@media (max-width: 701px) { + button { + font-size: 5vw; + } +} diff --git a/client/views/404.html b/client/views/404.html index 07ab078..4a212d6 100644 --- a/client/views/404.html +++ b/client/views/404.html @@ -3,22 +3,77 @@ - - Werewolf Utility - - + Create A Game + + - - + + - - - + + + + + + + + + + -

404

+ + +

404

+

The game or other resource that you are looking for could not be found, or you don't have permission to access it. + If this error is unexpected, the application may have restarted.

+
+ diff --git a/client/views/create.html b/client/views/create.html index 251eb12..1250225 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -26,6 +26,7 @@
diff --git a/client/views/game.html b/client/views/game.html index 78e0b17..cae06da 100644 --- a/client/views/game.html +++ b/client/views/game.html @@ -18,6 +18,11 @@ + +