Merge remote-tracking branch 'upstream/beta' into move-use-type

This commit is contained in:
Bertie690 2025-06-15 10:01:28 -04:00
commit 185749ef64
173 changed files with 1144 additions and 1367 deletions

View File

@ -4,7 +4,7 @@ module.exports = {
{ {
name: "only-type-imports", name: "only-type-imports",
severity: "error", severity: "error",
comment: "Files in enums and @types may only use type imports.", comment: "Files in 'enums/' and '@types/' must only use type imports.",
from: { from: {
path: ["(^|/)src/@types", "(^|/)src/enums"], path: ["(^|/)src/@types", "(^|/)src/enums"],
}, },
@ -14,7 +14,7 @@ module.exports = {
}, },
{ {
name: "no-circular-at-runtime", name: "no-circular-at-runtime",
severity: "warn", severity: "error",
comment: comment:
"This dependency is part of a circular relationship. You might want to revise " + "This dependency is part of a circular relationship. You might want to revise " +
"your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ", "your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ",
@ -34,7 +34,7 @@ module.exports = {
"add an exception for it in your dependency-cruiser configuration. By default " + "add an exception for it in your dependency-cruiser configuration. By default " +
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " + "this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.", "files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
severity: "warn", severity: "error",
from: { from: {
orphan: true, orphan: true,
pathNot: [ pathNot: [
@ -42,8 +42,7 @@ module.exports = {
"[.]d[.]ts$", // TypeScript declaration files "[.]d[.]ts$", // TypeScript declaration files
"(^|/)tsconfig[.]json$", // TypeScript config "(^|/)tsconfig[.]json$", // TypeScript config
"(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$", // other configs "(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$", // other configs
// anything in src/@types "(^|/)test/.+[.]setup[.]ts", // Vitest setup files
"(^|/)src/@types/",
], ],
}, },
to: {}, to: {},
@ -53,7 +52,7 @@ module.exports = {
comment: comment:
"A module depends on a node core module that has been deprecated. Find an alternative - these are " + "A module depends on a node core module that has been deprecated. Find an alternative - these are " +
"bound to exist - node doesn't deprecate lightly.", "bound to exist - node doesn't deprecate lightly.",
severity: "warn", severity: "error",
from: {}, from: {},
to: { to: {
dependencyTypes: ["core"], dependencyTypes: ["core"],
@ -86,7 +85,7 @@ module.exports = {
comment: comment:
"This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " + "This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later " +
"version of that module, or find an alternative. Deprecated modules are a security risk.", "version of that module, or find an alternative. Deprecated modules are a security risk.",
severity: "warn", severity: "error",
from: {}, from: {},
to: { to: {
dependencyTypes: ["deprecated"], dependencyTypes: ["deprecated"],
@ -122,7 +121,7 @@ module.exports = {
"Likely this module depends on an external ('npm') package that occurs more than once " + "Likely this module depends on an external ('npm') package that occurs more than once " +
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " + "in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
"maintenance problems later on.", "maintenance problems later on.",
severity: "warn", severity: "error",
from: {}, from: {},
to: { to: {
moreThanOneDependencyType: true, moreThanOneDependencyType: true,
@ -133,7 +132,7 @@ module.exports = {
}, },
}, },
/* rules you might want to tweak for your specific situation: */ // rules you might want to tweak for your specific situation:
{ {
name: "not-to-spec", name: "not-to-spec",
@ -188,7 +187,7 @@ module.exports = {
"in your package.json. This makes sense if your package is e.g. a plugin, but in " + "in your package.json. This makes sense if your package is e.g. a plugin, but in " +
"other cases - maybe not so much. If the use of a peer dependency is intentional " + "other cases - maybe not so much. If the use of a peer dependency is intentional " +
"add an exception to your dependency-cruiser configuration.", "add an exception to your dependency-cruiser configuration.",
severity: "warn", severity: "error",
from: {}, from: {},
to: { to: {
dependencyTypes: ["npm-peer"], dependencyTypes: ["npm-peer"],
@ -196,6 +195,7 @@ module.exports = {
}, },
], ],
options: { options: {
exclude: ["src/plugins/vite/*", "src/vite.env.d.ts"],
/* Which modules not to follow further when encountered */ /* Which modules not to follow further when encountered */
doNotFollow: { doNotFollow: {
/* path: an array of regular expressions in strings to match against */ /* path: an array of regular expressions in strings to match against */
@ -235,7 +235,7 @@ module.exports = {
true: also detect dependencies that only exist before typescript-to-javascript compilation true: also detect dependencies that only exist before typescript-to-javascript compilation
"specify": for each dependency identify whether it only exists before compilation or also after "specify": for each dependency identify whether it only exists before compilation or also after
*/ */
// tsPreCompilationDeps: false, tsPreCompilationDeps: true,
/* list of extensions to scan that aren't javascript or compile-to-javascript. /* list of extensions to scan that aren't javascript or compile-to-javascript.
Empty by default. Only put extensions in here that you want to take into Empty by default. Only put extensions in here that you want to take into

42
.github/workflows/linting.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Linting
on:
push:
branches:
- main
- beta
pull_request:
branches:
- main
- beta
merge_group:
types: [checks_requested]
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install Node.js dependencies
run: npm ci
- name: Run ESLint
run: npm run eslint-ci
- name: Lint with Biome
run: npm run biome-ci
- name: Check dependencies with depcruise
run: npm run depcruise

View File

@ -1,41 +0,0 @@
name: Biome Code Quality
on:
# Trigger the workflow on push or pull request,
# but only for the main branch
push:
branches:
- main # Trigger on push events to the main branch
- beta # Trigger on push events to the beta branch
pull_request:
branches:
- main # Trigger on pull request events targeting the main branch
- beta # Trigger on pull request events targeting the beta branch
merge_group:
types: [checks_requested]
jobs:
run-linters: # Define a job named "run-linters"
name: Run linters # Human-readable name for the job
runs-on: ubuntu-latest # Specify the latest Ubuntu runner for the job
steps:
- name: Check out Git repository # Step to check out the repository
uses: actions/checkout@v4 # Use the checkout action version 4
with:
submodules: 'recursive'
- name: Set up Node.js # Step to set up Node.js environment
uses: actions/setup-node@v4 # Use the setup-node action version 4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install Node.js dependencies # Step to install Node.js dependencies
run: npm ci # Use 'npm ci' to install dependencies
- name: eslint # Step to run linters
run: npm run eslint-ci
- name: Lint with Biome # Step to run linters
run: npm run biome-ci

View File

@ -20,7 +20,7 @@
"biome": "biome check --write --changed --no-errors-on-unmatched", "biome": "biome check --write --changed --no-errors-on-unmatched",
"biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched", "biome-ci": "biome ci --diagnostic-level=error --reporter=github --no-errors-on-unmatched",
"docs": "typedoc", "docs": "typedoc",
"depcruise": "depcruise src", "depcruise": "depcruise src test",
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg", "depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg",
"postinstall": "npx lefthook install && npx lefthook run post-merge", "postinstall": "npx lefthook install && npx lefthook run post-merge",
"update-version:patch": "npm version patch --force --no-git-tag-version", "update-version:patch": "npm version patch --force --no-git-tag-version",

View File

@ -1,12 +1,18 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { allMoves } from "./data-lists"; import { allMoves } from "#app/data/data-lists";
import { MoveFlags } from "#enums/MoveFlags"; import { MoveFlags } from "#enums/MoveFlags";
import type Pokemon from "../field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName, coerceArray } from "../utils/common"; import {
type nil,
getFrameMs,
getEnumKeys,
getEnumValues,
animationFileName,
coerceArray,
isNullOrUndefined,
} from "#app/utils/common";
import type { BattlerIndex } from "#enums/battler-index"; import type { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SubstituteTag } from "./battler-tags";
import { isNullOrUndefined } from "../utils/common";
import Phaser from "phaser"; import Phaser from "phaser";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common"; import { AnimBlendType, AnimFrameTarget, AnimFocus, ChargeAnim, CommonAnim } from "#enums/move-anims-common";
@ -845,7 +851,7 @@ export abstract class BattleAnim {
return; return;
} }
const targetSubstitute = !!onSubstitute && user !== target ? target.getTag(SubstituteTag) : null; const targetSubstitute = !!onSubstitute && user !== target ? target.getTag(BattlerTagType.SUBSTITUTE) : null;
const userSprite = user.getSprite(); const userSprite = user.getSprite();
const targetSprite = targetSubstitute?.sprite ?? target.getSprite(); const targetSprite = targetSubstitute?.sprite ?? target.getSprite();

View File

@ -1,13 +0,0 @@
// biome-ignore lint/correctness/noUnusedImports: Used in tsdoc
import type ConfirmUiHandler from "#app/ui/confirm-ui-handler";
/**
* Used by {@linkcode ConfirmUiHandler} to determine whether the cursor should start on Yes or No
*/
export const ConfirmUiMode = Object.freeze({
/** Start cursor on Yes */
DEFAULT_YES: 1,
/** Start cursor on No */
DEFAULT_NO: 2
});
export type ConfirmUiMode = typeof ConfirmUiMode[keyof typeof ConfirmUiMode];

View File

@ -12,7 +12,6 @@ import { UiMode } from "#enums/ui-mode";
import i18next from "i18next"; import i18next from "i18next";
import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import { ConfirmUiMode } from "#enums/confirm-ui-mode";
import { LearnMoveType } from "#enums/learn-move-type"; import { LearnMoveType } from "#enums/learn-move-type";
export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
@ -164,10 +163,6 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
globalScene.ui.setMode(this.messageMode); globalScene.ui.setMode(this.messageMode);
this.replaceMoveCheck(move, pokemon); this.replaceMoveCheck(move, pokemon);
}, },
false,
0,
0,
ConfirmUiMode.DEFAULT_NO,
); );
} }

View File

@ -4,11 +4,8 @@ import { UiMode } from "#enums/ui-mode";
import i18next from "i18next"; import i18next from "i18next";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { ConfirmUiMode } from "#enums/confirm-ui-mode";
export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
private confirmUiMode: ConfirmUiMode;
public static readonly windowWidth: number = 48; public static readonly windowWidth: number = 48;
private switchCheck: boolean; private switchCheck: boolean;
@ -108,16 +105,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
this.optionSelectContainer.setPosition(globalScene.game.canvas.width / 6 - 1 + xOffset, -48 + yOffset); this.optionSelectContainer.setPosition(globalScene.game.canvas.width / 6 - 1 + xOffset, -48 + yOffset);
this.confirmUiMode = args.length >= 6 ? (args[5] as ConfirmUiMode) : ConfirmUiMode.DEFAULT_YES; this.setCursor(this.switchCheck ? this.switchCheckCursor : 0);
switch (this.confirmUiMode) {
case ConfirmUiMode.DEFAULT_YES:
this.setCursor(this.switchCheck ? this.switchCheckCursor : 0);
break;
case ConfirmUiMode.DEFAULT_NO:
this.setCursor(this.switchCheck ? this.switchCheckCursor : 1);
break;
}
return true; return true;
} }

View File

@ -50,5 +50,5 @@ describe("Ability Timing", () => {
await game.phaseInterceptor.to("MessagePhase"); await game.phaseInterceptor.to("MessagePhase");
expect(i18next.t).toHaveBeenCalledWith("battle:statFell", expect.objectContaining({ count: 1 })); expect(i18next.t).toHaveBeenCalledWith("battle:statFell", expect.objectContaining({ count: 1 }));
}, 5000); });
}); });

View File

@ -26,11 +26,12 @@ describe("Abilities - Battery", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.enemySpecies(SpeciesId.SHUCKLE); .battleStyle("double")
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemySpecies(SpeciesId.SHUCKLE)
game.override.moveset([MoveId.TACKLE, MoveId.BREAKING_SWIPE, MoveId.SPLASH, MoveId.DAZZLING_GLEAM]); .enemyAbility(AbilityId.BALL_FETCH)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.TACKLE, MoveId.BREAKING_SWIPE, MoveId.SPLASH, MoveId.DAZZLING_GLEAM])
.enemyMoveset(MoveId.SPLASH);
}); });
it("raises the power of allies' special moves by 30%", async () => { it("raises the power of allies' special moves by 30%", async () => {

View File

@ -47,7 +47,7 @@ describe("Abilities - Beast Boost", () => {
await game.phaseInterceptor.to("VictoryPhase"); await game.phaseInterceptor.to("VictoryPhase");
expect(playerPokemon.getStatStage(Stat.DEF)).toBe(1); expect(playerPokemon.getStatStage(Stat.DEF)).toBe(1);
}, 20000); });
it("should use in-battle overriden stats when determining the stat stage to raise by 1", async () => { it("should use in-battle overriden stats when determining the stat stage to raise by 1", async () => {
game.override.enemyMoveset([MoveId.GUARD_SPLIT]); game.override.enemyMoveset([MoveId.GUARD_SPLIT]);
@ -66,7 +66,7 @@ describe("Abilities - Beast Boost", () => {
await game.phaseInterceptor.to("VictoryPhase"); await game.phaseInterceptor.to("VictoryPhase");
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
}, 20000); });
it("should have order preference in case of stat ties", async () => { it("should have order preference in case of stat ties", async () => {
// Order preference follows the order of EFFECTIVE_STAT // Order preference follows the order of EFFECTIVE_STAT
@ -84,5 +84,5 @@ describe("Abilities - Beast Boost", () => {
await game.phaseInterceptor.to("VictoryPhase"); await game.phaseInterceptor.to("VictoryPhase");
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
}, 20000); });
}); });

View File

@ -36,7 +36,7 @@ describe("Abilities - Contrary", () => {
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
}, 20000); });
describe("With Clear Body", () => { describe("With Clear Body", () => {
it("should apply positive effects", async () => { it("should apply positive effects", async () => {

View File

@ -24,10 +24,11 @@ describe("Abilities - COSTAR", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.ability(AbilityId.COSTAR); .battleStyle("double")
game.override.moveset([MoveId.SPLASH, MoveId.NASTY_PLOT]); .ability(AbilityId.COSTAR)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.SPLASH, MoveId.NASTY_PLOT])
.enemyMoveset(MoveId.SPLASH);
}); });
test("ability copies positive stat stages", async () => { test("ability copies positive stat stages", async () => {

View File

@ -66,8 +66,7 @@ describe("Abilities - Disguise", () => {
}); });
it("takes no damage from the first hit of a multihit move and transforms to Busted form, then takes damage from the second hit", async () => { it("takes no damage from the first hit of a multihit move and transforms to Busted form, then takes damage from the second hit", async () => {
game.override.moveset([MoveId.SURGING_STRIKES]); game.override.moveset([MoveId.SURGING_STRIKES]).enemyLevel(5);
game.override.enemyLevel(5);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!; const mimikyu = game.scene.getEnemyPokemon()!;
@ -106,8 +105,7 @@ describe("Abilities - Disguise", () => {
}); });
it("persists form change when switched out", async () => { it("persists form change when switched out", async () => {
game.override.enemyMoveset([MoveId.SHADOW_SNEAK]); game.override.enemyMoveset([MoveId.SHADOW_SNEAK]).starterSpecies(0);
game.override.starterSpecies(0);
await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]); await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]);
@ -131,8 +129,7 @@ describe("Abilities - Disguise", () => {
}); });
it("persists form change when wave changes with no arena reset", async () => { it("persists form change when wave changes with no arena reset", async () => {
game.override.starterSpecies(0); game.override.starterSpecies(0).starterForms({
game.override.starterForms({
[SpeciesId.MIMIKYU]: bustedForm, [SpeciesId.MIMIKYU]: bustedForm,
}); });
await game.classicMode.startBattle([SpeciesId.FURRET, SpeciesId.MIMIKYU]); await game.classicMode.startBattle([SpeciesId.FURRET, SpeciesId.MIMIKYU]);
@ -148,11 +145,12 @@ describe("Abilities - Disguise", () => {
}); });
it("reverts to Disguised form on arena reset", async () => { it("reverts to Disguised form on arena reset", async () => {
game.override.startingWave(4); game.override
game.override.starterSpecies(SpeciesId.MIMIKYU); .startingWave(4)
game.override.starterForms({ .starterSpecies(SpeciesId.MIMIKYU)
[SpeciesId.MIMIKYU]: bustedForm, .starterForms({
}); [SpeciesId.MIMIKYU]: bustedForm,
});
await game.classicMode.startBattle(); await game.classicMode.startBattle();
@ -168,11 +166,12 @@ describe("Abilities - Disguise", () => {
}); });
it("reverts to Disguised form on biome change when fainted", async () => { it("reverts to Disguised form on biome change when fainted", async () => {
game.override.startingWave(10); game.override
game.override.starterSpecies(0); .startingWave(10)
game.override.starterForms({ .starterSpecies(0)
[SpeciesId.MIMIKYU]: bustedForm, .starterForms({
}); [SpeciesId.MIMIKYU]: bustedForm,
});
await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]); await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]);
@ -206,8 +205,7 @@ describe("Abilities - Disguise", () => {
}); });
it("activates when Aerilate circumvents immunity to the move's base type", async () => { it("activates when Aerilate circumvents immunity to the move's base type", async () => {
game.override.ability(AbilityId.AERILATE); game.override.ability(AbilityId.AERILATE).moveset([MoveId.TACKLE]);
game.override.moveset([MoveId.TACKLE]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();

View File

@ -44,7 +44,7 @@ describe("Abilities - Flash Fire", () => {
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(blissey.hp).toBe(blissey.getMaxHp()); expect(blissey.hp).toBe(blissey.getMaxHp());
}, 20000); });
it("not activate if the Pokémon is protected from the Fire-type move", async () => { it("not activate if the Pokémon is protected from the Fire-type move", async () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.PROTECT]); game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.PROTECT]);
@ -55,7 +55,7 @@ describe("Abilities - Flash Fire", () => {
game.move.select(MoveId.PROTECT); game.move.select(MoveId.PROTECT);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined();
}, 20000); });
it("activated by Will-O-Wisp", async () => { it("activated by Will-O-Wisp", async () => {
game.override.enemyMoveset([MoveId.WILL_O_WISP]).moveset(MoveId.SPLASH); game.override.enemyMoveset([MoveId.WILL_O_WISP]).moveset(MoveId.SPLASH);
@ -70,11 +70,10 @@ describe("Abilities - Flash Fire", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined();
}, 20000); });
it("activated after being frozen", async () => { it("activated after being frozen", async () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH); game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH).statusEffect(StatusEffect.FREEZE);
game.override.statusEffect(StatusEffect.FREEZE);
await game.classicMode.startBattle([SpeciesId.BLISSEY]); await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!; const blissey = game.scene.getPlayerPokemon()!;
@ -83,7 +82,7 @@ describe("Abilities - Flash Fire", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined();
}, 20000); });
it("not passing with baton pass", async () => { it("not passing with baton pass", async () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.BATON_PASS]); game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.BATON_PASS]);
@ -99,11 +98,14 @@ describe("Abilities - Flash Fire", () => {
const chansey = game.scene.getPlayerPokemon()!; const chansey = game.scene.getPlayerPokemon()!;
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.CHANSEY); expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.CHANSEY);
expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined();
}, 20000); });
it("boosts Fire-type move when the ability is activated", async () => { it("boosts Fire-type move when the ability is activated", async () => {
game.override.enemyMoveset([MoveId.FIRE_PLEDGE]).moveset([MoveId.EMBER, MoveId.SPLASH]); game.override
game.override.enemyAbility(AbilityId.FLASH_FIRE).ability(AbilityId.NONE); .enemyMoveset([MoveId.FIRE_PLEDGE])
.moveset([MoveId.EMBER, MoveId.SPLASH])
.enemyAbility(AbilityId.FLASH_FIRE)
.ability(AbilityId.NONE);
await game.classicMode.startBattle([SpeciesId.BLISSEY]); await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!; const blissey = game.scene.getPlayerPokemon()!;
const initialHP = 1000; const initialHP = 1000;
@ -124,12 +126,15 @@ describe("Abilities - Flash Fire", () => {
const flashFireDmg = initialHP - blissey.hp; const flashFireDmg = initialHP - blissey.hp;
expect(flashFireDmg).toBeGreaterThan(originalDmg); expect(flashFireDmg).toBeGreaterThan(originalDmg);
}, 20000); });
it("still activates regardless of accuracy check", async () => { it("still activates regardless of accuracy check", async () => {
game.override.moveset(MoveId.FIRE_PLEDGE).enemyMoveset(MoveId.EMBER); game.override
game.override.enemyAbility(AbilityId.NONE).ability(AbilityId.FLASH_FIRE); .moveset(MoveId.FIRE_PLEDGE)
game.override.enemySpecies(SpeciesId.BLISSEY); .enemyMoveset(MoveId.EMBER)
.enemyAbility(AbilityId.NONE)
.ability(AbilityId.FLASH_FIRE)
.enemySpecies(SpeciesId.BLISSEY);
await game.classicMode.startBattle([SpeciesId.RATTATA]); await game.classicMode.startBattle([SpeciesId.RATTATA]);
const blissey = game.scene.getEnemyPokemon()!; const blissey = game.scene.getEnemyPokemon()!;
@ -153,5 +158,5 @@ describe("Abilities - Flash Fire", () => {
const flashFireDmg = initialHP - blissey.hp; const flashFireDmg = initialHP - blissey.hp;
expect(flashFireDmg).toBeGreaterThan(originalDmg); expect(flashFireDmg).toBeGreaterThan(originalDmg);
}, 20000); });
}); });

View File

@ -47,9 +47,10 @@ describe("Abilities - Flower Gift", () => {
allyAbility = AbilityId.BALL_FETCH, allyAbility = AbilityId.BALL_FETCH,
enemyAbility = AbilityId.BALL_FETCH, enemyAbility = AbilityId.BALL_FETCH,
): Promise<[number, number]> => { ): Promise<[number, number]> => {
game.override.battleStyle("double"); game.override
game.override.moveset([MoveId.SPLASH, MoveId.SUNNY_DAY, move, MoveId.HEAL_PULSE]); .battleStyle("double")
game.override.enemyMoveset([MoveId.SPLASH, MoveId.HEAL_PULSE]); .moveset([MoveId.SPLASH, MoveId.SUNNY_DAY, move, MoveId.HEAL_PULSE])
.enemyMoveset([MoveId.SPLASH, MoveId.HEAL_PULSE]);
const target_index = allyAttacker ? BattlerIndex.ENEMY : BattlerIndex.PLAYER_2; const target_index = allyAttacker ? BattlerIndex.ENEMY : BattlerIndex.PLAYER_2;
const attacker_index = allyAttacker ? BattlerIndex.PLAYER_2 : BattlerIndex.ENEMY; const attacker_index = allyAttacker ? BattlerIndex.PLAYER_2 : BattlerIndex.ENEMY;
const ally_move = allyAttacker ? move : MoveId.SPLASH; const ally_move = allyAttacker ? move : MoveId.SPLASH;

View File

@ -89,7 +89,6 @@ describe("Abilities - Flower Veil", () => {
await game.move.selectEnemyMove(MoveId.THUNDER_WAVE); await game.move.selectEnemyMove(MoveId.THUNDER_WAVE);
await game.toNextTurn(); await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
vi.spyOn(allMoves[MoveId.THUNDER_WAVE], "accuracy", "get").mockClear();
}); });
it("should not prevent status conditions for a non-grass user and its non-grass allies", async () => { it("should not prevent status conditions for a non-grass user and its non-grass allies", async () => {

View File

@ -63,9 +63,10 @@ describe("Abilities - Good As Gold", () => {
}); });
it("should not block any status moves that target the field, one side, or all pokemon", async () => { it("should not block any status moves that target the field, one side, or all pokemon", async () => {
game.override.battleStyle("double"); game.override
game.override.enemyMoveset([MoveId.STEALTH_ROCK, MoveId.HAZE]); .battleStyle("double")
game.override.moveset([MoveId.SWORDS_DANCE, MoveId.SAFEGUARD]); .enemyMoveset([MoveId.STEALTH_ROCK, MoveId.HAZE])
.moveset([MoveId.SWORDS_DANCE, MoveId.SAFEGUARD]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]);
const [good_as_gold, ball_fetch] = game.scene.getPlayerField(); const [good_as_gold, ball_fetch] = game.scene.getPlayerField();
@ -85,8 +86,7 @@ describe("Abilities - Good As Gold", () => {
}); });
it("should not block field targeted effects in singles", async () => { it("should not block field targeted effects in singles", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single").enemyMoveset([MoveId.SPIKES]);
game.override.enemyMoveset([MoveId.SPIKES]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.SPLASH, 0); game.move.select(MoveId.SPLASH, 0);
@ -96,8 +96,7 @@ describe("Abilities - Good As Gold", () => {
}); });
it("should block the ally's helping hand", async () => { it("should block the ally's helping hand", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").moveset([MoveId.HELPING_HAND, MoveId.TACKLE]);
game.override.moveset([MoveId.HELPING_HAND, MoveId.TACKLE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]);
game.move.select(MoveId.HELPING_HAND, 0); game.move.select(MoveId.HELPING_HAND, 0);
@ -129,8 +128,7 @@ describe("Abilities - Good As Gold", () => {
}); });
it("should not block field targeted effects like rain dance", async () => { it("should not block field targeted effects like rain dance", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single").enemyMoveset([MoveId.RAIN_DANCE]);
game.override.enemyMoveset([MoveId.RAIN_DANCE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.use(MoveId.SPLASH, 0); game.move.use(MoveId.SPLASH, 0);

View File

@ -4,17 +4,15 @@ import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
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, vi, type MockInstance } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { isNullOrUndefined } from "#app/utils/common"; import { isNullOrUndefined } from "#app/utils/common";
import { allAbilities } from "#app/data/data-lists"; import { allAbilities } from "#app/data/data-lists";
import type Pokemon from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon";
import type { PostTurnResetStatusAbAttr } from "#app/@types/ability-types"; import { PostTurnResetStatusAbAttr } from "#app/data/abilities/ability";
describe("Abilities - Healer", () => { describe("Abilities - Healer", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
let game: GameManager; let game: GameManager;
let healerAttrSpy: MockInstance;
let healerAttr: PostTurnResetStatusAbAttr;
beforeAll(() => { beforeAll(() => {
phaserGame = new Phaser.Game({ phaserGame = new Phaser.Game({
@ -24,7 +22,6 @@ describe("Abilities - Healer", () => {
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
healerAttrSpy.mockRestore();
}); });
beforeEach(() => { beforeEach(() => {
@ -38,30 +35,28 @@ describe("Abilities - Healer", () => {
.enemyAbility(AbilityId.BALL_FETCH) .enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset(MoveId.SPLASH); .enemyMoveset(MoveId.SPLASH);
healerAttr = allAbilities[AbilityId.HEALER].getAttrs("PostTurnResetStatusAbAttr")[0]; // Mock healer to have a 100% chance of healing its ally
healerAttrSpy = vi vi.spyOn(allAbilities[AbilityId.HEALER].getAttrs("PostTurnResetStatusAbAttr")[0], "getCondition").mockReturnValue(
.spyOn(healerAttr, "getCondition") (pokemon: Pokemon) => !isNullOrUndefined(pokemon.getAlly()),
.mockReturnValue((pokemon: Pokemon) => !isNullOrUndefined(pokemon.getAlly())); );
}); });
it("should not queue a message phase for healing if the ally has fainted", async () => { it("should not queue a message phase for healing if the ally has fainted", async () => {
const abSpy = vi.spyOn(PostTurnResetStatusAbAttr.prototype, "canApplyPostTurn");
game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]); game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
const user = game.scene.getPlayerPokemon()!; const user = game.scene.getPlayerPokemon()!;
// Only want one magikarp to have the ability. // Only want one magikarp to have the ability
vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[AbilityId.HEALER]); vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[AbilityId.HEALER]);
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
// faint the ally // faint the ally
game.move.select(MoveId.LUNAR_DANCE, 1); game.move.select(MoveId.LUNAR_DANCE, 1);
const abSpy = vi.spyOn(healerAttr, "canApplyPostTurn");
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
// It's not enough to just test that the ally still has its status. // It's not enough to just test that the ally still has its status.
// We need to ensure that the ability failed to meet its condition // We need to ensure that the ability failed to meet its condition
expect(abSpy).toHaveReturnedWith(false); expect(abSpy).toHaveReturnedWith(false);
// Explicitly restore the mock to ensure pollution doesn't happen
abSpy.mockRestore();
}); });
it("should heal the status of an ally if the ally has a status", async () => { it("should heal the status of an ally if the ally has a status", async () => {

View File

@ -75,8 +75,7 @@ describe("Abilities - Hustle", () => {
}); });
it("does not affect OHKO moves", async () => { it("does not affect OHKO moves", async () => {
game.override.startingLevel(100); game.override.startingLevel(100).enemyLevel(30);
game.override.enemyLevel(30);
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!; const pikachu = game.scene.getPlayerPokemon()!;

View File

@ -30,10 +30,11 @@ describe("Abilities - Ice Face", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.EISCUE); .battleStyle("single")
game.override.enemyAbility(AbilityId.ICE_FACE); .enemySpecies(SpeciesId.EISCUE)
game.override.moveset([MoveId.TACKLE, MoveId.ICE_BEAM, MoveId.TOXIC_THREAD, MoveId.HAIL]); .enemyAbility(AbilityId.ICE_FACE)
.moveset([MoveId.TACKLE, MoveId.ICE_BEAM, MoveId.TOXIC_THREAD, MoveId.HAIL]);
}); });
it("takes no damage from physical move and transforms to Noice", async () => { it("takes no damage from physical move and transforms to Noice", async () => {
@ -51,8 +52,7 @@ describe("Abilities - Ice Face", () => {
}); });
it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => { it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => {
game.override.moveset([MoveId.SURGING_STRIKES]); game.override.moveset([MoveId.SURGING_STRIKES]).enemyLevel(1);
game.override.enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.HITMONLEE]); await game.classicMode.startBattle([SpeciesId.HITMONLEE]);
game.move.select(MoveId.SURGING_STRIKES); game.move.select(MoveId.SURGING_STRIKES);
@ -196,12 +196,13 @@ describe("Abilities - Ice Face", () => {
}); });
it("reverts to Ice Face on arena reset", async () => { it("reverts to Ice Face on arena reset", async () => {
game.override.startingWave(4); game.override
game.override.startingLevel(4); .startingWave(4)
game.override.enemySpecies(SpeciesId.MAGIKARP); .startingLevel(4)
game.override.starterForms({ .enemySpecies(SpeciesId.MAGIKARP)
[SpeciesId.EISCUE]: noiceForm, .starterForms({
}); [SpeciesId.EISCUE]: noiceForm,
});
await game.classicMode.startBattle([SpeciesId.EISCUE]); await game.classicMode.startBattle([SpeciesId.EISCUE]);

View File

@ -116,26 +116,23 @@ describe("Abilities - Illusion", () => {
expect(psychicEffectiveness).above(flameThrowerEffectiveness); expect(psychicEffectiveness).above(flameThrowerEffectiveness);
}); });
it("does not break from indirect damage", async () => { it("should not break from indirect damage from status, weather or recoil", async () => {
game.override.enemySpecies(SpeciesId.GIGALITH); game.override.enemySpecies(SpeciesId.GIGALITH).enemyAbility(AbilityId.SAND_STREAM);
game.override.enemyAbility(AbilityId.SAND_STREAM);
game.override.enemyMoveset(MoveId.WILL_O_WISP);
game.override.moveset([MoveId.FLARE_BLITZ]);
await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.AZUMARILL]); await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.AZUMARILL]);
game.move.select(MoveId.FLARE_BLITZ); game.move.use(MoveId.FLARE_BLITZ);
await game.move.forceEnemyMove(MoveId.WILL_O_WISP);
await game.phaseInterceptor.to("TurnEndPhase"); await game.toEndOfTurn();
const zoroark = game.scene.getPlayerPokemon()!; const zoroark = game.scene.getPlayerPokemon()!;
expect(!!zoroark.summonData.illusion).equals(true); expect(!!zoroark.summonData.illusion).equals(true);
}); });
it("copies the the name, nickname, gender, shininess, and pokeball from the illusion source", async () => { it("copies the the name, nickname, gender, shininess, and pokeball from the illusion source", async () => {
game.override.enemyMoveset(MoveId.SPLASH); game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.ZOROARK, SpeciesId.AXEW]); await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.ZOROARK, SpeciesId.AXEW]);
const axew = game.scene.getPlayerParty().at(2)!; const axew = game.scene.getPlayerParty().at(2)!;
axew.shiny = true; axew.shiny = true;
axew.nickname = btoa(unescape(encodeURIComponent("axew nickname"))); axew.nickname = btoa(unescape(encodeURIComponent("axew nickname")));

View File

@ -162,8 +162,7 @@ describe("Abilities - Imposter", () => {
}); });
it("should stay transformed with the correct form after reload", async () => { it("should stay transformed with the correct form after reload", async () => {
game.override.moveset([MoveId.ABSORB]); game.override.moveset([MoveId.ABSORB]).enemySpecies(SpeciesId.UNOWN);
game.override.enemySpecies(SpeciesId.UNOWN);
await game.classicMode.startBattle([SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.DITTO]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;

View File

@ -62,7 +62,7 @@ describe("Abilities - Intimidate", () => {
expect(playerPokemon.species.speciesId).toBe(SpeciesId.POOCHYENA); expect(playerPokemon.species.speciesId).toBe(SpeciesId.POOCHYENA);
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2);
}, 20000); });
it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => { it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => {
game.override.battleStyle("double").startingWave(3); game.override.battleStyle("double").startingWave(3);
@ -85,11 +85,10 @@ describe("Abilities - Intimidate", () => {
expect(enemyField[1].getStatStage(Stat.ATK)).toBe(-2); expect(enemyField[1].getStatStage(Stat.ATK)).toBe(-2);
expect(playerField[0].getStatStage(Stat.ATK)).toBe(-2); expect(playerField[0].getStatStage(Stat.ATK)).toBe(-2);
expect(playerField[1].getStatStage(Stat.ATK)).toBe(-2); expect(playerField[1].getStatStage(Stat.ATK)).toBe(-2);
}, 20000); });
it("should not activate again if there is no switch or new entry", async () => { it("should not activate again if there is no switch or new entry", async () => {
game.override.startingWave(2); game.override.startingWave(2).moveset([MoveId.SPLASH]);
game.override.moveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]); await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.POOCHYENA]);
const playerPokemon = game.scene.getPlayerPokemon()!; const playerPokemon = game.scene.getPlayerPokemon()!;
@ -103,7 +102,7 @@ describe("Abilities - Intimidate", () => {
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
}, 20000); });
it("should lower ATK stat stage by 1 for every switch", async () => { it("should lower ATK stat stage by 1 for every switch", async () => {
game.override.moveset([MoveId.SPLASH]).enemyMoveset([MoveId.VOLT_SWITCH]).startingWave(5); game.override.moveset([MoveId.SPLASH]).enemyMoveset([MoveId.VOLT_SWITCH]).startingWave(5);
@ -130,5 +129,5 @@ describe("Abilities - Intimidate", () => {
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-3); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-3);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
}, 200000); });
}); });

View File

@ -22,10 +22,11 @@ describe("Abilities - Intrepid Sword", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.ZACIAN); .battleStyle("single")
game.override.enemyAbility(AbilityId.INTREPID_SWORD); .enemySpecies(SpeciesId.ZACIAN)
game.override.ability(AbilityId.INTREPID_SWORD); .enemyAbility(AbilityId.INTREPID_SWORD)
.ability(AbilityId.INTREPID_SWORD);
}); });
it("should raise ATK stat stage by 1 on entry", async () => { it("should raise ATK stat stage by 1 on entry", async () => {
@ -38,5 +39,5 @@ describe("Abilities - Intrepid Sword", () => {
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
}, 20000); });
}); });

View File

@ -108,8 +108,7 @@ describe("Abilities - Libero", () => {
}); });
test("ability applies correctly even if the type has changed by another ability", async () => { test("ability applies correctly even if the type has changed by another ability", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).passiveAbility(AbilityId.REFRIGERATE);
game.override.passiveAbility(AbilityId.REFRIGERATE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -156,8 +155,7 @@ describe("Abilities - Libero", () => {
}); });
test("ability applies correctly even if the pokemon's move misses", async () => { test("ability applies correctly even if the pokemon's move misses", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.SPLASH);
game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -188,8 +186,7 @@ describe("Abilities - Libero", () => {
}); });
test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => { test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemySpecies(SpeciesId.GASTLY);
game.override.enemySpecies(SpeciesId.GASTLY);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -262,8 +259,7 @@ describe("Abilities - Libero", () => {
}); });
test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => { test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => {
game.override.moveset([MoveId.TRICK_OR_TREAT]); game.override.moveset([MoveId.TRICK_OR_TREAT]).enemySpecies(SpeciesId.GASTLY);
game.override.enemySpecies(SpeciesId.GASTLY);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -41,18 +41,16 @@ describe("Abilities - Magic Bounce", () => {
it("should reflect basic status moves", async () => { it("should reflect basic status moves", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.GROWL); game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
}); });
it("should not bounce moves while the target is in the semi-invulnerable state", async () => { it("should not bounce moves while the target is in the semi-invulnerable state", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.override.moveset([MoveId.GROWL]);
game.override.enemyMoveset([MoveId.FLY]);
game.move.select(MoveId.GROWL); game.move.use(MoveId.GROWL);
await game.move.selectEnemyMove(MoveId.FLY); await game.move.forceEnemyMove(MoveId.FLY);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
@ -61,11 +59,10 @@ describe("Abilities - Magic Bounce", () => {
it("should individually bounce back multi-target moves", async () => { it("should individually bounce back multi-target moves", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double");
game.override.moveset([MoveId.GROWL, MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
game.move.select(MoveId.GROWL, 0); game.move.use(MoveId.GROWL, 0);
game.move.select(MoveId.SPLASH, 1); game.move.use(MoveId.SPLASH, 1);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
const user = game.scene.getPlayerField()[0]; const user = game.scene.getPlayerField()[0];
@ -75,9 +72,8 @@ describe("Abilities - Magic Bounce", () => {
it("should still bounce back a move that would otherwise fail", async () => { it("should still bounce back a move that would otherwise fail", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.scene.getEnemyPokemon()?.setStatStage(Stat.ATK, -6); game.scene.getEnemyPokemon()?.setStatStage(Stat.ATK, -6);
game.override.moveset([MoveId.GROWL]);
game.move.select(MoveId.GROWL); game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
@ -107,29 +103,26 @@ describe("Abilities - Magic Bounce", () => {
game.override.ability(AbilityId.MOLD_BREAKER); game.override.ability(AbilityId.MOLD_BREAKER);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.GROWL); game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
}); });
it("should bounce back a spread status move against both pokemon", async () => { it("should bounce back a spread status move against both pokemon", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").enemyMoveset([MoveId.SPLASH]);
game.override.moveset([MoveId.GROWL, MoveId.SPLASH]);
game.override.enemyMoveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
game.move.select(MoveId.GROWL, 0); game.move.use(MoveId.GROWL, 0);
game.move.select(MoveId.SPLASH, 1); game.move.use(MoveId.SPLASH, 1);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerField().every(p => p.getStatStage(Stat.ATK) === -2)).toBeTruthy(); expect(game.scene.getPlayerField().every(p => p.getStatStage(Stat.ATK) === -2)).toBeTruthy();
}); });
it("should only bounce spikes back once in doubles when both targets have magic bounce", async () => { it("should only bounce spikes back once in doubles when both targets have magic bounce", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").moveset([MoveId.SPIKES]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.override.moveset([MoveId.SPIKES]);
game.move.select(MoveId.SPIKES); game.move.select(MoveId.SPIKES);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
@ -139,8 +132,7 @@ describe("Abilities - Magic Bounce", () => {
}); });
it("should bounce spikes even when the target is protected", async () => { it("should bounce spikes even when the target is protected", async () => {
game.override.moveset([MoveId.SPIKES]); game.override.moveset([MoveId.SPIKES]).enemyMoveset([MoveId.PROTECT]);
game.override.enemyMoveset([MoveId.PROTECT]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.SPIKES); game.move.select(MoveId.SPIKES);
@ -149,8 +141,7 @@ describe("Abilities - Magic Bounce", () => {
}); });
it("should not bounce spikes when the target is in the semi-invulnerable state", async () => { it("should not bounce spikes when the target is in the semi-invulnerable state", async () => {
game.override.moveset([MoveId.SPIKES]); game.override.moveset([MoveId.SPIKES]).enemyMoveset([MoveId.FLY]);
game.override.enemyMoveset([MoveId.FLY]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.SPIKES); game.move.select(MoveId.SPIKES);
@ -160,9 +151,8 @@ describe("Abilities - Magic Bounce", () => {
}); });
it("should not bounce back curse", async () => { it("should not bounce back curse", async () => {
game.override.starterSpecies(SpeciesId.GASTLY);
await game.classicMode.startBattle([SpeciesId.GASTLY]);
game.override.moveset([MoveId.CURSE]); game.override.moveset([MoveId.CURSE]);
await game.classicMode.startBattle([SpeciesId.GASTLY]);
game.move.select(MoveId.CURSE); game.move.select(MoveId.CURSE);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
@ -171,8 +161,7 @@ describe("Abilities - Magic Bounce", () => {
}); });
it("should not cause encore to be interrupted after bouncing", async () => { it("should not cause encore to be interrupted after bouncing", async () => {
game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]); game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]).enemyMoveset([MoveId.TACKLE, MoveId.GROWL]);
game.override.enemyMoveset([MoveId.TACKLE, MoveId.GROWL]);
// game.override.ability(AbilityId.MOLD_BREAKER); // game.override.ability(AbilityId.MOLD_BREAKER);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!; const playerPokemon = game.scene.getPlayerPokemon()!;
@ -199,9 +188,10 @@ describe("Abilities - Magic Bounce", () => {
// TODO: encore is failing if the last move was virtual. // TODO: encore is failing if the last move was virtual.
it.todo("should not cause the bounced move to count for encore", async () => { it.todo("should not cause the bounced move to count for encore", async () => {
game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]); game.override
game.override.enemyMoveset([MoveId.GROWL, MoveId.TACKLE]); .moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE])
game.override.enemyAbility(AbilityId.MAGIC_BOUNCE); .enemyMoveset([MoveId.GROWL, MoveId.TACKLE])
.enemyAbility(AbilityId.MAGIC_BOUNCE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!; const playerPokemon = game.scene.getPlayerPokemon()!;
@ -227,9 +217,8 @@ describe("Abilities - Magic Bounce", () => {
// TODO: stomping tantrum should consider moves that were bounced. // TODO: stomping tantrum should consider moves that were bounced.
it.todo("should cause stomping tantrum to double in power when the last move was bounced", async () => { it.todo("should cause stomping tantrum to double in power when the last move was bounced", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single").moveset([MoveId.STOMPING_TANTRUM, MoveId.CHARM]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.override.moveset([MoveId.STOMPING_TANTRUM, MoveId.CHARM]);
const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM]; const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM];
vi.spyOn(stomping_tantrum, "calculateBattlePower"); vi.spyOn(stomping_tantrum, "calculateBattlePower");
@ -242,36 +231,30 @@ describe("Abilities - Magic Bounce", () => {
expect(stomping_tantrum.calculateBattlePower).toHaveReturnedWith(150); expect(stomping_tantrum.calculateBattlePower).toHaveReturnedWith(150);
}); });
// TODO: stomping tantrum should consider moves that were bounced. // TODO: stomping tantrum should consider moves that were bounced
it.todo( it.todo("should boost enemy's stomping tantrum after failed bounce", async () => {
"should properly cause the enemy's stomping tantrum to be doubled in power after bouncing and failing", game.override.enemyMoveset([MoveId.STOMPING_TANTRUM, MoveId.SPLASH, MoveId.CHARM]);
async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
game.override.enemyMoveset([MoveId.STOMPING_TANTRUM, MoveId.SPLASH, MoveId.CHARM]);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM]; const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM];
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(stomping_tantrum, "calculateBattlePower"); vi.spyOn(stomping_tantrum, "calculateBattlePower");
game.move.select(MoveId.SPORE); // Spore gets reflected back onto us
await game.move.selectEnemyMove(MoveId.CHARM); game.move.select(MoveId.SPORE);
await game.phaseInterceptor.to("TurnEndPhase"); await game.move.selectEnemyMove(MoveId.CHARM);
expect(enemy.getLastXMoves(1)[0].result).toBe("success"); await game.toNextTurn();
expect(enemy.getLastXMoves(1)[0].result).toBe("success");
await game.phaseInterceptor.to("BerryPhase"); game.move.select(MoveId.SPORE);
expect(stomping_tantrum.calculateBattlePower).toHaveReturnedWith(75); await game.move.selectEnemyMove(MoveId.STOMPING_TANTRUM);
await game.toNextTurn();
await game.toNextTurn(); expect(stomping_tantrum.calculateBattlePower).toHaveReturnedWith(150);
game.move.select(MoveId.GROWL); });
await game.phaseInterceptor.to("BerryPhase");
expect(stomping_tantrum.calculateBattlePower).toHaveReturnedWith(75);
},
);
it("should respect immunities when bouncing a move", async () => { it("should respect immunities when bouncing a move", async () => {
vi.spyOn(allMoves[MoveId.THUNDER_WAVE], "accuracy", "get").mockReturnValue(100); vi.spyOn(allMoves[MoveId.THUNDER_WAVE], "accuracy", "get").mockReturnValue(100);
game.override.moveset([MoveId.THUNDER_WAVE, MoveId.GROWL]); game.override.moveset([MoveId.THUNDER_WAVE, MoveId.GROWL]).ability(AbilityId.SOUNDPROOF);
game.override.ability(AbilityId.SOUNDPROOF);
await game.classicMode.startBattle([SpeciesId.PHANPY]); await game.classicMode.startBattle([SpeciesId.PHANPY]);
// Turn 1 - thunder wave immunity test // Turn 1 - thunder wave immunity test
@ -309,8 +292,7 @@ describe("Abilities - Magic Bounce", () => {
}); });
it("should always apply the leftmost available target's magic bounce when bouncing moves like sticky webs in doubles", async () => { it("should always apply the leftmost available target's magic bounce when bouncing moves like sticky webs in doubles", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").moveset([MoveId.STICKY_WEB, MoveId.SPLASH, MoveId.TRICK_ROOM]);
game.override.moveset([MoveId.STICKY_WEB, MoveId.SPLASH, MoveId.TRICK_ROOM]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
const [enemy_1, enemy_2] = game.scene.getEnemyField(); const [enemy_1, enemy_2] = game.scene.getEnemyField();
@ -345,6 +327,7 @@ describe("Abilities - Magic Bounce", () => {
it("should not bounce back status moves that hit through semi-invulnerable states", async () => { it("should not bounce back status moves that hit through semi-invulnerable states", async () => {
game.override.moveset([MoveId.TOXIC, MoveId.CHARM]); game.override.moveset([MoveId.TOXIC, MoveId.CHARM]);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
game.move.select(MoveId.TOXIC); game.move.select(MoveId.TOXIC);
await game.move.selectEnemyMove(MoveId.FLY); await game.move.selectEnemyMove(MoveId.FLY);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);

View File

@ -30,16 +30,16 @@ describe("Abilities - Magic Guard", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
/** Player Pokemon overrides */ game.override
game.override.ability(AbilityId.MAGIC_GUARD); /** Player Pokemon overrides */
game.override.moveset([MoveId.SPLASH]); .ability(AbilityId.MAGIC_GUARD)
game.override.startingLevel(100); .moveset([MoveId.SPLASH])
.startingLevel(100)
/** Enemy Pokemon overrides */ /** Enemy Pokemon overrides */
game.override.enemySpecies(SpeciesId.SNORLAX); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyAbility(AbilityId.INSOMNIA); .enemyAbility(AbilityId.INSOMNIA)
game.override.enemyMoveset(MoveId.SPLASH); .enemyMoveset(MoveId.SPLASH)
game.override.enemyLevel(100); .enemyLevel(100);
}); });
//Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Magic_Guard_(Ability) //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Magic_Guard_(Ability)
@ -89,8 +89,9 @@ describe("Abilities - Magic Guard", () => {
}); });
it("ability effect should not persist when the ability is replaced", async () => { it("ability effect should not persist when the ability is replaced", async () => {
game.override.enemyMoveset([MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED]); game.override
game.override.statusEffect(StatusEffect.POISON); .enemyMoveset([MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED, MoveId.WORRY_SEED])
.statusEffect(StatusEffect.POISON);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -108,8 +109,7 @@ describe("Abilities - Magic Guard", () => {
}); });
it("Magic Guard prevents damage caused by burn but other non-damaging effects are still applied", async () => { it("Magic Guard prevents damage caused by burn but other non-damaging effects are still applied", async () => {
game.override.enemyStatusEffect(StatusEffect.BURN); game.override.enemyStatusEffect(StatusEffect.BURN).enemyAbility(AbilityId.MAGIC_GUARD);
game.override.enemyAbility(AbilityId.MAGIC_GUARD);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -130,8 +130,7 @@ describe("Abilities - Magic Guard", () => {
}); });
it("Magic Guard prevents damage caused by toxic but other non-damaging effects are still applied", async () => { it("Magic Guard prevents damage caused by toxic but other non-damaging effects are still applied", async () => {
game.override.enemyStatusEffect(StatusEffect.TOXIC); game.override.enemyStatusEffect(StatusEffect.TOXIC).enemyAbility(AbilityId.MAGIC_GUARD);
game.override.enemyAbility(AbilityId.MAGIC_GUARD);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -208,8 +207,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents against damage from volatile status effects", async () => { it("Magic Guard prevents against damage from volatile status effects", async () => {
await game.classicMode.startBattle([SpeciesId.DUSKULL]); await game.classicMode.startBattle([SpeciesId.DUSKULL]);
game.override.moveset([MoveId.CURSE]); game.override.moveset([MoveId.CURSE]).enemyAbility(AbilityId.MAGIC_GUARD);
game.override.enemyAbility(AbilityId.MAGIC_GUARD);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
@ -331,8 +329,9 @@ describe("Abilities - Magic Guard", () => {
//Tests the ability Bad Dreams //Tests the ability Bad Dreams
game.override.statusEffect(StatusEffect.SLEEP); game.override.statusEffect(StatusEffect.SLEEP);
//enemy pokemon is given Spore just in case player pokemon somehow awakens during test //enemy pokemon is given Spore just in case player pokemon somehow awakens during test
game.override.enemyMoveset([MoveId.SPORE, MoveId.SPORE, MoveId.SPORE, MoveId.SPORE]); game.override
game.override.enemyAbility(AbilityId.BAD_DREAMS); .enemyMoveset([MoveId.SPORE, MoveId.SPORE, MoveId.SPORE, MoveId.SPORE])
.enemyAbility(AbilityId.BAD_DREAMS);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -353,8 +352,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => { it("Magic Guard prevents damage from abilities with PostFaintContactDamageAbAttr", async () => {
//Tests the abilities Innards Out/Aftermath //Tests the abilities Innards Out/Aftermath
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemyAbility(AbilityId.AFTERMATH);
game.override.enemyAbility(AbilityId.AFTERMATH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -377,8 +375,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => { it("Magic Guard prevents damage from abilities with PostDefendContactDamageAbAttr", async () => {
//Tests the abilities Iron Barbs/Rough Skin //Tests the abilities Iron Barbs/Rough Skin
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemyAbility(AbilityId.IRON_BARBS);
game.override.enemyAbility(AbilityId.IRON_BARBS);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -400,8 +397,7 @@ describe("Abilities - Magic Guard", () => {
it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => { it("Magic Guard prevents damage from abilities with ReverseDrainAbAttr", async () => {
//Tests the ability Liquid Ooze //Tests the ability Liquid Ooze
game.override.moveset([MoveId.ABSORB]); game.override.moveset([MoveId.ABSORB]).enemyAbility(AbilityId.LIQUID_OOZE);
game.override.enemyAbility(AbilityId.LIQUID_OOZE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -422,9 +418,7 @@ describe("Abilities - Magic Guard", () => {
}); });
it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => { it("Magic Guard prevents HP loss from abilities with PostWeatherLapseDamageAbAttr", async () => {
//Tests the abilities Solar Power/Dry Skin game.override.passiveAbility(AbilityId.SOLAR_POWER).weather(WeatherType.SUNNY);
game.override.passiveAbility(AbilityId.SOLAR_POWER);
game.override.weather(WeatherType.SUNNY);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;

View File

@ -37,8 +37,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Player side + single battle Intimidate - opponent loses stats", async () => { it("Player side + single battle Intimidate - opponent loses stats", async () => {
game.override.ability(AbilityId.MIRROR_ARMOR); game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
game.override.enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -54,8 +53,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Enemy side + single battle Intimidate - player loses stats", async () => { it("Enemy side + single battle Intimidate - player loses stats", async () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR); game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
game.override.ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -71,9 +69,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Player side + double battle Intimidate - opponents each lose -2 atk", async () => { it("Player side + double battle Intimidate - opponents each lose -2 atk", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
game.override.ability(AbilityId.MIRROR_ARMOR);
game.override.enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const [enemy1, enemy2] = game.scene.getEnemyField();
@ -93,9 +89,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Enemy side + double battle Intimidate - players each lose -2 atk", async () => { it("Enemy side + double battle Intimidate - players each lose -2 atk", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
game.override.enemyAbility(AbilityId.MIRROR_ARMOR);
game.override.ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const [enemy1, enemy2] = game.scene.getEnemyField();
@ -115,8 +109,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Player side + single battle Intimidate + Tickle - opponent loses stats", async () => { it("Player side + single battle Intimidate + Tickle - opponent loses stats", async () => {
game.override.ability(AbilityId.MIRROR_ARMOR); game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
game.override.enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -134,9 +127,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Player side + double battle Intimidate + Tickle - opponents each lose -3 atk, -1 def", async () => { it("Player side + double battle Intimidate + Tickle - opponents each lose -3 atk, -1 def", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
game.override.ability(AbilityId.MIRROR_ARMOR);
game.override.enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const [enemy1, enemy2] = game.scene.getEnemyField();
@ -159,8 +150,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Enemy side + single battle Intimidate + Tickle - player loses stats", async () => { it("Enemy side + single battle Intimidate + Tickle - player loses stats", async () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR); game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
game.override.ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -178,8 +168,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Player side + single battle Intimidate + oppoenent has white smoke - no one loses stats", async () => { it("Player side + single battle Intimidate + oppoenent has white smoke - no one loses stats", async () => {
game.override.enemyAbility(AbilityId.WHITE_SMOKE); game.override.enemyAbility(AbilityId.WHITE_SMOKE).ability(AbilityId.MIRROR_ARMOR);
game.override.ability(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -197,8 +186,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Enemy side + single battle Intimidate + player has white smoke - no one loses stats", async () => { it("Enemy side + single battle Intimidate + player has white smoke - no one loses stats", async () => {
game.override.ability(AbilityId.WHITE_SMOKE); game.override.ability(AbilityId.WHITE_SMOKE).enemyAbility(AbilityId.MIRROR_ARMOR);
game.override.enemyAbility(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -252,9 +240,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Both sides have mirror armor - does not loop, player loses attack", async () => { it("Both sides have mirror armor - does not loop, player loses attack", async () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR); game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
game.override.ability(AbilityId.MIRROR_ARMOR);
game.override.ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
@ -288,8 +274,7 @@ describe("Ability - Mirror Armor", () => {
}); });
it("Double battle + sticky web applied player side - player switches out and enemy 1 should lose -1 speed", async () => { it("Double battle + sticky web applied player side - player switches out and enemy 1 should lose -1 speed", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").ability(AbilityId.MIRROR_ARMOR);
game.override.ability(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]);
const [enemy1, enemy2] = game.scene.getEnemyField(); const [enemy1, enemy2] = game.scene.getEnemyField();

View File

@ -27,13 +27,14 @@ describe("Abilities - Moxie", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
const moveToUse = MoveId.AERIAL_ACE; const moveToUse = MoveId.AERIAL_ACE;
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.RATTATA); .battleStyle("single")
game.override.enemyAbility(AbilityId.MOXIE); .enemySpecies(SpeciesId.RATTATA)
game.override.ability(AbilityId.MOXIE); .enemyAbility(AbilityId.MOXIE)
game.override.startingLevel(2000); .ability(AbilityId.MOXIE)
game.override.moveset([moveToUse]); .startingLevel(2000)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([moveToUse])
.enemyMoveset(MoveId.SPLASH);
}); });
it("should raise ATK stat stage by 1 when winning a battle", async () => { it("should raise ATK stat stage by 1 when winning a battle", async () => {
@ -48,7 +49,7 @@ describe("Abilities - Moxie", () => {
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase);
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
}, 20000); });
// TODO: Activate this test when MOXIE is corrected to work on faint and not on battle victory // TODO: Activate this test when MOXIE is corrected to work on faint and not on battle victory
it.todo( it.todo(

View File

@ -63,7 +63,7 @@ describe("Abilities - Mycelium Might", () => {
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
}, 20000); });
it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => {
game.override.enemyMoveset(MoveId.TACKLE); game.override.enemyMoveset(MoveId.TACKLE);
@ -86,7 +86,7 @@ describe("Abilities - Mycelium Might", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
}, 20000); });
it("will not affect non-status moves", async () => { it("will not affect non-status moves", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
@ -105,5 +105,5 @@ describe("Abilities - Mycelium Might", () => {
// This means that the commandOrder should be identical to the speedOrder // This means that the commandOrder should be identical to the speedOrder
expect(speedOrder).toEqual([playerIndex, enemyIndex]); expect(speedOrder).toEqual([playerIndex, enemyIndex]);
expect(commandOrder).toEqual([playerIndex, enemyIndex]); expect(commandOrder).toEqual([playerIndex, enemyIndex]);
}, 20000); });
}); });

View File

@ -45,8 +45,9 @@ describe("Abilities - Normalize", () => {
}); });
it("should not apply the old type boost item after changing a move's type", async () => { it("should not apply the old type boost item after changing a move's type", async () => {
game.override.startingHeldItems([{ name: "ATTACK_TYPE_BOOSTER", count: 1, type: PokemonType.GRASS }]); game.override
game.override.moveset([MoveId.LEAFAGE]); .startingHeldItems([{ name: "ATTACK_TYPE_BOOSTER", count: 1, type: PokemonType.GRASS }])
.moveset([MoveId.LEAFAGE]);
const powerSpy = vi.spyOn(allMoves[MoveId.LEAFAGE], "calculateBattlePower"); const powerSpy = vi.spyOn(allMoves[MoveId.LEAFAGE], "calculateBattlePower");
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -58,8 +59,9 @@ describe("Abilities - Normalize", () => {
}); });
it("should apply silk scarf's power boost after changing a move's type", async () => { it("should apply silk scarf's power boost after changing a move's type", async () => {
game.override.startingHeldItems([{ name: "ATTACK_TYPE_BOOSTER", count: 1, type: PokemonType.NORMAL }]); game.override
game.override.moveset([MoveId.LEAFAGE]); .startingHeldItems([{ name: "ATTACK_TYPE_BOOSTER", count: 1, type: PokemonType.NORMAL }])
.moveset([MoveId.LEAFAGE]);
const powerSpy = vi.spyOn(allMoves[MoveId.LEAFAGE], "calculateBattlePower"); const powerSpy = vi.spyOn(allMoves[MoveId.LEAFAGE], "calculateBattlePower");
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -26,14 +26,15 @@ describe("Abilities - Parental Bond", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.disableCrits(); .battleStyle("single")
game.override.ability(AbilityId.PARENTAL_BOND); .disableCrits()
game.override.enemySpecies(SpeciesId.SNORLAX); .ability(AbilityId.PARENTAL_BOND)
game.override.enemyAbility(AbilityId.FUR_COAT); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyMoveset(MoveId.SPLASH); .enemyAbility(AbilityId.FUR_COAT)
game.override.startingLevel(100); .enemyMoveset(MoveId.SPLASH)
game.override.enemyLevel(100); .startingLevel(100)
.enemyLevel(100);
}); });
it("should add second strike to attack move", async () => { it("should add second strike to attack move", async () => {
@ -61,8 +62,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("should apply secondary effects to both strikes", async () => { it("should apply secondary effects to both strikes", async () => {
game.override.moveset([MoveId.POWER_UP_PUNCH]); game.override.moveset([MoveId.POWER_UP_PUNCH]).enemySpecies(SpeciesId.AMOONGUSS);
game.override.enemySpecies(SpeciesId.AMOONGUSS);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -148,8 +148,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("should not apply multiplier to counter moves", async () => { it("should not apply multiplier to counter moves", async () => {
game.override.moveset([MoveId.COUNTER]); game.override.moveset([MoveId.COUNTER]).enemyMoveset([MoveId.TACKLE]);
game.override.enemyMoveset([MoveId.TACKLE]);
await game.classicMode.startBattle([SpeciesId.SHUCKLE]); await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
@ -167,9 +166,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("should not apply to multi-target moves", async () => { it("should not apply to multi-target moves", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").moveset([MoveId.EARTHQUAKE]).passiveAbility(AbilityId.LEVITATE);
game.override.moveset([MoveId.EARTHQUAKE]);
game.override.passiveAbility(AbilityId.LEVITATE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]);
@ -237,8 +234,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("Moves boosted by this ability and Multi-Lens should strike 3 times", async () => { it("Moves boosted by this ability and Multi-Lens should strike 3 times", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -252,8 +248,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("Seismic Toss boosted by this ability and Multi-Lens should strike 3 times", async () => { it("Seismic Toss boosted by this ability and Multi-Lens should strike 3 times", async () => {
game.override.moveset([MoveId.SEISMIC_TOSS]); game.override.moveset([MoveId.SEISMIC_TOSS]).startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -378,8 +373,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("should not cause user to hit into King's Shield more than once", async () => { it("should not cause user to hit into King's Shield more than once", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemyMoveset([MoveId.KINGS_SHIELD]);
game.override.enemyMoveset([MoveId.KINGS_SHIELD]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -393,8 +387,7 @@ describe("Abilities - Parental Bond", () => {
}); });
it("should not cause user to hit into Storm Drain more than once", async () => { it("should not cause user to hit into Storm Drain more than once", async () => {
game.override.moveset([MoveId.WATER_GUN]); game.override.moveset([MoveId.WATER_GUN]).enemyAbility(AbilityId.STORM_DRAIN);
game.override.enemyAbility(AbilityId.STORM_DRAIN);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -21,19 +21,18 @@ describe("Abilities - Perish Song", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.disableCrits(); .battleStyle("single")
.disableCrits()
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemySpecies(SpeciesId.MAGIKARP)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyAbility(AbilityId.BALL_FETCH)
.starterSpecies(SpeciesId.CURSOLA)
game.override.starterSpecies(SpeciesId.CURSOLA); .ability(AbilityId.PERISH_BODY)
game.override.ability(AbilityId.PERISH_BODY); .moveset([MoveId.SPLASH])
game.override.moveset([MoveId.SPLASH]); .enemyMoveset([MoveId.AQUA_JET]);
}); });
it("should trigger when hit with damaging move", async () => { it("should trigger when hit with damaging move", async () => {
game.override.enemyMoveset([MoveId.AQUA_JET]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const cursola = game.scene.getPlayerPokemon(); const cursola = game.scene.getPlayerPokemon();
const magikarp = game.scene.getEnemyPokemon(); const magikarp = game.scene.getEnemyPokemon();
@ -46,7 +45,7 @@ describe("Abilities - Perish Song", () => {
}); });
it("should trigger even when fainting", async () => { it("should trigger even when fainting", async () => {
game.override.enemyMoveset([MoveId.AQUA_JET]).enemyLevel(100).startingLevel(1); game.override.enemyLevel(100).startingLevel(1);
await game.classicMode.startBattle([SpeciesId.CURSOLA, SpeciesId.FEEBAS]); await game.classicMode.startBattle([SpeciesId.CURSOLA, SpeciesId.FEEBAS]);
const magikarp = game.scene.getEnemyPokemon(); const magikarp = game.scene.getEnemyPokemon();
@ -87,9 +86,10 @@ describe("Abilities - Perish Song", () => {
}); });
it("should activate if cursola already has perish song, but not reset its counter", async () => { it("should activate if cursola already has perish song, but not reset its counter", async () => {
game.override.enemyMoveset([MoveId.PERISH_SONG, MoveId.AQUA_JET, MoveId.SPLASH]); game.override
game.override.moveset([MoveId.WHIRLWIND, MoveId.SPLASH]); .enemyMoveset([MoveId.PERISH_SONG, MoveId.AQUA_JET, MoveId.SPLASH])
game.override.startingWave(5); .moveset([MoveId.WHIRLWIND, MoveId.SPLASH])
.startingWave(5);
await game.classicMode.startBattle([SpeciesId.CURSOLA]); await game.classicMode.startBattle([SpeciesId.CURSOLA]);
const cursola = game.scene.getPlayerPokemon(); const cursola = game.scene.getPlayerPokemon();

View File

@ -35,8 +35,7 @@ describe("Abilities - POWER CONSTRUCT", () => {
test("check if fainted 50% Power Construct Pokemon switches to base form on arena reset", async () => { test("check if fainted 50% Power Construct Pokemon switches to base form on arena reset", async () => {
const baseForm = 2, const baseForm = 2,
completeForm = 4; completeForm = 4;
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.ZYGARDE]: completeForm, [SpeciesId.ZYGARDE]: completeForm,
}); });
@ -62,8 +61,7 @@ describe("Abilities - POWER CONSTRUCT", () => {
test("check if fainted 10% Power Construct Pokemon switches to base form on arena reset", async () => { test("check if fainted 10% Power Construct Pokemon switches to base form on arena reset", async () => {
const baseForm = 3, const baseForm = 3,
completeForm = 5; completeForm = 5;
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.ZYGARDE]: completeForm, [SpeciesId.ZYGARDE]: completeForm,
}); });

View File

@ -26,11 +26,12 @@ describe("Abilities - Power Spot", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.moveset([MoveId.TACKLE, MoveId.BREAKING_SWIPE, MoveId.SPLASH, MoveId.DAZZLING_GLEAM]); .battleStyle("double")
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.TACKLE, MoveId.BREAKING_SWIPE, MoveId.SPLASH, MoveId.DAZZLING_GLEAM])
game.override.enemySpecies(SpeciesId.SHUCKLE); .enemyMoveset(MoveId.SPLASH)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemySpecies(SpeciesId.SHUCKLE)
.enemyAbility(AbilityId.BALL_FETCH);
}); });
it("raises the power of allies' special moves by 30%", async () => { it("raises the power of allies' special moves by 30%", async () => {

View File

@ -108,8 +108,7 @@ describe("Abilities - Protean", () => {
}); });
test("ability applies correctly even if the type has changed by another ability", async () => { test("ability applies correctly even if the type has changed by another ability", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).passiveAbility(AbilityId.REFRIGERATE);
game.override.passiveAbility(AbilityId.REFRIGERATE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -156,8 +155,7 @@ describe("Abilities - Protean", () => {
}); });
test("ability applies correctly even if the pokemon's move misses", async () => { test("ability applies correctly even if the pokemon's move misses", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemyMoveset(MoveId.SPLASH);
game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -188,8 +186,7 @@ describe("Abilities - Protean", () => {
}); });
test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => { test("ability applies correctly even if the pokemon's move fails because of type immunity", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]).enemySpecies(SpeciesId.GASTLY);
game.override.enemySpecies(SpeciesId.GASTLY);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
@ -262,8 +259,7 @@ describe("Abilities - Protean", () => {
}); });
test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => { test("ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => {
game.override.moveset([MoveId.TRICK_OR_TREAT]); game.override.moveset([MoveId.TRICK_OR_TREAT]).enemySpecies(SpeciesId.GASTLY);
game.override.enemySpecies(SpeciesId.GASTLY);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -23,16 +23,15 @@ describe("Abilities - Quick Draw", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.starterSpecies(SpeciesId.MAGIKARP); .starterSpecies(SpeciesId.MAGIKARP)
game.override.ability(AbilityId.QUICK_DRAW); .ability(AbilityId.QUICK_DRAW)
game.override.moveset([MoveId.TACKLE, MoveId.TAIL_WHIP]); .moveset([MoveId.TACKLE, MoveId.TAIL_WHIP])
.enemyLevel(100)
game.override.enemyLevel(100); .enemySpecies(SpeciesId.MAGIKARP)
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemyAbility(AbilityId.BALL_FETCH)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyMoveset([MoveId.TACKLE]);
game.override.enemyMoveset([MoveId.TACKLE]);
vi.spyOn( vi.spyOn(
allAbilities[AbilityId.QUICK_DRAW].getAttrs("BypassSpeedChanceAbAttr")[0], allAbilities[AbilityId.QUICK_DRAW].getAttrs("BypassSpeedChanceAbAttr")[0],
@ -55,8 +54,8 @@ describe("Abilities - Quick Draw", () => {
expect(pokemon.isFainted()).toBe(false); expect(pokemon.isFainted()).toBe(false);
expect(enemy.isFainted()).toBe(true); expect(enemy.isFainted()).toBe(true);
expect(pokemon.waveData.abilitiesApplied).contain(AbilityId.QUICK_DRAW); expect(pokemon.waveData.abilitiesApplied).toContain(AbilityId.QUICK_DRAW);
}, 20000); });
test( test(
"does not triggered by non damage moves", "does not triggered by non damage moves",
@ -97,6 +96,6 @@ describe("Abilities - Quick Draw", () => {
expect(pokemon.isFainted()).toBe(true); expect(pokemon.isFainted()).toBe(true);
expect(enemy.isFainted()).toBe(false); expect(enemy.isFainted()).toBe(false);
expect(pokemon.waveData.abilitiesApplied).contain(AbilityId.QUICK_DRAW); expect(pokemon.waveData.abilitiesApplied).toContain(AbilityId.QUICK_DRAW);
}, 20000); });
}); });

View File

@ -22,15 +22,14 @@ describe("Abilities - Sand Spit", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.disableCrits(); .battleStyle("single")
.disableCrits()
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemySpecies(SpeciesId.MAGIKARP)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyAbility(AbilityId.BALL_FETCH)
.starterSpecies(SpeciesId.SILICOBRA)
game.override.starterSpecies(SpeciesId.SILICOBRA); .ability(AbilityId.SAND_SPIT)
game.override.ability(AbilityId.SAND_SPIT); .moveset([MoveId.SPLASH, MoveId.COIL]);
game.override.moveset([MoveId.SPLASH, MoveId.COIL]);
}); });
it("should trigger when hit with damaging move", async () => { it("should trigger when hit with damaging move", async () => {
@ -41,7 +40,7 @@ describe("Abilities - Sand Spit", () => {
await game.toNextTurn(); await game.toNextTurn();
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM);
}, 20000); });
it("should trigger even when fainting", async () => { it("should trigger even when fainting", async () => {
game.override.enemyMoveset([MoveId.TACKLE]).enemyLevel(100).startingLevel(1); game.override.enemyMoveset([MoveId.TACKLE]).enemyLevel(100).startingLevel(1);
@ -62,5 +61,5 @@ describe("Abilities - Sand Spit", () => {
await game.toNextTurn(); await game.toNextTurn();
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SANDSTORM); expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SANDSTORM);
}, 20000); });
}); });

View File

@ -31,8 +31,7 @@ describe("Abilities - SCHOOLING", () => {
test("check if fainted pokemon switches to base form on arena reset", async () => { test("check if fainted pokemon switches to base form on arena reset", async () => {
const soloForm = 0, const soloForm = 0,
schoolForm = 1; schoolForm = 1;
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.WISHIWASHI]: schoolForm, [SpeciesId.WISHIWASHI]: schoolForm,
}); });

View File

@ -24,9 +24,7 @@ describe("Abilities - Screen Cleaner", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override.battleStyle("single").ability(AbilityId.SCREEN_CLEANER).enemySpecies(SpeciesId.SHUCKLE);
game.override.ability(AbilityId.SCREEN_CLEANER);
game.override.enemySpecies(SpeciesId.SHUCKLE);
}); });
it("removes Aurora Veil", async () => { it("removes Aurora Veil", async () => {

View File

@ -22,15 +22,14 @@ describe("Abilities - Seed Sower", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.disableCrits(); .battleStyle("single")
.disableCrits()
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemySpecies(SpeciesId.MAGIKARP)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyAbility(AbilityId.BALL_FETCH)
.starterSpecies(SpeciesId.ARBOLIVA)
game.override.starterSpecies(SpeciesId.ARBOLIVA); .ability(AbilityId.SEED_SOWER)
game.override.ability(AbilityId.SEED_SOWER); .moveset([MoveId.SPLASH]);
game.override.moveset([MoveId.SPLASH]);
}); });
it("should trigger when hit with damaging move", async () => { it("should trigger when hit with damaging move", async () => {

View File

@ -69,7 +69,7 @@ describe("Abilities - Sheer Force", () => {
await game.phaseInterceptor.to("BerryPhase", false); await game.phaseInterceptor.to("BerryPhase", false);
expect(bindMove.calculateBattlePower).toHaveLastReturnedWith(bindMove.power); expect(bindMove.calculateBattlePower).toHaveLastReturnedWith(bindMove.power);
}, 20000); });
it("Sheer Force does not boost the base damage of moves with no secondary effect", async () => { it("Sheer Force does not boost the base damage of moves with no secondary effect", async () => {
game.override.moveset([MoveId.TACKLE]); game.override.moveset([MoveId.TACKLE]);

View File

@ -26,12 +26,13 @@ describe("Abilities - Shield Dust", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.ONIX); .battleStyle("single")
game.override.enemyAbility(AbilityId.SHIELD_DUST); .enemySpecies(SpeciesId.ONIX)
game.override.startingLevel(100); .enemyAbility(AbilityId.SHIELD_DUST)
game.override.moveset(MoveId.AIR_SLASH); .startingLevel(100)
game.override.enemyMoveset(MoveId.TACKLE); .moveset(MoveId.AIR_SLASH)
.enemyMoveset(MoveId.TACKLE);
}); });
it("Shield Dust", async () => { it("Shield Dust", async () => {

View File

@ -26,17 +26,17 @@ describe("Abilities - SHIELDS DOWN", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
const moveToUse = MoveId.SPLASH; const moveToUse = MoveId.SPLASH;
game.override.battleStyle("single"); game.override
game.override.ability(AbilityId.SHIELDS_DOWN); .battleStyle("single")
game.override.moveset([moveToUse]); .ability(AbilityId.SHIELDS_DOWN)
game.override.enemyMoveset([MoveId.TACKLE]); .moveset([moveToUse])
.enemyMoveset([MoveId.TACKLE]);
}); });
test("check if fainted pokemon switched to base form on arena reset", async () => { test("check if fainted pokemon switched to base form on arena reset", async () => {
const meteorForm = 0, const meteorForm = 0,
coreForm = 7; coreForm = 7;
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.MINIOR]: coreForm, [SpeciesId.MINIOR]: coreForm,
}); });
@ -70,8 +70,7 @@ describe("Abilities - SHIELDS DOWN", () => {
}); });
test("should still ignore non-volatile status moves used by a pokemon with mold breaker", async () => { test("should still ignore non-volatile status moves used by a pokemon with mold breaker", async () => {
game.override.enemyAbility(AbilityId.MOLD_BREAKER); game.override.enemyAbility(AbilityId.MOLD_BREAKER).enemyMoveset([MoveId.SPORE]);
game.override.enemyMoveset([MoveId.SPORE]);
await game.classicMode.startBattle([SpeciesId.MINIOR]); await game.classicMode.startBattle([SpeciesId.MINIOR]);
@ -94,8 +93,7 @@ describe("Abilities - SHIELDS DOWN", () => {
}); });
test("should ignore status moves even through mold breaker", async () => { test("should ignore status moves even through mold breaker", async () => {
game.override.enemyMoveset([MoveId.SPORE]); game.override.enemyMoveset([MoveId.SPORE]).enemyAbility(AbilityId.MOLD_BREAKER);
game.override.enemyAbility(AbilityId.MOLD_BREAKER);
await game.classicMode.startBattle([SpeciesId.MINIOR]); await game.classicMode.startBattle([SpeciesId.MINIOR]);
@ -108,8 +106,9 @@ describe("Abilities - SHIELDS DOWN", () => {
// toxic spikes currently does not poison flying types when gravity is in effect // toxic spikes currently does not poison flying types when gravity is in effect
test.todo("should become poisoned by toxic spikes when grounded", async () => { test.todo("should become poisoned by toxic spikes when grounded", async () => {
game.override.enemyMoveset([MoveId.GRAVITY, MoveId.TOXIC_SPIKES, MoveId.SPLASH]); game.override
game.override.moveset([MoveId.GRAVITY, MoveId.SPLASH]); .enemyMoveset([MoveId.GRAVITY, MoveId.TOXIC_SPIKES, MoveId.SPLASH])
.moveset([MoveId.GRAVITY, MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MINIOR]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MINIOR]);
@ -155,9 +154,7 @@ describe("Abilities - SHIELDS DOWN", () => {
// the `NoTransformAbilityAbAttr` attribute is not checked anywhere, so this test cannot pass. // the `NoTransformAbilityAbAttr` attribute is not checked anywhere, so this test cannot pass.
test.todo("ditto should not be immune to status after transforming", async () => { test.todo("ditto should not be immune to status after transforming", async () => {
game.override.enemySpecies(SpeciesId.DITTO); game.override.enemySpecies(SpeciesId.DITTO).enemyAbility(AbilityId.IMPOSTER).moveset([MoveId.SPLASH, MoveId.SPORE]);
game.override.enemyAbility(AbilityId.IMPOSTER);
game.override.moveset([MoveId.SPLASH, MoveId.SPORE]);
await game.classicMode.startBattle([SpeciesId.MINIOR]); await game.classicMode.startBattle([SpeciesId.MINIOR]);
@ -169,11 +166,12 @@ describe("Abilities - SHIELDS DOWN", () => {
}); });
test("should not prevent minior from receiving the fainted status effect in trainer battles", async () => { test("should not prevent minior from receiving the fainted status effect in trainer battles", async () => {
game.override.enemyMoveset([MoveId.TACKLE]); game.override
game.override.moveset([MoveId.THUNDERBOLT]); .enemyMoveset([MoveId.TACKLE])
game.override.startingLevel(100); .moveset([MoveId.THUNDERBOLT])
game.override.startingWave(5); .startingLevel(100)
game.override.enemySpecies(SpeciesId.MINIOR); .startingWave(5)
.enemySpecies(SpeciesId.MINIOR);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const minior = game.scene.getEnemyPokemon()!; const minior = game.scene.getEnemyPokemon()!;

View File

@ -36,5 +36,5 @@ describe("Abilities - Simple", () => {
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2);
}, 20000); });
}); });

View File

@ -53,7 +53,7 @@ describe("Abilities - Stall", () => {
// The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon. // The opponent Pokemon (with Stall) goes last despite having higher speed than the player Pokemon.
expect(speedOrder).toEqual([enemyIndex, playerIndex]); expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([playerIndex, enemyIndex]); expect(commandOrder).toEqual([playerIndex, enemyIndex]);
}, 20000); });
it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]); await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
@ -71,7 +71,7 @@ describe("Abilities - Stall", () => {
// The player Pokemon goes second because its move is in a lower priority bracket. // The player Pokemon goes second because its move is in a lower priority bracket.
expect(speedOrder).toEqual([enemyIndex, playerIndex]); expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([enemyIndex, playerIndex]); expect(commandOrder).toEqual([enemyIndex, playerIndex]);
}, 20000); });
it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => { it("If both Pokemon have stall and use the same move, speed is used to determine who goes first.", async () => {
game.override.ability(AbilityId.STALL); game.override.ability(AbilityId.STALL);
@ -91,5 +91,5 @@ describe("Abilities - Stall", () => {
// The player Pokemon (with Stall) goes second because its speed is lower. // The player Pokemon (with Stall) goes second because its speed is lower.
expect(speedOrder).toEqual([enemyIndex, playerIndex]); expect(speedOrder).toEqual([enemyIndex, playerIndex]);
expect(commandOrder).toEqual([enemyIndex, playerIndex]); expect(commandOrder).toEqual([enemyIndex, playerIndex]);
}, 20000); });
}); });

View File

@ -28,11 +28,12 @@ describe("Abilities - Steely Spirit", () => {
beforeEach(() => { beforeEach(() => {
ironHeadPower = allMoves[moveToCheck].power; ironHeadPower = allMoves[moveToCheck].power;
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.enemySpecies(SpeciesId.SHUCKLE); .battleStyle("double")
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemySpecies(SpeciesId.SHUCKLE)
game.override.moveset([MoveId.IRON_HEAD, MoveId.SPLASH]); .enemyAbility(AbilityId.BALL_FETCH)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.IRON_HEAD, MoveId.SPLASH])
.enemyMoveset(MoveId.SPLASH);
vi.spyOn(allMoves[moveToCheck], "calculateBattlePower"); vi.spyOn(allMoves[moveToCheck], "calculateBattlePower");
}); });

View File

@ -24,15 +24,14 @@ describe("Abilities - Sturdy", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.starterSpecies(SpeciesId.LUCARIO); .starterSpecies(SpeciesId.LUCARIO)
game.override.startingLevel(100); .startingLevel(100)
game.override.moveset([MoveId.CLOSE_COMBAT, MoveId.FISSURE]); .moveset([MoveId.CLOSE_COMBAT, MoveId.FISSURE])
.enemySpecies(SpeciesId.ARON)
game.override.enemySpecies(SpeciesId.ARON); .enemyLevel(5)
game.override.enemyLevel(5); .enemyAbility(AbilityId.STURDY);
game.override.enemyAbility(AbilityId.STURDY);
}); });
test("Sturdy activates when user is at full HP", async () => { test("Sturdy activates when user is at full HP", async () => {

View File

@ -30,13 +30,13 @@ describe("Abilities - Super Luck", () => {
.enemyMoveset(MoveId.SPLASH); .enemyMoveset(MoveId.SPLASH);
}); });
it("should increase the crit stage of a user by 1", async () => { it("should increase the user's crit stage by 1", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
const fn = vi.spyOn(enemy, "getCritStage"); const critSpy = vi.spyOn(enemy, "getCritStage"); // crit stage is called on enemy
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
expect(fn).toHaveReturnedWith(1); expect(critSpy).toHaveReturnedWith(1);
fn.mockRestore();
}); });
}); });

View File

@ -69,10 +69,7 @@ describe("Abilities - Sweet Veil", () => {
}); });
it("prevents the user and its allies already drowsy due to Yawn from falling asleep.", async () => { it("prevents the user and its allies already drowsy due to Yawn from falling asleep.", async () => {
game.override.enemySpecies(SpeciesId.PIKACHU); game.override.enemySpecies(SpeciesId.PIKACHU).enemyLevel(5).startingLevel(5).enemyMoveset(MoveId.SPLASH);
game.override.enemyLevel(5);
game.override.startingLevel(5);
game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE, SpeciesId.SWIRLIX]); await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE, SpeciesId.SWIRLIX]);

View File

@ -93,8 +93,6 @@ describe("Abilities - Tera Shell", () => {
await game.phaseInterceptor.to("BerryPhase", false); await game.phaseInterceptor.to("BerryPhase", false);
expect(spy).toHaveLastReturnedWith(1); expect(spy).toHaveLastReturnedWith(1);
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40); expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40);
spy.mockRestore();
}); });
it("should change the effectiveness of all strikes of a multi-strike move", async () => { it("should change the effectiveness of all strikes of a multi-strike move", async () => {
@ -114,6 +112,5 @@ describe("Abilities - Tera Shell", () => {
expect(spy).toHaveLastReturnedWith(0.5); expect(spy).toHaveLastReturnedWith(0.5);
} }
expect(spy).toHaveReturnedTimes(2); expect(spy).toHaveReturnedTimes(2);
spy.mockRestore();
}); });
}); });

View File

@ -51,8 +51,7 @@ describe("Abilities - Unseen Fist", () => {
await testUnseenFistHitResult(game, MoveId.BULLDOZE, MoveId.WIDE_GUARD, false)); await testUnseenFistHitResult(game, MoveId.BULLDOZE, MoveId.WIDE_GUARD, false));
it("should cause a contact move to ignore Protect, but not Substitute", async () => { it("should cause a contact move to ignore Protect, but not Substitute", async () => {
game.override.enemyLevel(1); game.override.enemyLevel(1).moveset([MoveId.TACKLE]);
game.override.moveset([MoveId.TACKLE]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();

View File

@ -3,11 +3,11 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id";
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 } from "vitest";
import { BattlerIndex } from "#enums/battler-index"; import { BattlerIndex } from "#enums/battler-index";
import { SpeciesId } from "#enums/species-id";
// See also: TypeImmunityAbAttr // See also: TypeImmunityAbAttr
describe("Abilities - Volt Absorb", () => { describe("Abilities - Volt Absorb", () => {
@ -26,19 +26,19 @@ describe("Abilities - Volt Absorb", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override.battleStyle("single").disableCrits();
game.override.disableCrits();
}); });
it("does not activate when CHARGE is used", async () => { it("does not activate when CHARGE is used", async () => {
const moveToUse = MoveId.CHARGE; const moveToUse = MoveId.CHARGE;
const ability = AbilityId.VOLT_ABSORB; const ability = AbilityId.VOLT_ABSORB;
game.override.moveset([moveToUse]); game.override
game.override.ability(ability); .moveset([moveToUse])
game.override.enemyMoveset([MoveId.SPLASH, MoveId.NONE, MoveId.NONE, MoveId.NONE]); .ability(ability)
game.override.enemySpecies(SpeciesId.DUSKULL); .enemyMoveset([MoveId.SPLASH])
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemySpecies(SpeciesId.DUSKULL)
.enemyAbility(AbilityId.BALL_FETCH);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
@ -54,10 +54,11 @@ describe("Abilities - Volt Absorb", () => {
}); });
it("should activate regardless of accuracy checks", async () => { it("should activate regardless of accuracy checks", async () => {
game.override.moveset(MoveId.THUNDERBOLT); game.override
game.override.enemyMoveset(MoveId.SPLASH); .moveset(MoveId.THUNDERBOLT)
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemyMoveset(MoveId.SPLASH)
game.override.enemyAbility(AbilityId.VOLT_ABSORB); .enemySpecies(SpeciesId.MAGIKARP)
.enemyAbility(AbilityId.VOLT_ABSORB);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
@ -74,10 +75,11 @@ describe("Abilities - Volt Absorb", () => {
}); });
it("regardless of accuracy should not trigger on pokemon in semi invulnerable state", async () => { it("regardless of accuracy should not trigger on pokemon in semi invulnerable state", async () => {
game.override.moveset(MoveId.THUNDERBOLT); game.override
game.override.enemyMoveset(MoveId.DIVE); .moveset(MoveId.THUNDERBOLT)
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemyMoveset(MoveId.DIVE)
game.override.enemyAbility(AbilityId.VOLT_ABSORB); .enemySpecies(SpeciesId.MAGIKARP)
.enemyAbility(AbilityId.VOLT_ABSORB);
await game.classicMode.startBattle(); await game.classicMode.startBattle();

View File

@ -23,14 +23,15 @@ describe("Abilities - Wind Power", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.SHIFTRY); .battleStyle("single")
game.override.enemyAbility(AbilityId.WIND_POWER); .enemySpecies(SpeciesId.SHIFTRY)
game.override.moveset([MoveId.TAILWIND, MoveId.SPLASH, MoveId.PETAL_BLIZZARD, MoveId.SANDSTORM]); .enemyAbility(AbilityId.WIND_POWER)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.TAILWIND, MoveId.SPLASH, MoveId.PETAL_BLIZZARD, MoveId.SANDSTORM])
.enemyMoveset(MoveId.SPLASH);
}); });
it("it becomes charged when hit by wind moves", async () => { it("becomes charged when hit by wind moves", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const shiftry = game.scene.getEnemyPokemon()!; const shiftry = game.scene.getEnemyPokemon()!;
@ -42,9 +43,8 @@ describe("Abilities - Wind Power", () => {
expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined(); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeDefined();
}); });
it("it becomes charged when Tailwind takes effect on its side", async () => { it("becomes charged when Tailwind takes effect on its side", async () => {
game.override.ability(AbilityId.WIND_POWER); game.override.ability(AbilityId.WIND_POWER).enemySpecies(SpeciesId.MAGIKARP);
game.override.enemySpecies(SpeciesId.MAGIKARP);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]); await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const shiftry = game.scene.getPlayerPokemon()!; const shiftry = game.scene.getPlayerPokemon()!;
@ -58,8 +58,7 @@ describe("Abilities - Wind Power", () => {
}); });
it("does not become charged when Tailwind takes effect on opposing side", async () => { it("does not become charged when Tailwind takes effect on opposing side", async () => {
game.override.enemySpecies(SpeciesId.MAGIKARP); game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_POWER);
game.override.ability(AbilityId.WIND_POWER);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]); await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const magikarp = game.scene.getEnemyPokemon()!; const magikarp = game.scene.getEnemyPokemon()!;

View File

@ -23,12 +23,13 @@ describe("Abilities - Wonder Skin", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.moveset([MoveId.TACKLE, MoveId.CHARM]); .battleStyle("single")
game.override.ability(AbilityId.BALL_FETCH); .moveset([MoveId.TACKLE, MoveId.CHARM])
game.override.enemySpecies(SpeciesId.SHUCKLE); .ability(AbilityId.BALL_FETCH)
game.override.enemyAbility(AbilityId.WONDER_SKIN); .enemySpecies(SpeciesId.SHUCKLE)
game.override.enemyMoveset(MoveId.SPLASH); .enemyAbility(AbilityId.WONDER_SKIN)
.enemyMoveset(MoveId.SPLASH);
}); });
it("lowers accuracy of status moves to 50%", async () => { it("lowers accuracy of status moves to 50%", async () => {

View File

@ -85,8 +85,7 @@ describe("Abilities - ZEN MODE", () => {
}); });
it("should switch to base form on arena reset", async () => { it("should switch to base form on arena reset", async () => {
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.DARMANITAN]: zenForm, [SpeciesId.DARMANITAN]: zenForm,
}); });

View File

@ -34,8 +34,7 @@ describe("Abilities - ZERO TO HERO", () => {
}); });
it("should swap to base form on arena reset", async () => { it("should swap to base form on arena reset", async () => {
game.override.startingWave(4); game.override.startingWave(4).starterForms({
game.override.starterForms({
[SpeciesId.PALAFIN]: heroForm, [SpeciesId.PALAFIN]: heroForm,
}); });

View File

@ -130,12 +130,6 @@ describe("RibbonAchv", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.moveset([]);
game.override.startingLevel(0);
game.override.starterSpecies(0);
game.override.enemyMoveset([]);
game.override.enemySpecies(0);
game.override.startingWave(0);
scene = game.scene; scene = game.scene;
}); });

View File

@ -24,12 +24,14 @@ describe("Weather - Fog", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.weather(WeatherType.FOG).battleStyle("single"); game.override
game.override.moveset([MoveId.TACKLE]); .weather(WeatherType.FOG)
game.override.ability(AbilityId.BALL_FETCH); .battleStyle("single")
game.override.enemyAbility(AbilityId.BALL_FETCH); .moveset([MoveId.TACKLE])
game.override.enemySpecies(SpeciesId.MAGIKARP); .ability(AbilityId.BALL_FETCH)
game.override.enemyMoveset([MoveId.SPLASH]); .enemyAbility(AbilityId.BALL_FETCH)
.enemySpecies(SpeciesId.MAGIKARP)
.enemyMoveset([MoveId.SPLASH]);
}); });
it("move accuracy is multiplied by 90%", async () => { it("move accuracy is multiplied by 90%", async () => {

View File

@ -24,11 +24,12 @@ describe("Weather - Strong Winds", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.startingLevel(10); .battleStyle("single")
game.override.enemySpecies(SpeciesId.TAILLOW); .startingLevel(10)
game.override.enemyAbility(AbilityId.DELTA_STREAM); .enemySpecies(SpeciesId.TAILLOW)
game.override.moveset([MoveId.THUNDERBOLT, MoveId.ICE_BEAM, MoveId.ROCK_SLIDE]); .enemyAbility(AbilityId.DELTA_STREAM)
.moveset([MoveId.THUNDERBOLT, MoveId.ICE_BEAM, MoveId.ROCK_SLIDE]);
}); });
it("electric type move is not very effective on Rayquaza", async () => { it("electric type move is not very effective on Rayquaza", async () => {

View File

@ -24,11 +24,12 @@ describe("Battle order", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.MEWTWO); .battleStyle("single")
game.override.enemyAbility(AbilityId.INSOMNIA); .enemySpecies(SpeciesId.MEWTWO)
game.override.ability(AbilityId.INSOMNIA); .enemyAbility(AbilityId.INSOMNIA)
game.override.moveset([MoveId.TACKLE]); .ability(AbilityId.INSOMNIA)
.moveset([MoveId.TACKLE]);
}); });
it("opponent faster than player 50 vs 150", async () => { it("opponent faster than player 50 vs 150", async () => {
@ -48,7 +49,7 @@ describe("Battle order", () => {
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
expect(order[0]).toBe(enemyPokemonIndex); expect(order[0]).toBe(enemyPokemonIndex);
expect(order[1]).toBe(playerPokemonIndex); expect(order[1]).toBe(playerPokemonIndex);
}, 20000); });
it("Player faster than opponent 150 vs 50", async () => { it("Player faster than opponent 150 vs 50", async () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
@ -67,7 +68,7 @@ describe("Battle order", () => {
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
expect(order[0]).toBe(playerPokemonIndex); expect(order[0]).toBe(playerPokemonIndex);
expect(order[1]).toBe(enemyPokemonIndex); expect(order[1]).toBe(enemyPokemonIndex);
}, 20000); });
it("double - both opponents faster than player 50/50 vs 150/150", async () => { it("double - both opponents faster than player 50/50 vs 150/150", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double");
@ -91,7 +92,7 @@ describe("Battle order", () => {
expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true);
expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true);
expect(order.slice(2, 4).includes(playerIndices[1])).toBe(true); expect(order.slice(2, 4).includes(playerIndices[1])).toBe(true);
}, 20000); });
it("double - speed tie except 1 - 100/100 vs 100/150", async () => { it("double - speed tie except 1 - 100/100 vs 100/150", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double");
@ -111,11 +112,10 @@ describe("Battle order", () => {
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
// enemy 2 should be first, followed by some other assortment of the other 3 pokemon
expect(order[0]).toBe(enemyIndices[1]); expect(order[0]).toBe(enemyIndices[1]);
expect(order.slice(1, 4).includes(enemyIndices[0])).toBe(true); expect(order.slice(1, 4)).toEqual(expect.arrayContaining([enemyIndices[0], ...playerIndices]));
expect(order.slice(1, 4).includes(playerIndices[0])).toBe(true); });
expect(order.slice(1, 4).includes(playerIndices[1])).toBe(true);
}, 20000);
it("double - speed tie 100/150 vs 100/150", async () => { it("double - speed tie 100/150 vs 100/150", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double");
@ -136,9 +136,8 @@ describe("Battle order", () => {
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
expect(order.slice(0, 2).includes(playerIndices[1])).toBe(true); // P2/E2 should be randomly first/second, then P1/E1 randomly 3rd/4th
expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); expect(order.slice(0, 2)).toStrictEqual(expect.arrayContaining([playerIndices[1], enemyIndices[1]]));
expect(order.slice(2, 4).includes(playerIndices[0])).toBe(true); expect(order.slice(2, 4)).toStrictEqual(expect.arrayContaining([playerIndices[0], enemyIndices[0]]));
expect(order.slice(2, 4).includes(enemyIndices[0])).toBe(true); });
}, 20000);
}); });

View File

@ -10,13 +10,11 @@ import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
import { LoginPhase } from "#app/phases/login-phase"; import { LoginPhase } from "#app/phases/login-phase";
import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
import { SelectGenderPhase } from "#app/phases/select-gender-phase"; import { SelectGenderPhase } from "#app/phases/select-gender-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { SelectStarterPhase } from "#app/phases/select-starter-phase"; import { SelectStarterPhase } from "#app/phases/select-starter-phase";
import { SummonPhase } from "#app/phases/summon-phase"; import { SummonPhase } from "#app/phases/summon-phase";
import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchPhase } from "#app/phases/switch-phase";
import { TitlePhase } from "#app/phases/title-phase"; import { TitlePhase } from "#app/phases/title-phase";
import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase";
import { VictoryPhase } from "#app/phases/victory-phase";
import GameManager from "#test/testUtils/gameManager"; import GameManager from "#test/testUtils/gameManager";
import { generateStarter } from "#test/testUtils/gameManagerUtils"; import { generateStarter } from "#test/testUtils/gameManagerUtils";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
@ -62,7 +60,7 @@ describe("Test Battle Phase", () => {
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE); expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
}, 20000); });
it("test phase interceptor with prompt with preparation for a future prompt", async () => { it("test phase interceptor with prompt with preparation for a future prompt", async () => {
await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase);
@ -83,40 +81,34 @@ describe("Test Battle Phase", () => {
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE); expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
}, 20000); });
it("newGame one-liner", async () => { it("newGame one-liner", async () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("do attack wave 3 - single battle - regular - OHKO", async () => { it("do attack wave 3 - single battle - regular - OHKO", async () => {
game.override.starterSpecies(SpeciesId.MEWTWO); game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3);
game.override.enemySpecies(SpeciesId.RATTATA); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
game.override.startingLevel(2000); game.move.use(MoveId.TACKLE);
game.override.startingWave(3).battleStyle("single"); await game.phaseInterceptor.to("SelectModifierPhase");
game.override.moveset([MoveId.TACKLE]); });
game.override.enemyAbility(AbilityId.HYDRATION);
game.override.enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]);
await game.classicMode.startBattle();
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(SelectModifierPhase, false);
}, 20000);
it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => { it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => {
game.override.starterSpecies(SpeciesId.MEWTWO); game.override
game.override.enemySpecies(SpeciesId.RATTATA); .enemySpecies(SpeciesId.RATTATA)
game.override.startingLevel(5); .startingLevel(5)
game.override.startingWave(3); .startingWave(3)
game.override.moveset([MoveId.TACKLE]); .moveset([MoveId.TACKLE])
game.override.enemyAbility(AbilityId.HYDRATION); .enemyAbility(AbilityId.HYDRATION)
game.override.enemyMoveset([MoveId.TAIL_WHIP, MoveId.TAIL_WHIP, MoveId.TAIL_WHIP, MoveId.TAIL_WHIP]); .enemyMoveset([MoveId.TAIL_WHIP, MoveId.TAIL_WHIP, MoveId.TAIL_WHIP, MoveId.TAIL_WHIP])
game.override.battleStyle("single"); .battleStyle("single");
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false);
}, 20000); });
it("load 100% data file", async () => { it("load 100% data file", async () => {
await game.importData("./test/testUtils/saves/everything.prsv"); await game.importData("./test/testUtils/saves/everything.prsv");
@ -125,14 +117,16 @@ describe("Test Battle Phase", () => {
return species.caughtAttr !== 0n; return species.caughtAttr !== 0n;
}).length; }).length;
expect(caughtCount).toBe(Object.keys(allSpecies).length); expect(caughtCount).toBe(Object.keys(allSpecies).length);
}, 20000); });
it("start battle with selected team", async () => { it("start battle with selected team", async () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHANSEY, SpeciesId.MEW]); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHANSEY, SpeciesId.MEW]);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.CHARIZARD); expect(game.scene.getPlayerParty().map(p => p.species.speciesId)).toEqual([
expect(game.scene.getPlayerParty()[1].species.speciesId).toBe(SpeciesId.CHANSEY); SpeciesId.CHARIZARD,
expect(game.scene.getPlayerParty()[2].species.speciesId).toBe(SpeciesId.MEW); SpeciesId.CHANSEY,
}, 20000); SpeciesId.MEW,
]);
});
it("test remove random battle seed int", async () => { it("test remove random battle seed int", async () => {
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
@ -146,12 +140,12 @@ describe("Test Battle Phase", () => {
await game.phaseInterceptor.run(LoginPhase).catch(e => { await game.phaseInterceptor.run(LoginPhase).catch(e => {
expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase"); expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase");
}); });
}, 20000); });
it("wrong phase but skip", async () => { it("wrong phase but skip", async () => {
await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase);
await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase)); await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase));
}, 20000); });
it("good run", async () => { it("good run", async () => {
await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase);
@ -166,7 +160,7 @@ describe("Test Battle Phase", () => {
); );
await game.phaseInterceptor.run(SelectGenderPhase, () => game.isCurrentPhase(TitlePhase)); await game.phaseInterceptor.run(SelectGenderPhase, () => game.isCurrentPhase(TitlePhase));
await game.phaseInterceptor.run(TitlePhase); await game.phaseInterceptor.run(TitlePhase);
}, 20000); });
it("good run from select gender to title", async () => { it("good run from select gender to title", async () => {
await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase);
@ -180,7 +174,7 @@ describe("Test Battle Phase", () => {
() => game.isCurrentPhase(TitlePhase), () => game.isCurrentPhase(TitlePhase),
); );
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase); await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase);
}, 20000); });
it("good run to SummonPhase phase", async () => { it("good run to SummonPhase phase", async () => {
await game.phaseInterceptor.run(LoginPhase); await game.phaseInterceptor.run(LoginPhase);
@ -201,7 +195,7 @@ describe("Test Battle Phase", () => {
selectStarterPhase.initBattle(starters); selectStarterPhase.initBattle(starters);
}); });
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase); await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase);
}, 20000); });
it("2vs1", async () => { it("2vs1", async () => {
game.override.battleStyle("single"); game.override.battleStyle("single");
@ -247,41 +241,42 @@ describe("Test Battle Phase", () => {
it("kill opponent pokemon", async () => { it("kill opponent pokemon", async () => {
const moveToUse = MoveId.SPLASH; const moveToUse = MoveId.SPLASH;
game.override.battleStyle("single"); game.override
game.override.starterSpecies(SpeciesId.MEWTWO); .battleStyle("single")
game.override.enemySpecies(SpeciesId.RATTATA); .starterSpecies(SpeciesId.MEWTWO)
game.override.enemyAbility(AbilityId.HYDRATION); .enemySpecies(SpeciesId.RATTATA)
game.override.ability(AbilityId.ZEN_MODE); .enemyAbility(AbilityId.HYDRATION)
game.override.startingLevel(2000); .ability(AbilityId.ZEN_MODE)
game.override.startingWave(3); .startingLevel(2000)
game.override.moveset([moveToUse]); .startingWave(3)
game.override.enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]); .moveset([moveToUse])
.enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]);
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
await game.killPokemon(game.scene.currentBattle.enemyParty[0]); await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
await game.phaseInterceptor.to(VictoryPhase, false); await game.phaseInterceptor.to("VictoryPhase");
}, 200000); });
it("to next turn", async () => { it("to next turn", async () => {
const moveToUse = MoveId.SPLASH; const moveToUse = MoveId.SPLASH;
game.override.battleStyle("single"); game.override
game.override.starterSpecies(SpeciesId.MEWTWO); .battleStyle("single")
game.override.enemySpecies(SpeciesId.RATTATA); .enemySpecies(SpeciesId.RATTATA)
game.override.enemyAbility(AbilityId.HYDRATION); .enemyAbility(AbilityId.HYDRATION)
game.override.ability(AbilityId.ZEN_MODE); .ability(AbilityId.ZEN_MODE)
game.override.startingLevel(2000); .startingLevel(2000)
game.override.startingWave(3); .startingWave(3)
game.override.moveset([moveToUse]); .moveset([moveToUse])
game.override.enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]); .enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]);
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
const turn = game.scene.currentBattle.turn; const turn = game.scene.currentBattle.turn;
game.move.select(moveToUse); game.move.select(moveToUse);
await game.toNextTurn(); await game.toNextTurn();
expect(game.scene.currentBattle.turn).toBeGreaterThan(turn); expect(game.scene.currentBattle.turn).toBeGreaterThan(turn);
}, 20000); });
it("does not set new weather if staying in same biome", async () => { it("does not set new weather if staying in same biome", async () => {
const moveToUse = MoveId.SPLASH; const moveToUse = MoveId.SPLASH;
@ -294,8 +289,8 @@ describe("Test Battle Phase", () => {
.startingLevel(2000) .startingLevel(2000)
.startingWave(3) .startingWave(3)
.startingBiome(BiomeId.LAKE) .startingBiome(BiomeId.LAKE)
.moveset([moveToUse]); .moveset([moveToUse])
game.override.enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]); .enemyMoveset([MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE, MoveId.TACKLE]);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const waveIndex = game.scene.currentBattle.waveIndex; const waveIndex = game.scene.currentBattle.waveIndex;
game.move.select(moveToUse); game.move.select(moveToUse);
@ -305,7 +300,7 @@ describe("Test Battle Phase", () => {
await game.toNextWave(); await game.toNextWave();
expect(game.scene.arena.trySetWeather).not.toHaveBeenCalled(); expect(game.scene.arena.trySetWeather).not.toHaveBeenCalled();
expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex); expect(game.scene.currentBattle.waveIndex).toBeGreaterThan(waveIndex);
}, 20000); });
it("does not force switch if active pokemon faints at same time as enemy mon and is revived in post-battle", async () => { it("does not force switch if active pokemon faints at same time as enemy mon and is revived in post-battle", async () => {
const moveToUse = MoveId.TAKE_DOWN; const moveToUse = MoveId.TAKE_DOWN;
@ -336,5 +331,5 @@ describe("Test Battle Phase", () => {
() => game.isCurrentPhase(NextEncounterPhase), () => game.isCurrentPhase(NextEncounterPhase),
); );
await game.phaseInterceptor.to(SwitchPhase); await game.phaseInterceptor.to(SwitchPhase);
}, 20000); });
}); });

View File

@ -56,7 +56,7 @@ describe("Double Battles", () => {
await game.phaseInterceptor.to(TurnInitPhase); await game.phaseInterceptor.to(TurnInitPhase);
expect(game.scene.getPlayerField().filter(p => !p.isFainted())).toHaveLength(2); expect(game.scene.getPlayerField().filter(p => !p.isFainted())).toHaveLength(2);
}, 20000); });
it("randomly chooses between single and double battles if there is no battle type override", async () => { it("randomly chooses between single and double battles if there is no battle type override", async () => {
let rngSweepProgress = 0; // Will simulate RNG rolls by slowly increasing from 0 to 1 let rngSweepProgress = 0; // Will simulate RNG rolls by slowly increasing from 0 to 1

View File

@ -1,4 +1,3 @@
import { CommandPhase } from "#app/phases/command-phase";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
@ -32,66 +31,67 @@ describe("Test Battle Phase", () => {
.enemyMoveset(MoveId.TACKLE); .enemyMoveset(MoveId.TACKLE);
}); });
// TODO: Make these into `it.each`es
it("startBattle 2vs1 boss", async () => { it("startBattle 2vs1 boss", async () => {
game.override.battleStyle("single").startingWave(10); game.override.battleStyle("single").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs2 boss", async () => { it("startBattle 2vs2 boss", async () => {
game.override.battleStyle("double").startingWave(10); game.override.battleStyle("double").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs2 trainer", async () => { it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs1 trainer", async () => { it("startBattle 2vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5); game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs1 rival", async () => { it("startBattle 2vs1 rival", async () => {
game.override.battleStyle("single").startingWave(8); game.override.battleStyle("single").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs2 rival", async () => { it("startBattle 2vs2 rival", async () => {
game.override.battleStyle("double").startingWave(8); game.override.battleStyle("double").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 1vs1 trainer", async () => { it("startBattle 1vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5); game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE]); await game.classicMode.startBattle([SpeciesId.BLASTOISE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 2vs2 trainer", async () => { it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
it("startBattle 4vs2 trainer", async () => { it("startBattle 4vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
}, 20000); });
}); });

View File

@ -13,7 +13,7 @@ import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import GameManager from "#test/testUtils/gameManager"; import GameManager from "#test/testUtils/gameManager";
import { mockI18next } from "#test/testUtils/testUtils"; import { mockI18next } from "#test/testUtils/testUtils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
const pokemonName = "PKM"; const pokemonName = "PKM";
const sourceText = "SOURCE"; const sourceText = "SOURCE";
@ -295,10 +295,6 @@ describe("Status Effect Messages", () => {
expect(text).toBe("statusEffect:burn.overlap"); expect(text).toBe("statusEffect:burn.overlap");
}); });
}); });
afterEach(() => {
vi.resetAllMocks();
});
}); });
describe("Status Effects", () => { describe("Status Effects", () => {

View File

@ -25,7 +25,6 @@ describe("Egg Generation Tests", () => {
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
vi.restoreAllMocks();
}); });
beforeEach(async () => { beforeEach(async () => {

View File

@ -21,7 +21,6 @@ describe("Manaphy Eggs", () => {
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
vi.restoreAllMocks();
}); });
beforeEach(async () => { beforeEach(async () => {

View File

@ -94,7 +94,7 @@ describe("Escape chance calculations", () => {
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
} }
}, 20000); });
it("double non-boss opponent", async () => { it("double non-boss opponent", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double");
@ -180,7 +180,7 @@ describe("Escape chance calculations", () => {
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
); );
} }
}, 20000); });
it("single boss opponent", async () => { it("single boss opponent", async () => {
game.override.startingWave(10); game.override.startingWave(10);
@ -259,11 +259,10 @@ describe("Escape chance calculations", () => {
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage); phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance); expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
} }
}, 20000); });
it("double boss opponent", async () => { it("double boss opponent", async () => {
game.override.battleStyle("double"); game.override.battleStyle("double").startingWave(10);
game.override.startingWave(10);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.ABOMASNOW]); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.ABOMASNOW]);
const playerPokemon = game.scene.getPlayerField(); const playerPokemon = game.scene.getPlayerField();
@ -358,5 +357,5 @@ describe("Escape chance calculations", () => {
escapeChances[i].pokemonSpeedRatio * totalEnemySpeed, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed,
); );
} }
}, 20000); });
}); });

View File

@ -28,12 +28,11 @@ describe("Evolution", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.enemySpecies(SpeciesId.MAGIKARP); .enemySpecies(SpeciesId.MAGIKARP)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyAbility(AbilityId.BALL_FETCH)
.startingLevel(60);
game.override.startingLevel(60);
}); });
it("should keep hidden ability after evolving", async () => { it("should keep hidden ability after evolving", async () => {

View File

@ -88,9 +88,7 @@ describe("Spec - Pokemon", () => {
let scene: BattleScene; let scene: BattleScene;
beforeEach(async () => { beforeEach(async () => {
game.override.enemySpecies(SpeciesId.ZUBAT); game.override.enemySpecies(SpeciesId.ZUBAT).starterSpecies(SpeciesId.ABRA).enableStarterFusion();
game.override.starterSpecies(SpeciesId.ABRA);
game.override.enableStarterFusion();
scene = game.scene; scene = game.scene;
}); });
@ -146,8 +144,7 @@ describe("Spec - Pokemon", () => {
}); });
it("Fusing mons with one and two types", async () => { it("Fusing mons with one and two types", async () => {
game.override.starterSpecies(SpeciesId.CHARMANDER); game.override.starterSpecies(SpeciesId.CHARMANDER).starterFusionSpecies(SpeciesId.HOUNDOUR);
game.override.starterFusionSpecies(SpeciesId.HOUNDOUR);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = scene.getPlayerParty()[0];
@ -157,8 +154,7 @@ describe("Spec - Pokemon", () => {
}); });
it("Fusing mons with two and one types", async () => { it("Fusing mons with two and one types", async () => {
game.override.starterSpecies(SpeciesId.NUMEL); game.override.starterSpecies(SpeciesId.NUMEL).starterFusionSpecies(SpeciesId.CHARMANDER);
game.override.starterFusionSpecies(SpeciesId.CHARMANDER);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = scene.getPlayerParty()[0];
@ -168,8 +164,7 @@ describe("Spec - Pokemon", () => {
}); });
it("Fusing two mons with two types", async () => { it("Fusing two mons with two types", async () => {
game.override.starterSpecies(SpeciesId.NATU); game.override.starterSpecies(SpeciesId.NATU).starterFusionSpecies(SpeciesId.HOUNDOUR);
game.override.starterFusionSpecies(SpeciesId.HOUNDOUR);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = scene.getPlayerParty()[0];

View File

@ -15,8 +15,6 @@ describe("game-mode", () => {
}); });
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
vi.clearAllMocks();
vi.resetAllMocks();
}); });
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);

View File

@ -37,7 +37,7 @@ describe("Items - Dire Hit", () => {
.moveset([MoveId.POUND]) .moveset([MoveId.POUND])
.startingHeldItems([{ name: "DIRE_HIT" }]) .startingHeldItems([{ name: "DIRE_HIT" }])
.battleStyle("single"); .battleStyle("single");
}, 20000); });
it("should raise CRIT stage by 1", async () => { it("should raise CRIT stage by 1", async () => {
await game.classicMode.startBattle([SpeciesId.GASTLY]); await game.classicMode.startBattle([SpeciesId.GASTLY]);
@ -51,7 +51,7 @@ describe("Items - Dire Hit", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyPokemon.getCritStage).toHaveReturnedWith(1); expect(enemyPokemon.getCritStage).toHaveReturnedWith(1);
}, 20000); });
it("should renew how many battles are left of existing DIRE_HIT when picking up new DIRE_HIT", async () => { it("should renew how many battles are left of existing DIRE_HIT when picking up new DIRE_HIT", async () => {
game.override.itemRewards([{ name: "DIRE_HIT" }]); game.override.itemRewards([{ name: "DIRE_HIT" }]);
@ -93,5 +93,5 @@ describe("Items - Dire Hit", () => {
} }
} }
expect(count).toBe(1); expect(count).toBe(1);
}, 20000); });
}); });

View File

@ -22,9 +22,7 @@ describe("EXP Modifier Items", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.enemyAbility(AbilityId.BALL_FETCH); game.override.enemyAbility(AbilityId.BALL_FETCH).ability(AbilityId.BALL_FETCH).battleStyle("single");
game.override.ability(AbilityId.BALL_FETCH);
game.override.battleStyle("single");
}); });
it("EXP booster items stack multiplicatively", async () => { it("EXP booster items stack multiplicatively", async () => {
@ -36,5 +34,5 @@ describe("EXP Modifier Items", () => {
const expHolder = new NumberHolder(partyMember.exp); const expHolder = new NumberHolder(partyMember.exp);
game.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, expHolder); game.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, expHolder);
expect(expHolder.value).toBe(440); expect(expHolder.value).toBe(440);
}, 20000); });
}); });

