mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 15:57:50 +01:00
4-step game creation
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
export class Game {
|
||||
constructor(deck, hasTimer, moderatorName, timerParams=null) {
|
||||
constructor(deck, hasTimer, hasDedicatedModerator, moderatorName, timerParams=null) {
|
||||
this.deck = deck;
|
||||
this.hasTimer = hasTimer;
|
||||
this.moderatorName = moderatorName;
|
||||
this.timerParams = timerParams;
|
||||
this.hasDedicatedModerator = hasDedicatedModerator
|
||||
}
|
||||
}
|
||||
|
||||
464
client/modules/GameCreationStepManager.js
Normal file
464
client/modules/GameCreationStepManager.js
Normal file
@@ -0,0 +1,464 @@
|
||||
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 =
|
||||
"<div id='moderation-dedicated'>I will be the <strong>dedicated mod.</strong> Don't deal me a card.</div>" +
|
||||
"<div id='moderation-self'>The <strong>first person out</strong> will mod. Deal me into the game.</div>";
|
||||
|
||||
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 =
|
||||
"<label for='mod-option'>Moderation</label>" +
|
||||
"<div id='mod-option' class='review-option'></div>" +
|
||||
"<label for='timer-option'>Timer</label>" +
|
||||
"<div id='timer-option' class='review-option'></div>" +
|
||||
"<label for='roles-option'>Game Deck</label>" +
|
||||
"<div id='roles-option' class='review-option'></div>";
|
||||
|
||||
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 =
|
||||
"<div class='compact-card-left'>" +
|
||||
"<p>-</p>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-header'>" +
|
||||
"<p class='card-role'></p>" +
|
||||
"<div class='card-quantity'></div>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-right'>" +
|
||||
"<p>+</p>" +
|
||||
"</div>";
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -5,7 +5,7 @@ export const toast = (message, type, positionAtTop = true) => {
|
||||
};
|
||||
|
||||
function buildAndInsertMessageElement (message, type, positionAtTop) {
|
||||
cancelCurrentMessage();
|
||||
cancelCurrentToast();
|
||||
let backgroundColor;
|
||||
const position = positionAtTop ? 'top:4rem;' : 'bottom: 15px;';
|
||||
switch (type) {
|
||||
@@ -27,7 +27,7 @@ function buildAndInsertMessageElement (message, type, positionAtTop) {
|
||||
document.body.prepend(messageEl);
|
||||
}
|
||||
|
||||
function cancelCurrentMessage () {
|
||||
export function cancelCurrentToast () {
|
||||
const currentMessage = document.getElementById('current-info-message');
|
||||
if (currentMessage !== null) {
|
||||
currentMessage.remove();
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { toast } from "../modules/Toast.js";
|
||||
import { ModalManager } from "../modules/ModalManager.js";
|
||||
import { defaultCards } from "../config/defaultCards.js";
|
||||
import { customCards } from "../config/customCards.js";
|
||||
import { DeckStateManager } from "../modules/DeckStateManager.js";
|
||||
import { XHRUtility } from "../modules/XHRUtility.js";
|
||||
import { Game } from "../model/Game.js";
|
||||
import { GameCreationStepManager } from "../modules/GameCreationStepManager.js";
|
||||
|
||||
export const create = () => {
|
||||
let deckManager = new DeckStateManager();
|
||||
let gameCreationStepManager = new GameCreationStepManager(deckManager);
|
||||
loadDefaultCards(deckManager);
|
||||
loadCustomCards(deckManager);
|
||||
initializeRemainingEventListeners(deckManager);
|
||||
loadCustomRoles(deckManager);
|
||||
gameCreationStepManager.renderStep("creation-step-container", 1);
|
||||
}
|
||||
|
||||
// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state.
|
||||
@@ -22,8 +22,6 @@ function loadDefaultCards(deckManager) {
|
||||
for (let i = 0; i < defaultCards.length; i ++) { // each dropdown should include every
|
||||
let card = defaultCards[i];
|
||||
card.quantity = 0;
|
||||
let cardEl = constructCompactDeckBuilderElement(defaultCards[i], deckManager);
|
||||
document.getElementById("deck").appendChild(cardEl);
|
||||
deck.push(card);
|
||||
}
|
||||
deckManager.deck = deck;
|
||||
@@ -31,138 +29,13 @@ function loadDefaultCards(deckManager) {
|
||||
|
||||
/* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and
|
||||
create a widget for it */
|
||||
function loadCustomCards(deckManager) {
|
||||
let form = document.getElementById("add-card-to-deck-form");
|
||||
function loadCustomRoles(deckManager) {
|
||||
customCards.sort((a, b) => {
|
||||
return a.role.localeCompare(b.role);
|
||||
});
|
||||
let selectEl = document.createElement("select");
|
||||
selectEl.setAttribute("id", "deck-select");
|
||||
selectEl.setAttribute("class", "ui search dropdown");
|
||||
addOptionsToList(customCards, 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);
|
||||
$('.ui.dropdown')
|
||||
.dropdown();
|
||||
deckManager.customRoleOptions = customCards;
|
||||
}
|
||||
|
||||
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 constructCompactDeckBuilderElement(card, deckManager) {
|
||||
const cardContainer = document.createElement("div");
|
||||
|
||||
cardContainer.setAttribute("class", "compact-card");
|
||||
|
||||
cardContainer.setAttribute("id", "card-" + card.role.replaceAll(' ', '-'));
|
||||
|
||||
cardContainer.innerHTML =
|
||||
"<div class='compact-card-left'>" +
|
||||
"<p>-</p>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-header'>" +
|
||||
"<p class='card-role'></p>" +
|
||||
"<div class='card-quantity'>0</div>" +
|
||||
"</div>" +
|
||||
"<div class='compact-card-right'>" +
|
||||
"<p>+</p>" +
|
||||
"</div>";
|
||||
|
||||
cardContainer.querySelector('.card-role').innerText = card.role;
|
||||
|
||||
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 hasTimer() {
|
||||
return document.getElementById("game-hours").value.length > 0 || document.getElementById("game-minutes").value.length > 0
|
||||
}
|
||||
|
||||
function createGameForHosting(deck, hasTimer, modName, timerParams) {
|
||||
XHRUtility.xhr(
|
||||
'/api/games/create',
|
||||
|
||||
@@ -25,7 +25,9 @@ function attemptToJoinGame(code) {
|
||||
.then((res) => {
|
||||
if (res.status === 200) {
|
||||
window.location = '/game/' + res.content;
|
||||
} else if (res.status === 404) {
|
||||
}
|
||||
}).catch((res) => {
|
||||
if (res.status === 404) {
|
||||
toast("Game not found", "error", true);
|
||||
} else if (res.status === 400) {
|
||||
toast(res.content, "error", true);
|
||||
|
||||
@@ -43,6 +43,12 @@ h1 {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: whitesmoke;
|
||||
font-size: 25px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #d7d7d7;
|
||||
font-family: 'signika-negative', sans-serif;
|
||||
@@ -91,6 +97,7 @@ button:active, input[type=submit]:active {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button:hover, input[type="submit"]:hover {
|
||||
@@ -142,6 +149,83 @@ input {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.flex-row-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-row-container-left-align {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.animated-placeholder {
|
||||
animation-fill-mode: forwards;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: pulse-background;
|
||||
animation-timing-function: ease-in;
|
||||
animation-direction: alternate;
|
||||
background: rgb(238,238,238);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
opacity: 0.15;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.animated-placeholder-short {
|
||||
width: 100%;
|
||||
max-width: 15em;
|
||||
height: 2em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.animated-placeholder-long {
|
||||
width: 100%;
|
||||
max-width: 30em;
|
||||
height: 8em;
|
||||
margin: 0 auto 0.5em auto;
|
||||
}
|
||||
|
||||
.animated-placeholder-invisible {
|
||||
background-color: transparent;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.placeholder-row {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.placeholder-row .animated-placeholder-short {
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
@keyframes placeholder {
|
||||
0%{
|
||||
background-position: 50% 0
|
||||
}
|
||||
100%{
|
||||
background-position: -50% 0
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-background {
|
||||
from {
|
||||
background-color: rgb(120 120 120);
|
||||
} to {
|
||||
background-color: rgb(255 255 255);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes fade-in-slide-down-then-exit {
|
||||
0% {
|
||||
|
||||
@@ -82,13 +82,19 @@
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#deck-container, #custom-roles-container {
|
||||
margin: 0.5em 0;
|
||||
#deck-container, #custom-roles-container, #step-3 {
|
||||
margin: 1em 0;
|
||||
background-color: #1f1f1f;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#step-3 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#deck {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -151,3 +157,99 @@ input[type="number"] {
|
||||
.dropdown {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.creation-step {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: transparent;
|
||||
border-radius: 50%;
|
||||
border: 2px solid whitesmoke;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.creation-step-filled {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
#creation-step-container {
|
||||
margin-top: 2em;
|
||||
width: 100%;
|
||||
min-height: 16em;
|
||||
}
|
||||
|
||||
#creation-step-container > div:nth-child(2) {
|
||||
animation: fade-in 0.5s ease-out;
|
||||
}
|
||||
|
||||
#step-title {
|
||||
margin: 0 auto 1em auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#creation-step-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#creation-step-tracker {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
#step-forward-button, #step-back-button {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#step-forward-button {
|
||||
right: 15%;
|
||||
}
|
||||
|
||||
#step-back-button {
|
||||
left: 15%;
|
||||
}
|
||||
|
||||
#step-1 div {
|
||||
background-color: black;
|
||||
color: whitesmoke;
|
||||
padding: 1em;
|
||||
max-width: 20em;
|
||||
margin: 0.5em;
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 3px;
|
||||
font-size: 25px;
|
||||
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
#step-1 div.option-selected {
|
||||
border: 2px solid #0075F2;
|
||||
background-color: #252730;
|
||||
}
|
||||
|
||||
#step-1 div > strong {
|
||||
color: #0075F2;
|
||||
}
|
||||
|
||||
#step-1 div:hover {
|
||||
border: 2px solid #0075F2;
|
||||
}
|
||||
|
||||
.review-option {
|
||||
background-color: #1f1f1f;
|
||||
color: whitesmoke;
|
||||
padding: 10px;
|
||||
width: fit-content;
|
||||
border-radius: 3px;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
} to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,36 +47,44 @@
|
||||
</form>
|
||||
</div>
|
||||
<h1>Create A Game</h1>
|
||||
<h3>
|
||||
Creating a game gives you the moderator role with certain special permissions. You will not be dealt a card.
|
||||
</h3>
|
||||
<div id="custom-roles-container">
|
||||
<label for="add-card-to-deck-form">Custom Roles</label>
|
||||
<form id="add-card-to-deck-form"></form>
|
||||
<button id="custom-role-btn">Create Custom Role</button>
|
||||
<div id="creation-step-tracker">
|
||||
<div id="tracker-step-1" class="creation-step creation-step-filled"></div>
|
||||
<div id="tracker-step-2" class="creation-step"></div>
|
||||
<div id="tracker-step-3" class="creation-step"></div>
|
||||
<div id="tracker-step-4" class="creation-step"></div>
|
||||
</div>
|
||||
<div id="deck-container">
|
||||
<label for="deck">Game Deck: 0 Players</label>
|
||||
<div id="deck"></div>
|
||||
<div id="creation-step-container">
|
||||
<h2 id="step-title">Select your method of moderation:</h2>
|
||||
<div class="placeholder-row">
|
||||
<div class="animated-placeholder animated-placeholder-short"></div>
|
||||
<div class="animated-placeholder animated-placeholder-short animated-placeholder-invisible"></div>
|
||||
</div>
|
||||
<div class="animated-placeholder animated-placeholder-long"></div>
|
||||
<div class="animated-placeholder animated-placeholder-long"></div>
|
||||
</div>
|
||||
<form id="game-form">
|
||||
<div>
|
||||
<label for="game-time">Timer (Optional)</label>
|
||||
<div id="game-time">
|
||||
<label for="game-hours">Hours (max 5)</label>
|
||||
<input type="number" id="game-hours" name="game-hours"
|
||||
min="0" max="5" />
|
||||
<label for="game-hours">Minutes</label>
|
||||
<input type="number" id="game-minutes" name="game-minutes"
|
||||
min="1" max="60" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="mod-name">Your Name</label>
|
||||
<input id="mod-name" type="text" maxlength="30" required/>
|
||||
</div>
|
||||
<input id="create-game" type="submit" value="Create"/>
|
||||
</form>
|
||||
<div id="creation-step-buttons"></div>
|
||||
<!-- <div id="deck-container">-->
|
||||
<!-- <label for="deck">Game Deck: 0 Players</label>-->
|
||||
<!-- <div id="deck"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <form id="game-form">-->
|
||||
<!-- <div>-->
|
||||
<!-- <label for="game-time">Timer (Optional)</label>-->
|
||||
<!-- <div id="game-time">-->
|
||||
<!-- <label for="game-hours">Hours (max 5)</label>-->
|
||||
<!-- <input type="number" id="game-hours" name="game-hours"-->
|
||||
<!-- min="0" max="5" />-->
|
||||
<!-- <label for="game-hours">Minutes</label>-->
|
||||
<!-- <input type="number" id="game-minutes" name="game-minutes"-->
|
||||
<!-- min="1" max="60" />-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div>-->
|
||||
<!-- <label for="mod-name">Your Name</label>-->
|
||||
<!-- <input id="mod-name" type="text" maxlength="30" required/>-->
|
||||
<!-- </div>-->
|
||||
<!-- <input id="create-game" type="submit" value="Create"/>-->
|
||||
<!-- </form>-->
|
||||
</div>
|
||||
<script type="module">
|
||||
import { create } from "../scripts/create.js";
|
||||
|
||||
Reference in New Issue
Block a user