diff --git a/src/phases.ts b/src/phases.ts index bbb45b55b17..83ca511406a 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -20,7 +20,7 @@ import { ModifierTier } from "./modifier/modifier-tier"; import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, RememberMoveModifierType, TmModifierType, getDailyRunStarterModifiers, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptions, getPlayerShopModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { BattlerTagLapseType, CenterOfAttentionTag, EncoreTag, ProtectedTag, SemiInvulnerableTag, TrappedTag } from "./data/battler-tags"; -import { getPokemonMessage, getPokemonNameWithAffix } from "./messages"; +import { getPokemonNameWithAffix } from "./messages"; import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; @@ -1437,7 +1437,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.scene.time.delayedCall(750, () => this.summon()); } else { const trainerName = this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); - const pokemonName = getPokemonNameWithAffix(this.getPokemon()); + const pokemonName = this.getPokemon().getNameToRender(); const message = i18next.t("battle:trainerSendOut", { trainerName, pokemonName }); this.scene.pbTrayEnemy.hide(); @@ -1650,7 +1650,7 @@ export class SwitchSummonPhase extends SummonPhase { i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedPokemon) }) : i18next.t("battle:trainerGo", { trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), - pokemonName: getPokemonNameWithAffix(this.getPokemon()) + pokemonName: this.getPokemon().getNameToRender() }) ); // Ensure improperly persisted summon data (such as tags) is cleared upon switching @@ -2168,6 +2168,15 @@ export class CommandPhase extends FieldPhase { } } +/** + * Phase for determining an enemy AI's action for the next turn. + * During this phase, the enemy decides whether to switch (if it has a trainer) + * or to use a move from its moveset. + * + * For more information on how the Enemy AI works, see docs/enemy-ai.md + * @see {@linkcode Pokemon.getMatchupScore} + * @see {@linkcode EnemyPokemon.getNextMove} + */ export class EnemyCommandPhase extends FieldPhase { protected fieldIndex: integer; @@ -2186,6 +2195,15 @@ export class EnemyCommandPhase extends FieldPhase { const trainer = battle.trainer; + /** + * If the enemy has a trainer, decide whether or not the enemy should switch + * to another member in its party. + * + * This block compares the active enemy Pokemon's {@linkcode Pokemon.getMatchupScore | matchup score} + * against the active player Pokemon with the enemy party's other non-fainted Pokemon. If a party + * member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers), + * the enemy will switch to that Pokemon. + */ if (trainer && !enemyPokemon.getMoveQueue().length) { const opponents = enemyPokemon.getOpponents(); @@ -2217,6 +2235,7 @@ export class EnemyCommandPhase extends FieldPhase { } } + /** Select a move to use (and a target to use it against, if applicable) */ const nextMove = enemyPokemon.getNextMove(); this.scene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = @@ -2410,7 +2429,7 @@ export class BerryPhase extends FieldPhase { pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); if (cancelled.value) { - pokemon.scene.queueMessage(getPokemonMessage(pokemon, " is too\nnervous to eat berries!")); + pokemon.scene.queueMessage(i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); } else { this.scene.unshiftPhase( new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM) @@ -3789,11 +3808,10 @@ export class FaintPhase extends PokemonPhase { const nonFaintedPartyMemberCount = nonFaintedLegalPartyMembers.length; if (!nonFaintedPartyMemberCount) { this.scene.unshiftPhase(new GameOverPhase(this.scene)); - } else if (nonFaintedPartyMemberCount >= this.scene.currentBattle.getBattlerCount() || (this.scene.currentBattle.double && !nonFaintedLegalPartyMembers[0].isActive(true))) { - this.scene.pushPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false)); - } - if (nonFaintedPartyMemberCount === 1 && this.scene.currentBattle.double) { + } else if (nonFaintedPartyMemberCount === 1 && this.scene.currentBattle.double) { this.scene.unshiftPhase(new ToggleDoublePositionPhase(this.scene, true)); + } else if (nonFaintedPartyMemberCount >= this.scene.currentBattle.getBattlerCount()) { + this.scene.pushPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false)); } } else { this.scene.unshiftPhase(new VictoryPhase(this.scene, this.battlerIndex)); @@ -4457,9 +4475,10 @@ export class SwitchPhase extends BattlePhase { start() { super.start(); + const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()); // Skip modal switch if impossible - if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) { + if (this.isModal && (!availablePartyMembers.filter(p => !p.isActive(true)).length || (!this.scene.currentBattle.started && availablePartyMembers.length === 1))) { return super.end(); } @@ -4469,7 +4488,7 @@ export class SwitchPhase extends BattlePhase { } // Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once - const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? this.fieldIndex : 0; + const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || availablePartyMembers.length > 1 ? this.fieldIndex : 0; this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => { if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) { @@ -5026,7 +5045,7 @@ export class AttemptCapturePhase extends PokemonPhase { Promise.all([pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon)]).then(() => { if (this.scene.getParty().length === 6) { const promptRelease = () => { - this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => { this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); this.scene.ui.setMode(Mode.CONFIRM, () => { const newPokemon = this.scene.addPlayerPokemon(pokemon.species, pokemon.level, pokemon.abilityIndex, pokemon.formIndex, pokemon.gender, pokemon.shiny, pokemon.variant, pokemon.ivs, pokemon.nature, pokemon); @@ -5173,9 +5192,11 @@ export class SelectModifierPhase extends BattlePhase { this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type.tier))); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); - this.scene.money -= rerollCost; - this.scene.updateMoneyText(); - this.scene.animateMoneyChanged(false); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= rerollCost; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + } this.scene.playSound("buy"); } break; @@ -5216,7 +5237,7 @@ export class SelectModifierPhase extends BattlePhase { break; } - if (cost && this.scene.money < cost) { + if (cost && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { this.scene.ui.playError(); return false; } @@ -5226,9 +5247,11 @@ export class SelectModifierPhase extends BattlePhase { if (cost) { result.then(success => { if (success) { - this.scene.money -= cost; - this.scene.updateMoneyText(); - this.scene.animateMoneyChanged(false); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= cost; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + } this.scene.playSound("buy"); (this.scene.ui.getHandler() as ModifierSelectUiHandler).updateCostText(); } else { @@ -5308,7 +5331,9 @@ export class SelectModifierPhase extends BattlePhase { getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): integer { let baseValue = 0; - if (lockRarities) { + if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + return baseValue; + } else if (lockRarities) { const tierValues = [50, 125, 300, 750, 2000]; for (const opt of typeOptions) { baseValue += tierValues[opt.type.tier];