Update callsites in moves.ts

This commit is contained in:
Sirz Benjie 2025-06-14 14:18:29 -05:00
parent b9781bd863
commit 8f66dbe69b
No known key found for this signature in database
GPG Key ID: 38AC42D68CF5E138
4 changed files with 76 additions and 53 deletions

View File

@ -465,7 +465,9 @@ type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move
* Often extended by other interfaces to add more parameters.
* Used, e.g. by {@linkcode PreDefendAbAttr} and {@linkcode PostAttackAbAttr}
*/
export interface AugmentMoveInteractionAbAttrParams extends AbAttrParamsWithCancel {
// TODO: Consider making this not require `cancelled`, as many abilities do not do anything with the parameter.
// Leaving it in bloats callsites.
export interface AugmentMoveInteractionAbAttrParams extends AbAttrBaseParams {
/** The move used by (or against, for defend attributes) */
move: Move;
/** The pokemon on the other side of the interaction*/
@ -593,6 +595,8 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip
export interface TypeMultiplierAbAttrParams extends AugmentMoveInteractionAbAttrParams {
/** Holds the type multiplier of an attack. In the case of an immunity, this value will be set to 0. */
typeMultiplier: NumberHolder;
/** Its particular meaning depends on the ability attribute, though usually means that the "no effect" message should not be played */
cancelled: BooleanHolder;
}
/**
@ -778,8 +782,13 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr {
}
}
export interface FieldPriorityMoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams {
/** Holds whether the pokemon is immune to the move being used */
cancelled: BooleanHolder;
}
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
override canApply({ move, opponent: attacker }: AugmentMoveInteractionAbAttrParams): boolean {
override canApply({ move, opponent: attacker }: FieldPriorityMoveImmunityAbAttrParams): boolean {
return (
!(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) &&
move.getPriority(attacker) > 0 &&
@ -787,11 +796,17 @@ export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
);
}
override apply({ cancelled }: AugmentMoveInteractionAbAttrParams): void {
override apply({ cancelled }: FieldPriorityMoveImmunityAbAttrParams): void {
cancelled.value = true;
}
}
export interface MoveImmunityAbAttrParams extends AugmentMoveInteractionAbAttrParams {
/** Holds whether the standard "no effect" message (due to a type-based immunity) should be suppressed */
cancelled: BooleanHolder;
}
// TODO: Consider examining whether the this move immunity ability attribute
// can be merged with the MoveTypeMultiplierAbAttr in some way.
export class MoveImmunityAbAttr extends PreDefendAbAttr {
private immuneCondition: PreDefendAbAttrCondition;
@ -801,15 +816,17 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr {
this.immuneCondition = immuneCondition;
}
override canApply({ pokemon, opponent: attacker, move }: AugmentMoveInteractionAbAttrParams): boolean {
override canApply({ pokemon, opponent: attacker, move }: MoveImmunityAbAttrParams): boolean {
// TODO: Investigate whether this method should be checking against `cancelled`, specifically
// if not checking this results in multiple flyouts showing when multiple abilities block the move.
return this.immuneCondition(pokemon, attacker, move);
}
override apply({ cancelled }: AugmentMoveInteractionAbAttrParams): void {
override apply({ cancelled }: MoveImmunityAbAttrParams): void {
cancelled.value = true;
}
override getTriggerMessage({ pokemon }: AugmentMoveInteractionAbAttrParams, _abilityName: string): string {
override getTriggerMessage({ pokemon }: MoveImmunityAbAttrParams, _abilityName: string): string {
return i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) });
}
}
@ -847,13 +864,13 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr {
this.stages = stages;
}
override canApply(params: AugmentMoveInteractionAbAttrParams): boolean {
override canApply(params: MoveImmunityAbAttrParams): boolean {
// TODO: Evaluate whether it makes sense to check against simulated here.
// We likely want to check 'simulated' when the apply method enqueues the phase
return !params.simulated && super.canApply(params);
}
override apply(params: AugmentMoveInteractionAbAttrParams): void {
override apply(params: MoveImmunityAbAttrParams): void {
super.apply(params);
// TODO: We probably should not unshift the phase if this is simulated
globalScene.phaseManager.unshiftNew(
@ -1479,8 +1496,14 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr {
}
}
export interface FieldPreventExplosiveMovesAbAttrParams extends AbAttrBaseParams {
/** Holds whether the explosive move should be prevented*/
cancelled: BooleanHolder;
}
export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
override apply({ cancelled }: AugmentMoveInteractionAbAttrParams): void {
// TODO: investigate whether we need to check against `cancelled` in a `canApply` method
override apply({ cancelled }: FieldPreventExplosiveMovesAbAttrParams): void {
cancelled.value = true;
}
}
@ -5327,8 +5350,6 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
pokemon: otherPokemon,
simulated,
cancelled,
move,
opponent: pokemon,
});
}
return !(!diedToDirectDamage || cancelled.value || attacker.hasAbilityWithAttr("BlockNonDirectDamageAbAttr"));

View File

@ -88,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;
@ -339,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)
@ -637,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;
}
@ -754,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;
@ -797,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(
@ -819,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;
@ -850,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;
}
@ -1302,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;
@ -1310,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;
}
@ -1688,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) {
@ -1822,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
@ -2021,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) {
@ -2393,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) {
@ -2501,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;
}
}
@ -2658,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;
@ -2775,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);
}
}
@ -2801,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;
}
@ -2815,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);
@ -3006,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;
};
}
@ -5418,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;
}
@ -6417,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) });
}
}
@ -6458,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;
}
@ -7967,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 }));

View File

@ -2278,14 +2278,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyMoveAttrs("VariableMoveTypeAttr", this, null, move, moveTypeHolder);
const cancelled = new BooleanHolder(false);
const power = new NumberHolder(move.power);
applyAbAttrs("MoveTypeChangeAbAttr", {
pokemon: this,
move,
simulated,
moveType: moveTypeHolder,
cancelled,
power,
opponent: this,
});
@ -3747,7 +3745,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
opponent: this,
move,
simulated,
cancelled: new BooleanHolder(false),
multiplier: multiStrikeEnhancementMultiplier,
});
}
@ -3855,8 +3852,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
move,
simulated,
damage,
// cancelled isn't necessary for this ability attribute, but is required by the interface
cancelled: new BooleanHolder(false),
});
}
@ -3872,7 +3867,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
pokemon: this,
opponent: source,
move,
cancelled,
simulated,
damage,
};

View File

@ -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;