diff --git a/README.md b/README.md index 66bf448..eb6dcff 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ smoothly when you don't have a deck, or when you and your friends are together v After a long hiatus from maintaining the application, I have come back and undertaken a large-scale redesign, rewriting most of the code and producing a result that I believe is more stable and has much more sensible client-server interaction. -It's a shame that my first attempt is what ended up in Github's Artic Code Vault :) ![player](./client/src/images/screenshots/player.PNG) diff --git a/client/src/images/screenshots/player.PNG b/client/src/images/screenshots/player.PNG index 79d25c7..45054c1 100644 Binary files a/client/src/images/screenshots/player.PNG and b/client/src/images/screenshots/player.PNG differ diff --git a/client/src/images/tutorial/custom-roles.PNG b/client/src/images/tutorial/custom-roles.PNG new file mode 100644 index 0000000..e5413d5 Binary files /dev/null and b/client/src/images/tutorial/custom-roles.PNG differ diff --git a/client/src/images/tutorial/default-roles.PNG b/client/src/images/tutorial/default-roles.PNG new file mode 100644 index 0000000..57f9c7c Binary files /dev/null and b/client/src/images/tutorial/default-roles.PNG differ diff --git a/client/src/images/tutorial/moderation-option.png b/client/src/images/tutorial/moderation-option.png new file mode 100644 index 0000000..b82fa70 Binary files /dev/null and b/client/src/images/tutorial/moderation-option.png differ diff --git a/client/src/modules/DeckStateManager.js b/client/src/modules/DeckStateManager.js index b0bf0a9..99912da 100644 --- a/client/src/modules/DeckStateManager.js +++ b/client/src/modules/DeckStateManager.js @@ -6,6 +6,8 @@ export class DeckStateManager { constructor() { this.deck = null; this.customRoleOptions = []; + this.createMode = false; + this.currentlyEditingRoleName = null; } addToDeck(role) { @@ -22,6 +24,13 @@ export class DeckStateManager { 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) { let option = this.customRoleOptions.find((option) => option.role === name); if (option) { diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index c6acab6..f06a0cb 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -470,32 +470,41 @@ function constructCompactDeckBuilderElement(card, deckManager) { } function initializeRemainingEventListeners(deckManager) { - document.getElementById("add-role-form").onsubmit = (e) => { + document.getElementById("role-form").onsubmit = (e) => { e.preventDefault(); let name = document.getElementById("role-name").value.trim(); let description = document.getElementById("role-description").value.trim(); let team = document.getElementById("role-alignment").value.toLowerCase().trim(); - if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { // confirm there is no existing custom role with the same name - if (name.length > 40) { - toast('Your name is too long (max 40 characters).', "error", true); - return; + 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); + } else { + toast("There is already a role with this name", "error", true, true, 3); } - if (description.length > 500) { - toast('Your description is too long (max 500 characters).', "error", true); - return; - } - deckManager.addToCustomRoleOptions({role: name, description: description, team: team, custom: true}); - updateCustomRoleOptionsList(deckManager, document.getElementById("deck-select")) - ModalManager.dispelModal("add-role-modal", "modal-background"); - toast("Role Created", "success", true); } else { - toast("There is already a role with this name", "error", true, true, 3); + let option = deckManager.getCustomRoleOption(deckManager.currentlyEditingRoleName); + if (name === option.role) { // did they edit the name? + processNewCustomRoleSubmission(name, description, team, deckManager,true, option); + } else { + if (!deckManager.getCustomRoleOption(name) && !deckManager.getCard(name)) { + processNewCustomRoleSubmission(name, description, team, deckManager, true, option); + } else { + toast("There is already a role with this name", "error", true, true, 3); + } + } } } document.getElementById("custom-role-btn").addEventListener( "click", () => { + let createBtn = document.getElementById("create-role-button"); + createBtn.setAttribute("value", "Create"); + deckManager.createMode = true; + deckManager.currentlyEditingRoleName = null; + document.getElementById("role-name").value = ""; + document.getElementById("role-alignment").value = globals.ALIGNMENT.GOOD; + document.getElementById("role-description").value = ""; ModalManager.displayModal( - "add-role-modal", + "role-modal", "modal-background", "close-modal-button" ) @@ -503,6 +512,28 @@ function initializeRemainingEventListeners(deckManager) { ) } +function processNewCustomRoleSubmission(name, description, team, deckManager, isUpdate, option=null) { + if (name.length > 40) { + toast('Your name is too long (max 40 characters).', "error", true); + return; + } + if (description.length > 500) { + toast('Your description is too long (max 500 characters).', "error", true); + return; + } + if (isUpdate) { + deckManager.updateCustomRoleOption(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}); + 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); @@ -568,17 +599,34 @@ function addCustomRoleEventListeners(deckManager, select) { 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-edit').addEventListener('click', (e) => { + e.preventDefault(); + let 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; + let createBtn = document.getElementById("create-role-button"); + createBtn.setAttribute("value", "Update"); + ModalManager.displayModal("role-modal", "modal-background", "close-modal-button"); + }); }); } +function displayCustomRoleModalInAddOrEditMode() { + let ad +} + function updateDeckStatus(deckManager) { document.querySelectorAll('.deck-role').forEach((el) => el.remove()); document.getElementById("deck-count").innerText = deckManager.getDeckSize() + " Players"; if (deckManager.getDeckSize() === 0) { let placeholder = document.createElement("div"); placeholder.setAttribute("id", "deck-list-placeholder"); - placeholder.innerText = "Add a card from the included roles below."; + placeholder.innerText = "Add a card from the available roles below."; document.getElementById("deck-list").appendChild(placeholder); } else { if (document.getElementById("deck-list-placeholder")) { diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 3b95c6d..1b91c39 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -215,6 +215,7 @@ export class GameStateRenderer { } displayAvailableModerators() { + document.getElementById("transfer-mod-modal-content").innerText = ""; document.querySelectorAll('.potential-moderator').forEach((el) => { let pointer = el.dataset.pointer; if (pointer && this.transferModHandlers[pointer]) { diff --git a/client/src/modules/StateBucket.js b/client/src/modules/StateBucket.js index fa71c56..7571fc2 100644 --- a/client/src/modules/StateBucket.js +++ b/client/src/modules/StateBucket.js @@ -4,6 +4,5 @@ */ export const stateBucket = { currentGameState: null, - timerWorker: null, - gameStateRequestInFlight: false + timerWorker: null } diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 299f199..b27a201 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -13,9 +13,7 @@ const game = () => { injectNavbar(); let timerWorker; const socket = io('/in-game'); - stateBucket.gameStateRequestInFlight = false; socket.on('disconnect', () => { - stateBucket.gameStateRequestInFlight = false; if (timerWorker) { timerWorker.terminate(); } @@ -24,10 +22,8 @@ const game = () => { socket.on('connect', () => { console.log('fired connect event'); socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function (returnedEnvironment) { - if (!stateBucket.gameStateRequestInFlight) { - timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); - prepareGamePage(returnedEnvironment, socket, timerWorker); - } + timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); + prepareGamePage(returnedEnvironment, socket, timerWorker); }); }) }; @@ -37,9 +33,7 @@ function prepareGamePage(environment, socket, timerWorker) { const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { - stateBucket.gameStateRequestInFlight = true; socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { - stateBucket.gameStateRequestInFlight = false; stateBucket.currentGameState = gameState; document.querySelector('.spinner-container')?.remove(); document.querySelector('.spinner-background')?.remove(); @@ -214,7 +208,6 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo stateBucket.currentGameState.accessCode, stateBucket.currentGameState.client.cookie, function (gameState) { - stateBucket.gameStateRequestInFlight = false; stateBucket.currentGameState = gameState; processGameState( stateBucket.currentGameState, @@ -230,26 +223,22 @@ function setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWo } if (!socket.hasListeners(globals.EVENTS.SYNC_GAME_STATE)) { socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { - if (!stateBucket.gameStateRequestInFlight) { - stateBucket.gameStateRequestInFlight = true; - socket.emit( - globals.COMMANDS.FETCH_IN_PROGRESS_STATE, - stateBucket.currentGameState.accessCode, - stateBucket.currentGameState.client.cookie, - function (gameState) { - stateBucket.gameStateRequestInFlight = false; - stateBucket.currentGameState = gameState; - processGameState( - stateBucket.currentGameState, - gameState.client.cookie, - socket, - gameStateRenderer, - gameTimerManager, - timerWorker - ); - } - ); - } + socket.emit( + globals.COMMANDS.FETCH_IN_PROGRESS_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); + } + ); }); } diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 04dbae5..4f6fcb5 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -180,6 +180,26 @@ button { border: 2px solid #1c8a36; } +.emphasized { + font-weight: bold; + color: #00a718; +} + +#how-to-use-container img { + max-width: 98%; + border: 1px solid #57566a; + border-radius: 3px; + width: 45em; + margin: 0 auto; + /* justify-self: center; */ + /* align-self: center; */ + display: flex; +} + +#how-to-use-container h1 { + font-family: signika-negative, sans-serif; +} + input { padding: 10px; } @@ -227,10 +247,22 @@ input { flex-direction: column; margin: 0 auto; width: 95%; - max-width: 70em; + max-width: 64em; line-height: 1.5; } +#how-to-use-container h3 { + font-size: 25px; +} + +#how-to-use-container h1 { + font-size: 40px !important; +} + +.tutorial-image-small { + width: 30em !important; +} + #desktop-links > a:nth-child(1), #mobile-links a:nth-child(1) { margin: 0 0.5em; width: 50px; diff --git a/client/src/styles/game.css b/client/src/styles/game.css index f53a7ae..aee49c9 100644 --- a/client/src/styles/game.css +++ b/client/src/styles/game.css @@ -694,7 +694,6 @@ label[for='moderator'] { button { font-size: 16px; - padding: 5px; } #play-pause img { diff --git a/client/src/views/create.html b/client/src/views/create.html index 7b11d76..cc0bbc2 100644 --- a/client/src/views/create.html +++ b/client/src/views/create.html @@ -23,8 +23,8 @@
-