basic pursuit functionality tested
This commit is contained in:
parent
c080ba0b46
commit
9d721f6610
|
@ -0,0 +1,327 @@
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import GameManager from "../utils/gameManager";
|
||||||
|
import { Moves } from "#app/enums/moves.js";
|
||||||
|
import { Species } from "#app/enums/species.js";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
import { allMoves } from "#app/data/move.js";
|
||||||
|
import { getMovePosition } from "../utils/gameManagerUtils";
|
||||||
|
import { BerryPhase } from "#app/phases.js";
|
||||||
|
import Pokemon, { MoveResult } from "#app/field/pokemon.js";
|
||||||
|
import { BattleStat } from "#app/data/battle-stat.js";
|
||||||
|
|
||||||
|
interface PokemonAssertionChainer {
|
||||||
|
and(expectation: (p?: Pokemon) => PokemonAssertionChainer): PokemonAssertionChainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chain(pokemon?: Pokemon): PokemonAssertionChainer {
|
||||||
|
return {
|
||||||
|
and: (expectation) => {
|
||||||
|
return expectation(pokemon);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Moves - Pursuit", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
const pursuitMoveDef = allMoves[Moves.PURSUIT];
|
||||||
|
|
||||||
|
const playerLead = Species.BULBASAUR;
|
||||||
|
const enemyLead = Species.KANGASKHAN;
|
||||||
|
|
||||||
|
function startBattle() {
|
||||||
|
return game.startBattle([playerLead, Species.RAICHU, Species.ABSOL]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCombatTurn() {
|
||||||
|
return game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playerUsesPursuit(pokemonIndex: 0 | 1 = 0) {
|
||||||
|
game.doAttack(getMovePosition(game.scene, pokemonIndex, Moves.PURSUIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
function playerUsesSwitchMove(pokemonIndex: 0 | 1 = 0, move: Moves.U_TURN | Moves.BATON_PASS | Moves.TELEPORT = Moves.U_TURN) {
|
||||||
|
game.doAttack(getMovePosition(game.scene, pokemonIndex, move));
|
||||||
|
game.doSelectPartyPokemon(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playerSwitches(pokemonIndex: number = 1) {
|
||||||
|
game.doSwitchPokemon(pokemonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enemyUses(move: Moves) {
|
||||||
|
game.override.enemyMoveset(move);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enemySwitches() {
|
||||||
|
game.override.forceTrainerSwitches();
|
||||||
|
}
|
||||||
|
|
||||||
|
function forceMovesLast(pokemon?: Pokemon) {
|
||||||
|
pokemon!.summonData.battleStats[BattleStat.SPD] = -6;
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectPursuitPowerDoubled() {
|
||||||
|
expect(pursuitMoveDef.calculateBattlePower).toHaveReturnedWith(80);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectPursuitPowerUnchanged() {
|
||||||
|
expect(pursuitMoveDef.calculateBattlePower).toHaveReturnedWith(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectPursuitFailed(pokemon?: Pokemon) {
|
||||||
|
const lastMove = pokemon!.getLastXMoves(0)[0];
|
||||||
|
expect(lastMove.result).toBe(MoveResult.FAIL);
|
||||||
|
return chain(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectWasHit(pokemon?: Pokemon) {
|
||||||
|
expect(pokemon!.hp).toBeLessThan(pokemon!.getMaxHp());
|
||||||
|
return chain(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectWasNotHit(pokemon?: Pokemon) {
|
||||||
|
expect(pokemon!.hp).toBe(pokemon!.getMaxHp());
|
||||||
|
return chain(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectNotOnField(pokemon?: Pokemon) {
|
||||||
|
expect(pokemon!.isOnField()).toBe(false);
|
||||||
|
return chain(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectHasFled(pokemon?: Pokemon) {
|
||||||
|
expect(pokemon!.wildFlee).toBe(true);
|
||||||
|
return chain(pokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPartyMember(party: Pokemon[], species: Species) {
|
||||||
|
return party.find(pkmn => pkmn.species.speciesId === species);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.enemyParty([enemyLead, Species.SNORLAX, Species.BASCULIN])
|
||||||
|
.startingLevel(20)
|
||||||
|
.startingWave(25)
|
||||||
|
.moveset([Moves.PURSUIT, Moves.U_TURN, Moves.BATON_PASS, Moves.TELEPORT])
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.disableCrits();
|
||||||
|
|
||||||
|
vi.spyOn(pursuitMoveDef, "calculateBattlePower");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hit for normal power if the target is not switching", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerUnchanged();
|
||||||
|
expectWasHit(game.scene.getEnemyPokemon());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hit a hard-switching target for double power (player attacks, enemy switches)", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemySwitches();
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerDoubled();
|
||||||
|
expectWasNotHit(game.scene.getEnemyPokemon());
|
||||||
|
expectNotOnField(findPartyMember(game.scene.getEnemyParty(), enemyLead))
|
||||||
|
.and(expectWasHit);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hit a hard-switching target for double power (player switches, enemy attacks)", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerSwitches();
|
||||||
|
enemyUses(Moves.PURSUIT);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerDoubled();
|
||||||
|
expectWasNotHit(game.scene.getPlayerPokemon());
|
||||||
|
expectNotOnField(findPartyMember(game.scene.getParty(), playerLead))
|
||||||
|
.and(expectWasHit);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hit an outgoing uturning target if pursuiter has not moved yet (player attacks, enemy switches)", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getPlayerPokemon());
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemyUses(Moves.U_TURN);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerDoubled();
|
||||||
|
expectWasNotHit(game.scene.getEnemyPokemon());
|
||||||
|
expectWasHit(findPartyMember(game.scene.getEnemyParty(), enemyLead))
|
||||||
|
.and(expectNotOnField);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should hit an outgoing uturning target if pursuiter has not moved yet (player switches, enemy attacks)", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getEnemyPokemon());
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesSwitchMove();
|
||||||
|
enemyUses(Moves.PURSUIT);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerDoubled();
|
||||||
|
expectWasNotHit(game.scene.getPlayerPokemon());
|
||||||
|
expectWasHit(findPartyMember(game.scene.getParty(), playerLead))
|
||||||
|
.and(expectNotOnField);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bypass accuracy checks when hitting a hard-switching target", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.ACC] = -6;
|
||||||
|
game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.EVA] = 6;
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemySwitches();
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectWasHit(findPartyMember(game.scene.getEnemyParty(), enemyLead));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bypass accuracy checks when hitting a uturning target", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getPlayerPokemon());
|
||||||
|
game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.ACC] = -6;
|
||||||
|
game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.EVA] = 6;
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemyUses(Moves.U_TURN);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectWasHit(findPartyMember(game.scene.getEnemyParty(), enemyLead));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not hit a baton pass user", async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getPlayerPokemon());
|
||||||
|
game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.ACC] = -6;
|
||||||
|
game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.EVA] = 6;
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemyUses(Moves.BATON_PASS);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerUnchanged();
|
||||||
|
expectWasHit(game.scene.getEnemyPokemon());
|
||||||
|
expectWasNotHit(findPartyMember(game.scene.getEnemyParty(), enemyLead))
|
||||||
|
.and(expectNotOnField);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not hit a teleport user", () => async () => {
|
||||||
|
// arrange
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getPlayerPokemon());
|
||||||
|
vi.spyOn(pursuitMoveDef, "priority", "get").mockReturnValue(-6);
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemyUses(Moves.TELEPORT);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitPowerUnchanged();
|
||||||
|
expectWasHit(game.scene.getEnemyPokemon());
|
||||||
|
expectWasNotHit(findPartyMember(game.scene.getEnemyParty(), enemyLead))
|
||||||
|
.and(expectNotOnField);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not hit a fleeing wild pokemon", async () => {
|
||||||
|
// arrange
|
||||||
|
game.override
|
||||||
|
.startingWave(24)
|
||||||
|
.disableTrainerWaves();
|
||||||
|
await startBattle();
|
||||||
|
forceMovesLast(game.scene.getPlayerPokemon());
|
||||||
|
|
||||||
|
// act
|
||||||
|
playerUsesPursuit();
|
||||||
|
enemyUses(Moves.U_TURN);
|
||||||
|
await runCombatTurn();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expectPursuitFailed(game.scene.getPlayerPokemon());
|
||||||
|
expectWasNotHit(game.scene.getEnemyParty()[0])
|
||||||
|
.and(expectHasFled);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.todo("should not hit a switch move user for double damage if the switch move fails and does not switch out the user");
|
||||||
|
|
||||||
|
it.todo("triggers contact abilities on the pokemon that is switching out (hard-switch)");
|
||||||
|
|
||||||
|
it.todo("triggers contact abilities on the pokemon that is switching out (switch move, player switching)");
|
||||||
|
|
||||||
|
it.todo("triggers contact abilities on the pokemon that is switching out (switch move, enemy switching)");
|
||||||
|
|
||||||
|
it.todo("should bypass follow me when hitting a switching target");
|
||||||
|
|
||||||
|
it.todo("should bypass substitute when hitting an escaping target");
|
||||||
|
|
||||||
|
it.todo("should hit a grounded, switching target under Psychic Terrain");
|
||||||
|
|
||||||
|
describe("doubles interactions", () => {
|
||||||
|
it.todo("should fail if both pokemon use pursuit on a target that is switching out and it faints after the first one");
|
||||||
|
|
||||||
|
it.todo("should not hit a pokemon being forced out with dragon tail");
|
||||||
|
|
||||||
|
it.todo("should not hit a uturning target for double power if the pursuiter moves before the uturner");
|
||||||
|
|
||||||
|
it.todo("should hit the first pokemon to switch out in a double battle regardless of who was targeted");
|
||||||
|
|
||||||
|
it.todo("should not hit both pokemon in a double battle if both switch out");
|
||||||
|
|
||||||
|
it.todo("should not hit a switching ally (hard-switch, player field)");
|
||||||
|
|
||||||
|
it.todo("should not hit a switching ally (hard-switch, enemy field)");
|
||||||
|
|
||||||
|
it.todo("should not hit a switching ally (switch move, player field)");
|
||||||
|
|
||||||
|
it.todo("should not hit a switching ally (switch move, enemy field)");
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue