mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-04 07:22:19 +02:00
Use InstanceType for proper narrowing
This commit is contained in:
parent
5f1a9f788f
commit
37af6d9c52
@ -14,21 +14,39 @@ export type * from "#app/data/moves/move";
|
||||
|
||||
/**
|
||||
* Map of move subclass names to their respective classes.
|
||||
* Does not include the ChargeMove subclasses. For that, use `ChargingMoveClassMap`.
|
||||
*
|
||||
* @privateremarks
|
||||
* The `Never` field is necessary to ensure typescript does not improperly narrow a failed `is` guard to `never`.
|
||||
*
|
||||
* For example, if we did not have the never, and wrote
|
||||
* ```
|
||||
* function Foo(move: Move) {
|
||||
* if (move.is("AttackMove")) {
|
||||
*
|
||||
* } else if (move.is("StatusMove")) { // typescript errors on the `is`, saying that `move` is `never`
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export type MoveClassMap = {
|
||||
AttackMove: typeof AttackMove;
|
||||
StatusMove: typeof StatusMove;
|
||||
SelfStatusMove: typeof SelfStatusMove;
|
||||
ChargingAttackMove: typeof ChargingAttackMove;
|
||||
ChargingSelfStatusMove: typeof ChargingSelfStatusMove;
|
||||
ChargeMove: typeof ChargingAttackMove | typeof ChargingSelfStatusMove;
|
||||
AttackMove: AttackMove;
|
||||
StatusMove: StatusMove;
|
||||
SelfStatusMove: SelfStatusMove;
|
||||
};
|
||||
|
||||
/*
|
||||
* Without the `Never: never` field, typescript will
|
||||
*/
|
||||
|
||||
/**
|
||||
* Union type of all move subclass names
|
||||
*/
|
||||
export type MoveClass = keyof MoveClassMap;
|
||||
export type MoveKindString = "AttackMove" | "StatusMove" | "SelfStatusMove";
|
||||
|
||||
/**
|
||||
* Map of move attribute names to attribute instances.
|
||||
*/
|
||||
export type MoveAttrMap = {
|
||||
[K in keyof MoveAttrConstructorMap]: InstanceType<MoveAttrConstructorMap[K]>;
|
||||
};
|
||||
|
@ -2527,17 +2527,11 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
|
||||
* @extends AbAttr
|
||||
*/
|
||||
export class ExecutedMoveAbAttr extends AbAttr {
|
||||
canApplyExecutedMove(
|
||||
_pokemon: Pokemon,
|
||||
_simulated: boolean,
|
||||
): boolean {
|
||||
canApplyExecutedMove(_pokemon: Pokemon, _simulated: boolean): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyExecutedMove(
|
||||
_pokemon: Pokemon,
|
||||
_simulated: boolean,
|
||||
): void {}
|
||||
applyExecutedMove(_pokemon: Pokemon, _simulated: boolean): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2545,7 +2539,7 @@ export class ExecutedMoveAbAttr extends AbAttr {
|
||||
* @extends ExecutedMoveAbAttr
|
||||
*/
|
||||
export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
|
||||
constructor(showAbility: boolean = false) {
|
||||
constructor(showAbility = false) {
|
||||
super(showAbility);
|
||||
}
|
||||
|
||||
@ -7773,7 +7767,7 @@ export function applyPreAttackAbAttrs(
|
||||
export function applyExecutedMoveAbAttrs(
|
||||
attrType: Constructor<ExecutedMoveAbAttr>,
|
||||
pokemon: Pokemon,
|
||||
simulated: boolean = false,
|
||||
simulated = false,
|
||||
...args: any[]
|
||||
): void {
|
||||
applyAbAttrsInternal<ExecutedMoveAbAttr>(
|
||||
|
@ -446,12 +446,11 @@ export function initMoveAnim(move: MoveId): Promise<void> {
|
||||
}
|
||||
} else {
|
||||
moveAnims.set(move, null);
|
||||
const defaultMoveAnim =
|
||||
allMoves[move].is("AttackMove")
|
||||
? MoveId.TACKLE
|
||||
: allMoves[move].is("SelfStatusMove")
|
||||
? MoveId.FOCUS_ENERGY
|
||||
: MoveId.TAIL_WHIP;
|
||||
const defaultMoveAnim = allMoves[move].is("AttackMove")
|
||||
? MoveId.TACKLE
|
||||
: allMoves[move].is("SelfStatusMove")
|
||||
? MoveId.FOCUS_ENERGY
|
||||
: MoveId.TAIL_WHIP;
|
||||
|
||||
const fetchAnimAndResolve = (move: MoveId) => {
|
||||
globalScene
|
||||
|
@ -123,7 +123,7 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
||||
import { MultiHitType } from "#enums/MultiHitType";
|
||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves";
|
||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import { ChargingMove, MoveAttrMap, MoveAttrString, MoveClass, MoveClassMap } from "#app/@types/move-types";
|
||||
import { ChargingMove, MoveAttrMap, MoveAttrString, MoveKindString, MoveClassMap } from "#app/@types/move-types";
|
||||
import { applyMoveAttrs } from "./apply-attrs";
|
||||
import { frenzyMissFunc, getMoveTargets } from "./move-utils";
|
||||
|
||||
@ -153,10 +153,13 @@ export default abstract class Move implements Localizable {
|
||||
/**
|
||||
* Check if the move is of the given subclass without requiring `instanceof`.
|
||||
*
|
||||
* ⚠️ Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. For those,
|
||||
* use {@linkcode isChargingMove} instead.
|
||||
*
|
||||
* @param moveKind - The string name of the move to check against
|
||||
* @returns Whether this move is of the provided type.
|
||||
*/
|
||||
public abstract is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K];
|
||||
public abstract is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K];
|
||||
|
||||
constructor(id: MoveId, type: PokemonType, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||
this.id = id;
|
||||
@ -976,6 +979,10 @@ export default abstract class Move implements Localizable {
|
||||
}
|
||||
|
||||
export class AttackMove extends Move {
|
||||
/** This field does not exist at runtime and must not be used.
|
||||
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||
*/
|
||||
declare private _: never;
|
||||
override is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K] {
|
||||
return moveKind === "AttackMove";
|
||||
}
|
||||
@ -1026,21 +1033,29 @@ export class AttackMove extends Move {
|
||||
}
|
||||
|
||||
export class StatusMove extends Move {
|
||||
/** This field does not exist at runtime and must not be used.
|
||||
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||
*/
|
||||
declare private _: never;
|
||||
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||
super(id, type, MoveCategory.STATUS, MoveTarget.NEAR_OTHER, -1, accuracy, pp, chance, priority, generation);
|
||||
}
|
||||
|
||||
override is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K] {
|
||||
override is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K] {
|
||||
return moveKind === "StatusMove";
|
||||
}
|
||||
}
|
||||
|
||||
export class SelfStatusMove extends Move {
|
||||
/** This field does not exist at runtime and must not be used.
|
||||
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||
*/
|
||||
declare private _: never;
|
||||
constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) {
|
||||
super(id, type, MoveCategory.STATUS, MoveTarget.USER, -1, accuracy, pp, chance, priority, generation);
|
||||
}
|
||||
|
||||
override is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K] {
|
||||
override is<K extends MoveKindString>(moveKind: K): this is MoveClassMap[K] {
|
||||
return moveKind === "SelfStatusMove";
|
||||
}
|
||||
}
|
||||
@ -1067,11 +1082,6 @@ function ChargeMove<TBase extends SubMove>(Base: TBase, nameAppend: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
override is<K extends keyof MoveClassMap>(moveKind: K): this is MoveClassMap[K] {
|
||||
// Anything subclassing this is a charge move.
|
||||
return moveKind === "ChargeMove" || moveKind === nameAppend || super.is(moveKind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text to be displayed during this move's charging phase.
|
||||
* References to the user Pokemon should be written as "{USER}", and
|
||||
@ -1151,14 +1161,14 @@ export abstract class MoveAttr {
|
||||
public selfTarget: boolean;
|
||||
|
||||
/**
|
||||
* Returns whether this attribute is of the given type.
|
||||
* Return whether this attribute is of the given type.
|
||||
*
|
||||
* @remarks
|
||||
* Used to avoid requring the caller to have imported the specific attribute type, avoiding circular dependencies.
|
||||
* @param attr - The attribute to check against
|
||||
* @returns Whether the attribute is an instanceof the given type.
|
||||
* @returns Whether the attribute is an instance of the given type.
|
||||
*/
|
||||
public is<T extends MoveAttrString>(attr: T): this is MoveAttrConstructorMap[MoveAttrString] {
|
||||
public is<T extends MoveAttrString>(attr: T): this is MoveAttrMap[T] {
|
||||
const targetAttr = MoveAttrs[attr];
|
||||
if (!targetAttr) {
|
||||
return false;
|
||||
@ -3096,7 +3106,11 @@ export class WeatherInstantChargeAttr extends InstantChargeAttr {
|
||||
}
|
||||
|
||||
export class OverrideMoveEffectAttr extends MoveAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
/** This field does not exist at runtime and must not be used.
|
||||
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||
*/
|
||||
declare private _: never;
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -8401,7 +8415,8 @@ const MoveAttrs = Object.freeze({
|
||||
ResistLastMoveTypeAttr,
|
||||
ExposedMoveAttr,
|
||||
});
|
||||
/** Map of names */
|
||||
|
||||
/** Map of of move attribute names to their constructors */
|
||||
export type MoveAttrConstructorMap = typeof MoveAttrs;
|
||||
|
||||
export const selfStatLowerMoves: MoveId[] = [];
|
||||
|
@ -28,7 +28,6 @@ import {
|
||||
} from "#app/data/battler-tags";
|
||||
import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type";
|
||||
import type { MoveAttr } from "#app/data/moves/move";
|
||||
import { MoveEffectAttr } from "#app/data/moves/move";
|
||||
import { getMoveTargets } from "#app/data/moves/move-utils";
|
||||
import { applyFilteredMoveAttrs, applyMoveAttrs } from "#app/data/moves/apply-attrs";
|
||||
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
||||
@ -746,7 +745,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
): void {
|
||||
applyFilteredMoveAttrs(
|
||||
(attr: MoveAttr) =>
|
||||
attr instanceof MoveEffectAttr &&
|
||||
attr.is("MoveEffectAttr") &&
|
||||
attr.trigger === triggerType &&
|
||||
(isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget) &&
|
||||
(!attr.firstHitOnly || this.firstHit) &&
|
||||
|
Loading…
Reference in New Issue
Block a user