From a4b27eb05e3b521cfbeaacd31fa8daa72d7a3064 Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:09:17 -0700 Subject: [PATCH] [Bug] Use InitEncounterPhase to queue PSPs for new encounters (#6614) * Use InitEncounterPhase to queue PSPs for new encounters * Add doc * Add manual PSP queues --- src/battle-scene.ts | 1 + .../encounters/safari-zone-encounter.ts | 2 + src/data/pokemon/pokemon-data.ts | 2 + src/phase-manager.ts | 20 +-------- src/phases/check-switch-phase.ts | 15 ++----- src/phases/encounter-phase.ts | 42 +++++++++---------- src/phases/game-over-phase.ts | 17 +++++--- src/phases/init-encounter-phase.ts | 20 +++++++++ src/phases/mystery-encounter-phases.ts | 28 ++++++------- src/phases/post-summon-phase.ts | 9 ---- src/phases/summon-phase.ts | 18 +------- src/phases/title-phase.ts | 22 ++++++---- .../post-summon-phase-priority-queue.ts | 4 +- 13 files changed, 95 insertions(+), 105 deletions(-) create mode 100644 src/phases/init-encounter-phase.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index ebabf5d4812..79c09c5a5c8 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -3358,6 +3358,7 @@ export class BattleScene extends SceneBase { this.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { this.phaseManager.pushNew("SummonPhase", 1); + this.phaseManager.pushNew("PostSummonPhase", 1); } } diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 764951877f0..cfa862b5743 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -3,6 +3,7 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { NON_LEGEND_PARADOX_POKEMON } from "#balance/special-species-groups"; import type { PokemonSpecies } from "#data/pokemon-species"; +import { BattlerIndex } from "#enums/battler-index"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -324,6 +325,7 @@ async function summonSafariPokemon() { encounter.misc.safariPokemonRemaining -= 1; globalScene.phaseManager.unshiftNew("SummonPhase", 0, false); + globalScene.phaseManager.unshiftNew("PostSummonPhase", BattlerIndex.ENEMY); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts index 8a238a73f93..0094e7310e9 100644 --- a/src/data/pokemon/pokemon-data.ts +++ b/src/data/pokemon/pokemon-data.ts @@ -322,7 +322,9 @@ export class PokemonTurnData { public statStagesDecreased = false; public moveEffectiveness: TypeDamageMultiplier | null = null; public combiningPledge?: MoveId; + /** The Pokemon was brought in this turn by a switch action (not an intial encounter/summon) */ public switchedInThisTurn = false; + public summonedThisTurn = false; public failedRunAway = false; public joinedRound = false; /** Tracker for a pending status effect diff --git a/src/phase-manager.ts b/src/phase-manager.ts index 350e77e52eb..927c343c9f2 100644 --- a/src/phase-manager.ts +++ b/src/phase-manager.ts @@ -12,7 +12,6 @@ import { DynamicQueueManager } from "#app/dynamic-queue-manager"; import { globalScene } from "#app/global-scene"; import type { Phase } from "#app/phase"; import { PhaseTree } from "#app/phase-tree"; -import { BattleType } from "#enums/battle-type"; import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier"; import type { Pokemon } from "#field/pokemon"; import type { PokemonMove } from "#moves/pokemon-move"; @@ -43,6 +42,7 @@ import { GameOverModifierRewardPhase } from "#phases/game-over-modifier-reward-p import { GameOverPhase } from "#phases/game-over-phase"; import { HideAbilityPhase } from "#phases/hide-ability-phase"; import { HidePartyExpBarPhase } from "#phases/hide-party-exp-bar-phase"; +import { InitEncounterPhase } from "#phases/init-encounter-phase"; import { LearnMovePhase } from "#phases/learn-move-phase"; import { LevelCapPhase } from "#phases/level-cap-phase"; import { LevelUpPhase } from "#phases/level-up-phase"; @@ -150,6 +150,7 @@ const PHASES = Object.freeze({ GameOverModifierRewardPhase, HideAbilityPhase, HidePartyExpBarPhase, + InitEncounterPhase, LearnMovePhase, LevelCapPhase, LevelUpPhase, @@ -510,23 +511,6 @@ export class PhaseManager { this.phaseQueue.addPhase(this.create("FaintPhase", ...args), true); } - /** - * Attempts to add {@linkcode PostSummonPhase}s for the enemy pokemon - * - * This is used to ensure that wild pokemon (which have no {@linkcode SummonPhase}) do not queue a {@linkcode PostSummonPhase} - * until all pokemon are on the field. - */ - public tryAddEnemyPostSummonPhases(): void { - if ( - ![BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) - && !this.phaseQueue.exists("SummonPhase") - ) { - globalScene.getEnemyField().forEach(p => { - this.pushPhase(new PostSummonPhase(p.getBattlerIndex(), "SummonPhase")); - }); - } - } - /** * Create a new phase and queue it to run after all others queued by the currently running phase. * @param phase - The name of the phase to create diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index a55db4203bc..ce830fd29d8 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -28,7 +28,7 @@ export class CheckSwitchPhase extends BattlePhase { // ...if the user is playing in Set Mode if (globalScene.battleStyle === BattleStyle.SET) { - this.end(true); + this.end(); return; } @@ -45,7 +45,7 @@ export class CheckSwitchPhase extends BattlePhase { .slice(1) .filter(p => p.isActive()).length === 0 ) { - this.end(true); + this.end(); return; } @@ -55,7 +55,7 @@ export class CheckSwitchPhase extends BattlePhase { || pokemon.isTrapped() || globalScene.getPlayerField().some(p => p.getTag(BattlerTagType.COMMANDED)) ) { - this.end(true); + this.end(); return; } @@ -74,17 +74,10 @@ export class CheckSwitchPhase extends BattlePhase { }, () => { globalScene.ui.setMode(UiMode.MESSAGE); - this.end(true); + this.end(); }, ); }, ); } - - public override end(queuePostSummon = false): void { - if (queuePostSummon) { - globalScene.phaseManager.unshiftNew("PostSummonPhase", this.fieldIndex); - } - super.end(); - } } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index e2ee0e07e95..74049d1f25c 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -562,41 +562,41 @@ export class EncounterPhase extends BattlePhase { if (!this.loaded) { const availablePartyMembers = globalScene.getPokemonAllowedInBattle(); - const minPartySize = globalScene.currentBattle.double ? 2 : 1; - const currentBattle = globalScene.currentBattle; - const checkSwitch = - currentBattle.battleType !== BattleType.TRAINER - && (currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily) - && availablePartyMembers.length > minPartySize; - const checkSwitchIndices: number[] = []; - const phaseManager = globalScene.phaseManager; if (!availablePartyMembers[0].isOnField()) { - phaseManager.pushNew("SummonPhase", 0, true, false, checkSwitch); - } else if (checkSwitch) { - checkSwitchIndices.push(0); + globalScene.phaseManager.pushNew("SummonPhase", 0); } - if (currentBattle.double) { + if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { - phaseManager.pushNew("ToggleDoublePositionPhase", true); + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { - phaseManager.pushNew("SummonPhase", 1, true, false, checkSwitch); - } else if (checkSwitch) { - checkSwitchIndices.push(1); + globalScene.phaseManager.pushNew("SummonPhase", 1); } } } else { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { - phaseManager.pushNew("ReturnPhase", 1); + globalScene.phaseManager.pushNew("ReturnPhase", 1); + } + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false); + } + + if ( + globalScene.currentBattle.battleType !== BattleType.TRAINER + && (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily) + ) { + const minPartySize = globalScene.currentBattle.double ? 2 : 1; + if (availablePartyMembers.length > minPartySize) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); + if (globalScene.currentBattle.double) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); + } } - phaseManager.pushNew("ToggleDoublePositionPhase", false); } - checkSwitchIndices.forEach(i => { - phaseManager.pushNew("CheckSwitchPhase", i, globalScene.currentBattle.double); - }); } handleTutorial(Tutorial.Access_Menu).then(() => super.end()); + + globalScene.phaseManager.pushNew("InitEncounterPhase"); } tryOverrideForBattleSpec(): boolean { diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index a8afa8bfb47..f3fbed26c32 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -85,12 +85,19 @@ export class GameOverPhase extends BattlePhase { globalScene.phaseManager.pushNew("EncounterPhase", true); const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - const checkSwitch = - globalScene.currentBattle.waveIndex > 1 - && globalScene.currentBattle.battleType !== BattleType.TRAINER; - globalScene.phaseManager.pushNew("SummonPhase", 0, true, false, checkSwitch); + + globalScene.phaseManager.pushNew("SummonPhase", 0); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.pushNew("SummonPhase", 1, true, false, checkSwitch); + globalScene.phaseManager.pushNew("SummonPhase", 1); + } + if ( + globalScene.currentBattle.waveIndex > 1 + && globalScene.currentBattle.battleType !== BattleType.TRAINER + ) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); + if (globalScene.currentBattle.double && availablePartyMembers > 1) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); + } } globalScene.ui.fadeIn(1250); diff --git a/src/phases/init-encounter-phase.ts b/src/phases/init-encounter-phase.ts new file mode 100644 index 00000000000..d911651506e --- /dev/null +++ b/src/phases/init-encounter-phase.ts @@ -0,0 +1,20 @@ +import { globalScene } from "#app/global-scene"; +import { Phase } from "#app/phase"; + +/** + * Phase to handle actions on a new encounter that must take place after other setup + * (i.e. queue {@linkcode PostSummonPhase}s) + */ +export class InitEncounterPhase extends Phase { + public override readonly phaseName = "InitEncounterPhase"; + + public override start(): void { + for (const pokemon of globalScene.getField(true)) { + if (pokemon.isEnemy() || pokemon.turnData.summonedThisTurn) { + globalScene.phaseManager.unshiftNew("PostSummonPhase", pokemon.getBattlerIndex()); + } + } + + super.end(); + } +} diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 015138b4665..39b1d1c5667 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -413,26 +413,16 @@ export class MysteryEncounterBattlePhase extends Phase { } const availablePartyMembers = globalScene.getPlayerParty().filter(p => p.isAllowedInBattle()); - const minPartySize = globalScene.currentBattle.double ? 2 : 1; - const checkSwitch = - encounterMode !== MysteryEncounterMode.TRAINER_BATTLE - && !this.disableSwitch - && availablePartyMembers.length > minPartySize; - const checkSwitchIndices: number[] = []; if (!availablePartyMembers[0].isOnField()) { - globalScene.phaseManager.pushNew("SummonPhase", 0, true, false, checkSwitch); - } else if (checkSwitch) { - checkSwitchIndices.push(0); + globalScene.phaseManager.pushNew("SummonPhase", 0); } if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { - globalScene.phaseManager.pushNew("SummonPhase", 1, true, false, checkSwitch); - } else if (checkSwitch) { - checkSwitchIndices.push(1); + globalScene.phaseManager.pushNew("SummonPhase", 1); } } } else { @@ -443,9 +433,17 @@ export class MysteryEncounterBattlePhase extends Phase { globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false); } - checkSwitchIndices.forEach(i => { - globalScene.phaseManager.pushNew("CheckSwitchPhase", i, globalScene.currentBattle.double); - }); + if (encounterMode !== MysteryEncounterMode.TRAINER_BATTLE && !this.disableSwitch) { + const minPartySize = globalScene.currentBattle.double ? 2 : 1; + if (availablePartyMembers.length > minPartySize) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); + if (globalScene.currentBattle.double) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); + } + } + } + + globalScene.phaseManager.pushNew("InitEncounterPhase"); this.end(); } diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 136f2fbd601..a985c629b7a 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -1,23 +1,14 @@ import { applyAbAttrs } from "#abilities/apply-ab-attrs"; -import type { PhaseString } from "#app/@types/phase-types"; import { globalScene } from "#app/global-scene"; import { EntryHazardTag } from "#data/arena-tag"; import { MysteryEncounterPostSummonTag } from "#data/battler-tags"; import { ArenaTagType } from "#enums/arena-tag-type"; -import type { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { StatusEffect } from "#enums/status-effect"; import { PokemonPhase } from "#phases/pokemon-phase"; export class PostSummonPhase extends PokemonPhase { public readonly phaseName = "PostSummonPhase"; - /** Used to determine whether to push or unshift {@linkcode PostSummonActivateAbilityPhase}s */ - public readonly source: PhaseString; - - constructor(battlerIndex?: BattlerIndex | number, source: PhaseString = "SwitchSummonPhase") { - super(battlerIndex); - this.source = source; - } start() { super.start(); diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 65092a15183..de5876f5558 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -17,13 +17,11 @@ export class SummonPhase extends PartyMemberPokemonPhase { // The union type is needed to keep typescript happy as these phases extend from SummonPhase public readonly phaseName: "SummonPhase" | "SummonMissingPhase" | "SwitchSummonPhase" | "ReturnPhase" = "SummonPhase"; private readonly loaded: boolean; - private readonly checkSwitch: boolean; - constructor(fieldIndex: number, player = true, loaded = false, checkSwitch = false) { + constructor(fieldIndex: number, player = true, loaded = false) { super(fieldIndex, player); this.loaded = loaded; - this.checkSwitch = checkSwitch; } start() { @@ -279,14 +277,6 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.resetTurnData(); - if (this.checkSwitch) { - globalScene.phaseManager.pushNew( - "CheckSwitchPhase", - this.getPokemon().getFieldIndex(), - globalScene.currentBattle.double, - ); - } - if ( !this.loaded || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) @@ -298,11 +288,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } queuePostSummon(): void { - if (!this.checkSwitch) { - globalScene.phaseManager.pushNew("PostSummonPhase", this.getPokemon().getBattlerIndex(), this.phaseName); - } - - globalScene.phaseManager.tryAddEnemyPostSummonPhases(); + this.getPokemon().turnData.summonedThisTurn = true; } end() { diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index f69ebc4bf4e..20258022be6 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -313,15 +313,23 @@ export class TitlePhase extends Phase { if (this.loaded) { const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - const minPartySize = globalScene.currentBattle.double ? 2 : 1; - const checkSwitch = + + globalScene.phaseManager.pushNew("SummonPhase", 0, true, true); + if (globalScene.currentBattle.double && availablePartyMembers > 1) { + globalScene.phaseManager.pushNew("SummonPhase", 1, true, true); + } + + if ( globalScene.currentBattle.battleType !== BattleType.TRAINER && (globalScene.currentBattle.waveIndex > 1 || !globalScene.gameMode.isDaily) - && availablePartyMembers > minPartySize; - - globalScene.phaseManager.pushNew("SummonPhase", 0, true, true, checkSwitch); - if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.pushNew("SummonPhase", 1, true, true, checkSwitch); + ) { + const minPartySize = globalScene.currentBattle.double ? 2 : 1; + if (availablePartyMembers > minPartySize) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); + if (globalScene.currentBattle.double) { + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); + } + } } } diff --git a/src/queues/post-summon-phase-priority-queue.ts b/src/queues/post-summon-phase-priority-queue.ts index 37da90a1427..28e727de01b 100644 --- a/src/queues/post-summon-phase-priority-queue.ts +++ b/src/queues/post-summon-phase-priority-queue.ts @@ -37,9 +37,7 @@ export class PostSummonPhasePriorityQueue extends PokemonPhasePriorityQueue