diff --git a/client/config/globals.js b/client/config/globals.js index 333dc99..23feb1e 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -14,7 +14,8 @@ export const globals = { GET_TIME_REMAINING: 'getTimeRemaining', KILL_PLAYER: 'killPlayer', REVEAL_PLAYER: 'revealPlayer', - TRANSFER_MODERATOR: 'transferModerator' + TRANSFER_MODERATOR: 'transferModerator', + CHANGE_NAME: 'changeName' }, STATUS: { LOBBY: "lobby", @@ -24,12 +25,16 @@ export const globals = { GOOD: "good", EVIL: "evil" }, + MESSAGES: { + ENTER_NAME: "Client must enter name." + }, EVENTS: { PLAYER_JOINED: "playerJoined", SYNC_GAME_STATE: "syncGameState", START_TIMER: "startTimer", KILL_PLAYER: "killPlayer", - REVEAL_PLAYER: 'revealPlayer' + REVEAL_PLAYER: 'revealPlayer', + CHANGE_NAME: 'changeName' }, USER_TYPES: { MODERATOR: "moderator", diff --git a/client/modules/GameCreationStepManager.js b/client/modules/GameCreationStepManager.js index 30e0dc7..d2a5c09 100644 --- a/client/modules/GameCreationStepManager.js +++ b/client/modules/GameCreationStepManager.js @@ -313,12 +313,13 @@ function updateTracker(step) { function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) { document.querySelector("#step-back-button")?.remove(); document.querySelector("#step-forward-button")?.remove(); + document.querySelector("#create-game")?.remove(); if (back) { let backButton = document.createElement("button"); backButton.innerText = "\u2bc7 Back"; backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); - document.getElementById("creation-step-buttons").appendChild(backButton); + document.getElementById("tracker-container").prepend(backButton); } if (forward && builtGame === null) { @@ -326,7 +327,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) fwdButton.innerHTML = "Next \u25b6"; fwdButton.addEventListener('click', forwardHandler); fwdButton.setAttribute("id", "step-forward-button"); - document.getElementById("creation-step-buttons").appendChild(fwdButton); + document.getElementById("tracker-container").appendChild(fwdButton); } else if (forward && builtGame !== null) { let createButton = document.createElement("button"); createButton.innerText = "Create"; @@ -344,7 +345,7 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) toast("You must provide your name.", "error", true); } }); - document.getElementById("creation-step-buttons").appendChild(createButton); + document.getElementById("tracker-container").appendChild(createButton); } } diff --git a/client/modules/GameStateRenderer.js b/client/modules/GameStateRenderer.js index 3200bb5..79fe67c 100644 --- a/client/modules/GameStateRenderer.js +++ b/client/modules/GameStateRenderer.js @@ -16,13 +16,13 @@ export class GameStateRenderer { renderLobbyPlayers() { document.querySelectorAll('.lobby-player').forEach((el) => el.remove()) let lobbyPlayersContainer = document.getElementById("lobby-players"); - if (this.gameState.client.userType === globals.USER_TYPES.PLAYER) { + if (this.gameState.client.userType === globals.USER_TYPES.PLAYER && this.gameState.moderator.userType === globals.USER_TYPES.MODERATOR) { lobbyPlayersContainer.appendChild(renderLobbyPerson(this.gameState.moderator.name, this.gameState.moderator.userType)) } for (let person of this.gameState.people) { lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name,person.userType)) } - let playerCount = this.gameState.people.filter((person) => person.userType === globals.USER_TYPES.PLAYER).length; + let playerCount = this.gameState.people.length; document.querySelector("label[for='lobby-players']").innerText = "People (" + playerCount + "/" + getGameSize(this.gameState.deck) + " Players)"; } @@ -129,8 +129,23 @@ export class GameStateRenderer { }); let teamGood = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.GOOD); let teamEvil = this.gameState.people.filter((person) => person.alignment === globals.ALIGNMENT.EVIL); - renderGroupOfPlayers(teamEvil, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, globals.ALIGNMENT.EVIL, true, this.socket); - renderGroupOfPlayers(teamGood, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, globals.ALIGNMENT.GOOD, true, this.socket); + renderGroupOfPlayers( + teamEvil, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + globals.ALIGNMENT.EVIL, + this.gameState.moderator.userType, + this.socket + ); + renderGroupOfPlayers( + teamGood, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + globals.ALIGNMENT.GOOD, + this.gameState.moderator.userType, + this.socket); document.getElementById("players-alive-label").innerText = 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; @@ -152,10 +167,17 @@ export class GameStateRenderer { }); } document.querySelectorAll('.game-player').forEach((el) => el.remove()); - this.gameState.people.sort((a, b) => { - return a.name >= b.name ? 1 : -1; - }); - renderGroupOfPlayers(this.gameState, this.killPlayerHandlers, this.revealRoleHandlers, this.gameState.accessCode, null, tempMod, this.socket); + sortPeopleByStatus(this.gameState.people); + let modType = tempMod ? this.gameState.moderator.userType : null; + renderGroupOfPlayers( + this.gameState.people, + this.killPlayerHandlers, + this.revealRoleHandlers, + this.gameState.accessCode, + null, + modType, + this.socket + ); document.getElementById("players-alive-label").innerText = 'Players: ' + this.gameState.people.filter((person) => !person.out).length + ' / ' + this.gameState.people.length + ' Alive'; @@ -220,6 +242,19 @@ function renderLobbyPerson(name, userType) { return el; } +function sortPeopleByStatus(people) { + people.sort((a, b) => { + if (a.out !== b.out) { + return a.out ? 1 : -1; + } else { + if (a.revealed !== b.revealed) { + return a.revealed? -1 : 1; + } + return a.name >= b.name ? 1 : -1; + } + }); +} + function getGameSize(cards) { let quantity = 0; for (let card of cards) { @@ -237,11 +272,11 @@ function removeExistingTitle() { } // TODO: refactor to reduce the cyclomatic complexity of this function -function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, accessCode=null, alignment=null, moderator=false, socket=null) { - for (let player of gameState.people) { +function renderGroupOfPlayers(people, killPlayerHandlers, revealRoleHandlers, accessCode=null, alignment=null, moderatorType, socket=null) { + for (let player of people) { let container = document.createElement("div"); container.classList.add('game-player'); - if (moderator) { + if (moderatorType) { container.dataset.pointer = player.id; container.innerHTML = templates.MODERATOR_PLAYER; } else { @@ -250,9 +285,9 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, container.querySelector('.game-player-name').innerText = player.name; let roleElement = container.querySelector('.game-player-role') - if (moderator) { + if (moderatorType) { roleElement.classList.add(alignment); - if (gameState.moderator.userType === globals.USER_TYPES.MODERATOR) { + if (moderatorType === globals.USER_TYPES.MODERATOR) { roleElement.innerText = player.gameRole; document.getElementById("player-list-moderator-team-" + alignment).appendChild(container); } else { @@ -275,11 +310,12 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, if (player.out) { container.classList.add('killed'); - if (moderator) { + if (moderatorType) { container.querySelector('.kill-player-button')?.remove(); + insertPlaceholderButton(container, false, "killed"); } } else { - if (moderator) { + if (moderatorType) { killPlayerHandlers[player.id] = () => { if (confirm("KILL " + player.name + "?")) { socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id); @@ -290,11 +326,12 @@ function renderGroupOfPlayers(gameState, killPlayerHandlers, revealRoleHandlers, } if (player.revealed) { - if (moderator) { + if (moderatorType) { container.querySelector('.reveal-role-button')?.remove(); + insertPlaceholderButton(container, true, "revealed"); } } else { - if (moderator) { + if (moderatorType) { revealRoleHandlers[player.id] = () => { if (confirm("REVEAL " + player.name + "?")) { socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id); @@ -340,3 +377,18 @@ function renderPlayerRole(gameState) { document.getElementById("game-role").style.display = 'none'; }); } + +function insertPlaceholderButton(container, append, type) { + let button = document.createElement("div"); + button.classList.add('placeholder-button'); + if (type === "killed") { + button.innerText = 'Killed'; + } else { + button.innerText = "Revealed"; + } + if (append) { + container.querySelector('.player-action-buttons').appendChild(button); + } else { + container.querySelector('.player-action-buttons').prepend(button); + } +} diff --git a/client/modules/Templates.js b/client/modules/Templates.js index 4a1a09b..3699a98 100644 --- a/client/modules/Templates.js +++ b/client/modules/Templates.js @@ -9,7 +9,7 @@ export const templates = { "
" + "" + "
" + - "
" + + "
" + "" + "
" + "
" + @@ -41,7 +41,7 @@ export const templates = { "

Click to reveal your role

" + "

(click again to hide)

" + "
" + - "
" + + "
" + "" + "
" + "
", @@ -121,7 +121,7 @@ export const templates = { "

Click to reveal your role

" + "

(click again to hide)

" + "
" + - "
" + + "
" + "" + "
" + "
" + @@ -131,13 +131,52 @@ export const templates = { "
" + "
" + "
" + - "
" + + "
" + "" + - "" + + "" + "
", GAME_PLAYER: "
" + "
" + "
" + + "
", + INITIAL_GAME_DOM: + "
" + + "
" + + "" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
", + // via https://loading.io/css/ + SPINNER: + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
" + + "
", + NAME_CHANGE_MODAL: + "" + + "" } diff --git a/client/scripts/game.js b/client/scripts/game.js index 0988a8c..62618a5 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -4,6 +4,7 @@ import {templates} from "../modules/Templates.js"; import {GameStateRenderer} from "../modules/GameStateRenderer.js"; import {cancelCurrentToast, toast} from "../modules/Toast.js"; import {GameTimerManager} from "../modules/GameTimerManager.js"; +import {ModalManager} from "../modules/ModalManager.js"; export const game = () => { let timerWorker; @@ -28,20 +29,43 @@ function prepareGamePage(environment, socket, timerWorker) { const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { + let currentGameState = gameState; + document.querySelector('.spinner-container')?.remove(); + document.querySelector('.spinner-background')?.remove(); if (gameState === null) { window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); + } else if (!gameState.client.hasEnteredName) { + userId = gameState.client.cookie; + UserUtility.setAnonymousUserId(userId, environment); + document.getElementById("game-content").innerHTML = templates.NAME_CHANGE_MODAL; + document.getElementById("change-name-form").onsubmit = (e) => { + e.preventDefault(); + let name = document.getElementById("player-new-name").value; + if (validateName(name)) { + socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { name: name, personId: gameState.client.id }, (result) => { + switch (result) { + case "taken": + toast('This name is already taken.', 'error', true, true, 8); + break; + case "changed": + ModalManager.dispelModal("change-name-modal", "change-name-modal-background") + toast('Name set.', 'success', true, true, 5); + document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + propagateNameChange(currentGameState, name, currentGameState.client.id); + initializeGame(currentGameState, socket, timerWorker, userId); + } + }) + } else { + toast("Name must be fewer than 30 characters.", 'error', true, true, 8); + } + } } else { - toast('You are connected.', 'success', true, true, 3); + document.getElementById("game-content").innerHTML = templates.INITIAL_GAME_DOM; + toast('You are connected.', 'success', true, true, 2); console.log(gameState); userId = gameState.client.cookie; UserUtility.setAnonymousUserId(userId, environment); - let gameStateRenderer = new GameStateRenderer(gameState, socket); - let gameTimerManager; - if (gameState.timerParams) { - gameTimerManager = new GameTimerManager(gameState, socket); - } - setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager); - processGameState(gameState, userId, socket, gameStateRenderer); + initializeGame(gameState, socket, timerWorker, userId); } }); } else { @@ -49,18 +73,28 @@ function prepareGamePage(environment, socket, timerWorker) { } } -function processGameState (gameState, userId, socket, gameStateRenderer) { - displayClientInfo(gameState.client.name, gameState.client.userType); - switch (gameState.status) { +function initializeGame(currentGameState, socket, timerWorker, userId) { + let gameStateRenderer = new GameStateRenderer(currentGameState, socket); + let gameTimerManager; + if (currentGameState.timerParams) { + gameTimerManager = new GameTimerManager(currentGameState, socket); + } + setClientSocketHandlers(currentGameState, gameStateRenderer, socket, timerWorker, gameTimerManager); + processGameState(currentGameState, userId, socket, gameStateRenderer); +} + +function processGameState (currentGameState, userId, socket, gameStateRenderer) { + displayClientInfo(currentGameState.client.name, currentGameState.client.userType); + switch (currentGameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; gameStateRenderer.renderLobbyHeader(); gameStateRenderer.renderLobbyPlayers(); if ( - gameState.isFull + currentGameState.isFull && ( - gameState.client.userType === globals.USER_TYPES.MODERATOR - || gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + currentGameState.client.userType === globals.USER_TYPES.MODERATOR + || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { displayStartGamePromptForModerators(gameStateRenderer, socket); @@ -68,7 +102,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { break; case globals.STATUS.IN_PROGRESS: gameStateRenderer.renderGameHeader(); - switch (gameState.client.userType) { + switch (currentGameState.client.userType) { case globals.USER_TYPES.PLAYER: document.getElementById("game-state-container").innerHTML = templates.PLAYER_GAME_VIEW; gameStateRenderer.renderPlayerView(); @@ -97,7 +131,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) { break; } - socket.emit(globals.COMMANDS.GET_TIME_REMAINING, gameState.accessCode); + socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode); break; default: break; @@ -110,17 +144,17 @@ function displayClientInfo(name, userType) { document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS[userType]; } -function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager) { +function setClientSocketHandlers(currentGameState, 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); - gameStateRenderer.gameState.people.push(player); + currentGameState.people.push(player); gameStateRenderer.renderLobbyPlayers(); if ( gameIsFull && ( - gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR - || gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + currentGameState.client.userType === globals.USER_TYPES.MODERATOR + || currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR ) ) { displayStartGamePromptForModerators(gameStateRenderer, socket); @@ -131,12 +165,13 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { socket.emit( globals.COMMANDS.FETCH_GAME_STATE, - gameStateRenderer.gameState.accessCode, - gameStateRenderer.gameState.client.cookie, + currentGameState.accessCode, + currentGameState.client.cookie, function (gameState) { - gameStateRenderer.gameState = gameState; - gameTimerManager.gameState = gameState; - processGameState(gameState, gameState.client.cookie, socket, gameStateRenderer); + currentGameState = gameState; + gameStateRenderer.gameState = currentGameState; + gameTimerManager.gameState = currentGameState; + processGameState(currentGameState, gameState.client.cookie, socket, gameStateRenderer); } ); }); @@ -148,14 +183,14 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim if (!socket.hasListeners(globals.EVENTS.KILL_PLAYER)) { socket.on(globals.EVENTS.KILL_PLAYER, (id) => { - let killedPerson = gameStateRenderer.gameState.people.find((person) => person.id === id); + let killedPerson = currentGameState.people.find((person) => person.id === id); if (killedPerson) { killedPerson.out = true; - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(killedPerson.name + ' killed.', 'success', true, true, 6); gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo() } else { - if (killedPerson.id === gameStateRenderer.gameState.client.id) { + if (killedPerson.id === currentGameState.client.id) { let clientUserType = document.getElementById("client-user-type"); if (clientUserType) { clientUserType.innerText = globals.USER_TYPES.KILLED_PLAYER + ' \uD83D\uDC80' @@ -165,7 +200,7 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } else { toast(killedPerson.name + ' was killed!', 'warning', false, true, 6); } - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); } else { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); @@ -177,21 +212,21 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim if (!socket.hasListeners(globals.EVENTS.REVEAL_PLAYER)) { socket.on(globals.EVENTS.REVEAL_PLAYER, (revealData) => { - let revealedPerson = gameStateRenderer.gameState.people.find((person) => person.id === revealData.id); + let revealedPerson = currentGameState.people.find((person) => person.id === revealData.id); if (revealedPerson) { revealedPerson.revealed = true; revealedPerson.gameRole = revealData.gameRole; revealedPerson.alignment = revealData.alignment; - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.MODERATOR) { toast(revealedPerson.name + ' revealed.', 'success', true, true, 6); gameStateRenderer.renderPlayersWithRoleAndAlignmentInfo() } else { - if (revealedPerson.id === gameStateRenderer.gameState.client.id) { + if (revealedPerson.id === currentGameState.client.id) { toast('Your role has been revealed!', 'warning', false, true, 6); } else { toast(revealedPerson.name + ' was revealed as a ' + revealedPerson.gameRole + '!', 'warning', false, true, 6); } - if (gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { + if (currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(true); } else { gameStateRenderer.renderPlayersWithNoRoleInformationUnlessRevealed(false); @@ -200,17 +235,23 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim } }); } + + if (!socket.hasListeners(globals.EVENTS.CHANGE_NAME)) { + socket.on(globals.EVENTS.CHANGE_NAME, (personId, name) => { + propagateNameChange(currentGameState, name, personId); + processGameState(currentGameState, currentGameState.client.cookie, socket, gameStateRenderer); + }); + } } function displayStartGamePromptForModerators(gameStateRenderer, socket) { - document.getElementById("lobby-players").setAttribute("style", 'margin-bottom: 130px'); let div = document.createElement("div"); div.innerHTML = templates.START_GAME_PROMPT; document.body.appendChild(div); document.getElementById("start-game-button").addEventListener('click', (e) => { e.preventDefault(); if (confirm("Start the game and deal roles?")) { - socket.emit(globals.COMMANDS.START_GAME, gameStateRenderer.gameState.accessCode, gameStateRenderer.gameState.client.cookie); + socket.emit(globals.COMMANDS.START_GAME, gameStateRenderer.gameState.accessCode,gameStateRenderer.gameState.client.cookie); } }); @@ -226,3 +267,24 @@ function runGameTimer (hours, minutes, tickRate, soundManager, timerWorker) { timerWorker.postMessage({ hours: hours, minutes: minutes, tickInterval: tickRate }); } } + +function validateName(name) { + return typeof name === 'string' && name.length <= 30; +} + +function propagateNameChange(gameState, name, personId) { + gameState.client.name = name; + let matchingPerson = gameState.people.find((person) => person.id === personId); + if (matchingPerson) { + matchingPerson.name = name; + } + + if (gameState.moderator.id === personId) { + gameState.moderator.name = name; + } + + let matchingSpectator = gameState.spectators?.find((spectator) => spectator.id === personId); + if (matchingSpectator) { + matchingSpectator.name = name; + } +} diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css index c00c31e..b782f18 100644 --- a/client/styles/GLOBAL.css +++ b/client/styles/GLOBAL.css @@ -97,6 +97,7 @@ button:active, input[type=submit]:active { justify-content: center; width: 95%; margin: 0 auto; + align-items: center; } button:hover, input[type="submit"]:hover, #game-link:hover { @@ -108,6 +109,7 @@ input { } .info-message { + pointer-events: none; display: flex; align-items: center; justify-content: center; @@ -295,3 +297,118 @@ input { font-size: 25px; } } + +.spinner-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: calc(100% + 100px); + background-color: rgba(0, 0, 0, 0.75); + z-index: 50; +} + + +/* via https://loading.io/css/ */ + +.spinner-container { + position: relative; +} + +.spinner-container p { + margin: auto; + position: fixed; + top: 0; + left: 0; + bottom: -8em; + font-size: 20px; + z-index: 51; + text-align: center; + right: 0; + color: #d7d7d7; + height: fit-content; +} + +.lds-spinner { + margin: auto; + position: fixed; + top: -80px; + left: 0; + bottom: 0; + z-index: 51; + right: 0; + height: fit-content; + display: inline-block; + width: 80px; +} +.lds-spinner div { + transform-origin: 40px 40px; + animation: lds-spinner 1.2s linear infinite; +} +.lds-spinner div:after { + content: " "; + display: block; + position: absolute; + top: 3px; + left: 37px; + width: 6px; + height: 18px; + border-radius: 20%; + background: #d7d7d7; +} +.lds-spinner div:nth-child(1) { + transform: rotate(0deg); + animation-delay: -1.1s; +} +.lds-spinner div:nth-child(2) { + transform: rotate(30deg); + animation-delay: -1s; +} +.lds-spinner div:nth-child(3) { + transform: rotate(60deg); + animation-delay: -0.9s; +} +.lds-spinner div:nth-child(4) { + transform: rotate(90deg); + animation-delay: -0.8s; +} +.lds-spinner div:nth-child(5) { + transform: rotate(120deg); + animation-delay: -0.7s; +} +.lds-spinner div:nth-child(6) { + transform: rotate(150deg); + animation-delay: -0.6s; +} +.lds-spinner div:nth-child(7) { + transform: rotate(180deg); + animation-delay: -0.5s; +} +.lds-spinner div:nth-child(8) { + transform: rotate(210deg); + animation-delay: -0.4s; +} +.lds-spinner div:nth-child(9) { + transform: rotate(240deg); + animation-delay: -0.3s; +} +.lds-spinner div:nth-child(10) { + transform: rotate(270deg); + animation-delay: -0.2s; +} +.lds-spinner div:nth-child(11) { + transform: rotate(300deg); + animation-delay: -0.1s; +} +.lds-spinner div:nth-child(12) { + transform: rotate(330deg); + animation-delay: 0s; +} +@keyframes lds-spinner { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} diff --git a/client/styles/create.css b/client/styles/create.css index 5976516..0903409 100644 --- a/client/styles/create.css +++ b/client/styles/create.css @@ -222,14 +222,25 @@ input[type="number"] { margin-bottom: 8em; } +#game-creation-container { + width: 95%; + max-width: 60em; +} + +#tracker-container { + display: flex; + align-items: center; + margin-top: 2em; + justify-content: center; + width: 100%; +} + #creation-step-tracker { display: flex; justify-content: center; - margin-top: 2em; } #step-forward-button, #step-back-button, #create-game { - position: absolute; font-family: sans-serif; font-size: 20px; padding: 10px 20px; diff --git a/client/styles/game.css b/client/styles/game.css index e703ee9..5ac025a 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -14,6 +14,19 @@ margin: 0.5em 0; } +#lobby-players { + overflow-y: auto; + max-height: 30em; + overflow-x: hidden; + padding: 0 10px; + border-radius: 3px; +} + +#lobby-people-container label { + display: block; + margin-bottom: 0.5em; +} + .lobby-player-client { border: 2px solid #21ba45; } @@ -30,6 +43,9 @@ justify-content: center; flex-direction: row; flex-wrap: wrap; + display: flex; + width: 95%; + margin: 0 auto 115px auto; } #lobby-header { @@ -124,7 +140,7 @@ h1 { display: flex; align-items: center; justify-content: center; - background-color: #333243; + background-color: #171522; border: 5px solid #61606a; position: relative; flex-direction: column; @@ -215,7 +231,7 @@ h1 { #client-container { max-width: 35em; - margin-top: 2em; + margin: 1em 0; } #client { @@ -226,12 +242,14 @@ h1 { justify-content: space-between; background-color: #333243; border-radius: 3px; + min-width: 15em; } #client-name { color: whitesmoke; font-family: 'diavlo', sans-serif; font-size: 30px; + margin: 0.25em 2em 0.25em 0; } #client-user-type { @@ -267,7 +285,7 @@ label[for='moderator'] { bottom: 0; /* width: fit-content; */ font-size: 20px; - height: 120px; + height: 85px; margin: 0 auto; animation: fade-in-slide-up 10s ease; animation-fill-mode: forwards; @@ -276,16 +294,20 @@ label[for='moderator'] { background-color: #333243; } +#end-game-prompt { + box-shadow: 0 -6px 40px black; +} + #start-game-button, #end-game-button { font-family: 'signika-negative', sans-serif !important; padding: 10px; 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%); + font-size: 25px; } #start-game-button { @@ -358,9 +380,16 @@ 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); } .game-player-name { + position: relative; width: 10em; overflow: hidden; white-space: nowrap; @@ -380,12 +409,27 @@ label[for='moderator'] { transition: background-color, border 0.3s ease-out; text-shadow: 0 3px 4px rgb(0 0 0 / 55%); margin: 5px 0 5px 25px; - min-width: 6em; + width: 117px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.placeholder-button { + font-family: 'signika-negative', sans-serif !important; + padding: 5px; + display: flex; + justify-content: center; + border-radius: 3px; + color: #767676; + font-weight: bold; + font-size: 16px; + border: 2px solid transparent; + text-shadow: 0 3px 4px rgb(0 0 0 / 55%); + margin: 5px 0 5px 25px; + width: 103px; +} + .reveal-role-button { background-color: #3f5256; } @@ -395,11 +439,10 @@ label[for='moderator'] { margin-left: 5px; } -.killed::after { +#game-player-list > .game-player.killed::after { content: '\01F480'; - position: absolute; - right: -44px; font-size: 24px; + margin-left: 1em; } .killed, .killed .game-player-role { @@ -442,6 +485,13 @@ label[for='moderator'] { flex-wrap: wrap; } +#game-player-list { + overflow-y: auto; + overflow-x: hidden; + padding: 0 10px; + max-height: 37em; +} + #game-player-list > div { padding: 2px 10px; border-radius: 3px; @@ -462,14 +512,14 @@ label[for='moderator'] { justify-content: center; } -@media(max-width: 685px) { - #end-game-button { - font-size: 25px; - } +#change-name-modal-background { + cursor: default; +} - #end-game-prompt { - height: 85px; - } +#lobby-people-container , #game-people-container { + background-color: #333243; + padding: 10px 10px 0 10px; + border-radius: 3px; } @keyframes pulse { diff --git a/client/views/create.html b/client/views/create.html index c6f2c0b..4dbbe2d 100644 --- a/client/views/create.html +++ b/client/views/create.html @@ -29,7 +29,7 @@ Home
-
+

Create A Game

-
-
-
-
-
+
+
+
+
+
+
+

Select your method of moderation:

@@ -70,7 +72,6 @@
-
diff --git a/client/views/game.html b/client/views/game.html index 3a12de1..f17d6f1 100644 --- a/client/views/game.html +++ b/client/views/game.html @@ -16,21 +16,33 @@ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Connecting to game...

+
-
-
- -
-
-
-
-
-
+