diff options
Diffstat (limited to 'resources/js/ui.js')
-rw-r--r-- | resources/js/ui.js | 512 |
1 files changed, 324 insertions, 188 deletions
diff --git a/resources/js/ui.js b/resources/js/ui.js index ee33a64..5626048 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -39,6 +39,8 @@ const Template = { dialogLoad: document.querySelector('#tpl___dialog__load'), menuSettings: document.querySelector('#tpl___menu__settings'), + + storyPopup: document.querySelector('#tpl___story__popup'), }; const UI = { @@ -98,17 +100,26 @@ const UI = { }, /** + * @param {Object} options + * @param {boolean} [options.isClosable=true] + * * @returns {HTMLElement} */ - createPopup () { + createPopup (options) { + options = Object.assign({ + isClosable: true, + }, options); + const popup = UI.createTemplate(Template.popup); - popup.addEventListener('click', ({ target }) => { - if (target === popup) { - popup.dispatchEvent(new Event('close')); - popup.remove(); - } - }); + if (options.isClosable) { + popup.addEventListener('click', ({ target }) => { + if (target === popup) { + popup.dispatchEvent(new Event('close')); + popup.remove(); + } + }); + } return popup; }, @@ -534,14 +545,21 @@ const UI = { UI.drawTown(); UI.closeLog(); UI.showMap(); - } else { - UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`; - UI.showBattle(); - UI.drawOpponentMonster(); - UI.drawActiveMonster(); - UI.drawActiveTechniques(); + UI.drawStatus(); + UI.drawActiveBall(); + } else { + UI.drawBattle(); } + }, + + drawBattle () { + UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`; + + UI.showBattle(); + UI.drawOpponentMonster(); + UI.drawActiveMonster(); + UI.drawActiveTechniques(); UI.drawStatus(); UI.drawActiveBall(); @@ -736,6 +754,22 @@ const UI = { } }))); + template.querySelectorAll('[data-interactable="true"]').forEach((node) => { + if (node.dataset.story) { + if (node.dataset.storyOnce && Memory.state.storyProgress[node.dataset.story]) { + node.dataset.interactable = false; + return; + } + } + + node.addEventListener('click', UI.wrapCallback(async () => { + if (node.dataset.story) { + await Story.progress(node.dataset.story); + UI.drawTown(); + } + })); + }); + return template; }, @@ -833,6 +867,11 @@ const UI = { })); template.querySelector('[data-template-slot="box.view"]').addEventListener('click', UI.wrapCallback(() => { + if (Memory.state.monsters.length === 0) { + alert(translate('ui:healing_center:box:view:no_tuxemon_in_box', true)); + return; + } + const boxPopup = UI.createPopup(); const monsterSelection = UI.createMonsterSelection(Memory.state.monsters); monsterSelection.addEventListener('monster:selected', UI.wrapCallback((event) => { @@ -1044,7 +1083,10 @@ const UI = { } const changeAreaButton = UI.elements.changeArea; - if (!Game.isTown(currentArea)) { + if (Game.isStoryBattle) { + changeAreaButton.disabled = true; + } + else if (!Game.isTown(currentArea)) { if ( Memory.state.Game.isInBattle || (Memory.state.opponent && Memory.state.opponent.type === 'trainer' && Memory.state.opponent.activeMonster !== Memory.state.opponent.monsters[0]) @@ -1061,14 +1103,16 @@ const UI = { /** * @param {Monster[]} monsters */ - openStarterMonsterSelection (monsters) { + openStarterMonsterSelection (monsters, { title }) { const popup = UI.createPopup().cloneNode(true); // remove close event const template = UI.createPartySelection(monsters); - const title = document.createElement('h1'); - title.textContent = 'Select your Tuxemon!'; - title.style.textAlign = 'center'; - template.prepend(title); + const titleNode = document.createElement('h1'); + titleNode.textContent = title; + titleNode.style.textAlign = 'center'; + titleNode.style.margin = 0; + titleNode.style.padding = '1rem'; + template.prepend(titleNode); template.addEventListener('party:monster:selected', (event) => { const monster = event.detail.monster; @@ -1160,8 +1204,12 @@ const UI = { } } - else if (condition.startsWith('event.')) { - canGo = false; + else if (condition.startsWith('story.')) { + const storyCondition = condition.replace('story.', ''); + const storySlug = storyCondition; + const storyProgress = Memory.state.storyProgress[storySlug] || false; + + canGo = canGo && storyProgress; } } @@ -1334,175 +1382,12 @@ const UI = { openSettingsMenu () { const popup = UI.createPopup(); - const template = UI.createTemplate(Template.menuSettings); - - - /* Language */ - - const languageSelectNode = template.querySelector('[data-template-slot="language"]'); - - const languages = { - 'cs_CZ': 'Czech (Czech Republic)', - 'de_DE': 'German (Germany)', - 'en_US': 'English (United States)', - 'eo': 'Esperanto', - 'es_ES': 'Spanish (Spain)', - 'es_MX': 'Spanish (Mexico)', - 'fi': 'Finnish', - 'fr_FR': 'French (France)', - 'it_IT': 'Italian (Italy)', - 'ja': 'Japanese', - 'nb_NO': 'Norwegian Bokmål (Norway)', - 'pl': 'Polish', - 'pt_BR': 'Portuguese (Brazil)', - 'zh_CN': 'Chinese (China)', - }; - - for (const languageCode of Object.keys(languages)) { - const languageName = languages[languageCode]; - const languageOptionNode = document.createElement('option'); - - languageOptionNode.value = languageCode; - languageOptionNode.textContent = languageName; - - if (languageCode === Memory.state.Settings.language) { - languageOptionNode.selected = true; - } - - languageSelectNode.appendChild(languageOptionNode); - } - - languageSelectNode.addEventListener('change', UI.wrapCallback(async () => { - const selected = [...languageSelectNode.children].find((node) => node.selected === true); - Memory.state.Settings.language = selected.value; - - await fetchTranslation(Memory.state.Settings.language); - applyTranslation(); - - UI.drawArea(); - })); - - - /* Currency */ - - const currencySelectNode = template.querySelector('[data-template-slot="currency"]'); - const currencyCodeMap = Object.keys(DB.currencies.map).sort((a, b) => { - const nameA = DB.currencies.map[a].name; - const nameB = DB.currencies.map[b].name; - - return nameA > nameB ? 1 : -1; - }); - - for (const currencyCode of currencyCodeMap) { - const currency = DB.currencies.map[currencyCode]; - const currencyOptionNode = document.createElement('option'); - - currencyOptionNode.value = currencyCode, - currencyOptionNode.textContent = `${currency.symbol} - ${currency.name}`; - - if (currencyCode === Memory.state.Settings.currency) { - currencyOptionNode.selected = true; - } - - currencySelectNode.appendChild(currencyOptionNode); - } - - currencySelectNode.addEventListener('change', UI.wrapCallback(async () => { - const selected = [...currencySelectNode.children].find((node) => node.selected === true); - const previousCurrencyCode = Memory.state.Settings.currency; - const newCurrencyCode = selected.value; - - Memory.state.Settings.currency = newCurrencyCode; + const template = UI.createSettingsMenu(); - // re-calculate money - const previousCurrency = DB.currencies.map[previousCurrencyCode]; - const newCurrency = DB.currencies.map[newCurrencyCode]; - const baseRateMoney = Memory.state.money / previousCurrency.rate; - const exchangedMoney = baseRateMoney * newCurrency.rate; - Memory.state.money = Number(exchangedMoney.toFixed(newCurrency.decimals)); - - UI.drawArea(); - UI.drawStatus(); + popup.addEventListener('close', UI.wrapCallback(() => { + Memory.saveToLocalStorage(); })); - template.querySelector('[data-template-slot="currency.lastUpdated"]').textContent = DB.currencies.last_updated; - - - // Highlight - - template.querySelector('[data-template-slot="highlight"]').addEventListener('click', UI.wrapCallback(() => { - UI.isHighlighting = !UI.isHighlighting; - - const elements = [ - UI.elements.battleOpponent, - UI.elements.battlePlayer.querySelector('[data-template-slot="sprite"]'), - UI.elements.techniques, - UI.elements.showMap, - UI.elements.nextTrainer, - UI.elements.changeArea, - UI.elements.menuParty, - UI.elements.menuCatch, - UI.elements.menuInventory, - UI.elements.menuLog, - UI.elements.menuJournal, - UI.elements.menuSettings, - - Template.technique.content.firstElementChild, - Template.healingCenter.content.querySelector('[data-template-slot="heal"]'), - Template.shopItem.content.firstElementChild, - ...Template.monsterStats.content.querySelectorAll('button'), - Template.movesetItem.content.firstElementChild, - Template.tabHeading.content.querySelector('[data-template-slot="label"]'), - ...Template.party.content.querySelectorAll('[data-template-slot="modes"] button'), - Template.partyMonster.content.firstElementChild, - Template.inventoryItem.content.firstElementChild, - ...Template.inventory.content.querySelectorAll('[data-template-slot="modes"] button'), - ...Template.menuSettings.content.querySelectorAll('select, button'), - - ...document.querySelector('.menu__settings').querySelectorAll('select, button'), - ]; - - for (const element of elements) { - if (!element) { - continue; - } - - if (UI.isHighlighting) { - element.classList.add(UI.highlightClassName); - } else { - element.classList.remove(UI.highlightClassName); - } - } - - // map - const mapElements = [ - UI.elements.sceneTown.querySelector('[data-template-slot="map"]').firstElementChild, - Template.map.content.firstElementChild, - ]; - for (const element of mapElements) { - if (!element) { - continue; - } - - if (UI.isHighlighting) { - element.classList.add(`${UI.highlightClassName}--map`); - } else { - element.classList.remove(`${UI.highlightClassName}--map`); - } - } - })); - - - // Clear save data - - template.querySelector('[data-template-slot="clearLocalSaveData"]').addEventListener('click', UI.wrapCallback(() => { - if (confirm(translate('ui:settings:clear_local_save_data:confirm', true))) { - localStorage.removeItem('state'); - window.location.reload(); - } - })); - - popup.querySelector('.popup').appendChild(template); UI.drawPopup(popup); }, @@ -1536,7 +1421,7 @@ const UI = { * * @returns {HTMLElement} */ - createStatsMenu (monster) { // TODO + createStatsMenu (monster) { const template = UI.createTemplate(Template.monsterStats); template.querySelector('[data-template-slot="name"]').textContent = monster.name; @@ -1544,6 +1429,8 @@ const UI = { 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="exp"]').innerHTML = `${monster.exp} / ${monster.getExperienceRequired(1)}`; + template.querySelector('[data-template-slot="stats.melee.name"]').textContent = translate(StatType.melee) || slugToName(StatType.melee); template.querySelector('[data-template-slot="stats.armour.name"]').textContent = translate(StatType.armour) || slugToName(StatType.armour); template.querySelector('[data-template-slot="stats.ranged.name"]').textContent = translate(StatType.ranged) || slugToName(StatType.ranged); @@ -1828,6 +1715,255 @@ const UI = { }, + /* Menu - Settings */ + + /** + * @returns {HTMLElement} + */ + createSettingsMenu () { + const template = UI.createTemplate(Template.menuSettings); + + + /* Name */ + + const nameNode = template.querySelector('[data-template-slot="name"]'); + nameNode.value = Memory.state.Settings.name; + nameNode.addEventListener('input', UI.wrapCallback((event) => { + Memory.state.Settings.name = event.target.value; + })); + + + /* Language */ + + const languageSelectNode = template.querySelector('[data-template-slot="language"]'); + + const languages = { + 'cs_CZ': 'Czech (Czech Republic)', + 'de_DE': 'German (Germany)', + 'en_US': 'English (United States)', + 'eo': 'Esperanto', + 'es_ES': 'Spanish (Spain)', + 'es_MX': 'Spanish (Mexico)', + 'fi': 'Finnish', + 'fr_FR': 'French (France)', + 'it_IT': 'Italian (Italy)', + 'ja': 'Japanese', + 'nb_NO': 'Norwegian Bokmål (Norway)', + 'pl': 'Polish', + 'pt_BR': 'Portuguese (Brazil)', + 'zh_CN': 'Chinese (China)', + }; + + for (const languageCode of Object.keys(languages)) { + const languageName = languages[languageCode]; + const languageOptionNode = document.createElement('option'); + + languageOptionNode.value = languageCode; + languageOptionNode.textContent = languageName; + + if (languageCode === Memory.state.Settings.language) { + languageOptionNode.selected = true; + } + + languageSelectNode.appendChild(languageOptionNode); + } + + languageSelectNode.addEventListener('change', UI.wrapCallback(async () => { + const selected = [...languageSelectNode.children].find((node) => node.selected === true); + Memory.state.Settings.language = selected.value; + + await fetchTranslation(Memory.state.Settings.language); + applyTranslation(); + + UI.drawArea(); + })); + + + /* Currency */ + + const currencySelectNode = template.querySelector('[data-template-slot="currency"]'); + const currencyCodeMap = Object.keys(DB.currencies.map).sort((a, b) => { + const nameA = DB.currencies.map[a].name; + const nameB = DB.currencies.map[b].name; + + return nameA > nameB ? 1 : -1; + }); + + for (const currencyCode of currencyCodeMap) { + const currency = DB.currencies.map[currencyCode]; + const currencyOptionNode = document.createElement('option'); + + currencyOptionNode.value = currencyCode, + currencyOptionNode.textContent = `${currency.symbol} - ${currency.name}`; + + if (currencyCode === Memory.state.Settings.currency) { + currencyOptionNode.selected = true; + } + + currencySelectNode.appendChild(currencyOptionNode); + } + + currencySelectNode.addEventListener('change', UI.wrapCallback(async () => { + const selected = [...currencySelectNode.children].find((node) => node.selected === true); + const previousCurrencyCode = Memory.state.Settings.currency; + const newCurrencyCode = selected.value; + + Memory.state.Settings.currency = newCurrencyCode; + + // re-calculate money + const previousCurrency = DB.currencies.map[previousCurrencyCode]; + const newCurrency = DB.currencies.map[newCurrencyCode]; + const baseRateMoney = Memory.state.money / previousCurrency.rate; + const exchangedMoney = baseRateMoney * newCurrency.rate; + Memory.state.money = Number(exchangedMoney.toFixed(newCurrency.decimals)); + + UI.drawArea(); + UI.drawStatus(); + })); + + template.querySelector('[data-template-slot="currency.lastUpdated"]').textContent = DB.currencies.last_updated; + + + // Highlight + + template.querySelector('[data-template-slot="highlight"]').addEventListener('click', UI.wrapCallback(() => { + UI.isHighlighting = !UI.isHighlighting; + + const elements = [ + UI.elements.battleOpponent, + UI.elements.battlePlayer.querySelector('[data-template-slot="sprite"]'), + UI.elements.techniques, + UI.elements.showMap, + UI.elements.nextTrainer, + UI.elements.changeArea, + UI.elements.menuParty, + UI.elements.menuCatch, + UI.elements.menuInventory, + UI.elements.menuLog, + UI.elements.menuJournal, + UI.elements.menuSettings, + + Template.technique.content.firstElementChild, + Template.healingCenter.content.querySelector('[data-template-slot="heal"]'), + Template.shopItem.content.firstElementChild, + ...Template.monsterStats.content.querySelectorAll('button'), + Template.movesetItem.content.firstElementChild, + Template.tabHeading.content.querySelector('[data-template-slot="label"]'), + ...Template.party.content.querySelectorAll('[data-template-slot="modes"] button'), + Template.partyMonster.content.firstElementChild, + Template.inventoryItem.content.firstElementChild, + ...Template.inventory.content.querySelectorAll('[data-template-slot="modes"] button'), + ...Template.menuSettings.content.querySelectorAll('select, button'), + + ...document.querySelector('.menu__settings').querySelectorAll('select, button'), + ]; + + for (const element of elements) { + if (!element) { + continue; + } + + if (UI.isHighlighting) { + element.classList.add(UI.highlightClassName); + } else { + element.classList.remove(UI.highlightClassName); + } + } + + // map + const mapElements = [ + UI.elements.sceneTown.querySelector('[data-template-slot="map"]').firstElementChild, + Template.map.content.firstElementChild, + ]; + for (const element of mapElements) { + if (!element) { + continue; + } + + if (UI.isHighlighting) { + element.classList.add(`${UI.highlightClassName}--map`); + } else { + element.classList.remove(`${UI.highlightClassName}--map`); + } + } + })); + + + // Clear save data + + template.querySelector('[data-template-slot="clearLocalSaveData"]').addEventListener('click', UI.wrapCallback(() => { + if (confirm(translate('ui:settings:clear_local_save_data:confirm', true))) { + localStorage.removeItem('state'); + window.location.reload(); + } + })); + + return template; + }, + + + // Story + + /** + * @returns {HTMLElement} + */ + createStoryPopup () { + const popup = UI.createPopup(); + + return popup; + }, + + /** + * @param {HTMLElement} popup + * + * @returns {HTMLElement} + */ + applyStoryPopupContent (popup, { speaker, text }) { + const template = UI.createTemplate(Template.storyPopup); + + template.querySelector('[data-template-slot="speaker.sprite"]').src = `/modules/tuxemon/mods/tuxemon/gfx/sprites/player/${speaker.template[0].sprite_name}.png`; + template.querySelector('[data-template-slot="speaker.name"]').textContent = slugToName(speaker.slug.replace('spyder_', '')); + template.querySelector('[data-template-slot="text"]').innerHTML = nl2br(text); + template.querySelector('[data-template-slot="next"]').addEventListener('click', UI.wrapCallback(() => { + popup.dispatchEvent(new Event('next')); + })); + + popup.querySelector('[data-template-slot="content"]').append(template); + + return popup; + }, + + /** + * @param {HTMLElement} popup + * + * @returns {Promise<any>} + */ + drawStoryPopup (popup) { + UI.drawPopup(popup); + + return new Promise((resolve, _reject) => { + popup.addEventListener('next', () => { + popup.remove(); + resolve(); + }); + + popup.addEventListener('close', () => { + resolve(); + }); + }); + }, + + /** + * @returns {Promise<any>} + */ + async buildAndShowStoryPopup ({ speaker, text }) { + let popup = UI.createStoryPopup(); + popup = UI.applyStoryPopupContent(popup, { speaker: speaker, text: text }); + + return await UI.drawStoryPopup(popup); + }, + + // Error /** |