Move application to applyPostSummonAbAttrs and stop assuming no other phases in queue

This commit is contained in:
Dean 2025-03-13 13:23:13 -07:00
parent ca05b94dbe
commit 6995c8c0c0
3 changed files with 64 additions and 39 deletions

View File

@ -5164,6 +5164,7 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
args: any[], args: any[],
gainedMidTurn = false, gainedMidTurn = false,
simulated = false, simulated = false,
priorityCondition?: (p: number) => boolean,
showAbilityInstant = false, showAbilityInstant = false,
messages: string[] = [] messages: string[] = []
) { ) {
@ -5172,7 +5173,10 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
} }
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); 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; return;
} }
@ -5453,6 +5457,19 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
return this.helper.getFailedText(target); 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<TAttr extends AbAttr>( function applyAbAttrsInternal<TAttr extends AbAttr>(
attrType: Constructor<TAttr>, attrType: Constructor<TAttr>,
pokemon: Pokemon | null, pokemon: Pokemon | null,
@ -5461,11 +5478,12 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(
showAbilityInstant = false, showAbilityInstant = false,
simulated = false, simulated = false,
messages: string[] = [], messages: string[] = [],
gainedMidTurn = false gainedMidTurn = false,
priorityCondition?: (p: number) => boolean
) { ) {
for (const passive of [ false, true ]) { for (const passive of [ false, true ]) {
if (pokemon) { 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(); globalScene.clearPhaseQueueSplice();
} }
} }
@ -5714,6 +5732,7 @@ export function applyPostSummonAbAttrs(
attrType: Constructor<PostSummonAbAttr>, attrType: Constructor<PostSummonAbAttr>,
pokemon: Pokemon, pokemon: Pokemon,
simulated = false, simulated = false,
priorityCondition?: (p: number) => boolean,
...args: any[] ...args: any[]
): void { ): void {
applyAbAttrsInternal<PostSummonAbAttr>( applyAbAttrsInternal<PostSummonAbAttr>(
@ -5723,6 +5742,9 @@ export function applyPostSummonAbAttrs(
args, args,
false, false,
simulated, simulated,
[],
false,
priorityCondition
); );
} }
@ -6002,20 +6024,6 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated
applySingleAbAttrs<PreLeaveFieldAbAttr>(pokemon, passive, PreLeaveFieldAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated); applySingleAbAttrs<PreLeaveFieldAbAttr>(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<PostSummonAbAttr>(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated)
}
}
}
function queueShowAbility(pokemon: Pokemon, passive: boolean): void { function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive));
globalScene.clearPhaseQueueSplice(); globalScene.clearPhaseQueueSplice();

View File

@ -1,5 +1,5 @@
import type { BattlerIndex } from "#app/battle"; 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"; import { PokemonPhase } from "#app/phases/pokemon-phase";
/** /**
@ -20,7 +20,7 @@ export class PostSummonActivateAbilityPhase extends PokemonPhase {
start() { start() {
super.start(); super.start();
applyPriorityBasedAbAttrs(this.getPokemon(), (p: number) => p === this.priority); applyPostSummonAbAttrs(PostSummonAbAttr, this.getPokemon(), false, (p: number) => p === this.priority);
this.end(); this.end();
} }

View File

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { BattlerIndex } from "#app/battle"; 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 { ArenaTrapTag } from "#app/data/arena-tag";
import { StatusEffect } from "#app/enums/status-effect"; import { StatusEffect } from "#app/enums/status-effect";
import { PokemonPhase } from "./pokemon-phase"; import { PokemonPhase } from "./pokemon-phase";
@ -23,21 +23,24 @@ export class PostSummonPhase extends PokemonPhase {
super.start(); super.start();
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
let indexAfterPostSummon = globalScene.phaseQueue.findIndex(phase => !(phase instanceof PostSummonPhase));
indexAfterPostSummon = indexAfterPostSummon === -1 ? globalScene.phaseQueue.length : indexAfterPostSummon;
if ( if (
!this.ordered && !this.ordered &&
globalScene.findPhase(phase => phase instanceof PostSummonPhase && phase.getPokemon() !== pokemon) 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.orderPostSummonPhases();
this.queueAbilityActivationPhases(indexAfterPostSummon);
this.end(); this.end();
return; return;
} }
if (!this.ordered) { if (!this.ordered) {
applyPriorityBasedAbAttrs(pokemon, (p: number) => p > 0); applyPostSummonAbAttrs(PostSummonAbAttr, pokemon, false, (p: number) => p > 0);
} }
if (pokemon.status?.effect === StatusEffect.TOXIC) { if (pokemon.status?.effect === StatusEffect.TOXIC) {
@ -52,9 +55,11 @@ export class PostSummonPhase extends PokemonPhase {
) { ) {
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
} }
if (!this.ordered) { 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(); const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
for (const p of field) { for (const p of field) {
applyAbAttrs(CommanderAbAttr, p, null, false); applyAbAttrs(CommanderAbAttr, p, null, false);
@ -63,6 +68,9 @@ export class PostSummonPhase extends PokemonPhase {
this.end(); this.end();
} }
/**
* Sorts the {@linkcode PostSummonPhase}s in the queue by effective speed
*/
private orderPostSummonPhases() { private orderPostSummonPhases() {
globalScene.sortPhaseType( globalScene.sortPhaseType(
PostSummonPhase, PostSummonPhase,
@ -70,28 +78,37 @@ export class PostSummonPhase extends PokemonPhase {
phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD), phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD),
); );
const positivePriorityPhases: PostSummonActivateAbilityPhase[] = []; for (let i = 0; i < globalScene.phaseQueue.length && globalScene.phaseQueue[i] instanceof PostSummonPhase; i++) {
const zeroNegativePriorityPhases: PostSummonActivateAbilityPhase[] = []; (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(); const phasePokemon = phase.getPokemon();
for (const priority of phasePokemon.getAbilityPriorities()) { phasePokemon
(priority > 0 ? positivePriorityPhases : zeroNegativePriorityPhases).push( .getAbilityPriorities()
new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority), .forEach(priority =>
abilityPhases.push(new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority)),
); );
}
}); });
for (const phaseList of [positivePriorityPhases, zeroNegativePriorityPhases]) { abilityPhases.sort(
phaseList.sort(
(phaseA: PostSummonActivateAbilityPhase, phaseB: PostSummonActivateAbilityPhase) => (phaseA: PostSummonActivateAbilityPhase, phaseB: PostSummonActivateAbilityPhase) =>
phaseB.getPriority() - phaseA.getPriority(), phaseB.getPriority() - phaseA.getPriority(),
); );
}
globalScene.unshiftPhase(...positivePriorityPhases); let zeroIndex = abilityPhases.findIndex(phase => phase.getPriority() === 0);
zeroNegativePriorityPhases.forEach(phase => globalScene.pushPhase(phase)); zeroIndex = zeroIndex === -1 ? abilityPhases.length : zeroIndex;
globalScene.unshiftPhase(...abilityPhases.slice(0, zeroIndex));
globalScene.phaseQueue.splice(endIndex, 0, ...abilityPhases.slice(zeroIndex));
} }
} }