diff options
author | Daniel Weipert <code@drogueronin.de> | 2023-08-17 22:46:12 +0200 |
---|---|---|
committer | Daniel Weipert <code@drogueronin.de> | 2023-08-17 22:46:12 +0200 |
commit | aa44f67ab57673528e96a4a075fbd8cd0354bd68 (patch) | |
tree | c232349cbe9583378ad510774be27b4371d50cd4 | |
parent | cc685bfe02b42b592987117fa008a4461785f53c (diff) |
ui
-rw-r--r-- | index.html | 145 | ||||
-rw-r--r-- | resources/css/battle.css | 6 | ||||
-rw-r--r-- | resources/css/menu.css | 86 | ||||
-rw-r--r-- | resources/js/classes/StatusEffect.js | 2 | ||||
-rw-r--r-- | resources/js/classes/Technique.js | 20 | ||||
-rw-r--r-- | resources/js/game.js | 90 | ||||
-rw-r--r-- | resources/js/ui.js | 154 |
7 files changed, 377 insertions, 126 deletions
@@ -1,3 +1,4 @@ +<!DOCTYPE html> <html> <head> <meta charset="utf-8"> @@ -20,22 +21,6 @@ </div> <div id="techniques"></div> - <template id="tpl___techniques"> - <div></div> - </template> - <template id="tpl___technique"> - <div class="techniques__technique"> - <div> - <div data-template-slot="name"></div> - <div data-template-slot="types"></div> - </div> - <div> - <div data-template-slot="power"></div> - <div data-template-slot="accuracy"></div> - <div data-template-slot="range"></div> - </div> - </div> - </template> <div id="status"> <div>Money: <span id="money"></span></div> @@ -63,30 +48,9 @@ <!-- Templates --> - <template id="tpl___party"> - <div class="party"> - <div data-template-slot="monsters" class="party__monsters"></div> - <div data-template-slot="modes" class="party__selection-modes"> - <button data-template-slot="modeSelect" data-party-selection-mode="select">Select</button> - <button data-template-slot="modeStats" data-party-selection-mode="stats">Stats</button> - <button data-template-slot="modeTechniques" data-party-selection-mode="techniques">Techniques</button> - </div> - </div> - </template> - - <template id="tpl___party__monster"> - <div class="party__monster"> - <div><div class="monster__level"></div></div> - <img data-template-slot="sprite" class="monster__img" src="" /> - <div class="monster__exp"> - <div class="monster__exp-bar"></div> - </div> - <div class="monster__exp-text"></div> - <div class="monster__active-technique"> - <div class="monster__active-technique__name"></div> - <div class="monster__active-technique__types"></div> - <div class="monster__active-technique__power"></div> - </div> + <template id="tpl___popup"> + <div class="popup__overlay"> + <div class="popup"></div> </div> </template> @@ -139,9 +103,86 @@ <div data-template-slot="text" class="damage"></div> </template> - <template id="tpl___popup"> - <div class="popup__overlay"> - <div class="popup"></div> + <template id="tpl___techniques"> + <div></div> + </template> + <template id="tpl___technique"> + <div class="techniques__technique"> + <div> + <div data-template-slot="name"></div> + </div> + <div> + <div data-template-slot="types"></div> + <div data-template-slot="range" title="range"></div> + <div data-template-slot="recharge" title="recharge"></div> + </div> + <div> + <div data-template-slot="power" title="power"></div> + <div data-template-slot="accuracy" title="accuracy"></div> + </div> + </div> + </template> + + <template id="tpl___party"> + <div class="party"> + <div data-template-slot="monsters" class="party__monsters"></div> + <div data-template-slot="modes" class="party__selection-modes"> + <button data-template-slot="modeSelect" data-party-selection-mode="select" class="menu-button">Select</button> + <button data-template-slot="modeStats" data-party-selection-mode="stats" class="menu-button">Stats</button> + <button data-template-slot="modeTechniques" data-party-selection-mode="techniques" class="menu-button">Techniques</button> + </div> + </div> + </template> + + <template id="tpl___party__monster"> + <div class="party__monster"> + <img data-template-slot="sprite" class="monster__img" src="" /> + <div class="party__monster__stats"> + <span data-template-slot="name"></span> + <span data-template-slot="gender"></span> + Lv. <span data-template-slot="level"></span> + </div> + <div> + HP <span data-template-slot="hpText"></span> + </div> + </div> + </template> + + <template id="tpl___monster-stats"> + <div class="monster-stats"> + <div class="monster-stats__identity"> + <span data-template-slot="name"></span> + <span data-template-slot="gender"></span> + Lv. <span data-template-slot="level"></span> + <span data-template-slot="types"></span> + </div> + + <div> + <table> + <tr> + <th>Melee</th> + <td data-template-slot="stats.melee"></td> + </tr> + <tr> + <th>Armour</th> + <td data-template-slot="stats.armour"></td> + </tr> + <tr> + <th>Ranged</th> + <td data-template-slot="stats.ranged"></td> + </tr> + <tr> + <th>Dodge</th> + <td data-template-slot="stats.dodge"></td> + </tr> + <tr> + <th>Speed</th> + <td data-template-slot="stats.speed"></td> + </tr> + </table> + </div> + + <button data-template-slot="techniques">Techniques</button> </div> </template> @@ -158,8 +199,22 @@ </template> <template id="tpl___menu__journal"> - <div> - <button data-template-slot="save">Save</button> + <div class="menu__journal"> + <button data-template-slot="save" class="menu-button">Save</button> + <button data-template-slot="load" class="menu-button">Load</button> + </div> + </template> + + <template id="tpl___dialog__save"> + <div class="dialog__save"> + <textarea data-template-slot="saveData"></textarea> + <button data-template-slot="saveClipboard" class="menu-button">Save to Clipboard</button> + </div> + </template> + + <template id="tpl___dialog__load"> + <div class="dialog__load"> + <textarea data-template-slot="saveData"></textarea> <button data-template-slot="load">Load</button> </div> </template> diff --git a/resources/css/battle.css b/resources/css/battle.css index 0f56234..099142b 100644 --- a/resources/css/battle.css +++ b/resources/css/battle.css @@ -76,6 +76,7 @@ margin-bottom: 0.25rem; } .battle__monster-img { + cursor: pointer; transition-property: filter; } .battle__monster-img.damaged { @@ -198,6 +199,11 @@ background-color: rgba(0, 0, 0, 0.05); } +.techniques__technique[disabled] { + filter: grayscale(100%); + opacity: 0.5; +} + .techniques__technique > div { display: flex; justify-content: space-between; diff --git a/resources/css/menu.css b/resources/css/menu.css index 41353b2..72c225a 100644 --- a/resources/css/menu.css +++ b/resources/css/menu.css @@ -15,12 +15,38 @@ } .popup { + max-width: 100%; + background-image: url('/modules/tuxemon/mods/tuxemon/gfx/ui/background/spyder_empty.png'); background-size: cover; background-position: center; } +.menu-button { + font-size: 1.5rem; + border: 1px solid #000; + padding: 0.4em 0.75em; + line-height: 1em; + cursor: pointer; + background-color: rgba(255, 255, 255, 0.9); +} + +.menu-button:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.menu-button[disabled] { + filter: grayscale(100%); + opacity: 0.5; +} + + +.gender-icon { + line-height: 1em; +} + + #status { @@ -39,12 +65,55 @@ } #menu > div { cursor: pointer; + padding: 1rem; } #menu > div:hover { background-color: rgba(0, 0, 0, 0.05); } +.menu__journal { + display: flex; + flex-direction: column; +} + +.menu__journal button { + margin: 0.5em; + font-size: 5rem; + padding: 1em 1em; +} + + +.dialog__save { + display: flex; + flex-direction: column; + align-items: center; +} +.dialog__save textarea { + width: 80vw; + height: 60vh; +} +.dialog__save button { + font-size: 2rem; + width: 80vw; +} + + +.dialog__load { + display: flex; + flex-direction: column; + align-items: center; +} +.dialog__load textarea { + width: 80vw; + height: 60vh; +} +.dialog__load button { + font-size: 2rem; + width: 80vw; +} + + .party__monsters { display: grid; @@ -53,7 +122,9 @@ .party__monster { cursor: pointer; - padding: 1rem; + padding: 0.75rem; + text-align: center; + font-size: 0.75rem; } .party__monster:hover { background-color: rgba(0, 0, 0, 0.1); @@ -67,4 +138,17 @@ .party__selection-modes button[selected] { border-color: var(--color-success); + color: var(--color-success); +} + + + +.monster-stats { + padding: 0.5rem; +} + +.monster-stats__identity { + display: flex; + justify-content: space-between; + align-items: center; } diff --git a/resources/js/classes/StatusEffect.js b/resources/js/classes/StatusEffect.js index ac6ae54..e1ae9c9 100644 --- a/resources/js/classes/StatusEffect.js +++ b/resources/js/classes/StatusEffect.js @@ -4,6 +4,8 @@ class StatusEffect { /** * @type {Monster} + * + * currently only used for lifeleech */ issuer = null; diff --git a/resources/js/classes/Technique.js b/resources/js/classes/Technique.js index a24e094..7558748 100644 --- a/resources/js/classes/Technique.js +++ b/resources/js/classes/Technique.js @@ -3,10 +3,14 @@ class Technique { #potency = 0; #power = 0; + turnLastUse = 0; + constructor (slug) { this.slug = slug; this.resetStats(); + + this.turnLastUse = -this.rechargeLength; } get name () { @@ -36,6 +40,22 @@ class Technique { return DB.techniques[this.slug].effects; } + get rechargeLength () { + return DB.techniques[this.slug].recharge; + } + + isUsable () { + if (this.turnLastUse >= Game.turn) { + return true; + } + + return Game.turn - this.turnLastUse >= this.rechargeLength; + } + + use () { + this.turnLastUse = Game.turn; + } + get accuracy () { return this.#accuracy; } diff --git a/resources/js/game.js b/resources/js/game.js index 1f1f92b..140f9d5 100644 --- a/resources/js/game.js +++ b/resources/js/game.js @@ -9,9 +9,12 @@ const Game = { }, didTechniqueHit: false, + turn: 0, async progressTurn () { + Game.turn++; + await Game.applyStatusEffect(state.enemy.monster); await Game.applyStatusEffect(state.activeMonster); @@ -28,11 +31,30 @@ const Game = { } Game.phases.postAction = []; + // enemy defeated + if (state.enemy.monster.hp <= 0) { + // money + state.money += state.enemy.monster.level * state.enemy.monster.moneyModifier; + + // exp + state.activeMonster.exp += calculateAwardedExperience(state.enemy.monster, [state.activeMonster])[0]; + + if (state.activeMonster.canLevelUp()) { + state.activeMonster.levelUp(); + } + if (state.activeMonster.canEvolve()) { + await fetchMonster(state.activeMonster.evolutions[0].monster_slug); + state.activeMonster.evolve(); + } + + await Game.spawnEnemyMonster(); + } + UI.drawEnemyMonster(); UI.drawActiveMonster(); UI.drawActiveTechniques(); - UI.elements.money.textContent = state.money; + UI.elements.money.textContent = `${state.money} €`; }, /** @@ -41,15 +63,13 @@ const Game = { * @param {Monster} target */ async useTechnique (technique, user, target) { + technique.use(); + if (!Game.didTechniqueHit) { UI.drawDamageMiss(UI.createDamageMiss()); return; } - if (state.activeMonster.hp === state.activeMonster.stats.hp) { - state.activeMonster.hp = 1; - } - for (const techniqueEffect of technique.effects) { // damage @@ -62,15 +82,24 @@ const Game = { UI.applyMultiplierToDamage(damageNode, simpleDamageMultiplier(state.activeTechnique.types, state.enemy.monster.types)); UI.applyTechniqueToDamage(damageNode, state.activeTechnique); UI.drawDamage(damageNode); + UI.drawTechniqueAnimation(); }); } else if (techniqueEffect === 'money') { - state.money += Math.floor(Math.random() * target.level); + Game.phases.action.push(() => { + const money = Math.max(1, Math.floor(Math.random() * target.level)); + state.money += money; + + const damageNode = UI.createDamage(`${money} €`); + UI.applyTechniqueToDamage(damageNode, state.activeTechnique); + UI.drawDamage(damageNode); + UI.drawTechniqueAnimation(); + }); } else if (techniqueEffect === 'enhance') { - UI.drawDamage(UI.createDamage('!!ENHANCE!!')); + UI.drawTechniqueAnimation(); } // status effect @@ -81,7 +110,10 @@ const Game = { const statusEffect_effect = techniqueEffect.split(',')[0].split(' ')[1].split('_')[1]; const statusEffect = await fetchStatusEffect(statusEffect_effect); - statusEffect.issuer = user; + + if (statusEffect.slug === 'lifeleech') { + statusEffect.issuer = user; + } let recipient; if (statusEffect_recipient === 'user') { @@ -108,6 +140,11 @@ const Game = { } }, + rechargeTechnique () { + const feedbackNode = UI.createActionFeedback('recharge'); + UI.drawActionFeedback(feedbackNode); + }, + /** * @param {Monster} monster */ @@ -165,9 +202,9 @@ const Game = { Game.phases.postAction.push(() => { monster.hp += statusEffectHeal; - const damageNode = UI.createDamage(statusEffectHeal); - UI.applyStatusEffectToDamage(damageNode, monster.statusEffect); - UI.drawDamage(damageNode); + const feedbackNode = UI.createActionFeedback(statusEffectHeal); + UI.applyStatusEffectToDamage(feedbackNode, monster.statusEffect); + UI.drawActionFeedback(feedbackNode); }); } @@ -255,28 +292,11 @@ const Game = { const accuracy = Math.random(); Game.didTechniqueHit = state.activeTechnique.accuracy >= accuracy; - await Game.useTechnique(state.activeTechnique, state.activeMonster, state.enemy.monster); - - Game.phases.postAction.push(async () => { - // enemy defeated - if (state.enemy.monster.hp <= 0) { - // money - state.money += state.enemy.monster.level * state.enemy.monster.moneyModifier; - - // exp - state.activeMonster.exp += calculateAwardedExperience(state.enemy.monster, [state.activeMonster])[0]; - - if (state.activeMonster.canLevelUp()) { - state.activeMonster.levelUp(); - } - if (state.activeMonster.canEvolve()) { - await fetchMonster(state.activeMonster.evolutions[0].monster_slug); - state.activeMonster.evolve(); - } - - await Game.spawnEnemyMonster(); - } - }); + if (state.activeTechnique.isUsable()) { + await Game.useTechnique(state.activeTechnique, state.activeMonster, state.enemy.monster); + } else { + Game.rechargeTechnique(); + } Game.progressTurn(); }, @@ -327,6 +347,10 @@ const Game = { monsterData.level = monsterState.level; monsterData.hp = monsterState.hp; + if (monsterData.statusEffect && monsterData.statusEffect.slug === 'lifeleech') { + monsterData.statusEffect = null; + } + return monsterData; }; diff --git a/resources/js/ui.js b/resources/js/ui.js index f6a36b8..3739ce6 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -6,16 +6,20 @@ const Template = { battleExpBar: document.querySelector('#tpl___battle__exp-bar'), battleDamage: document.querySelector('#tpl___battle__damage'), - movesetList: document.querySelector('#tpl___moveset__list'), - movesetItem: document.querySelector('#tpl___moveset__item'), - techniques: document.querySelector('#tpl___techniques'), technique: document.querySelector('#tpl___technique'), party: document.querySelector('#tpl___party'), partyMonster: document.querySelector('#tpl___party__monster'), + monsterStats: document.querySelector('#tpl___monster-stats'), + + movesetList: document.querySelector('#tpl___moveset__list'), + movesetItem: document.querySelector('#tpl___moveset__item'), + menuJournal: document.querySelector('#tpl___menu__journal'), + dialogSave: document.querySelector('#tpl___dialog__save'), + dialogLoad: document.querySelector('#tpl___dialog__load'), }; const UI = { @@ -92,11 +96,6 @@ const UI = { /* Battle */ - /** - * @type {MouseEvent} - */ - battleClickEvent: null, - techniqueAnimationIsRunning: false, techniqueAnimationNumber: 0, techniqueAnimationFps: 20, @@ -150,6 +149,21 @@ const UI = { }, /** + * @param {string} gender + * + * @returns {HTMLElement} + */ + createGenderIcon (gender) { + const icon = document.createElement('span'); + icon.textContent = gender === 'male' ? '♂' : gender === 'female' ? '♀' : '⚲'; + icon.title = slugToName(gender); + + icon.classList.add('gender-icon'); + + return icon; + }, + + /** * @param {string} type * * @returns {HTMLElement} @@ -186,7 +200,7 @@ const UI = { const template = UI.createTemplate(Template.battleMonster); template.querySelector('[data-template-slot="name"]').textContent = monster.name; - template.querySelector('[data-template-slot="gender"]').textContent = monster.gender === 'male' ? '♂' : monster.gender === 'female' ? '♀' : '⚲'; + template.querySelector('[data-template-slot="gender"]').innerHTML = UI.createGenderIcon(monster.gender).outerHTML; template.querySelector('[data-template-slot="level"]').textContent = monster.level; template.querySelector('[data-template-slot="statusEffect"]').innerHTML = UI.createStatusEffectIcon(monster.statusEffect).outerHTML; template.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/battle/${monster.slug}-front.png`; @@ -239,10 +253,15 @@ const UI = { const techniqueNode = UI.createTemplate(Template.technique); techniqueNode.querySelector('[data-template-slot="name"]').textContent = technique.name; + techniqueNode.querySelector('[data-template-slot="recharge"]').textContent = technique.rechargeLength; techniqueNode.querySelector('[data-template-slot="types"]').innerHTML = technique.types.map((type) => UI.createElementTypeIcon(type).outerHTML).join(''); + techniqueNode.querySelector('[data-template-slot="range"]').textContent = technique.range; techniqueNode.querySelector('[data-template-slot="power"]').textContent = technique.power; techniqueNode.querySelector('[data-template-slot="accuracy"]').textContent = technique.accuracy; - techniqueNode.querySelector('[data-template-slot="range"]').textContent = technique.range; + + if (!technique.isUsable()) { + techniqueNode.setAttribute('disabled', true); + } template.appendChild(techniqueNode); } @@ -295,13 +314,16 @@ const UI = { * @returns {void} */ drawTechniqueAnimation () { + const x = UI.battleClickEvent.clientX; + const y = UI.battleClickEvent.clientY; + if (!UI.techniqueAnimationIsRunning && state.activeTechnique.animation && DB.allAnimations[state.activeTechnique.animation]) { UI.techniqueAnimationIsRunning = true; const techniqueAnimationLoop = () => { UI.elements.battleEnemyAnimation.src = `/modules/tuxemon/mods/tuxemon/animations/technique/${state.activeTechnique.animation}_${("00" + UI.techniqueAnimationNumber).slice(-2)}.png`; - UI.elements.battleEnemyAnimation.style.top = UI.battleClickEvent.clientY - (UI.elements.battleEnemyAnimation.clientHeight / 2); - UI.elements.battleEnemyAnimation.style.left = UI.battleClickEvent.clientX - (UI.elements.battleEnemyAnimation.clientWidth / 2); + UI.elements.battleEnemyAnimation.style.top = y - (UI.elements.battleEnemyAnimation.clientHeight / 2) + 'px'; + UI.elements.battleEnemyAnimation.style.left = x - (UI.elements.battleEnemyAnimation.clientWidth / 2) + 'px'; // console.log(UI.elements.battleEnemyAnimation.src); UI.techniqueAnimationNumber++; @@ -323,32 +345,46 @@ const UI = { /* Battle - Damage */ + /** + * @type {MouseEvent} + */ + battleClickEvent: null, + damageHighlightClickDuration: 0.1, damageHighlightClickTimeout: null, /** - * @param {number|string} damage + * @param {any} feedback * * @returns {HTMLElement} */ - createDamage (damage) { - const damageNode = UI.createTemplate(Template.battleDamage); - damageNode.innerHTML = damage; + createActionFeedback (feedback) { + const feedbackNode = UI.createTemplate(Template.battleDamage); + feedbackNode.innerHTML = feedback; - damageNode.style.top = UI.battleClickEvent.pageY - UI.elements.battleEnemy.offsetTop + (Math.random() * 40 - 20); - damageNode.style.left = UI.battleClickEvent.pageX - UI.elements.battleEnemy.offsetLeft + (Math.random() * 40 - 20); + feedbackNode.style.top = UI.battleClickEvent.pageY - UI.elements.battleEnemy.offsetTop + (Math.random() * 40 - 20) + 'px'; + feedbackNode.style.left = UI.battleClickEvent.pageX - UI.elements.battleEnemy.offsetLeft + (Math.random() * 40 - 20) + 'px'; - damageNode.dataset.duration = 2; - damageNode.style.animationDuration = `${damageNode.dataset.duration}s`; + feedbackNode.dataset.duration = 2; + feedbackNode.style.animationDuration = `${feedbackNode.dataset.duration}s`; - return damageNode; + return feedbackNode; + }, + + /** + * @param {number|string} damage + * + * @returns {HTMLElement} + */ + createDamage (damage) { + return UI.createActionFeedback(damage); }, /** * @returns {HTMLElement} */ createDamageMiss () { - return UI.createDamage('MISS!'); + return UI.createActionFeedback('MISS!'); }, /** @@ -392,23 +428,27 @@ const UI = { /** * @param {HTMLElement} damageNode */ + drawActionFeedback (node) { + UI.elements.battleEnemy.appendChild(node); + setTimeout(() => node.remove(), (node.dataset.duration * 1000) - 500); + }, + + /** + * @param {HTMLElement} damageNode + */ drawDamage (damageNode) { - UI.elements.battleEnemy.appendChild(damageNode); - setTimeout(() => damageNode.remove(), (damageNode.dataset.duration * 1000) - 500); + UI.drawActionFeedback(damageNode); UI.elements.battleEnemySprite.classList.add('damaged'); clearTimeout(UI.damageHighlightClickTimeout); UI.damageHighlightClickTimeout = setTimeout(() => UI.elements.battleEnemySprite.classList.remove('damaged'), UI.damageHighlightClickDuration * 1000); - - UI.drawTechniqueAnimation(); }, /** * @param {HTMLElement} damageNode */ drawDamageMiss (damageNode) { - UI.elements.battleEnemy.appendChild(damageNode); - setTimeout(() => damageNode.remove(), (damageNode.dataset.duration * 1000) - 500); + UI.drawActionFeedback(damageNode); }, @@ -428,6 +468,10 @@ const UI = { const partyMonster = UI.createTemplate(Template.partyMonster); partyMonster.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/battle/${monster.slug}-front.png`; + partyMonster.querySelector('[data-template-slot="name"]').textContent = monster.name; + partyMonster.querySelector('[data-template-slot="gender"]').innerHTML = UI.createGenderIcon(monster.gender).outerHTML; + partyMonster.querySelector('[data-template-slot="level"]').textContent = monster.level; + partyMonster.querySelector('[data-template-slot="hpText"]').textContent = `${monster.hp} / ${monster.stats.hp}`; partyMonster.addEventListener('click', async (event) => { // bubble up to partyNode @@ -495,7 +539,7 @@ const UI = { UI.drawPopup(popup); }, - openJournalMenu () { // TODO + openJournalMenu () { const popup = UI.createPopup(); const journal = UI.createTemplate(Template.menuJournal); @@ -511,28 +555,32 @@ const UI = { UI.drawPopup(popup); }, - openSaveDialog () { // TODO + openSaveDialog () { const popup = UI.createPopup(); + const dialog = UI.createTemplate(Template.dialogSave); - const textarea = document.createElement('textarea'); - textarea.value = Game.save(); + dialog.querySelector('[data-template-slot="saveData"]').value = Game.save(); + dialog.querySelector('[data-template-slot="saveClipboard"]').addEventListener('click', () => { + alert('Saved to clipboard!'); + }); - popup.querySelector('.popup').appendChild(textarea); + popup.querySelector('.popup').appendChild(dialog); UI.drawPopup(popup); }, - openLoadDialog () { // TODO + openLoadDialog () { const popup = UI.createPopup(); + const dialog = UI.createTemplate(Template.dialogLoad); - const textarea = document.createElement('textarea'); - - const loadButton = document.createElement('button'); - loadButton.textContent = "Load"; - loadButton.addEventListener('click', () => Game.load(textarea.value.trim())); + dialog.querySelector('[data-template-slot="load"]').addEventListener('click', () => { + Game.load( + dialog.querySelector('[data-template-slot="saveData"]').value.trim() + ); - popup.querySelector('.popup').appendChild(textarea); - popup.querySelector('.popup').appendChild(loadButton); + document.querySelectorAll('.popup__overlay').forEach((element) => element.remove()) + }); + popup.querySelector('.popup').appendChild(dialog); UI.drawPopup(popup); }, @@ -545,12 +593,20 @@ const UI = { * @returns {HTMLElement} */ createStatsMenu (monster) { // TODO - const template = document.createElement('div'); - template.textContent = "select moves for " + monster.name; - template.style.width = '90vw'; - template.style.height = '90vh'; + const template = UI.createTemplate(Template.monsterStats); - template.addEventListener('click', () => UI.openMovesetSelection(monster)); + template.querySelector('[data-template-slot="name"]').textContent = monster.name; + template.querySelector('[data-template-slot="gender"]').innerHTML = UI.createGenderIcon(monster.gender).outerHTML; + template.querySelector('[data-template-slot="level"]').textContent = monster.level; + template.querySelector('[data-template-slot="types"]').innerHTML = monster.types.map((type) => UI.createElementTypeIcon(type).outerHTML).join(''); + + template.querySelector('[data-template-slot="stats.melee"]').textContent = monster.stats.melee; + template.querySelector('[data-template-slot="stats.armour"]').textContent = monster.stats.armour; + template.querySelector('[data-template-slot="stats.ranged"]').textContent = monster.stats.ranged; + template.querySelector('[data-template-slot="stats.dodge"]').textContent = monster.stats.dodge; + template.querySelector('[data-template-slot="stats.speed"]').textContent = monster.stats.speed; + + template.querySelector('[data-template-slot="techniques"]').addEventListener('click', () => UI.openMovesetSelection(monster)); return template; }, @@ -566,7 +622,7 @@ const UI = { const technique = await fetchTechnique(move.technique); const movesetItemNode = UI.createTemplate(Template.movesetItem); - movesetItemNode.querySelector('[data-template-slot="name"]').textContent = slugToName(technique.slug); + movesetItemNode.querySelector('[data-template-slot="name"]').textContent = technique.name; movesetItemNode.querySelector('[data-template-slot="types"]').innerHTML = technique.types.map((type) => UI.createElementTypeIcon(type).outerHTML).join(''); movesetItemNode.querySelector('[data-template-slot="power"]').textContent = technique.power; movesetItemNode.querySelector('[data-template-slot="level"]').textContent = move.level_learned; @@ -587,6 +643,10 @@ const UI = { return false; } + if (monster.activeTechniques.length === 4 && !movesetItemNode.hasAttribute('selected')) { + return; + } + // un/select movesetItemNode.toggleAttribute('selected'); |