mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-25 08:16:04 +00:00
[Test] Add forceEnemyMove
Game Manager util (#3678)
* Add `forceEnemyMove` test util * fix ceaseless edge test * Apply flx's suggestions Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> * Rewrite Follow Me test * Reorganize new imports in game manager * Rewrite Rage Powder + Spotlight tests --------- Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
This commit is contained in:
parent
11ac929a4d
commit
207b3e1eb7
@ -77,4 +77,8 @@ export class EnemyCommandPhase extends FieldPhase {
|
||||
|
||||
this.end();
|
||||
}
|
||||
|
||||
getFieldIndex(): number {
|
||||
return this.fieldIndex;
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ describe("Moves - Ceaseless Edge", () => {
|
||||
|
||||
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
|
||||
// Check HP of pokemon that WILL BE switched in (index 1)
|
||||
game.forceOpponentToSwitch();
|
||||
game.forceEnemyToSwitch();
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
|
||||
|
@ -123,7 +123,7 @@ describe("Moves - Focus Punch", () => {
|
||||
|
||||
await game.startBattle([Species.CHARIZARD]);
|
||||
|
||||
game.forceOpponentToSwitch();
|
||||
game.forceEnemyToSwitch();
|
||||
game.move.select(Moves.FOCUS_PUNCH);
|
||||
|
||||
await game.phaseInterceptor.to(TurnStartPhase);
|
||||
|
@ -28,48 +28,55 @@ describe("Moves - Follow Me", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override.battleType("double");
|
||||
game.override.starterSpecies(Species.AMOONGUSS);
|
||||
game.override.ability(Abilities.BALL_FETCH);
|
||||
game.override.enemySpecies(Species.SNORLAX);
|
||||
game.override.startingLevel(100);
|
||||
game.override.enemyLevel(100);
|
||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||
game.override.enemyMoveset([Moves.TACKLE, Moves.FOLLOW_ME, Moves.SPLASH]);
|
||||
});
|
||||
|
||||
test(
|
||||
"move should redirect enemy attacks to the user",
|
||||
async () => {
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
|
||||
const playerStartingHp = playerPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.FOLLOW_ME);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY);
|
||||
|
||||
// Force both enemies to target the player Pokemon that did not use Follow Me
|
||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]);
|
||||
expect(playerPokemon[1].hp).toBe(playerStartingHp[1]);
|
||||
expect(playerPokemon[0].hp).toBeLessThan(playerPokemon[0].getMaxHp());
|
||||
expect(playerPokemon[1].hp).toBe(playerPokemon[1].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test(
|
||||
"move should redirect enemy attacks to the first ally that uses it",
|
||||
async () => {
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerField();
|
||||
|
||||
const playerStartingHp = playerPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.FOLLOW_ME);
|
||||
game.move.select(Moves.FOLLOW_ME, 1);
|
||||
|
||||
// Each player is targeted by an enemy
|
||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
|
||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
||||
|
||||
expect(playerPokemon[1].hp).toBeLessThan(playerStartingHp[1]);
|
||||
expect(playerPokemon[0].hp).toBe(playerStartingHp[0]);
|
||||
expect(playerPokemon[1].hp).toBeLessThan(playerPokemon[1].getMaxHp());
|
||||
expect(playerPokemon[0].hp).toBe(playerPokemon[0].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -78,21 +85,23 @@ describe("Moves - Follow Me", () => {
|
||||
async () => {
|
||||
game.override.ability(Abilities.STALWART);
|
||||
game.override.moveset([Moves.QUICK_ATTACK]);
|
||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||
|
||||
// Target doesn't need to be specified if the move is self-targeted
|
||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
// If redirection was bypassed, both enemies should be damaged
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -100,21 +109,22 @@ describe("Moves - Follow Me", () => {
|
||||
"move effect should be bypassed by Snipe Shot",
|
||||
async () => {
|
||||
game.override.moveset([Moves.SNIPE_SHOT]);
|
||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2);
|
||||
|
||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
// If redirection was bypassed, both enemies should be damaged
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -31,27 +30,27 @@ describe("Moves - Rage Powder", () => {
|
||||
game.override.startingLevel(100);
|
||||
game.override.enemyLevel(100);
|
||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.TACKLE, Moves.SPLASH]);
|
||||
});
|
||||
|
||||
test(
|
||||
"move effect should be bypassed by Grass type",
|
||||
async () => {
|
||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
||||
|
||||
await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
await game.forceEnemyMove(Moves.RAGE_POWDER);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
// If redirection was bypassed, both enemies should be damaged
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
@ -59,10 +58,9 @@ describe("Moves - Rage Powder", () => {
|
||||
"move effect should be bypassed by Overcoat",
|
||||
async () => {
|
||||
game.override.ability(Abilities.OVERCOAT);
|
||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
||||
|
||||
// Test with two non-Grass type player Pokemon
|
||||
await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
@ -70,7 +68,7 @@ describe("Moves - Rage Powder", () => {
|
||||
|
||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
// If redirection was bypassed, both enemies should be damaged
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||
|
@ -73,7 +73,7 @@ describe("Moves - Spikes", () => {
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.forceOpponentToSwitch();
|
||||
game.forceEnemyToSwitch();
|
||||
await game.toNextTurn();
|
||||
|
||||
const enemy = game.scene.getEnemyParty()[0];
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -31,52 +30,46 @@ describe("Moves - Spotlight", () => {
|
||||
game.override.startingLevel(100);
|
||||
game.override.enemyLevel(100);
|
||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.SPLASH]);
|
||||
});
|
||||
|
||||
test(
|
||||
"move should redirect attacks to the target",
|
||||
async () => {
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||
|
||||
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||
expect(enemyPokemon[1].hp).toBe(enemyStartingHp[1]);
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test(
|
||||
"move should cause other redirection moves to fail",
|
||||
async () => {
|
||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||
|
||||
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
/**
|
||||
* Spotlight will target the slower enemy. In this situation without Spotlight being used,
|
||||
* the faster enemy would normally end up with the Center of Attention tag.
|
||||
*/
|
||||
enemyPokemon.sort((a, b) => b.getEffectiveStat(Stat.SPD) - a.getEffectiveStat(Stat.SPD));
|
||||
const spotTarget = enemyPokemon[1].getBattlerIndex();
|
||||
const attackTarget = enemyPokemon[0].getBattlerIndex();
|
||||
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||
|
||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||
await game.forceEnemyMove(Moves.SPLASH);
|
||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
||||
|
||||
game.move.select(Moves.SPOTLIGHT, 0, spotTarget);
|
||||
game.move.select(Moves.QUICK_ATTACK, 1, attackTarget);
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||
expect(enemyPokemon[0].hp).toBe(enemyStartingHp[0]);
|
||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
||||
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
});
|
||||
|
@ -2,6 +2,8 @@ import { updateUserInfo } from "#app/account";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { BattleStyle } from "#app/enums/battle-style";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { getMoveTargets } from "#app/data/move";
|
||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||
import Trainer from "#app/field/trainer";
|
||||
import { GameModes, getGameMode } from "#app/game-mode";
|
||||
@ -9,6 +11,7 @@ import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
|
||||
import overrides from "#app/overrides";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||
import { FaintPhase } from "#app/phases/faint-phase";
|
||||
import { LoginPhase } from "#app/phases/login-phase";
|
||||
import { MovePhase } from "#app/phases/move-phase";
|
||||
@ -243,7 +246,34 @@ export default class GameManager {
|
||||
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase));
|
||||
}
|
||||
|
||||
forceOpponentToSwitch() {
|
||||
/**
|
||||
* Forces the next enemy selecting a move to use the given move in its moveset against the
|
||||
* given target (if applicable).
|
||||
* @param moveId {@linkcode Moves} the move the enemy will use
|
||||
* @param target {@linkcode BattlerIndex} the target on which the enemy will use the given move
|
||||
*/
|
||||
async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
||||
// Wait for the next EnemyCommandPhase to start
|
||||
await this.phaseInterceptor.to(EnemyCommandPhase, false);
|
||||
const enemy = this.scene.getEnemyField()[(this.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()];
|
||||
const legalTargets = getMoveTargets(enemy, moveId);
|
||||
|
||||
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
||||
move: moveId,
|
||||
targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target))
|
||||
? [target]
|
||||
: enemy.getNextTargets(moveId)
|
||||
});
|
||||
|
||||
/**
|
||||
* Run the EnemyCommandPhase to completion.
|
||||
* This allows this function to be called consecutively to
|
||||
* force a move for each enemy in a double battle.
|
||||
*/
|
||||
await this.phaseInterceptor.to(EnemyCommandPhase);
|
||||
}
|
||||
|
||||
forceEnemyToSwitch() {
|
||||
const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores;
|
||||
Trainer.prototype.getPartyMemberMatchupScores = () => {
|
||||
Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore;
|
||||
|
Loading…
Reference in New Issue
Block a user