summaryrefslogtreecommitdiff
path: root/resources/js/ui.js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/js/ui.js')
-rw-r--r--resources/js/ui.js512
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
/**