mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-20 16:42:45 +02:00
Compare commits
25 Commits
a826029a3e
...
b3473d3932
Author | SHA1 | Date | |
---|---|---|---|
|
b3473d3932 | ||
|
73f113658c | ||
|
1f2b9c0a07 | ||
|
02e08184d0 | ||
|
f9bb930495 | ||
|
3252cfb7a5 | ||
|
250fae9f2f | ||
|
f51a4bfd4b | ||
|
186a2a5985 | ||
|
5bb423810c | ||
|
fdc7e86ee1 | ||
|
d0964c9d56 | ||
|
4ffa42bfdf | ||
|
1ddcf9b0d6 | ||
|
a6c5ff76c2 | ||
|
9e45323402 | ||
|
55e441b56b | ||
|
48f1088250 | ||
|
49a092fdc4 | ||
|
89c209f53e | ||
|
a4aba5d0a8 | ||
|
40e97f1633 | ||
|
4c5c5ca737 | ||
|
ba0115962c | ||
|
4beab5d07a |
@ -1,14 +1,14 @@
|
||||
import type { AbAttr } from "#app/data/abilities/ability";
|
||||
import type Move from "#app/data/moves/move";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { BattleStat } from "#enums/stat";
|
||||
import type { AbAttrConstructorMap } from "#app/data/abilities/ability";
|
||||
|
||||
// Intentionally re-export all types from the ability attributes module
|
||||
// intentionally re-export all types from abilities to have this be the centralized place to import ability types
|
||||
export type * from "#app/data/abilities/ability";
|
||||
|
||||
export type AbAttrApplyFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => void;
|
||||
export type AbAttrSuccessFunc<TAttr extends AbAttr> = (attr: TAttr, passive: boolean, ...args: any[]) => boolean;
|
||||
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
|
||||
import type { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
|
||||
export type AbAttrCondition = (pokemon: Pokemon) => boolean;
|
||||
export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean;
|
||||
export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean;
|
||||
@ -25,3 +25,22 @@ export type AbAttrString = keyof AbAttrConstructorMap;
|
||||
export type AbAttrMap = {
|
||||
[K in keyof AbAttrConstructorMap]: InstanceType<AbAttrConstructorMap[K]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subset of ability attribute classes that may be passed to {@linkcode applyAbAttrs } method
|
||||
*
|
||||
* @remarks
|
||||
* Our AbAttr classes violate Liskov Substitution Principle.
|
||||
*
|
||||
* AbAttrs that are not in this have subclasses with apply methods requiring different parameters than
|
||||
* the base apply method.
|
||||
*
|
||||
* Such attributes may not be passed to the {@linkcode applyAbAttrs} method
|
||||
*/
|
||||
export type CallableAbAttrString =
|
||||
| Exclude<AbAttrString, "PreDefendAbAttr" | "PreAttackAbAttr">
|
||||
| "PreApplyBattlerTagAbAttr";
|
||||
|
||||
export type AbAttrParamMap = {
|
||||
[K in keyof AbAttrMap]: Parameters<AbAttrMap[K]["apply"]>[0];
|
||||
};
|
||||
|
32
src/@types/type-helpers.ts
Normal file
32
src/@types/type-helpers.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* A collection of custom utility types that aid in type checking and ensuring strict type conformity
|
||||
*/
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
|
||||
import type { AbAttr } from "./ability-types";
|
||||
|
||||
/** Exactly matches the type of the argument, preventing adding additional properties.
|
||||
* ⚠️ Should never be used with `extends`, as this will nullify the exactness of the type.
|
||||
* As an example, used to ensure that the parameters of {@linkcode AbAttr#canApply} and {@linkcode AbAttr#getTriggerMessage} are compatible with
|
||||
* the type of the apply method
|
||||
*
|
||||
* @typeParam T - The type to match exactly
|
||||
*/
|
||||
export type Exact<T> = {
|
||||
[K in keyof T]: T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* Type hint that indicates that the type is intended to be closed to a specific shape.
|
||||
* Does not actually do anything special, is really just an alias for X.
|
||||
*
|
||||
*/
|
||||
export type Closed<X> = X;
|
||||
|
||||
/**
|
||||
* Remove `readonly` from all properties of the provided type
|
||||
* @typeParam T - The type to make mutable
|
||||
*/
|
||||
export type Mutable<T> = {
|
||||
-readonly [P in keyof T]: T[P];
|
||||
};
|
@ -67,7 +67,7 @@ import { modifierTypes } from "./data/data-lists";
|
||||
import { getModifierPoolForType } from "./utils/modifier-utils";
|
||||
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||
import AbilityBar from "#app/ui/ability-bar";
|
||||
import { applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs } from "./data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "./data/abilities/apply-ab-attrs";
|
||||
import { allAbilities } from "./data/data-lists";
|
||||
import type { FixedBattleConfig } from "#app/battle";
|
||||
import Battle from "#app/battle";
|
||||
@ -1256,7 +1256,7 @@ export default class BattleScene extends SceneBase {
|
||||
const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8);
|
||||
this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance);
|
||||
for (const p of playerField) {
|
||||
applyAbAttrs("DoubleBattleChanceAbAttr", p, null, false, doubleChance);
|
||||
applyAbAttrs("DoubleBattleChanceAbAttr", { pokemon: p, chance: doubleChance });
|
||||
}
|
||||
return Math.max(doubleChance.value, 1);
|
||||
}
|
||||
@ -1461,7 +1461,7 @@ export default class BattleScene extends SceneBase {
|
||||
for (const pokemon of this.getPlayerParty()) {
|
||||
pokemon.resetBattleAndWaveData();
|
||||
pokemon.resetTera();
|
||||
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||
applyAbAttrs("PostBattleInitAbAttr", { pokemon });
|
||||
if (
|
||||
pokemon.hasSpecies(SpeciesId.TERAPAGOS) ||
|
||||
(this.gameMode.isClassic && this.currentBattle.waveIndex > 180 && this.currentBattle.waveIndex <= 190)
|
||||
@ -2743,7 +2743,7 @@ export default class BattleScene extends SceneBase {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled });
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
@ -2783,13 +2783,13 @@ export default class BattleScene extends SceneBase {
|
||||
if (target.isPlayer()) {
|
||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant);
|
||||
if (source && itemLost) {
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||
applyAbAttrs("PostItemLostAbAttr", { pokemon: source });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant);
|
||||
if (source && itemLost) {
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", source, false);
|
||||
applyAbAttrs("PostItemLostAbAttr", { pokemon: source });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2812,7 +2812,7 @@ export default class BattleScene extends SceneBase {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (source && source.isPlayer() !== target.isPlayer()) {
|
||||
applyAbAttrs("BlockItemTheftAbAttr", source, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", { pokemon: source, cancelled });
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,63 +1,14 @@
|
||||
import type { AbAttrApplyFunc, AbAttrMap, AbAttrString, AbAttrSuccessFunc } from "#app/@types/ability-types";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import type { AbAttrParamMap } from "#app/@types/ability-types";
|
||||
import type { AbAttrBaseParams, AbAttrString, CallableAbAttrString } from "#app/@types/ability-types";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { BooleanHolder, NumberHolder } from "#app/utils/common";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import type { HitResult } from "#enums/hit-result";
|
||||
import type { BattleStat, Stat } from "#enums/stat";
|
||||
import type { StatusEffect } from "#enums/status-effect";
|
||||
import type { WeatherType } from "#enums/weather-type";
|
||||
import type { BattlerTag } from "../battler-tags";
|
||||
import type Move from "../moves/move";
|
||||
import type { PokemonMove } from "../moves/pokemon-move";
|
||||
import type { TerrainType } from "../terrain";
|
||||
import type { Weather } from "../weather";
|
||||
import type {
|
||||
PostBattleInitAbAttr,
|
||||
PreDefendAbAttr,
|
||||
PostDefendAbAttr,
|
||||
PostMoveUsedAbAttr,
|
||||
StatMultiplierAbAttr,
|
||||
AllyStatMultiplierAbAttr,
|
||||
PostSetStatusAbAttr,
|
||||
PostDamageAbAttr,
|
||||
FieldMultiplyStatAbAttr,
|
||||
PreAttackAbAttr,
|
||||
ExecutedMoveAbAttr,
|
||||
PostAttackAbAttr,
|
||||
PostKnockOutAbAttr,
|
||||
PostVictoryAbAttr,
|
||||
PostSummonAbAttr,
|
||||
PreSummonAbAttr,
|
||||
PreSwitchOutAbAttr,
|
||||
PreLeaveFieldAbAttr,
|
||||
PreStatStageChangeAbAttr,
|
||||
PostStatStageChangeAbAttr,
|
||||
PreSetStatusAbAttr,
|
||||
PreApplyBattlerTagAbAttr,
|
||||
PreWeatherEffectAbAttr,
|
||||
PreWeatherDamageAbAttr,
|
||||
PostTurnAbAttr,
|
||||
PostWeatherChangeAbAttr,
|
||||
PostWeatherLapseAbAttr,
|
||||
PostTerrainChangeAbAttr,
|
||||
CheckTrappedAbAttr,
|
||||
PostBattleAbAttr,
|
||||
PostFaintAbAttr,
|
||||
PostItemLostAbAttr,
|
||||
} from "./ability";
|
||||
|
||||
function applySingleAbAttrs<T extends AbAttrString>(
|
||||
pokemon: Pokemon,
|
||||
passive: boolean,
|
||||
attrType: T,
|
||||
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||
args: any[],
|
||||
params: AbAttrParamMap[T],
|
||||
gainedMidTurn = false,
|
||||
simulated = false,
|
||||
messages: string[] = [],
|
||||
) {
|
||||
const { simulated = false, passive = false, pokemon } = params;
|
||||
if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
|
||||
return;
|
||||
}
|
||||
@ -72,10 +23,15 @@ function applySingleAbAttrs<T extends AbAttrString>(
|
||||
return;
|
||||
}
|
||||
|
||||
// typescript assert
|
||||
for (const attr of ability.getAttrs(attrType)) {
|
||||
const condition = attr.getCondition();
|
||||
let abShown = false;
|
||||
if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) {
|
||||
if (
|
||||
(condition && !condition(pokemon)) ||
|
||||
// @ts-ignore: typescript can't unify the type of params with the generic type that was passed
|
||||
!attr.canApply(params)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -85,7 +41,8 @@ function applySingleAbAttrs<T extends AbAttrString>(
|
||||
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true);
|
||||
abShown = true;
|
||||
}
|
||||
const message = attr.getTriggerMessage(pokemon, ability.name, args);
|
||||
// @ts-expect-error - typescript can't unify the type of params with the generic type that was passed
|
||||
const message = attr.getTriggerMessage(params, ability.name);
|
||||
if (message) {
|
||||
if (!simulated) {
|
||||
globalScene.phaseManager.queueMessage(message);
|
||||
@ -93,7 +50,8 @@ function applySingleAbAttrs<T extends AbAttrString>(
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
applyFunc(attr, passive);
|
||||
// @ts-ignore: typescript can't unify the type of params with the generic type that was passed
|
||||
attr.apply(params);
|
||||
|
||||
if (abShown) {
|
||||
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false);
|
||||
@ -107,726 +65,60 @@ function applySingleAbAttrs<T extends AbAttrString>(
|
||||
}
|
||||
}
|
||||
|
||||
function applyAbAttrsInternal<T extends AbAttrString>(
|
||||
function applyAbAttrsInternal<T extends CallableAbAttrString>(
|
||||
attrType: T,
|
||||
pokemon: Pokemon | null,
|
||||
applyFunc: AbAttrApplyFunc<AbAttrMap[T]>,
|
||||
successFunc: AbAttrSuccessFunc<AbAttrMap[T]>,
|
||||
args: any[],
|
||||
simulated = false,
|
||||
params: AbAttrParamMap[T],
|
||||
messages: string[] = [],
|
||||
gainedMidTurn = false,
|
||||
) {
|
||||
// If the pokemon is not defined, no ability attributes to be applied.
|
||||
// TODO: Evaluate whether this check is even necessary anymore
|
||||
if (!params.pokemon) {
|
||||
return;
|
||||
}
|
||||
if (params.passive !== undefined) {
|
||||
applySingleAbAttrs(attrType, params, gainedMidTurn, messages);
|
||||
return;
|
||||
}
|
||||
for (const passive of [false, true]) {
|
||||
if (pokemon) {
|
||||
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages);
|
||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||
}
|
||||
params.passive = passive;
|
||||
applySingleAbAttrs(attrType, params, gainedMidTurn, messages);
|
||||
globalScene.phaseManager.clearPhaseQueueSplice();
|
||||
// We need to restore passive to its original state in case it was undefined earlier
|
||||
// this is necessary in case this method is called with an object that is reused.
|
||||
params.passive = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function applyAbAttrs<T extends AbAttrString>(
|
||||
/**
|
||||
* @param attrType - The type of the ability attribute to apply. (note: may not be any attribute that extends PostSummonAbAttr)
|
||||
* @param params - The parameters to pass to the ability attribute's apply method
|
||||
* @param messages - An optional array to which ability trigger messges will be added
|
||||
*/
|
||||
export function applyAbAttrs<T extends CallableAbAttrString>(
|
||||
attrType: T,
|
||||
pokemon: Pokemon,
|
||||
cancelled: BooleanHolder | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
params: AbAttrParamMap[T],
|
||||
messages?: string[],
|
||||
): void {
|
||||
applyAbAttrsInternal<T>(
|
||||
attrType,
|
||||
pokemon,
|
||||
// @ts-expect-error: TODO: fix the error on `cancelled`
|
||||
(attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args),
|
||||
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
applyAbAttrsInternal(attrType, params, messages);
|
||||
}
|
||||
|
||||
// TODO: Improve the type signatures of the following methods / refactor the apply methods
|
||||
|
||||
export function applyPostBattleInitAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostBattleInitAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostBattleInitAbAttr).applyPostBattleInit(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PostBattleInitAbAttr).canApplyPostBattleInit(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreDefendAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreDefendAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
attacker: Pokemon,
|
||||
move: Move | null,
|
||||
cancelled: BooleanHolder | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PreDefendAbAttr).applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||
(attr, passive) =>
|
||||
(attr as PreDefendAbAttr).canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostDefendAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostDefendAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
attacker: Pokemon,
|
||||
move: Move,
|
||||
hitResult: HitResult | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostDefendAbAttr).applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostDefendAbAttr).canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostMoveUsedAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostMoveUsedAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
move: PokemonMove,
|
||||
source: Pokemon,
|
||||
targets: BattlerIndex[],
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, _passive) => (attr as PostMoveUsedAbAttr).applyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||
(attr, _passive) =>
|
||||
(attr as PostMoveUsedAbAttr).canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends StatMultiplierAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
stat: BattleStat,
|
||||
statValue: NumberHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as StatMultiplierAbAttr).applyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||
(attr, passive) =>
|
||||
(attr as StatMultiplierAbAttr).canApplyStatStage(pokemon, passive, simulated, stat, statValue, args),
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an ally's Stat multiplier attribute
|
||||
* @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being
|
||||
* @param pokemon - The {@linkcode Pokemon} with the ability
|
||||
* @param stat - The type of the checked {@linkcode Stat}
|
||||
* @param statValue - {@linkcode NumberHolder} containing the value of the checked stat
|
||||
* @param checkedPokemon - The {@linkcode Pokemon} with the checked stat
|
||||
* @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move.
|
||||
* @param args - unused
|
||||
*/
|
||||
export function applyAllyStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends AllyStatMultiplierAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
stat: BattleStat,
|
||||
statValue: NumberHolder,
|
||||
simulated = false,
|
||||
checkedPokemon: Pokemon,
|
||||
ignoreAbility: boolean,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as AllyStatMultiplierAbAttr).applyAllyStat(
|
||||
pokemon,
|
||||
passive,
|
||||
simulated,
|
||||
stat,
|
||||
statValue,
|
||||
checkedPokemon,
|
||||
ignoreAbility,
|
||||
args,
|
||||
),
|
||||
(attr, passive) =>
|
||||
(attr as AllyStatMultiplierAbAttr).canApplyAllyStat(
|
||||
pokemon,
|
||||
passive,
|
||||
simulated,
|
||||
stat,
|
||||
statValue,
|
||||
checkedPokemon,
|
||||
ignoreAbility,
|
||||
args,
|
||||
),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostSetStatusAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostSetStatusAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
effect: StatusEffect,
|
||||
sourcePokemon?: Pokemon | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostSetStatusAbAttr).applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostSetStatusAbAttr).canApplyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostDamageAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostDamageAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
damage: number,
|
||||
_passive: boolean,
|
||||
simulated = false,
|
||||
args: any[],
|
||||
source?: Pokemon,
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostDamageAbAttr).applyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||
(attr, passive) => (attr as PostDamageAbAttr).canApplyPostDamage(pokemon, damage, passive, simulated, args, source),
|
||||
args,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Applies a field Stat multiplier attribute
|
||||
* @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
|
||||
* @param pokemon {@linkcode Pokemon} the Pokemon applying this ability
|
||||
* @param stat {@linkcode Stat} the type of the checked stat
|
||||
* @param statValue {@linkcode NumberHolder} the value of the checked stat
|
||||
* @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat
|
||||
* @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat
|
||||
* @param args unused
|
||||
*/
|
||||
|
||||
export function applyFieldStatMultiplierAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends FieldMultiplyStatAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
stat: Stat,
|
||||
statValue: NumberHolder,
|
||||
checkedPokemon: Pokemon,
|
||||
hasApplied: BooleanHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as FieldMultiplyStatAbAttr).applyFieldStat(
|
||||
pokemon,
|
||||
passive,
|
||||
simulated,
|
||||
stat,
|
||||
statValue,
|
||||
checkedPokemon,
|
||||
hasApplied,
|
||||
args,
|
||||
),
|
||||
(attr, passive) =>
|
||||
(attr as FieldMultiplyStatAbAttr).canApplyFieldStat(
|
||||
pokemon,
|
||||
passive,
|
||||
simulated,
|
||||
stat,
|
||||
statValue,
|
||||
checkedPokemon,
|
||||
hasApplied,
|
||||
args,
|
||||
),
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreAttackAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreAttackAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
defender: Pokemon | null,
|
||||
move: Move,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PreAttackAbAttr).applyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||
(attr, passive) => (attr as PreAttackAbAttr).canApplyPreAttack(pokemon, passive, simulated, defender, move, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyExecutedMoveAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends ExecutedMoveAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
attr => (attr as ExecutedMoveAbAttr).applyExecutedMove(pokemon, simulated),
|
||||
attr => (attr as ExecutedMoveAbAttr).canApplyExecutedMove(pokemon, simulated),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostAttackAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostAttackAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
defender: Pokemon,
|
||||
move: Move,
|
||||
hitResult: HitResult | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostAttackAbAttr).applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostAttackAbAttr).canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostKnockOutAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostKnockOutAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
knockedOut: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostKnockOutAbAttr).applyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||
(attr, passive) => (attr as PostKnockOutAbAttr).canApplyPostKnockOut(pokemon, passive, simulated, knockedOut, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostVictoryAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostVictoryAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostVictoryAbAttr).applyPostVictory(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PostVictoryAbAttr).canApplyPostVictory(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostSummonAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostSummonAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
passive = false,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applySingleAbAttrs(
|
||||
pokemon,
|
||||
passive,
|
||||
attrType,
|
||||
(attr, passive) => (attr as PostSummonAbAttr).applyPostSummon(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PostSummonAbAttr).canApplyPostSummon(pokemon, passive, simulated, args),
|
||||
args,
|
||||
false,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreSummonAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreSummonAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PreSummonAbAttr).applyPreSummon(pokemon, passive, args),
|
||||
(attr, passive) => (attr as PreSummonAbAttr).canApplyPreSummon(pokemon, passive, args),
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreSwitchOutAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreSwitchOutAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PreSwitchOutAbAttr).applyPreSwitchOut(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PreSwitchOutAbAttr).canApplyPreSwitchOut(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreLeaveFieldAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreLeaveFieldAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PreLeaveFieldAbAttr).applyPreLeaveField(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PreLeaveFieldAbAttr).canApplyPreLeaveField(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreStatStageChangeAbAttr ? K : never,
|
||||
pokemon: Pokemon | null,
|
||||
stat: BattleStat,
|
||||
cancelled: BooleanHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PreStatStageChangeAbAttr).applyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||
(attr, passive) =>
|
||||
(attr as PreStatStageChangeAbAttr).canApplyPreStatStageChange(pokemon, passive, simulated, stat, cancelled, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostStatStageChangeAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostStatStageChangeAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
stats: BattleStat[],
|
||||
stages: number,
|
||||
selfTarget: boolean,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, _passive) =>
|
||||
(attr as PostStatStageChangeAbAttr).applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args),
|
||||
(attr, _passive) =>
|
||||
(attr as PostStatStageChangeAbAttr).canApplyPostStatStageChange(
|
||||
pokemon,
|
||||
simulated,
|
||||
stats,
|
||||
stages,
|
||||
selfTarget,
|
||||
args,
|
||||
),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreSetStatusAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreSetStatusAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
effect: StatusEffect | undefined,
|
||||
cancelled: BooleanHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PreSetStatusAbAttr).applyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||
(attr, passive) =>
|
||||
(attr as PreSetStatusAbAttr).canApplyPreSetStatus(pokemon, passive, simulated, effect, cancelled, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreApplyBattlerTagAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreApplyBattlerTagAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
tag: BattlerTag,
|
||||
cancelled: BooleanHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PreApplyBattlerTagAbAttr).applyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||
(attr, passive) =>
|
||||
(attr as PreApplyBattlerTagAbAttr).canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPreWeatherEffectAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PreWeatherEffectAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
weather: Weather | null,
|
||||
cancelled: BooleanHolder,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PreWeatherDamageAbAttr).applyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||
(attr, passive) =>
|
||||
(attr as PreWeatherDamageAbAttr).canApplyPreWeatherEffect(pokemon, passive, simulated, weather, cancelled, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostTurnAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostTurnAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostTurnAbAttr).applyPostTurn(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PostTurnAbAttr).canApplyPostTurn(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostWeatherChangeAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostWeatherChangeAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
weather: WeatherType,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostWeatherChangeAbAttr).applyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostWeatherChangeAbAttr).canApplyPostWeatherChange(pokemon, passive, simulated, weather, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostWeatherLapseAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostWeatherLapseAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
weather: Weather | null,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostWeatherLapseAbAttr).applyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostWeatherLapseAbAttr).canApplyPostWeatherLapse(pokemon, passive, simulated, weather, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostTerrainChangeAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostTerrainChangeAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
terrain: TerrainType,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostTerrainChangeAbAttr).applyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostTerrainChangeAbAttr).canApplyPostTerrainChange(pokemon, passive, simulated, terrain, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyCheckTrappedAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends CheckTrappedAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
trapped: BooleanHolder,
|
||||
otherPokemon: Pokemon,
|
||||
messages: string[],
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as CheckTrappedAbAttr).applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||
(attr, passive) =>
|
||||
(attr as CheckTrappedAbAttr).canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args),
|
||||
args,
|
||||
simulated,
|
||||
messages,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostBattleAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostBattleAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) => (attr as PostBattleAbAttr).applyPostBattle(pokemon, passive, simulated, args),
|
||||
(attr, passive) => (attr as PostBattleAbAttr).canApplyPostBattle(pokemon, passive, simulated, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostFaintAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostFaintAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
attacker?: Pokemon,
|
||||
move?: Move,
|
||||
hitResult?: HitResult,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, passive) =>
|
||||
(attr as PostFaintAbAttr).applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||
(attr, passive) =>
|
||||
(attr as PostFaintAbAttr).canApplyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args),
|
||||
args,
|
||||
simulated,
|
||||
);
|
||||
}
|
||||
|
||||
export function applyPostItemLostAbAttrs<K extends AbAttrString>(
|
||||
attrType: AbAttrMap[K] extends PostItemLostAbAttr ? K : never,
|
||||
pokemon: Pokemon,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal(
|
||||
attrType,
|
||||
pokemon,
|
||||
(attr, _passive) => (attr as PostItemLostAbAttr).applyPostItemLost(pokemon, simulated, args),
|
||||
(attr, _passive) => (attr as PostItemLostAbAttr).canApplyPostItemLost(pokemon, simulated, args),
|
||||
args,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies abilities when they become active mid-turn (ability switch)
|
||||
*
|
||||
* Ignores passives as they don't change and shouldn't be reapplied when main abilities change
|
||||
*/
|
||||
export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||
applySingleAbAttrs(
|
||||
pokemon,
|
||||
passive,
|
||||
"PostSummonAbAttr",
|
||||
(attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args),
|
||||
(attr, passive) => attr.canApplyPostSummon(pokemon, passive, simulated, args),
|
||||
args,
|
||||
true,
|
||||
simulated,
|
||||
);
|
||||
export function applyOnGainAbAttrs(params: AbAttrBaseParams): void {
|
||||
applySingleAbAttrs("PostSummonAbAttr", params, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather)
|
||||
*/
|
||||
export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void {
|
||||
applySingleAbAttrs(
|
||||
pokemon,
|
||||
passive,
|
||||
"PreLeaveFieldAbAttr",
|
||||
(attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||
(attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]),
|
||||
args,
|
||||
true,
|
||||
simulated,
|
||||
);
|
||||
export function applyOnLoseAbAttrs(params: AbAttrBaseParams): void {
|
||||
applySingleAbAttrs("PreLeaveFieldAbAttr", params, true);
|
||||
|
||||
applySingleAbAttrs(
|
||||
pokemon,
|
||||
passive,
|
||||
"IllusionBreakAbAttr",
|
||||
(attr, passive) => attr.apply(pokemon, passive, simulated, null, args),
|
||||
(attr, passive) => attr.canApply(pokemon, passive, simulated, args),
|
||||
args,
|
||||
true,
|
||||
simulated,
|
||||
);
|
||||
applySingleAbAttrs("IllusionBreakAbAttr", params, true);
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ export class MistTag extends ArenaTag {
|
||||
if (attacker) {
|
||||
const bypassed = new BooleanHolder(false);
|
||||
// TODO: Allow this to be simulated
|
||||
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, simulated: false, bypassed });
|
||||
if (bypassed.value) {
|
||||
return false;
|
||||
}
|
||||
@ -202,7 +202,7 @@ export class WeakenMoveScreenTag extends ArenaTag {
|
||||
): boolean {
|
||||
if (this.weakenedCategories.includes(moveCategory)) {
|
||||
const bypassed = new BooleanHolder(false);
|
||||
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed });
|
||||
if (bypassed.value) {
|
||||
return false;
|
||||
}
|
||||
@ -758,7 +758,7 @@ class SpikesTag extends ArenaTrapTag {
|
||||
}
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
if (simulated || cancelled.value) {
|
||||
return !cancelled.value;
|
||||
}
|
||||
@ -946,7 +946,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||
|
||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
@ -1003,7 +1003,12 @@ class StickyWebTag extends ArenaTrapTag {
|
||||
override activateTrap(pokemon: Pokemon, simulated: boolean): boolean {
|
||||
if (pokemon.isGrounded()) {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("ProtectStatAbAttr", {
|
||||
pokemon,
|
||||
cancelled,
|
||||
stat: Stat.SPD,
|
||||
stages: -1,
|
||||
});
|
||||
|
||||
if (simulated) {
|
||||
return !cancelled.value;
|
||||
@ -1416,7 +1421,9 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
|
||||
for (const fieldPokemon of globalScene.getField(true)) {
|
||||
if (fieldPokemon && fieldPokemon.id !== pokemon.id) {
|
||||
[true, false].forEach(passive => applyOnLoseAbAttrs(fieldPokemon, passive));
|
||||
// TODO: investigate whether we can just remove the foreach and call `applyAbAttrs` directly, providing
|
||||
// the appropriate attributes (preLEaveField and IllusionBreak)
|
||||
[true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: fieldPokemon, passive }));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1438,7 +1445,10 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
const setter = globalScene
|
||||
.getField()
|
||||
.filter(p => p?.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0];
|
||||
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"));
|
||||
applyOnGainAbAttrs({
|
||||
pokemon: setter,
|
||||
passive: setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1451,7 +1461,7 @@ export class SuppressAbilitiesTag extends ArenaTag {
|
||||
for (const pokemon of globalScene.getField(true)) {
|
||||
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
|
||||
if (pokemon && !pokemon.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false)) {
|
||||
[true, false].forEach(passive => applyOnGainAbAttrs(pokemon, passive));
|
||||
[true, false].forEach(passive => applyOnGainAbAttrs({ pokemon, passive }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,7 +621,7 @@ export class FlinchedTag extends BattlerTag {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
applyAbAttrs("FlinchEffectAbAttr", pokemon, null);
|
||||
applyAbAttrs("FlinchEffectAbAttr", { pokemon });
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -916,7 +916,7 @@ export class SeedTag extends BattlerTag {
|
||||
const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex);
|
||||
if (source) {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
@ -1006,7 +1006,7 @@ export class PowderTag extends BattlerTag {
|
||||
globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER);
|
||||
|
||||
const cancelDamage = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelDamage);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled: cancelDamage });
|
||||
if (!cancelDamage.value) {
|
||||
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
}
|
||||
@ -1056,7 +1056,7 @@ export class NightmareTag extends BattlerTag {
|
||||
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
@ -1409,7 +1409,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
|
||||
phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim);
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
@ -1642,7 +1642,7 @@ export class ContactDamageProtectedTag extends ContactProtectedTag {
|
||||
*/
|
||||
override onContact(attacker: Pokemon, user: Pokemon): void {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: user, cancelled });
|
||||
if (!cancelled.value) {
|
||||
attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), {
|
||||
result: HitResult.INDIRECT,
|
||||
@ -2243,7 +2243,7 @@ export class SaltCuredTag extends BattlerTag {
|
||||
);
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER);
|
||||
@ -2297,7 +2297,7 @@ export class CursedTag extends BattlerTag {
|
||||
);
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
@ -2632,7 +2632,7 @@ export class GulpMissileTag extends BattlerTag {
|
||||
}
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", attacker, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: attacker, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT });
|
||||
@ -3021,14 +3021,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
|
||||
const ret = super.lapse(pokemon, lapseType);
|
||||
|
||||
if (lapseType === BattlerTagLapseType.CUSTOM) {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("ProtectStatAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", pokemon, cancelled, false, pokemon);
|
||||
if (!cancelled.value) {
|
||||
if (pokemon.mysteryEncounterBattleEffects) {
|
||||
pokemon.mysteryEncounterBattleEffects(pokemon);
|
||||
}
|
||||
}
|
||||
pokemon.mysteryEncounterBattleEffects?.(pokemon);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -35,28 +35,28 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||
case BerryType.APICOT:
|
||||
case BerryType.SALAC:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
const hpRatioReq = new NumberHolder(0.25);
|
||||
// Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||
return pokemon.getHpRatio() < threshold.value && pokemon.getStatStage(stat) < 6;
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq });
|
||||
return pokemon.getHpRatio() < hpRatioReq.value && pokemon.getStatStage(stat) < 6;
|
||||
};
|
||||
case BerryType.LANSAT:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||
const hpRatioReq = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq });
|
||||
return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
||||
};
|
||||
case BerryType.STARF:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||
const hpRatioReq = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq });
|
||||
return pokemon.getHpRatio() < 0.25;
|
||||
};
|
||||
case BerryType.LEPPA:
|
||||
return (pokemon: Pokemon) => {
|
||||
const threshold = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", pokemon, null, false, threshold);
|
||||
const hpRatioReq = new NumberHolder(0.25);
|
||||
applyAbAttrs("ReduceBerryUseThresholdAbAttr", { pokemon, hpRatioReq });
|
||||
return !!pokemon.getMoveset().find(m => !m.getPpRatio());
|
||||
};
|
||||
}
|
||||
@ -72,7 +72,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
case BerryType.ENIGMA:
|
||||
{
|
||||
const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4));
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, hpHealed);
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: hpHealed });
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"PokemonHealPhase",
|
||||
consumer.getBattlerIndex(),
|
||||
@ -105,7 +105,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
// Offset BerryType such that LIECHI --> Stat.ATK = 1, GANLON --> Stat.DEF = 2, etc etc.
|
||||
const stat: BattleStat = berryType - BerryType.ENIGMA;
|
||||
const statStages = new NumberHolder(1);
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, statStages);
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: statStages });
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
@ -126,7 +126,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||
{
|
||||
const randStat = randSeedInt(Stat.SPD, Stat.ATK);
|
||||
const stages = new NumberHolder(2);
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", consumer, null, false, stages);
|
||||
applyAbAttrs("DoubleBerryEffectAbAttr", { pokemon: consumer, effectValue: stages });
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"StatStageChangePhase",
|
||||
consumer.getBattlerIndex(),
|
||||
|
@ -33,11 +33,7 @@ import type { ArenaTrapTag } from "../arena-tag";
|
||||
import { WeakenMoveTypeTag } from "../arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
applyPostAttackAbAttrs,
|
||||
applyPostItemLostAbAttrs,
|
||||
applyPreAttackAbAttrs,
|
||||
applyPreDefendAbAttrs
|
||||
applyAbAttrs
|
||||
} from "../abilities/apply-ab-attrs";
|
||||
import { allAbilities, allMoves } from "../data-lists";
|
||||
import {
|
||||
@ -92,6 +88,7 @@ import { isVirtual, MoveUseMode } from "#enums/move-use-mode";
|
||||
import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types";
|
||||
import { applyMoveAttrs } from "./apply-attrs";
|
||||
import { frenzyMissFunc, getMoveTargets } from "./move-utils";
|
||||
import { AbAttrBaseParams, AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "../abilities/ability";
|
||||
|
||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||
export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||
@ -343,7 +340,7 @@ export default abstract class Move implements Localizable {
|
||||
|
||||
const bypassed = new BooleanHolder(false);
|
||||
// TODO: Allow this to be simulated
|
||||
applyAbAttrs("InfiltratorAbAttr", user, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", {pokemon: user, bypassed});
|
||||
|
||||
return !bypassed.value
|
||||
&& !this.hasFlag(MoveFlags.SOUND_BASED)
|
||||
@ -641,7 +638,7 @@ export default abstract class Move implements Localizable {
|
||||
case MoveFlags.IGNORE_ABILITIES:
|
||||
if (user.hasAbilityWithAttr("MoveAbilityBypassAbAttr")) {
|
||||
const abilityEffectsIgnored = new BooleanHolder(false);
|
||||
applyAbAttrs("MoveAbilityBypassAbAttr", user, abilityEffectsIgnored, false, this);
|
||||
applyAbAttrs("MoveAbilityBypassAbAttr", {pokemon: user, cancelled: abilityEffectsIgnored, move: this});
|
||||
if (abilityEffectsIgnored.value) {
|
||||
return true;
|
||||
}
|
||||
@ -758,7 +755,7 @@ export default abstract class Move implements Localizable {
|
||||
const moveAccuracy = new NumberHolder(this.accuracy);
|
||||
|
||||
applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy);
|
||||
applyPreDefendAbAttrs("WonderSkinAbAttr", target, user, this, { value: false }, simulated, moveAccuracy);
|
||||
applyAbAttrs("WonderSkinAbAttr", {pokemon: target, opponent: user, move: this, simulated, accuracy: moveAccuracy});
|
||||
|
||||
if (moveAccuracy.value === -1) {
|
||||
return moveAccuracy.value;
|
||||
@ -801,17 +798,25 @@ export default abstract class Move implements Localizable {
|
||||
const typeChangeMovePowerMultiplier = new NumberHolder(1);
|
||||
const typeChangeHolder = new NumberHolder(this.type);
|
||||
|
||||
applyPreAttackAbAttrs("MoveTypeChangeAbAttr", source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
|
||||
applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier});
|
||||
|
||||
const sourceTeraType = source.getTeraType();
|
||||
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||
power.value = 60;
|
||||
}
|
||||
|
||||
applyPreAttackAbAttrs("VariableMovePowerAbAttr", source, target, this, simulated, power);
|
||||
const abAttrParams: PreAttackModifyPowerAbAttrParams = {
|
||||
pokemon: source,
|
||||
opponent: target,
|
||||
simulated,
|
||||
power,
|
||||
move: this,
|
||||
}
|
||||
|
||||
applyAbAttrs("VariableMovePowerAbAttr", abAttrParams);
|
||||
const ally = source.getAlly();
|
||||
if (!isNullOrUndefined(ally)) {
|
||||
applyPreAttackAbAttrs("AllyMoveCategoryPowerBoostAbAttr", ally, target, this, simulated, power);
|
||||
applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally});
|
||||
}
|
||||
|
||||
const fieldAuras = new Set(
|
||||
@ -823,11 +828,12 @@ export default abstract class Move implements Localizable {
|
||||
.flat(),
|
||||
);
|
||||
for (const aura of fieldAuras) {
|
||||
aura.applyPreAttack(source, null, simulated, target, this, [ power ]);
|
||||
// TODO: Refactor the fieldAura attribute so that its apply method is not directly called
|
||||
aura.apply({pokemon: source, simulated, opponent: target, move: this, power});
|
||||
}
|
||||
|
||||
const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
alliedField.forEach(p => applyPreAttackAbAttrs("UserFieldMoveTypePowerBoostAbAttr", p, target, this, simulated, power));
|
||||
alliedField.forEach(p => applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power}));
|
||||
|
||||
power.value *= typeChangeMovePowerMultiplier.value;
|
||||
|
||||
@ -854,7 +860,7 @@ export default abstract class Move implements Localizable {
|
||||
const priority = new NumberHolder(this.priority);
|
||||
|
||||
applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority);
|
||||
applyAbAttrs("ChangeMovePriorityAbAttr", user, null, simulated, this, priority);
|
||||
applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority});
|
||||
|
||||
return priority.value;
|
||||
}
|
||||
@ -1306,7 +1312,7 @@ export class MoveEffectAttr extends MoveAttr {
|
||||
getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number {
|
||||
const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance);
|
||||
|
||||
applyAbAttrs("MoveEffectChanceMultiplierAbAttr", user, null, !showAbility, moveChance, move);
|
||||
applyAbAttrs("MoveEffectChanceMultiplierAbAttr", {pokemon: user, simulated: !showAbility, chance: moveChance, move});
|
||||
|
||||
if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) {
|
||||
const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
@ -1314,7 +1320,7 @@ export class MoveEffectAttr extends MoveAttr {
|
||||
}
|
||||
|
||||
if (!selfEffect) {
|
||||
applyPreDefendAbAttrs("IgnoreMoveEffectsAbAttr", target, user, null, null, !showAbility, moveChance);
|
||||
applyAbAttrs("IgnoreMoveEffectsAbAttr", {pokemon: target, move, simulated: !showAbility, chance: moveChance});
|
||||
}
|
||||
return moveChance.value;
|
||||
}
|
||||
@ -1692,8 +1698,9 @@ export class RecoilAttr extends MoveEffectAttr {
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
if (!this.unblockable) {
|
||||
applyAbAttrs("BlockRecoilDamageAttr", user, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||
const abAttrParams: AbAttrParamsWithCancel = {pokemon: user, cancelled};
|
||||
applyAbAttrs("BlockRecoilDamageAttr", abAttrParams);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", abAttrParams);
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
@ -1826,7 +1833,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
// Check to see if the Pokemon has an ability that blocks non-direct damage
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled});
|
||||
if (!cancelled.value) {
|
||||
user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message
|
||||
@ -2025,7 +2032,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
if (!isNullOrUndefined(targetAlly)) {
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", targetAlly, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: targetAlly, cancelled});
|
||||
}
|
||||
|
||||
if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) {
|
||||
@ -2397,7 +2404,7 @@ export class MultiHitAttr extends MoveAttr {
|
||||
{
|
||||
const rand = user.randBattleSeedInt(20);
|
||||
const hitValue = new NumberHolder(rand);
|
||||
applyAbAttrs("MaxMultiHitAbAttr", user, null, false, hitValue);
|
||||
applyAbAttrs("MaxMultiHitAbAttr", {pokemon: user, hits: hitValue});
|
||||
if (hitValue.value >= 13) {
|
||||
return 2;
|
||||
} else if (hitValue.value >= 6) {
|
||||
@ -2505,7 +2512,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||
}
|
||||
if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0))
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus, quiet)) {
|
||||
applyPostAttackAbAttrs("ConfusionOnStatusEffectAbAttr", user, target, move, null, false, this.effect);
|
||||
applyAbAttrs("ConfusionOnStatusEffectAbAttr", {pokemon: user, opponent: target, move, effect: this.effect});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2557,7 +2564,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
||||
const statusToApply: StatusEffect | undefined = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined);
|
||||
|
||||
if (target.status) {
|
||||
if (target.status || !statusToApply) {
|
||||
return false;
|
||||
} else {
|
||||
const canSetStatus = target.canSetStatus(statusToApply, true, false, user);
|
||||
@ -2573,7 +2580,8 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
return !target.status && target.canSetStatus(user.status?.effect, true, false, user) ? -10 : 0;
|
||||
const statusToApply = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : undefined);
|
||||
return !target.status && statusToApply && target.canSetStatus(statusToApply, true, false, user) ? -10 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2661,7 +2669,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
|
||||
// Check for abilities that block item theft
|
||||
// TODO: This should not trigger if the target would faint beforehand
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", target, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled});
|
||||
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
@ -2778,8 +2786,8 @@ export class EatBerryAttr extends MoveEffectAttr {
|
||||
protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) {
|
||||
// consumer eats berry, owner triggers unburden and similar effects
|
||||
getBerryEffectFunc(this.chosenBerry.berryType)(consumer);
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", berryOwner, false);
|
||||
applyAbAttrs("HealFromBerryUseAbAttr", consumer, new BooleanHolder(false));
|
||||
applyAbAttrs("PostItemLostAbAttr", {pokemon: berryOwner});
|
||||
applyAbAttrs("HealFromBerryUseAbAttr", {pokemon: consumer});
|
||||
consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest);
|
||||
}
|
||||
}
|
||||
@ -2804,7 +2812,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
// check for abilities that block item theft
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", target, cancelled);
|
||||
applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled});
|
||||
if (cancelled.value === true) {
|
||||
return false;
|
||||
}
|
||||
@ -2818,7 +2826,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
||||
|
||||
// pick a random berry and eat it
|
||||
this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)];
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", target, false);
|
||||
applyAbAttrs("PostItemLostAbAttr", {pokemon: target});
|
||||
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
|
||||
globalScene.phaseManager.queueMessage(message);
|
||||
this.reduceBerryModifier(target);
|
||||
@ -3009,7 +3017,7 @@ export class OneHitKOAttr extends MoveAttr {
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockOneHitKOAbAttr", target, cancelled);
|
||||
applyAbAttrs("BlockOneHitKOAbAttr", {pokemon: target, cancelled});
|
||||
return !cancelled.value && user.level >= target.level;
|
||||
};
|
||||
}
|
||||
@ -5421,7 +5429,7 @@ export class NoEffectAttr extends MoveAttr {
|
||||
|
||||
const crashDamageFunc = (user: Pokemon, move: Move) => {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", user, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled});
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
@ -6420,9 +6428,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined {
|
||||
const blockedByAbility = new BooleanHolder(false);
|
||||
applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility);
|
||||
if (blockedByAbility.value) {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled});
|
||||
if (cancelled.value) {
|
||||
return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) });
|
||||
}
|
||||
}
|
||||
@ -6461,7 +6469,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
}
|
||||
|
||||
const blockedByAbility = new BooleanHolder(false);
|
||||
applyAbAttrs("ForceSwitchOutImmunityAbAttr", target, blockedByAbility);
|
||||
applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled: blockedByAbility});
|
||||
if (blockedByAbility.value) {
|
||||
return false;
|
||||
}
|
||||
@ -7970,7 +7978,7 @@ const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScen
|
||||
|
||||
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", p, cancelled));
|
||||
globalScene.getField(true).map(p=>applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled}));
|
||||
// Queue a message if an ability prevented usage of the move
|
||||
if (cancelled.value) {
|
||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }));
|
||||
|
@ -24,7 +24,7 @@ import { PokemonType } from "#enums/pokemon-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { applyPostBattleInitAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import i18next from "i18next";
|
||||
@ -221,7 +221,7 @@ function endTrainerBattleAndShowDialogue(): Promise<void> {
|
||||
|
||||
// Each trainer battle is supposed to be a new fight, so reset all per-battle activation effects
|
||||
pokemon.resetBattleAndWaveData();
|
||||
applyPostBattleInitAbAttrs("PostBattleInitAbAttr", pokemon);
|
||||
applyAbAttrs("PostBattleInitAbAttr", { pokemon });
|
||||
}
|
||||
|
||||
globalScene.phaseManager.unshiftNew("ShowTrainerPhase");
|
||||
|
@ -20,11 +20,7 @@ import { ArenaTrapTag, getArenaTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { Terrain, TerrainType } from "#app/data/terrain";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
applyPostTerrainChangeAbAttrs,
|
||||
applyPostWeatherChangeAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import Overrides from "#app/overrides";
|
||||
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena";
|
||||
@ -372,7 +368,7 @@ export class Arena {
|
||||
pokemon.findAndRemoveTags(
|
||||
t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather),
|
||||
);
|
||||
applyPostWeatherChangeAbAttrs("PostWeatherChangeAbAttr", pokemon, weather);
|
||||
applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather });
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -461,8 +457,8 @@ export class Arena {
|
||||
pokemon.findAndRemoveTags(
|
||||
t => "terrainTypes" in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain),
|
||||
);
|
||||
applyPostTerrainChangeAbAttrs("PostTerrainChangeAbAttr", pokemon, terrain);
|
||||
applyAbAttrs("TerrainEventTypeChangeAbAttr", pokemon, null, false);
|
||||
applyAbAttrs("PostTerrainChangeAbAttr", { pokemon, terrain });
|
||||
applyAbAttrs("TerrainEventTypeChangeAbAttr", { pokemon });
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -108,23 +108,8 @@ import { WeatherType } from "#enums/weather-type";
|
||||
import { NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
|
||||
import type { Ability } from "#app/data/abilities/ability";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
applyStatMultiplierAbAttrs,
|
||||
applyPreApplyBattlerTagAbAttrs,
|
||||
applyPreAttackAbAttrs,
|
||||
applyPreDefendAbAttrs,
|
||||
applyPreSetStatusAbAttrs,
|
||||
applyFieldStatMultiplierAbAttrs,
|
||||
applyCheckTrappedAbAttrs,
|
||||
applyPostDamageAbAttrs,
|
||||
applyPostItemLostAbAttrs,
|
||||
applyOnGainAbAttrs,
|
||||
applyPreLeaveFieldAbAttrs,
|
||||
applyOnLoseAbAttrs,
|
||||
applyAllyStatMultiplierAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import type { Ability, PreAttackModifyDamageAbAttrParams } from "#app/data/abilities/ability";
|
||||
import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
@ -189,7 +174,7 @@ import { HitResult } from "#enums/hit-result";
|
||||
import { AiType } from "#enums/ai-type";
|
||||
import type { MoveResult } from "#enums/move-result";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
import type { AbAttrMap, AbAttrString } from "#app/@types/ability-types";
|
||||
import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#app/@types/ability-types";
|
||||
|
||||
/** Base typeclass for damage parameter methods, used for DRY */
|
||||
type damageParams = {
|
||||
@ -1364,7 +1349,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyMoveAttrs("HighCritAttr", source, this, move, critStage);
|
||||
globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||
globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||
applyAbAttrs("BonusCritAbAttr", source, null, false, critStage);
|
||||
applyAbAttrs("BonusCritAbAttr", { pokemon: source, critStage });
|
||||
const critBoostTag = source.getTag(CritBoostTag);
|
||||
if (critBoostTag) {
|
||||
// Dragon cheer only gives +1 crit stage to non-dragon types
|
||||
@ -1415,46 +1400,52 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
simulated = true,
|
||||
ignoreHeldItems = false,
|
||||
): number {
|
||||
const statValue = new NumberHolder(this.getStat(stat, false));
|
||||
const statVal = new NumberHolder(this.getStat(stat, false));
|
||||
if (!ignoreHeldItems) {
|
||||
globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue);
|
||||
globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statVal);
|
||||
}
|
||||
|
||||
// The Ruin abilities here are never ignored, but they reveal themselves on summon anyway
|
||||
const fieldApplied = new BooleanHolder(false);
|
||||
for (const pokemon of globalScene.getField(true)) {
|
||||
applyFieldStatMultiplierAbAttrs(
|
||||
"FieldMultiplyStatAbAttr",
|
||||
applyAbAttrs("FieldMultiplyStatAbAttr", {
|
||||
pokemon,
|
||||
stat,
|
||||
statValue,
|
||||
this,
|
||||
fieldApplied,
|
||||
statVal,
|
||||
target: this,
|
||||
hasApplied: fieldApplied,
|
||||
simulated,
|
||||
);
|
||||
});
|
||||
if (fieldApplied.value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ignoreAbility) {
|
||||
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, stat, statValue, simulated);
|
||||
applyAbAttrs("StatMultiplierAbAttr", {
|
||||
pokemon: this,
|
||||
stat,
|
||||
statVal,
|
||||
simulated,
|
||||
// TODO: maybe just don't call this if the move is none?
|
||||
move: move ?? allMoves[MoveId.NONE],
|
||||
});
|
||||
}
|
||||
|
||||
const ally = this.getAlly();
|
||||
if (!isNullOrUndefined(ally)) {
|
||||
applyAllyStatMultiplierAbAttrs(
|
||||
"AllyStatMultiplierAbAttr",
|
||||
ally,
|
||||
applyAbAttrs("AllyStatMultiplierAbAttr", {
|
||||
pokemon: ally,
|
||||
stat,
|
||||
statValue,
|
||||
statVal,
|
||||
simulated,
|
||||
this,
|
||||
move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility,
|
||||
);
|
||||
// TODO: maybe just don't call this if the move is none?
|
||||
move: move ?? allMoves[MoveId.NONE],
|
||||
ignoreAbility: move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility,
|
||||
});
|
||||
}
|
||||
|
||||
let ret =
|
||||
statValue.value *
|
||||
statVal.value *
|
||||
this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems);
|
||||
|
||||
switch (stat) {
|
||||
@ -2045,20 +2036,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @param ability New Ability
|
||||
*/
|
||||
public setTempAbility(ability: Ability, passive = false): void {
|
||||
applyOnLoseAbAttrs(this, passive);
|
||||
applyOnLoseAbAttrs({ pokemon: this, passive });
|
||||
if (passive) {
|
||||
this.summonData.passiveAbility = ability.id;
|
||||
} else {
|
||||
this.summonData.ability = ability.id;
|
||||
}
|
||||
applyOnGainAbAttrs(this, passive);
|
||||
applyOnGainAbAttrs({ pokemon: this, passive });
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppresses an ability and calls its onlose attributes
|
||||
*/
|
||||
public suppressAbility() {
|
||||
[true, false].forEach(passive => applyOnLoseAbAttrs(this, passive));
|
||||
[true, false].forEach(passive => applyOnLoseAbAttrs({ pokemon: this, passive }));
|
||||
this.summonData.abilitySuppressed = true;
|
||||
}
|
||||
|
||||
@ -2194,7 +2185,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const weight = new NumberHolder(this.species.weight - weightRemoved);
|
||||
|
||||
// This will trigger the ability overlay so only call this function when necessary
|
||||
applyAbAttrs("WeightMultiplierAbAttr", this, null, false, weight);
|
||||
applyAbAttrs("WeightMultiplierAbAttr", { pokemon: this, weight });
|
||||
return Math.max(minWeight, weight.value);
|
||||
}
|
||||
|
||||
@ -2256,7 +2247,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return false;
|
||||
}
|
||||
|
||||
const trappedByAbility = new BooleanHolder(false);
|
||||
/** Holds whether the pokemon is trapped due to an ability */
|
||||
const trapped = new BooleanHolder(false);
|
||||
/**
|
||||
* Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective
|
||||
* Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger
|
||||
@ -2265,14 +2257,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
||||
|
||||
for (const opponent of opposingField) {
|
||||
applyCheckTrappedAbAttrs("CheckTrappedAbAttr", opponent, trappedByAbility, this, trappedAbMessages, simulated);
|
||||
applyAbAttrs("CheckTrappedAbAttr", { pokemon: opponent, trapped, opponent: this, simulated }, trappedAbMessages);
|
||||
}
|
||||
|
||||
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
return (
|
||||
trappedByAbility.value ||
|
||||
!!this.getTag(TrappedTag) ||
|
||||
!!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side)
|
||||
trapped.value || !!this.getTag(TrappedTag) || !!globalScene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side)
|
||||
);
|
||||
}
|
||||
|
||||
@ -2287,7 +2277,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const moveTypeHolder = new NumberHolder(move.type);
|
||||
|
||||
applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder);
|
||||
applyPreAttackAbAttrs("MoveTypeChangeAbAttr", this, null, move, simulated, moveTypeHolder);
|
||||
|
||||
const power = new NumberHolder(move.power);
|
||||
applyAbAttrs("MoveTypeChangeAbAttr", {
|
||||
pokemon: this,
|
||||
move,
|
||||
simulated,
|
||||
moveType: moveTypeHolder,
|
||||
power,
|
||||
opponent: this,
|
||||
});
|
||||
|
||||
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
|
||||
// then bypass the check for ion deluge and electrify
|
||||
@ -2351,17 +2350,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
const cancelledHolder = cancelled ?? new BooleanHolder(false);
|
||||
// TypeMultiplierAbAttrParams is shared amongst the type of AbAttrs we will be invoking
|
||||
const commonAbAttrParams: TypeMultiplierAbAttrParams = {
|
||||
pokemon: this,
|
||||
opponent: source,
|
||||
move,
|
||||
cancelled: cancelledHolder,
|
||||
simulated,
|
||||
typeMultiplier,
|
||||
};
|
||||
if (!ignoreAbility) {
|
||||
applyPreDefendAbAttrs("TypeImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||
applyAbAttrs("TypeImmunityAbAttr", commonAbAttrParams);
|
||||
|
||||
if (!cancelledHolder.value) {
|
||||
applyPreDefendAbAttrs("MoveImmunityAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||
applyAbAttrs("MoveImmunityAbAttr", commonAbAttrParams);
|
||||
}
|
||||
|
||||
if (!cancelledHolder.value) {
|
||||
const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
defendingSidePlayField.forEach(p =>
|
||||
applyPreDefendAbAttrs("FieldPriorityMoveImmunityAbAttr", p, source, move, cancelledHolder),
|
||||
applyAbAttrs("FieldPriorityMoveImmunityAbAttr", {
|
||||
pokemon: p,
|
||||
opponent: source,
|
||||
move,
|
||||
cancelled: cancelledHolder,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -2376,7 +2389,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
// Apply Tera Shell's effect to attacks after all immunities are accounted for
|
||||
if (!ignoreAbility && move.category !== MoveCategory.STATUS) {
|
||||
applyPreDefendAbAttrs("FullHpResistTypeAbAttr", this, source, move, cancelledHolder, simulated, typeMultiplier);
|
||||
applyAbAttrs("FullHpResistTypeAbAttr", commonAbAttrParams);
|
||||
}
|
||||
|
||||
if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) {
|
||||
@ -2420,16 +2433,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
let multiplier = types
|
||||
.map(defType => {
|
||||
const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
||||
.map(defenderType => {
|
||||
const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defenderType));
|
||||
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||
if (move) {
|
||||
applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defType);
|
||||
applyMoveAttrs("VariableMoveTypeChartAttr", null, this, move, multiplier, defenderType);
|
||||
}
|
||||
if (source) {
|
||||
const ignoreImmunity = new BooleanHolder(false);
|
||||
if (source.isActive(true) && source.hasAbilityWithAttr("IgnoreTypeImmunityAbAttr")) {
|
||||
applyAbAttrs("IgnoreTypeImmunityAbAttr", source, ignoreImmunity, simulated, moveType, defType);
|
||||
applyAbAttrs("IgnoreTypeImmunityAbAttr", {
|
||||
pokemon: source,
|
||||
cancelled: ignoreImmunity,
|
||||
simulated,
|
||||
moveType,
|
||||
defenderType,
|
||||
});
|
||||
}
|
||||
if (ignoreImmunity.value) {
|
||||
if (multiplier.value === 0) {
|
||||
@ -2438,7 +2457,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[];
|
||||
if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) {
|
||||
if (exposedTags.some(t => t.ignoreImmunity(defenderType, moveType))) {
|
||||
if (multiplier.value === 0) {
|
||||
return 1;
|
||||
}
|
||||
@ -3358,7 +3377,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
if (!ignoreOppAbility) {
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", opponent, null, simulated, stat, ignoreStatStage);
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", {
|
||||
pokemon: opponent,
|
||||
ignored: ignoreStatStage,
|
||||
stat,
|
||||
simulated,
|
||||
});
|
||||
}
|
||||
if (move) {
|
||||
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, opponent, move, ignoreStatStage);
|
||||
@ -3397,8 +3421,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const ignoreAccStatStage = new BooleanHolder(false);
|
||||
const ignoreEvaStatStage = new BooleanHolder(false);
|
||||
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", target, null, false, Stat.ACC, ignoreAccStatStage);
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", this, null, false, Stat.EVA, ignoreEvaStatStage);
|
||||
// TODO: consider refactoring this method to accept `simulated` and then pass simulated to these applyAbAttrs
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: target, stat: Stat.ACC, ignored: ignoreAccStatStage });
|
||||
applyAbAttrs("IgnoreOpponentStatStagesAbAttr", { pokemon: this, stat: Stat.EVA, ignored: ignoreEvaStatStage });
|
||||
applyMoveAttrs("IgnoreOpponentStatStagesAttr", this, target, sourceMove, ignoreEvaStatStage);
|
||||
|
||||
globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage);
|
||||
@ -3418,33 +3443,40 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
: 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6));
|
||||
}
|
||||
|
||||
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", this, Stat.ACC, accuracyMultiplier, false, sourceMove);
|
||||
applyAbAttrs("StatMultiplierAbAttr", {
|
||||
pokemon: this,
|
||||
stat: Stat.ACC,
|
||||
statVal: accuracyMultiplier,
|
||||
move: sourceMove,
|
||||
});
|
||||
|
||||
const evasionMultiplier = new NumberHolder(1);
|
||||
applyStatMultiplierAbAttrs("StatMultiplierAbAttr", target, Stat.EVA, evasionMultiplier);
|
||||
applyAbAttrs("StatMultiplierAbAttr", {
|
||||
pokemon: target,
|
||||
stat: Stat.EVA,
|
||||
statVal: evasionMultiplier,
|
||||
move: sourceMove,
|
||||
});
|
||||
|
||||
const ally = this.getAlly();
|
||||
if (!isNullOrUndefined(ally)) {
|
||||
const ignore =
|
||||
this.hasAbilityWithAttr("MoveAbilityBypassAbAttr") || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES);
|
||||
applyAllyStatMultiplierAbAttrs(
|
||||
"AllyStatMultiplierAbAttr",
|
||||
ally,
|
||||
Stat.ACC,
|
||||
accuracyMultiplier,
|
||||
false,
|
||||
this,
|
||||
ignore,
|
||||
);
|
||||
applyAllyStatMultiplierAbAttrs(
|
||||
"AllyStatMultiplierAbAttr",
|
||||
ally,
|
||||
Stat.EVA,
|
||||
evasionMultiplier,
|
||||
false,
|
||||
this,
|
||||
ignore,
|
||||
);
|
||||
applyAbAttrs("AllyStatMultiplierAbAttr", {
|
||||
pokemon: ally,
|
||||
stat: Stat.ACC,
|
||||
statVal: accuracyMultiplier,
|
||||
ignoreAbility: ignore,
|
||||
move: sourceMove,
|
||||
});
|
||||
|
||||
applyAbAttrs("AllyStatMultiplierAbAttr", {
|
||||
pokemon: ally,
|
||||
stat: Stat.EVA,
|
||||
statVal: evasionMultiplier,
|
||||
ignoreAbility: ignore,
|
||||
move: sourceMove,
|
||||
});
|
||||
}
|
||||
|
||||
return accuracyMultiplier.value / evasionMultiplier.value;
|
||||
@ -3559,7 +3591,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyMoveAttrs("CombinedPledgeStabBoostAttr", source, this, move, stabMultiplier);
|
||||
|
||||
if (!ignoreSourceAbility) {
|
||||
applyAbAttrs("StabBoostAbAttr", source, null, simulated, stabMultiplier);
|
||||
applyAbAttrs("StabBoostAbAttr", { pokemon: source, simulated, multiplier: stabMultiplier });
|
||||
}
|
||||
|
||||
if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) {
|
||||
@ -3706,16 +3738,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
null,
|
||||
multiStrikeEnhancementMultiplier,
|
||||
);
|
||||
|
||||
if (!ignoreSourceAbility) {
|
||||
applyPreAttackAbAttrs(
|
||||
"AddSecondStrikeAbAttr",
|
||||
source,
|
||||
this,
|
||||
applyAbAttrs("AddSecondStrikeAbAttr", {
|
||||
pokemon: source,
|
||||
move,
|
||||
simulated,
|
||||
null,
|
||||
multiStrikeEnhancementMultiplier,
|
||||
);
|
||||
multiplier: multiStrikeEnhancementMultiplier,
|
||||
});
|
||||
}
|
||||
|
||||
/** Doubles damage if this Pokemon's last move was Glaive Rush */
|
||||
@ -3726,7 +3756,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
/** The damage multiplier when the given move critically hits */
|
||||
const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1);
|
||||
applyAbAttrs("MultCritAbAttr", source, null, simulated, criticalMultiplier);
|
||||
applyAbAttrs("MultCritAbAttr", { pokemon: source, simulated, critMult: criticalMultiplier });
|
||||
|
||||
/**
|
||||
* A multiplier for random damage spread in the range [0.85, 1]
|
||||
@ -3747,7 +3777,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
) {
|
||||
const burnDamageReductionCancelled = new BooleanHolder(false);
|
||||
if (!ignoreSourceAbility) {
|
||||
applyAbAttrs("BypassBurnDamageReductionAbAttr", source, burnDamageReductionCancelled, simulated);
|
||||
applyAbAttrs("BypassBurnDamageReductionAbAttr", {
|
||||
pokemon: source,
|
||||
cancelled: burnDamageReductionCancelled,
|
||||
simulated,
|
||||
});
|
||||
}
|
||||
if (!burnDamageReductionCancelled.value) {
|
||||
burnMultiplier = 0.5;
|
||||
@ -3811,7 +3845,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
|
||||
if (!ignoreSourceAbility) {
|
||||
applyPreAttackAbAttrs("DamageBoostAbAttr", source, this, move, simulated, damage);
|
||||
applyAbAttrs("DamageBoostAbAttr", {
|
||||
pokemon: source,
|
||||
opponent: this,
|
||||
move,
|
||||
simulated,
|
||||
damage,
|
||||
});
|
||||
}
|
||||
|
||||
/** Apply the enemy's Damage and Resistance tokens */
|
||||
@ -3822,14 +3862,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
globalScene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||
}
|
||||
|
||||
const abAttrParams: PreAttackModifyDamageAbAttrParams = {
|
||||
pokemon: this,
|
||||
opponent: source,
|
||||
move,
|
||||
simulated,
|
||||
damage,
|
||||
};
|
||||
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
|
||||
if (!ignoreAbility) {
|
||||
applyPreDefendAbAttrs("ReceivedMoveDamageMultiplierAbAttr", this, source, move, cancelled, simulated, damage);
|
||||
applyAbAttrs("ReceivedMoveDamageMultiplierAbAttr", abAttrParams);
|
||||
|
||||
const ally = this.getAlly();
|
||||
/** Additionally apply friend guard damage reduction if ally has it. */
|
||||
if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) {
|
||||
applyPreDefendAbAttrs("AlliedFieldDamageReductionAbAttr", ally, source, move, cancelled, simulated, damage);
|
||||
applyAbAttrs("AlliedFieldDamageReductionAbAttr", {
|
||||
...abAttrParams,
|
||||
// Same parameters as before, except we are applying the ally's ability
|
||||
pokemon: ally,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3837,7 +3888,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyMoveAttrs("ModifiedDamageAttr", source, this, move, damage);
|
||||
|
||||
if (this.isFullHp() && !ignoreAbility) {
|
||||
applyPreDefendAbAttrs("PreDefendFullHpEndureAbAttr", this, source, move, cancelled, false, damage);
|
||||
applyAbAttrs("PreDefendFullHpEndureAbAttr", abAttrParams);
|
||||
}
|
||||
|
||||
// debug message for when damage is applied (i.e. not simulated)
|
||||
@ -3875,7 +3926,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
const alwaysCrit = new BooleanHolder(false);
|
||||
applyMoveAttrs("CritOnlyAttr", source, this, move, alwaysCrit);
|
||||
applyAbAttrs("ConditionalCritAbAttr", source, null, false, alwaysCrit, this, move);
|
||||
applyAbAttrs("ConditionalCritAbAttr", { pokemon: source, isCritical: alwaysCrit, target: this, move });
|
||||
const alwaysCritTag = !!source.getTag(BattlerTagType.ALWAYS_CRIT);
|
||||
const critChance = [24, 8, 2, 1][Phaser.Math.Clamp(this.getCritStage(source, move), 0, 3)];
|
||||
|
||||
@ -3886,7 +3937,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
// apply crit block effects from lucky chant & co., overriding previous effects
|
||||
const blockCrit = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockCritAbAttr", this, null, false, blockCrit);
|
||||
applyAbAttrs("BlockCritAbAttr", { pokemon: this, blockCrit });
|
||||
const blockCritTag = globalScene.arena.getTagOnSide(
|
||||
NoCritTag,
|
||||
this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY,
|
||||
@ -3998,7 +4049,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr
|
||||
*/
|
||||
if (!source || source.turnData.hitCount <= 1) {
|
||||
applyPostDamageAbAttrs("PostDamageAbAttr", this, damage, this.hasPassive(), false, [], source);
|
||||
applyAbAttrs("PostDamageAbAttr", { pokemon: this, damage, source });
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
@ -4046,11 +4097,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const stubTag = new BattlerTag(tagType, 0, 0);
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, stubTag, cancelled, true);
|
||||
applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: stubTag, cancelled, simulated: true });
|
||||
|
||||
const userField = this.getAlliedField();
|
||||
userField.forEach(pokemon =>
|
||||
applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, stubTag, cancelled, true, this),
|
||||
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", {
|
||||
pokemon,
|
||||
tag: stubTag,
|
||||
cancelled,
|
||||
simulated: true,
|
||||
target: this,
|
||||
}),
|
||||
);
|
||||
|
||||
return !cancelled.value;
|
||||
@ -4066,13 +4123,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct?
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyPreApplyBattlerTagAbAttrs("BattlerTagImmunityAbAttr", this, newTag, cancelled);
|
||||
applyAbAttrs("BattlerTagImmunityAbAttr", { pokemon: this, tag: newTag, cancelled });
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const pokemon of this.getAlliedField()) {
|
||||
applyPreApplyBattlerTagAbAttrs("UserFieldBattlerTagImmunityAbAttr", pokemon, newTag, cancelled, false, this);
|
||||
applyAbAttrs("UserFieldBattlerTagImmunityAbAttr", { pokemon, tag: newTag, cancelled, target: this });
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
@ -4597,7 +4654,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* @param ignoreField Whether any field effects (weather, terrain, etc.) should be considered
|
||||
*/
|
||||
canSetStatus(
|
||||
effect: StatusEffect | undefined,
|
||||
effect: StatusEffect,
|
||||
quiet = false,
|
||||
overrideStatus = false,
|
||||
sourcePokemon: Pokemon | null = null,
|
||||
@ -4628,8 +4685,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
// Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity
|
||||
const cancelImmunity = new BooleanHolder(false);
|
||||
// TODO: Determine if we need to pass `quiet` as the value for simulated in this call
|
||||
if (sourcePokemon) {
|
||||
applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", sourcePokemon, cancelImmunity, false, effect, defType);
|
||||
applyAbAttrs("IgnoreTypeStatusEffectImmunityAbAttr", {
|
||||
pokemon: sourcePokemon,
|
||||
cancelled: cancelImmunity,
|
||||
statusEffect: effect,
|
||||
defenderType: defType,
|
||||
});
|
||||
if (cancelImmunity.value) {
|
||||
return false;
|
||||
}
|
||||
@ -4678,21 +4741,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyPreSetStatusAbAttrs("StatusEffectImmunityAbAttr", this, effect, cancelled, quiet);
|
||||
applyAbAttrs("StatusEffectImmunityAbAttr", { pokemon: this, effect, cancelled, simulated: quiet });
|
||||
if (cancelled.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const pokemon of this.getAlliedField()) {
|
||||
applyPreSetStatusAbAttrs(
|
||||
"UserFieldStatusEffectImmunityAbAttr",
|
||||
applyAbAttrs("UserFieldStatusEffectImmunityAbAttr", {
|
||||
pokemon,
|
||||
effect,
|
||||
cancelled,
|
||||
quiet,
|
||||
this,
|
||||
sourcePokemon,
|
||||
);
|
||||
simulated: quiet,
|
||||
target: this,
|
||||
source: sourcePokemon,
|
||||
});
|
||||
if (cancelled.value) {
|
||||
break;
|
||||
}
|
||||
@ -4723,6 +4785,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
overrideStatus?: boolean,
|
||||
quiet = true,
|
||||
): boolean {
|
||||
if (!effect) {
|
||||
return false;
|
||||
}
|
||||
if (!this.canSetStatus(effect, quiet, overrideStatus, sourcePokemon)) {
|
||||
return false;
|
||||
}
|
||||
@ -4781,7 +4846,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
sleepTurnsRemaining = sleepTurnsRemaining!; // tell TS compiler it's defined
|
||||
effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call
|
||||
this.status = new Status(effect, 0, sleepTurnsRemaining?.value);
|
||||
|
||||
return true;
|
||||
@ -4842,7 +4906,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) {
|
||||
const bypassed = new BooleanHolder(false);
|
||||
if (attacker) {
|
||||
applyAbAttrs("InfiltratorAbAttr", attacker, null, false, bypassed);
|
||||
applyAbAttrs("InfiltratorAbAttr", { pokemon: attacker, bypassed });
|
||||
}
|
||||
return !bypassed.value;
|
||||
}
|
||||
@ -5391,7 +5455,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.hideInfo();
|
||||
}
|
||||
// Trigger abilities that activate upon leaving the field
|
||||
applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", this);
|
||||
applyAbAttrs("PreLeaveFieldAbAttr", { pokemon: this });
|
||||
this.setSwitchOutStatus(true);
|
||||
globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true);
|
||||
globalScene.field.remove(this, destroy);
|
||||
@ -5451,7 +5515,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
globalScene.removeModifier(heldItem, this.isEnemy());
|
||||
}
|
||||
if (forBattle) {
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", this, false);
|
||||
applyAbAttrs("PostItemLostAbAttr", { pokemon: this });
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -42,7 +42,7 @@ import type {
|
||||
import { getModifierType } from "#app/utils/modifier-utils";
|
||||
import { Color, ShadowColor } from "#enums/color";
|
||||
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters";
|
||||
import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types";
|
||||
|
||||
@ -1879,7 +1879,7 @@ export class BerryModifier extends PokemonHeldItemModifier {
|
||||
|
||||
// munch the berry and trigger unburden-like effects
|
||||
getBerryEffectFunc(this.berryType)(pokemon);
|
||||
applyPostItemLostAbAttrs("PostItemLostAbAttr", pokemon, false);
|
||||
applyAbAttrs("PostItemLostAbAttr", { pokemon });
|
||||
|
||||
// Update berry eaten trackers for Belch, Harvest, Cud Chew, etc.
|
||||
// Don't recover it if we proc berry pouch (no item duplication)
|
||||
@ -1967,7 +1967,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
|
||||
// Reapply Commander on the Pokemon's side of the field, if applicable
|
||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
for (const p of field) {
|
||||
applyAbAttrs("CommanderAbAttr", p, null, false);
|
||||
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { applyAbAttrs, applyPreLeaveFieldAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
|
||||
@ -25,10 +25,10 @@ export class AttemptRunPhase extends PokemonPhase {
|
||||
|
||||
this.attemptRunAway(playerField, enemyField, escapeChance);
|
||||
|
||||
applyAbAttrs("RunSuccessAbAttr", playerPokemon, null, false, escapeChance);
|
||||
applyAbAttrs("RunSuccessAbAttr", { pokemon: playerPokemon, chance: escapeChance });
|
||||
|
||||
if (playerPokemon.randBattleSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
|
||||
enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs("PreLeaveFieldAbAttr", enemyPokemon));
|
||||
enemyField.forEach(enemyPokemon => applyAbAttrs("PreLeaveFieldAbAttr", { pokemon: enemyPokemon }));
|
||||
|
||||
globalScene.playSound("se/flee");
|
||||
globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
||||
@ -38,14 +38,11 @@ export class AttemptRunPhase extends PokemonPhase {
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
ease: "Sine.easeIn",
|
||||
onComplete: () =>
|
||||
// biome-ignore lint/complexity/noForEach: TODO
|
||||
enemyField.forEach(enemyPokemon => enemyPokemon.destroy()),
|
||||
onComplete: () => enemyField.forEach(enemyPokemon => enemyPokemon.destroy()),
|
||||
});
|
||||
|
||||
globalScene.clearEnemyHeldItemModifiers();
|
||||
|
||||
// biome-ignore lint/complexity/noForEach: TODO
|
||||
enemyField.forEach(enemyPokemon => {
|
||||
enemyPokemon.hideInfo().then(() => enemyPokemon.destroy());
|
||||
enemyPokemon.hp = 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||
import { BattlePhase } from "./battle-phase";
|
||||
|
||||
@ -65,7 +65,7 @@ export class BattleEndPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
|
||||
applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory);
|
||||
applyAbAttrs("PostBattleAbAttr", { pokemon, victory: this.isVictory });
|
||||
}
|
||||
|
||||
if (globalScene.currentBattle.moneyScattered) {
|
||||
|
@ -20,7 +20,7 @@ export class BerryPhase extends FieldPhase {
|
||||
|
||||
this.executeForAll(pokemon => {
|
||||
this.eatBerries(pokemon);
|
||||
applyAbAttrs("RepeatBerryNextTurnAbAttr", pokemon, null);
|
||||
applyAbAttrs("CudChewConsumeBerryAbAttr", { pokemon });
|
||||
});
|
||||
|
||||
this.end();
|
||||
@ -42,7 +42,7 @@ export class BerryPhase extends FieldPhase {
|
||||
|
||||
// TODO: If both opponents on field have unnerve, which one displays its message?
|
||||
const cancelled = new BooleanHolder(false);
|
||||
pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", opp, cancelled));
|
||||
pokemon.getOpponents().forEach(opp => applyAbAttrs("PreventBerryUseAbAttr", { pokemon: opp, cancelled }));
|
||||
if (cancelled.value) {
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("abilityTriggers:preventBerryUse", {
|
||||
@ -70,6 +70,6 @@ export class BerryPhase extends FieldPhase {
|
||||
globalScene.updateModifiers(pokemon.isPlayer());
|
||||
|
||||
// AbilityId.CHEEK_POUCH only works once per round of nom noms
|
||||
applyAbAttrs("HealFromBerryUseAbAttr", pokemon, new BooleanHolder(false));
|
||||
applyAbAttrs("HealFromBerryUseAbAttr", { pokemon });
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||
import { applyAbAttrs, applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
@ -128,7 +128,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
.slice(0, !battle.double ? 1 : 2)
|
||||
.reverse()
|
||||
.forEach(playerPokemon => {
|
||||
applyAbAttrs("SyncEncounterNatureAbAttr", playerPokemon, null, false, battle.enemyParty[e]);
|
||||
applyAbAttrs("SyncEncounterNatureAbAttr", { pokemon: playerPokemon, target: battle.enemyParty[e] });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -249,7 +249,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
if (e < (battle.double ? 2 : 1)) {
|
||||
if (battle.battleType === BattleType.WILD) {
|
||||
for (const pokemon of globalScene.getField()) {
|
||||
applyPreSummonAbAttrs("PreSummonAbAttr", pokemon, []);
|
||||
applyAbAttrs("PreSummonAbAttr", { pokemon });
|
||||
}
|
||||
globalScene.field.add(enemyPokemon);
|
||||
battle.seenEnemyPartyMemberIds.add(enemyPokemon.id);
|
||||
|
@ -1,11 +1,7 @@
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattleType } from "#enums/battle-type";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import {
|
||||
applyPostFaintAbAttrs,
|
||||
applyPostKnockOutAbAttrs,
|
||||
applyPostVictoryAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
@ -117,29 +113,31 @@ export class FaintPhase extends PokemonPhase {
|
||||
|
||||
pokemon.resetTera();
|
||||
|
||||
// TODO: this can be simplified by just checking whether lastAttack is defined
|
||||
if (pokemon.turnData.attacksReceived?.length) {
|
||||
const lastAttack = pokemon.turnData.attacksReceived[0];
|
||||
applyPostFaintAbAttrs(
|
||||
"PostFaintAbAttr",
|
||||
pokemon,
|
||||
globalScene.getPokemonById(lastAttack.sourceId)!,
|
||||
new PokemonMove(lastAttack.move).getMove(),
|
||||
lastAttack.result,
|
||||
); // TODO: is this bang correct?
|
||||
applyAbAttrs("PostFaintAbAttr", {
|
||||
pokemon: pokemon,
|
||||
// TODO: We should refactor lastAttack's sourceId to forbid null and just use undefined
|
||||
attacker: globalScene.getPokemonById(lastAttack.sourceId) ?? undefined,
|
||||
// TODO: improve the way that we provide the move that knocked out the pokemon...
|
||||
move: new PokemonMove(lastAttack.move).getMove(),
|
||||
hitResult: lastAttack.result,
|
||||
}); // TODO: is this bang correct?
|
||||
} else {
|
||||
//If killed by indirect damage, apply post-faint abilities without providing a last move
|
||||
applyPostFaintAbAttrs("PostFaintAbAttr", pokemon);
|
||||
applyAbAttrs("PostFaintAbAttr", { pokemon });
|
||||
}
|
||||
|
||||
const alivePlayField = globalScene.getField(true);
|
||||
for (const p of alivePlayField) {
|
||||
applyPostKnockOutAbAttrs("PostKnockOutAbAttr", p, pokemon);
|
||||
applyAbAttrs("PostKnockOutAbAttr", { pokemon: p, victim: pokemon });
|
||||
}
|
||||
if (pokemon.turnData.attacksReceived?.length) {
|
||||
const defeatSource = this.source;
|
||||
|
||||
if (defeatSource?.isOnField()) {
|
||||
applyPostVictoryAbAttrs("PostVictoryAbAttr", defeatSource);
|
||||
applyAbAttrs("PostVictoryAbAttr", { pokemon: defeatSource });
|
||||
const pvmove = allMoves[pokemon.turnData.attacksReceived[0].move];
|
||||
const pvattrs = pvmove.getAttrs("PostVictoryStatStageChangeAttr");
|
||||
if (pvattrs.length) {
|
||||
|
@ -1,12 +1,6 @@
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import {
|
||||
applyExecutedMoveAbAttrs,
|
||||
applyPostAttackAbAttrs,
|
||||
applyPostDamageAbAttrs,
|
||||
applyPostDefendAbAttrs,
|
||||
applyPreAttackAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { ConditionalProtectTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import { MoveAnim } from "#app/data/battle-anims";
|
||||
@ -322,7 +316,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
// Assume single target for multi hit
|
||||
applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount);
|
||||
// If Parental Bond is applicable, add another hit
|
||||
applyPreAttackAbAttrs("AddSecondStrikeAbAttr", user, null, move, false, hitCount, null);
|
||||
applyAbAttrs("AddSecondStrikeAbAttr", { pokemon: user, move, hitCount });
|
||||
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
|
||||
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
|
||||
// Set the user's relevant turnData fields to reflect the final hit count
|
||||
@ -370,7 +364,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
// Add to the move history entry
|
||||
if (this.firstHit) {
|
||||
user.pushMoveHistory(this.moveHistoryEntry);
|
||||
applyExecutedMoveAbAttrs("ExecutedMoveAbAttr", user);
|
||||
applyAbAttrs("ExecutedMoveAbAttr", { pokemon: user });
|
||||
}
|
||||
|
||||
try {
|
||||
@ -439,7 +433,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
||||
*/
|
||||
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void {
|
||||
applyPostDefendAbAttrs("PostDefendAbAttr", target, user, this.move, hitResult);
|
||||
applyAbAttrs("PostDefendAbAttr", { pokemon: target, opponent: user, move: this.move, hitResult });
|
||||
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
||||
}
|
||||
|
||||
@ -808,7 +802,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
|
||||
// Multi-hit check for Wimp Out/Emergency Exit
|
||||
if (user.turnData.hitCount > 1) {
|
||||
applyPostDamageAbAttrs("PostDamageAbAttr", target, 0, target.hasPassive(), false, [], user);
|
||||
// TODO: Investigate why 0 is being passed for damage amount here
|
||||
// and then determing if refactoring `applyMove` to return the damage dealt is appropriate.
|
||||
applyAbAttrs("PostDamageAbAttr", { pokemon: target, damage: 0, source: user });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1002,7 +998,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
|
||||
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
||||
this.applyOnGetHitAbEffects(user, target, hitResult);
|
||||
applyPostAttackAbAttrs("PostAttackAbAttr", user, target, this.move, hitResult);
|
||||
applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult });
|
||||
|
||||
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
||||
if (!user.isPlayer() && this.move.is("AttackMove")) {
|
||||
|
@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
|
||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
|
||||
export class MoveEndPhase extends PokemonPhase {
|
||||
public readonly phaseName = "MoveEndPhase";
|
||||
@ -30,7 +30,7 @@ export class MoveEndPhase extends PokemonPhase {
|
||||
globalScene.arena.setIgnoreAbilities(false);
|
||||
for (const target of this.targets) {
|
||||
if (target) {
|
||||
applyPostSummonAbAttrs("PostSummonRemoveEffectAbAttr", target);
|
||||
applyAbAttrs("PostSummonRemoveEffectAbAttr", { pokemon: target });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import type { DelayedAttackTag } from "#app/data/arena-tag";
|
||||
import { CommonAnim } from "#enums/move-anims-common";
|
||||
import { CenterOfAttentionTag } from "#app/data/battler-tags";
|
||||
@ -228,14 +228,11 @@ export class MovePhase extends BattlePhase {
|
||||
case StatusEffect.SLEEP: {
|
||||
applyMoveAttrs("BypassSleepAttr", this.pokemon, null, this.move.getMove());
|
||||
const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0);
|
||||
applyAbAttrs(
|
||||
"ReduceStatusEffectDurationAbAttr",
|
||||
this.pokemon,
|
||||
null,
|
||||
false,
|
||||
this.pokemon.status.effect,
|
||||
turnsRemaining,
|
||||
);
|
||||
applyAbAttrs("ReduceStatusEffectDurationAbAttr", {
|
||||
pokemon: this.pokemon,
|
||||
statusEffect: this.pokemon.status.effect,
|
||||
duration: turnsRemaining,
|
||||
});
|
||||
this.pokemon.status.sleepTurnsRemaining = turnsRemaining.value;
|
||||
healed = this.pokemon.status.sleepTurnsRemaining <= 0;
|
||||
activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP);
|
||||
@ -396,7 +393,8 @@ export class MovePhase extends BattlePhase {
|
||||
*/
|
||||
if (success) {
|
||||
const move = this.move.getMove();
|
||||
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, move);
|
||||
// TODO: Investigate whether PokemonTypeChangeAbAttr can drop the "opponent" parameter
|
||||
applyAbAttrs("PokemonTypeChangeAbAttr", { pokemon: this.pokemon, move, opponent: targets[0] });
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"MoveEffectPhase",
|
||||
this.pokemon.getBattlerIndex(),
|
||||
@ -406,7 +404,11 @@ export class MovePhase extends BattlePhase {
|
||||
);
|
||||
} else {
|
||||
if ([MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)) {
|
||||
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove());
|
||||
applyAbAttrs("PokemonTypeChangeAbAttr", {
|
||||
pokemon: this.pokemon,
|
||||
move: this.move.getMove(),
|
||||
opponent: targets[0],
|
||||
});
|
||||
}
|
||||
|
||||
this.pokemon.pushMoveHistory({
|
||||
@ -438,7 +440,7 @@ export class MovePhase extends BattlePhase {
|
||||
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !dancerModes.includes(this.useMode)) {
|
||||
// TODO: Fix in dancer PR to move to MEP for hit checks
|
||||
globalScene.getField(true).forEach(pokemon => {
|
||||
applyPostMoveUsedAbAttrs("PostMoveUsedAbAttr", pokemon, this.move, this.pokemon, this.targets);
|
||||
applyAbAttrs("PostMoveUsedAbAttr", { pokemon, move: this.move, source: this.pokemon, targets: this.targets });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -470,7 +472,11 @@ export class MovePhase extends BattlePhase {
|
||||
}
|
||||
|
||||
// Protean and Libero apply on the charging turn of charge moves
|
||||
applyPreAttackAbAttrs("PokemonTypeChangeAbAttr", this.pokemon, null, this.move.getMove());
|
||||
applyAbAttrs("PokemonTypeChangeAbAttr", {
|
||||
pokemon: this.pokemon,
|
||||
move: this.move.getMove(),
|
||||
opponent: targets[0],
|
||||
});
|
||||
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
"MoveChargePhase",
|
||||
@ -523,7 +529,12 @@ export class MovePhase extends BattlePhase {
|
||||
.getField(true)
|
||||
.filter(p => p !== this.pokemon)
|
||||
.forEach(p =>
|
||||
applyAbAttrs("RedirectMoveAbAttr", p, null, false, this.move.moveId, redirectTarget, this.pokemon),
|
||||
applyAbAttrs("RedirectMoveAbAttr", {
|
||||
pokemon: p,
|
||||
moveId: this.move.moveId,
|
||||
targetIndex: redirectTarget,
|
||||
sourcePokemon: this.pokemon,
|
||||
}),
|
||||
);
|
||||
|
||||
/** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */
|
||||
|
@ -14,7 +14,7 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
|
||||
if (pokemon) {
|
||||
pokemon.resetBattleAndWaveData();
|
||||
if (pokemon.isOnField()) {
|
||||
applyAbAttrs("PostBiomeChangeAbAttr", pokemon, null);
|
||||
applyAbAttrs("PostBiomeChangeAbAttr", { pokemon });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import type Pokemon from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
import { applyPostSetStatusAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { isNullOrUndefined } from "#app/utils/common";
|
||||
|
||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
@ -53,7 +53,11 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true);
|
||||
// If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards
|
||||
globalScene.arena.setIgnoreAbilities(false);
|
||||
applyPostSetStatusAbAttrs("PostSetStatusAbAttr", pokemon, this.statusEffect, this.sourcePokemon);
|
||||
applyAbAttrs("PostSetStatusAbAttr", {
|
||||
pokemon,
|
||||
effect: this.statusEffect,
|
||||
sourcePokemon: this.sourcePokemon ?? undefined,
|
||||
});
|
||||
}
|
||||
this.end();
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { applyPostSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
|
||||
@ -16,7 +16,8 @@ export class PostSummonActivateAbilityPhase extends PostSummonPhase {
|
||||
}
|
||||
|
||||
start() {
|
||||
applyPostSummonAbAttrs("PostSummonAbAttr", this.getPokemon(), this.passive, false);
|
||||
// TODO: Check with Dean on whether or not passive must be provided to `this.passive`
|
||||
applyAbAttrs("PostSummonAbAttr", { pokemon: this.getPokemon(), passive: this.passive });
|
||||
|
||||
this.end();
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ export class PostSummonPhase extends PokemonPhase {
|
||||
|
||||
const field = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField();
|
||||
for (const p of field) {
|
||||
applyAbAttrs("CommanderAbAttr", p, null, false);
|
||||
applyAbAttrs("CommanderAbAttr", { pokemon: p });
|
||||
}
|
||||
|
||||
this.end();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { applyAbAttrs, applyPostDamageAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { CommonBattleAnim } from "#app/data/battle-anims";
|
||||
import { CommonAnim } from "#enums/move-anims-common";
|
||||
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
||||
@ -22,8 +22,8 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||
if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) {
|
||||
pokemon.status.incrementTurn();
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockStatusDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
applyAbAttrs("BlockStatusDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (!cancelled.value) {
|
||||
globalScene.phaseManager.queueMessage(
|
||||
@ -39,14 +39,14 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||
break;
|
||||
case StatusEffect.BURN:
|
||||
damage.value = Math.max(pokemon.getMaxHp() >> 4, 1);
|
||||
applyAbAttrs("ReduceBurnDamageAbAttr", pokemon, null, false, damage);
|
||||
applyAbAttrs("ReduceBurnDamageAbAttr", { pokemon, burnDamage: damage });
|
||||
break;
|
||||
}
|
||||
if (damage.value) {
|
||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
||||
globalScene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||
pokemon.updateInfo();
|
||||
applyPostDamageAbAttrs("PostDamageAbAttr", pokemon, damage.value, pokemon.hasPassive(), false, []);
|
||||
applyAbAttrs("PostDamageAbAttr", { pokemon, damage: damage.value });
|
||||
}
|
||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(false, () => this.end());
|
||||
} else {
|
||||
|
@ -181,9 +181,10 @@ export class QuietFormChangePhase extends BattlePhase {
|
||||
}
|
||||
}
|
||||
if (this.formChange.trigger instanceof SpeciesFormChangeTeraTrigger) {
|
||||
applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", this.pokemon, null);
|
||||
applyAbAttrs("ClearWeatherAbAttr", this.pokemon, null);
|
||||
applyAbAttrs("ClearTerrainAbAttr", this.pokemon, null);
|
||||
const params = { pokemon: this.pokemon };
|
||||
applyAbAttrs("PostTeraFormChangeStatChangeAbAttr", params);
|
||||
applyAbAttrs("ClearWeatherAbAttr", params);
|
||||
applyAbAttrs("ClearTerrainAbAttr", params);
|
||||
}
|
||||
|
||||
super.end();
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import {
|
||||
applyAbAttrs,
|
||||
applyPostStatStageChangeAbAttrs,
|
||||
applyPreStatStageChangeAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { MistTag } from "#app/data/arena-tag";
|
||||
import { ArenaTagSide } from "#enums/arena-tag-side";
|
||||
import type { ArenaTag } from "#app/data/arena-tag";
|
||||
@ -18,6 +14,10 @@ import { PokemonPhase } from "./pokemon-phase";
|
||||
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
||||
import { OctolockTag } from "#app/data/battler-tags";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||
import type {
|
||||
ConditionalUserFieldProtectStatAbAttrParams,
|
||||
PreStatStageChangeAbAttrParams,
|
||||
} from "#app/@types/ability-types";
|
||||
|
||||
export type StatStageChangeCallback = (
|
||||
target: Pokemon | null,
|
||||
@ -126,7 +126,7 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||
const stages = new NumberHolder(this.stages);
|
||||
|
||||
if (!this.ignoreAbilities) {
|
||||
applyAbAttrs("StatStageChangeMultiplierAbAttr", pokemon, null, false, stages);
|
||||
applyAbAttrs("StatStageChangeMultiplierAbAttr", { pokemon, numStages: stages });
|
||||
}
|
||||
|
||||
let simulate = false;
|
||||
@ -146,42 +146,38 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
if (!cancelled.value && !this.selfTarget && stages.value < 0) {
|
||||
applyPreStatStageChangeAbAttrs("ProtectStatAbAttr", pokemon, stat, cancelled, simulate);
|
||||
applyPreStatStageChangeAbAttrs(
|
||||
"ConditionalUserFieldProtectStatAbAttr",
|
||||
const abAttrParams: PreStatStageChangeAbAttrParams & ConditionalUserFieldProtectStatAbAttrParams = {
|
||||
pokemon,
|
||||
stat,
|
||||
cancelled,
|
||||
simulate,
|
||||
pokemon,
|
||||
);
|
||||
simulated: simulate,
|
||||
target: pokemon,
|
||||
stages: this.stages,
|
||||
};
|
||||
applyAbAttrs("ProtectStatAbAttr", abAttrParams);
|
||||
applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", abAttrParams);
|
||||
// TODO: Consider skipping this call if `cancelled` is false.
|
||||
const ally = pokemon.getAlly();
|
||||
if (!isNullOrUndefined(ally)) {
|
||||
applyPreStatStageChangeAbAttrs(
|
||||
"ConditionalUserFieldProtectStatAbAttr",
|
||||
ally,
|
||||
stat,
|
||||
cancelled,
|
||||
simulate,
|
||||
pokemon,
|
||||
);
|
||||
applyAbAttrs("ConditionalUserFieldProtectStatAbAttr", { ...abAttrParams, pokemon: ally });
|
||||
}
|
||||
|
||||
/** Potential stat reflection due to Mirror Armor, does not apply to Octolock end of turn effect */
|
||||
if (
|
||||
opponentPokemon !== undefined &&
|
||||
// TODO: investigate whether this is stoping mirror armor from applying to non-octolock
|
||||
// reasons for stat drops if the user has the Octolock tag
|
||||
!pokemon.findTag(t => t instanceof OctolockTag) &&
|
||||
!this.comingFromMirrorArmorUser
|
||||
) {
|
||||
applyPreStatStageChangeAbAttrs(
|
||||
"ReflectStatStageChangeAbAttr",
|
||||
applyAbAttrs("ReflectStatStageChangeAbAttr", {
|
||||
pokemon,
|
||||
stat,
|
||||
cancelled,
|
||||
simulate,
|
||||
opponentPokemon,
|
||||
this.stages,
|
||||
);
|
||||
simulated: simulate,
|
||||
source: opponentPokemon,
|
||||
stages: this.stages,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,17 +218,16 @@ export class StatStageChangePhase extends PokemonPhase {
|
||||
|
||||
if (stages.value > 0 && this.canBeCopied) {
|
||||
for (const opponent of pokemon.getOpponents()) {
|
||||
applyAbAttrs("StatStageChangeCopyAbAttr", opponent, null, false, this.stats, stages.value);
|
||||
applyAbAttrs("StatStageChangeCopyAbAttr", { pokemon: opponent, stats: this.stats, numStages: stages.value });
|
||||
}
|
||||
}
|
||||
|
||||
applyPostStatStageChangeAbAttrs(
|
||||
"PostStatStageChangeAbAttr",
|
||||
applyAbAttrs("PostStatStageChangeAbAttr", {
|
||||
pokemon,
|
||||
filteredStats,
|
||||
this.stages,
|
||||
this.selfTarget,
|
||||
);
|
||||
stats: filteredStats,
|
||||
stages: this.stages,
|
||||
selfTarget: this.selfTarget,
|
||||
});
|
||||
|
||||
// Look for any other stat change phases; if this is the last one, do White Herb check
|
||||
const existingPhase = globalScene.phaseManager.findPhase(
|
||||
|
@ -10,7 +10,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import i18next from "i18next";
|
||||
import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { applyPreSummonAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
export class SummonPhase extends PartyMemberPokemonPhase {
|
||||
@ -27,7 +27,7 @@ export class SummonPhase extends PartyMemberPokemonPhase {
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
applyPreSummonAbAttrs("PreSummonAbAttr", this.getPokemon());
|
||||
applyAbAttrs("PreSummonAbAttr", { pokemon: this.getPokemon() });
|
||||
this.preSummon();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { applyPreSummonAbAttrs, applyPreSwitchOutAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { getPokeballTintColor } from "#app/data/pokeball";
|
||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms/form-change-triggers";
|
||||
@ -124,8 +124,8 @@ export class SwitchSummonPhase extends SummonPhase {
|
||||
switchedInPokemon.resetSummonData();
|
||||
switchedInPokemon.loadAssets(true);
|
||||
|
||||
applyPreSummonAbAttrs("PreSummonAbAttr", switchedInPokemon);
|
||||
applyPreSwitchOutAbAttrs("PreSwitchOutAbAttr", this.lastPokemon);
|
||||
applyAbAttrs("PreSummonAbAttr", { pokemon: switchedInPokemon });
|
||||
applyAbAttrs("PreSwitchOutAbAttr", { pokemon: this.lastPokemon });
|
||||
if (!switchedInPokemon) {
|
||||
this.end();
|
||||
return;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { applyPostTurnAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||
import { TerrainType } from "#app/data/terrain";
|
||||
import { WeatherType } from "#app/enums/weather-type";
|
||||
@ -49,7 +49,7 @@ export class TurnEndPhase extends FieldPhase {
|
||||
globalScene.applyModifier(EnemyStatusEffectHealChanceModifier, false, pokemon);
|
||||
}
|
||||
|
||||
applyPostTurnAbAttrs("PostTurnAbAttr", pokemon);
|
||||
applyAbAttrs("PostTurnAbAttr", { pokemon });
|
||||
}
|
||||
|
||||
globalScene.applyModifiers(TurnStatusEffectModifier, pokemon.isPlayer(), pokemon);
|
||||
|
@ -66,8 +66,12 @@ export class TurnStartPhase extends FieldPhase {
|
||||
globalScene.getField(true).forEach(p => {
|
||||
const bypassSpeed = new BooleanHolder(false);
|
||||
const canCheckHeldItems = new BooleanHolder(true);
|
||||
applyAbAttrs("BypassSpeedChanceAbAttr", p, null, false, bypassSpeed);
|
||||
applyAbAttrs("PreventBypassSpeedChanceAbAttr", p, null, false, bypassSpeed, canCheckHeldItems);
|
||||
applyAbAttrs("BypassSpeedChanceAbAttr", { pokemon: p, bypass: bypassSpeed });
|
||||
applyAbAttrs("PreventBypassSpeedChanceAbAttr", {
|
||||
pokemon: p,
|
||||
bypass: bypassSpeed,
|
||||
canCheckHeldItems: canCheckHeldItems,
|
||||
});
|
||||
if (canCheckHeldItems.value) {
|
||||
globalScene.applyModifiers(BypassSpeedChanceModifier, p.isPlayer(), p, bypassSpeed);
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import {
|
||||
applyPreWeatherEffectAbAttrs,
|
||||
applyAbAttrs,
|
||||
applyPostWeatherLapseAbAttrs,
|
||||
} from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { CommonAnim } from "#enums/move-anims-common";
|
||||
import type { Weather } from "#app/data/weather";
|
||||
import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather";
|
||||
@ -41,15 +37,15 @@ export class WeatherEffectPhase extends CommonAnimPhase {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
this.executeForAll((pokemon: Pokemon) =>
|
||||
applyPreWeatherEffectAbAttrs("SuppressWeatherEffectAbAttr", pokemon, this.weather, cancelled),
|
||||
applyAbAttrs("SuppressWeatherEffectAbAttr", { pokemon, weather: this.weather, cancelled }),
|
||||
);
|
||||
|
||||
if (!cancelled.value) {
|
||||
const inflictDamage = (pokemon: Pokemon) => {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
||||
applyPreWeatherEffectAbAttrs("PreWeatherDamageAbAttr", pokemon, this.weather, cancelled);
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", pokemon, cancelled);
|
||||
applyAbAttrs("PreWeatherDamageAbAttr", { pokemon, weather: this.weather, cancelled });
|
||||
applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon, cancelled });
|
||||
|
||||
if (
|
||||
cancelled.value ||
|
||||
@ -80,7 +76,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
|
||||
globalScene.ui.showText(getWeatherLapseMessage(this.weather.weatherType) ?? "", null, () => {
|
||||
this.executeForAll((pokemon: Pokemon) => {
|
||||
if (!pokemon.switchOutStatus) {
|
||||
applyPostWeatherLapseAbAttrs("PostWeatherLapseAbAttr", pokemon, this.weather);
|
||||
applyAbAttrs("PostWeatherLapseAbAttr", { pokemon, weather: this.weather });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { RepeatBerryNextTurnAbAttr } from "#app/data/abilities/ability";
|
||||
import { CudChewConsumeBerryAbAttr } from "#app/data/abilities/ability";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
@ -196,7 +196,7 @@ describe("Abilities - Cud Chew", () => {
|
||||
|
||||
describe("regurgiates berries", () => {
|
||||
it("re-triggers effects on eater without pushing to array", async () => {
|
||||
const apply = vi.spyOn(RepeatBerryNextTurnAbAttr.prototype, "apply");
|
||||
const apply = vi.spyOn(CudChewConsumeBerryAbAttr.prototype, "apply");
|
||||
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
|
||||
|
||||
const farigiraf = game.scene.getPlayerPokemon()!;
|
||||
|
@ -95,7 +95,7 @@ describe("Abilities - Harvest", () => {
|
||||
|
||||
// Give ourselves harvest and disable enemy neut gas,
|
||||
// but force our roll to fail so we don't accidentally recover anything
|
||||
vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApplyPostTurn").mockReturnValueOnce(false);
|
||||
vi.spyOn(PostTurnRestoreBerryAbAttr.prototype, "canApply").mockReturnValueOnce(false);
|
||||
game.override.ability(AbilityId.HARVEST);
|
||||
game.move.select(MoveId.GASTRO_ACID);
|
||||
await game.move.selectEnemyMove(MoveId.NUZZLE);
|
||||
|
@ -42,7 +42,7 @@ describe("Abilities - Healer", () => {
|
||||
});
|
||||
|
||||
it("should not queue a message phase for healing if the ally has fainted", async () => {
|
||||
const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApplyPostTurn");
|
||||
const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApply");
|
||||
game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
|
||||
|
||||
|
@ -68,7 +68,7 @@ describe("Abilities - Moody", () => {
|
||||
});
|
||||
|
||||
it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => {
|
||||
await game.classicMode.startBattle();
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
|
@ -178,7 +178,7 @@ describe("Abilities - Neutralizing Gas", () => {
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
const weatherChangeAttr = enemy.getAbilityAttrs("PostSummonWeatherChangeAbAttr", false)[0];
|
||||
vi.spyOn(weatherChangeAttr, "applyPostSummon");
|
||||
const weatherChangeSpy = vi.spyOn(weatherChangeAttr, "apply");
|
||||
|
||||
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
|
||||
|
||||
@ -187,6 +187,6 @@ describe("Abilities - Neutralizing Gas", () => {
|
||||
await game.killPokemon(game.scene.getPlayerPokemon()!);
|
||||
|
||||
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
|
||||
expect(weatherChangeAttr.applyPostSummon).not.toHaveBeenCalled();
|
||||
expect(weatherChangeSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { StatMultiplierAbAttrParams } from "#app/@types/ability-types";
|
||||
import { allAbilities } from "#app/data/data-lists";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
@ -46,15 +47,13 @@ describe("Abilities - Sand Veil", () => {
|
||||
vi.spyOn(leadPokemon[0], "getAbility").mockReturnValue(allAbilities[AbilityId.SAND_VEIL]);
|
||||
|
||||
const sandVeilAttr = allAbilities[AbilityId.SAND_VEIL].getAttrs("StatMultiplierAbAttr")[0];
|
||||
vi.spyOn(sandVeilAttr, "applyStatStage").mockImplementation(
|
||||
(_pokemon, _passive, _simulated, stat, statValue, _args) => {
|
||||
if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
||||
statValue.value *= -1; // will make all attacks miss
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
);
|
||||
vi.spyOn(sandVeilAttr, "apply").mockImplementation(({ stat, statVal }: StatMultiplierAbAttrParams) => {
|
||||
if (stat === Stat.EVA && game.scene.arena.weather?.weatherType === WeatherType.SANDSTORM) {
|
||||
statVal.value *= -1; // will make all attacks miss
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
expect(leadPokemon[0].hasAbility(AbilityId.SAND_VEIL)).toBe(true);
|
||||
expect(leadPokemon[1].hasAbility(AbilityId.SAND_VEIL)).toBe(false);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { applyAbAttrs, applyPreDefendAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { NumberHolder } from "#app/utils/common";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
@ -52,25 +52,16 @@ describe("Abilities - Shield Dust", () => {
|
||||
expect(move.id).toBe(MoveId.AIR_SLASH);
|
||||
|
||||
const chance = new NumberHolder(move.chance);
|
||||
await applyAbAttrs(
|
||||
"MoveEffectChanceMultiplierAbAttr",
|
||||
phase.getUserPokemon()!,
|
||||
null,
|
||||
false,
|
||||
applyAbAttrs("MoveEffectChanceMultiplierAbAttr", {
|
||||
pokemon: phase.getUserPokemon()!,
|
||||
chance,
|
||||
move,
|
||||
phase.getFirstTarget(),
|
||||
false,
|
||||
);
|
||||
await applyPreDefendAbAttrs(
|
||||
"IgnoreMoveEffectsAbAttr",
|
||||
phase.getFirstTarget()!,
|
||||
phase.getUserPokemon()!,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
});
|
||||
applyAbAttrs("IgnoreMoveEffectsAbAttr", {
|
||||
pokemon: phase.getFirstTarget()!,
|
||||
move,
|
||||
chance,
|
||||
);
|
||||
});
|
||||
expect(chance.value).toBe(0);
|
||||
});
|
||||
|
||||
|
@ -277,7 +277,7 @@ describe("Abilities - Unburden", () => {
|
||||
const initialTreeckoSpeed = treecko.getStat(Stat.SPD);
|
||||
const initialPurrloinSpeed = purrloin.getStat(Stat.SPD);
|
||||
const unburdenAttr = treecko.getAbilityAttrs("PostItemLostAbAttr")[0];
|
||||
vi.spyOn(unburdenAttr, "applyPostItemLost");
|
||||
vi.spyOn(unburdenAttr, "apply");
|
||||
|
||||
// Player uses Baton Pass, which also passes the Baton item
|
||||
game.move.select(MoveId.BATON_PASS);
|
||||
@ -288,7 +288,7 @@ describe("Abilities - Unburden", () => {
|
||||
expect(getHeldItemCount(purrloin)).toBe(1);
|
||||
expect(treecko.getEffectiveStat(Stat.SPD)).toBe(initialTreeckoSpeed);
|
||||
expect(purrloin.getEffectiveStat(Stat.SPD)).toBe(initialPurrloinSpeed);
|
||||
expect(unburdenAttr.applyPostItemLost).not.toHaveBeenCalled();
|
||||
expect(unburdenAttr.apply).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not speed up a Pokemon after it loses the ability Unburden", async () => {
|
||||
|
@ -31,7 +31,7 @@ describe("Spec - Pokemon", () => {
|
||||
const pkm = game.scene.getPlayerPokemon()!;
|
||||
expect(pkm).toBeDefined();
|
||||
|
||||
expect(pkm.trySetStatus(undefined)).toBe(true);
|
||||
expect(pkm.trySetStatus(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
describe("Add To Party", () => {
|
||||
|
@ -140,9 +140,8 @@ describe("Moves - Safeguard", () => {
|
||||
game.field.mockAbility(player, AbilityId.STATIC);
|
||||
vi.spyOn(
|
||||
allAbilities[AbilityId.STATIC].getAttrs("PostDefendContactApplyStatusEffectAbAttr")[0],
|
||||
"chance",
|
||||
"get",
|
||||
).mockReturnValue(100);
|
||||
"canApply",
|
||||
).mockReturnValue(true);
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.SAFEGUARD);
|
||||
|
Loading…
Reference in New Issue
Block a user