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.js399
1 files changed, 325 insertions, 74 deletions
diff --git a/resources/js/ui.js b/resources/js/ui.js
index 3940574..6f38867 100644
--- a/resources/js/ui.js
+++ b/resources/js/ui.js
@@ -14,6 +14,10 @@ const Template = {
techniques: document.querySelector('#tpl___techniques'),
technique: document.querySelector('#tpl___technique'),
+ healingCenter: document.querySelector('#tpl___healing-center'),
+ shop: document.querySelector('#tpl___shop'),
+ shopItem: document.querySelector('#tpl___shop__item'),
+
party: document.querySelector('#tpl___party'),
partyMonster: document.querySelector('#tpl___party__monster'),
@@ -51,6 +55,7 @@ const UI = {
log: document.querySelector('#log'),
status: document.querySelector('#status'),
+ showMap: document.querySelector('#status [data-template-slot="showMap"]'),
nextTrainer: document.querySelector('#status [data-template-slot="nextTrainer"]'),
changeArea: document.querySelector('#status [data-template-slot="changeArea"]'),
@@ -274,6 +279,14 @@ const UI = {
return document.createElement('i');
}
+ if (statusEffect.slug === 'faint') {
+ const node = document.createElement('b');
+ node.innerHTML = 'X';
+ node.title = statusEffect.name;
+
+ return node;
+ }
+
const img = document.createElement('img');
img.src = `/modules/tuxemon/mods/tuxemon/gfx/ui/icons/status/icon_${statusEffect.slug}.png`;
img.title = statusEffect.name;
@@ -420,6 +433,10 @@ const UI = {
* @returns {void}
*/
drawActiveTechniques () {
+ if (!Memory.state.player) { // on starter selection screen only
+ return;
+ }
+
const activeTechniques = UI.createActiveTechniques(Memory.state.player.activeMonster);
activeTechniques.id = 'techniques';
@@ -495,8 +512,35 @@ const UI = {
UI.elements.log.classList.toggle('log--is-hidden');
},
+ openLog () {
+ UI.elements.log.classList.remove('log--is-hidden');
+ },
+
+ closeLog () {
+ UI.elements.log.classList.add('log--is-hidden');
+ },
+
drawArea () {
- UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`;
+ if (Game.isTown(Memory.state.currentArea)) {
+ UI.elements.sceneTown.querySelector('[data-template-slot="map"]').replaceChildren(UI.createMap());
+
+ UI.closeLog();
+
+ UI.elements.sceneBattle.classList.add('hidden');
+ UI.elements.sceneTown.classList.remove('hidden');
+ } else {
+ UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${Memory.state.currentArea.environment.battle_graphics.background})`;
+
+ UI.elements.sceneTown.classList.add('hidden');
+ UI.elements.sceneBattle.classList.remove('hidden');
+
+ UI.drawOpponentMonster();
+ UI.drawActiveMonster();
+ UI.drawActiveTechniques();
+ }
+
+ UI.drawStatus();
+ UI.drawActiveBall();
},
progressTurn () {
@@ -633,39 +677,120 @@ const UI = {
/* Town */
- async drawTown () {
+ drawTown () {},
+
+
+ /* Map */
+
+ /**
+ * @returns {HTMLElement}
+ */
+ createMap () {
+ const template = document.createElement('div');
const currentArea = Memory.state.currentArea;
- UI.elements.sceneTown.innerHTML = Memory.state.currentArea.map;
+ template.innerHTML = currentArea.map;
+ template.style.width = '100vw';
+ template.style.maxWidth = '750px';
+
+ if (currentArea.locations) {
+ for (const locationId of Object.keys(currentArea.locations)) {
+ const location = currentArea.locations[locationId];
+
+ template.querySelector(`[data-location="${locationId}"]`).addEventListener('click', () => {
+ if (location.type === 'healingCenter') {
+ UI.openHealingCenter(location);
+ }
+
+ else if (location.type === 'shop') {
+ UI.openShop(location);
+ }
+ });
+ }
+ }
+
+ return template;
+ },
- for (const locationId of Object.keys(currentArea.locations)) {
- const location = currentArea.locations[locationId];
+ /**
+ * @param {Object} healingCenter
+ */
+ openHealingCenter (healingCenter) {
+ const popup = UI.createPopup();
+ const template = UI.createTemplate(Template.healingCenter);
+
+ const price = convertToCurrencyBase(healingCenter.price);
+ template.querySelector('[data-template-slot="price"]').innerHTML = formatPrice(price);
- UI.elements.sceneTown.querySelector(`[data-location="${locationId}"]`).addEventListener('click', () => {
- if (location.type === 'healingCenter') {
- UI.openHealingCenter(location);
+ template.querySelector('[data-template-slot="heal"]').addEventListener('click', () => {
+ const applicableMonsters = Memory.state.player.monsters.filter((monster) => monster.hp < monster.stats.hp || monster.statusEffect);
+ if (applicableMonsters.length === 0) {
+ alert('No applicable monsters.');
+ return;
+ }
+
+ const monsterSelectionPopup = UI.createPopup();
+ const monsterSelection = UI.createMonsterSelection(applicableMonsters);
+
+ monsterSelection.addEventListener('monster:selected', (event) => {
+ if (Memory.state.money < price) {
+ alert(`Not enough ${DB.currencies.map[Memory.state.Settings.currency].symbol}.`);
+ return;
}
- else if (location.type === 'shop') {
- UI.openShop(location);
+ Memory.state.money -= price;
+ event.detail.monster.hp = event.detail.monster.stats.hp;
+ event.detail.monster.statusEffect = null;
+ event.detail.node.remove();
+
+ if (monsterSelection.children.length === 0) {
+ monsterSelectionPopup.remove();
}
+
+ UI.drawStatus();
});
- }
- },
- openHealingCenter (healingCenter) {},
+ monsterSelectionPopup.querySelector('.popup').appendChild(monsterSelection);
+ UI.drawPopup(monsterSelectionPopup);
+ });
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
+ },
+ /**
+ * @param {Object} shop
+ */
async openShop (shop) {
const popup = UI.createPopup();
- const template = document.createElement('div');
+ const template = UI.createTemplate(Template.shop);
for (const itemData of shop.items) {
+ const price = convertToCurrencyBase(itemData.price);
const item = await fetchItem(itemData.item_name);
- const itemNode = document.createElement('div');
+ const itemNode = UI.createTemplate(Template.shopItem);
+
+ itemNode.querySelector('[data-template-slot="sprite"]').src = item.sprite;
+ itemNode.querySelector('[data-template-slot="name"]').innerHTML = item.name;
+ itemNode.querySelector('[data-template-slot="price"]').innerHTML = formatPrice(price);
+
+ itemNode.addEventListener('click', () => {
+ if (Memory.state.money < price) {
+ alert(`Not enough ${DB.currencies.map[Memory.state.Settings.currency].symbol}.`);
+ return;
+ }
+
+ Memory.state.money -= price;
- itemNode.innerHTML = `<img src="/modules/tuxemon/mods/tuxemon/${item.sprite}" />`;
- itemNode.innerHTML += `${item.name}`;
- itemNode.innerHTML += `${itemData.price} ${DB.currencies.map[Memory.state.Settings.currency].symbol}`;
+ const itemInInventory = Memory.state.player.inventory.find((inventoryItem) => inventoryItem.slug === item.slug);
+ if (itemInInventory) {
+ itemInInventory.quantity++;
+ } else {
+ Memory.state.player.inventory.push(new InventoryItem(item, 1));
+ }
+
+ UI.drawStatus();
+ });
template.appendChild(itemNode);
}
@@ -679,6 +804,7 @@ const UI = {
partySelectionMode: 'select',
inventorySelectionMode: 'use',
+ isHighlighting: false,
/**
@@ -727,6 +853,56 @@ const UI = {
return template;
},
+ /**
+ * @param {Monster[]} monsters
+ *
+ * @returns {HTMLElement}
+ */
+ createPartySelection (monsters) {
+ const party = UI.createTemplate(Template.party);
+ party.id = 'party';
+ for (const monsterIdx in monsters) {
+ const monster = monsters[monsterIdx];
+ const partyMonster = UI.createPartyMonster(monster);
+
+ partyMonster.addEventListener('click', async (event) => {
+ // bubble up to partyNode
+ let target = event.target;
+ while (target.parentNode.id !== party.id) {
+ target = target.parentNode;
+ }
+
+ party.dispatchEvent(new CustomEvent('party:monster:selected', {
+ detail: {
+ monster: monster,
+ mode: UI.partySelectionMode,
+ node: partyMonster,
+ },
+ }));
+ });
+
+ party.querySelector('[data-template-slot="monsters"]').appendChild(partyMonster);
+ }
+
+ const selectionModesNode = party.querySelector('[data-template-slot="modes"]');
+ const selectionModeNodes = selectionModesNode.querySelectorAll('[data-party-selection-mode]');
+ selectionModeNodes.forEach((node) => {
+ if (node.dataset.partySelectionMode === UI.partySelectionMode) {
+ node.setAttribute('selected', true);
+ }
+
+ node.addEventListener('click', () => {
+ selectionModesNode.querySelector(`[data-party-selection-mode="${UI.partySelectionMode}"]`).removeAttribute('selected');
+
+ UI.partySelectionMode = node.dataset.partySelectionMode;
+
+ node.setAttribute('selected', true);
+ });
+ });
+
+ return party;
+ },
+
drawStatus () {
const currentArea = Memory.state.currentArea;
@@ -739,15 +915,84 @@ const UI = {
UI.elements.status.querySelector('[data-template-slot="trainerProgress"]').textContent = `${currentArea.trainerProgress} / ${currentArea.trainers.length}`;
const nextTrainerButton = UI.elements.nextTrainer;
- if (
- Memory.state.opponent.type === 'monster' &&
- currentArea.monsterProgress >= currentArea.requiredEncounters &&
- currentArea.trainerProgress < currentArea.trainers.length
- ) {
- nextTrainerButton.disabled = false;
+ if (!Game.isTown(currentArea)) {
+ if (
+ Memory.state.opponent.type === 'monster' &&
+ currentArea.monsterProgress >= currentArea.requiredEncounters &&
+ currentArea.trainerProgress < currentArea.trainers.length &&
+ !Game.isInBattle
+ ) {
+ nextTrainerButton.disabled = false;
+ } else {
+ nextTrainerButton.disabled = true;
+ }
} else {
nextTrainerButton.disabled = true;
}
+
+ const changeAreaButton = UI.elements.changeArea;
+ if (!Game.isTown(currentArea)) {
+ if (
+ Game.isInBattle ||
+ (Memory.state.opponent && Memory.state.opponent.type === 'trainer')
+ ) {
+ changeAreaButton.disabled = true;
+ } else {
+ changeAreaButton.disabled = false;
+ }
+ } else {
+ changeAreaButton.disabled = false;
+ }
+ },
+
+ /**
+ * @param {Monster[]} monsters
+ */
+ openStarterMonsterSelection (monsters) {
+ 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);
+
+ template.addEventListener('party:monster:selected', (event) => {
+ const monster = event.detail.monster;
+
+ if (UI.partySelectionMode === 'select') {
+ template.dispatchEvent(new CustomEvent('starter:monster:selected', {
+ detail: {
+ monster: monster,
+ node: event.detail.node,
+ popup: popup,
+ },
+ }));
+ }
+ else if (UI.partySelectionMode === 'stats') {
+ UI.openStatsMenu(monster);
+ }
+ else if (UI.partySelectionMode === 'techniques') {
+ UI.openMovesetSelection(monster);
+ }
+ });
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
+
+ return template;
+ },
+
+ openMap () {
+ if (Game.isInBattle || Game.isTown(Memory.state.currentArea)) {
+ return;
+ }
+
+ const popup = UI.createPopup();
+ const template = UI.createMap();
+
+ popup.querySelector('.popup').appendChild(template);
+ UI.drawPopup(popup);
},
openAreaSelection () {
@@ -771,6 +1016,10 @@ const UI = {
canGo = canGo && currentArea.trainerProgress >= currentArea.trainers.length;
}
+ else if (condition.startsWith('area.')) {
+ canGo = Memory.state.areaProgress.hasOwnProperty(condition.replace('area.', ''));
+ }
+
else if (condition.startsWith('event.')) {
canGo = false;
}
@@ -798,60 +1047,25 @@ const UI = {
openPartyMenu () {
const popup = UI.createPopup();
+ const template = UI.createPartySelection(Memory.state.player.monsters);
- const party = UI.createTemplate(Template.party);
- party.id = 'party';
- for (const monsterIdx in Memory.state.player.monsters) {
- const monster = Memory.state.player.monsters[monsterIdx];
- const partyMonster = UI.createPartyMonster(monster);
-
- partyMonster.addEventListener('click', async (event) => {
- // bubble up to partyNode
- let target = event.target;
- while (target.parentNode.id !== party.id) {
- target = target.parentNode;
- }
-
- if (UI.partySelectionMode === 'select') {
- Game.setActivePlayerMonster(monster);
-
- popup.remove();
- }
- else if (UI.partySelectionMode === 'stats') {
- UI.openStatsMenu(monster);
- }
- else if (UI.partySelectionMode === 'techniques') {
- UI.openMovesetSelection(monster);
- }
-
- UI.events.dispatchEvent(new CustomEvent('party:monsterSelected', {
- detail: {
- monster: monster,
- mode: UI.partySelectionMode,
- },
- }));
- });
+ template.addEventListener('party:monster:selected', (event) => {
+ const monster = event.detail.monster;
- party.querySelector('[data-template-slot="monsters"]').appendChild(partyMonster);
- }
+ if (UI.partySelectionMode === 'select') {
+ Game.setActivePlayerMonster(monster);
- const selectionModesNode = party.querySelector('[data-template-slot="modes"]');
- const selectionModeNodes = selectionModesNode.querySelectorAll('[data-party-selection-mode]');
- selectionModeNodes.forEach((node) => {
- if (node.dataset.partySelectionMode === UI.partySelectionMode) {
- node.setAttribute('selected', true);
+ popup.remove();
+ }
+ else if (UI.partySelectionMode === 'stats') {
+ UI.openStatsMenu(monster);
+ }
+ else if (UI.partySelectionMode === 'techniques') {
+ UI.openMovesetSelection(monster);
}
-
- node.addEventListener('click', () => {
- selectionModesNode.querySelector(`[data-party-selection-mode="${UI.partySelectionMode}"]`).removeAttribute('selected');
-
- UI.partySelectionMode = node.dataset.partySelectionMode;
-
- node.setAttribute('selected', true);
- });
});
- popup.querySelector('.popup').appendChild(party);
+ popup.querySelector('.popup').appendChild(template);
UI.drawPopup(popup);
},
@@ -1067,13 +1281,44 @@ const UI = {
const exchangedMoney = baseRateMoney * newCurrency.rate;
Memory.state.money = Number(exchangedMoney.toFixed(newCurrency.decimals));
- UI.drawTown();
+ 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.isHighlighting = !UI.isHighlighting;
+
+ const elements = [
+ UI.elements.battleOpponent,
+ UI.elements.battlePlayer.querySelector('[data-template-slot="sprite"]'),
+ UI.elements.techniques,
+ ...UI.elements.sceneTown.querySelectorAll('[data-location]'),
+ 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,
+ ];
+
+ for (const element of elements) {
+ if (UI.isHighlighting) {
+ element.classList.add('setting-highlight');
+ } else {
+ element.classList.remove('setting-highlight');
+ }
+ }
+ });
+
+
popup.querySelector('.popup').appendChild(template);
UI.drawPopup(popup);
},
@@ -1093,6 +1338,7 @@ const UI = {
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="statusEffect"]').innerHTML = UI.createStatusEffectIcon(monster.statusEffect).outerHTML;
partyMonster.querySelector('[data-template-slot="hpText"]').textContent = `${monster.hp} / ${monster.stats.hp}`;
return partyMonster;
@@ -1235,7 +1481,7 @@ const UI = {
inventoryItemNode.title = item.description;
inventoryItemNode.dataset.inventoryItem = item.slug;
- inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/${item.sprite}`;
+ inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = item.sprite;
inventoryItemNode.querySelector('[data-template-slot="name"]').textContent = item.name;
inventoryItemNode.querySelector('[data-template-slot="quantity"]').textContent = item.quantity;
@@ -1245,6 +1491,10 @@ const UI = {
UI.openItemMonsterSelection(item);
}
+ else if (item.category === 'revive') {
+ UI.openItemMonsterSelection(item);
+ }
+
else if (item.category === 'capture') {
Game.useItem(item);
}
@@ -1389,6 +1639,7 @@ const UI = {
};
// UI element click bindings
+UI.elements.showMap.addEventListener('click', UI.openMap);
UI.elements.changeArea.addEventListener('click', UI.openAreaSelection);
UI.elements.menuParty.addEventListener('click', UI.openPartyMenu);
UI.elements.menuInventory.addEventListener('click', UI.openInventoryMenu);