View File

@ -43,7 +43,7 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(2); expect(enemyMember.getCritStage).toHaveReturnedWith(2);
}, 20000); });
it("should raise CRIT stage by 2 when held by GALAR_FARFETCHD", async () => { it("should raise CRIT stage by 2 when held by GALAR_FARFETCHD", async () => {
await game.classicMode.startBattle([SpeciesId.GALAR_FARFETCHD]); await game.classicMode.startBattle([SpeciesId.GALAR_FARFETCHD]);
@ -57,7 +57,7 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(2); expect(enemyMember.getCritStage).toHaveReturnedWith(2);
}, 20000); });
it("should raise CRIT stage by 2 when held by SIRFETCHD", async () => { it("should raise CRIT stage by 2 when held by SIRFETCHD", async () => {
await game.classicMode.startBattle([SpeciesId.SIRFETCHD]); await game.classicMode.startBattle([SpeciesId.SIRFETCHD]);
@ -71,7 +71,7 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(2); expect(enemyMember.getCritStage).toHaveReturnedWith(2);
}, 20000); });
it("should raise CRIT stage by 2 when held by FARFETCHD line fused with Pokemon", async () => { it("should raise CRIT stage by 2 when held by FARFETCHD line fused with Pokemon", async () => {
// Randomly choose from the Farfetch'd line // Randomly choose from the Farfetch'd line
@ -99,7 +99,7 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(2); expect(enemyMember.getCritStage).toHaveReturnedWith(2);
}, 20000); });
it("should raise CRIT stage by 2 when held by Pokemon fused with FARFETCHD line", async () => { it("should raise CRIT stage by 2 when held by Pokemon fused with FARFETCHD line", async () => {
// Randomly choose from the Farfetch'd line // Randomly choose from the Farfetch'd line
@ -127,7 +127,7 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(2); expect(enemyMember.getCritStage).toHaveReturnedWith(2);
}, 20000); });
it("should not raise CRIT stage when held by a Pokemon outside of FARFETCHD line", async () => { it("should not raise CRIT stage when held by a Pokemon outside of FARFETCHD line", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
@ -141,5 +141,5 @@ describe("Items - Leek", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyMember.getCritStage).toHaveReturnedWith(0); expect(enemyMember.getCritStage).toHaveReturnedWith(0);
}, 20000); });
}); });

