Deck templates

This commit is contained in:
AlecM33
2022-06-19 15:32:12 -04:00
parent a805d8b576
commit 99a5455713
11 changed files with 182 additions and 29 deletions

View File

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

View File

@@ -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');

View File

@@ -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,

View File

@@ -100,7 +100,7 @@ export const HTMLFragments = {
<div id="play-pause-placeholder"></div>
</div>
</div>
<button id='mod-transfer-button' class='moderator-player-button make-mod-button'>Transfer Mod Powers \uD83D\uDD00</button>
<button id='mod-transfer-button' class='moderator-player-button make-mod-button app-button'>Transfer Mod Powers \uD83D\uDD00</button>
<div>
<button id='role-info-button' class='app-button'>Roles in This Game <img src='/images/info.svg'/></button>
</div>
@@ -238,6 +238,9 @@ export const HTMLFragments = {
<div id='deck-evil'></div>
</div>
</div>`,
DECK_TEMPLATE:
`<div class='template-option-name'></div>
<div class='template-option-roles'></div>`,
CREATE_GAME_CUSTOM_ROLES:
`<div id="custom-roles-container">
<button id="custom-role-hamburger" class="hamburger hamburger--collapse" type="button">
@@ -259,7 +262,10 @@ export const HTMLFragments = {
</div>`,
CREATE_GAME_DECK_STATUS:
`<div id="deck-status-container">
<div id="deck-count">0 Players</div>
<div id="deck-status-header">
<div id="deck-count">0 Players</div>
<button id="deck-template-button" class="app-button">Use Template</button>
</div>
<div id="deck-list"></div>
</div>`,
DECK_SELECT_ROLE:

View File

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

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -41,6 +41,13 @@ const template =
<button id="close-custom-role-info-modal-button" class="cancel app-button">Close</button>
</div>
</div>
<div tabindex='-1' id='deck-template-modal' class='modal'>
<h2>Choose a pre-built game:</h2>
<div id='deck-template-container'></div>
<div class='modal-button-container'>
<button id='close-deck-template-modal-button' class='cancel app-button'>Close</button>
</div>
</div>
<h1>Create A Game</h1>
<div id="tracker-container">
<div id="creation-step-tracker">

View File

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