summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2023-08-22 15:01:03 +0200
committerDaniel Weipert <code@drogueronin.de>2023-08-22 15:01:03 +0200
commitc0354b250f84d578b609a7f25d71dee7fc24e9ca (patch)
treeaf586e0a4c44a2f2c8df956ca3b992be15daaba3
parent54e5ffaeb79f989463c144e58dfcd677b752c5a9 (diff)
currency, save/load
-rw-r--r--db/_generated/currencies.json1
-rw-r--r--db/currencies/currencies.php42
-rw-r--r--index.html8
-rw-r--r--resources/js/classes/State.js2
-rw-r--r--resources/js/classes/Trainer.js3
-rw-r--r--resources/js/db.js6
-rw-r--r--resources/js/formula.js14
-rw-r--r--resources/js/game.js130
-rw-r--r--resources/js/memory.js38
-rw-r--r--resources/js/ui.js49
10 files changed, 239 insertions, 54 deletions
diff --git a/db/_generated/currencies.json b/db/_generated/currencies.json
new file mode 100644
index 0000000..d3dc796
--- /dev/null
+++ b/db/_generated/currencies.json
@@ -0,0 +1 @@
+{"last_updated":"2023-08-21","map":{"AUD":{"rate":1.6995,"symbol":"$","name":"Australian Dollar","decimals":2},"BGN":{"rate":1.9558,"symbol":"\u043b\u0432","name":"Bulgarian Lev","decimals":2},"BRL":{"rate":5.414,"symbol":"R$","name":"Brazilian Real","decimals":2},"CAD":{"rate":1.4723,"symbol":"$","name":"Canadian Dollar","decimals":2},"CHF":{"rate":0.9588,"symbol":"CHF","name":"Swiss Franc","decimals":2},"CNY":{"rate":7.9456,"symbol":"\u00a5","name":"Chinese Yuan","decimals":2},"CZK":{"rate":24.02,"symbol":"K\u010d","name":"Czech Republic Koruna","decimals":2},"DKK":{"rate":7.4524,"symbol":"kr","name":"Danish Krone","decimals":2},"GBP":{"rate":0.85475,"symbol":"\u00a3","name":"British Pound Sterling","decimals":2},"HKD":{"rate":8.5488,"symbol":"$","name":"Hong Kong Dollar","decimals":2},"HUF":{"rate":381.73,"symbol":"Ft","name":"Hungarian Forint","decimals":2},"IDR":{"rate":16718,"symbol":"Rp","name":"Indonesian Rupiah","decimals":2},"ILS":{"rate":4.1395,"symbol":"\u20aa","name":"Israeli New Sheqel","decimals":2},"INR":{"rate":90.66,"symbol":"\u20b9","name":"Indian Rupee","decimals":2},"ISK":{"rate":143.7,"symbol":"kr","name":"Icelandic Kr\u00f3na","decimals":2},"JPY":{"rate":159.15,"symbol":"\u00a5","name":"Japanese Yen","decimals":0},"KRW":{"rate":1460.32,"symbol":"\u20a9","name":"South Korean Won","decimals":2},"MXN":{"rate":18.5927,"symbol":"$","name":"Mexican Peso","decimals":2},"MYR":{"rate":5.0706,"symbol":"RM","name":"Malaysian Ringgit","decimals":2},"NOK":{"rate":11.5205,"symbol":"kr","name":"Norwegian Krone","decimals":2},"NZD":{"rate":1.8407,"symbol":"$","name":"New Zealand Dollar","decimals":2},"PHP":{"rate":61.543,"symbol":"\u20b1","name":"Philippine Peso","decimals":2},"PLN":{"rate":4.4785,"symbol":"z\u0142","name":"Polish Zloty","decimals":2},"RON":{"rate":4.9406,"symbol":"lei","name":"Romanian Leu","decimals":2},"SEK":{"rate":11.9095,"symbol":"kr","name":"Swedish Krona","decimals":2},"SGD":{"rate":1.4791,"symbol":"S$","name":"Singapore Dollar","decimals":2},"THB":{"rate":38.314,"symbol":"\u0e3f","name":"Thai Baht","decimals":2},"TRY":{"rate":29.63,"symbol":"\u20ba","name":"Turkish Lira","decimals":2},"USD":{"rate":1.0908,"symbol":"$","name":"United States Dollar","decimals":2},"ZAR":{"rate":20.676,"symbol":"R","name":"South African Rand","decimals":2},"EUR":{"rate":1,"symbol":"\u20ac","name":"Euro","decimals":2}}} \ No newline at end of file
diff --git a/db/currencies/currencies.php b/db/currencies/currencies.php
new file mode 100644
index 0000000..7c8b2ac
--- /dev/null
+++ b/db/currencies/currencies.php
@@ -0,0 +1,42 @@
+<?php
+
+$currencySymbols = [];
+$handle = fopen('https://raw.githubusercontent.com/bengourley/currency-symbol-map/master/map.js', 'r');
+while ($line = fgets($handle)) {
+ preg_match("/(\w+): '(.+)'/", $line, $match);
+
+ if (isset($match[1])) {
+ $currencySymbols[$match[1]] = $match[2];
+ }
+}
+
+$currencyNames = json_decode(file_get_contents('https://openexchangerates.org/api/currencies.json'), true);
+
+$decimals = [
+ 'JPY' => 0,
+];
+
+$currencies = [];
+$apiResponse = json_decode(file_get_contents('https://api.frankfurter.app/latest?base=eur'), true);
+$currencies['last_updated'] = $apiResponse['date'];
+foreach ($apiResponse['rates'] as $code => $rate) {
+ $currencies['map'][$code] = [
+ 'rate' => $rate,
+ 'symbol' => $currencySymbols[$code],
+ 'name' => $currencyNames[$code],
+ 'decimals' => $decimals[$code] ?? 2,
+ ];
+}
+
+// add EUR
+$currencies['map']['EUR'] = [
+ 'rate' => 1,
+ 'symbol' => $currencySymbols['EUR'],
+ 'name' => $currencyNames['EUR'],
+ 'decimals' => 2,
+];
+
+file_put_contents(
+ dirname(__DIR__) . '/_generated/currencies.json',
+ json_encode($currencies)
+);
diff --git a/index.html b/index.html
index 4f498c6..e575ec8 100644
--- a/index.html
+++ b/index.html
@@ -285,6 +285,14 @@
Language
<select data-template-slot="language"></select>
</label>
+
+ <br><br>
+
+ <label>
+ Currency
+ <select data-template-slot="currency"></select>
+ <div>Last Updated: <span data-template-slot="currency.lastUpdated"></span></div>
+ </label>
</div>
</template>
diff --git a/resources/js/classes/State.js b/resources/js/classes/State.js
index b7e9f5d..9a72a8f 100644
--- a/resources/js/classes/State.js
+++ b/resources/js/classes/State.js
@@ -5,6 +5,8 @@ class State {
*/
language: 'en_US',
+ currency: 'EUR',
+
colors: {
player: '#00ff00',
player: '#ff0000',
diff --git a/resources/js/classes/Trainer.js b/resources/js/classes/Trainer.js
index d63e52d..4f083dc 100644
--- a/resources/js/classes/Trainer.js
+++ b/resources/js/classes/Trainer.js
@@ -31,7 +31,8 @@ class Trainer {
*/
constructor (trainerData) {
this.#monsters = trainerData.monsters;
- this.#inventory = trainerData.inventory || [];
+ this.#inventory = trainerData.inventory || this.inventory;
+ this.type = trainerData.type || this.type;
this.name = trainerData.name;
this.sprite = trainerData.sprite;
}
diff --git a/resources/js/db.js b/resources/js/db.js
index 5850a01..ede65aa 100644
--- a/resources/js/db.js
+++ b/resources/js/db.js
@@ -68,9 +68,11 @@ const DB = {
*/
items: {},
+ areas: {},
+
translations: {},
- areas: {},
+ currencies : {},
};
async function initializeDB () {
@@ -85,6 +87,8 @@ async function initializeDB () {
}
await fetchTranslation(Memory.state.Settings.language);
+
+ DB.currencies = await fetch('/db/_generated/currencies.json').then((response) => response.json());
}
/**
diff --git a/resources/js/formula.js b/resources/js/formula.js
index 5a3433f..7223cb8 100644
--- a/resources/js/formula.js
+++ b/resources/js/formula.js
@@ -91,3 +91,17 @@ function calculateAwardedExperience (opposingMonster, participants) {
return awardedExperienceDistribution;
}
+
+/**
+ * @param {Monster} opposingMonster
+ *
+ * @returns {number[]}
+ */
+function calculateAwardedMoney (opposingMonster) {
+ let money = opposingMonster.level * opposingMonster.moneyModifier;
+
+ const baseDecimalDiff = 2 - DB.currencies.map[Memory.state.Settings.currency].decimals;
+ money = money * Math.pow(10, baseDecimalDiff);
+
+ return money;
+}
diff --git a/resources/js/game.js b/resources/js/game.js
index d5e8669..5a31c39 100644
--- a/resources/js/game.js
+++ b/resources/js/game.js
@@ -1,17 +1,21 @@
const Game = {
phases: {
- preAction: {
- opponent: [],
- player: [],
- },
- action: {
- opponent: [],
- player: [],
- },
- postAction: {
- opponent: [],
- player: [],
+ preTurn: [],
+ battle: {
+ preAction: {
+ opponent: [],
+ player: [],
+ },
+ action: {
+ opponent: [],
+ player: [],
+ },
+ postAction: {
+ opponent: [],
+ player: [],
+ },
},
+ postTurn: [],
},
logMessages: [],
@@ -37,26 +41,36 @@ const Game = {
Game.logTurn('begin');
// Phases
- for (const phaseKey of Object.keys(Game.phases)) {
- for (const event of Game.phases[phaseKey].player) {
+ for (const event of Game.phases.preTurn) {
+ event();
+ }
+ Game.phases.preTurn = [];
+
+ for (const phaseKey of Object.keys(Game.phases.battle)) {
+ for (const event of Game.phases.battle[phaseKey].player) {
event();
await Game.handleDefeatOpponent();
if (!Game.playerIsChoosingNextMonster) await Game.handleDefeatPlayer();
}
- Game.phases[phaseKey].player = [];
+ Game.phases.battle[phaseKey].player = [];
Game.doBattleAnimation = false;
- for (const event of Game.phases[phaseKey].opponent) {
+ for (const event of Game.phases.battle[phaseKey].opponent) {
event();
await Game.handleDefeatOpponent();
if (!Game.playerIsChoosingNextMonster) await Game.handleDefeatPlayer();
}
Game.doBattleAnimation = true;
- Game.phases[phaseKey].opponent = [];
+ Game.phases.battle[phaseKey].opponent = [];
}
+ for (const event of Game.phases.postTurn) {
+ event();
+ }
+ Game.phases.postTurn = [];
+
Game.logTurn('end');
UI.progressTurn();
@@ -67,13 +81,17 @@ const Game = {
if (Memory.state.opponent.activeMonster.hp <= 0) {
clearTimeout(Game.opponentActionTimeout);
Game.opponentActionTimeout = null;
- for (const phase of Object.keys(Game.phases)) {
- Game.removePhaseEvents(phase, 'opponent');
+ for (const phase of Object.keys(Game.phases.battle)) {
+ Game.removeBattlePhaseEvents(phase, 'opponent');
}
- Game.removePhaseEvents('action', 'player');
+ Game.removeBattlePhaseEvents('action', 'player');
// money
- Memory.state.money += Memory.state.opponent.activeMonster.level * Memory.state.opponent.activeMonster.moneyModifier;
+ const money = calculateAwardedMoney(Memory.state.opponent.activeMonster);
+ Memory.state.money += money;
+ Game.addPhaseEvent('postTurn', () => {
+ Game.log(`Got ${money} money!`);
+ });
// exp
Memory.state.player.activeMonster.exp += calculateAwardedExperience(Memory.state.opponent.activeMonster, [Memory.state.player.activeMonster])[0];
@@ -109,10 +127,10 @@ const Game = {
if (Memory.state.player.activeMonster.hp <= 0) {
clearTimeout(Game.opponentActionTimeout);
Game.opponentActionTimeout = null;
- for (const phase of Object.keys(Game.phases)) {
- Game.removePhaseEvents(phase, 'player');
+ for (const phase of Object.keys(Game.phases.battle)) {
+ Game.removeBattlePhaseEvents(phase, 'player');
}
- Game.removePhaseEvents('action', 'opponent');
+ Game.removeBattlePhaseEvents('action', 'opponent');
// whole party defeated
if (!Memory.state.player.monsters.some((monster) => monster.hp > 0)) {
@@ -149,15 +167,15 @@ const Game = {
},
/**
- * @param {Object} phase
+ * @param {('preAction' | 'action' | 'postAction')} phase
* @param {Monster} monster
* @param {Function} event
*/
- addPhaseEvent (phase, monster, event) {
+ addBattlePhaseEvent (phase, monster, event) {
if (monster === Memory.state.player.activeMonster) {
- phase.player.push(event);
+ Game.phases.battle[phase].player.push(event);
} else {
- phase.opponent.push(event);
+ Game.phases.battle[phase].opponent.push(event);
}
},
@@ -165,17 +183,25 @@ const Game = {
* @param {('preAction' | 'action' | 'postAction')} phase
* @param {('player' | 'opponent')} type
*/
- removePhaseEvents (phase, type) {
- Game.phases[phase][type] = [];
+ removeBattlePhaseEvents (phase, type) {
+ Game.phases.battle[phase][type] = [];
+ },
+
+ /**
+ * @param {('preTurn' | 'postTurn')} phase
+ * @param {Function} event
+ */
+ addPhaseEvent (phase, event) {
+ Game.phases[phase].push(event);
},
clearAllPhaseEvents () {
- Game.removePhaseEvents('preAction', 'player');
- Game.removePhaseEvents('preAction', 'opponent');
- Game.removePhaseEvents('action', 'player');
- Game.removePhaseEvents('action', 'opponent');
- Game.removePhaseEvents('postAction', 'player');
- Game.removePhaseEvents('postAction', 'opponent');
+ Game.removeBattlePhaseEvents('preAction', 'player');
+ Game.removeBattlePhaseEvents('preAction', 'opponent');
+ Game.removeBattlePhaseEvents('action', 'player');
+ Game.removeBattlePhaseEvents('action', 'opponent');
+ Game.removeBattlePhaseEvents('postAction', 'player');
+ Game.removeBattlePhaseEvents('postAction', 'opponent');
},
clearCurrentTurn () {
@@ -193,7 +219,7 @@ const Game = {
let canUse = true;
const log = (message, indentation) => {
- Game.addPhaseEvent(Game.phases.preAction, user, () => {
+ Game.addBattlePhaseEvent('preAction', user, () => {
Game.log(message, indentation);
});
};
@@ -251,7 +277,7 @@ const Game = {
async useTechnique (technique, user, target) {
technique.use();
- Game.addPhaseEvent(Game.phases.action, user, () => {
+ Game.addBattlePhaseEvent('action', user, () => {
Game.log(`${user.name} is using ${technique.name}!`);
});
@@ -262,7 +288,7 @@ const Game = {
// damage
if (['damage', 'splash', 'area'].includes(techniqueEffect.type)) {
- Game.addPhaseEvent(Game.phases.action, user, () => {
+ Game.addBattlePhaseEvent('action', user, () => {
const damage = simpleDamageCalculation(technique, user, target);
target.hp -= damage;
@@ -281,7 +307,7 @@ const Game = {
// money
else if (techniqueEffect.type === 'money') {
- Game.addPhaseEvent(Game.phases.action, user, () => {
+ Game.addBattlePhaseEvent('action', user, () => {
const money = Math.max(1, Math.floor(Math.random() * target.level));
Memory.state.money += money;
@@ -297,7 +323,7 @@ const Game = {
// healing
else if (techniqueEffect.type === 'healing') {
for (const recipient of techniqueEffect.recipients) {
- Game.addPhaseEvent(Game.phases.action, user, () => {
+ Game.addBattlePhaseEvent('action', user, () => {
const heal = (user.level + 7) * technique.healingPower;
recipient.hp += heal;
@@ -324,7 +350,7 @@ const Game = {
statusEffect.issuer = user;
}
- Game.addPhaseEvent(Game.phases.action, user, () => {
+ Game.addBattlePhaseEvent('action', user, () => {
// add status effect
const potency = Math.random();
const success = technique.potency >= potency;
@@ -355,7 +381,7 @@ const Game = {
}
if (monster.statusEffect.turnsLeft === 0) {
- Game.addPhaseEvent(Game.phases.preAction, monster, () => {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
monster.statusEffect.onRemove && monster.statusEffect.onRemove();
// if still 0 turns left after remove action
@@ -379,7 +405,7 @@ const Game = {
if (monster.statusEffect.slug === 'poison' || monster.statusEffect.slug === 'burn') {
const statusEffectDamage = Math.floor(monster.stats.hp / 8);
- Game.addPhaseEvent(Game.phases.postAction, monster, () => {
+ Game.addBattlePhaseEvent('postAction', monster, () => {
monster.hp -= statusEffectDamage;
if (Game.doBattleAnimation) {
@@ -396,7 +422,7 @@ const Game = {
else if (monster.statusEffect.slug === 'lifeleech') {
const statusEffectLeech = Math.floor(monster.stats.hp / 16);
- Game.addPhaseEvent(Game.phases.postAction, monster, () => {
+ Game.addBattlePhaseEvent('postAction', monster, () => {
// if issuer is defeated => don't
if (monster.statusEffect.issuer.hp <= 0) {
return;
@@ -419,7 +445,7 @@ const Game = {
else if (monster.statusEffect.slug === 'recover') {
const statusEffectHeal = Math.floor(monster.stats.hp / 16);
- Game.addPhaseEvent(Game.phases.postAction, monster, () => {
+ Game.addBattlePhaseEvent('postAction', monster, () => {
monster.hp += statusEffectHeal;
if (Game.doBattleAnimation) {
@@ -436,7 +462,7 @@ const Game = {
else if (monster.statusEffect.slug === 'stuck') {
for (const technique of monster.activeTechniques) {
if ([TechniqueRange.melee, TechniqueRange.touch].includes(technique.range)) {
- Game.addPhaseEvent(Game.phases.preAction, monster, () => {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
technique.potency = technique.stats.potency * 0.5;
technique.power = technique.stats.power * 0.5;
@@ -454,7 +480,7 @@ const Game = {
else if (monster.statusEffect.slug === 'grabbed') {
for (const technique of monster.activeTechniques) {
if ([TechniqueRange.ranged, TechniqueRange.reach].includes(technique.range)) {
- Game.addPhaseEvent(Game.phases.preAction, monster, () => {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
technique.potency = technique.stats.potency * 0.5;
technique.power = technique.stats.power * 0.5;
@@ -472,7 +498,7 @@ const Game = {
else if (monster.statusEffect.slug === 'charging') {
const nextStatusEffect = await fetchStatusEffect('chargedup');
- Game.addPhaseEvent(Game.phases.preAction, monster, () => {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
logStatusIs();
});
@@ -489,7 +515,7 @@ const Game = {
const statChange = monster.statusEffect.stats[statType];
const modifiedValue = Math.floor(eval(`${monster.stats[statType]} ${statChange.operation} ${statChange.value}`));
- Game.addPhaseEvent(Game.phases.preAction, monster, () => {
+ Game.addBattlePhaseEvent('preAction', monster, () => {
monster.setStatModifier(statType, modifiedValue);
logStatusIs();
@@ -501,7 +527,7 @@ const Game = {
};
}
- Game.addPhaseEvent(Game.phases.postAction, monster, () => {
+ Game.addBattlePhaseEvent('postAction', monster, () => {
monster.statusEffect.turnsLeft--;
});
},
@@ -682,6 +708,9 @@ const Game = {
async jumpToArea (area) {
Game.clearCurrentTurn();
+ if (Memory.state.currentArea) {
+ Memory.state.areaProgress[Memory.state.currentArea.slug] = Memory.state.currentArea;
+ }
Memory.state.currentArea = area;
UI.drawArea(area);
@@ -741,6 +770,7 @@ const Game = {
if (itemEffect.type === 'heal') {
monster.hp += itemEffect.amount;
item.quantity--;
+ UI.drawActiveMonster();
}
else if (itemEffect.type === 'capture') {
diff --git a/resources/js/memory.js b/resources/js/memory.js
index 550ea55..47cde88 100644
--- a/resources/js/memory.js
+++ b/resources/js/memory.js
@@ -53,6 +53,19 @@ const Memory = {
*/
async load (saveData) {
/**
+ * @param {Area} areaData
+ */
+ const loadArea = async (areaData) => {
+ const area = await fetchArea(areaData.slug);
+
+ area.monsterProgress = areaData.monsterProgress;
+ area.trainerProgress = areaData.trainerProgress;
+ area.isCompleted = areaData.isCompleted;
+
+ return area;
+ };
+
+ /**
* @param {Monster} monsterData
*/
const loadMonster = async (monsterData) => {
@@ -116,6 +129,15 @@ const Memory = {
};
/**
+ * @param {Trainer} trainerData
+ */
+ const loadTrainer = async (trainerData) => {
+ const trainer = new Trainer(trainerData);
+
+ return trainer;
+ };
+
+ /**
* @param {Technique} techniqueData
*/
const loadTechnique = async (techniqueData) => {
@@ -131,20 +153,34 @@ const Memory = {
*/
const loadedState = JSON.parse(atob(saveData));
- Memory.state.Settings.language = loadedState.language;
+ Memory.state.Settings.language = loadedState.Settings.language;
await fetchTranslation(Memory.state.Settings.language);
+ Memory.state.Settings.currency = loadedState.Settings.currency;
+ Memory.state.Settings.logMaxLength = loadedState.Settings.logMaxLength;
+
+ for (const areaSlug of Object.keys(loadedState.areaProgress)) {
+ const areaData = loadedState.areaProgress[areaSlug];
+ Memory.state.areaProgress[areaSlug] = await loadArea(areaData);
+ }
+ Memory.state.currentArea = await loadArea(loadedState.currentArea);
Memory.state.turn = loadedState.turn;
Memory.state.money = loadedState.money;
Memory.state.monsters = await Promise.all(loadedState.monsters.map(async (monsterData) => await loadMonster(monsterData)));
+ Memory.state.player = await loadTrainer(loadedState.player);
Memory.state.player.monsters = await Promise.all(loadedState.player.monsters.map(async (monsterData) => await loadMonster(monsterData)));
Memory.state.player.inventory = await Promise.all(loadedState.player.inventory.map(async (itemData) => await loadInventoryItem(itemData)));
Memory.state.player.activeMonster = Memory.state.player.monsters[loadedState.player.activeMonsterIdx];
+ Memory.state.opponent = await loadTrainer(loadedState.opponent);
Memory.state.opponent.monsters = await Promise.all(loadedState.opponent.monsters.map(async (monsterData) => await loadMonster(monsterData)));
Memory.state.opponent.inventory = await Promise.all(loadedState.opponent.inventory.map(async (itemData) => await loadInventoryItem(itemData)));
Memory.state.opponent.activeMonster = Memory.state.opponent.monsters[loadedState.opponent.activeMonsterIdx];
+ Memory.state.rivalMonster = loadedState.rivalMonster;
Memory.state.activeTechnique = await loadTechnique(loadedState.activeTechnique);
+ Memory.state.activeBall = await loadInventoryItem(loadedState.activeBall);
+ UI.drawArea(Memory.state.currentArea);
+ UI.drawStatus();
UI.drawOpponentMonster();
UI.drawActiveMonster();
UI.drawActiveTechniques();
diff --git a/resources/js/ui.js b/resources/js/ui.js
index bfd9204..92ca313 100644
--- a/resources/js/ui.js
+++ b/resources/js/ui.js
@@ -684,7 +684,10 @@ const UI = {
drawStatus () {
const currentArea = Memory.state.currentArea;
- UI.elements.status.querySelector('[data-template-slot="money"]').textContent = `${Memory.state.money} €`;
+ UI.elements.status.querySelector('[data-template-slot="money"]').textContent =
+ `${Memory.state.money.toFixed(DB.currencies.map[Memory.state.Settings.currency].decimals)}` +
+ ' ' +
+ `${DB.currencies.map[Memory.state.Settings.currency].symbol}`;
UI.elements.status.querySelector('[data-template-slot="monsterProgress"]').textContent = `${currentArea.monsterProgress} / ${currentArea.requiredEncounters}`;
UI.elements.status.querySelector('[data-template-slot="trainerProgress"]').textContent = `${currentArea.trainerProgress} / ${currentArea.trainers.length}`;
@@ -943,6 +946,50 @@ const UI = {
});
+ /* 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', 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.drawStatus();
+ });
+
+ template.querySelector('[data-template-slot="currency.lastUpdated"]').textContent = DB.currencies.last_updated;
+
+
popup.querySelector('.popup').appendChild(template);
UI.drawPopup(popup);
},