View File

@ -56,5 +56,5 @@ describe("Items - Leftovers", () => {
// Check if leftovers heal us // Check if leftovers heal us
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(leadPokemon.hp).toBeGreaterThan(leadHpAfterDamage); expect(leadPokemon.hp).toBeGreaterThan(leadHpAfterDamage);
}, 20000); });
}); });

View File

@ -108,7 +108,7 @@ describe("Items - Light Ball", () => {
expect(atkValue.value / atkStat).toBe(2); expect(atkValue.value / atkStat).toBe(2);
expect(spAtkValue.value / spAtkStat).toBe(2); expect(spAtkValue.value / spAtkStat).toBe(2);
}, 20000); });
it("LIGHT_BALL held by fused PIKACHU (base)", async () => { it("LIGHT_BALL held by fused PIKACHU (base)", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.MAROWAK]);
@ -147,7 +147,7 @@ describe("Items - Light Ball", () => {
expect(atkValue.value / atkStat).toBe(2); expect(atkValue.value / atkStat).toBe(2);
expect(spAtkValue.value / spAtkStat).toBe(2); expect(spAtkValue.value / spAtkStat).toBe(2);
}, 20000); });
it("LIGHT_BALL held by fused PIKACHU (part)", async () => { it("LIGHT_BALL held by fused PIKACHU (part)", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.PIKACHU]);
@ -186,7 +186,7 @@ describe("Items - Light Ball", () => {
expect(atkValue.value / atkStat).toBe(2); expect(atkValue.value / atkStat).toBe(2);
expect(spAtkValue.value / spAtkStat).toBe(2); expect(spAtkValue.value / spAtkStat).toBe(2);
}, 20000); });
it("LIGHT_BALL not held by PIKACHU", async () => { it("LIGHT_BALL not held by PIKACHU", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.MAROWAK]);
@ -215,5 +215,5 @@ describe("Items - Light Ball", () => {
expect(atkValue.value / atkStat).toBe(1); expect(atkValue.value / atkStat).toBe(1);
expect(spAtkValue.value / spAtkStat).toBe(1); expect(spAtkValue.value / spAtkStat).toBe(1);
}, 20000); });
}); });

