Update abattr callsites in move-phase

This commit is contained in:
Sirz Benjie 2025-06-14 16:44:58 -05:00
parent 8f66dbe69b
commit 1deb74e926
No known key found for this signature in database
GPG Key ID: 38AC42D68CF5E138
8 changed files with 70 additions and 58 deletions

View File

@ -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) {

View File

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

View File

@ -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(),

View File

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

View File

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

View File

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

View File

@ -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) {

View File

@ -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 */