[Misc] Fix missing types in battler tags and remove DragonCheerTag (#6188)

* Fix missing types in battler tags

* Fix issues with dragon cheer
This commit is contained in:
Sirz Benjie 2025-08-01 12:51:11 -06:00 committed by GitHub
parent 1c59b67d7e
commit e50ebaa815
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 32 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,13 +2318,24 @@ 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 */
public readonly critStages: number;
constructor(tagType: CritStageBoostTagType, sourceMove: MoveId) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true);
}
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
// Dragon cheer adds +2 crit stages if the pokemon is a Dragon type when the tag is added
if (this.tagType === BattlerTagType.DRAGON_CHEER && pokemon.getTypes(true).includes(PokemonType.DRAGON)) {
(this as Mutable<this>).critStages = 2;
} else {
(this as Mutable<this>).critStages = 1;
}
globalScene.phaseManager.queueMessage(
i18next.t("battlerTags:critBoostOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
@ -2323,23 +2356,12 @@ 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);
public override loadTag(source: BaseBattlerTag & Pick<CritBoostTag, "tagType" | "critStages">): void {
super.loadTag(source);
// TODO: Remove the nullish coalescing once Zod Schemas come in
// For now, this is kept for backwards compatibility with older save files
(this as Mutable<this>).critStages = source.critStages ?? 1;
}
}
@ -2398,6 +2420,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 +2467,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 +2478,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);
}
}
@ -3719,9 +3744,8 @@ export function getBattlerTag(
case BattlerTagType.FIRE_BOOST:
return new TypeBoostTag(tagType, sourceMove, PokemonType.FIRE, 1.5, false);
case BattlerTagType.CRIT_BOOST:
return new CritBoostTag(tagType, sourceMove);
case BattlerTagType.DRAGON_CHEER:
return new DragonCheerTag();
return new CritBoostTag(tagType, sourceMove);
case BattlerTagType.ALWAYS_CRIT:
case BattlerTagType.IGNORE_ACCURACY:
return new SerializableBattlerTag(tagType, BattlerTagLapseType.TURN_END, 2, sourceMove);
@ -3813,7 +3837,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 +3878,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 +3918,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;

View File

@ -25,7 +25,6 @@ import {
AutotomizedTag,
BattlerTag,
CritBoostTag,
DragonCheerTag,
EncoreTag,
ExposedTag,
GroundedTag,
@ -1390,8 +1389,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
const critBoostTag = source.getTag(CritBoostTag);
if (critBoostTag) {
// Dragon cheer only gives +1 crit stage to non-dragon types
critStage.value +=
critBoostTag instanceof DragonCheerTag && !critBoostTag.typesOnAdd.includes(PokemonType.DRAGON) ? 1 : 2;
critStage.value += critBoostTag.critStages;
}
console.log(`crit stage: +${critStage.value}`);