Update to use priority numbers instead of a boolean

This commit is contained in:
Dean 2025-03-12 01:33:05 -07:00
parent d36f2105f1
commit 78882d4be2
4 changed files with 65 additions and 33 deletions

View File

@ -56,18 +56,18 @@ export class Ability implements Localizable {
public name: string; public name: string;
public description: string; public description: string;
public generation: number; public generation: number;
public postSummonPriority: number;
public isBypassFaint: boolean; public isBypassFaint: boolean;
public isIgnorable: boolean; public isIgnorable: boolean;
public isPriority: boolean;
public attrs: AbAttr[]; public attrs: AbAttr[];
public conditions: AbAttrCondition[]; public conditions: AbAttrCondition[];
constructor(id: Abilities, generation: number, isPriority: boolean = false) { constructor(id: Abilities, generation: number, postSummonPriority: number = 0) {
this.id = id; this.id = id;
this.nameAppend = ""; this.nameAppend = "";
this.generation = generation; this.generation = generation;
this.isPriority = isPriority; this.postSummonPriority = postSummonPriority;
this.attrs = []; this.attrs = [];
this.conditions = []; 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 ]) { for (const passive of [ false, true ]) {
const ability: Ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); const ability: Ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
if (ability.isPriority == priority) { if (condition(ability.postSummonPriority)) {
applySingleAbAttrs<PostSummonAbAttr>(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, false, simulated) applySingleAbAttrs<PostSummonAbAttr>(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 .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) new Ability(Abilities.GORILLA_TACTICS, 8)
.attr(GorillaTacticsAbAttr), .attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8, true) new Ability(Abilities.NEUTRALIZING_GAS, 8, 2)
.attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0) .attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0)
.attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr) .attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
@ -6937,14 +6937,14 @@ export function initAbilities() {
.attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1), .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1),
new Ability(Abilities.GRIM_NEIGH, 8) new Ability(Abilities.GRIM_NEIGH, 8)
.attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1), .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(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneGlastrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(PreventBerryUseAbAttr) .attr(PreventBerryUseAbAttr)
.attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1) .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr), .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(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAsOneSpectrier", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
.attr(PreventBerryUseAbAttr) .attr(PreventBerryUseAbAttr)
.attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1) .attr(PostVictoryStatStageChangeAbAttr, Stat.SPATK, 1)

View File

@ -2265,11 +2265,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false; return false;
} }
/** public getAbilityPriorities(): Set<number> {
* @returns If either of the Pokemon's abilities have priority activation return new Set([ this.getAbility().postSummonPriority, this.getPassiveAbility().postSummonPriority ]);
*/
public hasPriorityAbility() {
return [this.getAbility(), this.getPassiveAbility()].some(ability => ability.isPriority);
} }
/** /**

View File

@ -7,7 +7,7 @@ import { PokemonPhase } from "./pokemon-phase";
import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { Stat } from "#enums/stat"; 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 { export class PostSummonPhase extends PokemonPhase {
/** Represents whether or not this phase has already been placed in the correct (speed) order */ /** 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.findPhase(phase => phase instanceof PostSummonPhase && phase.getPokemon() !== pokemon)
) { ) {
globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex(), true)); 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()) { this.orderPostSummonPhases();
globalScene.unshiftPhase(new PriorityAbilityActivationPhase(this.getPokemon().getBattlerIndex()));
}
});
this.end(); this.end();
return; return;
} }
if (!this.ordered) { if (!this.ordered) {
applyPriorityBasedAbAttrs(pokemon, true); applyPriorityBasedAbAttrs(pokemon, (p: number) => p > 0);
} }
if (pokemon.status?.effect === StatusEffect.TOXIC) { if (pokemon.status?.effect === StatusEffect.TOXIC) {
@ -61,8 +52,9 @@ export class PostSummonPhase extends PokemonPhase {
) { ) {
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON); pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
} }
if (!this.ordered) {
applyPriorityBasedAbAttrs(pokemon, false); applyPriorityBasedAbAttrs(pokemon, (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);
@ -70,4 +62,35 @@ export class PostSummonPhase extends PokemonPhase {
this.end(); 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));
}
} }

View File

@ -1,19 +1,31 @@
import type { BattlerIndex } from "#app/battle";
import { applyPriorityBasedAbAttrs } from "#app/data/ability"; import { applyPriorityBasedAbAttrs } from "#app/data/ability";
import { PokemonPhase } from "#app/phases/pokemon-phase"; 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 * Priority abilities activate before others and before hazards
* *
* @see Example - {@link https://bulbapedia.bulbagarden.net/wiki/Neutralizing_Gas_(Ability) | Neutralizing Gas} * @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() { start() {
super.start(); super.start();
applyPriorityBasedAbAttrs(this.getPokemon(), true); applyPriorityBasedAbAttrs(this.getPokemon(), (p: number) => p === this.priority);
this.end(); this.end();
} }
public getPriority() {
return this.priority;
}
} }