From 7ba7c9c529646d7762b7c1d1fd1feacb4318e7ad Mon Sep 17 00:00:00 2001 From: Wlowscha <54003515+Wlowscha@users.noreply.github.com> Date: Sun, 1 Jun 2025 23:00:15 +0200 Subject: [PATCH] Refactored parameters passed to .apply() methods; introduced generic .applyHeldItems() function; all HeldItems classes specify an ITEM_EFFECT --- src/data/moves/move.ts | 9 +++-- src/items/all-held-items.ts | 33 ++++++++++++++--- src/items/held-item.ts | 27 +++++++------- src/items/held-items/attack-type-booster.ts | 28 ++++++++------- src/items/held-items/hit-heal.ts | 23 ++++++------ .../held-items/reset-negative-stat-stage.ts | 36 +++++++++---------- .../{turn-heal.ts => turn-end-heal.ts} | 25 ++++++------- src/phases/move-effect-phase.ts | 5 +-- src/phases/stat-stage-change-phase.ts | 5 +-- src/phases/turn-end-phase.ts | 5 +-- 10 files changed, 110 insertions(+), 86 deletions(-) rename src/items/held-items/{turn-heal.ts => turn-end-heal.ts} (69%) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 08d64a6d9fd..685fc087e3e 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -124,7 +124,8 @@ import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; import { TrainerVariant } from "#app/field/trainer"; import { SelectBiomePhase } from "#app/phases/select-biome-phase"; -import { applyAttackTypeBoosterHeldItem } from "#app/items/held-items/attack-type-booster"; +import { applyHeldItems } from "#app/items/all-held-items"; +import { ITEM_EFFECT } from "#app/items/held-item"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -853,7 +854,11 @@ export default class Move implements Localizable { if (!this.hasAttr(TypelessAttr)) { globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power); - applyAttackTypeBoosterHeldItem(source, typeChangeHolder.value, power); + applyHeldItems(ITEM_EFFECT.ATTACK_TYPE_BOOST, { + pokemon: source, + moveType: typeChangeHolder.value, + movePower: power, + }); } if (source.getTag(HelpingHandTag)) { diff --git a/src/items/all-held-items.ts b/src/items/all-held-items.ts index de745f65004..4b382f2992d 100644 --- a/src/items/all-held-items.ts +++ b/src/items/all-held-items.ts @@ -1,8 +1,15 @@ import { HeldItems } from "#enums/held-items"; import type { PokemonType } from "#enums/pokemon-type"; -import { AttackTypeBoosterHeldItem, attackTypeToHeldItem } from "./held-items/attack-type-booster"; -import { HitHealHeldItem } from "./held-items/hit-heal"; -import { TurnHealHeldItem } from "./held-items/turn-heal"; +import { ITEM_EFFECT } from "./held-item"; +import { + type ATTACK_TYPE_BOOST_PARAMS, + AttackTypeBoosterHeldItem, + attackTypeToHeldItem, +} from "./held-items/attack-type-booster"; +import { type HIT_HEAL_PARAMS, HitHealHeldItem } from "./held-items/hit-heal"; +import type { RESET_NEGATIVE_STAT_STAGE_PARAMS } from "./held-items/reset-negative-stat-stage"; +import type { TURN_END_HEAL_PARAMS } from "./held-items/turn-end-heal"; +import { TurnEndHealHeldItem } from "./held-items/turn-end-heal"; export const allHeldItems = {}; @@ -12,7 +19,25 @@ export function initHeldItems() { const pokemonType = Number(typeKey) as PokemonType; allHeldItems[heldItemType] = new AttackTypeBoosterHeldItem(heldItemType, 99, pokemonType, 0.2); } - allHeldItems[HeldItems.LEFTOVERS] = new TurnHealHeldItem(HeldItems.LEFTOVERS, 4); + allHeldItems[HeldItems.LEFTOVERS] = new TurnEndHealHeldItem(HeldItems.LEFTOVERS, 4); allHeldItems[HeldItems.SHELL_BELL] = new HitHealHeldItem(HeldItems.LEFTOVERS, 4); console.log(allHeldItems); } + +type APPLY_HELD_ITEMS_PARAMS = { + [ITEM_EFFECT.ATTACK_TYPE_BOOST]: ATTACK_TYPE_BOOST_PARAMS; + [ITEM_EFFECT.TURN_END_HEAL]: TURN_END_HEAL_PARAMS; + [ITEM_EFFECT.HIT_HEAL]: HIT_HEAL_PARAMS; + [ITEM_EFFECT.RESET_NEGATIVE_STAT_STAGE]: RESET_NEGATIVE_STAT_STAGE_PARAMS; +}; + +export function applyHeldItems(effect: T, params: APPLY_HELD_ITEMS_PARAMS[T]) { + const pokemon = params.pokemon; + if (pokemon) { + for (const item of Object.keys(pokemon.heldItemManager.heldItems)) { + if (allHeldItems[item].effects.includes(effect)) { + allHeldItems[item].apply(params); + } + } + } +} diff --git a/src/items/held-item.ts b/src/items/held-item.ts index db0aa70ce20..c2b5d10bb25 100644 --- a/src/items/held-item.ts +++ b/src/items/held-item.ts @@ -2,6 +2,15 @@ import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import type { HeldItems } from "#enums/held-items"; +export const ITEM_EFFECT = { + ATTACK_TYPE_BOOST: 1, + TURN_END_HEAL: 2, + HIT_HEAL: 3, + RESET_NEGATIVE_STAT_STAGE: 4, +} as const; + +export type ITEM_EFFECT = (typeof ITEM_EFFECT)[keyof typeof ITEM_EFFECT]; + export class HeldItem { // public pokemonId: number; public type: HeldItems; @@ -109,20 +118,10 @@ export class HeldItem { } export class ConsumableHeldItem extends HeldItem { - applyConsumable(_pokemon: Pokemon): boolean { + consume(pokemon: Pokemon, isPlayer: boolean): boolean { + pokemon.heldItemManager.remove(this.type, 1); + // TODO: Turn this into updateItemBar or something + globalScene.updateModifiers(isPlayer); return true; } - - apply(pokemon: Pokemon, isPlayer: boolean): boolean { - const consumed = this.applyConsumable(pokemon); - - if (consumed) { - pokemon.heldItemManager.remove(this.type, 1); - // TODO: Turn this into updateItemBar or something - globalScene.updateModifiers(isPlayer); - return true; - } - - return false; - } } diff --git a/src/items/held-items/attack-type-booster.ts b/src/items/held-items/attack-type-booster.ts index 275d1d5b8d5..4d1df7af8ff 100644 --- a/src/items/held-items/attack-type-booster.ts +++ b/src/items/held-items/attack-type-booster.ts @@ -3,8 +3,16 @@ import { PokemonType } from "#enums/pokemon-type"; import i18next from "i18next"; import type { NumberHolder } from "#app/utils/common"; import type Pokemon from "#app/field/pokemon"; -import { HeldItem } from "#app/items/held-item"; -import { allHeldItems } from "../all-held-items"; +import { HeldItem, ITEM_EFFECT } from "#app/items/held-item"; + +export interface ATTACK_TYPE_BOOST_PARAMS { + /** The pokemon with the item */ + pokemon: Pokemon; + /** The resolved type of the move */ + moveType: PokemonType; + /** Holder for the damage value */ + movePower: NumberHolder; +} interface AttackTypeToHeldItemMap { [key: number]: HeldItems; @@ -32,6 +40,7 @@ export const attackTypeToHeldItem: AttackTypeToHeldItemMap = { }; export class AttackTypeBoosterHeldItem extends HeldItem { + public effects: ITEM_EFFECT[] = [ITEM_EFFECT.TURN_END_HEAL]; public moveType: PokemonType; public powerBoost: number; @@ -55,20 +64,13 @@ export class AttackTypeBoosterHeldItem extends HeldItem { return `${HeldItemNames[this.type]?.toLowerCase()}`; } - apply(pokemon: Pokemon, moveType: PokemonType, movePower: NumberHolder): void { + apply(params: ATTACK_TYPE_BOOST_PARAMS): void { + const pokemon = params.pokemon; + const moveType = params.moveType; + const movePower = params.movePower; const stackCount = pokemon.heldItemManager.getStack(this.type); if (moveType === this.moveType && movePower.value >= 1) { movePower.value = Math.floor(movePower.value * (1 + stackCount * this.powerBoost)); } } } - -export function applyAttackTypeBoosterHeldItem(pokemon: Pokemon, moveType: PokemonType, movePower: NumberHolder) { - if (pokemon) { - for (const item of Object.keys(pokemon.heldItemManager.heldItems)) { - if (allHeldItems[item] instanceof AttackTypeBoosterHeldItem) { - allHeldItems[item].apply(pokemon, moveType, movePower); - } - } - } -} diff --git a/src/items/held-items/hit-heal.ts b/src/items/held-items/hit-heal.ts index eb32dccf104..dd2f8c73eeb 100644 --- a/src/items/held-items/hit-heal.ts +++ b/src/items/held-items/hit-heal.ts @@ -1,13 +1,19 @@ import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import i18next from "i18next"; -import { HeldItem } from "#app/items/held-item"; +import { HeldItem, ITEM_EFFECT } from "#app/items/held-item"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { toDmgValue } from "#app/utils/common"; import { getPokemonNameWithAffix } from "#app/messages"; -import { allHeldItems } from "../all-held-items"; + +export interface HIT_HEAL_PARAMS { + /** The pokemon with the item */ + pokemon: Pokemon; +} export class HitHealHeldItem extends HeldItem { + public effects: ITEM_EFFECT[] = [ITEM_EFFECT.TURN_END_HEAL]; + get name(): string { return i18next.t("modifierType:ModifierType.SHELL_BELL.name"); } @@ -25,7 +31,8 @@ export class HitHealHeldItem extends HeldItem { * @param pokemon The {@linkcode Pokemon} that holds the item * @returns `true` if the {@linkcode Pokemon} was healed */ - apply(pokemon: Pokemon): boolean { + apply(params: HIT_HEAL_PARAMS): boolean { + const pokemon = params.pokemon; const stackCount = pokemon.heldItemManager.getStack(this.type); if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) { // TODO: this shouldn't be undefined AFAIK @@ -44,13 +51,3 @@ export class HitHealHeldItem extends HeldItem { return true; } } - -export function applyHitHealHeldItem(pokemon: Pokemon) { - if (pokemon) { - for (const item of Object.keys(pokemon.heldItemManager.heldItems)) { - if (allHeldItems[item] instanceof HitHealHeldItem) { - allHeldItems[item].apply(pokemon); - } - } - } -} diff --git a/src/items/held-items/reset-negative-stat-stage.ts b/src/items/held-items/reset-negative-stat-stage.ts index d27514ec888..90a2e2cd8d6 100644 --- a/src/items/held-items/reset-negative-stat-stage.ts +++ b/src/items/held-items/reset-negative-stat-stage.ts @@ -2,9 +2,15 @@ import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import { BATTLE_STATS } from "#enums/stat"; import i18next from "i18next"; -import { ConsumableHeldItem } from "../held-item"; +import { ConsumableHeldItem, ITEM_EFFECT } from "../held-item"; import { getPokemonNameWithAffix } from "#app/messages"; -import { allHeldItems } from "../all-held-items"; + +export interface RESET_NEGATIVE_STAT_STAGE_PARAMS { + /** The pokemon with the item */ + pokemon: Pokemon; + /** Whether the move was used by a player pokemon */ + isPlayer: boolean; +} /** * Modifier used for held items, namely White Herb, that restore adverse stat @@ -13,6 +19,8 @@ import { allHeldItems } from "../all-held-items"; * @see {@linkcode apply} */ export class ResetNegativeStatStageHeldItem extends ConsumableHeldItem { + public effects: ITEM_EFFECT[] = [ITEM_EFFECT.RESET_NEGATIVE_STAT_STAGE]; + get name(): string { return i18next.t("modifierType:ModifierType.WHITE_HERB.name"); } @@ -30,7 +38,9 @@ export class ResetNegativeStatStageHeldItem extends ConsumableHeldItem { * @param pokemon {@linkcode Pokemon} that holds the item * @returns `true` if any stat stages were reset, false otherwise */ - applyConsumable(pokemon: Pokemon): boolean { + apply(params: RESET_NEGATIVE_STAT_STAGE_PARAMS): boolean { + const pokemon = params.pokemon; + const isPlayer = params.isPlayer; let statRestored = false; for (const s of BATTLE_STATS) { @@ -47,24 +57,10 @@ export class ResetNegativeStatStageHeldItem extends ConsumableHeldItem { typeName: this.name, }), ); + + this.consume(pokemon, isPlayer); } + return statRestored; } - - getMaxHeldItemCount(_pokemon: Pokemon): number { - return 2; - } -} - -// TODO: Do we need this to return true/false? -export function applyResetNegativeStatStageHeldItem(pokemon: Pokemon): boolean { - let applied = false; - if (pokemon) { - for (const item of Object.keys(pokemon.heldItemManager.heldItems)) { - if (allHeldItems[item] instanceof ResetNegativeStatStageHeldItem) { - applied ||= allHeldItems[item].apply(pokemon); - } - } - } - return applied; } diff --git a/src/items/held-items/turn-heal.ts b/src/items/held-items/turn-end-heal.ts similarity index 69% rename from src/items/held-items/turn-heal.ts rename to src/items/held-items/turn-end-heal.ts index c48205022e6..1bad2e42e7d 100644 --- a/src/items/held-items/turn-heal.ts +++ b/src/items/held-items/turn-end-heal.ts @@ -1,13 +1,19 @@ import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import i18next from "i18next"; -import { HeldItem } from "#app/items/held-item"; +import { HeldItem, ITEM_EFFECT } from "#app/items/held-item"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { toDmgValue } from "#app/utils/common"; import { getPokemonNameWithAffix } from "#app/messages"; -import { allHeldItems } from "../all-held-items"; -export class TurnHealHeldItem extends HeldItem { +export interface TURN_END_HEAL_PARAMS { + /** The pokemon with the item */ + pokemon: Pokemon; +} + +export class TurnEndHealHeldItem extends HeldItem { + public effects: ITEM_EFFECT[] = [ITEM_EFFECT.TURN_END_HEAL]; + get name(): string { return i18next.t("modifierType:ModifierType.LEFTOVERS.name") + " (new)"; } @@ -20,7 +26,8 @@ export class TurnHealHeldItem extends HeldItem { return "leftovers"; } - apply(pokemon: Pokemon): boolean { + apply(params: TURN_END_HEAL_PARAMS): boolean { + const pokemon = params.pokemon; const stackCount = pokemon.heldItemManager.getStack(this.type); if (pokemon.isFullHp()) { return false; @@ -39,13 +46,3 @@ export class TurnHealHeldItem extends HeldItem { return true; } } - -export function applyTurnHealHeldItem(pokemon: Pokemon) { - if (pokemon) { - for (const item of Object.keys(pokemon.heldItemManager.heldItems)) { - if (allHeldItems[item] instanceof TurnHealHeldItem) { - allHeldItems[item].apply(pokemon); - } - } - } -} diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 15df6e35355..6eec1be2216 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -78,7 +78,8 @@ import type Move from "#app/data/moves/move"; import { isFieldTargeted } from "#app/data/moves/move-utils"; import { FaintPhase } from "./faint-phase"; import { DamageAchv } from "#app/system/achv"; -import { applyHitHealHeldItem } from "#app/items/held-items/hit-heal"; +import { applyHeldItems } from "#app/items/all-held-items"; +import { ITEM_EFFECT } from "#app/items/held-item"; type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier]; @@ -417,7 +418,7 @@ export class MoveEffectPhase extends PokemonPhase { // If there are multiple hits, or if there are hits of the multi-hit move left globalScene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } - applyHitHealHeldItem(user); + applyHeldItems(ITEM_EFFECT.HIT_HEAL, { pokemon: user }); this.getTargets().forEach(target => (target.turnData.moveEffectiveness = null)); } } diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 77ed54979db..f8a9c7124a4 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -22,7 +22,8 @@ import { PokemonPhase } from "./pokemon-phase"; import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; import { OctolockTag } from "#app/data/battler-tags"; import { ArenaTagType } from "#app/enums/arena-tag-type"; -import { applyResetNegativeStatStageHeldItem } from "#app/items/held-items/reset-negative-stat-stage"; +import { applyHeldItems } from "#app/items/all-held-items"; +import { ITEM_EFFECT } from "#app/items/held-item"; export type StatStageChangeCallback = ( target: Pokemon | null, @@ -239,7 +240,7 @@ export class StatStageChangePhase extends PokemonPhase { ); if (!(existingPhase instanceof StatStageChangePhase)) { // Apply White Herb if needed - applyResetNegativeStatStageHeldItem(pokemon); + applyHeldItems(ITEM_EFFECT.RESET_NEGATIVE_STAT_STAGE, { pokemon: pokemon, isPlayer: this.player }); } pokemon.updateInfo(); diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 2f6b46d190c..184fbc491e1 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -15,7 +15,8 @@ import i18next from "i18next"; import { FieldPhase } from "./field-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase"; import { globalScene } from "#app/global-scene"; -import { applyTurnHealHeldItem } from "#app/items/held-items/turn-heal"; +import { applyHeldItems } from "#app/items/all-held-items"; +import { ITEM_EFFECT } from "#app/items/held-item"; export class TurnEndPhase extends FieldPhase { start() { @@ -30,7 +31,7 @@ export class TurnEndPhase extends FieldPhase { if (!pokemon.switchOutStatus) { pokemon.lapseTags(BattlerTagLapseType.TURN_END); - applyTurnHealHeldItem(pokemon); + applyHeldItems(ITEM_EFFECT.TURN_END_HEAL, { pokemon: pokemon }); if (globalScene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { globalScene.unshiftPhase(