mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-14 22:05:34 +01:00
[Misc] Improve type inference on PositionalTagManager#addTag (#6676)
* Improve type inference on `PositionalTagManager#addTag` - Remove unused `AddPositionalTagAttr` * Improved tests * fixed type errors * Update move.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
d3088c1729
commit
84dc143f74
@ -3384,7 +3384,10 @@ export class WeatherInstantChargeAttr extends InstantChargeAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OverrideMoveEffectAttr extends MoveAttr {
|
/**
|
||||||
|
* Abstract class used for `MoveAttr`s whose effect application can override normal move effect processing.
|
||||||
|
*/
|
||||||
|
abstract class OverrideMoveEffectAttr extends MoveAttr {
|
||||||
/** This field does not exist at runtime and must not be used.
|
/** 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.
|
* Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called.
|
||||||
*/
|
*/
|
||||||
@ -3404,41 +3407,37 @@ export class OverrideMoveEffectAttr extends MoveAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Abstract class for moves that add {@linkcode PositionalTag}s to the field. */
|
|
||||||
abstract class AddPositionalTagAttr extends OverrideMoveEffectAttr {
|
|
||||||
protected abstract readonly tagType: PositionalTagType;
|
|
||||||
|
|
||||||
public override getCondition(): MoveConditionFunc {
|
|
||||||
// Check the arena if another similar positional tag is active and affecting the same slot
|
|
||||||
return (_user, target, move) => globalScene.arena.positionalTagManager.canAddTag(this.tagType, target.getBattlerIndex())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute to implement delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}.
|
* Attribute to implement delayed attacks, such as {@linkcode MoveId.FUTURE_SIGHT} or {@linkcode MoveId.DOOM_DESIRE}.
|
||||||
* Delays the attack's effect with a {@linkcode DelayedAttackTag},
|
* Delays the attack's effect with a {@linkcode DelayedAttackTag},
|
||||||
* activating against the given slot after the given turn count has elapsed.
|
* activating against the given slot after the given turn count has elapsed.
|
||||||
*/
|
*/
|
||||||
export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
||||||
public chargeAnim: ChargeAnim;
|
|
||||||
private chargeText: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param chargeAnim - The {@linkcode ChargeAnim | charging animation} used for the move's charging phase.
|
* The {@linkcode ChargeAnim | charging animation} used for the move's charging phase.
|
||||||
* @param chargeKey - The `i18next` locales **key** to show when the delayed attack is used.
|
*
|
||||||
|
* Rendered public to allow for charge animation code to function
|
||||||
|
*/
|
||||||
|
public readonly chargeAnim: ChargeAnim;
|
||||||
|
/**
|
||||||
|
* The `i18next` locales key to show when the delayed attack is queued
|
||||||
|
* (**not** when it activates)! \
|
||||||
* In the displayed text, `{{pokemonName}}` will be populated with the user's name.
|
* In the displayed text, `{{pokemonName}}` will be populated with the user's name.
|
||||||
*/
|
*/
|
||||||
|
private readonly chargeKey: string;
|
||||||
|
|
||||||
constructor(chargeAnim: ChargeAnim, chargeKey: string) {
|
constructor(chargeAnim: ChargeAnim, chargeKey: string) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.chargeAnim = chargeAnim;
|
this.chargeAnim = chargeAnim;
|
||||||
this.chargeText = chargeKey;
|
this.chargeKey = chargeKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override apply(user: Pokemon, target: Pokemon, move: Move, args: [overridden: BooleanHolder, useMode: MoveUseMode]): boolean {
|
public override apply(user: Pokemon, target: Pokemon, move: Move, args: [overridden: BooleanHolder, useMode: MoveUseMode]): boolean {
|
||||||
const useMode = args[1];
|
const useMode = args[1];
|
||||||
if (useMode === MoveUseMode.DELAYED_ATTACK) {
|
if (useMode === MoveUseMode.DELAYED_ATTACK) {
|
||||||
// don't trigger if already queueing an indirect attack
|
// don't trigger if already queueing an indirect attack
|
||||||
|
// TODO: There should be a cleaner way of doing this...
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3449,14 +3448,14 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
|||||||
globalScene.phaseManager.unshiftNew("MoveAnimPhase", new MoveChargeAnim(this.chargeAnim, move.id, user));
|
globalScene.phaseManager.unshiftNew("MoveAnimPhase", new MoveChargeAnim(this.chargeAnim, move.id, user));
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t(
|
i18next.t(
|
||||||
this.chargeText,
|
this.chargeKey,
|
||||||
{ pokemonName: getPokemonNameWithAffix(user) }
|
{ pokemonName: getPokemonNameWithAffix(user) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn})
|
user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn})
|
||||||
// Queue up an attack on the given slot.
|
// Queue up an attack on the given slot
|
||||||
globalScene.arena.positionalTagManager.addTag<PositionalTagType.DELAYED_ATTACK>({
|
globalScene.arena.positionalTagManager.addTag({
|
||||||
tagType: PositionalTagType.DELAYED_ATTACK,
|
tagType: PositionalTagType.DELAYED_ATTACK,
|
||||||
sourceId: user.id,
|
sourceId: user.id,
|
||||||
targetIndex: target.getBattlerIndex(),
|
targetIndex: target.getBattlerIndex(),
|
||||||
@ -3474,11 +3473,12 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attribute to queue a {@linkcode WishTag} to activate in 2 turns.
|
* Attribute to queue a {@linkcode WishTag} to activate in 2 turns.
|
||||||
* The tag whill heal
|
* The tag will heal whichever Pokemon remains in the given slot for 50% of the user's
|
||||||
|
* maximum HP.
|
||||||
*/
|
*/
|
||||||
export class WishAttr extends MoveEffectAttr {
|
export class WishAttr extends MoveEffectAttr {
|
||||||
public override apply(user: Pokemon, target: Pokemon, _move: Move): boolean {
|
public override apply(user: Pokemon, target: Pokemon): boolean {
|
||||||
globalScene.arena.positionalTagManager.addTag<PositionalTagType.WISH>({
|
globalScene.arena.positionalTagManager.addTag({
|
||||||
tagType: PositionalTagType.WISH,
|
tagType: PositionalTagType.WISH,
|
||||||
healHp: toDmgValue(user.getMaxHp() / 2),
|
healHp: toDmgValue(user.getMaxHp() / 2),
|
||||||
targetIndex: target.getBattlerIndex(),
|
targetIndex: target.getBattlerIndex(),
|
||||||
@ -3489,7 +3489,7 @@ export class WishAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override getCondition(): MoveConditionFunc {
|
public override getCondition(): MoveConditionFunc {
|
||||||
// Check the arena if another wish is active and affecting the same slot
|
// Check the arena if another similar move is active and affecting the same slot
|
||||||
return (_user, target) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex())
|
return (_user, target) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import type { ObjectValues } from "#types/type-helpers";
|
|||||||
export function loadPositionalTag<T extends PositionalTagType>({
|
export function loadPositionalTag<T extends PositionalTagType>({
|
||||||
tagType,
|
tagType,
|
||||||
...args
|
...args
|
||||||
}: serializedPosTagMap[T]): posTagInstanceMap[T];
|
}: toSerializedPosTag<T>): posTagInstanceMap[T];
|
||||||
/**
|
/**
|
||||||
* Load the attributes of a {@linkcode PositionalTag}.
|
* Load the attributes of a {@linkcode PositionalTag}.
|
||||||
* @param tag - The {@linkcode SerializedPositionalTag} to instantiate
|
* @param tag - The {@linkcode SerializedPositionalTag} to instantiate
|
||||||
@ -26,7 +26,7 @@ export function loadPositionalTag(tag: SerializedPositionalTag): PositionalTag;
|
|||||||
export function loadPositionalTag<T extends PositionalTagType>({
|
export function loadPositionalTag<T extends PositionalTagType>({
|
||||||
tagType,
|
tagType,
|
||||||
...rest
|
...rest
|
||||||
}: serializedPosTagMap[T]): posTagInstanceMap[T] {
|
}: toSerializedPosTag<T>): posTagInstanceMap[T] {
|
||||||
// Note: We need 2 type assertions here:
|
// Note: We need 2 type assertions here:
|
||||||
// 1 because TS doesn't narrow the type of TagClass correctly based on `T`.
|
// 1 because TS doesn't narrow the type of TagClass correctly based on `T`.
|
||||||
// It converts it into `new (DelayedAttackTag | WishTag) => DelayedAttackTag & WishTag`
|
// It converts it into `new (DelayedAttackTag | WishTag) => DelayedAttackTag & WishTag`
|
||||||
@ -58,12 +58,19 @@ type posTagParamMap = {
|
|||||||
[k in PositionalTagType]: ConstructorParameters<posTagMap[k]>[0];
|
[k in PositionalTagType]: ConstructorParameters<posTagMap[k]>[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic type to convert a {@linkcode PositionalTagType} into the serialized representation of its corresponding class instance.
|
||||||
|
*
|
||||||
|
* Used in place of a mapped type to work around Typescript deficiencies in function type signatures.
|
||||||
|
*/
|
||||||
|
export type toSerializedPosTag<T extends PositionalTagType> = posTagParamMap[T] & { readonly tagType: T };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector.
|
* Type mapping all positional tag types to their constructors' parameters, alongside the `tagType` selector.
|
||||||
* Equivalent to their serialized representations.
|
* Equivalent to their serialized representations.
|
||||||
*/
|
*/
|
||||||
export type serializedPosTagMap = {
|
type serializedPosTagMap = {
|
||||||
[k in PositionalTagType]: posTagParamMap[k] & { tagType: k };
|
[k in PositionalTagType]: toSerializedPosTag<k>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
/** Union type containing all serialized {@linkcode PositionalTag}s. */
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { loadPositionalTag } from "#data/positional-tags/load-positional-tag";
|
import { loadPositionalTag, type toSerializedPosTag } from "#data/positional-tags/load-positional-tag";
|
||||||
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
import type { PositionalTag } from "#data/positional-tags/positional-tag";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
import type { BattlerIndex } from "#enums/battler-index";
|
||||||
import type { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
@ -16,7 +16,7 @@ export class PositionalTagManager {
|
|||||||
* @remarks
|
* @remarks
|
||||||
* This function does not perform any checking if the added tag is valid.
|
* This function does not perform any checking if the added tag is valid.
|
||||||
*/
|
*/
|
||||||
public addTag<T extends PositionalTagType = never>(tag: Parameters<typeof loadPositionalTag<T>>[0]): void {
|
public addTag<T extends PositionalTagType>(tag: toSerializedPosTag<T>): void {
|
||||||
this.tags.push(loadPositionalTag(tag));
|
this.tags.push(loadPositionalTag(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import i18next from "i18next";
|
|||||||
* and should refrain from adding extra serializable fields not contained in said interface.
|
* and should refrain from adding extra serializable fields not contained in said interface.
|
||||||
* This ensures that all tags truly "become" their respective interfaces when converted to and from JSON.
|
* This ensures that all tags truly "become" their respective interfaces when converted to and from JSON.
|
||||||
*/
|
*/
|
||||||
export interface PositionalTagBaseArgs {
|
interface PositionalTagBaseArgs {
|
||||||
/**
|
/**
|
||||||
* The number of turns remaining until this tag's activation. \
|
* The number of turns remaining until this tag's activation. \
|
||||||
* Decremented by 1 at the end of each turn until reaching 0, at which point it will
|
* Decremented by 1 at the end of each turn until reaching 0, at which point it will
|
||||||
@ -30,16 +30,16 @@ export interface PositionalTagBaseArgs {
|
|||||||
/**
|
/**
|
||||||
* The {@linkcode BattlerIndex} targeted by this effect.
|
* The {@linkcode BattlerIndex} targeted by this effect.
|
||||||
*/
|
*/
|
||||||
targetIndex: BattlerIndex;
|
readonly targetIndex: BattlerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@linkcode PositionalTag} is a variant of an {@linkcode ArenaTag} that targets a single *slot* of the battlefield.
|
* A {@linkcode PositionalTag} is a variant of an {@linkcode ArenaTag} that targets a single *slot* of the battlefield.
|
||||||
* Each tag can last one or more turns, triggering various effects on removal.
|
* Each tag can last one or more turns, triggering various effects on removal. \
|
||||||
* Multiple tags of the same kind can stack with one another, provided they are affecting different targets.
|
* Multiple tags of the same kind can stack with one another, provided they are affecting different targets.
|
||||||
*/
|
*/
|
||||||
export abstract class PositionalTag implements PositionalTagBaseArgs {
|
export abstract class PositionalTag implements PositionalTagBaseArgs {
|
||||||
/** This tag's {@linkcode PositionalTagType | type} */
|
/** This tag's {@linkcode PositionalTagType | type}. */
|
||||||
public abstract readonly tagType: PositionalTagType;
|
public abstract readonly tagType: PositionalTagType;
|
||||||
// These arguments have to be public to implement the interface, but are functionally private
|
// These arguments have to be public to implement the interface, but are functionally private
|
||||||
// outside this and the tag manager.
|
// outside this and the tag manager.
|
||||||
@ -76,9 +76,9 @@ interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
|||||||
/**
|
/**
|
||||||
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created this effect.
|
* The {@linkcode Pokemon.id | PID} of the {@linkcode Pokemon} having created this effect.
|
||||||
*/
|
*/
|
||||||
sourceId: number;
|
readonly sourceId: number;
|
||||||
/** The {@linkcode MoveId} that created this attack. */
|
/** The {@linkcode MoveId} that created this attack. */
|
||||||
sourceMove: MoveId;
|
readonly sourceMove: MoveId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -88,6 +88,7 @@ interface DelayedAttackArgs extends PositionalTagBaseArgs {
|
|||||||
*/
|
*/
|
||||||
export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs {
|
export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs {
|
||||||
public override readonly tagType = PositionalTagType.DELAYED_ATTACK;
|
public override readonly tagType = PositionalTagType.DELAYED_ATTACK;
|
||||||
|
|
||||||
public readonly sourceMove: MoveId;
|
public readonly sourceMove: MoveId;
|
||||||
public readonly sourceId: number;
|
public readonly sourceId: number;
|
||||||
|
|
||||||
@ -135,9 +136,9 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
|
|||||||
/** Interface containing arguments used to construct a {@linkcode WishTag}. */
|
/** Interface containing arguments used to construct a {@linkcode WishTag}. */
|
||||||
interface WishArgs extends PositionalTagBaseArgs {
|
interface WishArgs extends PositionalTagBaseArgs {
|
||||||
/** The amount of {@linkcode Stat.HP | HP} to heal; set to 50% of the user's max HP during move usage. */
|
/** The amount of {@linkcode Stat.HP | HP} to heal; set to 50% of the user's max HP during move usage. */
|
||||||
healHp: number;
|
readonly healHp: number;
|
||||||
/** The name of the {@linkcode Pokemon} having created the tag. */
|
/** The name of the {@linkcode Pokemon} having created the tag. */
|
||||||
pokemonName: string;
|
readonly pokemonName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -67,17 +67,6 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Expect that future sight is active with the specified number of attacks.
|
|
||||||
* @param numAttacks - The number of delayed attacks that should be queued; default `1`
|
|
||||||
*/
|
|
||||||
function expectFutureSightActive(numAttacks = 1) {
|
|
||||||
const delayedAttacks = game.scene.arena.positionalTagManager["tags"].filter(
|
|
||||||
t => t.tagType === PositionalTagType.DELAYED_ATTACK,
|
|
||||||
);
|
|
||||||
expect(delayedAttacks).toHaveLength(numAttacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
it.each<{ name: string; move: MoveId }>([
|
it.each<{ name: string; move: MoveId }>([
|
||||||
{ name: "Future Sight", move: MoveId.FUTURE_SIGHT },
|
{ name: "Future Sight", move: MoveId.FUTURE_SIGHT },
|
||||||
{ name: "Doom Desire", move: MoveId.DOOM_DESIRE },
|
{ name: "Doom Desire", move: MoveId.DOOM_DESIRE },
|
||||||
@ -88,7 +77,12 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(move);
|
game.move.use(move);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag({
|
||||||
|
tagType: PositionalTagType.DELAYED_ATTACK,
|
||||||
|
sourceMove: move,
|
||||||
|
targetIndex: BattlerIndex.ENEMY,
|
||||||
|
turnCount: 2,
|
||||||
|
});
|
||||||
|
|
||||||
game.doSwitchPokemon(1);
|
game.doSwitchPokemon(1);
|
||||||
game.forceEnemyToSwitch();
|
game.forceEnemyToSwitch();
|
||||||
@ -96,7 +90,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
await passTurns(1);
|
await passTurns(1);
|
||||||
|
|
||||||
expectFutureSightActive(0);
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
const enemy = game.field.getEnemyPokemon();
|
const enemy = game.field.getEnemyPokemon();
|
||||||
expect(enemy).not.toHaveFullHp();
|
expect(enemy).not.toHaveFullHp();
|
||||||
expect(game).toHaveShownMessage(
|
expect(game).toHaveShownMessage(
|
||||||
@ -113,14 +107,14 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.FUTURE_SIGHT);
|
game.move.use(MoveId.FUTURE_SIGHT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
const bronzong = game.field.getPlayerPokemon();
|
const bronzong = game.field.getPlayerPokemon();
|
||||||
expect(bronzong.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
|
expect(bronzong.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
|
||||||
|
|
||||||
game.move.use(MoveId.FUTURE_SIGHT);
|
game.move.use(MoveId.FUTURE_SIGHT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
expect(bronzong.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
expect(bronzong.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -131,13 +125,13 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.forceMetronomeMove(MoveId.FUTURE_SIGHT);
|
game.move.forceMetronomeMove(MoveId.FUTURE_SIGHT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
const enemy = game.field.getEnemyPokemon();
|
const enemy = game.field.getEnemyPokemon();
|
||||||
expect(enemy).toHaveFullHp();
|
expect(enemy).toHaveFullHp();
|
||||||
|
|
||||||
await passTurns(2);
|
await passTurns(2);
|
||||||
|
|
||||||
expectFutureSightActive(0);
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
expect(enemy).not.toHaveFullHp();
|
expect(enemy).not.toHaveFullHp();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,7 +145,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2);
|
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2);
|
||||||
await game.toEndOfTurn();
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expectFutureSightActive(2);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 2);
|
||||||
expect(enemy1).toHaveFullHp();
|
expect(enemy1).toHaveFullHp();
|
||||||
expect(enemy2).toHaveFullHp();
|
expect(enemy2).toHaveFullHp();
|
||||||
expect(karp.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
|
expect(karp.getLastXMoves()[0].result).toBe(MoveResult.OTHER);
|
||||||
@ -159,6 +153,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
await passTurns(2);
|
await passTurns(2);
|
||||||
|
|
||||||
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
expect(enemy1).not.toHaveFullHp();
|
expect(enemy1).not.toHaveFullHp();
|
||||||
expect(enemy2).not.toHaveFullHp();
|
expect(enemy2).not.toHaveFullHp();
|
||||||
});
|
});
|
||||||
@ -179,7 +174,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await game.setTurnOrder(oldOrder.map(p => p.getBattlerIndex()));
|
await game.setTurnOrder(oldOrder.map(p => p.getBattlerIndex()));
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(4);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 4);
|
||||||
|
|
||||||
// Lower speed to change turn order
|
// Lower speed to change turn order
|
||||||
alomomola.setStatStage(Stat.SPD, 6);
|
alomomola.setStatStage(Stat.SPD, 6);
|
||||||
@ -191,7 +186,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await passTurns(2, false);
|
await passTurns(2, false);
|
||||||
|
|
||||||
// All attacks have concluded at this point, unshifting new `MoveEffectPhase`s to the queue.
|
// All attacks have concluded at this point, unshifting new `MoveEffectPhase`s to the queue.
|
||||||
expectFutureSightActive(0);
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
|
|
||||||
const MEPs = game.scene.phaseManager["phaseQueue"].findAll("MoveEffectPhase");
|
const MEPs = game.scene.phaseManager["phaseQueue"].findAll("MoveEffectPhase");
|
||||||
expect(MEPs).toHaveLength(4);
|
expect(MEPs).toHaveLength(4);
|
||||||
@ -208,7 +203,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(1);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 1);
|
||||||
|
|
||||||
// Milotic / Feebas // Karp
|
// Milotic / Feebas // Karp
|
||||||
game.doSwitchPokemon(2);
|
game.doSwitchPokemon(2);
|
||||||
@ -246,7 +241,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(enemy2.isFainted()).toBe(true);
|
expect(enemy2.isFainted()).toBe(true);
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
|
|
||||||
expect(game).toHavePositionalTag({
|
expect(game).toHavePositionalTag({
|
||||||
tagType: PositionalTagType.DELAYED_ATTACK,
|
tagType: PositionalTagType.DELAYED_ATTACK,
|
||||||
@ -273,7 +268,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2);
|
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(1);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 1);
|
||||||
|
|
||||||
game.move.use(MoveId.SPLASH);
|
game.move.use(MoveId.SPLASH);
|
||||||
await game.killPokemon(enemy2);
|
await game.killPokemon(enemy2);
|
||||||
@ -282,7 +277,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.SPLASH);
|
game.move.use(MoveId.SPLASH);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(0);
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
expect(enemy1).toHaveFullHp();
|
expect(enemy1).toHaveFullHp();
|
||||||
expect(game).not.toHaveShownMessage(
|
expect(game).not.toHaveShownMessage(
|
||||||
i18next.t("moveTriggers:tookMoveAttack", {
|
i18next.t("moveTriggers:tookMoveAttack", {
|
||||||
@ -303,7 +298,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2);
|
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(1);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 1);
|
||||||
|
|
||||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
@ -341,7 +336,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.DOOM_DESIRE);
|
game.move.use(MoveId.DOOM_DESIRE);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
|
|
||||||
await passTurns(1);
|
await passTurns(1);
|
||||||
|
|
||||||
@ -371,7 +366,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.move.use(MoveId.FUTURE_SIGHT);
|
game.move.use(MoveId.FUTURE_SIGHT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive();
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
|
|
||||||
await passTurns(1);
|
await passTurns(1);
|
||||||
|
|
||||||
@ -401,7 +396,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT);
|
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(1);
|
expect(game).toHavePositionalTag(PositionalTagType.DELAYED_ATTACK, 1);
|
||||||
|
|
||||||
await passTurns(1);
|
await passTurns(1);
|
||||||
|
|
||||||
@ -412,7 +407,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
});
|
});
|
||||||
await game.toEndOfTurn();
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expectFutureSightActive(0);
|
expect(game).not.toHavePositionalTag(PositionalTagType.DELAYED_ATTACK);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Implement and move to a power spot's test file
|
// TODO: Implement and move to a power spot's test file
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import type { GameManager } from "#test/test-utils/game-manager";
|
import type { GameManager } from "#test/test-utils/game-manager";
|
||||||
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc
|
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc
|
||||||
|
|
||||||
import type { serializedPosTagMap } from "#data/positional-tags/load-positional-tag";
|
import type { toSerializedPosTag } from "#data/positional-tags/load-positional-tag";
|
||||||
import type { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import type { OneOther } from "#test/@types/test-helpers";
|
import type { OneOther } from "#test/@types/test-helpers";
|
||||||
import { getOnelineDiffStr } from "#test/test-utils/string-utils";
|
import { getOnelineDiffStr } from "#test/test-utils/string-utils";
|
||||||
@ -10,9 +10,10 @@ import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils"
|
|||||||
import { toTitleCase } from "#utils/strings";
|
import { toTitleCase } from "#utils/strings";
|
||||||
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
|
||||||
|
|
||||||
export type toHavePositionalTagOptions<P extends PositionalTagType> = OneOther<serializedPosTagMap[P], "tagType"> & {
|
/**
|
||||||
tagType: P;
|
* Options type for {@linkcode toHavePositionalTag}.
|
||||||
};
|
*/
|
||||||
|
export type toHavePositionalTagOptions<P extends PositionalTagType> = OneOther<toSerializedPosTag<P>, "tagType">;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Matcher to check if the {@linkcode Arena} has a certain number of {@linkcode PositionalTag}s active.
|
* Matcher to check if the {@linkcode Arena} has a certain number of {@linkcode PositionalTag}s active.
|
||||||
|
|||||||
@ -1,28 +1,27 @@
|
|||||||
import type { SerializedPositionalTag, serializedPosTagMap } from "#data/positional-tags/load-positional-tag";
|
import type { SerializedPositionalTag, toSerializedPosTag } from "#data/positional-tags/load-positional-tag";
|
||||||
import type { DelayedAttackTag, WishTag } from "#data/positional-tags/positional-tag";
|
import type { DelayedAttackTag, WishTag } from "#data/positional-tags/positional-tag";
|
||||||
import type { PositionalTagType } from "#enums/positional-tag-type";
|
import type { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import type { Mutable, NonFunctionPropertiesRecursive } from "#types/type-helpers";
|
import type { NonFunctionPropertiesRecursive } from "#types/type-helpers";
|
||||||
import { describe, expectTypeOf, it } from "vitest";
|
import { describe, expectTypeOf, it } from "vitest";
|
||||||
|
|
||||||
// Needed to get around properties being readonly in certain classes
|
describe("toSerializedPosTag", () => {
|
||||||
type NonFunctionMutable<T> = Mutable<NonFunctionPropertiesRecursive<T>>;
|
it("should map each class' tag type to their serialized forms", () => {
|
||||||
|
expectTypeOf<toSerializedPosTag<PositionalTagType.DELAYED_ATTACK>>().branded.toEqualTypeOf<
|
||||||
describe("serializedPositionalTagMap", () => {
|
NonFunctionPropertiesRecursive<DelayedAttackTag>
|
||||||
it("should contain representations of each tag's serialized form", () => {
|
>();
|
||||||
expectTypeOf<serializedPosTagMap[PositionalTagType.DELAYED_ATTACK]>().branded.toEqualTypeOf<
|
expectTypeOf<toSerializedPosTag<PositionalTagType.WISH>>().branded.toEqualTypeOf<
|
||||||
NonFunctionMutable<DelayedAttackTag>
|
NonFunctionPropertiesRecursive<WishTag>
|
||||||
>();
|
>();
|
||||||
expectTypeOf<serializedPosTagMap[PositionalTagType.WISH]>().branded.toEqualTypeOf<NonFunctionMutable<WishTag>>();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("SerializedPositionalTag", () => {
|
describe("SerializedPositionalTag", () => {
|
||||||
it("should accept a union of all serialized tag forms", () => {
|
it("should be a union of all serialized tag forms", () => {
|
||||||
expectTypeOf<SerializedPositionalTag>().branded.toEqualTypeOf<
|
expectTypeOf<SerializedPositionalTag>().branded.toEqualTypeOf<
|
||||||
NonFunctionMutable<DelayedAttackTag> | NonFunctionMutable<WishTag>
|
NonFunctionPropertiesRecursive<DelayedAttackTag> | NonFunctionPropertiesRecursive<WishTag>
|
||||||
>();
|
>();
|
||||||
});
|
});
|
||||||
it("should accept a union of all unserialized tag forms", () => {
|
it("should be extended by all unserialized tag forms", () => {
|
||||||
expectTypeOf<WishTag>().toExtend<SerializedPositionalTag>();
|
expectTypeOf<WishTag>().toExtend<SerializedPositionalTag>();
|
||||||
expectTypeOf<DelayedAttackTag>().toExtend<SerializedPositionalTag>();
|
expectTypeOf<DelayedAttackTag>().toExtend<SerializedPositionalTag>();
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user