diff options
Diffstat (limited to 'resources')
| -rw-r--r-- | resources/js/classes/State.js | 2 | ||||
| -rw-r--r-- | resources/js/classes/Trainer.js | 3 | ||||
| -rw-r--r-- | resources/js/db.js | 6 | ||||
| -rw-r--r-- | resources/js/formula.js | 14 | ||||
| -rw-r--r-- | resources/js/game.js | 130 | ||||
| -rw-r--r-- | resources/js/memory.js | 38 | ||||
| -rw-r--r-- | resources/js/ui.js | 49 | 
7 files changed, 188 insertions, 54 deletions
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);    },  | 
