From 1deb74e926f7bc161e39ec1b425a2185e82e10a2 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Sat, 14 Jun 2025 16:44:58 -0500 Subject: [PATCH] Update abattr callsites in move-phase --- src/battle-scene.ts | 14 +++++------ src/data/arena-tag.ts | 2 +- src/data/berry.ts | 24 +++++++++---------- src/modifier/modifier.ts | 6 ++--- src/phases/berry-phase.ts | 4 ++-- src/phases/encounter-phase.ts | 6 ++--- src/phases/faint-phase.ts | 28 +++++++++++----------- src/phases/move-phase.ts | 44 +++++++++++++++++++++++------------ 8 files changed, 70 insertions(+), 58 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b802466ee19..2a6c0a2a49c 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -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) { diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index fe9a6be06f4..c8dde455ea4 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -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; } diff --git a/src/data/berry.ts b/src/data/berry.ts index 69853547624..be6e5c28f84 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -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, stat}); - 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", { pokemon: consumer, effectValue: hpHealed, cancelled: null}); + 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(), diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 54b7323569a..1a6a44cc1f5 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -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; } diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 3348fa3902f..61124a7cda8 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -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 }); } } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index f2c23384627..52c2b2e465d 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -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); diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 675a198d096..c2658b62b23 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -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) { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 41a1042387b..20238c5320f 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -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); @@ -246,7 +243,10 @@ export class MovePhase extends BattlePhase { !!this.move .getMove() .findAttr( - attr => attr.is("HealStatusEffectAttr") && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE), + attr => + attr.is("HealStatusEffectAttr") && + attr.selfTarget && + (attr as unknown as HealStatusEffectAttr).isOfEffect(StatusEffect.FREEZE), ) || (!this.pokemon.randBattleSeedInt(5) && Overrides.STATUS_ACTIVATION_OVERRIDE !== true) || Overrides.STATUS_ACTIVATION_OVERRIDE === false; @@ -396,7 +396,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 +407,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 +443,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 +475,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 +532,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 */