View File

@ -49,5 +49,5 @@ describe("Items - Lock Capsule", () => {
game.doSelectModifier(); game.doSelectModifier();
await game.phaseInterceptor.to("SelectModifierPhase"); await game.phaseInterceptor.to("SelectModifierPhase");
}, 20000); });
}); });

View File

@ -102,7 +102,7 @@ describe("Items - Metal Powder", () => {
game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
expect(defValue.value / defStat).toBe(2); expect(defValue.value / defStat).toBe(2);
}, 20000); });
it("METAL_POWDER held by fused DITTO (base)", async () => { it("METAL_POWDER held by fused DITTO (base)", async () => {
await game.classicMode.startBattle([SpeciesId.DITTO, SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.DITTO, SpeciesId.MAROWAK]);
@ -135,7 +135,7 @@ describe("Items - Metal Powder", () => {
game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
expect(defValue.value / defStat).toBe(2); expect(defValue.value / defStat).toBe(2);
}, 20000); });
it("METAL_POWDER held by fused DITTO (part)", async () => { it("METAL_POWDER held by fused DITTO (part)", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.DITTO]);
@ -168,7 +168,7 @@ describe("Items - Metal Powder", () => {
game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
expect(defValue.value / defStat).toBe(2); expect(defValue.value / defStat).toBe(2);
}, 20000); });
it("METAL_POWDER not held by DITTO", async () => { it("METAL_POWDER not held by DITTO", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.MAROWAK]);
@ -191,5 +191,5 @@ describe("Items - Metal Powder", () => {
game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue);
expect(defValue.value / defStat).toBe(1); expect(defValue.value / defStat).toBe(1);
}, 20000); });
}); });

