diff --git a/client/src/config/defaultCards.js b/client/src/config/defaultCards.js index 9e25dce..115b804 100644 --- a/client/src/config/defaultCards.js +++ b/client/src/config/defaultCards.js @@ -12,31 +12,36 @@ export const defaultCards = [ { role: "Dream Wolf", team: "evil", - description: "If a Werewolf dies, you become a Werewolf. You do not wake up with the Werewolves until this happens. You count for parity only after converting to a wolf.", + description: "You are a Werewolf, but you don't wake up with the other Werewolves until one of them dies.", }, { - role: "Knowing Minion", + role: "Sorceress", team: "evil", - description: "You are an evil villager - you know who the wolves are, and you want them to win.", + description: "Each night, learn if a chosen person is the Seer.", }, { - role: "Double-Blind Minion", + role: "Minion", team: "evil", - description: "You are an evil villager. You don't know who the wolves are, but you want them to win.", + description: "You are an evil Villager, and you know who the Werewolves are.", + }, + { + role: "Blind Minion", + team: "evil", + description: "You are an evil villager, but you don't know who the Werewolves are.", }, { role: "Seer", team: "good", - description: "During each night, choose one person. The moderator will tell you whether that player is a wolf.", + description: "Each night, learn if a chosen person is a Werewolf.", + }, + { + role: "Parity Hunter", + team: "good", + description: "You beat a werewolf in a 1v1 situation, winning the game for the village.", }, { role: "Hunter", team: "good", - description: "If you are alive with a wolf at the end of the game, you best the wolf, and the village wins.", - }, - { - role: "Mason", - team: "good", - description: "Masons know who the other masons are.", + description: "When you are eliminated, choose another player to go with you.", } ]; diff --git a/client/src/images/roles/Double-BlindMinion.png b/client/src/images/roles/BlindMinion.png similarity index 100% rename from client/src/images/roles/Double-BlindMinion.png rename to client/src/images/roles/BlindMinion.png diff --git a/client/src/images/roles/KnowingMinion.png b/client/src/images/roles/Minion.png similarity index 100% rename from client/src/images/roles/KnowingMinion.png rename to client/src/images/roles/Minion.png diff --git a/client/src/images/roles/Sorcerer.png b/client/src/images/roles/Sorcerer.png deleted file mode 100644 index a85b4da..0000000 Binary files a/client/src/images/roles/Sorcerer.png and /dev/null differ diff --git a/client/src/images/roles/Shadow.png b/client/src/images/roles/Sorceress.png similarity index 100% rename from client/src/images/roles/Shadow.png rename to client/src/images/roles/Sorceress.png diff --git a/client/src/images/roles/Villager.png b/client/src/images/roles/Villager1.png similarity index 100% rename from client/src/images/roles/Villager.png rename to client/src/images/roles/Villager1.png diff --git a/client/src/images/roles/Villager-2.png b/client/src/images/roles/Villager2.png similarity index 100% rename from client/src/images/roles/Villager-2.png rename to client/src/images/roles/Villager2.png diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js index 5b5415d..cb6376a 100644 --- a/client/src/modules/DeckStateManager.js +++ b/client/src/modules/DeckStateManager.js @@ -6,8 +6,7 @@ export class DeckStateManager { addToDeck(role) { let option = this.customRoleOptions.find((option) => option.role === role); - let existingCard = this.deck.find((card) => card.role === role); - if (option && !existingCard) { + if (option) { option.quantity = 0; this.deck.push(option); this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1); diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index a208c07..1a9f681 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -4,6 +4,7 @@ import { customCards } from "../config/customCards.js"; import { ModalManager } from "./ModalManager.js"; import {XHRUtility} from "./XHRUtility.js"; import {globals} from "../config/globals.js"; +import {templates} from "./Templates.js"; export class GameCreationStepManager { constructor(deckManager) { @@ -33,14 +34,14 @@ export class GameCreationStepManager { 2: { title: "Create your deck of cards:", forwardHandler: () => { - if (this.deckManager.getDeckSize() >= 5) { + if (this.deckManager.getDeckSize() >= 5 && this.deckManager.getDeckSize() <= 50) { this.currentGame.deck = deckManager.getCurrentDeck().filter((card) => card.quantity > 0) cancelCurrentToast(); this.removeStepElementsFromDOM(this.step); this.incrementStep(); this.renderStep("creation-step-container", this.step); } else { - toast("You must include enough cards for 5 players.", "error", true); + toast("You must have a deck for between 5 and 50 players", "error", true); } }, backHandler: this.defaultBackHandler @@ -191,26 +192,28 @@ function renderRoleSelectionStep(game, containerId, step, deckManager) { const stepContainer = document.createElement("div"); setAttributes(stepContainer, {'id': 'step-' + step, 'class': 'flex-row-container-left-align step'}); - let div = document.createElement("div"); - div.setAttribute("id", "deck-container"); - let deckContainer = document.createElement("div"); - deckContainer.setAttribute("id", "deck"); - - deckContainer = loadIncludedCards(deckManager, deckContainer); - - let deckLabel = document.createElement("label"); - deckLabel.setAttribute("for", "deck"); - deckLabel.innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; - div.prepend(deckLabel); - div.appendChild(deckContainer); - stepContainer.appendChild(div); - - let customForm = loadCustomRoles(deckManager); - - stepContainer.prepend(customForm); + stepContainer.innerHTML =templates.CREATE_GAME_CUSTOM_ROLES; + stepContainer.innerHTML += templates.CREATE_GAME_DECK; document.getElementById(containerId).appendChild(stepContainer); + let clickHandler = () => { + console.log("fired"); + let actions = document.getElementById("custom-role-actions"); + if (actions.style.display !== 'none') { + actions.style.display = 'none'; + } else { + actions.style.display = 'block'; + } + }; + + //document.getElementById("custom-role-hamburger").addEventListener("click", clickHandler); + + loadIncludedCards(deckManager); + + loadCustomRoles(deckManager); + + initializeRemainingEventListeners(deckManager); } @@ -349,61 +352,41 @@ function showButtons(back, forward, forwardHandler, backHandler, builtGame=null) } // Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. -function loadIncludedCards(deckManager, deckContainer) { - +function loadIncludedCards(deckManager) { for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) { // each dropdown should include every let card = deckManager.getCurrentDeck()[i]; let cardEl = constructCompactDeckBuilderElement(card, deckManager); - deckContainer.appendChild(cardEl); + if (card.team === globals.ALIGNMENT.GOOD) { + document.getElementById("deck-good").appendChild(cardEl); + } else { + document.getElementById("deck-evil").appendChild(cardEl); + } } - - return deckContainer; } /* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and create a widget for it */ function loadCustomRoles(deckManager) { - let customContainer = document.createElement("div"); - customContainer.setAttribute("id", "custom-roles-container"); - - let formLabel = document.createElement("label"); - formLabel.innerText = 'Custom Roles'; - formLabel.setAttribute("for", "add-card-to-deck-form"); - - let createRoleButton = document.createElement("button"); - createRoleButton.setAttribute("id", "custom-role-btn"); - createRoleButton.classList.add('app-button'); - createRoleButton.innerText = '+ Create Custom Role'; - - let form = document.createElement("form"); - form.setAttribute("id", "add-card-to-deck-form"); - - let selectEl = document.createElement("select"); - selectEl.setAttribute("id", "deck-select"); - addOptionsToList(deckManager.getCurrentCustomRoleOptions(), selectEl); - - form.appendChild(selectEl); - - let submitBtn = document.createElement("input"); - submitBtn.setAttribute("type", "submit"); - submitBtn.setAttribute("value", "Include Role"); - submitBtn.addEventListener('click', (e) => { + let select = document.getElementById("deck-select"); + addOptionsToList(deckManager.getCurrentCustomRoleOptions(), document.getElementById("deck-select")); + document.getElementById("include-role").addEventListener('click', (e) => { e.preventDefault(); - if (selectEl.value && selectEl.value.length > 0) { - deckManager.addToDeck(selectEl.value); - let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); - toast('"' + selectEl.value + '" included.', 'success', true, true, 3); - updateCustomRoleOptionsList(deckManager, selectEl); - document.getElementById("deck").appendChild(cardEl); + if (select.value && select.value.length > 0) { + if (!deckManager.getCard(select.value)) { + deckManager.addToDeck(select.value); + let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(select.value), deckManager); + toast('"' + select.value + '" included.', 'success', true, true, 3); + if (deckManager.getCard(select.value).team === globals.ALIGNMENT.GOOD) { + document.getElementById("deck-good").appendChild(cardEl); + } else { + document.getElementById("deck-evil").appendChild(cardEl); + } + updateCustomRoleOptionsList(deckManager, select); + } else { + toast('"' + select.value + '" already included.', 'error', true, true, 3); + } } }) - form.appendChild(submitBtn); - - customContainer.appendChild(formLabel); - customContainer.appendChild(form); - customContainer.appendChild(createRoleButton); - - return customContainer; } function constructCompactDeckBuilderElement(card, deckManager) { @@ -437,7 +420,6 @@ function constructCompactDeckBuilderElement(card, deckManager) { cardContainer.querySelector('.compact-card-right').addEventListener('click', () => { deckManager.addCopyOfCard(card.role); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; if (deckManager.getCard(card.role).quantity > 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card') } @@ -445,7 +427,6 @@ function constructCompactDeckBuilderElement(card, deckManager) { cardContainer.querySelector('.compact-card-left').addEventListener('click', () => { deckManager.removeCopyOfCard(card.role); cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity; - document.querySelector('label[for="deck"]').innerText = 'Game Deck: ' + deckManager.getDeckSize() + ' Players'; if (deckManager.getCard(card.role).quantity === 0) { document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card') } @@ -459,7 +440,7 @@ function initializeRemainingEventListeners(deckManager) { let name = document.getElementById("role-name").value.trim(); let description = document.getElementById("role-description").value.trim(); let team = document.getElementById("role-alignment").value.toLowerCase().trim(); - if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name + if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name if (name.length > 40) { toast('Your name is too long (max 40 characters).', "error", true); return; @@ -473,7 +454,7 @@ function initializeRemainingEventListeners(deckManager) { ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); toast("Role Created", "success", true); } else { - toast("There is already a custom role with this name.", "error", true); + toast("There is already a role with this name", "error", true, true, 3); } } document.getElementById("custom-role-btn").addEventListener( @@ -494,6 +475,9 @@ function updateCustomRoleOptionsList(deckManager, selectEl) { function addOptionsToList(options, selectEl) { options.sort((a, b) => { + if (a.team !== b.team) { + return a.team === globals.ALIGNMENT.GOOD ? 1 : -1; + } return a.role.localeCompare(b.role); }); for (let i = 0; i < options.length; i ++) { diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 27709b9..15c18b4 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -389,8 +389,10 @@ function renderPlayerRole(gameState) { let name = document.querySelector('#role-name'); name.innerText = gameState.client.gameRole; if (gameState.client.alignment === globals.ALIGNMENT.GOOD) { + document.getElementById("game-role").classList.add('game-role-good'); name.classList.add('good'); } else { + document.getElementById("game-role").classList.add('game-role-evil'); name.classList.add('evil'); } name.setAttribute("title", gameState.client.gameRole); @@ -401,10 +403,17 @@ function renderPlayerRole(gameState) { '../images/tombstone.png' ); } else { - document.getElementById("role-image").setAttribute( - 'src', - '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' - ); + if (gameState.client.gameRole.toLowerCase() === 'villager') { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/Villager' + Math.ceil(Math.random() * 2) + '.png' + ); + } else { + document.getElementById("role-image").setAttribute( + 'src', + '../images/roles/' + gameState.client.gameRole.replaceAll(' ', '') + '.png' + ); + } } document.querySelector('#role-description').innerText = gameState.client.gameRoleDescription; diff --git a/client/src/modules/Templates.js b/client/src/modules/Templates.js index 3bbade3..2874ac4 100644 --- a/client/src/modules/Templates.js +++ b/client/src/modules/Templates.js @@ -78,7 +78,7 @@ export const templates = { "