mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 17:12:44 +02:00
Merge branch 'beta' into cleanup-evolution-scene-handler
This commit is contained in:
commit
0988cacc48
14
.github/test-filters.yml
vendored
14
.github/test-filters.yml
vendored
@ -1,7 +1,8 @@
|
|||||||
all:
|
all:
|
||||||
- "src/**"
|
# Negations syntax from https://github.com/dorny/paths-filter/issues/184#issuecomment-2786521554
|
||||||
- "test/**"
|
- "src/**/!(*.{md,py,sh,gitkeep,gitignore})"
|
||||||
- "public/**"
|
- "test/**/!(*.{md,py,sh,gitkeep,gitignore})"
|
||||||
|
- "public/**/!(*.{md,py,sh,gitkeep,gitignore})"
|
||||||
# Workflows that can impact tests
|
# Workflows that can impact tests
|
||||||
- ".github/workflows/test*.yml"
|
- ".github/workflows/test*.yml"
|
||||||
- ".github/test-filters.yml"
|
- ".github/test-filters.yml"
|
||||||
@ -11,9 +12,4 @@ all:
|
|||||||
- "vite*" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts
|
- "vite*" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts
|
||||||
- "tsconfig*.json" # tsconfig.json tweaking can impact compilation
|
- "tsconfig*.json" # tsconfig.json tweaking can impact compilation
|
||||||
- "global.d.ts"
|
- "global.d.ts"
|
||||||
- ".env*"
|
- ".env*"
|
||||||
# Blanket negations for files that cannot impact tests
|
|
||||||
- "!**/*.py" # No .py files
|
|
||||||
- "!**/*.sh" # No .sh files
|
|
||||||
- "!**/*.md" # No .md files
|
|
||||||
- "!**/.git*" # .gitkeep and family
|
|
9
.github/workflows/test-shard-template.yml
vendored
9
.github/workflows/test-shard-template.yml
vendored
@ -19,19 +19,20 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Shard ${{ inputs.shard }} of ${{ inputs.totalShards }}
|
# We can't use dynmically named jobs until https://github.com/orgs/community/discussions/13261 is implemented
|
||||||
|
name: Shard
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !inputs.skip }}
|
if: ${{ !inputs.skip }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v4.2.2
|
uses: actions/checkout@v4.2.2
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: "recursive"
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: ".nvmrc"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
- name: Install Node.js dependencies
|
- name: Install Node.js dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
7
.github/workflows/tests.yml
vendored
7
.github/workflows/tests.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
|||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36
|
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36
|
||||||
|
id: filter
|
||||||
with:
|
with:
|
||||||
filters: .github/test-filters.yml
|
filters: .github/test-filters.yml
|
||||||
|
|
||||||
@ -33,10 +34,10 @@ jobs:
|
|||||||
needs: check-path-change-filter
|
needs: check-path-change-filter
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
shard: [1, 2, 3, 4, 5]
|
||||||
uses: ./.github/workflows/test-shard-template.yml
|
uses: ./.github/workflows/test-shard-template.yml
|
||||||
with:
|
with:
|
||||||
project: main
|
project: main
|
||||||
shard: ${{ matrix.shard }}
|
shard: ${{ matrix.shard }}
|
||||||
totalShards: 10
|
totalShards: 5
|
||||||
skip: ${{ needs.check-path-change-filter.outputs.all == 'false'}}
|
skip: ${{ needs.check-path-change-filter.outputs.all != 'true'}}
|
||||||
|
@ -7425,7 +7425,12 @@ export function initAbilities() {
|
|||||||
.uncopiable()
|
.uncopiable()
|
||||||
.attr(NoTransformAbilityAbAttr),
|
.attr(NoTransformAbilityAbAttr),
|
||||||
new Ability(Abilities.GOOD_AS_GOLD, 9)
|
new Ability(Abilities.GOOD_AS_GOLD, 9)
|
||||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS && ![ MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES, MoveTarget.USER_SIDE ].includes(move.moveTarget))
|
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) =>
|
||||||
|
pokemon !== attacker
|
||||||
|
&& move.category === MoveCategory.STATUS
|
||||||
|
&& ![ MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES, MoveTarget.USER_SIDE ].includes(move.moveTarget)
|
||||||
|
)
|
||||||
|
.edgeCase() // Heal Bell should not cure the status of a Pokemon with Good As Gold
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.VESSEL_OF_RUIN, 9)
|
new Ability(Abilities.VESSEL_OF_RUIN, 9)
|
||||||
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)
|
.attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75)
|
||||||
|
9
src/enums/drop-down-column.ts
Normal file
9
src/enums/drop-down-column.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export enum DropDownColumn {
|
||||||
|
GEN,
|
||||||
|
TYPES,
|
||||||
|
BIOME,
|
||||||
|
CAUGHT,
|
||||||
|
UNLOCKS,
|
||||||
|
MISC,
|
||||||
|
SORT
|
||||||
|
}
|
@ -43,14 +43,13 @@ export default class EggCounterContainer extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
this.add(this.eggCountWindow);
|
this.add(this.eggCountWindow);
|
||||||
|
|
||||||
const eggSprite = globalScene.add.sprite(19, 18, "egg", "egg_0");
|
const eggSprite = globalScene.add.sprite(19, 18, "egg", "egg_0").setScale(0.32);
|
||||||
eggSprite.setScale(0.32);
|
|
||||||
|
|
||||||
this.eggCountText = addTextObject(28, 13, `${this.eggCount}`, TextStyle.MESSAGE, { fontSize: "66px" });
|
this.eggCountText = addTextObject(28, 13, `${this.eggCount}`, TextStyle.MESSAGE, { fontSize: "66px" }).setName(
|
||||||
this.eggCountText.setName("text-egg-count");
|
"text-egg-count",
|
||||||
|
);
|
||||||
|
|
||||||
this.add(eggSprite);
|
this.add([eggSprite, this.eggCountText]);
|
||||||
this.add(this.eggCountText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,16 +5,7 @@ import { addTextObject, getTextColor, TextStyle } from "./text";
|
|||||||
import type { UiTheme } from "#enums/ui-theme";
|
import type { UiTheme } from "#enums/ui-theme";
|
||||||
import { addWindow, WindowVariant } from "./ui-theme";
|
import { addWindow, WindowVariant } from "./ui-theme";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import type { DropDownColumn } from "#enums/drop-down-column";
|
||||||
export enum DropDownColumn {
|
|
||||||
GEN,
|
|
||||||
TYPES,
|
|
||||||
BIOME,
|
|
||||||
CAUGHT,
|
|
||||||
UNLOCKS,
|
|
||||||
MISC,
|
|
||||||
SORT,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FilterBar extends Phaser.GameObjects.Container {
|
export class FilterBar extends Phaser.GameObjects.Container {
|
||||||
private window: Phaser.GameObjects.NineSlice;
|
private window: Phaser.GameObjects.NineSlice;
|
||||||
@ -49,13 +40,9 @@ export class FilterBar extends Phaser.GameObjects.Container {
|
|||||||
this.cursorOffset = cursorOffset;
|
this.cursorOffset = cursorOffset;
|
||||||
|
|
||||||
this.window = addWindow(0, 0, width, height, false, false, undefined, undefined, WindowVariant.THIN);
|
this.window = addWindow(0, 0, width, height, false, false, undefined, undefined, WindowVariant.THIN);
|
||||||
this.add(this.window);
|
|
||||||
|
|
||||||
this.cursorObj = globalScene.add.image(1, 1, "cursor");
|
this.cursorObj = globalScene.add.image(1, 1, "cursor").setScale(0.5).setVisible(false).setOrigin(0);
|
||||||
this.cursorObj.setScale(0.5);
|
this.add([this.window, this.cursorObj]);
|
||||||
this.cursorObj.setVisible(false);
|
|
||||||
this.cursorObj.setOrigin(0, 0);
|
|
||||||
this.add(this.cursorObj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +23,8 @@ import type { Species } from "#enums/species";
|
|||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
|
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
|
||||||
import { PokedexMonContainer } from "#app/ui/pokedex-mon-container";
|
import { PokedexMonContainer } from "#app/ui/pokedex-mon-container";
|
||||||
import { DropDownColumn, FilterBar } from "#app/ui/filter-bar";
|
import { FilterBar } from "#app/ui/filter-bar";
|
||||||
|
import { DropDownColumn } from "#enums/drop-down-column";
|
||||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import {
|
import {
|
||||||
|
@ -53,7 +53,8 @@ import { Button } from "#enums/buttons";
|
|||||||
import { EggSourceType } from "#enums/egg-source-types";
|
import { EggSourceType } from "#enums/egg-source-types";
|
||||||
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
|
import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#app/ui/dropdown";
|
||||||
import { StarterContainer } from "#app/ui/starter-container";
|
import { StarterContainer } from "#app/ui/starter-container";
|
||||||
import { DropDownColumn, FilterBar } from "#app/ui/filter-bar";
|
import { FilterBar } from "#app/ui/filter-bar";
|
||||||
|
import { DropDownColumn } from "#enums/drop-down-column";
|
||||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||||
import { SelectChallengePhase } from "#app/phases/select-challenge-phase";
|
import { SelectChallengePhase } from "#app/phases/select-challenge-phase";
|
||||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { allAbilities } from "#app/data/data-lists";
|
|
||||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
import { Stat } from "#app/enums/stat";
|
import { Stat } from "#app/enums/stat";
|
||||||
@ -107,35 +107,33 @@ describe("Abilities - Good As Gold", () => {
|
|||||||
expect(game.scene.getPlayerField()[1].getTag(BattlerTagType.HELPING_HAND)).toBeUndefined();
|
expect(game.scene.getPlayerField()[1].getTag(BattlerTagType.HELPING_HAND)).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should block the ally's heal bell, but only if the good as gold user is on the field", async () => {
|
// TODO: re-enable when heal bell is fixed
|
||||||
game.override.battleStyle("double");
|
it.todo("should block the ally's heal bell, but only if the good as gold user is on the field", async () => {
|
||||||
game.override.moveset([Moves.HEAL_BELL, Moves.SPLASH]);
|
game.override.battleStyle("double").statusEffect(StatusEffect.BURN);
|
||||||
game.override.statusEffect(StatusEffect.BURN);
|
await game.classicMode.startBattle([Species.MILOTIC, Species.FEEBAS, Species.ABRA]);
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.ABRA]);
|
const [milotic, feebas, abra] = game.scene.getPlayerParty();
|
||||||
const [good_as_gold, ball_fetch] = game.scene.getPlayerField();
|
game.field.mockAbility(milotic, Abilities.GOOD_AS_GOLD);
|
||||||
|
game.field.mockAbility(feebas, Abilities.BALL_FETCH);
|
||||||
// Force second pokemon to have ball fetch to isolate to a single mon.
|
game.field.mockAbility(abra, Abilities.BALL_FETCH);
|
||||||
vi.spyOn(ball_fetch, "getAbility").mockReturnValue(allAbilities[Abilities.BALL_FETCH]);
|
|
||||||
|
|
||||||
// turn 1
|
// turn 1
|
||||||
game.move.select(Moves.SPLASH, 0);
|
game.move.use(Moves.SPLASH, 0);
|
||||||
game.move.select(Moves.HEAL_BELL, 1);
|
game.move.use(Moves.HEAL_BELL, 1);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
expect(good_as_gold.status?.effect).toBe(StatusEffect.BURN);
|
expect(milotic.status?.effect).toBe(StatusEffect.BURN);
|
||||||
|
|
||||||
game.doSwitchPokemon(2);
|
game.doSwitchPokemon(2);
|
||||||
game.move.select(Moves.HEAL_BELL, 0);
|
game.move.use(Moves.HEAL_BELL, 1);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
expect(good_as_gold.status?.effect).toBeUndefined();
|
expect(milotic.status?.effect).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
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");
|
||||||
game.override.enemyMoveset([Moves.RAIN_DANCE]);
|
game.override.enemyMoveset([Moves.RAIN_DANCE]);
|
||||||
game.override.weather(WeatherType.NONE);
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH, 0);
|
game.move.use(Moves.SPLASH, 0);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.RAIN);
|
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.RAIN);
|
||||||
|
@ -29,20 +29,18 @@ describe("Moves - Alluring Voice", () => {
|
|||||||
.disableCrits()
|
.disableCrits()
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.enemyAbility(Abilities.ICE_SCALES)
|
.enemyAbility(Abilities.ICE_SCALES)
|
||||||
.enemyMoveset([Moves.HOWL])
|
.enemyMoveset(Moves.HOWL)
|
||||||
.startingLevel(10)
|
.startingLevel(10)
|
||||||
.enemyLevel(10)
|
.enemyLevel(10)
|
||||||
.starterSpecies(Species.FEEBAS)
|
.ability(Abilities.BALL_FETCH);
|
||||||
.ability(Abilities.BALL_FETCH)
|
|
||||||
.moveset([Moves.ALLURING_VOICE]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should confuse the opponent if their stat stages were raised", async () => {
|
it("should confuse the opponent if their stat stages were raised", async () => {
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.ALLURING_VOICE);
|
game.move.use(Moves.ALLURING_VOICE);
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
await game.phaseInterceptor.to(BerryPhase);
|
await game.phaseInterceptor.to(BerryPhase);
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
@ -22,21 +23,23 @@ describe("Moves - Chloroblast", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
.moveset([Moves.CHLOROBLAST])
|
|
||||||
.ability(Abilities.BALL_FETCH)
|
.ability(Abilities.BALL_FETCH)
|
||||||
.battleStyle("single")
|
.battleStyle("single")
|
||||||
.disableCrits()
|
.disableCrits()
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
.enemyAbility(Abilities.BALL_FETCH);
|
||||||
.enemyMoveset(Moves.PROTECT);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not deal recoil damage if the opponent uses protect", async () => {
|
it("should not deal recoil damage if the opponent uses protect", async () => {
|
||||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||||
|
|
||||||
game.move.select(Moves.CHLOROBLAST);
|
game.move.use(Moves.CHLOROBLAST);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.move.forceEnemyMove(Moves.PROTECT);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBe(true);
|
const player = game.field.getPlayerPokemon();
|
||||||
|
|
||||||
|
expect(player.isFullHp()).toBe(true);
|
||||||
|
expect(player.getLastXMoves()[0]).toMatchObject({ result: MoveResult.MISS, move: Moves.CHLOROBLAST });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import Phaser from "phaser";
|
import { MoveResult, PokemonMove } from "#app/field/pokemon";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import { BerryPhase } from "#app/phases/berry-phase";
|
||||||
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
|
||||||
import { MoveResult, PokemonMove } from "#app/field/pokemon";
|
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { Species } from "#enums/species";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("Moves - Powder", () => {
|
describe("Moves - Powder", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -161,7 +161,6 @@ describe("Moves - Powder", () => {
|
|||||||
game.move.select(Moves.ROAR, 0, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.ROAR, 0, BattlerIndex.ENEMY_2);
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
await game.toNextTurn(); // Requires game.toNextTurn() twice due to double battle
|
|
||||||
|
|
||||||
// Turn 2: Enemy should activate Powder twice: From using Ember, and from copying Fiery Dance via Dancer
|
// Turn 2: Enemy should activate Powder twice: From using Ember, and from copying Fiery Dance via Dancer
|
||||||
playerPokemon.hp = playerPokemon.getMaxHp();
|
playerPokemon.hp = playerPokemon.getMaxHp();
|
||||||
|
@ -5,6 +5,7 @@ import { getMoveTargets } from "#app/data/moves/move";
|
|||||||
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import Trainer from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { GameModes, getGameMode } from "#app/game-mode";
|
||||||
|
import { globalScene } from "#app/global-scene";
|
||||||
import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
|
import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import overrides from "#app/overrides";
|
import overrides from "#app/overrides";
|
||||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||||
@ -22,15 +23,13 @@ import { TitlePhase } from "#app/phases/title-phase";
|
|||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||||
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
||||||
import ErrorInterceptor from "#test/testUtils/errorInterceptor";
|
|
||||||
import type InputsHandler from "#test/testUtils/inputsHandler";
|
|
||||||
import type BallUiHandler from "#app/ui/ball-ui-handler";
|
import type BallUiHandler from "#app/ui/ball-ui-handler";
|
||||||
import type BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
import type BattleMessageUiHandler from "#app/ui/battle-message-ui-handler";
|
||||||
import type CommandUiHandler from "#app/ui/command-ui-handler";
|
import type CommandUiHandler from "#app/ui/command-ui-handler";
|
||||||
import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||||
import type PartyUiHandler from "#app/ui/party-ui-handler";
|
import type PartyUiHandler from "#app/ui/party-ui-handler";
|
||||||
|
import type StarterSelectUiHandler from "#app/ui/starter-select-ui-handler";
|
||||||
import type TargetSelectUiHandler from "#app/ui/target-select-ui-handler";
|
import type TargetSelectUiHandler from "#app/ui/target-select-ui-handler";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
|
||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import { BattleStyle } from "#enums/battle-style";
|
import { BattleStyle } from "#enums/battle-style";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
@ -40,24 +39,26 @@ import type { Moves } from "#enums/moves";
|
|||||||
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import type { Species } from "#enums/species";
|
import type { Species } from "#enums/species";
|
||||||
|
import { UiMode } from "#enums/ui-mode";
|
||||||
|
import ErrorInterceptor from "#test/testUtils/errorInterceptor";
|
||||||
import { generateStarter, waitUntil } from "#test/testUtils/gameManagerUtils";
|
import { generateStarter, waitUntil } from "#test/testUtils/gameManagerUtils";
|
||||||
import GameWrapper from "#test/testUtils/gameWrapper";
|
import GameWrapper from "#test/testUtils/gameWrapper";
|
||||||
import { ChallengeModeHelper } from "#test/testUtils/helpers/challengeModeHelper";
|
import { ChallengeModeHelper } from "#test/testUtils/helpers/challengeModeHelper";
|
||||||
import { ClassicModeHelper } from "#test/testUtils/helpers/classicModeHelper";
|
import { ClassicModeHelper } from "#test/testUtils/helpers/classicModeHelper";
|
||||||
import { DailyModeHelper } from "#test/testUtils/helpers/dailyModeHelper";
|
import { DailyModeHelper } from "#test/testUtils/helpers/dailyModeHelper";
|
||||||
|
import { FieldHelper } from "#test/testUtils/helpers/field-helper";
|
||||||
import { ModifierHelper } from "#test/testUtils/helpers/modifiersHelper";
|
import { ModifierHelper } from "#test/testUtils/helpers/modifiersHelper";
|
||||||
import { MoveHelper } from "#test/testUtils/helpers/moveHelper";
|
import { MoveHelper } from "#test/testUtils/helpers/moveHelper";
|
||||||
import { OverridesHelper } from "#test/testUtils/helpers/overridesHelper";
|
import { OverridesHelper } from "#test/testUtils/helpers/overridesHelper";
|
||||||
import { ReloadHelper } from "#test/testUtils/helpers/reloadHelper";
|
import { ReloadHelper } from "#test/testUtils/helpers/reloadHelper";
|
||||||
import { SettingsHelper } from "#test/testUtils/helpers/settingsHelper";
|
import { SettingsHelper } from "#test/testUtils/helpers/settingsHelper";
|
||||||
|
import type InputsHandler from "#test/testUtils/inputsHandler";
|
||||||
|
import { MockFetch } from "#test/testUtils/mocks/mockFetch";
|
||||||
import PhaseInterceptor from "#test/testUtils/phaseInterceptor";
|
import PhaseInterceptor from "#test/testUtils/phaseInterceptor";
|
||||||
import TextInterceptor from "#test/testUtils/TextInterceptor";
|
import TextInterceptor from "#test/testUtils/TextInterceptor";
|
||||||
import { AES, enc } from "crypto-js";
|
import { AES, enc } from "crypto-js";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { expect, vi } from "vitest";
|
import { expect, vi } from "vitest";
|
||||||
import { globalScene } from "#app/global-scene";
|
|
||||||
import type StarterSelectUiHandler from "#app/ui/starter-select-ui-handler";
|
|
||||||
import { MockFetch } from "#test/testUtils/mocks/mockFetch";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manage the game state and transitions between phases.
|
* Class to manage the game state and transitions between phases.
|
||||||
@ -76,6 +77,7 @@ export default class GameManager {
|
|||||||
public readonly settings: SettingsHelper;
|
public readonly settings: SettingsHelper;
|
||||||
public readonly reload: ReloadHelper;
|
public readonly reload: ReloadHelper;
|
||||||
public readonly modifiers: ModifierHelper;
|
public readonly modifiers: ModifierHelper;
|
||||||
|
public readonly field: FieldHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of GameManager.
|
* Creates an instance of GameManager.
|
||||||
@ -123,6 +125,7 @@ export default class GameManager {
|
|||||||
this.settings = new SettingsHelper(this);
|
this.settings = new SettingsHelper(this);
|
||||||
this.reload = new ReloadHelper(this);
|
this.reload = new ReloadHelper(this);
|
||||||
this.modifiers = new ModifierHelper(this);
|
this.modifiers = new ModifierHelper(this);
|
||||||
|
this.field = new FieldHelper(this);
|
||||||
this.override.sanitizeOverrides();
|
this.override.sanitizeOverrides();
|
||||||
|
|
||||||
// Disables Mystery Encounters on all tests (can be overridden at test level)
|
// Disables Mystery Encounters on all tests (can be overridden at test level)
|
||||||
@ -383,6 +386,7 @@ export default class GameManager {
|
|||||||
* @param moveId - The {@linkcode Moves | move} the enemy will use
|
* @param moveId - The {@linkcode Moves | move} the enemy will use
|
||||||
* @param target - The {@linkcode BattlerIndex} of the target against which the enemy will use the given move;
|
* @param target - The {@linkcode BattlerIndex} of the target against which the enemy will use the given move;
|
||||||
* will use normal target selection priorities if omitted.
|
* will use normal target selection priorities if omitted.
|
||||||
|
* @deprecated Use {@linkcode MoveHelper.forceEnemyMove} or {@linkcode MoveHelper.selectEnemyMove}
|
||||||
*/
|
*/
|
||||||
async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
||||||
// Wait for the next EnemyCommandPhase to start
|
// Wait for the next EnemyCommandPhase to start
|
||||||
@ -417,9 +421,15 @@ export default class GameManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transition to the next upcoming {@linkcode CommandPhase} */
|
/** Transition to the first {@linkcode CommandPhase} of the next turn. */
|
||||||
async toNextTurn() {
|
async toNextTurn() {
|
||||||
await this.phaseInterceptor.to(CommandPhase);
|
await this.phaseInterceptor.to("TurnInitPhase");
|
||||||
|
await this.phaseInterceptor.to("CommandPhase");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transition to the {@linkcode TurnEndPhase | end of the current turn}. */
|
||||||
|
async toEndOfTurn() {
|
||||||
|
await this.phaseInterceptor.to("TurnEndPhase");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -541,8 +551,8 @@ export default class GameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select a pokemon from the party menu during the given phase.
|
* Select a pokemon from the party menu during the given phase.
|
||||||
* Only really handles the basic case of "navigate to party slot and press Action twice" -
|
* Only really handles the basic case of "navigate to party slot and press Action twice" -
|
||||||
* any menus that come up afterwards are ignored and must be handled separately by the caller.
|
* any menus that come up afterwards are ignored and must be handled separately by the caller.
|
||||||
* @param slot - The 0-indexed position of the pokemon in your party to switch to
|
* @param slot - The 0-indexed position of the pokemon in your party to switch to
|
||||||
* @param inPhase - Which phase to expect the selection to occur in. Defaults to `SwitchPhase`
|
* @param inPhase - Which phase to expect the selection to occur in. Defaults to `SwitchPhase`
|
||||||
|
87
test/testUtils/helpers/field-helper.ts
Normal file
87
test/testUtils/helpers/field-helper.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// -- start tsdoc imports --
|
||||||
|
// biome-ignore lint/correctness/noUnusedImports: TSDoc import
|
||||||
|
import type { globalScene } from "#app/global-scene";
|
||||||
|
// -- end tsdoc imports --
|
||||||
|
|
||||||
|
import type { BattlerIndex } from "#app/battle";
|
||||||
|
import type { Ability } from "#app/data/abilities/ability-class";
|
||||||
|
import { allAbilities } from "#app/data/data-lists";
|
||||||
|
import type Pokemon from "#app/field/pokemon";
|
||||||
|
import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import type { Abilities } from "#enums/abilities";
|
||||||
|
import type { PokemonType } from "#enums/pokemon-type";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
||||||
|
import { expect, type MockInstance, vi } from "vitest";
|
||||||
|
|
||||||
|
/** Helper to manage pokemon */
|
||||||
|
export class FieldHelper extends GameManagerHelper {
|
||||||
|
/**
|
||||||
|
* Passthrough for {@linkcode globalScene.getPlayerPokemon} that adds an `undefined` check for
|
||||||
|
* the Pokemon so that the return type for the function doesn't have `undefined`.
|
||||||
|
* This removes the need to add a `!` like when calling `game.scene.getPlayerPokemon()!`.
|
||||||
|
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
|
||||||
|
* @returns The first {@linkcode PlayerPokemon} that is {@linkcode globalScene.getPlayerField on the field}
|
||||||
|
* and {@linkcode PlayerPokemon.isActive is active}
|
||||||
|
* (aka {@linkcode PlayerPokemon.isAllowedInBattle is allowed in battle}).
|
||||||
|
*/
|
||||||
|
public getPlayerPokemon(includeSwitching = true): PlayerPokemon {
|
||||||
|
const pokemon = this.game.scene.getPlayerPokemon(includeSwitching);
|
||||||
|
expect(pokemon).toBeDefined();
|
||||||
|
return pokemon!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passthrough for {@linkcode globalScene.getEnemyPokemon} that adds an `undefined` check for
|
||||||
|
* the Pokemon so that the return type for the function doesn't have `undefined`.
|
||||||
|
* This removes the need to add a `!` like when calling `game.scene.getEnemyPokemon()!`.
|
||||||
|
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
|
||||||
|
* @returns The first {@linkcode EnemyPokemon} that is {@linkcode globalScene.getEnemyField on the field}
|
||||||
|
* and {@linkcode EnemyPokemon.isActive is active}
|
||||||
|
* (aka {@linkcode EnemyPokemon.isAllowedInBattle is allowed in battle}).
|
||||||
|
*/
|
||||||
|
public getEnemyPokemon(includeSwitching = true): EnemyPokemon {
|
||||||
|
const pokemon = this.game.scene.getEnemyPokemon(includeSwitching);
|
||||||
|
expect(pokemon).toBeDefined();
|
||||||
|
return pokemon!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The {@linkcode BattlerIndex | indexes} of Pokemon on the field in order of decreasing Speed.
|
||||||
|
* Speed ties are returned in increasing order of index.
|
||||||
|
*
|
||||||
|
* Note: Trick Room does not modify the speed of Pokemon on the field.
|
||||||
|
*/
|
||||||
|
public getSpeedOrder(): BattlerIndex[] {
|
||||||
|
return this.game.scene
|
||||||
|
.getField(true)
|
||||||
|
.sort((pA, pB) => pB.getEffectiveStat(Stat.SPD) - pA.getEffectiveStat(Stat.SPD))
|
||||||
|
.map(p => p.getBattlerIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks a pokemon's ability, overriding its existing ability (takes precedence over global overrides)
|
||||||
|
* @param pokemon - The pokemon to mock the ability of
|
||||||
|
* @param ability - The ability to be mocked
|
||||||
|
* @returns A {@linkcode MockInstance} object
|
||||||
|
* @see {@linkcode vi.spyOn}
|
||||||
|
* @see https://vitest.dev/api/mock#mockreturnvalue
|
||||||
|
*/
|
||||||
|
public mockAbility(pokemon: Pokemon, ability: Abilities): MockInstance<(baseOnly?: boolean) => Ability> {
|
||||||
|
return vi.spyOn(pokemon, "getAbility").mockReturnValue(allAbilities[ability]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces a pokemon to be terastallized. Defaults to the pokemon's primary type if not specified.
|
||||||
|
*
|
||||||
|
* This function only mocks the Pokemon's tera-related variables; it does NOT activate any tera-related abilities.
|
||||||
|
*
|
||||||
|
* @param pokemon - The pokemon to terastallize.
|
||||||
|
* @param teraType - (optional) The {@linkcode PokemonType} to terastallize it as.
|
||||||
|
*/
|
||||||
|
public forceTera(pokemon: Pokemon, teraType?: PokemonType): void {
|
||||||
|
vi.spyOn(pokemon, "isTerastallized", "get").mockReturnValue(true);
|
||||||
|
teraType ??= pokemon.getSpeciesForm(true).type1;
|
||||||
|
vi.spyOn(pokemon, "teraType", "get").mockReturnValue(teraType);
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,16 @@
|
|||||||
import type { BattlerIndex } from "#app/battle";
|
import type { BattlerIndex } from "#app/battle";
|
||||||
|
import { getMoveTargets } from "#app/data/moves/move";
|
||||||
import { Button } from "#app/enums/buttons";
|
import { Button } from "#app/enums/buttons";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import type { CommandPhase } from "#app/phases/command-phase";
|
import type { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { getMovePosition } from "#test/testUtils/gameManagerUtils";
|
import { getMovePosition } from "#test/testUtils/gameManagerUtils";
|
||||||
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
@ -92,6 +94,35 @@ export class MoveHelper extends GameManagerHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies a player pokemon's moveset to contain only the selected move and then
|
||||||
|
* selects it to be used during the next {@linkcode CommandPhase}.
|
||||||
|
*
|
||||||
|
* Warning: Will disable the player moveset override if it is enabled!
|
||||||
|
*
|
||||||
|
* Note: If you need to check for changes in the player's moveset as part of the test, it may be
|
||||||
|
* best to use {@linkcode changeMoveset} and {@linkcode select} instead.
|
||||||
|
* @param moveId - the move to use
|
||||||
|
* @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0)
|
||||||
|
* @param targetIndex - (optional) The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required
|
||||||
|
* @param useTera - If `true`, the Pokemon also chooses to Terastallize. This does not require a Tera Orb. Default: `false`.
|
||||||
|
*/
|
||||||
|
public use(moveId: Moves, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null, useTera = false): void {
|
||||||
|
if ([Overrides.MOVESET_OVERRIDE].flat().length > 0) {
|
||||||
|
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]);
|
||||||
|
console.warn("Warning: `use` overwrites the Pokemon's moveset and disables the player moveset override!");
|
||||||
|
}
|
||||||
|
|
||||||
|
const pokemon = this.game.scene.getPlayerField()[pkmIndex];
|
||||||
|
pokemon.moveset = [new PokemonMove(moveId)];
|
||||||
|
|
||||||
|
if (useTera) {
|
||||||
|
this.selectWithTera(moveId, pkmIndex, targetIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.select(moveId, pkmIndex, targetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces the Paralysis or Freeze status to activate on the next move by temporarily mocking {@linkcode Overrides.STATUS_ACTIVATION_OVERRIDE},
|
* Forces the Paralysis or Freeze status to activate on the next move by temporarily mocking {@linkcode Overrides.STATUS_ACTIVATION_OVERRIDE},
|
||||||
* advancing to the next `MovePhase`, and then resetting the override to `null`
|
* advancing to the next `MovePhase`, and then resetting the override to `null`
|
||||||
@ -132,6 +163,77 @@ export class MoveHelper extends GameManagerHelper {
|
|||||||
console.log(`Pokemon ${pokemon.species.name}'s moveset manually set to ${movesetStr} (=[${moveset.join(", ")}])!`);
|
console.log(`Pokemon ${pokemon.species.name}'s moveset manually set to ${movesetStr} (=[${moveset.join(", ")}])!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the next enemy selecting a move to use the given move in its moveset
|
||||||
|
* against the given target (if applicable).
|
||||||
|
* @param moveId The {@linkcode MoveId | move} the enemy will use
|
||||||
|
* @param target (Optional) the {@linkcode BattlerIndex | target} which the enemy will use the given move against
|
||||||
|
*/
|
||||||
|
public async selectEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
||||||
|
// Wait for the next EnemyCommandPhase to start
|
||||||
|
await this.game.phaseInterceptor.to("EnemyCommandPhase", false);
|
||||||
|
const enemy =
|
||||||
|
this.game.scene.getEnemyField()[(this.game.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()];
|
||||||
|
const legalTargets = getMoveTargets(enemy, moveId);
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
||||||
|
move: moveId,
|
||||||
|
targets:
|
||||||
|
target !== undefined && !legalTargets.multiple && legalTargets.targets.includes(target)
|
||||||
|
? [target]
|
||||||
|
: enemy.getNextTargets(moveId),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the EnemyCommandPhase to completion.
|
||||||
|
* This allows this function to be called consecutively to
|
||||||
|
* force a move for each enemy in a double battle.
|
||||||
|
*/
|
||||||
|
await this.game.phaseInterceptor.to("EnemyCommandPhase");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the next enemy selecting a move to use the given move against the given target (if applicable).
|
||||||
|
*
|
||||||
|
* Warning: Overwrites the pokemon's moveset and disables the moveset override!
|
||||||
|
*
|
||||||
|
* Note: If you need to check for changes in the enemy's moveset as part of the test, it may be
|
||||||
|
* best to use {@linkcode changeMoveset} and {@linkcode selectEnemyMove} instead.
|
||||||
|
* @param moveId The {@linkcode MoveId | move} the enemy will use
|
||||||
|
* @param target (Optional) the {@linkcode BattlerIndex | target} which the enemy will use the given move against
|
||||||
|
*/
|
||||||
|
public async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
||||||
|
// Wait for the next EnemyCommandPhase to start
|
||||||
|
await this.game.phaseInterceptor.to("EnemyCommandPhase", false);
|
||||||
|
|
||||||
|
const enemy =
|
||||||
|
this.game.scene.getEnemyField()[(this.game.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()];
|
||||||
|
|
||||||
|
if ([Overrides.OPP_MOVESET_OVERRIDE].flat().length > 0) {
|
||||||
|
vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([]);
|
||||||
|
console.warn(
|
||||||
|
"Warning: `forceEnemyMove` overwrites the Pokemon's moveset and disables the enemy moveset override!",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
enemy.moveset = [new PokemonMove(moveId)];
|
||||||
|
const legalTargets = getMoveTargets(enemy, moveId);
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
||||||
|
move: moveId,
|
||||||
|
targets:
|
||||||
|
target !== undefined && !legalTargets.multiple && legalTargets.targets.includes(target)
|
||||||
|
? [target]
|
||||||
|
: enemy.getNextTargets(moveId),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the EnemyCommandPhase to completion.
|
||||||
|
* This allows this function to be called consecutively to
|
||||||
|
* force a move for each enemy in a double battle.
|
||||||
|
*/
|
||||||
|
await this.game.phaseInterceptor.to("EnemyCommandPhase");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates learning a move for a player pokemon.
|
* Simulates learning a move for a player pokemon.
|
||||||
* @param move The {@linkcode Moves} being learnt
|
* @param move The {@linkcode Moves} being learnt
|
||||||
|
@ -8,7 +8,7 @@ import { Abilities } from "#enums/abilities";
|
|||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { allSpecies, getPokemonSpecies, type PokemonForm } from "#app/data/pokemon-species";
|
import { allSpecies, getPokemonSpecies, type PokemonForm } from "#app/data/pokemon-species";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { DropDownColumn } from "#app/ui/filter-bar";
|
import { DropDownColumn } from "#enums/drop-down-column";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
|
Loading…
Reference in New Issue
Block a user