mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-30 21:42:20 +02:00
Fixed various bugs and added tests for previous bugfixes
This commit is contained in:
parent
ee1b2176bc
commit
c2e7c95620
@ -311,10 +311,9 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
|
|||||||
* and showing a message.
|
* and showing a message.
|
||||||
*/
|
*/
|
||||||
override onAdd(pokemon: Pokemon): void {
|
override onAdd(pokemon: Pokemon): void {
|
||||||
// Disable fails against struggle or an empty move history, but we still need the nullish check
|
// Disable fails against struggle or an empty move history
|
||||||
// for cursed body
|
|
||||||
const move = pokemon.getLastNonVirtualMove();
|
const move = pokemon.getLastNonVirtualMove();
|
||||||
if (isNullOrUndefined(move)) {
|
if (isNullOrUndefined(move) || move.move === Moves.STRUGGLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,7 +367,6 @@ export class DisabledTag extends MoveRestrictionBattlerTag {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tag used by Gorilla Tactics to restrict the user to using only one move.
|
* Tag used by Gorilla Tactics to restrict the user to using only one move.
|
||||||
* @extends MoveRestrictionBattlerTag
|
|
||||||
*/
|
*/
|
||||||
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
||||||
private moveId = Moves.NONE;
|
private moveId = Moves.NONE;
|
||||||
@ -383,27 +381,27 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @override
|
* Ensures that move history exists on {@linkcode Pokemon} and has a valid move to lock into.
|
||||||
* @param {Pokemon} pokemon the {@linkcode Pokemon} to check if the tag can be added
|
* @param pokemon - the {@linkcode Pokemon} to add the tag to
|
||||||
* @returns `true` if the pokemon has a valid move and no existing {@linkcode GorillaTacticsTag}; `false` otherwise
|
* @returns `true` if the tag can be added
|
||||||
*/
|
*/
|
||||||
override canAdd(pokemon: Pokemon): boolean {
|
override canAdd(pokemon: Pokemon): boolean {
|
||||||
return !isNullOrUndefined(pokemon.getLastNonVirtualMove(true)) && !pokemon.getTag(GorillaTacticsTag);
|
// Choice items ignore struggle
|
||||||
|
// TODO: Check if struggle also gets the 50% power boost
|
||||||
|
const lastSelectedMove = pokemon.getLastNonVirtualMove(false);
|
||||||
|
return (
|
||||||
|
(isNullOrUndefined(lastSelectedMove) || lastSelectedMove.move === Moves.STRUGGLE) &&
|
||||||
|
!pokemon.getTag(GorillaTacticsTag)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that move history exists on {@linkcode Pokemon} and has a valid move.
|
* Sets this tag's {@linkcode moveId} and increases the user's Attack by 50%.
|
||||||
* If so, sets the {@linkcode moveId} and increases the user's Attack by 50%.
|
* @param pokemon - The {@linkcode Pokemon} to add the tag to
|
||||||
* @override
|
|
||||||
* @param {Pokemon} pokemon the {@linkcode Pokemon} to add the tag to
|
|
||||||
*/
|
*/
|
||||||
override onAdd(pokemon: Pokemon): void {
|
override onAdd(pokemon: Pokemon): void {
|
||||||
const lastValidMove = pokemon.getLastNonVirtualMove(true); // TODO: Check if should work with struggle or not
|
super.onAdd(pokemon);
|
||||||
if (isNullOrUndefined(lastValidMove)) {
|
this.moveId = pokemon.getLastNonVirtualMove(false)!.move; // `canAdd` returns false if no move
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.moveId = lastValidMove.move;
|
|
||||||
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
|
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,11 +416,10 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @override
|
* @override
|
||||||
* @param {Pokemon} pokemon n/a
|
* @param pokemon - The {@linkcode Pokemon} attempting to select a move
|
||||||
* @param {Moves} _move {@linkcode Moves} ID of the move being denied
|
* @param _move - Unused
|
||||||
* @returns {string} text to display when the move is denied
|
* @returns The text to display when the move is rendered unselectable
|
||||||
*/
|
*/
|
||||||
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
|
override selectionDeniedText(pokemon: Pokemon, _move: Moves): string {
|
||||||
return i18next.t("battle:canOnlyUseMove", {
|
return i18next.t("battle:canOnlyUseMove", {
|
||||||
|
@ -7895,7 +7895,7 @@ export class ForceLastAttr extends MoveEffectAttr {
|
|||||||
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean {
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
globalScene.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
||||||
|
|
||||||
// TODO: Refactor this to be more readable
|
// TODO: Refactor this to be more readable and less janky
|
||||||
const targetMovePhase = globalScene.findPhase<MovePhase>((phase) => phase.pokemon === target);
|
const targetMovePhase = globalScene.findPhase<MovePhase>((phase) => phase.pokemon === target);
|
||||||
if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) {
|
if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) {
|
||||||
// Finding the phase to insert the move in front of -
|
// Finding the phase to insert the move in front of -
|
||||||
|
@ -5161,7 +5161,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* - Non-virtual ({@linkcode MoveUseType | useType} < {@linkcode MoveUseType.INDIRECT})
|
* - Non-virtual ({@linkcode MoveUseType | useType} < {@linkcode MoveUseType.INDIRECT})
|
||||||
* @param ignoreStruggle - Whether to additionally ignore {@linkcode Moves.STRUGGLE}; default `false`
|
* @param ignoreStruggle - Whether to additionally ignore {@linkcode Moves.STRUGGLE}; default `false`
|
||||||
* @param ignoreFollowUp - Whether to ignore moves with a use type of {@linkcode MoveUseType.FOLLOW_UP}
|
* @param ignoreFollowUp - Whether to ignore moves with a use type of {@linkcode MoveUseType.FOLLOW_UP}
|
||||||
* (Copycat, Mirror Move, etc.); default `true`
|
* (Copycat, Mirror Move, etc.); default `true`.
|
||||||
* @returns The last move this Pokemon has used satisfying the aforementioned conditions,
|
* @returns The last move this Pokemon has used satisfying the aforementioned conditions,
|
||||||
* or `undefined` if no applicable moves have been used since switching in.
|
* or `undefined` if no applicable moves have been used since switching in.
|
||||||
*/
|
*/
|
||||||
@ -7250,12 +7250,13 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
const moveIndex = this.getMoveset().findIndex(m => m.moveId === queuedMove.move);
|
const moveIndex = this.getMoveset().findIndex(m => m.moveId === queuedMove.move);
|
||||||
// If the queued move was called indirectly, ignore all PP and usability checks.
|
// If the queued move was called indirectly, ignore all PP and usability checks.
|
||||||
// Otherwise, ensure that the move being used is actually usable
|
// Otherwise, ensure that the move being used is actually usable
|
||||||
|
// TODO: Virtual moves shouldn't use the move queue
|
||||||
if (
|
if (
|
||||||
queuedMove.useType >= MoveUseType.INDIRECT ||
|
queuedMove.useType >= MoveUseType.INDIRECT ||
|
||||||
(moveIndex > -1 &&
|
(moveIndex > -1 &&
|
||||||
this.getMoveset()[moveIndex].isUsable(
|
this.getMoveset()[moveIndex].isUsable(
|
||||||
this,
|
this,
|
||||||
queuedMove.useType >= MoveUseType.IGNORE_PP )
|
queuedMove.useType >= MoveUseType.IGNORE_PP)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return queuedMove;
|
return queuedMove;
|
||||||
|
@ -123,7 +123,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
return (
|
return (
|
||||||
this.pokemon.isActive(true) &&
|
this.pokemon.isActive(true) &&
|
||||||
this.move.isUsable(this.pokemon, this.useType >= MoveUseType.IGNORE_PP, ignoreDisableTags) &&
|
this.move.isUsable(this.pokemon, this.useType >= MoveUseType.IGNORE_PP, ignoreDisableTags) &&
|
||||||
!!this.targets.length
|
this.targets.length > 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { RandomMoveAttr } from "#app/data/moves/move";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { Species } from "#app/enums/species";
|
import { Species } from "#app/enums/species";
|
||||||
import { Stat } from "#app/enums/stat";
|
import { Stat } from "#app/enums/stat";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
describe("Abilities - Gorilla Tactics", () => {
|
describe("Abilities - Gorilla Tactics", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -25,10 +26,10 @@ describe("Abilities - Gorilla Tactics", () => {
|
|||||||
game.override
|
game.override
|
||||||
.battleStyle("single")
|
.battleStyle("single")
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
.enemyMoveset([Moves.SPLASH, Moves.DISABLE])
|
.enemyMoveset(Moves.SPLASH)
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.enemyLevel(30)
|
.enemyLevel(30)
|
||||||
.moveset([Moves.SPLASH, Moves.TACKLE, Moves.GROWL])
|
.moveset([Moves.SPLASH, Moves.TACKLE, Moves.GROWL, Moves.METRONOME])
|
||||||
.ability(Abilities.GORILLA_TACTICS);
|
.ability(Abilities.GORILLA_TACTICS);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,8 +40,6 @@ describe("Abilities - Gorilla Tactics", () => {
|
|||||||
const initialAtkStat = darmanitan.getStat(Stat.ATK);
|
const initialAtkStat = darmanitan.getStat(Stat.ATK);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5);
|
expect(darmanitan.getStat(Stat.ATK, false)).toBeCloseTo(initialAtkStat * 1.5);
|
||||||
@ -50,6 +49,7 @@ describe("Abilities - Gorilla Tactics", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should struggle if the only usable move is disabled", async () => {
|
it("should struggle if the only usable move is disabled", async () => {
|
||||||
|
game.override.enemyMoveset([Moves.DISABLE, Moves.SPLASH]);
|
||||||
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
|
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
|
||||||
|
|
||||||
const darmanitan = game.scene.getPlayerPokemon()!;
|
const darmanitan = game.scene.getPlayerPokemon()!;
|
||||||
@ -78,4 +78,18 @@ describe("Abilities - Gorilla Tactics", () => {
|
|||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
|
expect(darmanitan.hp).toBeLessThan(darmanitan.getMaxHp());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should lock into calling moves, even if also in moveset", async () => {
|
||||||
|
vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(Moves.TACKLE);
|
||||||
|
await game.classicMode.startBattle([Species.GALAR_DARMANITAN]);
|
||||||
|
|
||||||
|
const darmanitan = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
// Gorilla Tactics should bypass dancer and instruct
|
||||||
|
expect(darmanitan.isMoveRestricted(Moves.TACKLE)).toBe(true);
|
||||||
|
expect(darmanitan.isMoveRestricted(Moves.METRONOME)).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,7 @@ import { BattlerIndex } from "#app/battle";
|
|||||||
import { Abilities } from "#app/enums/abilities";
|
import { Abilities } from "#app/enums/abilities";
|
||||||
import { MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
|
import { MoveUseType } from "#enums/move-use-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
@ -60,4 +61,34 @@ describe("Moves - After You", () => {
|
|||||||
|
|
||||||
expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
|
expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should maintain PP ignore status of rampaging moves", async () => {
|
||||||
|
game.override.moveset([]);
|
||||||
|
await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]);
|
||||||
|
|
||||||
|
const [accelgor, rattata] = game.scene.getPlayerField();
|
||||||
|
expect(accelgor).toBeDefined();
|
||||||
|
expect(rattata).toBeDefined();
|
||||||
|
|
||||||
|
game.move.changeMoveset(accelgor, [Moves.SPLASH, Moves.AFTER_YOU]);
|
||||||
|
game.move.changeMoveset(rattata, Moves.OUTRAGE);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER);
|
||||||
|
game.move.select(Moves.OUTRAGE, BattlerIndex.PLAYER_2);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const outrageMove = rattata.getMoveset().find(m => m.moveId === Moves.OUTRAGE);
|
||||||
|
expect(outrageMove?.ppUsed).toBe(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.AFTER_YOU, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(accelgor.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||||
|
expect(outrageMove?.ppUsed).toBe(1);
|
||||||
|
expect(rattata.getLastXMoves()[0]).toMatchObject({
|
||||||
|
move: Moves.OUTRAGE,
|
||||||
|
result: MoveResult.SUCCESS,
|
||||||
|
useType: MoveUseType.IGNORE_PP,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { allMoves, RandomMoveAttr } from "#app/data/moves/move";
|
import { RandomMoveAttr } from "#app/data/moves/move";
|
||||||
import { Stat } from "#app/enums/stat";
|
import { Stat } from "#app/enums/stat";
|
||||||
import { MoveResult } from "#app/field/pokemon";
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { MoveUseType } from "#enums/move-use-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
@ -13,8 +14,6 @@ describe("Moves - Copycat", () => {
|
|||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
let game: GameManager;
|
let game: GameManager;
|
||||||
|
|
||||||
let randomMoveAttr: RandomMoveAttr;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
phaserGame = new Phaser.Game({
|
phaserGame = new Phaser.Game({
|
||||||
type: Phaser.HEADLESS,
|
type: Phaser.HEADLESS,
|
||||||
@ -26,14 +25,12 @@ describe("Moves - Copycat", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
randomMoveAttr = allMoves[Moves.METRONOME].getAttrs(RandomMoveAttr)[0];
|
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
.moveset([Moves.COPYCAT, Moves.SPIKY_SHIELD, Moves.SWORDS_DANCE, Moves.SPLASH])
|
.moveset([Moves.COPYCAT, Moves.SPIKY_SHIELD, Moves.SWORDS_DANCE, Moves.SPLASH])
|
||||||
.ability(Abilities.BALL_FETCH)
|
.ability(Abilities.BALL_FETCH)
|
||||||
.battleStyle("single")
|
.battleStyle("single")
|
||||||
.disableCrits()
|
.disableCrits()
|
||||||
.starterSpecies(Species.FEEBAS)
|
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
.enemyMoveset(Moves.SPLASH);
|
.enemyMoveset(Moves.SPLASH);
|
||||||
@ -41,7 +38,7 @@ describe("Moves - Copycat", () => {
|
|||||||
|
|
||||||
it("should copy the last move successfully executed", async () => {
|
it("should copy the last move successfully executed", async () => {
|
||||||
game.override.enemyMoveset(Moves.SUCKER_PUNCH);
|
game.override.enemyMoveset(Moves.SUCKER_PUNCH);
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
game.move.select(Moves.SWORDS_DANCE);
|
game.move.select(Moves.SWORDS_DANCE);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
@ -54,7 +51,7 @@ describe("Moves - Copycat", () => {
|
|||||||
|
|
||||||
it("should fail when the last move used is not a valid Copycat move", async () => {
|
it("should fail when the last move used is not a valid Copycat move", async () => {
|
||||||
game.override.enemyMoveset(Moves.PROTECT); // Protect is not a valid move for Copycat to copy
|
game.override.enemyMoveset(Moves.PROTECT); // Protect is not a valid move for Copycat to copy
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
game.move.select(Moves.SPIKY_SHIELD); // Spiky Shield is not a valid move for Copycat to copy
|
game.move.select(Moves.SPIKY_SHIELD); // Spiky Shield is not a valid move for Copycat to copy
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
@ -67,19 +64,25 @@ describe("Moves - Copycat", () => {
|
|||||||
|
|
||||||
it("should copy the called move when the last move successfully calls another", async () => {
|
it("should copy the called move when the last move successfully calls another", async () => {
|
||||||
game.override.moveset([Moves.SPLASH, Moves.METRONOME]).enemyMoveset(Moves.COPYCAT);
|
game.override.moveset([Moves.SPLASH, Moves.METRONOME]).enemyMoveset(Moves.COPYCAT);
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.DRAMPA]);
|
||||||
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.SWORDS_DANCE);
|
vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(Moves.SWORDS_DANCE);
|
||||||
|
|
||||||
game.move.select(Moves.METRONOME);
|
game.move.select(Moves.METRONOME);
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); // Player moves first, so enemy can copy Swords Dance
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); // Player moves first so enemy can copy Swords Dance
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(2);
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemy.getLastXMoves()[0]).toMatchObject({
|
||||||
|
move: Moves.SWORDS_DANCE,
|
||||||
|
result: MoveResult.SUCCESS,
|
||||||
|
useType: MoveUseType.FOLLOW_UP,
|
||||||
|
});
|
||||||
|
expect(enemy.getStatStage(Stat.ATK)).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should apply secondary effects of a move", async () => {
|
it("should apply move secondary effects", async () => {
|
||||||
game.override.enemyMoveset(Moves.ACID_SPRAY); // Secondary effect lowers SpDef by 2 stages
|
game.override.enemyMoveset(Moves.ACID_SPRAY); // Secondary effect lowers SpDef by 2 stages
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
game.move.select(Moves.COPYCAT);
|
game.move.select(Moves.COPYCAT);
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
@ -3,8 +3,10 @@ import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags";
|
|||||||
import { allMoves, RandomMoveAttr } from "#app/data/moves/move";
|
import { allMoves, RandomMoveAttr } from "#app/data/moves/move";
|
||||||
import { Abilities } from "#app/enums/abilities";
|
import { Abilities } from "#app/enums/abilities";
|
||||||
import { Stat } from "#app/enums/stat";
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MoveUseType } from "#enums/move-use-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
@ -79,7 +81,7 @@ describe("Moves - Metronome", () => {
|
|||||||
expect(player.getTag(RechargingTag)).toBeTruthy();
|
expect(player.getTag(RechargingTag)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should charge when calling charging moves while still maintaining follow-up status", async () => {
|
it("should charge for charging moves while still maintaining follow-up status", async () => {
|
||||||
game.override.moveset([]).enemyMoveset(Moves.SPITE);
|
game.override.moveset([]).enemyMoveset(Moves.SPITE);
|
||||||
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.SOLAR_BEAM);
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.SOLAR_BEAM);
|
||||||
await game.classicMode.startBattle([Species.REGIELEKI]);
|
await game.classicMode.startBattle([Species.REGIELEKI]);
|
||||||
@ -107,6 +109,11 @@ describe("Moves - Metronome", () => {
|
|||||||
const turn2PpUsed = metronomeMove.ppUsed - turn1PpUsed;
|
const turn2PpUsed = metronomeMove.ppUsed - turn1PpUsed;
|
||||||
expect(turn2PpUsed).toBeGreaterThan(1);
|
expect(turn2PpUsed).toBeGreaterThan(1);
|
||||||
expect(solarBeamMove.ppUsed).toBe(0);
|
expect(solarBeamMove.ppUsed).toBe(0);
|
||||||
|
expect(player.getLastXMoves()[0]).toMatchObject({
|
||||||
|
move: Moves.SOLAR_BEAM,
|
||||||
|
result: MoveResult.SUCCESS,
|
||||||
|
useType: MoveUseType.FOLLOW_UP,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should only target ally for Aromatic Mist", async () => {
|
it("should only target ally for Aromatic Mist", async () => {
|
||||||
|
@ -7,6 +7,7 @@ import { MoveResult } from "#app/field/pokemon";
|
|||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { describe, beforeAll, afterEach, beforeEach, it, expect } from "vitest";
|
import { describe, beforeAll, afterEach, beforeEach, it, expect } from "vitest";
|
||||||
|
import { MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
describe("Moves - Quash", () => {
|
describe("Moves - Quash", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -49,8 +50,8 @@ describe("Moves - Quash", () => {
|
|||||||
|
|
||||||
it("fails if the target has already moved", async () => {
|
it("fails if the target has already moved", async () => {
|
||||||
await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]);
|
await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]);
|
||||||
game.move.select(Moves.SPLASH, 0);
|
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER);
|
||||||
game.move.select(Moves.QUASH, 1, BattlerIndex.PLAYER);
|
game.move.select(Moves.QUASH, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
@ -58,6 +59,36 @@ describe("Moves - Quash", () => {
|
|||||||
expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
|
expect(game.scene.getPlayerField()[1].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should maintain PP ignore status of rampaging moves", async () => {
|
||||||
|
game.override.moveset([]);
|
||||||
|
await game.classicMode.startBattle([Species.ACCELGOR, Species.RATTATA]);
|
||||||
|
|
||||||
|
const [accelgor, rattata] = game.scene.getPlayerField();
|
||||||
|
expect(accelgor).toBeDefined();
|
||||||
|
expect(rattata).toBeDefined();
|
||||||
|
|
||||||
|
game.move.changeMoveset(accelgor, [Moves.SPLASH, Moves.QUASH]);
|
||||||
|
game.move.changeMoveset(rattata, Moves.OUTRAGE);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, BattlerIndex.PLAYER);
|
||||||
|
game.move.select(Moves.OUTRAGE, BattlerIndex.PLAYER_2);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const outrageMove = rattata.getMoveset().find(m => m.moveId === Moves.OUTRAGE);
|
||||||
|
expect(outrageMove?.ppUsed).toBe(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.QUASH, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(accelgor.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||||
|
expect(outrageMove?.ppUsed).toBe(1);
|
||||||
|
expect(rattata.getLastXMoves()[0]).toMatchObject({
|
||||||
|
move: Moves.OUTRAGE,
|
||||||
|
result: MoveResult.SUCCESS,
|
||||||
|
useType: MoveUseType.IGNORE_PP,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("makes multiple quashed targets move in speed order at the end of the turn", async () => {
|
it("makes multiple quashed targets move in speed order at the end of the turn", async () => {
|
||||||
game.override.enemySpecies(Species.NINJASK).enemyLevel(100);
|
game.override.enemySpecies(Species.NINJASK).enemyLevel(100);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user