diff options
Diffstat (limited to 'resources')
-rw-r--r-- | resources/css/menu.css | 8 | ||||
-rw-r--r-- | resources/js/game.js | 230 | ||||
-rw-r--r-- | resources/js/main.js | 2 | ||||
-rw-r--r-- | resources/js/ui.js | 140 |
4 files changed, 256 insertions, 124 deletions
diff --git a/resources/css/menu.css b/resources/css/menu.css index 1c5fb76..eee0b5f 100644 --- a/resources/css/menu.css +++ b/resources/css/menu.css @@ -85,6 +85,14 @@ +.monster-selection { + display: grid; + grid-template-columns: 1fr 1fr 1fr; +} + + + + #status { color: #fff; background-color: #000; diff --git a/resources/js/game.js b/resources/js/game.js index d31d6e5..fad882b 100644 --- a/resources/js/game.js +++ b/resources/js/game.js @@ -22,6 +22,8 @@ const Game = { isInBattle: false, didTechniqueHit: false, + /* Battle */ + async progressTurn () { Game.isProgressingTurn = true; Memory.state.turn++; @@ -106,8 +108,37 @@ const Game = { } Game.removePhaseEvents('action', 'opponent'); - Game.playerIsChoosingNextMonster = true; - UI.openPartyMenu(); + // whole party defeated + if (!Memory.state.player.monsters.some((monster) => monster.hp > 0)) { + if (Game.isBattleType('trainer')) { + if (Memory.state.currentArea.encounters.length > 0) { + await Game.encounterWildMonster(); + } else { + await Game.encounterTrainer(); + } + } + + else if (Game.isBattleType('monster')) { + if (Memory.state.currentArea.monsterProgress < Memory.state.currentArea.requiredEncounters) { + Memory.state.currentArea.monsterProgress = 0; + UI.drawStatus(); + } + + await Game.encounterWildMonster(); + } + + // heal all monsters full + for (const monster of Memory.state.player.monsters) { + monster.hp = monster.stats.hp; + } + } + + // party members still left + else { + Game.playerIsChoosingNextMonster = true; + const monsterSelectionNode = UI.createPlayerDefeatedMonsterSelection(); + UI.openPlayerDefeatedMonsterSelection(monsterSelectionNode); + } } }, @@ -325,6 +356,11 @@ const Game = { const statusEffectLeech = Math.floor(monster.stats.hp / 16); Game.addPhaseEvent(Game.phases.postAction, monster, () => { + // if issuer is defeated => don't + if (monster.statusEffect.issuer.hp <= 0) { + return; + } + monster.hp -= statusEffectLeech; monster.statusEffect.issuer.hp += statusEffectLeech; @@ -442,6 +478,7 @@ const Game = { return; } + // technique Game.doBattleAnimation = false; await Game.tryUseTechnique( await fetchTechnique(Memory.state.opponent.activeMonster.getLearnableTechniques()[Math.floor(Math.random() * Memory.state.opponent.activeMonster.getLearnableTechniques().length)].technique), @@ -452,6 +489,10 @@ const Game = { await Game.progressTurn(); + // item + if (Memory.state.opponent.inventory.length > 0) { + } + Game.opponentActionTimeout = null; }, Math.max(500, 2000 - (speedDifference * 10))); console.log( @@ -473,7 +514,7 @@ const Game = { } let target = event.target; - while (!target.classList.contains('techniques__technique')) { + while (target.dataset.gameElementType !== 'menuBattleTechniquesTechnique') { target = target.parentNode; } @@ -492,6 +533,81 @@ const Game = { })); }, + + /* Progression */ + + 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({ monsters: [monster] }); + wildMonster.type = 'monster'; + await wildMonster.initialize(); + Memory.state.opponent = wildMonster; + + UI.drawOpponentMonster(); + }, + + async encounterTrainer () { + Game.clearCurrentTurn(); + + const nextTrainer = Memory.state.currentArea.trainers[Memory.state.currentArea.trainerProgress]; + + const trainer = new Trainer(nextTrainer); + if (nextTrainer.name === 'Rival') { + trainer.monsters.push(Memory.state.rivalMonster); + } + await trainer.initialize() + Memory.state.opponent = trainer; + + UI.drawOpponentMonster(); + UI.drawStatus(); + }, + + 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(); + }, + + async progressToNextArea () { + Game.isLoadingArea = true; + Game.clearCurrentTurn(); + + const currentArea = Memory.state.currentArea; + const nextArea = await fetchArea(currentArea.nextArea); + + await Game.jumpToArea(nextArea); + + if (nextArea.encounters.length > 0) { + await Game.encounterWildMonster(); + } else { + await Game.encounterTrainer(); + } + + UI.drawStatus(); + + Game.isLoadingArea = false; + }, + + /** + * @param {Area} area + */ + async jumpToArea (area) { + Game.clearCurrentTurn(); + + Memory.state.currentArea = area; + + UI.drawArea(area); + }, + + + /* Menu - Inventory */ + /** * @param {InventoryItem} item * @param {Monster} monster @@ -571,22 +687,8 @@ const Game = { inventory.splice(inventory.indexOf(item), 1); }, - /** - * @param {string} type - * - * @returns {boolean} - */ - isBattleType (type) { - return Memory.state.opponent.type === type; - }, - /** - * @param {Monster} monster - */ - async evolveMonster (monster) { - await fetchMonster(monster.evolutions[0].monster_slug); - monster.evolve(monster.evolutions[0]); - }, + /* Menu - Catch */ /** * @returns {boolean} @@ -620,82 +722,44 @@ const Game = { await Game.progressTurn(); }, - getStageOfDaySimple () { - const hours = (new Date()).getHours(); - if (hours >= 6 && hours < 18) { - return 'day'; - } else { - return 'night'; - } - }, + + /* Helper */ /** - * @param {Area} area + * @param {Monster} monster */ - async jumpToArea (area) { - Game.clearCurrentTurn(); + setActivePlayerMonster (monster) { + Memory.state.player.activeMonster = monster; + Memory.state.activeTechnique = Memory.state.player.activeMonster.activeTechniques[0]; - Memory.state.currentArea = area; - - UI.drawArea(area); + UI.drawActiveMonster(); + UI.drawActiveTechniques(); }, - async progressToNextArea () { - Game.isLoadingArea = true; - Game.clearCurrentTurn(); - - const currentArea = Memory.state.currentArea; - const nextArea = await fetchArea(currentArea.nextArea); - - await Game.jumpToArea(nextArea); - - if (nextArea.encounters.length > 0) { - await Game.encounterWildMonster(); - } else { - await Game.encounterTrainer(); - } - - UI.drawStatus(); - - Game.isLoadingArea = false; + /** + * @param {Monster} monster + */ + async evolveMonster (monster) { + await fetchMonster(monster.evolutions[0].monster_slug); + monster.evolve(monster.evolutions[0]); }, - 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({ monsters: [monster] }); - wildMonster.type = 'monster'; - await wildMonster.initialize(); - Memory.state.opponent = wildMonster; - - UI.drawOpponentMonster(); + /** + * @param {string} type + * + * @returns {boolean} + */ + isBattleType (type) { + return Memory.state.opponent.type === type; }, - async encounterTrainer () { - Game.clearCurrentTurn(); - - const nextTrainer = Memory.state.currentArea.trainers[Memory.state.currentArea.trainerProgress]; - - const trainer = new Trainer(nextTrainer); - if (nextTrainer.name === 'Rival') { - trainer.monsters.push(Memory.state.rivalMonster); + getStageOfDaySimple () { + const hours = (new Date()).getHours(); + if (hours >= 6 && hours < 18) { + return 'day'; + } else { + return 'night'; } - await trainer.initialize() - Memory.state.opponent = trainer; - - UI.drawOpponentMonster(); - UI.drawStatus(); - }, - - 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(); }, }; diff --git a/resources/js/main.js b/resources/js/main.js index 8622731..4fd9c62 100644 --- a/resources/js/main.js +++ b/resources/js/main.js @@ -15,7 +15,7 @@ }); await Memory.state.player.initialize(); - Memory.state.activeTechnique = Memory.state.player.activeMonster.activeTechniques[0]; + Game.setActivePlayerMonster(Memory.state.player.monsters[0]); Memory.state.activeBall = Memory.state.player.inventory[0]; // tuxeball Memory.state.rivalMonster = await fetchMonster(possibleStarterMonsters[Math.round(Math.random() * (possibleStarterMonsters.length - 1))]); diff --git a/resources/js/ui.js b/resources/js/ui.js index c67e2f0..820a49c 100644 --- a/resources/js/ui.js +++ b/resources/js/ui.js @@ -350,6 +350,20 @@ const UI = { }, /** + * @returns {HTMLElement} + */ + createPlayerDefeatedMonsterSelection () { + const monsterSelectionNode = UI.createMonsterSelection(Memory.state.player.monsters.filter((monster) => monster.hp > 0)); + + monsterSelectionNode.addEventListener('monster:selected', (event) => { + Game.setActivePlayerMonster(event.detail.monster); + Game.playerIsChoosingNextMonster = false; + }); + + return monsterSelectionNode; + }, + + /** * @param {HTMLElement} battleMonsterNode */ drawOpponentMonster () { @@ -454,6 +468,20 @@ const UI = { UI.drawStatus(); }, + /** + * @param {HTMLElement} monsterSelectionNode + */ + openPlayerDefeatedMonsterSelection (monsterSelectionNode) { + const popup = UI.createPopup().cloneNode(true); // remove event listeners + + monsterSelectionNode.addEventListener('monster:selected', () => { + popup.remove(); + }); + + popup.querySelector('[data-template-slot="content"]').appendChild(monsterSelectionNode); + UI.drawPopup(popup); + }, + /* Battle - Action Feedback */ @@ -572,6 +600,52 @@ const UI = { inventorySelectionMode: 'use', + /** + * @param {Monster[]} monsters + * + * @returns {HTMLElement} + */ + createMonsterSelection (monsters) { + const template = document.createElement('div'); + template.classList.add('monster-selection'); + + for (const monster of monsters) { + const monsterNode = UI.createMonsterSelectionMonster(monster); + + monsterNode.addEventListener('monster:selected', (event) => { + template.dispatchEvent(new CustomEvent('monster:selected', { + detail: { + node: monsterNode, + monster: event.detail.monster, + }, + })); + }); + + template.appendChild(monsterNode); + } + + return template; + }, + + /** + * @param {Monster} monster + * + * @returns {HTMLElement} + */ + createMonsterSelectionMonster (monster) { + const template = UI.createPartyMonster(monster); + + template.addEventListener('click', () => { + template.dispatchEvent(new CustomEvent('monster:selected', { + detail: { + monster: monster, + }, + })); + }); + + return template; + }, + drawStatus () { const currentArea = Memory.state.currentArea; @@ -618,13 +692,8 @@ const UI = { } if (UI.partySelectionMode === 'select') { - Memory.state.player.activeMonster = monster; - Memory.state.activeTechnique = Memory.state.player.activeMonster.activeTechniques[0]; - - UI.drawActiveMonster(); - UI.drawActiveTechniques(); + Game.setActivePlayerMonster(monster); - Game.playerIsChoosingNextMonster = false; popup.remove(); } else if (UI.partySelectionMode === 'stats') { @@ -998,7 +1067,7 @@ const UI = { const inventoryItemNode = UI.createTemplate(Template.inventoryItem); inventoryItemNode.title = item.description; - inventoryItemNode.dataset.templateItem = item.slug; + inventoryItemNode.dataset.inventoryItem = item.slug; inventoryItemNode.querySelector('[data-template-slot="sprite"]').src = `/modules/tuxemon/mods/tuxemon/${item.sprite}`; inventoryItemNode.querySelector('[data-template-slot="name"]').textContent = item.name; @@ -1026,7 +1095,7 @@ const UI = { * @param {InventoryItem} item */ redrawInventoryItem (item) { - const itemNode = document.querySelector(`#inventory *[data-template-item="${item.slug}"]`); + const itemNode = document.querySelector(`#inventory *[data-inventory-item="${item.slug}"]`); if (item.quantity === 0) { itemNode.remove(); } @@ -1042,47 +1111,38 @@ const UI = { * @returns {HTMLElement} */ createItemMonsterSelection (item, monsters) { - const template = document.createElement('div'); + const template = UI.createMonsterSelection( + monsters.filter((monster) => Game.canUseItem(item, monster)) + ); - /** - * @param {Monster} monster - * - * @returns {(HTMLElement|null)} - */ - const createMonsterNode = (monster) => { - if (!Game.canUseItem(item, monster)) { - return null; - } + const onMonsterSelectd = async (event) => { + const monster = event.detail.monster; + const monsterNode = event.detail.node || event.target; + + await Game.useItem(item, monster); - const monsterNode = UI.createPartyMonster(monster); + if (item.quantity === 0) { + Game.removeItemFromInventory(Memory.state.player.inventory, item); + template.dispatchEvent(new Event('item:isExhausted')); + } - monsterNode.addEventListener('click', async () => { - await Game.useItem(item, monster); + else { + const canStillUseItem = Game.canUseItem(item, monster); + if (canStillUseItem) { + const replacingMonsterNode = UI.createMonsterSelectionMonster(monster); + replacingMonsterNode.addEventListener('monster:selected', onMonsterSelectd); - if (item.quantity === 0) { - Game.removeItemFromInventory(Memory.state.player.inventory, item); - template.dispatchEvent(new Event('item:isExhausted')); + monsterNode.replaceWith(replacingMonsterNode); } else { - const replacingMonsterNode = createMonsterNode(monster); - if (replacingMonsterNode) { - monsterNode.replaceWith(replacingMonsterNode); - } else { - monsterNode.remove(); - template.dispatchEvent(new Event('monster:lostCondition')); - } + monsterNode.remove(); + template.dispatchEvent(new Event('monster:lostCondition')); } + } - UI.redrawInventoryItem(item); - }); - - return monsterNode; + UI.redrawInventoryItem(item); }; - for (const monster of monsters) { - const monsterNode = createMonsterNode(monster); - - monsterNode && template.appendChild(monsterNode); - } + template.addEventListener('monster:selected', onMonsterSelectd); return template; }, |