From 99a5455713c4a6e5021600374f80e3f00facf0a5 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Sun, 19 Jun 2022 15:32:12 -0400 Subject: [PATCH] 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 = {
- +
@@ -238,6 +238,9 @@ export const HTMLFragments = {
`, + DECK_TEMPLATE: + `
+
`, CREATE_GAME_CUSTOM_ROLES: `
`, CREATE_GAME_DECK_STATUS: `
-
0 Players
+
+
0 Players
+ +
`, 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 = +

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); + }); }); });