diff --git a/client/src/modules/game_creation/GameCreationStepManager.js b/client/src/modules/game_creation/GameCreationStepManager.js
index b608d73..3624518 100644
--- a/client/src/modules/game_creation/GameCreationStepManager.js
+++ b/client/src/modules/game_creation/GameCreationStepManager.js
@@ -130,7 +130,8 @@ export class GameCreationStepManager {
this.currentGame.hasTimer,
this.currentGame.hasDedicatedModerator,
this.currentGame.moderatorName,
- this.currentGame.timerParams
+ this.currentGame.timerParams,
+ this.currentGame.isTestGame
)
)
)
@@ -304,6 +305,12 @@ function renderNameStep (containerId, step, game, steps) {
const nameInput = document.querySelector('#moderator-name');
nameInput.value = game.moderatorName;
nameInput.addEventListener('keyup', steps['4'].forwardHandler);
+
+ const testGameInput = document.getElementById('test-game');
+ testGameInput.onchange = (event) => {
+ game.isTestGame = testGameInput.value === 'yes';
+ };
+ testGameInput.value = game.isTestGame ? 'yes' : 'no';
}
function renderModerationTypeStep (game, containerId, stepNumber) {
@@ -390,22 +397,28 @@ function renderReviewAndCreateStep (containerId, stepNumber, game, deckManager)
div.innerHTML =
'
' +
- "
" +
+ "
" +
"
" +
'
' +
'
' +
- "
" +
+ "
" +
+ "
" +
+ '
' +
+ '
' +
+ "
" +
"
" +
'
' +
'
' +
- "
" +
+ "
" +
"
" +
'
' +
'
' +
- "
" +
+ "
" +
"
" +
'
';
+ div.querySelector('#test-game').innerText = game.isTestGame ? 'Yes' : 'No';
+
div.querySelector('#mod-option').innerText = game.hasDedicatedModerator
? "Dedicated Moderator - don't deal me a card."
: 'Temporary Moderator - deal me into the game.';
diff --git a/client/src/modules/game_state/states/Ended.js b/client/src/modules/game_state/states/Ended.js
index 72b03cb..5f67064 100644
--- a/client/src/modules/game_state/states/Ended.js
+++ b/client/src/modules/game_state/states/Ended.js
@@ -28,8 +28,7 @@ export class Ended {
const modType = tempMod ? this.stateBucket.currentGameState.moderator.userType : null;
renderGroupOfPlayers(
this.stateBucket.currentGameState.people.filter(
- p => p.userType === globals.USER_TYPES.PLAYER
- || p.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ p => (p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR)
|| p.killed
),
this.stateBucket.currentGameState.accessCode,
diff --git a/client/src/modules/game_state/states/InProgress.js b/client/src/modules/game_state/states/InProgress.js
index 015ce8b..2d7d59f 100644
--- a/client/src/modules/game_state/states/InProgress.js
+++ b/client/src/modules/game_state/states/InProgress.js
@@ -101,8 +101,7 @@ export class InProgress {
: null;
this.renderGroupOfPlayers(
this.stateBucket.currentGameState.people.filter(
- p => p.userType === globals.USER_TYPES.PLAYER
- || p.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ p => (p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR)
|| p.killed
),
this.killPlayerHandlers,
@@ -168,7 +167,9 @@ export class InProgress {
if (killedPerson) {
killedPerson.out = true;
killedPerson.killed = true;
- killedPerson.userType = globals.USER_TYPES.KILLED_PLAYER;
+ killedPerson.userType = killedPerson.userType === globals.USER_TYPES.BOT
+ ? globals.USER_TYPES.KILLED_BOT
+ : globals.USER_TYPES.KILLED_PLAYER;
if (this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR) {
toast(killedPerson.name + ' killed.', 'success', true, true, 'medium');
this.renderPlayersWithRoleAndAlignmentInfo(this.stateBucket.currentGameState.status === globals.STATUS.ENDED);
@@ -253,14 +254,12 @@ export class InProgress {
});
const teamGood = this.stateBucket.currentGameState.people.filter(
(p) => p.alignment === globals.ALIGNMENT.GOOD
- && (p.userType === globals.USER_TYPES.PLAYER
- || p.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ && ((p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR)
|| p.killed)
);
const teamEvil = this.stateBucket.currentGameState.people.filter((p) => p.alignment === globals.ALIGNMENT.EVIL
- && (p.userType === globals.USER_TYPES.PLAYER
- || p.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ && ((p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR)
|| p.killed)
);
this.renderGroupOfPlayers(
@@ -385,7 +384,8 @@ export class InProgress {
);
if (document.querySelectorAll('.potential-moderator').length === 0) {
- document.getElementById('transfer-mod-modal-content').innerText = 'There is nobody available to transfer to.';
+ document.getElementById('transfer-mod-modal-content').innerText =
+ 'There is nobody available to transfer to. Only spectators or killed players (who are not bots) can be mods.';
}
}
diff --git a/client/src/modules/game_state/states/Lobby.js b/client/src/modules/game_state/states/Lobby.js
index 1e018b7..28eef6f 100644
--- a/client/src/modules/game_state/states/Lobby.js
+++ b/client/src/modules/game_state/states/Lobby.js
@@ -82,7 +82,7 @@ export class Lobby {
const lobbyPlayersContainer = this.container.querySelector('#lobby-players');
const sorted = this.stateBucket.currentGameState.people.sort(
function (a, b) {
- if (a.userType === globals.USER_TYPES.MODERATOR) {
+ if (a.userType === globals.USER_TYPES.MODERATOR || a.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
return -1;
}
return 1;
@@ -92,7 +92,7 @@ export class Lobby {
lobbyPlayersContainer.appendChild(renderLobbyPerson(person.name, person.userType));
}
const playerCount = this.stateBucket.currentGameState.people.filter(
- p => p.userType === globals.USER_TYPES.PLAYER || p.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
+ p => p.userType !== globals.USER_TYPES.MODERATOR && p.userType !== globals.USER_TYPES.SPECTATOR
).length;
document.querySelector("label[for='lobby-players']").innerText =
'Participants (' + playerCount + '/' + this.stateBucket.currentGameState.gameSize + ' Players)';
@@ -194,7 +194,7 @@ function renderLobbyPerson (name, userType) {
personNameEl.innerText = name;
personTypeEl.innerText = userType + globals.USER_TYPE_ICONS[userType];
el.classList.add('lobby-player');
- if (userType === globals.USER_TYPES.MODERATOR) {
+ if (userType === globals.USER_TYPES.MODERATOR || userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
el.classList.add('moderator');
}
diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css
index d08cc91..438887e 100644
--- a/client/src/styles/GLOBAL.css
+++ b/client/src/styles/GLOBAL.css
@@ -209,6 +209,12 @@ a {
text-decoration: none;
}
+.checkbox {
+ border-radius: 5px;
+ border: 2px solid #d7d7d7;
+ padding: 10px;
+}
+
textarea, input {
font-family: 'signika-negative', sans-serif;
font-size: 16px;
@@ -572,7 +578,8 @@ input {
}
.good, .compact-card.good .card-role {
- color: #5f7cfb;
+ color: #5f7cfb !important;
+ font-weight: bold;
}
.good-players, #deck-good {
@@ -584,7 +591,8 @@ input {
}
.evil, .compact-card.evil .card-role {
- color: #e73333;
+ color: #dd2929 !important;
+ font-weight: bold;
}
@keyframes placeholder {
diff --git a/client/src/styles/confirmation.css b/client/src/styles/confirmation.css
index 83e44b7..c75bde2 100644
--- a/client/src/styles/confirmation.css
+++ b/client/src/styles/confirmation.css
@@ -7,7 +7,7 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
- background-color: #2d2c38;
+ background-color: #16141e;
align-items: center;
justify-content: center;
max-width: 25em;
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index 9d3cb2e..e8c4975 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -314,6 +314,15 @@ option {
margin: 0 auto;
}
+#step-4 div:nth-child(2) {
+ margin-top: 25px;
+}
+
+#step-4 label[for="test-game"] {
+ display: block;
+ margin-bottom: 10px;
+}
+
#step-4 input {
padding: 15px 5px;
width: 95%;
diff --git a/client/src/styles/game.css b/client/src/styles/game.css
index 7c6c28f..d65b8aa 100644
--- a/client/src/styles/game.css
+++ b/client/src/styles/game.css
@@ -3,7 +3,7 @@
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
- background-color: #171522;
+ background-color: #000000;
color: #e7e7e7;
padding: 5px;
border-radius: 3px;
@@ -19,6 +19,12 @@
.potential-moderator {
margin: 0.5em auto;
+ border: 2px solid #46455299;
+ background: #4645523b;
+}
+
+#transfer-mod-modal h2 {
+ margin-bottom: 15px;
}
#lobby-players {
@@ -615,14 +621,14 @@ label[for='moderator'] {
canvas {
border-radius: 5px;
- margin: 1em;
+ margin: 20px 5px;
}
.game-player {
border-left: 3px solid #21ba45;
display: flex;
- color: #d7d7d7;
- background-color: #171522;
+ color: #f1f1f1;
+ background-color: #000000;
align-items: center;
padding: 0 5px;
justify-content: space-between;
@@ -714,6 +720,7 @@ canvas {
.game-player-role {
min-width: 7em;
+ color: #bbbbbb;
}
.game-player.killed {
@@ -800,9 +807,10 @@ canvas {
padding: 10px;
border-radius: 5px;
min-height: 25em;
- background-color: #3b3a4a;
+ border: 1px solid #46455299;
+ background: #4645523b;
max-width: 35em;
- min-width: 17em;
+ min-width: 19em;
margin-top: 1em;
}
diff --git a/server/config/globals.js b/server/config/globals.js
index 0595b1e..11af3fa 100644
--- a/server/config/globals.js
+++ b/server/config/globals.js
@@ -100,7 +100,9 @@ const globals = {
PLAYER: 'player',
TEMPORARY_MODERATOR: 'temp mod',
KILLED_PLAYER: 'killed',
- SPECTATOR: 'spectator'
+ KILLED_BOT: 'killed bot',
+ SPECTATOR: 'spectator',
+ BOT: 'bot'
},
ERROR_MESSAGE: {
GAME_IS_FULL: 'This game is full',
diff --git a/server/model/Game.js b/server/model/Game.js
index 6ef153f..03b254b 100644
--- a/server/model/Game.js
+++ b/server/model/Game.js
@@ -9,7 +9,8 @@ class Game {
hasDedicatedModerator,
originalModeratorId,
createTime,
- timerParams = null
+ timerParams = null,
+ isTestGame = false
) {
this.accessCode = accessCode;
this.status = status;
@@ -26,7 +27,7 @@ class Game {
this.previousModeratorId = null;
this.createTime = createTime;
this.timerParams = timerParams;
- this.isFull = this.gameSize === 1 && !this.hasDedicatedModerator;
+ this.isFull = (this.gameSize === 1 && !this.hasDedicatedModerator) || isTestGame;
this.timeRemaining = null;
}
}
diff --git a/server/model/GameCreationRequest.js b/server/model/GameCreationRequest.js
index f41e119..29e20a7 100644
--- a/server/model/GameCreationRequest.js
+++ b/server/model/GameCreationRequest.js
@@ -6,17 +6,19 @@ class GameCreationRequest {
hasTimer,
timerParams,
moderatorName,
- hasDedicatedModerator
+ hasDedicatedModerator,
+ isTestGame
) {
this.deck = deck;
this.hasTimer = hasTimer;
this.timerParams = timerParams;
this.moderatorName = moderatorName;
this.hasDedicatedModerator = hasDedicatedModerator;
+ this.isTestGame = isTestGame;
}
static validate = (gameParams) => {
- const expectedKeys = ['deck', 'hasTimer', 'timerParams', 'moderatorName', 'hasDedicatedModerator'];
+ const expectedKeys = ['deck', 'hasTimer', 'timerParams', 'moderatorName', 'hasDedicatedModerator', 'isTestGame'];
if (gameParams === null
|| typeof gameParams !== 'object'
|| expectedKeys.some((key) => !Object.keys(gameParams).includes(key))
@@ -31,6 +33,7 @@ class GameCreationRequest {
function valid (gameParams) {
return typeof gameParams.hasTimer === 'boolean'
+ && typeof gameParams.isTestGame === 'boolean'
&& typeof gameParams.hasDedicatedModerator === 'boolean'
&& typeof gameParams.moderatorName === 'string'
&& gameParams.moderatorName.length > 0
diff --git a/server/modules/Events.js b/server/modules/Events.js
index 1d88d72..2254097 100644
--- a/server/modules/Events.js
+++ b/server/modules/Events.js
@@ -86,7 +86,9 @@ const Events = [
stateChange: async (game, socketArgs, vars) => {
const person = game.people.find((person) => person.id === socketArgs.personId);
if (person && !person.out) {
- person.userType = globals.USER_TYPES.KILLED_PLAYER;
+ person.userType = person.userType === globals.USER_TYPES.BOT
+ ? globals.USER_TYPES.KILLED_BOT
+ : globals.USER_TYPES.KILLED_PLAYER;
person.out = true;
person.killed = true;
}
diff --git a/server/modules/singletons/GameManager.js b/server/modules/singletons/GameManager.js
index 2aa1cc5..4742284 100644
--- a/server/modules/singletons/GameManager.js
+++ b/server/modules/singletons/GameManager.js
@@ -59,8 +59,10 @@ class GameManager {
gameParams.hasTimer,
gameParams.timerParams,
gameParams.moderatorName,
- gameParams.hasDedicatedModerator
+ gameParams.hasDedicatedModerator,
+ gameParams.isTestGame
);
+ console.log(req.isTestGame);
const newAccessCode = await this.generateAccessCode(globals.ACCESS_CODE_CHAR_POOL);
if (newAccessCode === null) {
return Promise.reject(globals.ERROR_MESSAGE.NO_UNIQUE_ACCESS_CODE);
@@ -76,14 +78,15 @@ class GameManager {
const newGame = new Game(
newAccessCode,
globals.STATUS.LOBBY,
- initializePeopleForGame(req.deck, moderator, this.shuffle),
+ initializePeopleForGame(req.deck, moderator, this.shuffle, req.isTestGame),
req.deck,
req.hasTimer,
moderator.id,
req.hasDedicatedModerator,
moderator.id,
new Date().toJSON(),
- req.timerParams
+ req.timerParams,
+ req.isTestGame
);
await this.eventManager.publisher.set(newAccessCode, JSON.stringify(newGame), {
EX: globals.STALE_GAME_SECONDS
@@ -242,6 +245,10 @@ class GameManager {
game.people[i].userType = globals.USER_TYPES.PLAYER;
game.people[i].out = false;
}
+ if (game.people[i].userType === globals.USER_TYPES.KILLED_BOT) {
+ game.people[i].userType = globals.USER_TYPES.BOT;
+ game.people[i].out = false;
+ }
game.people[i].revealed = false;
game.people[i].killed = false;
if (game.people[i].gameRole) {
@@ -314,7 +321,7 @@ function initializeModerator (name, hasDedicatedModerator) {
return new Person(createRandomId(), createRandomId(), name, userType);
}
-function initializePeopleForGame (uniqueRoles, moderator, shuffle) {
+function initializePeopleForGame (uniqueRoles, moderator, shuffle, isTestGame) {
const people = [];
const cards = [];
@@ -335,10 +342,11 @@ function initializePeopleForGame (uniqueRoles, moderator, shuffle) {
createRandomId(),
createRandomId(),
UsernameGenerator.generate(),
- globals.USER_TYPES.PLAYER,
+ isTestGame ? globals.USER_TYPES.BOT : globals.USER_TYPES.PLAYER,
cards[j].role,
cards[j].description,
- cards[j].team
+ cards[j].team,
+ isTestGame
);
person.customRole = cards[j].custom;
person.hasEnteredName = false;