From 41330c5e7f93cb79009eaa5e90efaa077a07319b Mon Sep 17 00:00:00 2001
From: Alec
Date: Thu, 12 May 2022 18:17:27 -0400
Subject: [PATCH 1/9] Update README.md
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3029d22..c5970c6 100644
--- a/README.md
+++ b/README.md
@@ -41,8 +41,10 @@ The application prioritizes responsiveness. A key scenario would be when a group
This is a Node.js application. It is written purely using JavaScript/HTML/CSS. The main dependencies are
Express.js and Socket.io . It runs as a containerized application
-via Google Cloud Run .
+via Google Cloud Run . There is no data persisted in any database.
+Currently there is one container instance, which is sufficient scaling at this time. In the event I need to scale to multiple containers, I will likely
+integrate with a message queue like Redis .
## Contributing and Developers' Guide
### Running Locally
From 0e46309157c958744a066d35d1e7d9c67e748f8a Mon Sep 17 00:00:00 2001
From: Alec
Date: Thu, 12 May 2022 18:40:37 -0400
Subject: [PATCH 2/9] Update README.md
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index c5970c6..932173e 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,12 @@
+- [Features](https://github.com/AlecM33/Werewolf#features)
+- [Tech Stack]
+- [Contributing and Developers' Guide]
+- [Testing]
+- [Code Formatting]
+
[](https://github.com/AlecM33/Werewolf/actions/workflows/node.js.yml)
Find the latest production deployment at: https://play-werewolf.app/
From aacd7d241c28c531fc63fbbb0facdae3e42c99c4 Mon Sep 17 00:00:00 2001
From: Alec
Date: Thu, 12 May 2022 18:41:21 -0400
Subject: [PATCH 3/9] Update README.md
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 932173e..13b89a6 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,10 @@
- [Features](https://github.com/AlecM33/Werewolf#features)
-- [Tech Stack]
-- [Contributing and Developers' Guide]
-- [Testing]
-- [Code Formatting]
+- [Tech Stack](https://github.com/AlecM33/Werewolf/blob/master/README.md#tech-stack)
+- [Contributing and Developers' Guide](https://github.com/AlecM33/Werewolf/blob/master/README.md#contributing-and-developers-guide)
+- [Testing](https://github.com/AlecM33/Werewolf/blob/master/README.md#testing)
+- [Code Formatting](https://github.com/AlecM33/Werewolf/blob/master/README.md#code-formatting)
[](https://github.com/AlecM33/Werewolf/actions/workflows/node.js.yml)
From 125272807a58a14ce3f8539df80259c12b3d0927 Mon Sep 17 00:00:00 2001
From: Alec
Date: Thu, 12 May 2022 18:42:20 -0400
Subject: [PATCH 4/9] Update README.md
---
README.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index 13b89a6..2875c64 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@
-- [Features](https://github.com/AlecM33/Werewolf#features)
-- [Tech Stack](https://github.com/AlecM33/Werewolf/blob/master/README.md#tech-stack)
-- [Contributing and Developers' Guide](https://github.com/AlecM33/Werewolf/blob/master/README.md#contributing-and-developers-guide)
-- [Testing](https://github.com/AlecM33/Werewolf/blob/master/README.md#testing)
-- [Code Formatting](https://github.com/AlecM33/Werewolf/blob/master/README.md#code-formatting)
+- [Features](#features)
+- [Tech Stack](#tech-stack)
+- [Contributing and Developers' Guide](#contributing-and-developers-guide)
+- [Testing](#testing)
+- [Code Formatting](#code-formatting)
[](https://github.com/AlecM33/Werewolf/actions/workflows/node.js.yml)
From 76d10fea489324700f2673a1b65b528003375f7d Mon Sep 17 00:00:00 2001
From: Alec
Date: Thu, 12 May 2022 18:42:59 -0400
Subject: [PATCH 5/9] Update README.md
---
README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 2875c64..bb80aef 100644
--- a/README.md
+++ b/README.md
@@ -2,16 +2,16 @@
+[](https://github.com/AlecM33/Werewolf/actions/workflows/node.js.yml)
+
+Find the latest production deployment at: https://play-werewolf.app/
+
- [Features](#features)
- [Tech Stack](#tech-stack)
- [Contributing and Developers' Guide](#contributing-and-developers-guide)
- [Testing](#testing)
- [Code Formatting](#code-formatting)
-[](https://github.com/AlecM33/Werewolf/actions/workflows/node.js.yml)
-
-Find the latest production deployment at: https://play-werewolf.app/
-
An application to run games of Werewolf (Mafia)
smoothly when you don't have a deck, or when you and your friends are together virtually. Basically, a host builds a game and deals a role to everyone's device, and then the app keeps track of the game state (timer, player statuses, etc).
From ff1b4e0ad8d4c748e983f7708c31ac337d6b8361 Mon Sep 17 00:00:00 2001
From: AlecM33
Date: Sun, 12 Jun 2022 13:54:13 -0400
Subject: [PATCH 6/9] tweak end of game page styling
---
client/src/modules/HTMLFragments.js | 4 +---
client/src/styles/game.css | 10 +++++++++-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/HTMLFragments.js
index e91a2d5..7ed5a29 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/HTMLFragments.js
@@ -226,9 +226,7 @@ export const HTMLFragments = {
`,
RESTART_GAME_BUTTON:
- `
- Run it back 🔄
-
`,
+ `Run it back 🔄 `,
CREATE_GAME_DECK:
`
diff --git a/client/src/styles/game.css b/client/src/styles/game.css
index 8cb87c1..b3d187f 100644
--- a/client/src/styles/game.css
+++ b/client/src/styles/game.css
@@ -104,9 +104,17 @@ h1 {
align-items: center;
}
+#end-of-game-header h2 {
+ border: 1px solid #333243;
+ border-radius: 5px;
+ background-color: #1a1726;
+ padding: 7px;
+ margin: 0.5em;
+}
+
#end-of-game-header button {
margin: 0.5em;
- min-width: 10em;
+ min-width: 12em;
}
.potential-moderator {
display: flex;
From 43344b217ab6341e5a1e63dc20bd2ebf869c3a2a Mon Sep 17 00:00:00 2001
From: AlecM33
Date: Sun, 12 Jun 2022 13:58:37 -0400
Subject: [PATCH 7/9] lint
---
client/src/modules/HTMLFragments.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/HTMLFragments.js
index 7ed5a29..2dd103b 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/HTMLFragments.js
@@ -226,7 +226,7 @@ export const HTMLFragments = {
`,
RESTART_GAME_BUTTON:
- `
Run it back 🔄 `,
+ '
Run it back 🔄 ',
CREATE_GAME_DECK:
`
From 99a5455713c4a6e5021600374f80e3f00facf0a5 Mon Sep 17 00:00:00 2001
From: AlecM33
Date: Sun, 19 Jun 2022 15:32:12 -0400
Subject: [PATCH 8/9] Deck templates
---
client/src/modules/DeckStateManager.js | 75 ++++++++++++++++++-
client/src/modules/GameCreationStepManager.js | 11 +++
client/src/modules/GameStateRenderer.js | 4 +-
client/src/modules/HTMLFragments.js | 10 ++-
client/src/modules/RoleBox.js | 5 +-
client/src/scripts/game.js | 2 +-
client/src/styles/create.css | 51 ++++++++++++-
client/src/styles/game.css | 35 ++++-----
client/src/styles/modal.css | 2 +-
client/src/view_templates/CreateTemplate.js | 7 ++
spec/e2e/create_spec.js | 9 +++
11 files changed, 182 insertions(+), 29 deletions(-)
diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js
index 23a79bf..dede3bc 100644
--- a/client/src/modules/DeckStateManager.js
+++ b/client/src/modules/DeckStateManager.js
@@ -1,10 +1,43 @@
import { globals } from '../config/globals.js';
import { HTMLFragments } from './HTMLFragments.js';
import { toast } from './Toast.js';
+import {ModalManager} from "./ModalManager.js";
export class DeckStateManager {
constructor () {
this.deck = [];
+ this.templates = {
+ '5 Players': {
+ 'Villager': 1,
+ 'Werewolf': 1,
+ 'Sorceress': 1,
+ 'Parity Hunter': 1,
+ 'Seer': 1
+ },
+ '7 Players': {
+ 'Villager': 6,
+ 'Werewolf': 1
+ },
+ '9 Players': {
+ 'Villager': 7,
+ 'Werewolf': 2
+ },
+ '11 Players': {
+ 'Villager': 8,
+ 'Werewolf': 2,
+ 'Seer': 1
+ },
+ '13 Players': {
+ 'Villager': 10,
+ 'Werewolf': 2,
+ 'Seer': 1
+ },
+ '15 Players': {
+ 'Villager': 12,
+ 'Werewolf': 2,
+ 'Seer': 1
+ }
+ }
}
addToDeck (role) {
@@ -54,6 +87,46 @@ export class DeckStateManager {
return total;
}
+ loadDeckTemplates = (roleBox) => {
+ if (document.querySelectorAll('.template-option').length === 0) {
+ for (let templateName of Object.keys(this.templates)) {
+ let templateOption = document.createElement('div');
+ templateOption.classList.add('template-option');
+ templateOption.innerHTML = HTMLFragments.DECK_TEMPLATE;
+ templateOption.querySelector('.template-option-name').innerText = templateName;
+ for (let i = 0; i < Object.keys(this.templates[templateName]).length; i++) {
+ let role = Object.keys(this.templates[templateName])[i];
+ let roleEl = document.createElement('span');
+ roleEl.innerText = this.templates[templateName][role] + ' ' + role;
+ if (i < Object.keys(this.templates[templateName]).length - 1) { // construct comma-delimited list
+ roleEl.innerText += ', ';
+ }
+ roleEl.classList.add(roleBox.defaultRoles.find((entry) => entry.role === role).team);
+ templateOption.querySelector('.template-option-roles').appendChild(roleEl);
+ }
+ templateOption.addEventListener('click', (e) => {
+ e.preventDefault();
+ for (let card of this.deck) {
+ card.quantity = 0;
+ }
+ for (let role of Object.keys(this.templates[templateName])) {
+ let roleObj = roleBox.getDefaultRole(role);
+ if (!this.hasRole(roleObj.role)) {
+ this.addToDeck(roleObj);
+ }
+ for (let i = roleObj.quantity; i < this.templates[templateName][role]; i++) {
+ this.addCopyOfCard(roleObj.role);
+ }
+ }
+ this.updateDeckStatus();
+ ModalManager.dispelModal('deck-template-modal', 'modal-background');
+ toast('Template loaded', 'success', true, true, 'short');
+ });
+ document.getElementById('deck-template-container').appendChild(templateOption);
+ }
+ }
+ }
+
displayDeckPlaceHolder = () => {
const placeholder = document.createElement('div');
placeholder.setAttribute('id', 'deck-list-placeholder');
@@ -69,7 +142,7 @@ export class DeckStateManager {
}
const sortedDeck = this.deck.sort((a, b) => {
if (a.team !== b.team) {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
+ return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
}
return a.role.localeCompare(b.role);
});
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js
index bc26b5b..1eb9399 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/GameCreationStepManager.js
@@ -198,6 +198,7 @@ export class GameCreationStepManager {
}
renderRoleSelectionStep = (game, containerId, step, deckManager) => {
+
const stepContainer = document.createElement('div');
setAttributes(stepContainer, { id: 'step-' + step, class: 'flex-row-container-left-align step' });
@@ -205,11 +206,14 @@ export class GameCreationStepManager {
stepContainer.innerHTML += HTMLFragments.CREATE_GAME_DECK_STATUS;
document.getElementById(containerId).appendChild(stepContainer);
+
this.roleBox = new RoleBox(stepContainer, deckManager);
this.roleBox.loadDefaultRoles();
this.roleBox.loadCustomRolesFromCookies();
this.roleBox.displayDefaultRoles(document.getElementById('role-select'));
+ deckManager.loadDeckTemplates(this.roleBox);
+
const exportHandler = (e) => {
if (e.type === 'click' || e.code === 'Enter') {
e.preventDefault();
@@ -502,6 +506,13 @@ function initializeRemainingEventListeners (deckManager, roleBox) {
}
}
};
+ document.getElementById('deck-template-button').addEventListener('click', () => {
+ ModalManager.displayModal(
+ 'deck-template-modal',
+ 'modal-background',
+ 'close-deck-template-modal-button'
+ );
+ });
document.getElementById('custom-role-btn').addEventListener(
'click', () => {
const createBtn = document.getElementById('create-role-button');
diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js
index 1ae1bdd..4538989 100644
--- a/client/src/modules/GameStateRenderer.js
+++ b/client/src/modules/GameStateRenderer.js
@@ -234,7 +234,9 @@ export class GameStateRenderer {
});
}
document.querySelectorAll('.game-player').forEach((el) => el.remove());
- sortPeopleByStatus(this.stateBucket.currentGameState.people);
+ /* TODO: UX issue - it's easier to parse visually when players are sorted this way,
+ but shifting players around when they are killed or revealed is bad UX for the moderator. */
+ // sortPeopleByStatus(this.stateBucket.currentGameState.people);
const modType = tempMod ? this.stateBucket.currentGameState.moderator.userType : null;
renderGroupOfPlayers(
this.stateBucket.currentGameState.people,
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/HTMLFragments.js
index 2dd103b..604d6ac 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/HTMLFragments.js
@@ -100,7 +100,7 @@ export const HTMLFragments = {
-
Transfer Mod Powers \uD83D\uDD00
+
Transfer Mod Powers \uD83D\uDD00
Roles in This Game
@@ -238,6 +238,9 @@ export const HTMLFragments = {
`,
+ DECK_TEMPLATE:
+ `
+
`,
CREATE_GAME_CUSTOM_ROLES:
`
@@ -259,7 +262,10 @@ export const HTMLFragments = {
`,
CREATE_GAME_DECK_STATUS:
``,
DECK_SELECT_ROLE:
diff --git a/client/src/modules/RoleBox.js b/client/src/modules/RoleBox.js
index 82eeee7..d5bbe5a 100644
--- a/client/src/modules/RoleBox.js
+++ b/client/src/modules/RoleBox.js
@@ -11,7 +11,6 @@ export class RoleBox {
this.category = 'default';
this.deckManager = deckManager;
this.defaultRoles = [];
- console.log('hi');
this.customRoles = [];
container.innerHTML += HTMLFragments.CREATE_GAME_CUSTOM_ROLES;
this.defaultButton = document.getElementById('role-category-default');
@@ -36,7 +35,7 @@ export class RoleBox {
loadDefaultRoles = () => {
this.defaultRoles = defaultRoles.sort((a, b) => {
if (a.team !== b.team) {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
+ return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
}
return a.role.localeCompare(b.role);
}).map((role) => {
@@ -162,7 +161,7 @@ export class RoleBox {
this.categoryTransition.play();
this.customRoles.sort((a, b) => {
if (a.team !== b.team) {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
+ return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
}
return a.role.localeCompare(b.role);
});
diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js
index 22b7f7a..995b34c 100644
--- a/client/src/scripts/game.js
+++ b/client/src/scripts/game.js
@@ -383,7 +383,7 @@ function updateDOMWithNameChange (gameState, gameStateRenderer) {
function activateRoleInfoButton (deck) {
deck.sort((a, b) => {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
+ return a.team === globals.ALIGNMENT.GOOD ? -1 : 1;
});
document.getElementById('role-info-button').addEventListener('click', (e) => {
e.preventDefault();
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index fa1a988..02902e5 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -101,6 +101,42 @@
width: fit-content;
}
+.template-option {
+ color: #d7d7d7;
+ display: flex;
+ justify-content: space-between;
+ background-color: #0f0f10;
+ border: 2px solid #333243;
+ padding: 5px;
+ border-radius: 3px;
+ font-size: 16px;
+ flex-direction: column;
+ align-items: flex-start;
+ text-align: left;
+ height: 4em;
+ margin: 10px 0;
+}
+
+#deck-template-container {
+ margin: 1em 0;
+ max-height: 30em;
+ overflow-y: auto;
+}
+
+.template-option-name {
+ font-size: 20px;
+}
+
+.template-option:hover, .template-option:active {
+ border: 2px solid #e7e7e7;
+ background-color: #33343c;
+ cursor: pointer;
+}
+
+#deck-template-modal h2 {
+ font-size: 20px;
+}
+
#custom-roles-container {
width: 95%;
max-width: 25em;
@@ -136,15 +172,20 @@
#deck-count {
font-size: 30px;
- position: sticky;
- top: 0;
- left: 5px;
background-color: #333243;
width: fit-content;
padding: 0 5px;
border-radius: 3px;
}
+#deck-status-header {
+ position: sticky;
+ z-index: 25;
+ top: 0;
+ display: flex;
+ justify-content: space-between;
+}
+
#deck-list {
margin-top: 0.5em;
}
@@ -486,6 +527,10 @@ input[type="number"] {
margin-bottom: 4em;
}
+#step-2 .app-button {
+ padding: 5px;
+}
+
#tracker-container {
display: flex;
align-items: center;
diff --git a/client/src/styles/game.css b/client/src/styles/game.css
index b3d187f..140f950 100644
--- a/client/src/styles/game.css
+++ b/client/src/styles/game.css
@@ -505,6 +505,7 @@ label[for='moderator'] {
.paused {
animation: pulse 0.75s linear infinite alternate;
+ border: 1px solid #ffc83d !important;
}
.paused-low {
@@ -515,11 +516,11 @@ label[for='moderator'] {
display: flex;
flex-wrap: wrap;
flex-direction: column;
- align-items: flex-start;
+ align-items: center;
}
#game-header button {
- min-width: 10em;
+ min-width: 12em;
}
#timer-container-moderator {
@@ -595,7 +596,7 @@ label[for='moderator'] {
background-color: #586a6e;
}
-.reveal-role-button:hover, #mod-transfer-button:hover {
+.reveal-role-button:hover {
background-color: #4e5664;
}
@@ -627,15 +628,15 @@ label[for='moderator'] {
align-items: center;
}
-.make-mod-button {
- background-color: #3f5256;
- font-size: 18px;
- padding: 10px;
- border: 2px transparent;
- border-radius: 3px;
- color: #d7d7d7;
- font-family: signika-negative, sans-serif;
-}
+/*.make-mod-button {*/
+/* background-color: #3f5256;*/
+/* font-size: 18px;*/
+/* padding: 10px;*/
+/* border: 2px transparent;*/
+/* border-radius: 3px;*/
+/* color: #d7d7d7;*/
+/* font-family: signika-negative, sans-serif;*/
+/*}*/
.make-mod-button:hover {
cursor: pointer;
@@ -734,10 +735,10 @@ label[for='moderator'] {
width: 45px;
}
- .make-mod-button {
- font-size: 16px;
- padding: 5px;
- }
+ /*.make-mod-button {*/
+ /* font-size: 16px;*/
+ /* padding: 5px;*/
+ /*}*/
.game-player-name {
font-size: 16px;
@@ -749,7 +750,7 @@ label[for='moderator'] {
height: 30px;
}
- #role-info-button {
+ #role-info-button, #mod-transfer-button {
padding: 7px;
}
diff --git a/client/src/styles/modal.css b/client/src/styles/modal.css
index 06e1044..0a86111 100644
--- a/client/src/styles/modal.css
+++ b/client/src/styles/modal.css
@@ -11,7 +11,7 @@
align-items: center;
justify-content: center;
max-width: 25em;
- font-family: sans-serif;
+ font-family: 'signika-negative', sans-serif;
flex-direction: column;
padding: 1em;
display: none;
diff --git a/client/src/view_templates/CreateTemplate.js b/client/src/view_templates/CreateTemplate.js
index 3d43086..87a6bc2 100644
--- a/client/src/view_templates/CreateTemplate.js
+++ b/client/src/view_templates/CreateTemplate.js
@@ -41,6 +41,13 @@ const template =
Close
+
+
Choose a pre-built game:
+
+
+ Close
+
+
Create A Game
diff --git a/spec/e2e/create_spec.js b/spec/e2e/create_spec.js
index bd7cc5b..f1c6884 100644
--- a/spec/e2e/create_spec.js
+++ b/spec/e2e/create_spec.js
@@ -95,5 +95,14 @@ describe('Create page', function () {
.querySelector('.role-name').innerText
).role).toEqual('Test name edited');
});
+
+ it('should load a deck template', () => {
+ document.getElementById('role-category-default').click();
+ document.getElementById('deck-template-button').click();
+ document.querySelectorAll('.template-option')[0].click();
+
+ expect(gameCreationStepManager.deckManager.deck.length).toEqual(5);
+ expect(document.querySelectorAll('.added-role').length).toEqual(5);
+ });
});
});
From c86e106060ad487d10930ad4fd3f8a0ef3ef3901 Mon Sep 17 00:00:00 2001
From: AlecM33
Date: Sun, 19 Jun 2022 16:24:47 -0400
Subject: [PATCH 9/9] lint, add spec, add info button to added roles
---
client/src/modules/DeckStateManager.js | 74 +++++++++++--------
client/src/modules/GameCreationStepManager.js | 2 +-
client/src/modules/GameStateRenderer.js | 13 ----
client/src/modules/HTMLFragments.js | 1 +
client/src/styles/create.css | 3 +-
spec/e2e/create_spec.js | 15 ++++
6 files changed, 64 insertions(+), 44 deletions(-)
diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js
index dede3bc..5eed73b 100644
--- a/client/src/modules/DeckStateManager.js
+++ b/client/src/modules/DeckStateManager.js
@@ -1,43 +1,43 @@
import { globals } from '../config/globals.js';
import { HTMLFragments } from './HTMLFragments.js';
import { toast } from './Toast.js';
-import {ModalManager} from "./ModalManager.js";
+import { ModalManager } from './ModalManager.js';
export class DeckStateManager {
constructor () {
this.deck = [];
this.templates = {
'5 Players': {
- 'Villager': 1,
- 'Werewolf': 1,
- 'Sorceress': 1,
+ Villager: 1,
+ Werewolf: 1,
+ Sorceress: 1,
'Parity Hunter': 1,
- 'Seer': 1
+ Seer: 1
},
'7 Players': {
- 'Villager': 6,
- 'Werewolf': 1
+ Villager: 6,
+ Werewolf: 1
},
'9 Players': {
- 'Villager': 7,
- 'Werewolf': 2
+ Villager: 7,
+ Werewolf: 2
},
'11 Players': {
- 'Villager': 8,
- 'Werewolf': 2,
- 'Seer': 1
+ Villager: 8,
+ Werewolf: 2,
+ Seer: 1
},
'13 Players': {
- 'Villager': 10,
- 'Werewolf': 2,
- 'Seer': 1
+ Villager: 10,
+ Werewolf: 2,
+ Seer: 1
},
'15 Players': {
- 'Villager': 12,
- 'Werewolf': 2,
- 'Seer': 1
+ Villager: 12,
+ Werewolf: 2,
+ Seer: 1
}
- }
+ };
}
addToDeck (role) {
@@ -89,14 +89,14 @@ export class DeckStateManager {
loadDeckTemplates = (roleBox) => {
if (document.querySelectorAll('.template-option').length === 0) {
- for (let templateName of Object.keys(this.templates)) {
- let templateOption = document.createElement('div');
+ for (const templateName of Object.keys(this.templates)) {
+ const templateOption = document.createElement('div');
templateOption.classList.add('template-option');
templateOption.innerHTML = HTMLFragments.DECK_TEMPLATE;
templateOption.querySelector('.template-option-name').innerText = templateName;
- for (let i = 0; i < Object.keys(this.templates[templateName]).length; i++) {
- let role = Object.keys(this.templates[templateName])[i];
- let roleEl = document.createElement('span');
+ for (let i = 0; i < Object.keys(this.templates[templateName]).length; i ++) {
+ const role = Object.keys(this.templates[templateName])[i];
+ const roleEl = document.createElement('span');
roleEl.innerText = this.templates[templateName][role] + ' ' + role;
if (i < Object.keys(this.templates[templateName]).length - 1) { // construct comma-delimited list
roleEl.innerText += ', ';
@@ -106,15 +106,15 @@ export class DeckStateManager {
}
templateOption.addEventListener('click', (e) => {
e.preventDefault();
- for (let card of this.deck) {
+ for (const card of this.deck) {
card.quantity = 0;
}
- for (let role of Object.keys(this.templates[templateName])) {
- let roleObj = roleBox.getDefaultRole(role);
+ for (const role of Object.keys(this.templates[templateName])) {
+ const roleObj = roleBox.getDefaultRole(role);
if (!this.hasRole(roleObj.role)) {
this.addToDeck(roleObj);
}
- for (let i = roleObj.quantity; i < this.templates[templateName][role]; i++) {
+ for (let i = roleObj.quantity; i < this.templates[templateName][role]; i ++) {
this.addCopyOfCard(roleObj.role);
}
}
@@ -125,7 +125,7 @@ export class DeckStateManager {
document.getElementById('deck-template-container').appendChild(templateOption);
}
}
- }
+ };
displayDeckPlaceHolder = () => {
const placeholder = document.createElement('div');
@@ -182,6 +182,22 @@ export class DeckStateManager {
};
roleEl.querySelector('.role-remove').addEventListener('click', minusOneHandler);
roleEl.querySelector('.role-remove').addEventListener('keyup', minusOneHandler);
+
+ const infoHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ const alignmentEl = document.getElementById('custom-role-info-modal-alignment');
+ alignmentEl.classList.remove(globals.ALIGNMENT.GOOD);
+ alignmentEl.classList.remove(globals.ALIGNMENT.EVIL);
+ e.preventDefault();
+ document.getElementById('custom-role-info-modal-name').innerText = sortedDeck[i].role;
+ alignmentEl.classList.add(sortedDeck[i].team);
+ document.getElementById('custom-role-info-modal-description').innerText = sortedDeck[i].description;
+ alignmentEl.innerText = sortedDeck[i].team;
+ ModalManager.displayModal('custom-role-info-modal', 'modal-background', 'close-custom-role-info-modal-button');
+ }
+ };
+ roleEl.querySelector('.role-info').addEventListener('click', infoHandler);
+ roleEl.querySelector('.role-info').addEventListener('keyup', infoHandler);
}
} else {
sortedDeck[i].markedForRemoval = true;
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js
index 1eb9399..b6fdfa9 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/GameCreationStepManager.js
@@ -198,7 +198,6 @@ export class GameCreationStepManager {
}
renderRoleSelectionStep = (game, containerId, step, deckManager) => {
-
const stepContainer = document.createElement('div');
setAttributes(stepContainer, { id: 'step-' + step, class: 'flex-row-container-left-align step' });
@@ -208,6 +207,7 @@ export class GameCreationStepManager {
document.getElementById(containerId).appendChild(stepContainer);
this.roleBox = new RoleBox(stepContainer, deckManager);
+ deckManager.roleBox = this.roleBox;
this.roleBox.loadDefaultRoles();
this.roleBox.loadCustomRolesFromCookies();
this.roleBox.displayDefaultRoles(document.getElementById('role-select'));
diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js
index 4538989..97b560d 100644
--- a/client/src/modules/GameStateRenderer.js
+++ b/client/src/modules/GameStateRenderer.js
@@ -345,19 +345,6 @@ 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 (const card of cards) {
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/HTMLFragments.js
index 604d6ac..d2517be 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/HTMLFragments.js
@@ -286,5 +286,6 @@ export const HTMLFragments = {
`
+
`
};
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index 02902e5..f55c90a 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -119,8 +119,9 @@
#deck-template-container {
margin: 1em 0;
- max-height: 30em;
+ max-height: 64vh;
overflow-y: auto;
+ padding: 0 0.5em;
}
.template-option-name {
diff --git a/spec/e2e/create_spec.js b/spec/e2e/create_spec.js
index f1c6884..0c04938 100644
--- a/spec/e2e/create_spec.js
+++ b/spec/e2e/create_spec.js
@@ -104,5 +104,20 @@ describe('Create page', function () {
expect(gameCreationStepManager.deckManager.deck.length).toEqual(5);
expect(document.querySelectorAll('.added-role').length).toEqual(5);
});
+
+ it('clear existing added cards and leave only what roles are part of the template', () => {
+ document.getElementById('role-category-default').click();
+ const roles = document.querySelectorAll('.default-role');
+ roles.forEach((el) => {
+ const plusElement = el.querySelector('.role-include');
+ plusElement.click();
+ });
+
+ document.getElementById('deck-template-button').click();
+ document.querySelectorAll('.template-option')[0].click();
+
+ expect(gameCreationStepManager.deckManager.deck.length).toEqual(5);
+ expect(document.querySelectorAll('.added-role').length).toEqual(5);
+ });
});
});