From 69430d3536ee3251aa4477d6a9e9f5365dcbb87c Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 31 Jul 2025 22:50:25 -0600 Subject: [PATCH] Fix missing types in battler tags --- src/@types/battler-tags.ts | 9 +++++ src/data/battler-tags.ts | 75 +++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/@types/battler-tags.ts b/src/@types/battler-tags.ts index 0057280e4e5..211eb25113d 100644 --- a/src/@types/battler-tags.ts +++ b/src/@types/battler-tags.ts @@ -88,6 +88,15 @@ export type AbilityBattlerTagType = | BattlerTagType.SLOW_START | BattlerTagType.TRUANT; +/** Subset of {@linkcode BattlerTagType}s that provide type boosts */ +export type TypeBoostTagType = BattlerTagType.FIRE_BOOST | BattlerTagType.CHARGED; + +/** Subset of {@linkcode BattlerTagType}s that boost the user's critical stage */ +export type CritStageBoostTagType = BattlerTagType.CRIT_BOOST | BattlerTagType.DRAGON_CHEER; + +/** Subset of {@linkcode BattlerTagType}s that remove one of the users' types */ +export type RemovedTypeTagType = BattlerTagType.DOUBLE_SHOCKED | BattlerTagType.BURNED_UP; + /** * Subset of {@linkcode BattlerTagType}s related to abilities that boost the highest stat. */ diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index edeff293aa0..027f1782481 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -34,15 +34,19 @@ import type { StatStageChangeCallback } from "#phases/stat-stage-change-phase"; import i18next from "#plugins/i18n"; import type { AbilityBattlerTagType, + BattlerTagTypeData, ContactSetStatusProtectedTagType, ContactStatStageChangeProtectedTagType, + CritStageBoostTagType, DamageProtectedTagType, EndureTagType, HighestStatBoostTagType, MoveRestrictionBattlerTagType, ProtectionBattlerTagType, + RemovedTypeTagType, SemiInvulnerableTagType, TrappingBattlerTagType, + TypeBoostTagType, } from "#types/battler-tags"; import type { Mutable } from "#types/type-helpers"; import { BooleanHolder, coerceArray, getFrameMs, isNullOrUndefined, NumberHolder, toDmgValue } from "#utils/common"; @@ -203,6 +207,19 @@ export class SerializableBattlerTag extends BattlerTag { private declare __SerializableBattlerTag: never; } +/** + * Interface for a generic serializable battler tag, i.e. one that does not have a + * dedicated subclass. + * + * @remarks + * Used to ensure type safety when serializing battler tags, + * allowing typescript to properly infer the type of the tag. + * @see BattlerTagTypeMap + */ +interface GenericSerializableBattlerTag extends SerializableBattlerTag { + tagType: T; +} + /** * Base class for tags that restrict the usage of moves. This effect is generally referred to as "disabling" a move * in-game (not to be confused with {@linkcode MoveId.DISABLE}). @@ -560,6 +577,7 @@ export class BeakBlastChargingTag extends BattlerTag { * @see {@link https://bulbapedia.bulbagarden.net/wiki/Shell_Trap_(move) | Shell Trap} */ export class ShellTrapTag extends BattlerTag { + public override readonly tagType = BattlerTagType.SHELL_TRAP; public activated = false; constructor() { @@ -1144,6 +1162,7 @@ export class PowderTag extends BattlerTag { } export class NightmareTag extends SerializableBattlerTag { + public override readonly tagType = BattlerTagType.NIGHTMARE; constructor() { super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.TURN_END, 1, MoveId.NIGHTMARE); } @@ -1197,6 +1216,7 @@ export class NightmareTag extends SerializableBattlerTag { } export class FrenzyTag extends SerializableBattlerTag { + public override readonly tagType = BattlerTagType.FRENZY; constructor(turnCount: number, sourceMove: MoveId, sourceId: number) { super(BattlerTagType.FRENZY, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); } @@ -2199,7 +2219,7 @@ export class SemiInvulnerableTag extends SerializableBattlerTag { } } -export class TypeImmuneTag extends SerializableBattlerTag { +export abstract class TypeImmuneTag extends SerializableBattlerTag { #immuneType: PokemonType; public get immuneType(): PokemonType { return this.#immuneType; @@ -2218,6 +2238,7 @@ export class TypeImmuneTag extends SerializableBattlerTag { * @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | MoveId.TELEKINESIS} */ export class FloatingTag extends TypeImmuneTag { + public override readonly tagType = BattlerTagType.FLOATING; constructor(tagType: BattlerTagType, sourceMove: MoveId, turnCount: number) { super(tagType, sourceMove, PokemonType.GROUND, turnCount); } @@ -2247,6 +2268,7 @@ export class FloatingTag extends TypeImmuneTag { } export class TypeBoostTag extends SerializableBattlerTag { + public declare readonly tagType: TypeBoostTagType; #boostedType: PokemonType; #boostValue: number; #oneUse: boolean; @@ -2296,8 +2318,16 @@ export class TypeBoostTag extends SerializableBattlerTag { } export class CritBoostTag extends SerializableBattlerTag { - constructor(tagType: BattlerTagType, sourceMove: MoveId) { + public declare readonly tagType: CritStageBoostTagType; + /** The number of stages boosted by this tag */ + #critStages: number; + /** The number of stages boosted by this tag */ + public get critStages(): number { + return this.#critStages; + } + constructor(tagType: CritStageBoostTagType, sourceMove: MoveId, stages = 1) { super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true); + this.#critStages = stages; } onAdd(pokemon: Pokemon): void { @@ -2325,24 +2355,6 @@ export class CritBoostTag extends SerializableBattlerTag { } } -/** - * Tag for the effects of Dragon Cheer, which boosts the critical hit ratio of the user's allies. - */ -export class DragonCheerTag extends CritBoostTag { - /** The types of the user's ally when the tag is added */ - public typesOnAdd: PokemonType[]; - - constructor() { - super(BattlerTagType.CRIT_BOOST, MoveId.DRAGON_CHEER); - } - - onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); - - this.typesOnAdd = pokemon.getTypes(true); - } -} - export class SaltCuredTag extends SerializableBattlerTag { public override readonly tagType = BattlerTagType.SALT_CURED; constructor(sourceId: number) { @@ -2398,6 +2410,7 @@ export class SaltCuredTag extends SerializableBattlerTag { } export class CursedTag extends SerializableBattlerTag { + public override readonly tagType = BattlerTagType.CURSED; constructor(sourceId: number) { super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, MoveId.CURSE, sourceId, true); } @@ -2444,7 +2457,8 @@ export class CursedTag extends SerializableBattlerTag { * Battler tag for attacks that remove a type post use. */ export class RemovedTypeTag extends SerializableBattlerTag { - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: MoveId) { + public declare readonly tagType: RemovedTypeTagType; + constructor(tagType: RemovedTypeTagType, lapseType: BattlerTagLapseType, sourceMove: MoveId) { super(tagType, lapseType, 1, sourceMove); } } @@ -2454,7 +2468,8 @@ export class RemovedTypeTag extends SerializableBattlerTag { * @description `IGNORE_FLYING`: Persistent grounding effects (i.e. from Smack Down and Thousand Waves) */ export class GroundedTag extends SerializableBattlerTag { - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: MoveId) { + public override readonly tagType = BattlerTagType.IGNORE_FLYING; + constructor(tagType: BattlerTagType.IGNORE_FLYING, lapseType: BattlerTagLapseType, sourceMove: MoveId) { super(tagType, lapseType, 1, sourceMove); } } @@ -3721,7 +3736,7 @@ export function getBattlerTag( case BattlerTagType.CRIT_BOOST: return new CritBoostTag(tagType, sourceMove); case BattlerTagType.DRAGON_CHEER: - return new DragonCheerTag(); + return new CritBoostTag(tagType, sourceMove, 2); case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.IGNORE_ACCURACY: return new SerializableBattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove); @@ -3813,7 +3828,7 @@ export function getBattlerTag( * @param source - An object containing the data necessary to reconstruct the BattlerTag. * @returns The valid battler tag */ -export function loadBattlerTag(source: SerializableBattlerTag): BattlerTag { +export function loadBattlerTag(source: BattlerTag | BattlerTagTypeData): BattlerTag { // TODO: Remove this bang by fixing the signature of `getBattlerTag` // to allow undefined sourceIds and sourceMoves (with appropriate fallback for tags that require it) const tag = getBattlerTag(source.tagType, source.turnCount, source.sourceMove!, source.sourceId!); @@ -3854,7 +3869,7 @@ export type BattlerTagTypeMap = { [BattlerTagType.POWDER]: PowderTag; [BattlerTagType.NIGHTMARE]: NightmareTag; [BattlerTagType.FRENZY]: FrenzyTag; - [BattlerTagType.CHARGING]: SerializableBattlerTag; + [BattlerTagType.CHARGING]: GenericSerializableBattlerTag; [BattlerTagType.ENCORE]: EncoreTag; [BattlerTagType.HELPING_HAND]: HelpingHandTag; [BattlerTagType.INGRAIN]: IngrainTag; @@ -3894,11 +3909,11 @@ export type BattlerTagTypeMap = { [BattlerTagType.HIDDEN]: SemiInvulnerableTag; [BattlerTagType.FIRE_BOOST]: TypeBoostTag; [BattlerTagType.CRIT_BOOST]: CritBoostTag; - [BattlerTagType.DRAGON_CHEER]: DragonCheerTag; - [BattlerTagType.ALWAYS_CRIT]: SerializableBattlerTag; - [BattlerTagType.IGNORE_ACCURACY]: SerializableBattlerTag; - [BattlerTagType.ALWAYS_GET_HIT]: SerializableBattlerTag; - [BattlerTagType.RECEIVE_DOUBLE_DAMAGE]: SerializableBattlerTag; + [BattlerTagType.DRAGON_CHEER]: CritBoostTag; + [BattlerTagType.ALWAYS_CRIT]: GenericSerializableBattlerTag; + [BattlerTagType.IGNORE_ACCURACY]: GenericSerializableBattlerTag; + [BattlerTagType.ALWAYS_GET_HIT]: GenericSerializableBattlerTag; + [BattlerTagType.RECEIVE_DOUBLE_DAMAGE]: GenericSerializableBattlerTag; [BattlerTagType.BYPASS_SLEEP]: BattlerTag; [BattlerTagType.IGNORE_FLYING]: GroundedTag; [BattlerTagType.ROOSTED]: RoostedTag;