diff --git a/src/data/ability.ts b/src/data/ability.ts index 4ca61024b6f..6e9f9f0e252 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -5164,6 +5164,7 @@ function applySingleAbAttrs( args: any[], gainedMidTurn = false, simulated = false, + priorityCondition?: (p: number) => boolean, showAbilityInstant = false, messages: string[] = [] ) { @@ -5172,7 +5173,10 @@ function applySingleAbAttrs( } const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) { + if ( + gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain()) + || (priorityCondition && !priorityCondition(ability.postSummonPriority)) + ) { return; } @@ -5453,6 +5457,19 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { return this.helper.getFailedText(target); } } + +/** + * Main Function for handling ability application. Applies both the normal ability and passive of the Pokemon + * @param attrType The type of {@linkcode AbAttr} to apply + * @param pokemon The {@linkcode Pokemon} whose abilities should be applied + * @param applyFunc The {@linkcode AbAttrApplyFunc} corresponding to {@linkcode attrType} + * @param args Extra arguments, handled by individual {@linkcode AbAttr}s + * @param showAbilityInstant If `true`, show the ability bar instantly instead of queuing it + * @param simulated `true` if the call is simulated and the battle state should not be changes + * @param messages Array of messages which will be added to if the ability displays a message + * @param gainedMidTurn `true` if the ability is activating because it was gained during the battle + * @param priorityCondition If defined, only abilities with priority that meets the condition will be applied + */ function applyAbAttrsInternal( attrType: Constructor, pokemon: Pokemon | null, @@ -5461,11 +5478,12 @@ function applyAbAttrsInternal( showAbilityInstant = false, simulated = false, messages: string[] = [], - gainedMidTurn = false + gainedMidTurn = false, + priorityCondition?: (p: number) => boolean ) { for (const passive of [ false, true ]) { if (pokemon) { - applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages); + applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, priorityCondition, showAbilityInstant, messages); globalScene.clearPhaseQueueSplice(); } } @@ -5714,6 +5732,7 @@ export function applyPostSummonAbAttrs( attrType: Constructor, pokemon: Pokemon, simulated = false, + priorityCondition?: (p: number) => boolean, ...args: any[] ): void { applyAbAttrsInternal( @@ -5723,6 +5742,9 @@ export function applyPostSummonAbAttrs( args, false, simulated, + [], + false, + priorityCondition ); } @@ -6002,20 +6024,6 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated applySingleAbAttrs(pokemon, passive, PreLeaveFieldAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated); } -/** - * Applies only abilities whose priority meets a condition - * - * @param condition Apply to abilities whose priority meets the condition - */ -export function applyPriorityBasedAbAttrs(pokemon: Pokemon, condition: (p: number) => boolean, simulated: boolean = false, ...args: any[]) { - for (const passive of [ false, true ]) { - const ability: Ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - if (condition(ability.postSummonPriority)) { - applySingleAbAttrs(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated) - } - } -} - function queueShowAbility(pokemon: Pokemon, passive: boolean): void { globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); globalScene.clearPhaseQueueSplice(); diff --git a/src/phases/post-summon-activate-ability-phase.ts b/src/phases/post-summon-activate-ability-phase.ts index 0d09cb1a1e6..fb1c3294a63 100644 --- a/src/phases/post-summon-activate-ability-phase.ts +++ b/src/phases/post-summon-activate-ability-phase.ts @@ -1,5 +1,5 @@ import type { BattlerIndex } from "#app/battle"; -import { applyPriorityBasedAbAttrs } from "#app/data/ability"; +import { applyPostSummonAbAttrs, PostSummonAbAttr } from "#app/data/ability"; import { PokemonPhase } from "#app/phases/pokemon-phase"; /** @@ -20,7 +20,7 @@ export class PostSummonActivateAbilityPhase extends PokemonPhase { start() { super.start(); - applyPriorityBasedAbAttrs(this.getPokemon(), (p: number) => p === this.priority); + applyPostSummonAbAttrs(PostSummonAbAttr, this.getPokemon(), false, (p: number) => p === this.priority); this.end(); } diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index f648ea313d3..0500d63698f 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; -import { applyAbAttrs, applyPriorityBasedAbAttrs, CommanderAbAttr, PostSummonAbAttr } from "#app/data/ability"; +import { applyAbAttrs, applyPostSummonAbAttrs, CommanderAbAttr, PostSummonAbAttr } from "#app/data/ability"; import { ArenaTrapTag } from "#app/data/arena-tag"; import { StatusEffect } from "#app/enums/status-effect"; import { PokemonPhase } from "./pokemon-phase"; @@ -23,21 +23,24 @@ export class PostSummonPhase extends PokemonPhase { super.start(); const pokemon = this.getPokemon(); + let indexAfterPostSummon = globalScene.phaseQueue.findIndex(phase => !(phase instanceof PostSummonPhase)); + indexAfterPostSummon = indexAfterPostSummon === -1 ? globalScene.phaseQueue.length : indexAfterPostSummon; if ( !this.ordered && globalScene.findPhase(phase => phase instanceof PostSummonPhase && phase.getPokemon() !== pokemon) ) { - globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex(), true)); + globalScene.phaseQueue.splice(indexAfterPostSummon++, 0, new PostSummonPhase(pokemon.getBattlerIndex(), true)); this.orderPostSummonPhases(); + this.queueAbilityActivationPhases(indexAfterPostSummon); this.end(); return; } if (!this.ordered) { - applyPriorityBasedAbAttrs(pokemon, (p: number) => p > 0); + applyPostSummonAbAttrs(PostSummonAbAttr, pokemon, false, (p: number) => p > 0); } if (pokemon.status?.effect === StatusEffect.TOXIC) { @@ -52,9 +55,11 @@ export class PostSummonPhase extends PokemonPhase { ) { pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); } + if (!this.ordered) { - applyPriorityBasedAbAttrs(pokemon, (p: number) => p <= 0); + applyPostSummonAbAttrs(PostSummonAbAttr, pokemon, false, (p: number) => p <= 0); } + const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { applyAbAttrs(CommanderAbAttr, p, null, false); @@ -63,6 +68,9 @@ export class PostSummonPhase extends PokemonPhase { this.end(); } + /** + * Sorts the {@linkcode PostSummonPhase}s in the queue by effective speed + */ private orderPostSummonPhases() { globalScene.sortPhaseType( PostSummonPhase, @@ -70,28 +78,37 @@ export class PostSummonPhase extends PokemonPhase { phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD), ); - const positivePriorityPhases: PostSummonActivateAbilityPhase[] = []; - const zeroNegativePriorityPhases: PostSummonActivateAbilityPhase[] = []; + for (let i = 0; i < globalScene.phaseQueue.length && globalScene.phaseQueue[i] instanceof PostSummonPhase; i++) { + (globalScene.phaseQueue[i] as PostSummonPhase).ordered = true; + } + } - globalScene.phaseQueue.forEach((phase: PostSummonPhase) => { - phase.ordered = true; + /** + * Adds {@linkcode PostSummonActivateAbilityPhase}s for all {@linkcode PostSummonPhase}s in the queue + * @param endIndex The index of the first non-{@linkcode PostSummonPhase} Phase in the queue, or the length if none exists + */ + private queueAbilityActivationPhases(endIndex: number) { + const abilityPhases: PostSummonActivateAbilityPhase[] = []; + + globalScene.phaseQueue.slice(0, endIndex).forEach((phase: PostSummonPhase) => { const phasePokemon = phase.getPokemon(); - for (const priority of phasePokemon.getAbilityPriorities()) { - (priority > 0 ? positivePriorityPhases : zeroNegativePriorityPhases).push( - new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority), + phasePokemon + .getAbilityPriorities() + .forEach(priority => + abilityPhases.push(new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority)), ); - } }); - for (const phaseList of [positivePriorityPhases, zeroNegativePriorityPhases]) { - phaseList.sort( - (phaseA: PostSummonActivateAbilityPhase, phaseB: PostSummonActivateAbilityPhase) => - phaseB.getPriority() - phaseA.getPriority(), - ); - } + abilityPhases.sort( + (phaseA: PostSummonActivateAbilityPhase, phaseB: PostSummonActivateAbilityPhase) => + phaseB.getPriority() - phaseA.getPriority(), + ); - globalScene.unshiftPhase(...positivePriorityPhases); - zeroNegativePriorityPhases.forEach(phase => globalScene.pushPhase(phase)); + let zeroIndex = abilityPhases.findIndex(phase => phase.getPriority() === 0); + zeroIndex = zeroIndex === -1 ? abilityPhases.length : zeroIndex; + + globalScene.unshiftPhase(...abilityPhases.slice(0, zeroIndex)); + globalScene.phaseQueue.splice(endIndex, 0, ...abilityPhases.slice(zeroIndex)); } }