From 78882d4be2dbb9efa3f081a62757610823f2abf1 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 12 Mar 2025 01:33:05 -0700 Subject: [PATCH] Update to use priority numbers instead of a boolean --- src/data/ability.ts | 20 +++---- src/field/pokemon.ts | 7 +-- src/phases/post-summon-phase.ts | 53 +++++++++++++------ .../priority-ability-activation-phase.ts | 18 +++++-- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 6f2ae37678e..675cf6b3823 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -56,18 +56,18 @@ export class Ability implements Localizable { public name: string; public description: string; public generation: number; + public postSummonPriority: number; public isBypassFaint: boolean; public isIgnorable: boolean; - public isPriority: boolean; public attrs: AbAttr[]; public conditions: AbAttrCondition[]; - constructor(id: Abilities, generation: number, isPriority: boolean = false) { + constructor(id: Abilities, generation: number, postSummonPriority: number = 0) { this.id = id; this.nameAppend = ""; this.generation = generation; - this.isPriority = isPriority; + this.postSummonPriority = postSummonPriority; this.attrs = []; this.conditions = []; @@ -6003,14 +6003,14 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated } /** - * Applies only abilities that are priority or are not, based on parameters + * Applies only abilities whose priority meets a condition * - * @param priority If true, apply only priority abilities. If false, apply only non-priority abilities + * @param condition Apply to abilities whose priority meets the condition */ -export function applyPriorityBasedAbAttrs(pokemon: Pokemon, priority: boolean, simulated: boolean = false, ...args: any[]) { +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 (ability.isPriority == priority) { + if (condition(ability.postSummonPriority)) { applySingleAbAttrs(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated) } } @@ -6904,7 +6904,7 @@ export function initAbilities() { .edgeCase(), // interacts incorrectly with rock head. It's meant to switch abilities before recoil would apply so that a pokemon with rock head would lose rock head first and still take the recoil new Ability(Abilities.GORILLA_TACTICS, 8) .attr(GorillaTacticsAbAttr), - new Ability(Abilities.NEUTRALIZING_GAS, 8, true) + new Ability(Abilities.NEUTRALIZING_GAS, 8, 2) .attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0) .attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr) .attr(UncopiableAbilityAbAttr) @@ -6937,14 +6937,14 @@ export function initAbilities() { .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1), new Ability(Abilities.GRIM_NEIGH, 8) .attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1), - new Ability(Abilities.AS_ONE_GLASTRIER, 8, true) + new Ability(Abilities.AS_ONE_GLASTRIER, 8, 1) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneGlastrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PreventBerryUseAbAttr) .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr), - new Ability(Abilities.AS_ONE_SPECTRIER, 8, true) + new Ability(Abilities.AS_ONE_SPECTRIER, 8, 1) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneSpectrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(PreventBerryUseAbAttr) .attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index dd5fb26cbec..058996ba093 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2265,11 +2265,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - /** - * @returns If either of the Pokemon's abilities have priority activation - */ - public hasPriorityAbility() { - return [this.getAbility(), this.getPassiveAbility()].some(ability => ability.isPriority); + public getAbilityPriorities(): Set { + return new Set([ this.getAbility().postSummonPriority, this.getPassiveAbility().postSummonPriority ]); } /** diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 9effd59ceba..4154593022d 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -7,7 +7,7 @@ import { PokemonPhase } from "./pokemon-phase"; import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Stat } from "#enums/stat"; -import { PriorityAbilityActivationPhase } from "#app/phases/priority-ability-activation-phase"; +import { PostSummonActivateAbilityPhase } from "#app/phases/priority-ability-activation-phase"; export class PostSummonPhase extends PokemonPhase { /** Represents whether or not this phase has already been placed in the correct (speed) order */ @@ -29,24 +29,15 @@ export class PostSummonPhase extends PokemonPhase { globalScene.findPhase(phase => phase instanceof PostSummonPhase && phase.getPokemon() !== pokemon) ) { globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex(), true)); - globalScene.phaseQueue.sort( - (phaseA: PostSummonPhase, phaseB: PostSummonPhase) => - phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD), - ); - globalScene.phaseQueue.forEach((phase: PostSummonPhase) => { - phase.ordered = true; - const phasePokemon = phase.getPokemon(); - if (phasePokemon.hasPriorityAbility()) { - globalScene.unshiftPhase(new PriorityAbilityActivationPhase(this.getPokemon().getBattlerIndex())); - } - }); + this.orderPostSummonPhases(); + this.end(); return; } if (!this.ordered) { - applyPriorityBasedAbAttrs(pokemon, true); + applyPriorityBasedAbAttrs(pokemon, (p: number) => p > 0); } if (pokemon.status?.effect === StatusEffect.TOXIC) { @@ -61,8 +52,9 @@ export class PostSummonPhase extends PokemonPhase { ) { pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); } - - applyPriorityBasedAbAttrs(pokemon, false); + if (!this.ordered) { + applyPriorityBasedAbAttrs(pokemon, (p: number) => p <= 0); + } const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); for (const p of field) { applyAbAttrs(CommanderAbAttr, p, null, false); @@ -70,4 +62,35 @@ export class PostSummonPhase extends PokemonPhase { this.end(); } + + private orderPostSummonPhases() { + globalScene.phaseQueue.sort( + (phaseA: PostSummonPhase, phaseB: PostSummonPhase) => + phaseB.getPokemon().getEffectiveStat(Stat.SPD) - phaseA.getPokemon().getEffectiveStat(Stat.SPD), + ); + + const positivePriorityPhases: PostSummonActivateAbilityPhase[] = []; + const zeroNegativePriorityPhases: PostSummonActivateAbilityPhase[] = []; + + globalScene.phaseQueue.forEach((phase: PostSummonPhase) => { + phase.ordered = true; + const phasePokemon = phase.getPokemon(); + + for (const priority of phasePokemon.getAbilityPriorities()) { + (priority > 0 ? positivePriorityPhases : zeroNegativePriorityPhases).push( + new PostSummonActivateAbilityPhase(phasePokemon.getBattlerIndex(), priority), + ); + } + }); + + for (const phaseList of [positivePriorityPhases, zeroNegativePriorityPhases]) { + phaseList.sort( + (phaseA: PostSummonActivateAbilityPhase, phaseB: PostSummonActivateAbilityPhase) => + phaseB.getPriority() - phaseA.getPriority(), + ); + } + + globalScene.unshiftPhase(...positivePriorityPhases); + zeroNegativePriorityPhases.forEach(phase => globalScene.pushPhase(phase)); + } } diff --git a/src/phases/priority-ability-activation-phase.ts b/src/phases/priority-ability-activation-phase.ts index 2d1ca08ebbc..0d09cb1a1e6 100644 --- a/src/phases/priority-ability-activation-phase.ts +++ b/src/phases/priority-ability-activation-phase.ts @@ -1,19 +1,31 @@ +import type { BattlerIndex } from "#app/battle"; import { applyPriorityBasedAbAttrs } from "#app/data/ability"; import { PokemonPhase } from "#app/phases/pokemon-phase"; /** - * Phase to apply (post-summon) ability attributes for "priority" abilities + * Phase to apply (post-summon) ability attributes for abilities with nonzero priority * * Priority abilities activate before others and before hazards * * @see Example - {@link https://bulbapedia.bulbagarden.net/wiki/Neutralizing_Gas_(Ability) | Neutralizing Gas} */ -export class PriorityAbilityActivationPhase extends PokemonPhase { +export class PostSummonActivateAbilityPhase extends PokemonPhase { + private priority: number; + + constructor(battlerIndex: BattlerIndex, priority: number) { + super(battlerIndex); + this.priority = priority; + } + start() { super.start(); - applyPriorityBasedAbAttrs(this.getPokemon(), true); + applyPriorityBasedAbAttrs(this.getPokemon(), (p: number) => p === this.priority); this.end(); } + + public getPriority() { + return this.priority; + } }