View File

@ -28,7 +28,7 @@ describe("Items - Scope Lens", () => {
.moveset([MoveId.POUND]) .moveset([MoveId.POUND])
.startingHeldItems([{ name: "SCOPE_LENS" }]) .startingHeldItems([{ name: "SCOPE_LENS" }])
.battleStyle("single"); .battleStyle("single");
}, 20000); });
it("should raise CRIT stage by 1", async () => { it("should raise CRIT stage by 1", async () => {
await game.classicMode.startBattle([SpeciesId.GASTLY]); await game.classicMode.startBattle([SpeciesId.GASTLY]);
@ -42,5 +42,5 @@ describe("Items - Scope Lens", () => {
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);
expect(enemyPokemon.getCritStage).toHaveReturnedWith(1); expect(enemyPokemon.getCritStage).toHaveReturnedWith(1);
}, 20000); });
}); });

View File

@ -50,7 +50,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase); await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase);
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
}, 20000); });
it("should increase existing ACC stat stage by 1 for X_ACCURACY only", async () => { it("should increase existing ACC stat stage by 1 for X_ACCURACY only", async () => {
game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]).ability(AbilityId.SIMPLE); game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]).ability(AbilityId.SIMPLE);
@ -72,7 +72,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
// ACC at +3 stat stages yields a x2 multiplier // ACC at +3 stat stages yields a x2 multiplier
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2); expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2);
}, 20000); });
it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async () => { it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
@ -92,7 +92,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
// ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK // ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8);
}, 20000); });
it("should not increase past maximum stat stage multiplier", async () => { it("should not increase past maximum stat stage multiplier", async () => {
game.override.startingModifier([ game.override.startingModifier([
@ -116,7 +116,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3); expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3);
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4);
}, 20000); });
it("should renew how many battles are left of existing booster when picking up new booster of same type", async () => { it("should renew how many battles are left of existing booster when picking up new booster of same type", async () => {
game.override.startingLevel(200).itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); game.override.startingLevel(200).itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]);
@ -161,5 +161,5 @@ describe("Items - Temporary Stat Stage Boosters", () => {
} }
} }
expect(count).toBe(1); expect(count).toBe(1);
}, 20000); });
}); });

