From aa44f67ab57673528e96a4a075fbd8cd0354bd68 Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Thu, 17 Aug 2023 22:46:12 +0200 Subject: ui --- resources/js/ui.js | 154 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 47 deletions(-) (limited to 'resources/js/ui.js') 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, @@ -149,6 +148,21 @@ const UI = { return template; }, + /** + * @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 * @@ -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!'); }, /** @@ -389,26 +425,30 @@ const UI = { return damageNode; }, + /** + * @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'); -- cgit v1.2.3