diff --git a/client/model/Game.js b/client/model/Game.js
index c80044b..763a34f 100644
--- a/client/model/Game.js
+++ b/client/model/Game.js
@@ -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
}
}
diff --git a/client/modules/GameCreationStepManager.js b/client/modules/GameCreationStepManager.js
new file mode 100644
index 0000000..3bd2943
--- /dev/null
+++ b/client/modules/GameCreationStepManager.js
@@ -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 =
+ "
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));
+}
diff --git a/client/modules/Toast.js b/client/modules/Toast.js
index c5b5691..34585be 100644
--- a/client/modules/Toast.js
+++ b/client/modules/Toast.js
@@ -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();
diff --git a/client/scripts/create.js b/client/scripts/create.js
index bdd5018..b0260a1 100644
--- a/client/scripts/create.js
+++ b/client/scripts/create.js
@@ -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 =
- "" +
- "" +
- "";
-
- 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',
diff --git a/client/scripts/home.js b/client/scripts/home.js
index 4731090..87e4872 100644
--- a/client/scripts/home.js
+++ b/client/scripts/home.js
@@ -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);
diff --git a/client/styles/GLOBAL.css b/client/styles/GLOBAL.css
index bcfb95c..0efbcaf 100644
--- a/client/styles/GLOBAL.css
+++ b/client/styles/GLOBAL.css
@@ -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% {
diff --git a/client/styles/create.css b/client/styles/create.css
index c077ff0..92d1766 100644
--- a/client/styles/create.css
+++ b/client/styles/create.css
@@ -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;
+ }
+}
diff --git a/client/views/create.html b/client/views/create.html
index 906b51f..97a5ba1 100644
--- a/client/views/create.html
+++ b/client/views/create.html
@@ -47,36 +47,44 @@
Create A Game
-
- Creating a game gives you the moderator role with certain special permissions. You will not be dealt a card.
-
-
-
-
-
+
-
-
-
+
+
Select your method of moderation:
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+