View File

@ -24,16 +24,14 @@ describe("Moves - Baneful Bunker", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.moveset(MoveId.SLASH); .moveset(MoveId.SLASH)
.enemySpecies(SpeciesId.SNORLAX)
game.override.enemySpecies(SpeciesId.SNORLAX); .enemyAbility(AbilityId.INSOMNIA)
game.override.enemyAbility(AbilityId.INSOMNIA); .enemyMoveset(MoveId.BANEFUL_BUNKER)
game.override.enemyMoveset(MoveId.BANEFUL_BUNKER); .startingLevel(100)
.enemyLevel(100);
game.override.startingLevel(100);
game.override.enemyLevel(100);
}); });
test("should protect the user and poison attackers that make contact", async () => { test("should protect the user and poison attackers that make contact", async () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.CHARIZARD]);

View File

@ -55,39 +55,34 @@ describe("Moves - Baton Pass", () => {
playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.species.speciesId).toEqual(SpeciesId.SHUCKLE); expect(playerPokemon.species.speciesId).toEqual(SpeciesId.SHUCKLE);
expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2); expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2);
}, 20000); });
it("passes stat stage buffs when AI uses it", async () => { it("passes stat stage buffs when AI uses it", async () => {
// arrange // arrange
game.override.startingWave(5).enemyMoveset(new Array(4).fill([MoveId.NASTY_PLOT])); game.override.startingWave(5).enemyMoveset([MoveId.NASTY_PLOT, MoveId.BATON_PASS]);
await game.classicMode.startBattle([SpeciesId.RAICHU, SpeciesId.SHUCKLE]); await game.classicMode.startBattle([SpeciesId.RAICHU, SpeciesId.SHUCKLE]);
// round 1 - ai buffs // round 1 - ai buffs
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.move.forceEnemyMove(MoveId.NASTY_PLOT);
await game.toNextTurn(); await game.toNextTurn();
// round 2 - baton pass // round 2 - baton pass
game.scene.getEnemyPokemon()!.hp = 100;
game.override.enemyMoveset([MoveId.BATON_PASS]);
// Force moveset to update mid-battle
// TODO: replace with enemy ai control function when it's added
game.scene.getEnemyParty()[0].getMoveset();
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.move.forceEnemyMove(MoveId.BATON_PASS);
await game.phaseInterceptor.to("PostSummonPhase", false); await game.phaseInterceptor.to("PostSummonPhase", false);
// assert
// check buffs are still there // check buffs are still there
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPATK)).toEqual(2); expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.SPATK)).toEqual(2);
// confirm that a switch actually happened. can't use species because I // confirm that a switch actually happened. can't use species because I
// can't find a way to override trainer parties with more than 1 pokemon species // can't find a way to override trainer parties with more than 1 pokemon species
expect(game.scene.getEnemyPokemon()!.hp).not.toEqual(100);
expect(game.phaseInterceptor.log.slice(-4)).toEqual([ expect(game.phaseInterceptor.log.slice(-4)).toEqual([
"MoveEffectPhase", "MoveEffectPhase",
"SwitchSummonPhase", "SwitchSummonPhase",
"SummonPhase", "SummonPhase",
"PostSummonPhase", "PostSummonPhase",
]); ]);
}, 20000); });
it("doesn't transfer effects that aren't transferrable", async () => { it("doesn't transfer effects that aren't transferrable", async () => {
game.override.enemyMoveset([MoveId.SALT_CURE]); game.override.enemyMoveset([MoveId.SALT_CURE]);
@ -103,7 +98,7 @@ describe("Moves - Baton Pass", () => {
await game.toNextTurn(); await game.toNextTurn();
expect(player2.findTag(t => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined(); expect(player2.findTag(t => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined();
}, 20000); });
it("doesn't allow binding effects from the user to persist", async () => { it("doesn't allow binding effects from the user to persist", async () => {
game.override.moveset([MoveId.FIRE_SPIN, MoveId.BATON_PASS]); game.override.moveset([MoveId.FIRE_SPIN, MoveId.BATON_PASS]);

View File

@ -129,8 +129,7 @@ describe("Moves - Beak Blast", () => {
}); });
it("should not burn a long reach enemy that hits the user with a contact move", async () => { it("should not burn a long reach enemy that hits the user with a contact move", async () => {
game.override.enemyAbility(AbilityId.LONG_REACH); game.override.enemyAbility(AbilityId.LONG_REACH).enemyMoveset([MoveId.FALSE_SWIPE]).enemyLevel(100);
game.override.enemyMoveset([MoveId.FALSE_SWIPE]).enemyLevel(100);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.move.select(MoveId.BEAK_BLAST); game.move.select(MoveId.BEAK_BLAST);
await game.phaseInterceptor.to("BerryPhase", false); await game.phaseInterceptor.to("BerryPhase", false);

View File

@ -23,15 +23,14 @@ describe("Moves - Beat Up", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.enemySpecies(SpeciesId.SNORLAX); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyLevel(100); .enemyLevel(100)
game.override.enemyMoveset([MoveId.SPLASH]); .enemyMoveset([MoveId.SPLASH])
game.override.enemyAbility(AbilityId.INSOMNIA); .enemyAbility(AbilityId.INSOMNIA)
.startingLevel(100)
game.override.startingLevel(100); .moveset([MoveId.BEAT_UP]);
game.override.moveset([MoveId.BEAT_UP]);
}); });
it("should hit once for each healthy player Pokemon", async () => { it("should hit once for each healthy player Pokemon", async () => {

View File

@ -27,14 +27,15 @@ describe("Moves - Ceaseless Edge", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.enemySpecies(SpeciesId.RATTATA); .battleStyle("single")
game.override.enemyAbility(AbilityId.RUN_AWAY); .enemySpecies(SpeciesId.RATTATA)
game.override.enemyPassiveAbility(AbilityId.RUN_AWAY); .enemyAbility(AbilityId.RUN_AWAY)
game.override.startingLevel(100); .enemyPassiveAbility(AbilityId.RUN_AWAY)
game.override.enemyLevel(100); .startingLevel(100)
game.override.moveset([MoveId.CEASELESS_EDGE, MoveId.SPLASH, MoveId.ROAR]); .enemyLevel(100)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.CEASELESS_EDGE, MoveId.SPLASH, MoveId.ROAR])
.enemyMoveset(MoveId.SPLASH);
vi.spyOn(allMoves[MoveId.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100); vi.spyOn(allMoves[MoveId.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100);
}); });
@ -82,8 +83,7 @@ describe("Moves - Ceaseless Edge", () => {
}); });
test("trainer - move should hit twice, apply two layers of spikes, force switch opponent - opponent takes damage", async () => { test("trainer - move should hit twice, apply two layers of spikes, force switch opponent - opponent takes damage", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS" }]); game.override.startingHeldItems([{ name: "MULTI_LENS" }]).startingWave(25);
game.override.startingWave(25);
await game.classicMode.startBattle([SpeciesId.ILLUMISE]); await game.classicMode.startBattle([SpeciesId.ILLUMISE]);

View File

@ -27,12 +27,13 @@ describe("Moves - Clangorous Soul", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.starterSpecies(SpeciesId.MAGIKARP); game.override
game.override.enemySpecies(SpeciesId.SNORLAX); .starterSpecies(SpeciesId.MAGIKARP)
game.override.startingLevel(100); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyLevel(100); .startingLevel(100)
game.override.moveset([MoveId.CLANGOROUS_SOUL]); .enemyLevel(100)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.CLANGOROUS_SOUL])
.enemyMoveset(MoveId.SPLASH);
}); });
//Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move) //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/Clangorous_Soul_(move)

View File

@ -26,16 +26,14 @@ describe("Moves - Crafty Shield", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
.battleStyle("double")
game.override.moveset([MoveId.CRAFTY_SHIELD, MoveId.SPLASH, MoveId.SWORDS_DANCE]); .moveset([MoveId.CRAFTY_SHIELD, MoveId.SPLASH, MoveId.SWORDS_DANCE])
.enemySpecies(SpeciesId.SNORLAX)
game.override.enemySpecies(SpeciesId.SNORLAX); .enemyMoveset([MoveId.GROWL])
game.override.enemyMoveset([MoveId.GROWL]); .enemyAbility(AbilityId.INSOMNIA)
game.override.enemyAbility(AbilityId.INSOMNIA); .startingLevel(100)
.enemyLevel(100);
game.override.startingLevel(100);
game.override.enemyLevel(100);
}); });
test("should protect the user and allies from status moves", async () => { test("should protect the user and allies from status moves", async () => {
@ -73,8 +71,7 @@ describe("Moves - Crafty Shield", () => {
}); });
test("should protect the user and allies from moves that ignore other protection", async () => { test("should protect the user and allies from moves that ignore other protection", async () => {
game.override.enemySpecies(SpeciesId.DUSCLOPS); game.override.enemySpecies(SpeciesId.DUSCLOPS).enemyMoveset([MoveId.CURSE]);
game.override.enemyMoveset([MoveId.CURSE]);
await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]);

View File

@ -1,6 +1,5 @@
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { PokemonType } from "#enums/pokemon-type"; import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
@ -9,6 +8,7 @@ import { MoveId } from "#enums/move-id";
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, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { SpeciesId } from "#enums/species-id";
describe("Moves - Dragon Rage", () => { describe("Moves - Dragon Rage", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -31,19 +31,18 @@ describe("Moves - Dragon Rage", () => {
beforeEach(async () => { beforeEach(async () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
.battleStyle("single")
game.override.starterSpecies(SpeciesId.SNORLAX); .starterSpecies(SpeciesId.SNORLAX)
game.override.moveset([MoveId.DRAGON_RAGE]); .moveset([MoveId.DRAGON_RAGE])
game.override.ability(AbilityId.BALL_FETCH); .ability(AbilityId.BALL_FETCH)
game.override.passiveAbility(AbilityId.BALL_FETCH); .passiveAbility(AbilityId.BALL_FETCH)
game.override.startingLevel(100); .startingLevel(100)
.enemySpecies(SpeciesId.SNORLAX)
game.override.enemySpecies(SpeciesId.SNORLAX); .enemyMoveset(MoveId.SPLASH)
game.override.enemyMoveset(MoveId.SPLASH); .enemyAbility(AbilityId.BALL_FETCH)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemyPassiveAbility(AbilityId.BALL_FETCH)
game.override.enemyPassiveAbility(AbilityId.BALL_FETCH); .enemyLevel(100);
game.override.enemyLevel(100);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
@ -101,8 +100,7 @@ describe("Moves - Dragon Rage", () => {
}); });
it("ignores damage modification from abilities, for example ICE_SCALES", async () => { it("ignores damage modification from abilities, for example ICE_SCALES", async () => {
game.override.disableCrits(); game.override.disableCrits().enemyAbility(AbilityId.ICE_SCALES);
game.override.enemyAbility(AbilityId.ICE_SCALES);
game.move.select(MoveId.DRAGON_RAGE); game.move.select(MoveId.DRAGON_RAGE);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to(TurnEndPhase);

View File

@ -38,15 +38,7 @@ describe("Moves - Dynamax Cannon", () => {
.enemySpecies(SpeciesId.MAGIKARP) .enemySpecies(SpeciesId.MAGIKARP)
.enemyMoveset(MoveId.SPLASH); .enemyMoveset(MoveId.SPLASH);
// Note that, for Waves 1-10, the level cap is 10 vi.spyOn(allMoves[MoveId.DYNAMAX_CANNON], "calculateBattlePower");
game.override.startingWave(1);
game.override.battleStyle("single");
game.override.disableCrits();
game.override.enemySpecies(SpeciesId.MAGIKARP);
game.override.enemyMoveset([MoveId.SPLASH, MoveId.SPLASH, MoveId.SPLASH, MoveId.SPLASH]);
vi.spyOn(dynamaxCannon, "calculateBattlePower");
}); });
it("should return 100 power against an enemy below level cap", async () => { it("should return 100 power against an enemy below level cap", async () => {
@ -59,7 +51,7 @@ describe("Moves - Dynamax Cannon", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100);
}, 20000); });
it("should return 100 power against an enemy at level cap", async () => { it("should return 100 power against an enemy at level cap", async () => {
game.override.enemyLevel(10); game.override.enemyLevel(10);
@ -71,7 +63,7 @@ describe("Moves - Dynamax Cannon", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100);
}, 20000); });
it("should return 120 power against an enemy 1% above level cap", async () => { it("should return 120 power against an enemy 1% above level cap", async () => {
game.override.enemyLevel(101); game.override.enemyLevel(101);
@ -86,7 +78,7 @@ describe("Moves - Dynamax Cannon", () => {
vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120);
}, 20000); });
it("should return 140 power against an enemy 2% above level capp", async () => { it("should return 140 power against an enemy 2% above level capp", async () => {
game.override.enemyLevel(102); game.override.enemyLevel(102);
@ -101,7 +93,7 @@ describe("Moves - Dynamax Cannon", () => {
vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140);
}, 20000); });
it("should return 160 power against an enemy 3% above level cap", async () => { it("should return 160 power against an enemy 3% above level cap", async () => {
game.override.enemyLevel(103); game.override.enemyLevel(103);
@ -116,7 +108,7 @@ describe("Moves - Dynamax Cannon", () => {
vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160);
}, 20000); });
it("should return 180 power against an enemy 4% above level cap", async () => { it("should return 180 power against an enemy 4% above level cap", async () => {
game.override.enemyLevel(104); game.override.enemyLevel(104);
@ -131,7 +123,7 @@ describe("Moves - Dynamax Cannon", () => {
vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180);
}, 20000); });
it("should return 200 power against an enemy 5% above level cap", async () => { it("should return 200 power against an enemy 5% above level cap", async () => {
game.override.enemyLevel(105); game.override.enemyLevel(105);
@ -146,7 +138,7 @@ describe("Moves - Dynamax Cannon", () => {
vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("should return 200 power against an enemy way above level cap", async () => { it("should return 200 power against an enemy way above level cap", async () => {
game.override.enemyLevel(999); game.override.enemyLevel(999);
@ -159,5 +151,5 @@ describe("Moves - Dynamax Cannon", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
}); });

View File

@ -28,12 +28,13 @@ describe("Moves - FILLET AWAY", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.starterSpecies(SpeciesId.MAGIKARP); game.override
game.override.enemySpecies(SpeciesId.SNORLAX); .starterSpecies(SpeciesId.MAGIKARP)
game.override.startingLevel(100); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyLevel(100); .startingLevel(100)
game.override.moveset([MoveId.FILLET_AWAY]); .enemyLevel(100)
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.FILLET_AWAY])
.enemyMoveset(MoveId.SPLASH);
}); });
//Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move) //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/fillet_away_(move)

View File

@ -1,5 +1,4 @@
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase";
@ -8,6 +7,7 @@ import { MoveId } from "#enums/move-id";
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, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { SpeciesId } from "#enums/species-id";
describe("Moves - Fissure", () => { describe("Moves - Fissure", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -28,18 +28,17 @@ describe("Moves - Fissure", () => {
beforeEach(async () => { beforeEach(async () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("single"); game.override
game.override.disableCrits(); .battleStyle("single")
.disableCrits()
game.override.starterSpecies(SpeciesId.SNORLAX); .starterSpecies(SpeciesId.SNORLAX)
game.override.moveset([MoveId.FISSURE]); .moveset([MoveId.FISSURE])
game.override.passiveAbility(AbilityId.BALL_FETCH); .passiveAbility(AbilityId.BALL_FETCH)
game.override.startingLevel(100); .startingLevel(100)
.enemySpecies(SpeciesId.SNORLAX)
game.override.enemySpecies(SpeciesId.SNORLAX); .enemyMoveset(MoveId.SPLASH)
game.override.enemyMoveset(MoveId.SPLASH); .enemyPassiveAbility(AbilityId.BALL_FETCH)
game.override.enemyPassiveAbility(AbilityId.BALL_FETCH); .enemyLevel(100);
game.override.enemyLevel(100);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
@ -48,8 +47,7 @@ describe("Moves - Fissure", () => {
}); });
it("ignores damage modification from abilities, for example FUR_COAT", async () => { it("ignores damage modification from abilities, for example FUR_COAT", async () => {
game.override.ability(AbilityId.NO_GUARD); game.override.ability(AbilityId.NO_GUARD).enemyAbility(AbilityId.FUR_COAT);
game.override.enemyAbility(AbilityId.FUR_COAT);
game.move.select(MoveId.FISSURE); game.move.select(MoveId.FISSURE);
await game.phaseInterceptor.to(DamageAnimPhase, true); await game.phaseInterceptor.to(DamageAnimPhase, true);

View File

@ -35,14 +35,15 @@ describe("Moves - Flame Burst", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.moveset([MoveId.FLAME_BURST, MoveId.SPLASH]); .battleStyle("double")
game.override.disableCrits(); .moveset([MoveId.FLAME_BURST, MoveId.SPLASH])
game.override.ability(AbilityId.UNNERVE); .disableCrits()
game.override.startingWave(4); .ability(AbilityId.UNNERVE)
game.override.enemySpecies(SpeciesId.SHUCKLE); .startingWave(4)
game.override.enemyAbility(AbilityId.BALL_FETCH); .enemySpecies(SpeciesId.SHUCKLE)
game.override.enemyMoveset([MoveId.SPLASH]); .enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset([MoveId.SPLASH]);
}); });
it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => { it("inflicts damage to the target's ally equal to 1/16 of its max HP", async () => {

View File

@ -26,11 +26,12 @@ describe("Moves - Flower Shield", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.ability(AbilityId.NONE); game.override
game.override.enemyAbility(AbilityId.NONE); .ability(AbilityId.NONE)
game.override.battleStyle("single"); .enemyAbility(AbilityId.NONE)
game.override.moveset([MoveId.FLOWER_SHIELD, MoveId.SPLASH]); .battleStyle("single")
game.override.enemyMoveset(MoveId.SPLASH); .moveset([MoveId.FLOWER_SHIELD, MoveId.SPLASH])
.enemyMoveset(MoveId.SPLASH);
}); });
it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - single battle", async () => { it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - single battle", async () => {

View File

@ -24,14 +24,15 @@ describe("Moves - Follow Me", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.starterSpecies(SpeciesId.AMOONGUSS); .battleStyle("double")
game.override.ability(AbilityId.BALL_FETCH); .starterSpecies(SpeciesId.AMOONGUSS)
game.override.enemySpecies(SpeciesId.SNORLAX); .ability(AbilityId.BALL_FETCH)
game.override.startingLevel(100); .enemySpecies(SpeciesId.SNORLAX)
game.override.enemyLevel(100); .startingLevel(100)
game.override.moveset([MoveId.FOLLOW_ME, MoveId.RAGE_POWDER, MoveId.SPOTLIGHT, MoveId.QUICK_ATTACK]); .enemyLevel(100)
game.override.enemyMoveset([MoveId.TACKLE, MoveId.FOLLOW_ME, MoveId.SPLASH]); .moveset([MoveId.FOLLOW_ME, MoveId.RAGE_POWDER, MoveId.SPOTLIGHT, MoveId.QUICK_ATTACK])
.enemyMoveset([MoveId.TACKLE, MoveId.FOLLOW_ME, MoveId.SPLASH]);
}); });
test("move should redirect enemy attacks to the user", async () => { test("move should redirect enemy attacks to the user", async () => {
@ -73,8 +74,7 @@ describe("Moves - Follow Me", () => {
}); });
test("move effect should be bypassed by Stalwart", async () => { test("move effect should be bypassed by Stalwart", async () => {
game.override.ability(AbilityId.STALWART); game.override.ability(AbilityId.STALWART).moveset([MoveId.QUICK_ATTACK]);
game.override.moveset([MoveId.QUICK_ATTACK]);
await game.classicMode.startBattle([SpeciesId.AMOONGUSS, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.AMOONGUSS, SpeciesId.CHARIZARD]);

View File

@ -45,5 +45,5 @@ describe("Moves - Fusion Bolt", () => {
await game.toNextTurn(); await game.toNextTurn();
expect(initialHp - partyMember.hp).toBe(0); expect(initialHp - partyMember.hp).toBe(0);
}, 20000); });
}); });

View File

@ -64,7 +64,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async () => { it("FUSION_BOLT should double power of subsequent FUSION_FLARE", async () => {
await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.ZEKROM]); await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.ZEKROM]);
@ -84,7 +84,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async () => { it("FUSION_FLARE should double power of subsequent FUSION_BOLT if a move failed in between", async () => {
await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.ZEKROM]); await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.ZEKROM]);
@ -109,7 +109,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async () => { it("FUSION_FLARE should not double power of subsequent FUSION_BOLT if a move succeeded in between", async () => {
game.override.enemyMoveset(MoveId.SPLASH); game.override.enemyMoveset(MoveId.SPLASH);
@ -134,7 +134,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
}, 20000); });
it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async () => { it("FUSION_FLARE should double power of subsequent FUSION_BOLT if moves are aimed at allies", async () => {
await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.RESHIRAM]); await game.classicMode.startBattle([SpeciesId.ZEKROM, SpeciesId.RESHIRAM]);
@ -154,7 +154,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async () => { it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves", async () => {
game.override.enemyMoveset(fusionFlare.id); game.override.enemyMoveset(fusionFlare.id);
@ -208,7 +208,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async () => { it("FUSION_FLARE and FUSION_BOLT alternating throughout turn should double power of subsequent moves if moves are aimed at allies", async () => {
game.override.enemyMoveset(fusionFlare.id); game.override.enemyMoveset(fusionFlare.id);
@ -262,5 +262,5 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to(DamageAnimPhase, false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}, 20000); });
}); });

View File

@ -22,14 +22,15 @@ describe("Moves - Gastro Acid", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override.battleStyle("double"); game.override
game.override.startingLevel(1); .battleStyle("double")
game.override.enemyLevel(100); .startingLevel(1)
game.override.ability(AbilityId.BALL_FETCH); .enemyLevel(100)
game.override.moveset([MoveId.GASTRO_ACID, MoveId.WATER_GUN, MoveId.SPLASH, MoveId.CORE_ENFORCER]); .ability(AbilityId.NONE)
game.override.enemySpecies(SpeciesId.BIDOOF); .moveset([MoveId.GASTRO_ACID, MoveId.WATER_GUN, MoveId.SPLASH, MoveId.CORE_ENFORCER])
game.override.enemyMoveset(MoveId.SPLASH); .enemySpecies(SpeciesId.BIDOOF)
game.override.enemyAbility(AbilityId.WATER_ABSORB); .enemyMoveset(MoveId.SPLASH)
.enemyAbility(AbilityId.WATER_ABSORB);
}); });
it("suppresses effect of ability", async () => { it("suppresses effect of ability", async () => {

View File

@ -52,7 +52,7 @@ describe("Moves - Gigaton Hammer", () => {
const enemy2 = game.scene.getEnemyPokemon()!; const enemy2 = game.scene.getEnemyPokemon()!;
expect(enemy2.hp).toBe(enemy2.getMaxHp()); expect(enemy2.hp).toBe(enemy2.getMaxHp());
}, 20000); });
it("can be used again if recalled and sent back out", async () => { it("can be used again if recalled and sent back out", async () => {
game.override.startingWave(4); game.override.startingWave(4);
@ -75,5 +75,5 @@ describe("Moves - Gigaton Hammer", () => {
const enemy2 = game.scene.getEnemyPokemon()!; const enemy2 = game.scene.getEnemyPokemon()!;
expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp());
}, 20000); });
}); });

View File

@ -28,13 +28,12 @@ describe("Moves - Glaive Rush", () => {
.enemySpecies(SpeciesId.MAGIKARP) .enemySpecies(SpeciesId.MAGIKARP)
.enemyAbility(AbilityId.BALL_FETCH) .enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset([MoveId.GLAIVE_RUSH]) .enemyMoveset([MoveId.GLAIVE_RUSH])
.starterSpecies(SpeciesId.KLINK)
.ability(AbilityId.BALL_FETCH) .ability(AbilityId.BALL_FETCH)
.moveset([MoveId.SHADOW_SNEAK, MoveId.AVALANCHE, MoveId.SPLASH, MoveId.GLAIVE_RUSH]); .moveset([MoveId.SHADOW_SNEAK, MoveId.AVALANCHE, MoveId.SPLASH, MoveId.GLAIVE_RUSH]);
}); });
it("takes double damage from attacks", async () => { it("takes double damage from attacks", async () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.KLINK]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
enemy.hp = 1000; enemy.hp = 1000;
@ -49,7 +48,7 @@ describe("Moves - Glaive Rush", () => {
}); });
it("always gets hit by attacks", async () => { it("always gets hit by attacks", async () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.KLINK]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
enemy.hp = 1000; enemy.hp = 1000;
@ -62,7 +61,7 @@ describe("Moves - Glaive Rush", () => {
it("interacts properly with multi-lens", async () => { it("interacts properly with multi-lens", async () => {
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]).enemyMoveset([MoveId.AVALANCHE]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]).enemyMoveset([MoveId.AVALANCHE]);
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.KLINK]);
const player = game.scene.getPlayerPokemon()!; const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
@ -82,7 +81,7 @@ describe("Moves - Glaive Rush", () => {
it("secondary effects only last until next move", async () => { it("secondary effects only last until next move", async () => {
game.override.enemyMoveset([MoveId.SHADOW_SNEAK]); game.override.enemyMoveset([MoveId.SHADOW_SNEAK]);
await game.classicMode.startBattle(); await game.classicMode.startBattle([SpeciesId.KLINK]);
const player = game.scene.getPlayerPokemon()!; const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
@ -106,7 +105,7 @@ describe("Moves - Glaive Rush", () => {
}); });
it("secondary effects are removed upon switching", async () => { it("secondary effects are removed upon switching", async () => {
game.override.enemyMoveset([MoveId.SHADOW_SNEAK]).starterSpecies(0); game.override.enemyMoveset([MoveId.SHADOW_SNEAK]);
await game.classicMode.startBattle([SpeciesId.KLINK, SpeciesId.FEEBAS]); await game.classicMode.startBattle([SpeciesId.KLINK, SpeciesId.FEEBAS]);
const player = game.scene.getPlayerPokemon()!; const player = game.scene.getPlayerPokemon()!;
@ -127,8 +126,10 @@ describe("Moves - Glaive Rush", () => {
}); });
it("secondary effects don't activate if move fails", async () => { it("secondary effects don't activate if move fails", async () => {
game.override.moveset([MoveId.SHADOW_SNEAK, MoveId.PROTECT, MoveId.SPLASH, MoveId.GLAIVE_RUSH]); game.override
await game.classicMode.startBattle(); .moveset([MoveId.SHADOW_SNEAK, MoveId.PROTECT, MoveId.SPLASH, MoveId.GLAIVE_RUSH])
.enemyMoveset([MoveId.GLAIVE_RUSH, MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.KLINK]);
const player = game.scene.getPlayerPokemon()!; const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
@ -137,15 +138,17 @@ describe("Moves - Glaive Rush", () => {
player.hp = 1000; player.hp = 1000;
game.move.select(MoveId.PROTECT); game.move.select(MoveId.PROTECT);
await game.move.forceEnemyMove(MoveId.GLAIVE_RUSH);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
game.move.select(MoveId.SHADOW_SNEAK); game.move.select(MoveId.SHADOW_SNEAK);
await game.move.forceEnemyMove(MoveId.GLAIVE_RUSH);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
game.override.enemyMoveset([MoveId.SPLASH]);
const damagedHP1 = 1000 - enemy.hp; const damagedHP1 = 1000 - enemy.hp;
enemy.hp = 1000; enemy.hp = 1000;
game.move.select(MoveId.SHADOW_SNEAK); game.move.select(MoveId.SHADOW_SNEAK);
await game.move.forceEnemyMove(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
const damagedHP2 = 1000 - enemy.hp; const damagedHP2 = 1000 - enemy.hp;

Some files were not shown because too many files have changed in this diff Show More