import { Game } from "../model/Game.js"; import { cancelCurrentToast, toast } from "./Toast.js"; import { customCards } from "../config/customCards.js"; import { ModalManager } from "./ModalManager.js"; export class GameCreationStepManager { constructor(deckManager) { this.step = 1; this.currentGame = new Game(null, null, null, null); this.deckManager = deckManager; this.defaultBackHandler = () => { cancelCurrentToast(); this.removeStepElementsFromDOM(this.step); this.decrementStep(); this.renderStep("creation-step-container", this.step); } this.steps = { 1: { title: "Select your method of moderation:", forwardHandler: () => { if (this.currentGame.hasDedicatedModerator !== null) { cancelCurrentToast(); this.removeStepElementsFromDOM(this.step); this.incrementStep(); this.renderStep("creation-step-container", this.step); } else { toast("You must select a moderation option.", "error", true); } } }, 2: { title: "Create your deck of cards:", forwardHandler: () => { if (this.deckManager.getDeckSize() >= 5) { 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); } }, backHandler: this.defaultBackHandler }, 3: { title: "Set an optional timer:", forwardHandler: () => { let hours = parseInt(document.getElementById("game-hours").value); let minutes = parseInt(document.getElementById("game-minutes").value); if ((isNaN(hours) && isNaN(minutes)) || (isNaN(hours) && minutes > 0 && minutes < 60) || (isNaN(minutes) && hours > 0 && hours < 6) || (hours === 0 && minutes > 0 && minutes < 60) || (minutes === 0 && hours > 0 && hours < 6) || (hours > 0 && hours < 6 && minutes >= 0 && minutes < 60) ) { if (hasTimer(hours, minutes)) { this.currentGame.hasTimer = true; this.currentGame.timerParams = { hours: hours, minutes: minutes } } else { this.currentGame.hasTimer = false; this.currentGame.timerParams = null; } console.log(this.currentGame); cancelCurrentToast(); this.removeStepElementsFromDOM(this.step); this.incrementStep(); this.renderStep("creation-step-container", this.step); } else { if (hours === 0 && minutes === 0) { toast("You must enter a non-zero amount of time.", "error", true); } else { toast("Invalid timer options. Hours can be a max of 5, Minutes a max of 59.", "error", true); } } }, backHandler: this.defaultBackHandler }, 4: { title: "Review and submit:", backHandler: this.defaultBackHandler } } } incrementStep() { if (this.step < Object.keys(this.steps).length) { this.step += 1; } } decrementStep() { if (this.step > 1) { this.step -= 1; } } renderStep(containerId, step) { document.querySelectorAll('.animated-placeholder').forEach((el) => el.remove()); document.querySelectorAll('.placeholder-row').forEach((el) => el.remove()); document.getElementById("step-title").innerText = this.steps[step].title; switch (step) { case 1: renderModerationTypeStep(this.currentGame, containerId, step); showButtons(false, true, this.steps[step].forwardHandler, null); break; case 2: renderRoleSelectionStep(this.currentGame, containerId, step, this.deckManager); showButtons(true, true, this.steps[step].forwardHandler, this.steps[step].backHandler); break; case 3: renderTimerStep(containerId, step, this.currentGame); showButtons(true, true, this.steps[step].forwardHandler, this.steps[step].backHandler); break; case 4: renderReviewAndCreateStep(containerId, step, this.currentGame); showButtons(true, false, null, this.steps[step].backHandler); break; default: break; } updateTracker(step); } removeStepElementsFromDOM(stepNumber) { document.getElementById('step-' + stepNumber)?.remove(); } } function renderModerationTypeStep(game, containerId, stepNumber) { const stepContainer = document.createElement("div"); setAttributes(stepContainer, {'id': 'step-' + stepNumber, 'class': 'flex-row-container'}); stepContainer.innerHTML = "
I will be the dedicated mod. Don't deal me a card.
" + "
The first person out will mod. Deal me into the game.
"; let dedicatedOption = stepContainer.querySelector('#moderation-dedicated'); if (game.hasDedicatedModerator) { dedicatedOption.classList.add('option-selected'); } let selfOption = stepContainer.querySelector('#moderation-self'); if (game.hasDedicatedModerator === false) { selfOption.classList.add('option-selected'); } dedicatedOption.addEventListener('click', () => { dedicatedOption.classList.add('option-selected'); selfOption.classList.remove('option-selected'); game.hasDedicatedModerator = true; }); selfOption.addEventListener('click', () => { selfOption.classList.add('option-selected'); dedicatedOption.classList.remove('option-selected'); game.hasDedicatedModerator = false; }); document.getElementById(containerId).appendChild(stepContainer); } function renderRoleSelectionStep(game, containerId, step, deckManager) { const stepContainer = document.createElement("div"); setAttributes(stepContainer, {'id': 'step-' + step, 'class': 'flex-row-container-left-align'}); 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); document.getElementById(containerId).appendChild(stepContainer); $('.ui.dropdown') .dropdown(); initializeRemainingEventListeners(deckManager); } function renderTimerStep(containerId, stepNumber, game) { let div = document.createElement("div"); div.setAttribute("id", "step-" + stepNumber); let timeContainer = document.createElement("div"); timeContainer.setAttribute("id", "game-time"); let hoursLabel = document.createElement("label"); hoursLabel.setAttribute("for", "game-hours"); hoursLabel.innerText = "Hours (max 5)" let hours = document.createElement("input"); setAttributes(hours, {type: "number", id: "game-hours", name: "game-hours", min: "0", max: "5", value: game.timerParams?.hours}) let minsLabel = document.createElement("label"); minsLabel.setAttribute("for", "game-minutes"); minsLabel.innerText = "Minutes" let minutes = document.createElement("input"); setAttributes(minutes, {type: "number", id: "game-minutes", name: "game-minutes", min: "1", max: "60", value: game.timerParams?.minutes}) timeContainer.appendChild(hoursLabel); timeContainer.appendChild(hours); timeContainer.appendChild(minsLabel); timeContainer.appendChild(minutes); div.appendChild(timeContainer); document.getElementById(containerId).appendChild(div); } function renderReviewAndCreateStep(containerId, stepNumber, game) { let div = document.createElement("div"); div.setAttribute("id", "step-" + stepNumber); div.innerHTML = "" + "
" + "" + "
" + "" + "
"; div.querySelector('#mod-option').innerText = game.hasDedicatedModerator ? "I will be the dedicated mod. Don't deal me a card." : "The first person out will mod. Deal me into the game."; if (game.hasTimer) { let formattedHours = !isNaN(game.timerParams.hours) ? game.timerParams.hours + ' Hours' : '0 Hours' let formattedMinutes = !isNaN(game.timerParams.minutes) ? game.timerParams.minutes + ' Minutes' : '0 Minutes' div.querySelector('#timer-option').innerText = formattedHours + " " + formattedMinutes; } else { div.querySelector('#timer-option').innerText = "untimed" } for (let card of game.deck) { let roleEl = document.createElement("div"); roleEl.innerText = card.quantity + 'x ' + card.role; div.querySelector('#roles-option').appendChild(roleEl); } document.getElementById(containerId).appendChild(div); } function setAttributes(element, attributeObject) { for (let key of Object.keys(attributeObject)) { element.setAttribute(key, attributeObject[key]); } } function updateTracker(step) { document.querySelectorAll('.creation-step').forEach((element, i) => { if ((i + 1) <= step) { element.classList.add('creation-step-filled'); } else { element.classList.remove('creation-step-filled'); } }) } function showButtons(back, forward, forwardHandler, backHandler) { document.querySelector("#step-back-button")?.remove(); document.querySelector("#step-forward-button")?.remove(); if (back) { let backButton = document.createElement("button"); backButton.innerText = "Back"; backButton.addEventListener('click', backHandler); backButton.setAttribute("id", "step-back-button"); document.getElementById("creation-step-buttons").appendChild(backButton); } if (forward) { let fwdButton = document.createElement("button"); fwdButton.innerText = "Next"; fwdButton.addEventListener('click', forwardHandler); fwdButton.setAttribute("id", "step-forward-button"); document.getElementById("creation-step-buttons").appendChild(fwdButton); } } // Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. function loadIncludedCards(deckManager, deckContainer) { 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); } 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.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"); selectEl.setAttribute("class", "ui search dropdown"); 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) => { e.preventDefault(); if (selectEl.value && selectEl.value.length > 0) { deckManager.addToDeck(selectEl.value); let cardEl = constructCompactDeckBuilderElement(deckManager.getCard(selectEl.value), deckManager); updateCustomRoleOptionsList(deckManager, selectEl); document.getElementById("deck").appendChild(cardEl); document.querySelector("#add-card-to-deck-form .text").innerText = ""; } }) form.appendChild(submitBtn); customContainer.appendChild(formLabel); customContainer.appendChild(form); customContainer.appendChild(createRoleButton); return customContainer; } function constructCompactDeckBuilderElement(card, deckManager) { const cardContainer = document.createElement("div"); cardContainer.setAttribute("class", "compact-card"); cardContainer.setAttribute("id", "card-" + card.role.replaceAll(' ', '-')); cardContainer.innerHTML = "
" + "

-

" + "
" + "
" + "

" + "
" + "
" + "
" + "

+

" + "
"; cardContainer.querySelector('.card-role').innerText = card.role; cardContainer.querySelector('.card-quantity').innerText = card.quantity; if (card.quantity > 0) { cardContainer.classList.add('selected-card'); } 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') } }); 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') } }); return cardContainer; } function initializeRemainingEventListeners(deckManager) { // document.getElementById("game-form").onsubmit = (e) => { // e.preventDefault(); // let timerBool = hasTimer(); // let timerParams = timerBool // ? { // hours: document.getElementById("game-hours").value, // minutes: document.getElementById("game-minutes").value // } // : null; // if (deckManager.getDeckSize() >= 5) { // createGameForHosting( // deckManager.getCurrentDeck().filter((card) => card.quantity > 0), // timerBool, // document.getElementById("mod-name").value, // timerParams // ); // } else { // toast("You must include enough cards for 5 players.", "error", true); // } // } document.getElementById("add-role-form").onsubmit = (e) => { e.preventDefault(); let name = document.getElementById("role-name").value.trim(); let description = document.getElementById("role-description").value.trim(); if (!deckManager.getCustomRoleOption(name)) { // confirm there is no existing custom role with the same name deckManager.addToCustomRoleOptions({role: name, description: description}); updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) ModalManager.dispelModal("add-role-modal", "add-role-modal-background"); toast("Role Added", "success", true); } else { toast("There is already a custom role with this name.", "error", true); } } document.getElementById("custom-role-btn").addEventListener( "click", () => { ModalManager.displayModal( "add-role-modal", "add-role-modal-background", "close-modal-button" ) } ) } function updateCustomRoleOptionsList(deckManager, selectEl) { document.querySelectorAll('#deck-select option').forEach(e => e.remove()); addOptionsToList(deckManager.customRoleOptions, selectEl); } function addOptionsToList(options, selectEl) { for (let i = 0; i < options.length; i ++) { let optionEl = document.createElement("option"); optionEl.setAttribute("value", customCards[i].role); optionEl.innerText = customCards[i].role; selectEl.appendChild(optionEl); } } function hasTimer(hours, minutes) { return (!isNaN(hours) || !isNaN(minutes)); }