Fix missing types in battler tags

This commit is contained in:
Sirz Benjie 2025-07-31 22:50:25 -06:00
parent 1c59b67d7e
commit 69430d3536
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
2 changed files with 54 additions and 30 deletions

View File

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

View File

@ -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<T extends BattlerTagType> 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.CHARGING>;
[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.ALWAYS_CRIT>;
[BattlerTagType.IGNORE_ACCURACY]: GenericSerializableBattlerTag<BattlerTagType.IGNORE_ACCURACY>;
[BattlerTagType.ALWAYS_GET_HIT]: GenericSerializableBattlerTag<BattlerTagType.ALWAYS_GET_HIT>;
[BattlerTagType.RECEIVE_DOUBLE_DAMAGE]: GenericSerializableBattlerTag<BattlerTagType.RECEIVE_DOUBLE_DAMAGE>;
[BattlerTagType.BYPASS_SLEEP]: BattlerTag;
[BattlerTagType.IGNORE_FLYING]: GroundedTag;
[BattlerTagType.ROOSTED]: RoostedTag;