diff --git a/client/src/config/defaultCards.js b/client/src/config/defaultRoles.js
similarity index 95%
rename from client/src/config/defaultCards.js
rename to client/src/config/defaultRoles.js
index b22c004..c589fc6 100644
--- a/client/src/config/defaultCards.js
+++ b/client/src/config/defaultRoles.js
@@ -1,4 +1,4 @@
-export const defaultCards = [
+export const defaultRoles = [
{
role: 'Villager',
team: 'good',
@@ -20,7 +20,7 @@ export const defaultCards = [
description: 'Each night, learn if a chosen person is the Seer.'
},
{
- role: 'Minion',
+ role: 'Knowing Minion',
team: 'evil',
description: 'You are an evil Villager, and you know who the Werewolves are.'
},
diff --git a/client/src/config/globals.js b/client/src/config/globals.js
index 72d5bb6..72f5e8b 100644
--- a/client/src/config/globals.js
+++ b/client/src/config/globals.js
@@ -1,4 +1,5 @@
export const globals = {
+ CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789',
USER_SIGNATURE_LENGTH: 25,
CLOCK_TICK_INTERVAL_MILLIS: 100,
MAX_CUSTOM_ROLE_NAME_LENGTH: 30,
diff --git a/client/src/images/framed-phone-screenshot-2.png b/client/src/images/framed-phone-screenshot-2.png
new file mode 100644
index 0000000..4ad6746
Binary files /dev/null and b/client/src/images/framed-phone-screenshot-2.png differ
diff --git a/client/src/images/remove.svg b/client/src/images/remove.svg
new file mode 100644
index 0000000..11ba3cf
--- /dev/null
+++ b/client/src/images/remove.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/client/src/images/roles/BrutalHunter.png b/client/src/images/roles/BrutalHunter.png
new file mode 100644
index 0000000..c4251e3
Binary files /dev/null and b/client/src/images/roles/BrutalHunter.png differ
diff --git a/client/src/images/roles/Minion.png b/client/src/images/roles/KnowingMinion.png
similarity index 100%
rename from client/src/images/roles/Minion.png
rename to client/src/images/roles/KnowingMinion.png
diff --git a/client/src/images/tutorial/add-custom-role.gif b/client/src/images/tutorial/add-custom-role.gif
deleted file mode 100644
index ad9b7b2..0000000
Binary files a/client/src/images/tutorial/add-custom-role.gif and /dev/null differ
diff --git a/client/src/images/tutorial/add-role-to-deck.gif b/client/src/images/tutorial/add-role-to-deck.gif
new file mode 100644
index 0000000..884a7f3
Binary files /dev/null and b/client/src/images/tutorial/add-role-to-deck.gif differ
diff --git a/client/src/images/tutorial/create-custom-role.gif b/client/src/images/tutorial/create-custom-role.gif
new file mode 100644
index 0000000..7043beb
Binary files /dev/null and b/client/src/images/tutorial/create-custom-role.gif differ
diff --git a/client/src/images/tutorial/custom-roles.PNG b/client/src/images/tutorial/custom-roles.PNG
deleted file mode 100644
index e5413d5..0000000
Binary files a/client/src/images/tutorial/custom-roles.PNG and /dev/null differ
diff --git a/client/src/images/tutorial/default-roles.PNG b/client/src/images/tutorial/default-roles.PNG
deleted file mode 100644
index 57f9c7c..0000000
Binary files a/client/src/images/tutorial/default-roles.PNG and /dev/null differ
diff --git a/client/src/images/tutorial/moderation-option.png b/client/src/images/tutorial/moderation-option.png
index b82fa70..0bf06d0 100644
Binary files a/client/src/images/tutorial/moderation-option.png and b/client/src/images/tutorial/moderation-option.png differ
diff --git a/client/src/images/tutorial/transfer-mod.gif b/client/src/images/tutorial/transfer-mod.gif
index 5470e96..d6c4609 100644
Binary files a/client/src/images/tutorial/transfer-mod.gif and b/client/src/images/tutorial/transfer-mod.gif differ
diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js
index 1299271..fb31516 100644
--- a/client/src/modules/DeckStateManager.js
+++ b/client/src/modules/DeckStateManager.js
@@ -1,43 +1,14 @@
import { globals } from '../config/globals.js';
-import { toast } from './Toast.js';
-import { ModalManager } from './ModalManager.js';
+import { HTMLFragments } from './HTMLFragments.js';
export class DeckStateManager {
constructor () {
- this.deck = null;
- this.customRoleOptions = [];
- this.createMode = false;
- this.currentlyEditingRoleName = null;
+ this.deck = [];
}
addToDeck (role) {
- const option = this.customRoleOptions.find((option) => option.role === role);
- if (option) {
- option.quantity = 0;
- this.deck.push(option);
- this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1);
- }
- }
-
- addToCustomRoleOptions (role) {
- this.customRoleOptions.push(role);
- localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true))));
- }
-
- updateCustomRoleOption (option, name, description, team) {
- option.role = name;
- option.description = description;
- option.team = team;
- localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true))));
- }
-
- removeFromCustomRoleOptions (name) {
- const option = this.customRoleOptions.find((option) => option.role === name);
- if (option) {
- this.customRoleOptions.splice(this.customRoleOptions.indexOf(option), 1);
- localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoleOptions.concat(this.deck.filter(card => card.custom === true))));
- toast('"' + name + '" deleted.', 'error', true, true, 'short');
- }
+ role.quantity = 1;
+ this.deck.push(role);
}
addCopyOfCard (role) {
@@ -49,27 +20,25 @@ export class DeckStateManager {
removeCopyOfCard (role) {
const existingCard = this.deck.find((card) => card.role === role);
- if (existingCard && existingCard.quantity > 0) {
+ if (existingCard.quantity > 0) {
existingCard.quantity -= 1;
}
}
- getCurrentDeck () { return this.deck; }
+ removeRoleEntirelyFromDeck (entry) {
+ const existingCard = this.deck.find((card) => card.role === entry.role);
+ if (existingCard) {
+ existingCard.quantity = 0;
+ this.updateDeckStatus();
+ }
+ }
- getCard (role) {
+ hasRole (role) {
return this.deck.find(
(card) => card.role.toLowerCase().trim() === role.toLowerCase().trim()
);
}
- getCurrentCustomRoleOptions () { return this.customRoleOptions; }
-
- getCustomRoleOption (role) {
- return this.customRoleOptions.find(
- (option) => option.role.toLowerCase().trim() === role.toLowerCase().trim()
- );
- };
-
getDeckSize () {
let total = 0;
for (const role of this.deck) {
@@ -78,86 +47,67 @@ export class DeckStateManager {
return total;
}
- loadCustomRolesFromCookies () {
- const customRoles = localStorage.getItem('play-werewolf-custom-roles');
- if (customRoles !== null && validateCustomRoleCookie(customRoles)) {
- this.customRoleOptions = JSON.parse(customRoles); // we know it is valid JSON from the validate function
- }
- }
-
- loadCustomRolesFromFile (file, updateRoleListFunction, loadDefaultCardsFn, showIncludedCardsFn) {
- const reader = new FileReader();
- reader.onerror = (e) => {
- toast(reader.error.message, 'error', true, true, 'medium');
- };
- reader.onload = (e) => {
- let string;
- if (typeof e.target.result !== 'string') {
- string = new TextDecoder('utf-8').decode(e.target.result);
- } else {
- string = e.target.result;
+ updateDeckStatus = () => {
+ document.getElementById('deck-count').innerText = this.getDeckSize() + ' Players';
+ if (this.deck.length > 0) {
+ if (document.getElementById('deck-list-placeholder')) {
+ document.getElementById('deck-list-placeholder').remove();
}
- if (validateCustomRoleCookie(string)) {
- this.customRoleOptions = JSON.parse(string); // we know it is valid JSON from the validate function
- ModalManager.dispelModal('upload-custom-roles-modal', 'modal-background');
- toast('Roles imported successfully', 'success', true, true, 'short');
- localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoleOptions));
- updateRoleListFunction(this, document.getElementById('deck-select'));
- // loadDefaultCardsFn(this);
- // showIncludedCardsFn(this);
- } else {
- toast(
- 'Invalid formatting. Make sure you import the file as downloaded from this page.',
- 'error',
- true,
- true,
- 'medium'
- );
- }
- };
- reader.readAsText(file);
- }
-
- // via https://stackoverflow.com/a/18197341
- downloadCustomRoles (filename, text) {
- const element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
- element.setAttribute('download', filename);
-
- element.style.display = 'none';
- document.body.appendChild(element);
-
- element.click();
-
- document.body.removeChild(element);
- }
-}
-
-// this is user-supplied, so we should validate it fully
-function validateCustomRoleCookie (cookie) {
- const valid = false;
- if (typeof cookie === 'string' && new Blob([cookie]).size <= 1000000) {
- try {
- const cookieJSON = JSON.parse(cookie);
- if (Array.isArray(cookieJSON)) {
- for (const entry of cookieJSON) {
- if (typeof entry === 'object') {
- if (typeof entry.role !== 'string' || entry.role.length > globals.MAX_CUSTOM_ROLE_NAME_LENGTH
- || typeof entry.team !== 'string' || (entry.team !== globals.ALIGNMENT.GOOD && entry.team !== globals.ALIGNMENT.EVIL)
- || typeof entry.description !== 'string' || entry.description.length > globals.MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH
- ) {
- return false;
- }
+ const sortedDeck = this.deck.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 < sortedDeck.length; i ++) {
+ const existingCardEl = document.querySelector('#deck-list [data-role-id="' + sortedDeck[i].id + '"]');
+ if (sortedDeck[i].quantity > 0) {
+ if (existingCardEl) {
+ existingCardEl.querySelector('.role-name').innerText = sortedDeck[i].quantity + 'x ' + sortedDeck[i].role;
} else {
- return false;
+ const roleEl = document.createElement('div');
+ roleEl.dataset.roleId = sortedDeck[i].id;
+ roleEl.classList.add('added-role');
+ roleEl.innerHTML = HTMLFragments.DECK_SELECT_ROLE_ADDED_TO_DECK;
+ // roleEl.classList.add('deck-role');
+ if (sortedDeck[i].team === globals.ALIGNMENT.GOOD) {
+ roleEl.classList.add(globals.ALIGNMENT.GOOD);
+ } else {
+ roleEl.classList.add(globals.ALIGNMENT.EVIL);
+ }
+ roleEl.querySelector('.role-name').innerText = sortedDeck[i].quantity + 'x ' + sortedDeck[i].role;
+ document.getElementById('deck-list').appendChild(roleEl);
+ const minusOneHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ e.preventDefault();
+ this.removeCopyOfCard(sortedDeck[i].role);
+ this.updateDeckStatus();
+ }
+ };
+ roleEl.querySelector('.role-remove').addEventListener('click', minusOneHandler);
+ roleEl.querySelector('.role-remove').addEventListener('keyup', minusOneHandler);
+ }
+ } else {
+ sortedDeck[i].markedForRemoval = true;
+ if (existingCardEl) {
+ existingCardEl.remove();
}
}
- return true;
+ this.deck = this.deck.filter((card) => {
+ if (card.markedForRemoval) {
+ card.markedForRemoval = false;
+ return false;
+ } else {
+ return true;
+ }
+ });
+ }
+ if (this.deck.length === 0) {
+ const placeholder = document.createElement('div');
+ placeholder.setAttribute('id', 'deck-list-placeholder');
+ placeholder.innerText = 'Add a card from the role box.';
+ document.getElementById('deck-list').appendChild(placeholder);
}
- } catch (e) {
- return false;
}
- }
-
- return valid;
+ };
}
diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js
index 1d79166..169d35b 100644
--- a/client/src/modules/GameCreationStepManager.js
+++ b/client/src/modules/GameCreationStepManager.js
@@ -4,16 +4,15 @@ import { ModalManager } from './ModalManager.js';
import { XHRUtility } from './XHRUtility.js';
import { globals } from '../config/globals.js';
import { HTMLFragments } from './HTMLFragments.js';
-import { defaultCards } from '../config/defaultCards.js';
import { UserUtility } from './UserUtility.js';
+import { RoleBox } from './RoleBox.js';
export class GameCreationStepManager {
constructor (deckManager) {
- loadDefaultCards(deckManager);
- deckManager.loadCustomRolesFromCookies();
this.step = 1;
this.currentGame = new Game(null, null, null, null);
this.deckManager = deckManager;
+ this.roleBox = null;
this.defaultBackHandler = () => {
cancelCurrentToast();
this.removeStepElementsFromDOM(this.step);
@@ -38,7 +37,7 @@ export class GameCreationStepManager {
title: 'Create your deck of cards:',
forwardHandler: () => {
if (this.deckManager.getDeckSize() >= 3 && this.deckManager.getDeckSize() <= 50) {
- this.currentGame.deck = deckManager.getCurrentDeck().filter((card) => card.quantity > 0);
+ this.currentGame.deck = this.deckManager.deck.filter((card) => card.quantity > 0);
cancelCurrentToast();
this.removeStepElementsFromDOM(this.step);
this.incrementStep();
@@ -173,7 +172,7 @@ export class GameCreationStepManager {
showButtons(false, true, this.steps[step].forwardHandler, null);
break;
case 2:
- renderRoleSelectionStep(this.currentGame, containerId, step, this.deckManager);
+ this.renderRoleSelectionStep(this.currentGame, containerId, step, this.deckManager);
showButtons(true, true, this.steps[step].forwardHandler, this.steps[step].backHandler);
break;
case 3:
@@ -197,6 +196,81 @@ export class GameCreationStepManager {
removeStepElementsFromDOM (stepNumber) {
document.getElementById('step-' + stepNumber)?.remove();
}
+
+ renderRoleSelectionStep = (game, containerId, step, deckManager) => {
+ const stepContainer = document.createElement('div');
+
+ setAttributes(stepContainer, { id: 'step-' + step, class: 'flex-row-container-left-align step' });
+
+ stepContainer.innerHTML += HTMLFragments.CREATE_GAME_DECK_STATUS;
+
+ document.getElementById(containerId).appendChild(stepContainer);
+ this.roleBox = new RoleBox(stepContainer, deckManager);
+ this.roleBox.loadDefaultRoles();
+ this.roleBox.loadCustomRolesFromCookies();
+ this.roleBox.displayDefaultRoles(document.getElementById('role-select'));
+
+ const exportHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ e.preventDefault();
+ this.roleBox.downloadCustomRoles('play-werewolf-custom-roles', JSON.stringify(
+ this.roleBox.customRoles
+ .map((option) => (
+ { role: option.role, team: option.team, description: option.description, custom: option.custom }
+ ))
+ ));
+ toast('Custom roles downloading', 'success', true, true);
+ document.getElementById('custom-role-actions').style.display = 'none';
+ }
+ };
+
+ document.querySelector('#custom-roles-export').addEventListener('click', exportHandler);
+ document.querySelector('#custom-roles-export').addEventListener('keyup', exportHandler);
+
+ const importHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ e.preventDefault();
+ ModalManager.displayModal('upload-custom-roles-modal', 'modal-background', 'close-upload-custom-roles-modal-button');
+ }
+ };
+ document.querySelector('#custom-roles-import').addEventListener('click', importHandler);
+ document.querySelector('#custom-roles-import').addEventListener('keyup', importHandler);
+
+ document.getElementById('upload-custom-roles-form').onsubmit = (e) => {
+ e.preventDefault();
+ const fileList = document.getElementById('upload-custom-roles').files;
+ if (fileList.length > 0) {
+ const file = fileList[0];
+ if (file.size > 1000000) {
+ toast('Your file is too large (max 1MB)', 'error', true, true, 'medium');
+ return;
+ }
+ if (file.type !== 'text/plain') {
+ toast('Your file must be a text file', 'error', true, true, 'medium');
+ return;
+ }
+
+ this.roleBox.loadCustomRolesFromFile(file);
+ } else {
+ toast('You must upload a text file', 'error', true, true, 'medium');
+ }
+ };
+
+ const clickHandler = () => {
+ const actions = document.getElementById('custom-role-actions');
+ if (window.getComputedStyle(actions, null).display !== 'none') {
+ actions.style.display = 'none';
+ } else {
+ actions.style.display = 'block';
+ }
+ };
+
+ document.getElementById('custom-role-hamburger').addEventListener('click', clickHandler);
+
+ deckManager.updateDeckStatus();
+
+ initializeRemainingEventListeners(deckManager, this.roleBox);
+ };
}
function renderNameStep (containerId, step, game, steps) {
@@ -208,7 +282,6 @@ function renderNameStep (containerId, step, game, steps) {
const nameInput = document.querySelector('#moderator-name');
nameInput.value = game.moderatorName;
nameInput.addEventListener('keyup', steps['4'].forwardHandler);
- nameInput.focus();
}
function renderModerationTypeStep (game, containerId, stepNumber) {
@@ -253,78 +326,6 @@ function renderModerationTypeStep (game, containerId, stepNumber) {
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 step' });
-
- stepContainer.innerHTML = HTMLFragments.CREATE_GAME_CUSTOM_ROLES;
- stepContainer.innerHTML += HTMLFragments.CREATE_GAME_DECK_STATUS;
- stepContainer.innerHTML += HTMLFragments.CREATE_GAME_DECK;
-
- const exportHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- e.preventDefault();
- deckManager.downloadCustomRoles('play-werewolf-custom-roles', JSON.stringify(
- deckManager.getCurrentCustomRoleOptions()
- .map((option) => (
- { role: option.role, team: option.team, description: option.description, custom: option.custom }
- ))
- ));
- }
- };
- document.getElementById(containerId).appendChild(stepContainer);
- document.querySelector('#custom-roles-export').addEventListener('click', exportHandler);
- document.querySelector('#custom-roles-export').addEventListener('keyup', exportHandler);
-
- const importHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- e.preventDefault();
- ModalManager.displayModal('upload-custom-roles-modal', 'modal-background', 'close-upload-custom-roles-modal-button');
- }
- };
- document.querySelector('#custom-roles-import').addEventListener('click', importHandler);
- document.querySelector('#custom-roles-import').addEventListener('keyup', importHandler);
-
- document.getElementById('upload-custom-roles-form').onsubmit = (e) => {
- e.preventDefault();
- const fileList = document.getElementById('upload-custom-roles').files;
- if (fileList.length > 0) {
- const file = fileList[0];
- if (file.size > 1000000) {
- toast('Your file is too large (max 1MB)', 'error', true, true, 'medium');
- return;
- }
- if (file.type !== 'text/plain') {
- toast('Your file must be a text file', 'error', true, true, 'medium');
- return;
- }
-
- deckManager.loadCustomRolesFromFile(file, updateCustomRoleOptionsList, loadDefaultCards, showIncludedCards);
- } else {
- toast('You must upload a text file', 'error', true, true, 'medium');
- }
- };
-
- const clickHandler = () => {
- const actions = document.getElementById('custom-role-actions');
- if (window.getComputedStyle(actions, null).display !== 'none') {
- actions.style.display = 'none';
- } else {
- actions.style.display = 'block';
- }
- };
-
- document.getElementById('custom-role-hamburger').addEventListener('click', clickHandler);
-
- showIncludedCards(deckManager);
-
- loadCustomRoles(deckManager);
-
- updateDeckStatus(deckManager);
-
- initializeRemainingEventListeners(deckManager);
-}
-
function renderTimerStep (containerId, stepNumber, game, steps) {
const div = document.createElement('div');
div.setAttribute('id', 'step-' + stepNumber);
@@ -465,119 +466,25 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu
}
}
-// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state.
-function showIncludedCards (deckManager) {
- document.querySelectorAll('.compact-card').forEach((el) => { el.remove(); });
- for (let i = 0; i < deckManager.getCurrentDeck().length; i ++) {
- const card = deckManager.getCurrentDeck()[i];
- const cardEl = constructCompactDeckBuilderElement(card, deckManager);
- if (card.team === globals.ALIGNMENT.GOOD) {
- document.getElementById('deck-good').appendChild(cardEl);
- } else {
- document.getElementById('deck-evil').appendChild(cardEl);
- }
- }
-}
-
-/* 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) {
- addOptionsToList(deckManager, document.getElementById('deck-select'));
-}
-
-function loadDefaultCards (deckManager) {
- defaultCards.sort((a, b) => {
- if (a.team !== b.team) {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
- }
- return a.role.localeCompare(b.role);
- });
- const deck = [];
- for (let i = 0; i < defaultCards.length; i ++) {
- const card = defaultCards[i];
- card.quantity = 0;
- deck.push(card);
- }
- deckManager.deck = deck;
-}
-
-function constructCompactDeckBuilderElement (card, deckManager) {
- const cardContainer = document.createElement('div');
- const alignmentClass = card.team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL;
-
- cardContainer.setAttribute('class', 'compact-card ' + alignmentClass);
-
- cardContainer.setAttribute('id', 'card-' + card.role.replaceAll(' ', '-'));
-
- cardContainer.innerHTML =
- "
' +
- "' +
- "';
-
- cardContainer.querySelector('.card-role').innerText = card.role;
- cardContainer.title = card.role;
- cardContainer.querySelector('.card-quantity').innerText = card.quantity;
-
- if (card.quantity > 0) {
- cardContainer.classList.add('selected-card');
- }
-
- const addHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- deckManager.addCopyOfCard(card.role);
- updateDeckStatus(deckManager);
- cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity;
- if (deckManager.getCard(card.role).quantity > 0) {
- document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.add('selected-card');
- }
- }
- };
-
- const removeHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- deckManager.removeCopyOfCard(card.role);
- updateDeckStatus(deckManager);
- cardContainer.querySelector('.card-quantity').innerText = deckManager.getCard(card.role).quantity;
- if (deckManager.getCard(card.role).quantity === 0) {
- document.getElementById('card-' + card.role.replaceAll(' ', '-')).classList.remove('selected-card');
- }
- }
- };
-
- cardContainer.querySelector('.compact-card-right').addEventListener('click', addHandler);
- cardContainer.querySelector('.compact-card-right').addEventListener('keyup', addHandler);
- cardContainer.querySelector('.compact-card-left').addEventListener('click', removeHandler);
- cardContainer.querySelector('.compact-card-left').addEventListener('keyup', removeHandler);
-
- return cardContainer;
-}
-
-function initializeRemainingEventListeners (deckManager) {
+function initializeRemainingEventListeners (deckManager, roleBox) {
document.getElementById('role-form').onsubmit = (e) => {
e.preventDefault();
const name = document.getElementById('role-name').value.trim();
const description = document.getElementById('role-description').value.trim();
const team = document.getElementById('role-alignment').value.toLowerCase().trim();
- if (deckManager.createMode) {
- if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name
- processNewCustomRoleSubmission(name, description, team, deckManager, false);
+ if (roleBox.createMode) {
+ if (!roleBox.getCustomRole(name) && !roleBox.getDefaultRole(name)) { // confirm there is no existing custom role with the same name
+ processNewCustomRoleSubmission(name, description, team, deckManager, false, roleBox);
} else {
- toast('There is already a role with this name', 'error', true, true, 'short');
+ toast('There is already a default or custom role with this name', 'error', true, true, 'short');
}
} else {
- const option = deckManager.getCustomRoleOption(deckManager.currentlyEditingRoleName);
- if (name === option.role) { // did they edit the name?
- processNewCustomRoleSubmission(name, description, team, deckManager, true, option);
+ const entry = roleBox.getCustomRole(roleBox.currentlyEditingRoleName);
+ if (name === entry.role) { // did they edit the name?
+ processNewCustomRoleSubmission(name, description, team, deckManager, true, roleBox, entry);
} else {
- if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) {
- processNewCustomRoleSubmission(name, description, team, deckManager, true, option);
+ if (!roleBox.getCustomRole(name) && !roleBox.getDefaultRole(name)) {
+ processNewCustomRoleSubmission(name, description, team, deckManager, true, roleBox, entry);
} else {
toast('There is already a role with this name', 'error', true, true, 'short');
}
@@ -588,8 +495,8 @@ function initializeRemainingEventListeners (deckManager) {
'click', () => {
const createBtn = document.getElementById('create-role-button');
createBtn.setAttribute('value', 'Create');
- deckManager.createMode = true;
- deckManager.currentlyEditingRoleName = null;
+ roleBox.createMode = true;
+ roleBox.currentlyEditingRoleName = null;
document.getElementById('role-name').value = '';
document.getElementById('role-alignment').value = globals.ALIGNMENT.GOOD;
document.getElementById('role-description').value = '';
@@ -602,7 +509,7 @@ function initializeRemainingEventListeners (deckManager) {
);
}
-function processNewCustomRoleSubmission (name, description, team, deckManager, isUpdate, option = null) {
+function processNewCustomRoleSubmission (name, description, team, deckManager, isUpdate, roleBox, option = null) {
if (name.length > 40) {
toast('Your name is too long (max 40 characters).', 'error', true);
return;
@@ -612,147 +519,16 @@ function processNewCustomRoleSubmission (name, description, team, deckManager, i
return;
}
if (isUpdate) {
- deckManager.updateCustomRoleOption(option, name, description, team);
+ roleBox.updateCustomRole(option, name, description, team);
ModalManager.dispelModal('role-modal', 'modal-background');
toast('Role Updated', 'success', true);
} else {
- deckManager.addToCustomRoleOptions({ role: name, description: description, team: team, custom: true });
+ roleBox.addCustomRole({ role: name, description: description, team: team, custom: true });
ModalManager.dispelModal('role-modal', 'modal-background');
toast('Role Created', 'success', true);
}
-
- updateCustomRoleOptionsList(deckManager, document.getElementById('deck-select'));
-}
-
-function updateCustomRoleOptionsList (deckManager, selectEl) {
- document.querySelectorAll('#deck-select .deck-select-role').forEach(e => e.remove());
- addOptionsToList(deckManager, selectEl);
-}
-
-function addOptionsToList (deckManager, selectEl) {
- const options = deckManager.getCurrentCustomRoleOptions();
- 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 ++) {
- const optionEl = document.createElement('div');
- optionEl.innerHTML = HTMLFragments.DECK_SELECT_ROLE;
- optionEl.classList.add('deck-select-role');
- const alignmentClass = options[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL;
- optionEl.classList.add(alignmentClass);
- optionEl.querySelector('.deck-select-role-name').innerText = options[i].role;
- selectEl.appendChild(optionEl);
- }
-
- addCustomRoleEventListeners(deckManager, selectEl);
-}
-
-function addCustomRoleEventListeners (deckManager, select) {
- document.querySelectorAll('.deck-select-role').forEach((role) => {
- const name = role.querySelector('.deck-select-role-name').innerText;
- const includeHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- e.preventDefault();
- if (!deckManager.getCard(name)) {
- deckManager.addToDeck(name);
- const cardEl = constructCompactDeckBuilderElement(deckManager.getCard(name), deckManager);
- toast('"' + name + '" made available below.', 'success', true, true, 'medium');
- if (deckManager.getCard(name).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, 'short');
- }
- }
- };
- role.querySelector('.deck-select-include').addEventListener('click', includeHandler);
- role.querySelector('.deck-select-include').addEventListener('keyup', includeHandler);
-
- const removeHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- if (confirm("Delete the role '" + name + "'?")) {
- e.preventDefault();
- deckManager.removeFromCustomRoleOptions(name);
- updateCustomRoleOptionsList(deckManager, select);
- }
- }
- };
- role.querySelector('.deck-select-remove').addEventListener('click', removeHandler);
- role.querySelector('.deck-select-remove').addEventListener('keyup', removeHandler);
-
- const infoHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- const alignmentEl = document.getElementById('custom-role-info-modal-alignment');
- alignmentEl.classList.remove(globals.ALIGNMENT.GOOD);
- alignmentEl.classList.remove(globals.ALIGNMENT.EVIL);
- e.preventDefault();
- const option = deckManager.getCustomRoleOption(name);
- document.getElementById('custom-role-info-modal-name').innerText = name;
- alignmentEl.classList.add(option.team);
- document.getElementById('custom-role-info-modal-description').innerText = option.description;
- alignmentEl.innerText = option.team;
- ModalManager.displayModal('custom-role-info-modal', 'modal-background', 'close-custom-role-info-modal-button');
- }
- };
- role.querySelector('.deck-select-info').addEventListener('click', infoHandler);
- role.querySelector('.deck-select-info').addEventListener('keyup', infoHandler);
-
- const editHandler = (e) => {
- if (e.type === 'click' || e.code === 'Enter') {
- e.preventDefault();
- const option = deckManager.getCustomRoleOption(name);
- document.getElementById('role-name').value = option.role;
- document.getElementById('role-alignment').value = option.team;
- document.getElementById('role-description').value = option.description;
- deckManager.createMode = false;
- deckManager.currentlyEditingRoleName = option.role;
- const createBtn = document.getElementById('create-role-button');
- createBtn.setAttribute('value', 'Update');
- ModalManager.displayModal('role-modal', 'modal-background', 'close-modal-button');
- }
- };
- role.querySelector('.deck-select-edit').addEventListener('click', editHandler);
- role.querySelector('.deck-select-edit').addEventListener('keyup', editHandler);
- });
-}
-
-function updateDeckStatus (deckManager) {
- document.querySelectorAll('.deck-role').forEach((el) => el.remove());
- document.getElementById('deck-count').innerText = deckManager.getDeckSize() + ' Players';
- if (deckManager.getDeckSize() === 0) {
- const placeholder = document.createElement('div');
- placeholder.setAttribute('id', 'deck-list-placeholder');
- placeholder.innerText = 'Add a card from the available roles below.';
- document.getElementById('deck-list').appendChild(placeholder);
- } else {
- if (document.getElementById('deck-list-placeholder')) {
- document.getElementById('deck-list-placeholder').remove();
- }
- const sortedDeck = deckManager.getCurrentDeck().sort((a, b) => {
- if (a.team !== b.team) {
- return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
- }
- return a.role.localeCompare(b.role);
- });
- for (const card of sortedDeck) {
- if (card.quantity > 0) {
- const roleEl = document.createElement('div');
- roleEl.classList.add('deck-role');
- if (card.team === globals.ALIGNMENT.GOOD) {
- roleEl.classList.add(globals.ALIGNMENT.GOOD);
- } else {
- roleEl.classList.add(globals.ALIGNMENT.EVIL);
- }
- roleEl.innerText = card.quantity + 'x ' + card.role;
- document.getElementById('deck-list').appendChild(roleEl);
- }
- }
+ if (roleBox.category === 'custom') {
+ roleBox.displayCustomRoles(document.getElementById('role-select'));
}
}
diff --git a/client/src/modules/HTMLFragments.js b/client/src/modules/HTMLFragments.js
index db78500..e91a2d5 100644
--- a/client/src/modules/HTMLFragments.js
+++ b/client/src/modules/HTMLFragments.js
@@ -248,11 +248,15 @@ export const HTMLFragments = {
-
Export
-
Import
+
Export Roles
+
Import Roles
-
-
+
+
+
+
+
+
`,
CREATE_GAME_DECK_STATUS:
@@ -261,11 +265,22 @@ export const HTMLFragments = {
`,
DECK_SELECT_ROLE:
- `
-
-

-

-

-

+ `
+
`,
+ DECK_SELECT_ROLE_DEFAULT:
+ `
+
+

+

+
`,
+ DECK_SELECT_ROLE_ADDED_TO_DECK:
+ `
+
+
`
};
diff --git a/client/src/modules/RoleBox.js b/client/src/modules/RoleBox.js
new file mode 100644
index 0000000..8656572
--- /dev/null
+++ b/client/src/modules/RoleBox.js
@@ -0,0 +1,342 @@
+import { HTMLFragments } from './HTMLFragments.js';
+import { globals } from '../config/globals.js';
+import { defaultRoles } from '../config/defaultRoles.js';
+import { toast } from './Toast.js';
+import { ModalManager } from './ModalManager.js';
+
+export class RoleBox {
+ constructor (container, deckManager) {
+ this.createMode = false;
+ this.currentlyEditingRoleName = null;
+ this.category = 'default';
+ this.deckManager = deckManager;
+ this.defaultRoles = [];
+ console.log('hi');
+ this.customRoles = [];
+ container.innerHTML += HTMLFragments.CREATE_GAME_CUSTOM_ROLES;
+ this.defaultButton = document.getElementById('role-category-default');
+ this.customButton = document.getElementById('role-category-custom');
+ this.defaultButton.addEventListener('click', () => { this.changeRoleCategory('default'); });
+ this.customButton.addEventListener('click', () => { this.changeRoleCategory('custom'); });
+ this.categoryTransition = document.getElementById('role-select').animate(
+ [
+ { opacity: 0 },
+ { opacity: 1 }
+ ], {
+ fill: 'forwards',
+ easing: 'linear',
+ duration: 500
+ });
+ }
+
+ render = () => {
+
+ };
+
+ loadDefaultRoles = () => {
+ this.defaultRoles = defaultRoles.sort((a, b) => {
+ if (a.team !== b.team) {
+ return a.team === globals.ALIGNMENT.GOOD ? 1 : -1;
+ }
+ return a.role.localeCompare(b.role);
+ }).map((role) => {
+ role.id = createRandomId();
+ return role;
+ });
+ };
+
+ loadCustomRolesFromCookies () {
+ const customRoles = localStorage.getItem('play-werewolf-custom-roles');
+ if (customRoles !== null && validateCustomRoleCookie(customRoles)) {
+ this.customRoles = JSON.parse(customRoles).map((role) => {
+ role.id = createRandomId();
+ return role;
+ }); // we know it is valid JSON from the validate function
+ }
+ }
+
+ loadCustomRolesFromFile (file) {
+ const reader = new FileReader();
+ reader.onerror = (e) => {
+ toast(reader.error.message, 'error', true, true, 'medium');
+ };
+ reader.onload = (e) => {
+ let string;
+ if (typeof e.target.result !== 'string') {
+ string = new TextDecoder('utf-8').decode(e.target.result);
+ } else {
+ string = e.target.result;
+ }
+ if (validateCustomRoleCookie(string)) {
+ this.customRoles = JSON.parse(string).map((role) => {
+ role.id = createRandomId();
+ return role;
+ }); // we know it is valid JSON from the validate function
+ const initialLength = this.customRoles.length;
+ // If any imported roles match a default role, exclude them.
+ this.customRoles = this.customRoles.filter((entry) => !this.defaultRoles
+ .find((defaultEntry) => defaultEntry.role.toLowerCase().trim() === entry.role.toLowerCase().trim()));
+ let message = this.customRoles.length === initialLength
+ ? 'All roles imported successfully!'
+ : 'Success, but one or more roles were excluded because their names match default roles.'
+ let messageType = this.customRoles.length === initialLength ? 'success' : 'warning'
+ ModalManager.dispelModal('upload-custom-roles-modal', 'modal-background');
+ toast(message, messageType, true, true, 'medium');
+ document.getElementById('custom-role-actions').style.display = 'none';
+ localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoles));
+ this.changeRoleCategory('custom');
+ this.displayCustomRoles(document.getElementById('role-select'));
+ for (const card of this.deckManager.deck) {
+ card.quantity = 0;
+ }
+ this.deckManager.updateDeckStatus();
+ } else {
+ toast(
+ 'Invalid formatting. Make sure you import the file as downloaded from this page.',
+ 'error',
+ true,
+ true,
+ 'medium'
+ );
+ }
+ };
+ reader.readAsText(file);
+ }
+
+ // via https://stackoverflow.com/a/18197341
+ downloadCustomRoles = (filename, text) => {
+ const element = document.createElement('a');
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
+ element.setAttribute('download', filename);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+
+ document.body.removeChild(element);
+ };
+
+ changeRoleCategory = (category) => {
+ this.category = category;
+ if (category === 'default') {
+ this.displayDefaultRoles(document.getElementById('role-select'));
+ if (this.defaultButton) {
+ this.defaultButton.classList.add('role-category-button-selected');
+ }
+ if (this.customButton) {
+ this.customButton.classList.remove('role-category-button-selected');
+ }
+ } else if (category === 'custom') {
+ this.displayCustomRoles(document.getElementById('role-select'));
+ if (this.customButton) {
+ this.customButton.classList.add('role-category-button-selected');
+ }
+ if (this.defaultButton) {
+ this.defaultButton.classList.remove('role-category-button-selected');
+ }
+ }
+ };
+
+ displayDefaultRoles = (selectEl) => {
+ document.querySelectorAll('#role-select .default-role, #role-select .custom-role').forEach(e => e.remove());
+ this.categoryTransition.play();
+ for (let i = 0; i < this.defaultRoles.length; i ++) {
+ const defaultRole = document.createElement('div');
+ defaultRole.innerHTML = HTMLFragments.DECK_SELECT_ROLE_DEFAULT;
+ defaultRole.classList.add('default-role');
+ defaultRole.dataset.roleId = this.defaultRoles[i].id;
+ const alignmentClass = this.defaultRoles[i].team === globals.ALIGNMENT.GOOD
+ ? globals.ALIGNMENT.GOOD
+ : globals.ALIGNMENT.EVIL;
+ defaultRole.classList.add(alignmentClass);
+ defaultRole.querySelector('.role-name').innerText = this.defaultRoles[i].role;
+ selectEl.appendChild(defaultRole);
+ }
+
+ this.addRoleEventListeners(selectEl, true, true, false, false, false);
+ };
+
+ displayCustomRoles = (selectEl) => {
+ document.querySelectorAll('#role-select .default-role, #role-select .custom-role').forEach(e => e.remove());
+ this.categoryTransition.play();
+ this.customRoles.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 < this.customRoles.length; i ++) {
+ const customRole = document.createElement('div');
+ customRole.innerHTML = HTMLFragments.DECK_SELECT_ROLE;
+ customRole.classList.add('custom-role');
+ customRole.dataset.roleId = this.customRoles[i].id;
+ const alignmentClass = this.customRoles[i].team === globals.ALIGNMENT.GOOD ? globals.ALIGNMENT.GOOD : globals.ALIGNMENT.EVIL;
+ customRole.classList.add(alignmentClass);
+ customRole.querySelector('.role-name').innerText = this.customRoles[i].role;
+ selectEl.appendChild(customRole);
+ }
+
+ this.addRoleEventListeners(selectEl, true, true, true, true, true);
+ };
+
+ addRoleEventListeners = (select, addOne, info, edit, remove, isCustom) => {
+ const elements = isCustom
+ ? document.querySelectorAll('#role-select .custom-role')
+ : document.querySelectorAll('#role-select .default-role');
+ elements.forEach((role) => {
+ if (addOne) {
+ const plusOneHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ e.preventDefault();
+ if (!this.deckManager.hasRole(name)) {
+ if (isCustom) {
+ this.deckManager.addToDeck(this.getCustomRole(name));
+ } else {
+ this.deckManager.addToDeck(this.getDefaultRole(name));
+ }
+ } else {
+ this.deckManager.addCopyOfCard(name);
+ }
+ this.deckManager.updateDeckStatus();
+ }
+ };
+ role.querySelector('.role-include').addEventListener('click', plusOneHandler);
+ role.querySelector('.role-include').addEventListener('keyup', plusOneHandler);
+ }
+ const name = role.querySelector('.role-name').innerText;
+
+ if (remove) {
+ const removeHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ if (confirm("Delete the role '" + name + "'?")) {
+ e.preventDefault();
+ this.removeFromCustomRoles(name);
+ if (this.category === 'custom') {
+ this.displayCustomRoles(document.getElementById('role-select'));
+ }
+ }
+ }
+ };
+ role.querySelector('.role-remove').addEventListener('click', removeHandler);
+ role.querySelector('.role-remove').addEventListener('keyup', removeHandler);
+ }
+ if (info) {
+ const infoHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ const alignmentEl = document.getElementById('custom-role-info-modal-alignment');
+ alignmentEl.classList.remove(globals.ALIGNMENT.GOOD);
+ alignmentEl.classList.remove(globals.ALIGNMENT.EVIL);
+ e.preventDefault();
+ let role;
+ if (isCustom) {
+ role = this.getCustomRole(name);
+ } else {
+ role = this.getDefaultRole(name);
+ }
+ document.getElementById('custom-role-info-modal-name').innerText = name;
+ alignmentEl.classList.add(role.team);
+ document.getElementById('custom-role-info-modal-description').innerText = role.description;
+ alignmentEl.innerText = role.team;
+ ModalManager.displayModal('custom-role-info-modal', 'modal-background', 'close-custom-role-info-modal-button');
+ }
+ };
+ role.querySelector('.role-info').addEventListener('click', infoHandler);
+ role.querySelector('.role-info').addEventListener('keyup', infoHandler);
+ }
+
+ if (edit) {
+ const editHandler = (e) => {
+ if (e.type === 'click' || e.code === 'Enter') {
+ e.preventDefault();
+ const entry = this.getCustomRole(name);
+ document.getElementById('role-name').value = entry.role;
+ document.getElementById('role-alignment').value = entry.team;
+ document.getElementById('role-description').value = entry.description;
+ this.createMode = false;
+ this.currentlyEditingRoleName = entry.role;
+ const createBtn = document.getElementById('create-role-button');
+ createBtn.setAttribute('value', 'Update');
+ ModalManager.displayModal('role-modal', 'modal-background', 'close-modal-button');
+ }
+ };
+ role.querySelector('.role-edit').addEventListener('click', editHandler);
+ role.querySelector('.role-edit').addEventListener('keyup', editHandler);
+ }
+ });
+ };
+
+ removeFromCustomRoles = (name) => {
+ const role = this.customRoles.find((entry) => entry.role === name);
+ if (role) {
+ this.customRoles.splice(this.customRoles.indexOf(role), 1);
+ this.deckManager.removeRoleEntirelyFromDeck(role);
+ localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoles));
+ toast('"' + name + '" deleted.', 'error', true, true, 'short');
+ }
+ };
+
+ getCustomRole (name) {
+ return this.customRoles.find(
+ (entry) => entry.role.toLowerCase().trim() === name.toLowerCase().trim()
+ );
+ };
+
+ addCustomRole (role) {
+ role.id = createRandomId();
+ this.customRoles.push(role);
+ localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoles));
+ }
+
+ updateCustomRole (entry, name, description, team) {
+ entry.role = name;
+ entry.description = description;
+ entry.team = team;
+ this.deckManager.updateDeckStatus();
+ localStorage.setItem('play-werewolf-custom-roles', JSON.stringify(this.customRoles));
+ }
+
+ getDefaultRole (name) {
+ return this.defaultRoles.find(
+ (entry) => entry.role.toLowerCase().trim() === name.toLowerCase().trim()
+ );
+ };
+}
+
+function createRandomId () {
+ let id = '';
+ for (let i = 0; i < 25; i ++) {
+ id += globals.CHAR_POOL[Math.floor(Math.random() * globals.CHAR_POOL.length)];
+ }
+ return id;
+}
+
+// this is user-supplied, so we should validate it fully
+function validateCustomRoleCookie (cookie) {
+ const valid = false;
+ if (typeof cookie === 'string' && new Blob([cookie]).size <= 1000000) {
+ try {
+ const cookieJSON = JSON.parse(cookie);
+ if (Array.isArray(cookieJSON)) {
+ for (const entry of cookieJSON) {
+ if (typeof entry === 'object') {
+ if (typeof entry.role !== 'string' || entry.role.length > globals.MAX_CUSTOM_ROLE_NAME_LENGTH
+ || typeof entry.team !== 'string' || (entry.team !== globals.ALIGNMENT.GOOD && entry.team !== globals.ALIGNMENT.EVIL)
+ || typeof entry.description !== 'string' || entry.description.length > globals.MAX_CUSTOM_ROLE_DESCRIPTION_LENGTH
+ ) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
+ }
+
+ return valid;
+}
diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css
index 57b8903..3e93edb 100644
--- a/client/src/styles/GLOBAL.css
+++ b/client/src/styles/GLOBAL.css
@@ -455,7 +455,6 @@ input {
display: flex;
flex-direction: row;
flex-wrap: wrap;
- align-items: center;
}
.animated-placeholder {
diff --git a/client/src/styles/create.css b/client/src/styles/create.css
index fa383be..0771085 100644
--- a/client/src/styles/create.css
+++ b/client/src/styles/create.css
@@ -13,6 +13,28 @@
height: 55px;
}
+.role-category-button {
+ background-color: transparent;
+ color: #b1afcd;
+ border: 1px solid #b1afcd;
+ border-radius: 25px;
+ font-size: 16px;
+ padding: 5px 10px;
+ margin: 5px;
+ font-family: 'signika-negative', sans-serif !important;
+ cursor: pointer;
+}
+
+.role-category-button-selected {
+ color: black;
+ background-color: #b1afcd;
+}
+
+#role-category-buttons {
+ margin-top: 10px;
+ display: flex;
+}
+
.compact-card h1 {
display: flex;
align-items: center;
@@ -107,7 +129,7 @@
#deck-status-container {
width: 20em;
max-width: 95%;
- height: 10em;
+ height: 20em;
overflow-y: auto;
position: relative;
}
@@ -169,6 +191,7 @@
display: none;
color: #e7e7e7;
position: absolute;
+ z-index: 25;
top: 38px;
right: 29px;
background-color: #333243;
@@ -353,13 +376,13 @@ input[type="number"] {
border: 2px solid #1c8a36;
}
-#deck-select {
+#role-select {
margin: 0.5em 1em 1.5em 0;
overflow-y: auto;
- height: 12em;
+ height: 16em;
}
-.deck-select-role {
+.default-role, .custom-role, .added-role {
display: flex;
justify-content: space-between;
background-color: black;
@@ -371,23 +394,23 @@ input[type="number"] {
font-size: 16px;
}
-.deck-select-role-name {
+.role-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
-.deck-select-role:hover {
+.default-role:hover, .custom-role:hover, .added-role:hover {
border: 1px solid #d7d7d7;
}
-.deck-select-role-options {
+.role-options {
display: flex;
align-items: center;
justify-content: center;
}
-#deck-select img {
+#role-select img, #deck-status-container img {
height: 20px;
margin: 0 8px;
cursor: pointer;
@@ -395,11 +418,11 @@ input[type="number"] {
border-radius: 3px;
}
-#deck-select img:nth-child(4) {
+#role-select img:nth-child(4) {
height: 18px;
}
-#deck-select img:hover {
+#role-select img:hover, #deck-status-container img:hover {
filter: brightness(1.5);
background-color: #8080804d;
}
@@ -540,6 +563,9 @@ input[type="number"] {
}
@media(max-width: 550px) {
+ #custom-roles-container, #deck-status-container {
+ min-width: 90%;
+ }
h1 {
font-size: 35px;
}
@@ -558,7 +584,7 @@ input[type="number"] {
font-size: 16px;
}
- .deck-select-role-name {
+ .role-name {
font-size: 13px;
font-weight: bold;
}
diff --git a/client/src/styles/home.css b/client/src/styles/home.css
index e33b47f..179b7c0 100644
--- a/client/src/styles/home.css
+++ b/client/src/styles/home.css
@@ -11,7 +11,7 @@ button#home-create-button {
padding: 20px;
}
-#framed-phone-screenshot {
+#framed-phone-screenshot, #framed-phone-screenshot-2 {
max-width: 250px;
width: 40vw;
min-width: 175px;
@@ -23,8 +23,16 @@ button#home-create-button {
align-items: center;
flex-wrap: wrap;
justify-content: center;
- padding: 0 1em;
- margin-top: 25px;
+ width: 100%;
+}
+
+#about-container > div {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: fit-content;
+ flex-wrap: wrap;
+ padding: 25px 0;
}
#join-container form {
@@ -34,6 +42,8 @@ button#home-create-button {
#about-container h2 {
max-width: 17em;
font-size: 22px;
+ border-left: 1px solid #bababa;
+ padding: 15px;
}
#about-container img {
@@ -52,14 +62,12 @@ button#home-create-button {
align-items: center;
width: 100%;
background-color: #1e1b26;
- margin-bottom: 15px;
}
form {
display: flex;
flex-wrap: wrap;
margin: 10px 0;
- padding: 10px;
border-radius: 3px;
justify-content: center;
align-items: center;
diff --git a/client/src/views/home.html b/client/src/views/home.html
index 22e9760..d52d038 100644
--- a/client/src/views/home.html
+++ b/client/src/views/home.html
@@ -43,8 +43,14 @@
-

-
Create your game, have everyone join, and deal a role to their device.
+
+

+
Join a game and have a role dealt to your device.
+
+
+

+
Create your own game with default or custom roles.
+