play and pause button, more improved lobby view

This commit is contained in:
Alec
2021-12-01 15:07:24 -05:00
parent 7f86aa22aa
commit 27dfd55cda
14 changed files with 222 additions and 130 deletions

View File

@@ -35,8 +35,8 @@ export const globals = {
PRODUCTION: "production"
},
USER_TYPE_ICONS: {
PLAYER: ' \uD83C\uDFAE',
MODERATOR: ' \uD83D\uDC51',
TEMP_MOD: ' \uD83C\uDFAE\uD83D\uDC51'
player: ' \uD83C\uDFAE',
moderator: ' \uD83D\uDC51',
'player / temp mod': ' \uD83C\uDFAE\uD83D\uDC51'
}
};

View File

@@ -0,0 +1,8 @@
<svg width="263" height="271" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<ellipse stroke="#d7d7d7" ry="131" rx="126" id="svg_5" fill="none" cy="135.237498" cx="131.999999" stroke-opacity="null" stroke-width="8"/>
<rect stroke="#d7d7d7" id="svg_1" height="123.000006" width="41" y="73.737494" x="77.5" stroke-width="0" fill="#d7d7d7"/>
<rect stroke="#d7d7d7" id="svg_3" height="123.000006" width="41" y="73.737494" x="144.5" stroke-width="0" fill="#d7d7d7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@@ -0,0 +1,7 @@
<svg width="263" height="271" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<ellipse stroke="#d7d7d7" ry="131" rx="126" id="svg_5" cy="135.237498" cx="129.999999" fill-opacity="null" stroke-opacity="null" stroke-width="8" fill="none"/>
<path transform="rotate(90.18392181396484 140.08586120605474,135.38354492187497) " stroke="#7d0b0b" id="svg_7" d="m86.585877,180.883554l53.499995,-91.000007l53.499995,91.000007l-106.99999,0z" fill-opacity="null" stroke-opacity="null" stroke-width="0" fill="#d7d7d7"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 541 B

View File

