mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-09 08:59:29 +02:00
Fixed tests for real
This commit is contained in:
parent
9a8e4bad40
commit
49f0231982
@ -39,18 +39,16 @@ export class PositionalTagManager {
|
|||||||
const leftoverTags: PositionalTag[] = [];
|
const leftoverTags: PositionalTag[] = [];
|
||||||
for (const tag of this.tags) {
|
for (const tag of this.tags) {
|
||||||
// Check for silent removal, immediately removing invalid tags.
|
// Check for silent removal, immediately removing invalid tags.
|
||||||
if (tag.shouldDisappear()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (--tag.turnCount > 0) {
|
if (--tag.turnCount > 0) {
|
||||||
// tag still cooking
|
// tag still cooking
|
||||||
leftoverTags.push(tag);
|
leftoverTags.push(tag);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!tag.shouldDisappear()) {
|
||||||
tag.trigger();
|
tag.trigger();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.tags = leftoverTags;
|
this.tags = leftoverTags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,8 @@ export abstract class PositionalTag implements PositionalTagBaseArgs {
|
|||||||
public abstract trigger(): void;
|
public abstract trigger(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether this tag should be removed without triggering.
|
* Check whether this tag should be removed without calling {@linkcode trigger} and triggering effects.
|
||||||
* @returns Whether this tag should disappear.
|
* @returns Whether this tag should disappear without triggering.
|
||||||
* @privateRemarks
|
|
||||||
* Silent removal is accomplished by setting the attack's turn count to -1.
|
|
||||||
*/
|
*/
|
||||||
abstract shouldDisappear(): boolean;
|
abstract shouldDisappear(): boolean;
|
||||||
|
|
||||||
@ -89,6 +87,7 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
override trigger(): void {
|
override trigger(): void {
|
||||||
|
// Bangs are justified as the `shouldDisappear` method will queue the tag for removal if the source or target leave the field
|
||||||
const source = globalScene.getPokemonById(this.sourceId)!;
|
const source = globalScene.getPokemonById(this.sourceId)!;
|
||||||
const target = this.getTarget()!;
|
const target = this.getTarget()!;
|
||||||
|
|
||||||
@ -102,7 +101,7 @@ export class DelayedAttackTag extends PositionalTag implements DelayedAttackArgs
|
|||||||
|
|
||||||
globalScene.phaseManager.unshiftNew(
|
globalScene.phaseManager.unshiftNew(
|
||||||
"MoveEffectPhase",
|
"MoveEffectPhase",
|
||||||
this.sourceId,
|
this.sourceId, // TODO: Find an alternate method of passing the source pokemon without a source ID
|
||||||
[this.targetIndex],
|
[this.targetIndex],
|
||||||
allMoves[this.sourceMove],
|
allMoves[this.sourceMove],
|
||||||
MoveUseMode.TRANSPARENT,
|
MoveUseMode.TRANSPARENT,
|
||||||
|
@ -639,6 +639,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
||||||
public getUserPokemon(): Pokemon | null {
|
public getUserPokemon(): Pokemon | null {
|
||||||
|
// TODO: Make this purely a battler index
|
||||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||||
return globalScene.getPokemonById(this.battlerIndex);
|
return globalScene.getPokemonById(this.battlerIndex);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { PokemonType } from "#enums/pokemon-type";
|
|||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { GameManager } from "#test/testUtils/gameManager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
@ -40,9 +40,9 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until a number of turns have passed and a delayed attack has struck.
|
* Wait until a number of turns have passed.
|
||||||
* @param numTurns - Number of turns to pass.
|
* @param numTurns - Number of turns to pass.
|
||||||
* @param toEndOfTurn - Whether to advance to the `TurnEndPhase` (true) or the `PositionalTagPhase` (`false`);
|
* @param toEndOfTurn - Whether to advance to the `TurnEndPhase` (`true`) or the `PositionalTagPhase` (`false`);
|
||||||
* default `true`
|
* default `true`
|
||||||
* @returns A Promise that resolves once the specified number of turns has elapsed
|
* @returns A Promise that resolves once the specified number of turns has elapsed
|
||||||
* and the specified phase has been reached.
|
* and the specified phase has been reached.
|
||||||
@ -50,15 +50,15 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
async function passTurns(numTurns: number, toEndOfTurn = true): Promise<void> {
|
async function passTurns(numTurns: number, toEndOfTurn = true): Promise<void> {
|
||||||
for (let i = 0; i < numTurns; i++) {
|
for (let i = 0; i < numTurns; i++) {
|
||||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||||
if (game.scene.getPlayerField()[1]) {
|
if (game.scene.getPlayerField()[1]?.isActive()) {
|
||||||
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||||
}
|
}
|
||||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||||
if (game.scene.getEnemyField()[1]) {
|
if (game.scene.getEnemyField()[1]?.isActive()) {
|
||||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
await game.phaseInterceptor.to("PositionalTagPhase");
|
await game.phaseInterceptor.to("PositionalTagPhase");
|
||||||
|
}
|
||||||
if (toEndOfTurn) {
|
if (toEndOfTurn) {
|
||||||
await game.toEndOfTurn();
|
await game.toEndOfTurn();
|
||||||
}
|
}
|
||||||
@ -91,9 +91,9 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.forceEnemyToSwitch();
|
game.forceEnemyToSwitch();
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
game.move.use(MoveId.SPLASH);
|
await passTurns(1);
|
||||||
await game.toEndOfTurn();
|
|
||||||
|
|
||||||
|
expectFutureSightActive(0);
|
||||||
const enemy = game.field.getEnemyPokemon();
|
const enemy = game.field.getEnemyPokemon();
|
||||||
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
||||||
expect(game.textInterceptor.logs).toContain(
|
expect(game.textInterceptor.logs).toContain(
|
||||||
@ -134,6 +134,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
await passTurns(2);
|
await passTurns(2);
|
||||||
|
|
||||||
|
expectFutureSightActive(0);
|
||||||
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -163,10 +164,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double");
|
||||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]);
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]);
|
||||||
|
|
||||||
const [alomomola, blissey, karp1, karp2] = game.scene.getField();
|
const [alomomola, blissey] = game.scene.getField();
|
||||||
|
|
||||||
vi.spyOn(karp1, "getNameToRender").mockReturnValue("Karp 1");
|
|
||||||
vi.spyOn(karp2, "getNameToRender").mockReturnValue("Karp 2");
|
|
||||||
|
|
||||||
const oldOrder = game.field.getSpeedOrder();
|
const oldOrder = game.field.getSpeedOrder();
|
||||||
|
|
||||||
@ -175,8 +173,9 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER);
|
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER);
|
||||||
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER_2);
|
await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER_2);
|
||||||
// Ensure that the moves are used deterministically in speed order (for speed ties)
|
// Ensure that the moves are used deterministically in speed order (for speed ties)
|
||||||
await game.setTurnOrder(oldOrder);
|
await game.setTurnOrder(oldOrder.map(p => p.getBattlerIndex()));
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(4);
|
expectFutureSightActive(4);
|
||||||
|
|
||||||
// Lower speed to change turn order
|
// Lower speed to change turn order
|
||||||
@ -193,30 +192,36 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
const MEPs = game.scene.phaseManager.phaseQueue.filter(p => p.is("MoveEffectPhase"));
|
const MEPs = game.scene.phaseManager.phaseQueue.filter(p => p.is("MoveEffectPhase"));
|
||||||
expect(MEPs).toHaveLength(4);
|
expect(MEPs).toHaveLength(4);
|
||||||
expect(MEPs.map(mep => mep["battlerIndex"])).toEqual(oldOrder);
|
expect(MEPs.map(mep => mep.getPokemon())).toEqual(oldOrder);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should vanish silently if it would otherwise hit the user", async () => {
|
it("should vanish silently if it would otherwise hit the user", async () => {
|
||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double");
|
||||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS, SpeciesId.MIENFOO]);
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS, SpeciesId.MILOTIC]);
|
||||||
|
|
||||||
const [karp, feebas] = game.scene.getPlayerField();
|
const [karp, feebas, milotic] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2);
|
game.move.use(MoveId.FUTURE_SIGHT, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2);
|
||||||
// Karp / Feebas / Milotic
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||||
game.doSwitchPokemon(2);
|
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(1);
|
expectFutureSightActive(1);
|
||||||
|
|
||||||
// Milotic / Feebas // Karp
|
// Milotic / Feebas // Karp
|
||||||
game.doSwitchPokemon(2);
|
game.doSwitchPokemon(2);
|
||||||
// Feebas / Karp // Milotic
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||||
game.doSwitchPokemon(2);
|
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()).toEqual([milotic, feebas, karp]);
|
||||||
|
|
||||||
|
// Milotic / Karp // Feebas
|
||||||
|
game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER);
|
||||||
|
game.doSwitchPokemon(2);
|
||||||
|
|
||||||
await passTurns(1);
|
await passTurns(1);
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()).toEqual([milotic, karp, feebas]);
|
||||||
|
|
||||||
expect(karp.hp).toBe(karp.getMaxHp());
|
expect(karp.hp).toBe(karp.getMaxHp());
|
||||||
expect(feebas.hp).toBe(feebas.getMaxHp());
|
expect(feebas.hp).toBe(feebas.getMaxHp());
|
||||||
expect(game.textInterceptor.logs).not.toContain(
|
expect(game.textInterceptor.logs).not.toContain(
|
||||||
@ -227,7 +232,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should redirect normally if target is fainted when attack is launched", async () => {
|
it("should redirect normally if target is fainted when move is used", async () => {
|
||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double");
|
||||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||||
|
|
||||||
@ -238,7 +243,13 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(enemy2.isFainted()).toBe(true);
|
expect(enemy2.isFainted()).toBe(true);
|
||||||
expectFutureSightActive(1);
|
expectFutureSightActive();
|
||||||
|
|
||||||
|
const attack = game.scene.arena.positionalTagManager.tags.find(
|
||||||
|
t => t.tagType === PositionalTagType.DELAYED_ATTACK,
|
||||||
|
)!;
|
||||||
|
expect(attack).toBeDefined();
|
||||||
|
expect(attack.targetIndex).toBe(enemy1.getBattlerIndex());
|
||||||
|
|
||||||
await passTurns(2);
|
await passTurns(2);
|
||||||
|
|
||||||
@ -251,7 +262,7 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should vanish silently if target is fainted when attack lands", async () => {
|
it("should vanish silently if slot is vacant when attack lands", async () => {
|
||||||
game.override.battleStyle("double");
|
game.override.battleStyle("double");
|
||||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||||
|
|
||||||
@ -264,8 +275,10 @@ describe("Moves - Delayed Attacks", () => {
|
|||||||
|
|
||||||
game.move.use(MoveId.SPLASH);
|
game.move.use(MoveId.SPLASH);
|
||||||
await game.killPokemon(enemy2);
|
await game.killPokemon(enemy2);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
await passTurns(1);
|
game.move.use(MoveId.SPLASH);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectFutureSightActive(0);
|
expectFutureSightActive(0);
|
||||||
expect(enemy1.hp).toBe(enemy1.getMaxHp());
|
expect(enemy1.hp).toBe(enemy1.getMaxHp());
|
@ -6,7 +6,7 @@ import { MoveResult } from "#enums/move-result";
|
|||||||
import { PositionalTagType } from "#enums/positional-tag-type";
|
import { PositionalTagType } from "#enums/positional-tag-type";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { GameManager } from "#test/testUtils/gameManager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import { toDmgValue } from "#utils/common";
|
import { toDmgValue } from "#utils/common";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
@ -124,7 +124,7 @@ describe("Move - Wish", () => {
|
|||||||
await game.move.forceEnemyMove(MoveId.WISH);
|
await game.move.forceEnemyMove(MoveId.WISH);
|
||||||
await game.move.forceEnemyMove(MoveId.WISH);
|
await game.move.forceEnemyMove(MoveId.WISH);
|
||||||
// Ensure that the wishes are used deterministically in speed order (for speed ties)
|
// Ensure that the wishes are used deterministically in speed order (for speed ties)
|
||||||
await game.setTurnOrder(oldOrder);
|
await game.setTurnOrder(oldOrder.map(p => p.getBattlerIndex()));
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expectWishActive(4);
|
expectWishActive(4);
|
||||||
@ -145,7 +145,7 @@ describe("Move - Wish", () => {
|
|||||||
|
|
||||||
const healPhases = game.scene.phaseManager.phaseQueue.filter(p => p.is("PokemonHealPhase"));
|
const healPhases = game.scene.phaseManager.phaseQueue.filter(p => p.is("PokemonHealPhase"));
|
||||||
expect(healPhases).toHaveLength(4);
|
expect(healPhases).toHaveLength(4);
|
||||||
expect.soft(healPhases.map(php => php["battlerIndex"])).toEqual(oldOrder);
|
expect.soft(healPhases.map(php => php.getPokemon())).toEqual(oldOrder);
|
||||||
|
|
||||||
await game.toEndOfTurn();
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import type { globalScene } from "#app/global-scene";
|
|||||||
import type { Ability } from "#abilities/ability";
|
import type { Ability } from "#abilities/ability";
|
||||||
import { allAbilities } from "#data/data-lists";
|
import { allAbilities } from "#data/data-lists";
|
||||||
import type { AbilityId } from "#enums/ability-id";
|
import type { AbilityId } from "#enums/ability-id";
|
||||||
import type { BattlerIndex } from "#enums/battler-index";
|
|
||||||
import type { PokemonType } from "#enums/pokemon-type";
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
|
||||||
@ -45,18 +44,21 @@ export class FieldHelper extends GameManagerHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns The {@linkcode BattlerIndex | indexes} of Pokemon on the field in order of decreasing Speed.
|
* Helper function to return all on-field {@linkcode Pokemon} in speed order (fastest first).
|
||||||
|
* @returns An array containing all {@linkcode Pokemon} on the field in order of descending Speed.
|
||||||
* Speed ties are returned in increasing order of index.
|
* Speed ties are returned in increasing order of index.
|
||||||
*
|
*
|
||||||
* @remarks
|
* @remarks
|
||||||
* This does not account for Trick Room as it does not modify the _speed_ of Pokemon on the field,
|
* This does not account for Trick Room as it does not modify the _speed_ of Pokemon on the field,
|
||||||
* only their turn order.
|
* only their turn order.
|
||||||
*/
|
*/
|
||||||
public getSpeedOrder(): BattlerIndex[] {
|
public getSpeedOrder(): Pokemon[] {
|
||||||
return this.game.scene
|
return this.game.scene
|
||||||
.getField(true)
|
.getField(true)
|
||||||
.sort((pA, pB) => pB.getEffectiveStat(Stat.SPD) - pA.getEffectiveStat(Stat.SPD))
|
.sort(
|
||||||
.map(p => p.getBattlerIndex());
|
(pA, pB) =>
|
||||||
|
pB.getEffectiveStat(Stat.SPD) - pA.getEffectiveStat(Stat.SPD) || pA.getBattlerIndex() - pB.getBattlerIndex(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user