mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-20 16:42:45 +02:00
Update callsites in pokemon.ts
This commit is contained in:
parent
55093ec0af
commit
b9781bd863
@ -1,7 +0,0 @@
|
||||
export type * from "#app/data/abilities/ability";
|
||||
import type { AbAttrMap } from "./ability-types";
|
||||
|
||||
|
||||
export type AbAttrParamMap = {
|
||||
[K in keyof AbAttrMap]: Parameters<AbAttrMap[K]["apply"]>[0];
|
||||
}
|
@ -1,17 +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 abilities to have this be the centralized place to import ability types
|
||||
export type * from "#app/data/abilities/ability";
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
|
||||
import type { applyAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||
|
||||
// Intentionally re-export all types from the ability attributes module
|
||||
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;
|
||||
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;
|
||||
@ -33,11 +30,17 @@ export type AbAttrMap = {
|
||||
* Subset of ability attribute classes that may be passed to {@linkcode applyAbAttrs } method
|
||||
*
|
||||
* @remarks
|
||||
* Our AbAttr classes violate Liskov Substitution Principal.
|
||||
* 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">;
|
||||
export type CallableAbAttrString =
|
||||
| Exclude<AbAttrString, "PreDefendAbAttr" | "PreAttackAbAttr">
|
||||
| "PreApplyBattlerTagAbAttr";
|
||||
|
||||
export type AbAttrParamMap = {
|
||||
[K in keyof AbAttrMap]: Parameters<AbAttrMap[K]["apply"]>[0];
|
||||
};
|
||||
|
@ -22,3 +22,11 @@ export type Exact<T> = {
|
||||
*
|
||||
*/
|
||||
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];
|
||||
};
|
||||
|
@ -238,7 +238,7 @@ export interface AbAttrBaseParams {
|
||||
readonly simulated?: boolean;
|
||||
|
||||
/** Whether the ability is the passive ability. Default false */
|
||||
readonly passive?: boolean;
|
||||
passive?: boolean;
|
||||
}
|
||||
|
||||
export interface AbAttrParamsWithCancel extends AbAttrBaseParams {
|
||||
@ -306,6 +306,7 @@ export abstract class AbAttr {
|
||||
}
|
||||
|
||||
export class BlockRecoilDamageAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
@ -314,7 +315,8 @@ export class BlockRecoilDamageAttr extends AbAttr {
|
||||
cancelled.value = true;
|
||||
}
|
||||
|
||||
getTriggerMessage({ pokemon }: AbAttrParamsWithCancel, abilityName: string) {
|
||||
override getTriggerMessage({ pokemon }: AbAttrParamsWithCancel, abilityName: string) {
|
||||
// TODO: remove this because this does not exist on cartridge
|
||||
return i18next.t("abilityTriggers:blockRecoilDamage", {
|
||||
pokemonName: getPokemonNameWithAffix(pokemon),
|
||||
abilityName: abilityName,
|
||||
@ -333,6 +335,7 @@ export interface DoubleBattleChanceAbAttrParams extends AbAttrBaseParams {
|
||||
* @see {@linkcode apply}
|
||||
*/
|
||||
export class DoubleBattleChanceAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
@ -1380,7 +1383,9 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostStatStageChangeAbAttr extends AbAttr {}
|
||||
export class PostStatStageChangeAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
export interface PostStatStageChangeAbAttrParams extends AbAttrBaseParams {
|
||||
/** The stats that were changed */
|
||||
@ -1424,7 +1429,9 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class PreAttackAbAttr extends AbAttr {}
|
||||
export abstract class PreAttackAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
export interface ModifyMoveEffectChanceAbAttrParams extends AbAttrBaseParams {
|
||||
/** The move being used by the attacker */
|
||||
@ -1628,9 +1635,9 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr {
|
||||
*/
|
||||
export interface AddSecondStrikeAbAttrParams extends AugmentMoveInteractionAbAttrParams {
|
||||
/** Holder for the number of hits. May be modified by ability application */
|
||||
hitCount: NumberHolder;
|
||||
hitCount?: NumberHolder;
|
||||
/** Holder for the damage multiplier _of the current hit_ */
|
||||
multiplier: NumberHolder;
|
||||
multiplier?: NumberHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1660,11 +1667,11 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr {
|
||||
* to the damage multiplier of this ability.
|
||||
*/
|
||||
override apply({ hitCount, multiplier, pokemon }: AddSecondStrikeAbAttrParams): void {
|
||||
if (hitCount.value) {
|
||||
if (hitCount?.value) {
|
||||
hitCount.value += 1;
|
||||
}
|
||||
|
||||
if (multiplier.value && pokemon.turnData.hitsLeft === 1) {
|
||||
if (multiplier?.value && pokemon.turnData.hitsLeft === 1) {
|
||||
multiplier.value = this.damageMultiplier;
|
||||
}
|
||||
}
|
||||
@ -1806,27 +1813,13 @@ export class FieldMovePowerBoostAbAttr extends AbAttr {
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
canApplyPreAttack(
|
||||
_pokemon: Pokemon | null,
|
||||
_passive: boolean | null,
|
||||
_simulated: boolean,
|
||||
_defender: Pokemon | null,
|
||||
_move: Move,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
canApply(_params: PreAttackModifyPowerAbAttrParams): boolean {
|
||||
return true; // logic for this attr is handled in move.ts instead of normally
|
||||
}
|
||||
|
||||
applyPreAttack(
|
||||
pokemon: Pokemon | null,
|
||||
_passive: boolean | null,
|
||||
_simulated: boolean,
|
||||
defender: Pokemon | null,
|
||||
move: Move,
|
||||
args: any[],
|
||||
): void {
|
||||
if (this.condition(pokemon, defender, move)) {
|
||||
(args[0] as NumberHolder).value *= this.powerMultiplier;
|
||||
apply({ pokemon, opponent, move, power }: PreAttackModifyPowerAbAttrParams): void {
|
||||
if (this.condition(pokemon, opponent, move)) {
|
||||
power.value *= this.powerMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1880,6 +1873,7 @@ export interface StatMultiplierAbAttrParams extends AbAttrBaseParams {
|
||||
}
|
||||
|
||||
export class StatMultiplierAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
private stat: BattleStat;
|
||||
private multiplier: number;
|
||||
/** Function determining if the stat multiplier is able to be applied to the move.
|
||||
@ -1897,11 +1891,11 @@ export class StatMultiplierAbAttr extends AbAttr {
|
||||
this.condition = condition ?? null;
|
||||
}
|
||||
|
||||
canApplyStatStage({ pokemon, move, stat }: StatMultiplierAbAttrParams): boolean {
|
||||
override canApply({ pokemon, move, stat }: StatMultiplierAbAttrParams): boolean {
|
||||
return stat === this.stat && (!this.condition || this.condition(pokemon, null, move));
|
||||
}
|
||||
|
||||
applyStatStage({ statVal }: StatMultiplierAbAttrParams): void {
|
||||
override apply({ statVal }: StatMultiplierAbAttrParams): void {
|
||||
statVal.value *= this.multiplier;
|
||||
}
|
||||
}
|
||||
@ -2380,7 +2374,7 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
interface IgnoreOpponentStatStagesAbAttrParam extends AbAttrBaseParams {
|
||||
export interface IgnoreOpponentStatStagesAbAttrParams extends AbAttrBaseParams {
|
||||
/** The to check for ignorability */
|
||||
stat: BattleStat;
|
||||
/** Holds whether the stat is ignored by the ability */
|
||||
@ -2402,14 +2396,14 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr {
|
||||
/**
|
||||
* @returns Whether `stat` is one of the stats ignored by the ability
|
||||
*/
|
||||
override canApply({ stat }: IgnoreOpponentStatStagesAbAttrParam): boolean {
|
||||
override canApply({ stat }: IgnoreOpponentStatStagesAbAttrParams): boolean {
|
||||
return this.stats.includes(stat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ignored holder to true.
|
||||
*/
|
||||
override apply({ ignored }: IgnoreOpponentStatStagesAbAttrParam): void {
|
||||
override apply({ ignored }: IgnoreOpponentStatStagesAbAttrParams): void {
|
||||
ignored.value = true;
|
||||
}
|
||||
}
|
||||
@ -3314,11 +3308,11 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr {
|
||||
* Base class for ability attributes that apply their effect just before the user leaves the field
|
||||
*/
|
||||
export class PreLeaveFieldAbAttr extends AbAttr {
|
||||
canApplyPreLeaveField(_params: Closed<AbAttrBaseParams>): boolean {
|
||||
canApply(_params: Closed<AbAttrBaseParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPreLeaveField(_params: Closed<AbAttrBaseParams>): void {}
|
||||
apply(_params: Closed<AbAttrBaseParams>): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3408,11 +3402,11 @@ export interface PreStatStageChangeAbAttrParams extends AbAttrBaseParams {
|
||||
* Base class for ability attributes that apply their effect before a stat stage change.
|
||||
*/
|
||||
export abstract class PreStatStageChangeAbAttr extends AbAttr {
|
||||
canApplyPreStatStageChange(_params: Closed<PreStatStageChangeAbAttrParams>): boolean {
|
||||
canApply(_params: Closed<PreStatStageChangeAbAttrParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPreStatStageChange(_params: Closed<PreStatStageChangeAbAttrParams>): void {}
|
||||
apply(_params: Closed<PreStatStageChangeAbAttrParams>): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3672,8 +3666,9 @@ export interface ConditionalUserFieldProtectStatAbAttrParams extends AbAttrBaseP
|
||||
stat: BattleStat;
|
||||
/** Holds whether the stat stage change is prevented by the ability */
|
||||
cancelled: BooleanHolder;
|
||||
// TODO: consider making this required and not inherit from PreStatStageChangeAbAttr
|
||||
/** The target of the stat stage change */
|
||||
target: Pokemon;
|
||||
target?: Pokemon;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3724,21 +3719,20 @@ export interface PreApplyBattlerTagAbAttrParams extends AbAttrBaseParams {
|
||||
|
||||
/**
|
||||
* Base class for ability attributes that apply their effect before a BattlerTag {@linkcode BattlerTag} is applied.
|
||||
* Subclasses violate Liskov Substitution Principle, so this class must not be provided to {@linkcode applyAbAttrs}
|
||||
*/
|
||||
export abstract class PreApplyBattlerTagAbAttr extends AbAttr {
|
||||
canApplyPreApplyBattlerTag(_params: Closed<PreApplyBattlerTagAbAttrParams>): boolean {
|
||||
canApply(_params: PreApplyBattlerTagAbAttrParams): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPreApplyBattlerTag(_params: Closed<PreApplyBattlerTagAbAttrParams>): void {}
|
||||
apply(_params: PreApplyBattlerTagAbAttrParams): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets.
|
||||
*
|
||||
* This does not check whether the tag is already applied; that check should happen in the caller.
|
||||
*/
|
||||
export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
|
||||
// Intentionally not exported because this shouldn't be able to be passed to `applyAbAttrs`. It only exists so that
|
||||
// PreApplyBattlerTagImmunityAbAttr and UserFieldPreApplyBattlerTagImmunityAbAttr can avoid code duplication
|
||||
// while preserving type safety. (Since the UserField version require an additional parameter, target, in its apply methods)
|
||||
abstract class BaseBattlerTagImmunityAbAttr<P extends PreApplyBattlerTagAbAttrParams> extends PreApplyBattlerTagAbAttr {
|
||||
protected immuneTagTypes: BattlerTagType[];
|
||||
|
||||
constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) {
|
||||
@ -3747,15 +3741,15 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
|
||||
this.immuneTagTypes = coerceArray(immuneTagTypes);
|
||||
}
|
||||
|
||||
override canApply({ cancelled, tag }: PreApplyBattlerTagAbAttrParams): boolean {
|
||||
override canApply({ cancelled, tag }: P): boolean {
|
||||
return !cancelled.value && this.immuneTagTypes.includes(tag.tagType);
|
||||
}
|
||||
|
||||
override apply({ cancelled }: PreApplyBattlerTagAbAttrParams): void {
|
||||
override apply({ cancelled }: P): void {
|
||||
cancelled.value = true;
|
||||
}
|
||||
|
||||
override getTriggerMessage({ pokemon, tag }: PreApplyBattlerTagAbAttrParams, abilityName: string): string {
|
||||
override getTriggerMessage({ pokemon, tag }: P, abilityName: string): string {
|
||||
return i18next.t("abilityTriggers:battlerTagImmunity", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
abilityName,
|
||||
@ -3764,18 +3758,31 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The battler tag ability attributes are in dire need of improvement
|
||||
// It is unclear why there is a `PreApplyBattlerTagImmunityAbAttr` class that isn't used,
|
||||
// and then why there's a BattlerTagImmunityAbAttr class as well.
|
||||
|
||||
/**
|
||||
* Provides immunity to BattlerTags {@linkcode BattlerTag} to specified targets.
|
||||
*
|
||||
* This does not check whether the tag is already applied; that check should happen in the caller.
|
||||
*/
|
||||
export class PreApplyBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr<PreApplyBattlerTagAbAttrParams> {}
|
||||
|
||||
/**
|
||||
* Provides immunity to BattlerTags {@linkcode BattlerTag} to the user.
|
||||
*/
|
||||
export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {}
|
||||
|
||||
export interface UserFieldBattlerTagImmunityAbAttrParams extends PreApplyBattlerTagAbAttrParams {
|
||||
/** The pokemon that the battler tag is being applied to */
|
||||
target: Pokemon;
|
||||
}
|
||||
/**
|
||||
* Provides immunity to BattlerTags {@linkcode BattlerTag} to the user's field.
|
||||
* @extends PreApplyBattlerTagImmunityAbAttr
|
||||
*/
|
||||
export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {}
|
||||
export class UserFieldBattlerTagImmunityAbAttr extends BaseBattlerTagImmunityAbAttr<UserFieldBattlerTagImmunityAbAttrParams> {}
|
||||
|
||||
// NOTE: We are inheriting from `PreApplyBattlerTagImmunityAbAttr` which has a different signature
|
||||
export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattlerTagImmunityAbAttr {
|
||||
private condition: (target: Pokemon) => boolean;
|
||||
|
||||
@ -3783,12 +3790,14 @@ export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattl
|
||||
* Determine whether the {@linkcode ConditionalUserFieldBattlerTagImmunityAbAttr} can be applied by passing the target pokemon to the condition.
|
||||
* @returns Whether the ability can be used to cancel the battler tag
|
||||
*/
|
||||
override canApply(params: PreApplyBattlerTagAbAttrParams & { target: Pokemon }): boolean {
|
||||
override canApply(params: UserFieldBattlerTagImmunityAbAttrParams): boolean {
|
||||
// the `!!params` here is to ensure the target is not null or undefined. This is defensive programming
|
||||
// to guard against the case where
|
||||
return !!params.target && super.canApply(params) && this.condition(params.target ?? params.pokemon);
|
||||
}
|
||||
|
||||
override apply(_params: UserFieldBattlerTagImmunityAbAttrParams) {}
|
||||
|
||||
constructor(condition: (target: Pokemon) => boolean, immuneTagTypes: BattlerTagType | BattlerTagType[]) {
|
||||
super(immuneTagTypes);
|
||||
|
||||
@ -3798,9 +3807,9 @@ export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattl
|
||||
|
||||
export interface BlockCritAbAttrParams extends AbAttrBaseParams {
|
||||
/**
|
||||
* Holds a boolean that will be set to false if the owner may not be crit
|
||||
* Holds a boolean that will be set to true if the user's ability prevents the attack from being critical
|
||||
*/
|
||||
readonly canCrit: BooleanHolder;
|
||||
readonly blockCrit: BooleanHolder;
|
||||
}
|
||||
|
||||
export class BlockCritAbAttr extends AbAttr {
|
||||
@ -3863,7 +3872,7 @@ export interface ConditionalCritAbAttrParams extends AbAttrBaseParams {
|
||||
/** The move being used */
|
||||
move: Move;
|
||||
/** Holds whether the attack will critically hit */
|
||||
crit: BooleanHolder;
|
||||
isCritical: BooleanHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3878,12 +3887,12 @@ export class ConditionalCritAbAttr extends AbAttr {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
override canApply({ crit, pokemon, target, move }: ConditionalCritAbAttrParams): boolean {
|
||||
return !crit.value && this.condition(pokemon, target, move);
|
||||
override canApply({ isCritical, pokemon, target, move }: ConditionalCritAbAttrParams): boolean {
|
||||
return !isCritical.value && this.condition(pokemon, target, move);
|
||||
}
|
||||
|
||||
override apply({ crit }: ConditionalCritAbAttrParams): void {
|
||||
crit.value = true;
|
||||
override apply({ isCritical }: ConditionalCritAbAttrParams): void {
|
||||
isCritical.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3904,7 +3913,7 @@ export class BlockStatusDamageAbAttr extends AbAttr {
|
||||
private effects: StatusEffect[];
|
||||
|
||||
/**
|
||||
* @param {StatusEffect[]} effects The status effect(s) that will be blocked from damaging the ability pokemon
|
||||
* @param effects - The status effect(s) that will be blocked from damaging the ability pokemon
|
||||
*/
|
||||
constructor(...effects: StatusEffect[]) {
|
||||
super(false);
|
||||
@ -3916,8 +3925,6 @@ export class BlockStatusDamageAbAttr extends AbAttr {
|
||||
return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect);
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
override apply({ cancelled }: AbAttrParamsWithCancel): void {
|
||||
cancelled.value = true;
|
||||
}
|
||||
@ -3967,7 +3974,9 @@ export class ChangeMovePriorityAbAttr extends AbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class IgnoreContactAbAttr extends AbAttr {}
|
||||
export class IgnoreContactAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared interface for attributes that respond to a weather.
|
||||
@ -4321,23 +4330,11 @@ export class PostWeatherLapseAbAttr extends AbAttr {
|
||||
this.weatherTypes = weatherTypes;
|
||||
}
|
||||
|
||||
canApplyPostWeatherLapse(
|
||||
_pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_weather: Weather | null,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
canApply(_params: Closed<PreWeatherEffectAbAttrParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPostWeatherLapse(
|
||||
_pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_weather: Weather | null,
|
||||
_args: any[],
|
||||
): void {}
|
||||
apply(_params: Closed<PreWeatherEffectAbAttrParams>): void {}
|
||||
|
||||
getCondition(): AbAttrCondition {
|
||||
return getWeatherCondition(...this.weatherTypes);
|
||||
@ -4353,23 +4350,11 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr {
|
||||
this.healFactor = healFactor;
|
||||
}
|
||||
|
||||
override canApplyPostWeatherLapse(
|
||||
pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_weather: Weather | null,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
override canApply({ pokemon }: AbAttrBaseParams): boolean {
|
||||
return !pokemon.isFullHp();
|
||||
}
|
||||
|
||||
override applyPostWeatherLapse(
|
||||
pokemon: Pokemon,
|
||||
passive: boolean,
|
||||
simulated: boolean,
|
||||
_weather: Weather,
|
||||
_args: any[],
|
||||
): void {
|
||||
override apply({ pokemon, passive, simulated }: PreWeatherEffectAbAttrParams): void {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
if (!simulated) {
|
||||
globalScene.phaseManager.unshiftNew(
|
||||
@ -4395,23 +4380,11 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
||||
this.damageFactor = damageFactor;
|
||||
}
|
||||
|
||||
override canApplyPostWeatherLapse(
|
||||
pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_weather: Weather | null,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
override canApply({ pokemon }: PreWeatherEffectAbAttrParams): boolean {
|
||||
return !pokemon.hasAbilityWithAttr("BlockNonDirectDamageAbAttr");
|
||||
}
|
||||
|
||||
override applyPostWeatherLapse(
|
||||
pokemon: Pokemon,
|
||||
passive: boolean,
|
||||
simulated: boolean,
|
||||
_weather: Weather,
|
||||
_args: any[],
|
||||
): void {
|
||||
override apply({ simulated, pokemon, passive }: PreWeatherEffectAbAttrParams): void {
|
||||
if (!simulated) {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
globalScene.phaseManager.queueMessage(
|
||||
@ -4430,8 +4403,6 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
||||
export interface PostTerrainChangeAbAttrParams extends AbAttrBaseParams {
|
||||
/** The terrain type that is being changed to */
|
||||
terrain: TerrainType;
|
||||
/** Holds whether the terrain change is prevented by the ability */
|
||||
cancelled: BooleanHolder;
|
||||
}
|
||||
|
||||
export class PostTerrainChangeAbAttr extends AbAttr {
|
||||
@ -4858,7 +4829,9 @@ export class FetchBallAbAttr extends PostTurnAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class PostBiomeChangeAbAttr extends AbAttr {}
|
||||
export class PostBiomeChangeAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr {
|
||||
private weatherType: WeatherType;
|
||||
@ -4914,11 +4887,11 @@ export interface PostMoveUsedAbAttrParams extends AbAttrBaseParams {
|
||||
* Triggers just after a move is used either by the opponent or the player
|
||||
*/
|
||||
export class PostMoveUsedAbAttr extends AbAttr {
|
||||
canApplyPostMoveUsed(_params: Closed<PostMoveUsedAbAttrParams>): boolean {
|
||||
canApply(_params: Closed<PostMoveUsedAbAttrParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPostMoveUsed(_params: Closed<PostMoveUsedAbAttrParams>): void {}
|
||||
apply(_params: Closed<PostMoveUsedAbAttrParams>): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4926,7 +4899,7 @@ export class PostMoveUsedAbAttr extends AbAttr {
|
||||
* @extends PostMoveUsedAbAttr
|
||||
*/
|
||||
export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
||||
override canApplyPostMoveUsed({ source, pokemon }: PostMoveUsedAbAttrParams): boolean {
|
||||
override canApply({ source, pokemon }: PostMoveUsedAbAttrParams): boolean {
|
||||
// List of tags that prevent the Dancer from replicating the move
|
||||
const forbiddenTags = [
|
||||
BattlerTagType.FLYING,
|
||||
@ -4985,11 +4958,11 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
||||
* @extends AbAttr
|
||||
*/
|
||||
export class PostItemLostAbAttr extends AbAttr {
|
||||
canApplyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): boolean {
|
||||
canApply(_params: Closed<AbAttrBaseParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): void {}
|
||||
apply(_params: Closed<AbAttrBaseParams>): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5002,7 +4975,7 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr {
|
||||
this.tagType = tagType;
|
||||
}
|
||||
|
||||
override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, _args: any[]): boolean {
|
||||
override canApply({ pokemon, simulated }: AbAttrBaseParams): boolean {
|
||||
return !pokemon.getTag(this.tagType) && !simulated;
|
||||
}
|
||||
|
||||
@ -5011,7 +4984,7 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr {
|
||||
* @param pokemon {@linkcode Pokemon} with this ability
|
||||
* @param _args N/A
|
||||
*/
|
||||
override applyPostItemLost(pokemon: Pokemon, _simulated: boolean, _args: any[]): void {
|
||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
pokemon.addTag(this.tagType);
|
||||
}
|
||||
}
|
||||
@ -5176,25 +5149,11 @@ export class CheckTrappedAbAttr extends AbAttr {
|
||||
this.arenaTrapCondition = condition;
|
||||
}
|
||||
|
||||
canApplyCheckTrapped(
|
||||
_pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_trapped: BooleanHolder,
|
||||
_otherPokemon: Pokemon,
|
||||
_args: any[],
|
||||
): boolean {
|
||||
override canApply(_params: Closed<CheckTrappedAbAttrParams>): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyCheckTrapped(
|
||||
_pokemon: Pokemon,
|
||||
_passive: boolean,
|
||||
_simulated: boolean,
|
||||
_trapped: BooleanHolder,
|
||||
_otherPokemon: Pokemon,
|
||||
_args: any[],
|
||||
): void {}
|
||||
override apply(_params: Closed<CheckTrappedAbAttrParams>): void {}
|
||||
}
|
||||
|
||||
export interface CheckTrappedAbAttrParams extends AbAttrBaseParams {
|
||||
@ -5232,7 +5191,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
|
||||
trapped.value = true;
|
||||
}
|
||||
|
||||
getTriggerMessage({ pokemon }: CheckTrappedAbAttrParams, abilityName: string): string {
|
||||
override getTriggerMessage({ pokemon }: CheckTrappedAbAttrParams, abilityName: string): string {
|
||||
return i18next.t("abilityTriggers:arenaTrap", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
abilityName,
|
||||
@ -5543,7 +5502,9 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
export class IncreasePpAbAttr extends AbAttr {}
|
||||
export class IncreasePpAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
/** @sealed */
|
||||
export class ForceSwitchOutImmunityAbAttr extends AbAttr {
|
||||
@ -5682,10 +5643,13 @@ export class InfiltratorAbAttr extends AbAttr {
|
||||
* moves as if the user had used {@linkcode MoveId.MAGIC_COAT | Magic Coat}.
|
||||
* @sealed
|
||||
*/
|
||||
export class ReflectStatusMoveAbAttr extends AbAttr {}
|
||||
export class ReflectStatusMoveAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
|
||||
/** @sealed */
|
||||
export class NoTransformAbilityAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
@ -5693,6 +5657,7 @@ export class NoTransformAbilityAbAttr extends AbAttr {
|
||||
|
||||
/** @sealed */
|
||||
export class NoFusionAbilityAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
constructor() {
|
||||
super(false);
|
||||
}
|
||||
@ -5917,6 +5882,7 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr {
|
||||
|
||||
/** @sealed */
|
||||
export class IllusionBreakAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
// TODO: Consider adding a `canApply` method that checks if the pokemon has an active illusion
|
||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
pokemon.breakIllusion();
|
||||
@ -6294,11 +6260,11 @@ export interface PostDamageAbAttrParams extends AbAttrBaseParams {
|
||||
* Triggers after the Pokemon takes any damage
|
||||
*/
|
||||
export class PostDamageAbAttr extends AbAttr {
|
||||
public canApplyPostDamage(_params: PostDamageAbAttrParams): boolean {
|
||||
override canApply(_params: PostDamageAbAttrParams): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public applyPostDamage(_params: PostDamageAbAttrParams): void {}
|
||||
override apply(_params: PostDamageAbAttrParams): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type { AbAttrParamMap } from "#app/@types/ab-attr-types";
|
||||
import type { AbAttr, AbAttrBaseParams, AbAttrMap, CallableAbAttrString } from "#app/@types/ability-types";
|
||||
import type { AbAttrParamMap } from "#app/@types/ability-types";
|
||||
import type { AbAttr, AbAttrBaseParams, AbAttrString, CallableAbAttrString } from "#app/@types/ability-types";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
|
||||
function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
function applySingleAbAttrs<T extends AbAttrString>(
|
||||
attrType: T,
|
||||
params: AbAttrParamMap[T],
|
||||
gainedMidTurn = false,
|
||||
@ -15,11 +15,10 @@ function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
|
||||
const attr = 1 as unknown as AbAttr;
|
||||
|
||||
if (attr.is("BlockRedirectAbAttr")) {
|
||||
attr
|
||||
if (attr.is("BypassSpeedChanceAbAttr")) {
|
||||
attr;
|
||||
}
|
||||
|
||||
|
||||
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
|
||||
if (
|
||||
gainedMidTurn &&
|
||||
@ -30,12 +29,15 @@ function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// typescript assert
|
||||
// typescript assert
|
||||
for (const attr of ability.getAttrs(attrType)) {
|
||||
const condition = attr.getCondition();
|
||||
let abShown = false;
|
||||
if ((condition && !condition(pokemon)) || !attr.canApply(params)) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -45,6 +47,7 @@ function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true);
|
||||
abShown = true;
|
||||
}
|
||||
// @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) {
|
||||
@ -53,7 +56,8 @@ function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
|
||||
// @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);
|
||||
@ -69,31 +73,42 @@ function applySingleAbAttrs<T extends CallableAbAttrString>(
|
||||
|
||||
function applyAbAttrsInternal<T extends CallableAbAttrString>(
|
||||
attrType: T,
|
||||
params: Parameters<AbAttrMap[T]["apply"]>[0],
|
||||
params: AbAttrParamMap[T],
|
||||
messages: string[] = [],
|
||||
gainedMidTurn = false,
|
||||
) {
|
||||
const { pokemon } = params;
|
||||
// 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(attrType, { ...params, passive }, gainedMidTurn, 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param attrType - The type of the ability attribute to apply
|
||||
* @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,
|
||||
params: Parameters<AbAttrMap[T]["apply"]>[0],
|
||||
params: AbAttrParamMap[T],
|
||||
messages?: string[],
|
||||
): void {
|
||||
applyAbAttrsInternal(attrType, params);
|
||||
applyAbAttrsInternal(attrType, params, messages);
|
||||
}
|
||||
|
||||
|
||||
// TODO: Improve the type signatures of the following methods / refactor the apply methods
|
||||
|
||||
/**
|
||||
@ -108,17 +123,8 @@ export function applyOnGainAbAttrs(params: AbAttrBaseParams): void {
|
||||
/**
|
||||
* Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather)
|
||||
*/
|
||||
export function applyOnLoseAbAttrs(params): void {
|
||||
applySingleAbAttrs("PreLeaveFieldAbAttr");
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1438,7 +1438,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 +1454,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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2553,7 +2553,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);
|
||||
@ -2569,7 +2569,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ export class Arena {
|
||||
pokemon.findAndRemoveTags(
|
||||
t => "weatherTypes" in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather),
|
||||
);
|
||||
applyAbAttrs("PostWeatherChangeAbAttr", {pokemon, weather});
|
||||
applyAbAttrs("PostWeatherChangeAbAttr", { pokemon, weather });
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -457,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,18 @@ 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 cancelled = new BooleanHolder(false);
|
||||
const power = new NumberHolder(move.power);
|
||||
applyAbAttrs("MoveTypeChangeAbAttr", {
|
||||
pokemon: this,
|
||||
move,
|
||||
simulated,
|
||||
moveType: moveTypeHolder,
|
||||
cancelled,
|
||||
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 +2352,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 +2391,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 +2435,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 +2459,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 +3379,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 +3423,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 +3445,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 +3593,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 +3740,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
null,
|
||||
multiStrikeEnhancementMultiplier,
|
||||
);
|
||||
|
||||
if (!ignoreSourceAbility) {
|
||||
applyPreAttackAbAttrs(
|
||||
"AddSecondStrikeAbAttr",
|
||||
source,
|
||||
this,
|
||||
applyAbAttrs("AddSecondStrikeAbAttr", {
|
||||
pokemon: source,
|
||||
opponent: this,
|
||||
move,
|
||||
simulated,
|
||||
null,
|
||||
multiStrikeEnhancementMultiplier,
|
||||
);
|
||||
cancelled: new BooleanHolder(false),
|
||||
multiplier: multiStrikeEnhancementMultiplier,
|
||||
});
|
||||
}
|
||||
|
||||
/** Doubles damage if this Pokemon's last move was Glaive Rush */
|
||||
@ -3726,7 +3760,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 +3781,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 +3849,15 @@ 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,
|
||||
// cancelled isn't necessary for this ability attribute, but is required by the interface
|
||||
cancelled: new BooleanHolder(false),
|
||||
});
|
||||
}
|
||||
|
||||
/** Apply the enemy's Damage and Resistance tokens */
|
||||
@ -3822,14 +3868,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
globalScene.applyModifiers(EnemyDamageReducerModifier, false, damage);
|
||||
}
|
||||
|
||||
const abAttrParams: PreAttackModifyDamageAbAttrParams = {
|
||||
pokemon: this,
|
||||
opponent: source,
|
||||
move,
|
||||
cancelled,
|
||||
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 +3895,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 +3933,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 +3944,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 +4056,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 });
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
@ -4046,11 +4104,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const stubTag = new BattlerTag(tagType, 0, 0);
|
||||
|
||||
const cancelled = new BooleanHolder(false);
|
||||
applyAbAttrs("BattlerTagImmunityAbAttr", this, stubTag, cancelled, true, this);
|
||||
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 +4130,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 +4661,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 +4692,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 +4748,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 +4792,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 +4853,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 +4913,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 +5462,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 +5522,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;
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 () => {
|
||||
|
Loading…
Reference in New Issue
Block a user