@@ -10,33 +10,25 @@ export class GameStateRenderer {
renderLobbyPlayers() {
document.querySelectorAll('.lobby-player').forEach((el) => el.remove())
let lobbyPlayersContainer = document.getElementById("lobby-players");
if (this.gameState.userType !== globals.USER_TYPES.MODERATOR) {
let modEl = document.createElement("div");
modEl.innerText = this.gameState.moderator.name;
modEl.classList.add('lobby-player');
lobbyPlayersContainer.appendChild(modEl);
if (this.gameState.client.userType === globals.USER_TYPES.PLAYER) {
lobbyPlayersContainer.appendChild(renderLobbyPerson(this.gameState.moderator.name, this.gameState.moderator.userType))
}
for (let person of this.gameState.people) {
let personEl = document.createElement("div");
personEl.innerText = person.name;
personEl.classList.add('lobby-player');
lobbyPlayersContainer.appendChild(personEl);
lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name,person.userType))
}
let playerCount;
if (this.gameState.userType === globals.USER_TYPES.MODERATOR) {
playerCount = this.gameState.people.length;
} else {
playerCount = 1 + this.gameState.people.length;
let playerCount = this.gameState.people.filter((person) => person.userType === globals.USER_TYPES.PLAYER).length;
if (this.gameState.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
playerCount += 1;
}
if (this.gameState.client.userType === globals.USER_TYPES.PLAYER) {
playerCount += 1;
}
document.querySelector("label[for='lobby-players']").innerText =
"Other People ( " + playerCount + " )";
"Other People (" + playerCount + "/" + getGameSize(this.gameState.deck) + " Players)";
}
renderLobbyHeader() {
let existingTitle = document.querySelector('#game-link h1');
if (existingTitle) {
existingTitle.remove();
}
removeExistingTitle();
let title = document.createElement("h1");
title.innerText = "Lobby";
document.getElementById("game-title").appendChild(title);
@@ -62,9 +54,9 @@ export class GameStateRenderer {
}
renderGameHeader() {
removeExistingTitle();
// let title = document.createElement("h1");
// title.innerText = "Game";
// document.querySelector('#game-title h1')?.remove();
// document.getElementById("game-title").appendChild(title);
}
@@ -99,13 +91,18 @@ export class GameStateRenderer {
}
}
function renderLobbyPlayer(name, userType) {
function renderLobbyPerson(name, userType) {
let el = document.createElement("div");
el.innerHTML = ""
clientEl.innerText = client.name + ' (you)';
clientEl.classList.add('lobby-player');
clientEl.classList.add('lobby-player-client');
container.prepend(clientEl);
let personNameEl = document.createElement("div");
let personTypeEl = document.createElement("div");
personNameEl.innerText = name;
personTypeEl.innerText = userType + globals.USER_TYPE_ICONS[userType];
el.classList.add('lobby-player');
el.appendChild(personNameEl);
el.appendChild(personTypeEl);
return el;
}
function getGameSize(cards) {
@@ -116,3 +113,10 @@ function getGameSize(cards) {
return quantity;
}
function removeExistingTitle() {
let existingTitle = document.querySelector('#game-title h1');
if (existingTitle) {
existingTitle.remove();
}
}

View File

@@ -1,24 +1,34 @@
import {globals} from "../config/globals.js";
export class GameTimerManager {
constructor() {
}
startGameTimer (hours, minutes, tickRate, soundManager, timerWorker) {
if (window.Worker) {
timerWorker.onmessage = function (e) {
if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) {
document.getElementById('game-timer').innerText = e.data.displayTime;
}
};
const totalTime = convertFromHoursToMilliseconds(hours) + convertFromMinutesToMilliseconds(minutes);
timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate });
constructor(gameState, socket) {
this.gameState = gameState;
this.playListener = () => {
socket.emit(globals.COMMANDS.RESUME_TIMER, this.gameState.accessCode);
}
this.pauseListener = () => {
socket.emit(globals.COMMANDS.PAUSE_TIMER, this.gameState.accessCode);
}
}
// startGameTimer (hours, minutes, tickRate, soundManager, timerWorker) {
// if (window.Worker) {
// timerWorker.onmessage = function (e) {
// if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) {
// document.getElementById('game-timer').innerText = e.data.displayTime;
// }
// };
// const totalTime = convertFromHoursToMilliseconds(hours) + convertFromMinutesToMilliseconds(minutes);
// timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate });
// }
// }
resumeGameTimer(totalTime, tickRate, soundManager, timerWorker) {
if (window.Worker) {
if (this.gameState.client.userType !== globals.USER_TYPES.PLAYER) {
this.swapToPauseButton();
}
let timer = document.getElementById('game-timer');
timer.classList.remove('paused');
timer.innerText = totalTime < 60000
@@ -35,6 +45,10 @@ export class GameTimerManager {
pauseGameTimer(timerWorker, timeRemaining) {
if (window.Worker) {
if (this.gameState.client.userType !== globals.USER_TYPES.PLAYER) {
this.swapToPlayButton();
}
timerWorker.postMessage('stop');
let timer = document.getElementById('game-timer');
timer.innerText = timeRemaining < 60000
@@ -45,6 +59,10 @@ export class GameTimerManager {
}
displayPausedTime(time) {
if (this.gameState.client.userType !== globals.USER_TYPES.PLAYER) {
this.swapToPlayButton();
}
let timer = document.getElementById('game-timer');
timer.innerText = time < 60000
? returnHumanReadableTime(time, true)
@@ -53,17 +71,17 @@ export class GameTimerManager {
}
attachTimerSocketListeners(socket, timerWorker, gameStateRenderer) {
if (!socket.hasListeners(globals.EVENTS.START_TIMER)) {
socket.on(globals.EVENTS.START_TIMER, () => {
this.startGameTimer(
gameStateRenderer.gameState.timerParams.hours,
gameStateRenderer.gameState.timerParams.minutes,
globals.CLOCK_TICK_INTERVAL_MILLIS,
null,
timerWorker
)
});
}
// if (!socket.hasListeners(globals.EVENTS.START_TIMER)) {
// socket.on(globals.EVENTS.START_TIMER, () => {
// this.startGameTimer(
// gameStateRenderer.gameState.timerParams.hours,
// gameStateRenderer.gameState.timerParams.minutes,
// globals.CLOCK_TICK_INTERVAL_MILLIS,
// null,
// timerWorker
// )
// });
// }
if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) {
socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => {
@@ -88,6 +106,32 @@ export class GameTimerManager {
});
}
}
swapToPlayButton() {
let currentBtn = document.querySelector('#play-pause img');
if (currentBtn) {
currentBtn.removeEventListener('click', this.pauseListener);
currentBtn.remove();
}
let playBtn = document.createElement('img');
playBtn.setAttribute('src', '../images/play-button.svg');
playBtn.addEventListener('click', this.playListener);
document.getElementById('play-pause').appendChild(playBtn);
}
swapToPauseButton() {
let currentBtn = document.querySelector('#play-pause img');
if (currentBtn) {
currentBtn.removeEventListener('click', this.playListener);
currentBtn.remove();
}
let pauseBtn = document.createElement('img');
pauseBtn.setAttribute('src', '../images/pause-button.svg');
pauseBtn.addEventListener('click', this.pauseListener);
document.getElementById('play-pause').appendChild(pauseBtn);
}
}

View File

@@ -22,7 +22,6 @@ export const templates = {
"<button id='start-game-button'>Start Game</button>" +
"</div>",
GAME:
"<div id='person-name'></div>" +
"<div id='game-header'>" +
"<div>" +
"<label for='game-timer'>Time Remaining</label>" +
@@ -43,16 +42,15 @@ export const templates = {
"<p>(click again to hide)</p>" +
"</div>",
MODERATOR_GAME_VIEW:
"<div id='person-name'></div>" +
"<h2 class='user-type user-type-moderator'>Moderator</h2>" +
"<div id='game-header'>" +
"<div class='timer-container-moderator'>" +
"<label for='game-timer'>Time Remaining</label>" +
"<div id='game-timer'></div>" +
"</div>" +
"<div id='play-pause'>" +
"<button id='pause-button'>Pause</button>" +
"<button id='play-button'>Play</button>" +
"<div>" +
"<label for='game-timer'>Time Remaining</label>" +
"<div id='game-timer'></div>" +
"</div>" +
"<div id='play-pause'>" +
"</div>" +
"</div>" +
"<div>" +
"<label for='alive-count'>Players Left</label>" +

View File

@@ -35,10 +35,10 @@ function prepareGamePage(environment, socket, timerWorker) {
let gameStateRenderer = new GameStateRenderer(gameState);
let gameTimerManager;
if (gameState.timerParams) {
gameTimerManager = new GameTimerManager();
gameTimerManager = new GameTimerManager(gameState, socket);
}
setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager);
displayClientInfo(gameState.client.name, gameState.userType);
displayClientInfo(gameState.client.name, gameState.client.userType);
processGameState(gameState, userId, socket, gameStateRenderer);
}
});
@@ -56,8 +56,8 @@ function processGameState (gameState, userId, socket, gameStateRenderer) {
if (
gameState.isFull
&& (
gameState.userType === globals.USER_TYPES.MODERATOR
|| gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
gameState.client.userType === globals.USER_TYPES.MODERATOR
|| gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
)
) {
displayStartGamePromptForModerators(gameStateRenderer, socket);
@@ -67,21 +67,14 @@ function processGameState (gameState, userId, socket, gameStateRenderer) {
document.querySelector("#start-game-prompt")?.remove();
gameStateRenderer.gameState = gameState;
gameStateRenderer.renderGameHeader();
if (gameState.userType === globals.USER_TYPES.PLAYER || gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
if (gameState.client.userType === globals.USER_TYPES.PLAYER || gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
document.getElementById("game-state-container").innerHTML = templates.GAME;
gameStateRenderer.renderPlayerRole();
} else if (gameState.userType === globals.USER_TYPES.MODERATOR) {
} else if (gameState.client.userType === globals.USER_TYPES.MODERATOR) {
document.getElementById("game-state-container").innerHTML = templates.MODERATOR_GAME_VIEW;
gameStateRenderer.renderModeratorView();
console.log(gameState);
console.log(gameState.accessCode);
document.getElementById("pause-button").addEventListener('click', () => {
socket.emit(globals.COMMANDS.PAUSE_TIMER, gameState.accessCode);
});
document.getElementById("play-button").addEventListener('click', () => {
socket.emit(globals.COMMANDS.RESUME_TIMER, gameState.accessCode);
})
}
socket.emit(globals.COMMANDS.GET_TIME_REMAINING, gameState.accessCode);
break;
default:
break;
@@ -91,14 +84,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer) {
function displayClientInfo(name, userType) {
document.getElementById("client-name").innerText = name;
document.getElementById("client-user-type").innerText = userType;
if (userType === globals.USER_TYPES.MODERATOR) {
document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS.MODERATOR;
} else if (userType === globals.USER_TYPES.PLAYER) {
document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS.PLAYER;
} else if (userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS.TEMP_MOD;
}
document.getElementById("client-user-type").innerText += globals.USER_TYPE_ICONS[userType];
}
function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager) {
@@ -110,8 +96,8 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTim
if (
gameIsFull
&& (
gameStateRenderer.gameState.userType === globals.USER_TYPES.MODERATOR
|| gameStateRenderer.gameState.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
gameStateRenderer.gameState.client.userType === globals.USER_TYPES.MODERATOR
|| gameStateRenderer.gameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
)
) {
displayStartGamePromptForModerators(gameStateRenderer, socket);

View File

@@ -57,7 +57,7 @@ h3 {
label {
color: #d7d7d7;
font-family: 'signika-negative', sans-serif;
font-size: 18px;
font-size: 20px;
font-weight: normal;
}

View File

@@ -1,4 +1,8 @@
.lobby-player, #moderator {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
background-color: black;
color: whitesmoke;
padding: 10px;
@@ -14,6 +18,10 @@
border: 2px solid #21ba45;
}
.lobby-player div:nth-child(2) {
color: #21ba45;
}
#moderator.moderator-client {
border: 2px solid lightgray;
}
@@ -182,7 +190,8 @@ h1 {
}
#client-container {
max-width: 30em;
max-width: 35em;
margin-top: 2em;
}
#client {
@@ -198,7 +207,7 @@ h1 {
#client-name {
color: whitesmoke;
font-family: 'diavlo', sans-serif;
font-size: 37px;
font-size: 30px;
}
#client-user-type {
@@ -219,13 +228,6 @@ label[for='moderator'] {
font-size: 30px;
}
label[for='lobby-players'] {
font-family: 'diavlo', sans-serif;
color: #21ba45;
filter: drop-shadow(2px 2px 4px black);
font-size: 30px;
}
#start-game-prompt {
display: flex;
align-items: center;
@@ -268,10 +270,46 @@ label[for='lobby-players'] {
border: 2px solid #1c8a36;
}
#play-pause {
display: flex;
align-items: center;
}
#play-pause img {
cursor: pointer;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
width: 60px;
}
#play-pause img:hover {
filter: brightness(0.5);
}
#play-pause img:active {
transform: scale(0.95);
}
.paused {
animation: pulse 0.75s linear infinite alternate;
}
#game-header {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-evenly;
}
.timer-container-moderator {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
@keyframes pulse {
from {
color: rgba(255, 255, 255, 0.1);

View File

@@ -7,7 +7,8 @@ const globals = {
GET_ENVIRONMENT: 'getEnvironment',
START_GAME: 'startGame',
PAUSE_TIMER: 'pauseTimer',
RESUME_TIMER: 'resumeTimer'
RESUME_TIMER: 'resumeTimer',
GET_TIME_REMAINING: 'getTimeRemaining'
},
STATUS: {
LOBBY: "lobby",

View File

@@ -60,7 +60,7 @@ class ActiveGameRunner {
minutes: game.timerParams.minutes
});
game.startTime = new Date().toJSON();
namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.START_TIMER);
//namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.START_TIMER);
}
}

View File

@@ -39,6 +39,7 @@ class GameManager {
game.status = globals.STATUS.IN_PROGRESS;
namespace.in(accessCode).emit(globals.EVENTS.SYNC_GAME_STATE);
if (game.hasTimer) {
game.timerParams.paused = true;
this.activeGameRunner.runGame(game, namespace);
}
}
@@ -58,7 +59,7 @@ class GameManager {
});
}
}
})
});
socket.on(globals.CLIENT_COMMANDS.RESUME_TIMER, (accessCode) => {
this.logger.trace(accessCode);
@@ -74,7 +75,22 @@ class GameManager {
});
}
}
})
});
socket.on(globals.CLIENT_COMMANDS.GET_TIME_REMAINING, (accessCode) => {
let game = this.activeGameRunner.activeGames[accessCode];
if (game) {
let thread = this.activeGameRunner.timerThreads[accessCode];
if (thread) {
thread.send({
command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING,
accessCode: accessCode,
socketId: socket.id,
logLevel: this.logger.logLevel
});
}
}
});
}
@@ -250,7 +266,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe
game.isFull = isFull;
socket.to(accessCode).emit(
globals.EVENTS.PLAYER_JOINED,
{name: unassignedPerson.name},
{name: unassignedPerson.name, userType: unassignedPerson.userType},
isFull
);
} else {

View File

@@ -2,19 +2,17 @@ const globals = require("../config/globals")
const GameStateCurator = {
getGameStateFromPerspectiveOfPerson: (game, person, gameRunner, socket, logger) => {
if (game.timerParams && game.status === globals.STATUS.IN_PROGRESS) {
getTimeRemaining(game.accessCode, gameRunner, socket, logger)
}
return getGameStateBasedOnPermissions(game, person, gameRunner);
}
}
function getGameStateBasedOnPermissions(game, person, gameRunner) {
let client = game.status === globals.STATUS.LOBBY // people won't be able to know their role until past the lobby stage.
? { name: person.name, id: person.id }
? { name: person.name, id: person.id, userType: person.userType }
: {
name: person.name,
id: person.id,
userType: person.userType,
gameRole: person.gameRole,
gameRoleDescription: person.gameRoleDescription,
alignment: person.alignment
@@ -25,12 +23,12 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
accessCode: game.accessCode,
status: game.status,
moderator: mapPerson(game.moderator),
userType: globals.USER_TYPES.PLAYER,
client: client,
deck: game.deck,
people: game.people
.filter((person) => {
return person.assigned === true && person.id !== client.id && person.userType !== globals.USER_TYPES.MODERATOR
return person.assigned === true && person.id !== client.id
&& (person.userType !== globals.USER_TYPES.MODERATOR && person.userType !== globals.USER_TYPES.TEMPORARY_MODERATOR)
})
.map((filteredPerson) => ({ name: filteredPerson.name, userType: filteredPerson.userType })),
timerParams: game.timerParams,
@@ -41,7 +39,6 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
accessCode: game.accessCode,
status: game.status,
moderator: mapPerson(game.moderator),
userType: globals.USER_TYPES.MODERATOR,
client: client,
deck: game.deck,
people: mapPeopleForModerator(game.people, client),
@@ -53,7 +50,6 @@ function getGameStateBasedOnPermissions(game, person, gameRunner) {
accessCode: game.accessCode,
status: game.status,
moderator: mapPerson(game.moderator),
userType: globals.USER_TYPES.TEMPORARY_MODERATOR,
client: client,
deck: game.deck,
people: mapPeopleForTempModerator(game.people, client),
@@ -72,6 +68,7 @@ function mapPeopleForModerator(people, client) {
})
.map((person) => ({
name: person.name,
userType: person.userType,
gameRole: person.gameRole,
gameRoleDescription: person.gameRoleDescription,
alignment: person.alignment
@@ -85,23 +82,12 @@ function mapPeopleForTempModerator(people, client) {
})
.map((person) => ({
name: person.name,
userType: person.userType
}));
}
function mapPerson(person) {
return { name: person.name };
}
function getTimeRemaining(accessCode, gameRunner, socket, logger) {
let thread = gameRunner.timerThreads[accessCode];
if (thread) {
thread.send({
command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING,
accessCode: accessCode,
socketId: socket.id,
logLevel: logger.logLevel
});
}
return { name: person.name, userType: person.userType };
}
module.exports = GameStateCurator;

View File

@@ -36,7 +36,7 @@ class ServerTimer {
this.totalTime = null;
}
runTimer () {
runTimer (pausedInitially=true) {
let total = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes);
this.totalTime = total;
this.currentTimeInMillis = total;
@@ -47,18 +47,22 @@ class ServerTimer {
this.timesUpResolver = resolve;
});
const instance = this;
this.ticking = setTimeout(function () {
stepFn(
instance,
expected
);
}, this.tickInterval);
if (!pausedInitially) {
this.ticking = setTimeout(function () {
stepFn(
instance,
expected
);
}, this.tickInterval);
}
return this.timesUpPromise;
}
stopTimer() {
clearTimeout(this.ticking);
if (this.ticking) {
clearTimeout(this.ticking);
}
let now = Date.now();
this.logger.debug(
'ELAPSED (PAUSE): ' + (now - this.start) + 'ms (~'