summaryrefslogtreecommitdiff
path: root/resources/js
diff options
context:
space:
mode:
Diffstat (limited to 'resources/js')
-rw-r--r--resources/js/classes/Area.js37
-rw-r--r--resources/js/classes/State.js15
-rw-r--r--resources/js/classes/Trainer.js33
-rw-r--r--resources/js/db.js18
-rw-r--r--resources/js/game.js135
-rw-r--r--resources/js/main.js54
-rw-r--r--resources/js/ui.js50
7 files changed, 264 insertions, 78 deletions
diff --git a/resources/js/classes/Area.js b/resources/js/classes/Area.js
new file mode 100644
index 0000000..3451757
--- /dev/null
+++ b/resources/js/classes/Area.js
@@ -0,0 +1,37 @@
+class Area {
+ slug = '';
+
+ monsterProgress = 0;
+ trainerProgress = 0;
+ isCompleted = false;
+
+ constructor (slug) {
+ this.slug = slug;
+ }
+
+ async initialize () {}
+
+ get encounters () {
+ return DB.areas[this.slug].encounters;
+ }
+
+ get requiredEncounters () {
+ return DB.areas[this.slug].requiredEncounters;
+ }
+
+ get trainers () {
+ return DB.areas[this.slug].trainers;
+ }
+
+ get environment () {
+ return DB.areas[this.slug].environment;
+ }
+
+ get previousArea () {
+ return DB.areas[this.slug].previousArea;
+ }
+
+ get nextArea () {
+ return DB.areas[this.slug].nextArea;
+ }
+}
diff --git a/resources/js/classes/State.js b/resources/js/classes/State.js
index 4b209e2..5d3cfff 100644
--- a/resources/js/classes/State.js
+++ b/resources/js/classes/State.js
@@ -5,6 +5,16 @@ class State {
language = 'en_US';
/**
+ * @type {Object.<string, Area>}
+ */
+ areaProgress = {};
+
+ /**
+ * @type {Area}
+ */
+ currentArea = null;
+
+ /**
* @type {number}
*/
turn = 0;
@@ -30,6 +40,11 @@ class State {
opponent = null;
/**
+ * @type {Monster}
+ */
+ rivalMonster = null;
+
+ /**
* @type {Technique}
*/
activeTechnique = null;
diff --git a/resources/js/classes/Trainer.js b/resources/js/classes/Trainer.js
index 668c92b..f2c2df9 100644
--- a/resources/js/classes/Trainer.js
+++ b/resources/js/classes/Trainer.js
@@ -1,4 +1,12 @@
class Trainer {
+ #monsters = [];
+ #inventory = [];
+
+ /**
+ * @type {('trainer' | 'monster')}
+ */
+ type = 'trainer';
+
/**
* @type {Monster[]}
*/
@@ -13,4 +21,29 @@ class Trainer {
* @type {Monster}
*/
activeMonster = [];
+
+ constructor (monsters, inventory = []) {
+ this.#monsters = monsters;
+ this.#inventory = inventory;
+ }
+
+ async initialize () {
+ for (const monsterData of this.#monsters) {
+ const monster = await fetchMonster(monsterData.slug);
+
+ monster.level = monsterData.level || monster.level;
+
+ this.monsters.push(monster);
+ }
+
+ this.activeMonster = this.monsters[0];
+
+ for (const itemData of this.#inventory) {
+ const item = new InventoryItem(await fetchItem(itemData.slug));
+
+ item.amount = itemData.amount || 1;
+
+ this.inventory.push(item);
+ }
+ }
}
diff --git a/resources/js/db.js b/resources/js/db.js
index e084818..aaf5423 100644
--- a/resources/js/db.js
+++ b/resources/js/db.js
@@ -69,6 +69,8 @@ const DB = {
items: {},
translations: {},
+
+ areas: {},
};
async function initializeDB () {
@@ -152,3 +154,19 @@ async function fetchTranslation (locale) {
return DB.translations[locale];
}
+
+/**
+ * @param {string} slug
+ *
+ * @returns {Promise<Area>}
+ */
+async function fetchArea (slug) {
+ if (! DB.areas[slug]) {
+ DB.areas[slug] = await fetch(`/db/_generated/areas/${slug}.json`).then((response) => response.json());
+ }
+
+ const area = new Area(slug);
+
+
+ return area;
+}
diff --git a/resources/js/game.js b/resources/js/game.js
index 1a95ce4..1a91f06 100644
--- a/resources/js/game.js
+++ b/resources/js/game.js
@@ -5,6 +5,7 @@ const Game = {
postAction: [],
},
+ isInBattle: false,
didTechniqueHit: false,
async progressTurn () {
@@ -41,14 +42,21 @@ const Game = {
await Game.evolveMonster(Memory.state.player.activeMonster);
}
- await Game.spawnOpponentMonster();
- }
+ Game.isInBattle = false;
- UI.drawOpponentMonster();
- UI.drawActiveMonster();
- UI.drawActiveTechniques();
+ if (Memory.state.opponent.type === 'monster') {
+ Memory.state.currentArea.monsterProgress++;
+ await Game.encounterWildMonster();
+ } else if (Memory.state.opponent.type === 'trainer') {
+ if (Memory.state.opponent.activeMonster === Memory.state.opponent.monsters[Memory.state.opponent.monsters.length - 1]) {
+ await Game.progressToArea(await fetchArea(Memory.state.currentArea.nextArea));
+ } else {
+ await Game.encounterNextTrainerMonster();
+ }
+ }
+ }
- UI.elements.money.textContent = `${Memory.state.money} €`;
+ UI.progressTurn();
},
/**
@@ -301,27 +309,10 @@ const Game = {
},
/**
- * @param {Monster} monster
- */
- async evolveMonster (monster) {
- await fetchMonster(monster.evolutions[0].monster_slug);
- monster.evolve(monster.evolutions[0]);
- },
-
- async spawnOpponentMonster () {
- Memory.state.opponent.activeMonster = await fetchMonster(DB.allMonsters[Math.floor(Math.random() * DB.allMonsters.length)]);
- Memory.state.opponent.activeMonster.level = Math.ceil(Math.random() * Memory.state.player.activeMonster.level);
-
- // Memory.state.opponent.activeMonster.experienceModifier = Memory.state.opponent.activeMonster.level;
- Memory.state.opponent.activeMonster.moneyModifier = Memory.state.opponent.activeMonster.level;
-
- UI.drawOpponentMonster();
- },
-
- /**
* @param {MouseEvent} event
*/
async battleClick (event) {
+ Game.isInBattle = true;
UI.battleClickEvent = event;
await Game.tryUseTechnique(Memory.state.activeTechnique, Memory.state.player.activeMonster, Memory.state.opponent.activeMonster);
@@ -357,18 +348,110 @@ const Game = {
}));
},
- catchMonster () {
+ /**
+ * @param {InventoryItem}
+ */
+ async useItem (item) {
+ for (const itemEffectCode of item.effects) {
+ const itemEffect = new ItemEffect(itemEffectCode);
+
+ if (itemEffect.type === 'evolve') {
+ const evolution = Memory.state.player.activeMonster.getPossibleEvolutions('item')[0];
+ if (evolution) {
+ await fetchMonster(evolution.monster_slug);
+ Memory.state.player.activeMonster.evolve(evolution);
+ UI.drawActiveMonster();
+ }
+ }
+ }
+ },
+
+ /**
+ * @param {Monster} monster
+ */
+ async evolveMonster (monster) {
+ await fetchMonster(monster.evolutions[0].monster_slug);
+ monster.evolve(monster.evolutions[0]);
+ },
+
+ async catchMonster () {
+ if (Memory.state.opponent.type === 'trainer') {
+ return;
+ }
+
const caughtMonster = new Monster(Memory.state.opponent.activeMonster.slug);
caughtMonster.initialize();
caughtMonster.level = Memory.state.opponent.activeMonster.level;
Memory.state.player.monsters.push(caughtMonster);
- Game.spawnOpponentMonster();
+ await Game.encounterWildMonster();
+ await Game.progressTurn();
+ },
+
+ getStageOfDaySimple () {
+ const hours = (new Date()).getHours();
+ if (hours >= 6 && hours < 18) {
+ return 'day';
+ } else {
+ return 'night';
+ }
+ },
+
+ /**
+ * @param {Area} area
+ */
+ async progressToArea (area) {
+ Memory.state.currentArea = area;
+
+ UI.drawArea(area);
+ UI.drawStatus();
+
+ if (area.encounters.length > 0) {
+ await Game.encounterWildMonster();
+ } else {
+ await Game.encounterTrainer();
+ }
+ },
+
+ async encounterWildMonster () {
+ const randomMonster = Memory.state.currentArea.encounters[Math.floor(Math.random() * Memory.state.currentArea.encounters.length)];
+ const randomLevel = Math.floor(Math.random() * (randomMonster.level_range[1] - randomMonster.level_range[0]) + randomMonster.level_range[0]);
+
+ const monster = await fetchMonster(randomMonster.monster);
+ monster.level = randomLevel;
+
+ const wildMonster = new Trainer([monster]);
+ wildMonster.type = 'monster';
+ await wildMonster.initialize();
+ Memory.state.opponent = wildMonster;
+
+ UI.drawOpponentMonster();
+ },
+
+ async encounterTrainer () {
+ const nextTrainer = Memory.state.currentArea.trainers[Memory.state.currentArea.trainerProgress];
+
+ const trainer = new Trainer(nextTrainer.monsters, nextTrainer.inventory);
+ if (nextTrainer.name === 'Rival') {
+ trainer.monsters.push(Memory.state.rivalMonster);
+ }
+ await trainer.initialize()
+ Memory.state.opponent = trainer;
+
+ UI.drawOpponentMonster();
+ },
+
+ async encounterNextTrainerMonster () {
+ const activeMonsterIdx = Memory.state.opponent.monsters.indexOf(Memory.state.opponent.activeMonster);
+ Memory.state.opponent.activeMonster = Memory.state.opponent.monsters[activeMonsterIdx + 1];
+
+ UI.drawOpponentMonster();
},
};
// Game click bindings
+UI.elements.status.querySelector('[data-template-slot="next-trainer"]').addEventListener('click', Game.encounterTrainer);
UI.elements.battleOpponent.addEventListener('click', Game.battleClick);
UI.elements.techniques.addEventListener('click', Game.techniqueClick);
UI.elements.menuCatch.addEventListener('click', Game.catchMonster);
diff --git a/resources/js/main.js b/resources/js/main.js
index 12d1475..815d734 100644
--- a/resources/js/main.js
+++ b/resources/js/main.js
@@ -1,51 +1,27 @@
(async function () {
await initializeDB();
+ // Start Game
const possibleStarterMonsters = ['budaye', 'dollfin', 'grintot', 'ignibus', 'memnomnom'];
- Memory.state.opponent = new Trainer();
- Memory.state.opponent.monsters = [
- await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))])
- ];
+ Memory.state.player = new Trainer(
+ [
+ await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]),
+ ],
+ [
+ new InventoryItem(await fetchItem('tuxeball')),
+ new InventoryItem(await fetchItem('potion')),
+ ]
+ );
+ await Memory.state.player.initialize();
- Memory.state.player = new Trainer();
- Memory.state.player.monsters = [
- await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]),
- await fetchMonster('corvix'),
- await fetchMonster('lunight'),
- await fetchMonster('prophetoise'),
- await fetchMonster('drashimi'),
- await fetchMonster('glombroc'),
- await fetchMonster('uneye'),
- await fetchMonster('nostray'),
- await fetchMonster('dragarbor'),
- await fetchMonster('mk01_omega'),
- await fetchMonster('jelillow'),
- await fetchMonster('picc'),
- ];
+ Memory.state.activeTechnique = Memory.state.player.activeMonster.activeTechniques[0];
- Memory.state.player.inventory = [
- new InventoryItem(await fetchItem('tuxeball')),
- new InventoryItem(await fetchItem('ancient_egg')),
- new InventoryItem(await fetchItem('sweet_sand')),
- new InventoryItem(await fetchItem('tectonic_drill')),
- new InventoryItem(await fetchItem('surfboard')),
- new InventoryItem(await fetchItem('sledgehammer')),
- new InventoryItem(await fetchItem('raise_melee')),
- new InventoryItem(await fetchItem('raise_speed')),
- new InventoryItem(await fetchItem('mm_fire')),
- new InventoryItem(await fetchItem('mm_water')),
- new InventoryItem(await fetchItem('cureall')),
- new InventoryItem(await fetchItem('potion')),
- new InventoryItem(await fetchItem('super_potion')),
- new InventoryItem(await fetchItem('revive')),
- ];
+ Memory.state.rivalMonster = await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]);
- Memory.state.opponent.activeMonster = Memory.state.opponent.monsters[0];
- Memory.state.player.activeMonster = Memory.state.player.monsters[0];
- Memory.state.activeTechnique = Memory.state.player.activeMonster.activeTechniques[0];
+ const area = await fetchArea('paper-town');
+ await Game.progressToArea(area);
- UI.drawOpponentMonster();
UI.drawActiveMonster();
UI.drawActiveTechniques();
})();
diff --git a/resources/js/ui.js b/resources/js/ui.js
index a623f9d..0d6a8f9 100644
--- a/resources/js/ui.js
+++ b/resources/js/ui.js
@@ -34,6 +34,7 @@ const Template = {
const UI = {
elements: {
+ battle: document.querySelector('#battle'),
battleOpponent: document.querySelector('#battle__opponent'),
battleOpponentSprite: null,
battleOpponentAnimation: document.querySelector('.battle__monster-sprite__animation'),
@@ -41,7 +42,7 @@ const UI = {
techniques: document.querySelector('#techniques'),
- money: document.querySelector('#money'),
+ status: document.querySelector('#status'),
menuParty: document.querySelector('#menu__party'),
menuInventory: document.querySelector('#menu__inventory'),
@@ -354,6 +355,13 @@ const UI = {
UI.elements.battleOpponentSprite = battleMonsterNode.querySelector('[data-template-slot="sprite"]');
UI.elements.battleOpponentSprite.style.transitionDuration = `${UI.damageHighlightClickDuration}s`;
+ // en/disable catch
+ if (Memory.state.opponent.type === 'trainer') {
+ UI.elements.menuCatch.setAttribute('disabled', true);
+ } else {
+ UI.elements.menuCatch.removeAttribute('disabled');
+ }
+
const previousBattleMonsterNode = UI.elements.battleOpponent.querySelector('.battle__monster');
if (previousBattleMonsterNode) {
UI.elements.battleOpponentSprite.classList = previousBattleMonsterNode.querySelector('[data-template-slot="sprite"]').classList;
@@ -427,6 +435,20 @@ const UI = {
requestAnimationFrame(techniqueAnimationLoop);
},
+ /**
+ * @param {Area} area
+ */
+ drawArea (area) {
+ UI.elements.battle.style.backgroundImage = `url(/modules/tuxemon/mods/tuxemon/gfx/ui/combat/${area.environment.battle_graphics.background})`;
+ },
+
+ progressTurn () {
+ UI.drawOpponentMonster();
+ UI.drawActiveMonster();
+ UI.drawActiveTechniques();
+ UI.drawStatus();
+ },
+
/* Battle - Action Feedback */
@@ -544,6 +566,19 @@ const UI = {
partySelectionMode: 'select',
+ drawStatus () {
+ UI.elements.status.querySelector('[data-template-slot="money"]').textContent = `${Memory.state.money} €`;
+ UI.elements.status.querySelector('[data-template-slot="monster-progress"]').textContent = `${Memory.state.currentArea.monsterProgress} / ${'10'}`;
+ UI.elements.status.querySelector('[data-template-slot="trainer-progress"]').textContent = `${Memory.state.currentArea.trainerProgress} / ${Memory.state.currentArea.trainers.length}`;
+
+ const nextTrainerButton = UI.elements.status.querySelector('[data-template-slot="next-trainer"]');
+ if (Memory.state.currentArea.monsterProgress >= Memory.state.currentArea.requiredEncounters) {
+ nextTrainerButton.disabled = false;
+ } else {
+ nextTrainerButton.disabled = true;
+ }
+ },
+
openPartyMenu () {
const popup = UI.createPopup();
@@ -654,18 +689,7 @@ const UI = {
inventoryItemNode.querySelector('[data-template-slot="quantity"]').textContent = item.quantity;
inventoryItemNode.addEventListener('click', async () => {
- for (const itemEffectCode of item.effects) {
- const itemEffect = new ItemEffect(itemEffectCode);
-
- if (itemEffect.type === 'evolve') {
- const evolution = Memory.state.player.activeMonster.getPossibleEvolutions('item')[0];
- if (evolution) {
- await fetchMonster(evolution.monster_slug);
- Memory.state.player.activeMonster.evolve(evolution);
- UI.drawActiveMonster();
- }
- }
- }
+ Game.useItem(item);
});
if (['potion', 'revive'].includes(item.category)) {