From 01b6ba89ede7db0554e44469a7a71ca91ad3950f Mon Sep 17 00:00:00 2001 From: RedstonewolfX <108761527+RedstonewolfX@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:05:15 -0400 Subject: [PATCH] Display enemy choices The opponent makes their selection before the player does (this does not affect RNG) The opponent's choice(s) are shown in the V menu and in the move PP flyout (switches are only shown in the V menu, for obvious reasons) --- src/field/pokemon.ts | 18 +++++++- src/interfaces/locales.ts | 3 +- src/locales/en/ability.ts | 15 +++++++ src/logger.ts | 2 + src/phases.ts | 92 ++++++++++++++++++++++++++++++++------- src/ui/arena-flyout.ts | 6 +++ src/ui/battle-flyout.ts | 30 +++++++++++-- 7 files changed, 144 insertions(+), 22 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index de7292a13d6..d25b78ea043 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -51,6 +51,7 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { getPokemonNameWithAffix } from "#app/messages.js"; +import BattleFlyout from "#app/ui/battle-flyout.js"; export enum FieldPosition { CENTER, @@ -88,6 +89,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public pauseEvolutions: boolean; public pokerus: boolean; + public flyout: BattleFlyout; + public fusionSpecies: PokemonSpecies; public fusionFormIndex: integer; public fusionAbilityIndex: integer; @@ -3669,6 +3672,12 @@ export class EnemyPokemon extends Pokemon { : null; if (queuedMove) { if (queuedMove.isUsable(this, this.getMoveQueue()[0].ignorePP)) { + this.flyout.setText() + for (var i = 0; i < this.moveset.length; i++) { + if (this.moveset[i].moveId == queuedMove.moveId) { + this.flyout.setText(i) + } + } return { move: queuedMove.moveId, targets: this.getMoveQueue()[0].targets, ignorePP: this.getMoveQueue()[0].ignorePP }; } else { this.getMoveQueue().shift(); @@ -3679,18 +3688,22 @@ export class EnemyPokemon extends Pokemon { const movePool = this.getMoveset().filter(m => m.isUsable(this)); if (movePool.length) { if (movePool.length === 1) { + this.flyout.setText(this.getMoveset().indexOf(movePool[0])) return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) }; } const encoreTag = this.getTag(EncoreTag) as EncoreTag; if (encoreTag) { const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId); if (encoreMove) { + this.flyout.setText(this.getMoveset().indexOf(encoreMove)) return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) }; } } switch (this.aiType) { case AiType.RANDOM: - const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)].moveId; + var i = this.scene.randBattleSeedInt(movePool.length) + const moveId = movePool[i].moveId; + this.flyout.setText(i) return { move: moveId, targets: this.getNextTargets(moveId) }; case AiType.SMART_RANDOM: case AiType.SMART: @@ -3763,10 +3776,11 @@ export class EnemyPokemon extends Pokemon { } } console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName())); + this.flyout.setText(movePool.indexOf(sortedMovePool[r])) return { move: sortedMovePool[r].moveId, targets: moveTargets[sortedMovePool[r].moveId] }; } } - + this.flyout.setText() return { move: Moves.STRUGGLE, targets: this.getNextTargets(Moves.STRUGGLE) }; } diff --git a/src/interfaces/locales.ts b/src/interfaces/locales.ts index 5f7c52100c1..ca8f15625b6 100644 --- a/src/interfaces/locales.ts +++ b/src/interfaces/locales.ts @@ -20,7 +20,8 @@ export interface MoveTranslationEntries { export interface AbilityTranslationEntry { name: string, - description: string + description: string, + partial?: string } export interface AbilityTranslationEntries { diff --git a/src/locales/en/ability.ts b/src/locales/en/ability.ts index 7e81f90afff..f9564e316af 100644 --- a/src/locales/en/ability.ts +++ b/src/locales/en/ability.ts @@ -348,6 +348,7 @@ export const ability: AbilityTranslationEntries = { drySkin: { name: "Dry Skin", description: "Restores HP in rain or when hit by Water-type moves. Reduces HP in harsh sunlight, and increases the damage received from Fire-type moves.", + partial: "Bypasses Heal Block." }, download: { name: "Download", @@ -460,6 +461,7 @@ export const ability: AbilityTranslationEntries = { iceBody: { name: "Ice Body", description: "The Pokémon gradually regains HP in snow.", + partial: "Bypasses Heal Block." }, solidRock: { name: "Solid Rock", @@ -488,6 +490,7 @@ export const ability: AbilityTranslationEntries = { flowerGift: { name: "Flower Gift", description: "Boosts the Attack and Sp. Def stats of itself and allies in harsh sunlight.", + partial: "Doesn't cause Cherrim to change forms." }, badDreams: { name: "Bad Dreams", @@ -556,6 +559,7 @@ export const ability: AbilityTranslationEntries = { harvest: { name: "Harvest", description: "May create another Berry after one is used.", + partial: "Activation chance not doubled in Harsh Sunlight." }, telepathy: { name: "Telepathy", @@ -668,6 +672,7 @@ export const ability: AbilityTranslationEntries = { cheekPouch: { name: "Cheek Pouch", description: "Restores HP as well when the Pokémon eats a Berry.", + partial: "Bypasses Heal Block." }, protean: { name: "Protean", @@ -836,6 +841,7 @@ export const ability: AbilityTranslationEntries = { disguise: { name: "Disguise", description: "Once per battle, the shroud that covers the Pokémon can protect it from an attack.", + partial: "Multi-Hit moves will heal this Pokemon instead of damaging it." }, battleBond: { name: "Battle Bond", @@ -844,10 +850,12 @@ export const ability: AbilityTranslationEntries = { powerConstruct: { name: "Power Construct", description: "Other Cells gather to aid when its HP becomes half or less. Then the Pokémon changes its form to Complete Forme.", + partial: "Doesn't account for Zygarde 10%." }, corrosion: { name: "Corrosion", description: "The Pokémon can poison the target even if it's a Steel or Poison type.", + partial: "May not interact with Magic Bounce and its counterparts." }, comatose: { name: "Comatose", @@ -1024,6 +1032,7 @@ export const ability: AbilityTranslationEntries = { neutralizingGas: { name: "Neutralizing Gas", description: "If the Pokémon with Neutralizing Gas is in the battle, the effects of all Pokémon's Abilities will be nullified or will not be triggered.", + partial: "Weird interaction with some moves." }, pastelVeil: { name: "Pastel Veil", @@ -1124,14 +1133,17 @@ export const ability: AbilityTranslationEntries = { protosynthesis: { name: "Protosynthesis", description: "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", + partial: "Incorrectly accounts for modifiers other than stat stages." }, quarkDrive: { name: "Quark Drive", description: "Boosts the Pokémon's most proficient stat on Electric Terrain or if the Pokémon is holding Booster Energy.", + partial: "Incorrectly accounts for modifiers other than stat stages." }, goodAsGold: { name: "Good as Gold", description: "A body of pure, solid gold gives the Pokémon full immunity to other Pokémon's status moves.", + partial: "Some interactions need fixing." }, vesselOfRuin: { name: "Vessel of Ruin", @@ -1188,10 +1200,12 @@ export const ability: AbilityTranslationEntries = { earthEater: { name: "Earth Eater", description: "If hit by a Ground-type move, the Pokémon has its HP restored instead of taking damage.", + partial: "Bypasses Heal Block." }, myceliumMight: { name: "Mycelium Might", description: "The Pokémon will always act more slowly when using status moves, but these moves will be unimpeded by the Ability of the target.", + partial: "Does not reduce the priority of status moves." }, mindsEye: { name: "Mind's Eye", @@ -1204,6 +1218,7 @@ export const ability: AbilityTranslationEntries = { hospitality: { name: "Hospitality", description: "When the Pokémon enters a battle, it showers its ally with hospitality, restoring a small amount of the ally's HP.", + partial: "Bypasses Heal Block." }, toxicChain: { name: "Toxic Chain", diff --git a/src/logger.ts b/src/logger.ts index 828b1b2ae47..6581bbcc496 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -68,6 +68,8 @@ export const rarityslot = [0, ""] * * Its contents are printed to the current wave's actions list, separated by pipes `|`, when the turn begins playing out. */ export const Actions = [] +/** Used for enemy attack prediction. Stored here so that it's universally available. */ +export const enemyPlan = [] // Booleans export const isPreSwitch: Utils.BooleanHolder = new Utils.BooleanHolder(false); diff --git a/src/phases.ts b/src/phases.ts index 00d44edeb41..47059930a3f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1697,6 +1697,7 @@ export class EncounterPhase extends BattlePhase { const enemyField = this.scene.getEnemyField(); enemyField.forEach((enemyPokemon, e) => { + enemyPokemon.flyout.revealMoves() if (enemyPokemon.isShiny()) { this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); } @@ -2618,6 +2619,7 @@ export class CheckSwitchPhase extends BattlePhase { this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].text = "???" this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[3].text = "???" this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].setColor("#f8f8f8") + this.scene.getEnemyField()[i].flyout.setText() } //this.scene.pokemonInfoContainer.hide() this.end(); @@ -2776,20 +2778,47 @@ export class TurnInitPhase extends FieldPhase { //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); - this.scene.getField().forEach((pokemon, i) => { - if (pokemon?.isActive()) { - if (pokemon.isPlayer()) { - this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); - } else { - pokemon.usedInBattle = true; - pokemon.getBattleInfo().iconsActive = true + LoggerTools.enemyPlan[0] = "" + LoggerTools.enemyPlan[1] = "" + LoggerTools.enemyPlan[2] = "" + LoggerTools.enemyPlan[3] = "" + + if (false) { + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (pokemon.isPlayer()) { + this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + } else { + pokemon.flyout.setText() + pokemon.usedInBattle = true; + pokemon.getBattleInfo().iconsActive = true + } + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); } - - pokemon.resetTurnData(); - - this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); - } - }); + }); + } else { + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (!pokemon.isPlayer()) { + pokemon.flyout.setText() + pokemon.usedInBattle = true; + pokemon.getBattleInfo().iconsActive = true + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + } + } + }); + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (pokemon.isPlayer()) { + this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + } + } + }); + } var Pt = this.scene.getEnemyParty() var Pt1 = [] @@ -3147,9 +3176,16 @@ export class EnemyCommandPhase extends FieldPhase { battle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = { command: Command.POKEMON, cursor: index, args: [false] }; - + console.log(enemyPokemon.name + " selects:", "Switch to " + this.scene.getEnemyParty()[index].name) battle.enemySwitchCounter++; + LoggerTools.enemyPlan[this.fieldIndex*2] = "Switching out" + LoggerTools.enemyPlan[this.fieldIndex*2 + 1] = "→ " + this.scene.getEnemyParty()[index].name + + enemyPokemon.flyout.setText() + + this.scene.arenaFlyout.updateFieldText() + return this.end(); } } @@ -3157,12 +3193,38 @@ export class EnemyCommandPhase extends FieldPhase { } const nextMove = enemyPokemon.getNextMove(); + const mv = new PokemonMove(nextMove.move) this.scene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = { command: Command.FIGHT, move: nextMove }; - + const targetLabels = ["Counter", "[PLAYER L]", "[PLAYER R]", "[ENEMY L]", "[ENEMY R]"] + this.scene.getParty().forEach((v, i, a) => { + if (v.isActive() && v.name) { + targetLabels[i + 1] = v.name + } + }) + this.scene.getEnemyParty().forEach((v, i, a) => { + if (v.isActive() && v.name) { + targetLabels[i + 3] = v.name + } + }) + if (this.fieldIndex == 0) { + targetLabels[3] = "Self" + } + if (this.fieldIndex == 1) { + targetLabels[4] = "Self" + } + if (targetLabels[1] == targetLabels[2]) { + targetLabels[1] += " (L)" + targetLabels[2] += " (R)" + } + console.log(enemyPokemon.name + " selects:", mv.getName() + " → " + nextMove.targets.map((m) => targetLabels[m + 1])) this.scene.currentBattle.enemySwitchCounter = Math.max(this.scene.currentBattle.enemySwitchCounter - 1, 0); + LoggerTools.enemyPlan[this.fieldIndex*2] = mv.getName() + LoggerTools.enemyPlan[this.fieldIndex*2 + 1] = "→ " + nextMove.targets.map((m) => targetLabels[m + 1]) + this.scene.arenaFlyout.updateFieldText() + this.end(); } } diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index dc8c09bf5c2..ff55d8c6dfc 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -306,6 +306,12 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { } this.flyoutTextPlayer.text += "\n" } + if (true) + for (var i = 0; i < LoggerTools.enemyPlan.length; i++) { + if (LoggerTools.enemyPlan[i] != "") { + this.flyoutTextEnemy.text += LoggerTools.enemyPlan[i] + "\n" + } + } } /** diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index bca457a64ea..dc25092664a 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -1,5 +1,5 @@ import { default as Pokemon } from "../field/pokemon"; -import { addTextObject, TextStyle } from "./text"; +import { addTextObject, setTextStyle, TextStyle } from "./text"; import * as Utils from "../utils"; import BattleScene from "#app/battle-scene.js"; import Move from "#app/data/move.js"; @@ -8,6 +8,7 @@ import { BerryType } from "#enums/berry-type"; import { Moves } from "#enums/moves"; import { UiTheme } from "#enums/ui-theme"; import { getPokemonNameWithAffix } from "#app/messages.js"; +import * as LoggerTools from "../logger"; /** Container for info about a {@linkcode Move} */ interface MoveInfo { @@ -114,6 +115,8 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { initInfo(pokemon: Pokemon) { this.pokemon = pokemon; + pokemon.flyout = this; + this.name = `Flyout ${getPokemonNameWithAffix(this.pokemon)}`; this.flyoutParent.name = `Flyout Parent ${getPokemonNameWithAffix(this.pokemon)}`; @@ -122,17 +125,22 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { } /** Sets and formats the text property for all {@linkcode Phaser.GameObjects.Text} in the flyoutText array */ - private setText() { + setText(highlight?: integer) { + var e = this.battleScene.getEnemyField() + console.log(this.moveInfo.map(v => v.move.name)) for (let i = 0; i < this.flyoutText.length; i++) { const flyoutText = this.flyoutText[i]; const moveInfo = this.moveInfo[i]; if (!moveInfo) { + const mv = this.pokemon.getMoveset()[i] + flyoutText.text = `${highlight == i ? ">> " : ""}${mv.getName()}${highlight == i ? " <<" : ""}`; + flyoutText.text = `${highlight == i ? ">> " : ""}???${highlight == i ? " <<" : ""}`; continue; } const currentPp = moveInfo.maxPp - moveInfo.ppUsed; - flyoutText.text = `${moveInfo.move.name} ${currentPp}/${moveInfo.maxPp}`; + flyoutText.text = `${highlight == i ? ">> " : ""}${moveInfo.move.name} ${currentPp}/${moveInfo.maxPp}${highlight == i ? " <<" : ""}`; } } @@ -149,7 +157,12 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { if (foundInfo) { foundInfo.ppUsed = Math.min(foundInfo.ppUsed + moveUsedEvent.ppUsed, foundInfo.maxPp); } else { - this.moveInfo.push({move: moveUsedEvent.move, maxPp: moveUsedEvent.move.pp, ppUsed: moveUsedEvent.ppUsed}); + var idx = this.pokemon.moveset.indexOf(this.pokemon.moveset.find((v) => (v.getMove().id == moveUsedEvent.move.id))) + if (idx == -1) { + this.moveInfo.push({move: moveUsedEvent.move, maxPp: moveUsedEvent.move.pp, ppUsed: moveUsedEvent.ppUsed}); + } else { + this.moveInfo[idx] = {move: moveUsedEvent.move, maxPp: moveUsedEvent.move.pp, ppUsed: moveUsedEvent.ppUsed}; + } } this.setText(); @@ -172,6 +185,15 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { this.setText(); } + public revealMoves() { + return; + this.moveInfo = new Array(4); + this.pokemon.moveset.forEach((mv, idx) => { + this.moveInfo[idx] = {move: mv.getMove(), maxPp: mv.getMovePp(), ppUsed: mv.ppUsed} + }) + this.setText() + } + /** Animates the flyout to either show or hide it by applying a fade and translation */ toggleFlyout(visible: boolean): void { this.flyoutVisible = visible;