Compare commits

...

16 Commits

Author SHA1 Message Date
Bertie690
6ec30e2451
Merge ab968f1ac6 into f42237d415 2025-08-14 13:28:35 -04:00
Bertie690
f42237d415
[Refactor] Removed map(x => x) (#6256)
* Enforced a few usages of `toCamelCase`

* Removed `map(x => x)`

* Removed more maps and sufff

* Update test/mystery-encounter/encounters/weird-dream-encounter.test.ts

* Update game-data.ts types to work
2025-08-14 10:25:44 -07:00
fabske0
b44f0a4176
[Refactor] Remove bgm param from arena constructor (#6254) 2025-08-14 16:52:56 +00:00
Sirz Benjie
076ef81691
[Bug] [UI/UX] [Beta] Fix icons not showing in save slot selection (#6262)
Fix icons not showing in save slot selection
2025-08-13 20:49:46 -05:00
fabske0
23271901cf
[Docs] Add locale key naming info to localization.md (#6260) 2025-08-14 01:12:00 +00:00
Inês Simões
1517e0512e
[UI/UX] [Feature] Save Management Tool (Rename/Delete Saves) (#5978)
* Implement Name Run Feat
Modified load session ui component, adding a submenu when selecting a 3
slot. This menu has 4 options:
Load Game -> Behaves as before, allowing the player to continue
progress from the last saved state in the slot.

Rename Run -> Overlays a rename form, allowing the player to type a
name for the run, checking for string validity, with the option to
cancel or confirm (Rename).

Delete Run -> Prompts user confirmation to delete save data, removing
the current save slot from the users save data.

Cancel -> Hides menu overlay.

Modified game data to implement a function to accept and store
runNameText to the users data.

Modified run info ui component, to display the chosen name when
viewing run information.

Example: When loading the game, the user can choose the Load Game
menu option, then select a save slot, prompting the menu, then choose
"Rename Run" and type the name "Monotype Water Run" then confirm,
thus being able to better organize their save files.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Implement Rename Input Design and Tests for Name Run Feat
Created a test to verify Name Run Feature behaviour in the
backend (rename_run.test.ts), checking possible errors and
 expected behaviours.

Created a UiHandler RenameRunFormUiHandler
(rename-run-ui-handler.ts), creating a frontend input
overlay for the Name Run Feature.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Fixed formating and best practices issues:
Rewrote renameSession to be more inline with other
API call funtions, removed debugging comments and
whitespaces.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Minor Sanitization for aesthetics
Deleting the input when closing the overlay for
aesthetics purpose

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Fixed minor rebase alterations.

Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Inês Simões ines.p.simoes@tecnico.ulisboa.pt

* Implemented Default Name Logic
Altered logic in save-slot-select-ui-handler.ts to
support default naming of runs based on the run
game mode with decideFallback function.

In game-data.ts, to prevent inconsistent naming,
added check for unfilled input, ignoring empty
rename requests.

Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Inês Simões ines.p.simoes@tecnico.ulisboa.pt

* Replace fallback name logic: use first active challenge instead
of game mode

Previously used game mode as the fallback name, updated to use the
first active challenge instead (e.g. Monogen or Mono Type), which
better reflects the run's theme.
Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Rebasing and conflict resolution

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Lint fix

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Minor compile fix

* Dependency resolved

* Format name respected

* Add all active challenges to default challenge session name if possible

If more than 3 challenges are active, only the first 3 are added
to the name (to prevent the text going off-screen)
and then "..." is appended to the end to indicate
there were more challenges active than the ones listed

* Allow deleting malformed sessions

---------

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
2025-08-13 20:08:12 -05:00
Bertie690
ab968f1ac6
Merge branch 'beta' into test-cleanup 2025-08-12 10:37:22 -04:00
Bertie690
64be310b9a Merge remote-tracking branch 'upstream/beta' into test-cleanup 2025-08-03 14:54:35 -04:00
Bertie690
bf855d5acf Fixed remaining error 2025-08-03 14:54:22 -04:00
Bertie690
bad73f0ce2 Fixed remaining issues; removed direct assignment to Pokemon.moveset 2025-08-01 11:15:43 -04:00
Bertie690
034c56473c Fiexd syntax errors 2025-07-30 22:22:55 -04:00
Bertie690
3db1cb7486 Deleted duplicate sturdy test case 2025-07-30 22:18:54 -04:00
Bertie690
a9a3b0760a Replaced game.scene.getXXXParty()[0] with game.field.getEnemyPokemon 2025-07-30 18:21:35 -04:00
Bertie690
c55f105d31 More array destructuring!!! 2025-07-30 18:15:23 -04:00
Bertie690
6013bc8c00 Replaced instances of consecutive game.scene.getPlayerParty with destructuring 2025-07-30 18:14:05 -04:00
Bertie690
5d4e93b009 Removed bangs from getEnemyParty and getPlayerParty 2025-07-30 18:08:14 -04:00
50 changed files with 644 additions and 309 deletions

View File

@ -90,9 +90,13 @@ If this feature requires new text, the text should be integrated into the code w
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text. - For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice. [Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response. - You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
3. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes). 3. Your locales should use the following format:
4. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`. - File names should be in `kebab-case`. Example: `trainer-names.json`
5. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment. - Key names should be in `camelCase`. Example: `aceTrainer`
- If you make use of i18next's inbuilt [context support](https://www.i18next.com/translation-function/context), you need to use `snake_case` for the context key. Example: `aceTrainer_male`
4. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
5. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
6. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates). [^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle. If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle.

View File

@ -104,6 +104,7 @@ import {
getLuckString, getLuckString,
getLuckTextTint, getLuckTextTint,
getPartyLuckValue, getPartyLuckValue,
type ModifierType,
PokemonHeldItemModifierType, PokemonHeldItemModifierType,
} from "#modifiers/modifier-type"; } from "#modifiers/modifier-type";
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
@ -1203,7 +1204,9 @@ export class BattleScene extends SceneBase {
this.updateScoreText(); this.updateScoreText();
this.scoreText.setVisible(false); this.scoreText.setVisible(false);
[this.luckLabelText, this.luckText].map(t => t.setVisible(false)); [this.luckLabelText, this.luckText].forEach(t => {
t.setVisible(false);
});
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN); this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
@ -1237,8 +1240,7 @@ export class BattleScene extends SceneBase {
Object.values(mp) Object.values(mp)
.flat() .flat()
.map(mt => mt.modifierType) .map(mt => mt.modifierType)
.filter(mt => "localize" in mt) .filter((mt): mt is ModifierType & Localizable => "localize" in mt && typeof mt.localize === "function"),
.map(lpb => lpb as unknown as Localizable),
), ),
]; ];
for (const item of localizable) { for (const item of localizable) {
@ -1513,8 +1515,8 @@ export class BattleScene extends SceneBase {
return this.currentBattle; return this.currentBattle;
} }
newArena(biome: BiomeId, playerFaints?: number): Arena { newArena(biome: BiomeId, playerFaints = 0): Arena {
this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints); this.arena = new Arena(biome, playerFaints);
this.eventTarget.dispatchEvent(new NewArenaEvent()); this.eventTarget.dispatchEvent(new NewArenaEvent());
this.arenaBg.pipelineData = { this.arenaBg.pipelineData = {
@ -2711,7 +2713,9 @@ export class BattleScene extends SceneBase {
} }
} }
this.party.map(p => p.updateInfo(instant)); this.party.forEach(p => {
p.updateInfo(instant);
});
} else { } else {
const args = [this]; const args = [this];
if (modifier.shouldApply(...args)) { if (modifier.shouldApply(...args)) {

View File

@ -74,6 +74,7 @@ import {
randSeedItem, randSeedItem,
toDmgValue, toDmgValue,
} from "#utils/common"; } from "#utils/common";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
export class Ability implements Localizable { export class Ability implements Localizable {
@ -109,13 +110,9 @@ export class Ability implements Localizable {
} }
localize(): void { localize(): void {
const i18nKey = AbilityId[this.id] const i18nKey = toCamelCase(AbilityId[this.id]);
.split("_")
.filter(f => f)
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("") as string;
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : ""; this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`)}${this.nameAppend}` : "";
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : ""; this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
} }

View File

@ -1866,17 +1866,16 @@ interface PokemonPrevolutions {
export const pokemonPrevolutions: PokemonPrevolutions = {}; export const pokemonPrevolutions: PokemonPrevolutions = {};
export function initPokemonPrevolutions(): void { export function initPokemonPrevolutions(): void {
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ].map(sfk => sfk as string); // TODO: Why do we have empty strings in our array?
const prevolutionKeys = Object.keys(pokemonEvolutions); const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ];
prevolutionKeys.forEach(pk => { for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) {
const evolutions = pokemonEvolutions[pk];
for (const ev of evolutions) { for (const ev of evolutions) {
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) { if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
continue; continue;
} }
pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId; pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId;
} }
}); }
} }

View File

@ -90,7 +90,7 @@ import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindS
import type { TurnMove } from "#types/turn-move"; import type { TurnMove } from "#types/turn-move";
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
import { getEnumValues } from "#utils/enums"; import { getEnumValues } from "#utils/enums";
import { toTitleCase } from "#utils/strings"; import { toCamelCase, toTitleCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
import { applyChallenges } from "#utils/challenge-utils"; import { applyChallenges } from "#utils/challenge-utils";
@ -162,10 +162,16 @@ export abstract class Move implements Localizable {
} }
localize(): void { localize(): void {
const i18nKey = MoveId[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string; const i18nKey = toCamelCase(MoveId[this.id])
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : ""; if (this.id === MoveId.NONE) {
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : ""; this.name = "";
this.effect = ""
return;
}
this.name = `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}`;
this.effect = `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}`;
} }
/** /**

View File

@ -12,6 +12,7 @@ import { WeatherType } from "#enums/weather-type";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier"; import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
import { type Constructor, coerceArray } from "#utils/common"; import { type Constructor, coerceArray } from "#utils/common";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
export abstract class SpeciesFormChangeTrigger { export abstract class SpeciesFormChangeTrigger {
@ -143,11 +144,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
super(); super();
this.move = move; this.move = move;
this.known = known; this.known = known;
const moveKey = MoveId[this.move] const moveKey = toCamelCase(MoveId[this.move]);
.split("_")
.filter(f => f)
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("") as unknown as string;
this.description = known this.description = known
? i18next.t("pokemonEvolutions:Forms.moveLearned", { ? i18next.t("pokemonEvolutions:Forms.moveLearned", {
move: i18next.t(`move:${moveKey}.name`), move: i18next.t(`move:${moveKey}.name`),

View File

@ -38,6 +38,7 @@ export enum UiMode {
UNAVAILABLE, UNAVAILABLE,
CHALLENGE_SELECT, CHALLENGE_SELECT,
RENAME_POKEMON, RENAME_POKEMON,
RENAME_RUN,
RUN_HISTORY, RUN_HISTORY,
RUN_INFO, RUN_INFO,
TEST_DIALOGUE, TEST_DIALOGUE,

View File

@ -54,7 +54,7 @@ export class Arena {
public bgm: string; public bgm: string;
public ignoreAbilities: boolean; public ignoreAbilities: boolean;
public ignoringEffectSource: BattlerIndex | null; public ignoringEffectSource: BattlerIndex | null;
public playerTerasUsed: number; public playerTerasUsed = 0;
/** /**
* Saves the number of times a party pokemon faints during a arena encounter. * Saves the number of times a party pokemon faints during a arena encounter.
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave). * {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
@ -68,12 +68,11 @@ export class Arena {
public readonly eventTarget: EventTarget = new EventTarget(); public readonly eventTarget: EventTarget = new EventTarget();
constructor(biome: BiomeId, bgm: string, playerFaints = 0) { constructor(biome: BiomeId, playerFaints = 0) {
this.biomeType = biome; this.biomeType = biome;
this.bgm = bgm; this.bgm = BiomeId[biome].toLowerCase();
this.trainerPool = biomeTrainerPools[biome]; this.trainerPool = biomeTrainerPools[biome];
this.updatePoolsForTimeOfDay(); this.updatePoolsForTimeOfDay();
this.playerTerasUsed = 0;
this.playerFaints = playerFaints; this.playerFaints = playerFaints;
} }

View File

@ -447,7 +447,9 @@ export class LoadingScene extends SceneBase {
); );
if (!mobile) { if (!mobile) {
loadingGraphics.map(g => g.setVisible(false)); loadingGraphics.forEach(g => {
g.setVisible(false);
});
} }
const intro = this.add.video(0, 0); const intro = this.add.video(0, 0);

View File

@ -121,8 +121,8 @@ export class ModifierBar extends Phaser.GameObjects.Container {
} }
updateModifierOverflowVisibility(ignoreLimit: boolean) { updateModifierOverflowVisibility(ignoreLimit: boolean) {
const modifierIcons = this.getAll().reverse(); const modifierIcons = this.getAll().reverse() as Phaser.GameObjects.Container[];
for (const modifier of modifierIcons.map(m => m as Phaser.GameObjects.Container).slice(iconOverflowIndex)) { for (const modifier of modifierIcons.slice(iconOverflowIndex)) {
modifier.setVisible(ignoreLimit); modifier.setVisible(ignoreLimit);
} }
} }

View File

@ -127,6 +127,7 @@ export interface SessionSaveData {
battleType: BattleType; battleType: BattleType;
trainer: TrainerData; trainer: TrainerData;
gameVersion: string; gameVersion: string;
runNameText: string;
timestamp: number; timestamp: number;
challenges: ChallengeData[]; challenges: ChallengeData[];
mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME, mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
@ -206,10 +207,12 @@ export interface StarterData {
[key: number]: StarterDataEntry; [key: number]: StarterDataEntry;
} }
export interface TutorialFlags { // TODO: Rework into a bitmask
[key: string]: boolean; export type TutorialFlags = {
} [key in Tutorial]: boolean;
};
// TODO: Rework into a bitmask
export interface SeenDialogues { export interface SeenDialogues {
[key: string]: boolean; [key: string]: boolean;
} }
@ -822,52 +825,51 @@ export class GameData {
return true; // TODO: is `true` the correct return value? return true; // TODO: is `true` the correct return value?
} }
private loadGamepadSettings(): boolean { private loadGamepadSettings(): void {
Object.values(SettingGamepad) Object.values(SettingGamepad).forEach(setting => {
.map(setting => setting as SettingGamepad) setSettingGamepad(setting, settingGamepadDefaults[setting]);
.forEach(setting => setSettingGamepad(setting, settingGamepadDefaults[setting])); });
if (!localStorage.hasOwnProperty("settingsGamepad")) { if (!localStorage.hasOwnProperty("settingsGamepad")) {
return false; return;
} }
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct? const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct?
for (const setting of Object.keys(settingsGamepad)) { for (const setting of Object.keys(settingsGamepad)) {
setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]); setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]);
} }
return true; // TODO: is `true` the correct return value?
} }
public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { /**
const key = getDataTypeKey(GameDataType.TUTORIALS); * Save the specified tutorial as having the specified completion status.
let tutorials: object = {}; * @param tutorial - The {@linkcode Tutorial} whose completion status is being saved
if (localStorage.hasOwnProperty(key)) { * @param status - The completion status to set
tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? */
} public saveTutorialFlag(tutorial: Tutorial, status: boolean): void {
// Grab the prior save data tutorial
const saveDataKey = getDataTypeKey(GameDataType.TUTORIALS);
const tutorials: TutorialFlags = localStorage.hasOwnProperty(saveDataKey)
? JSON.parse(localStorage.getItem(saveDataKey)!)
: {};
Object.keys(Tutorial) // TODO: We shouldn't be storing this like that
.map(t => t as Tutorial) for (const key of Object.values(Tutorial)) {
.forEach(t => {
const key = Tutorial[t];
if (key === tutorial) { if (key === tutorial) {
tutorials[key] = flag; tutorials[key] = status;
} else { } else {
tutorials[key] ??= false; tutorials[key] ??= false;
} }
}); }
localStorage.setItem(key, JSON.stringify(tutorials)); localStorage.setItem(saveDataKey, JSON.stringify(tutorials));
return true;
} }
public getTutorialFlags(): TutorialFlags { public getTutorialFlags(): TutorialFlags {
const key = getDataTypeKey(GameDataType.TUTORIALS); const key = getDataTypeKey(GameDataType.TUTORIALS);
const ret: TutorialFlags = {}; const ret: TutorialFlags = Object.values(Tutorial).reduce((acc, tutorial) => {
Object.values(Tutorial) acc[Tutorial[tutorial]] = false;
.map(tutorial => tutorial as Tutorial) return acc;
.forEach(tutorial => (ret[Tutorial[tutorial]] = false)); }, {} as TutorialFlags);
if (!localStorage.hasOwnProperty(key)) { if (!localStorage.hasOwnProperty(key)) {
return ret; return ret;
@ -979,6 +981,54 @@ export class GameData {
}); });
} }
async renameSession(slotId: number, newName: string): Promise<boolean> {
return new Promise(async resolve => {
if (slotId < 0) {
return resolve(false);
}
const sessionData: SessionSaveData | null = await this.getSession(slotId);
if (!sessionData) {
return resolve(false);
}
if (newName === "") {
return resolve(true);
}
sessionData.runNameText = newName;
const updatedDataStr = JSON.stringify(sessionData);
const encrypted = encrypt(updatedDataStr, bypassLogin);
const secretId = this.secretId;
const trainerId = this.trainerId;
if (bypassLogin) {
localStorage.setItem(
`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
encrypt(updatedDataStr, bypassLogin),
);
resolve(true);
return;
}
pokerogueApi.savedata.session
.update({ slot: slotId, trainerId, secretId, clientSessionId }, encrypted)
.then(error => {
if (error) {
console.error("Failed to update session name:", error);
resolve(false);
} else {
localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypted);
updateUserInfo().then(success => {
if (success !== null && !success) {
return resolve(false);
}
});
resolve(true);
}
});
});
}
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> { loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this // biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {

View File

@ -0,0 +1,54 @@
import i18next from "i18next";
import type { InputFieldConfig } from "./form-modal-ui-handler";
import { FormModalUiHandler } from "./form-modal-ui-handler";
import type { ModalConfig } from "./modal-ui-handler";
export class RenameRunFormUiHandler extends FormModalUiHandler {
getModalTitle(_config?: ModalConfig): string {
return i18next.t("menu:renamerun");
}
getWidth(_config?: ModalConfig): number {
return 160;
}
getMargin(_config?: ModalConfig): [number, number, number, number] {
return [0, 0, 48, 0];
}
getButtonLabels(_config?: ModalConfig): string[] {
return [i18next.t("menu:rename"), i18next.t("menu:cancel")];
}
getReadableErrorMessage(error: string): string {
const colonIndex = error?.indexOf(":");
if (colonIndex > 0) {
error = error.slice(0, colonIndex);
}
return super.getReadableErrorMessage(error);
}
override getInputFieldConfigs(): InputFieldConfig[] {
return [{ label: i18next.t("menu:runName") }];
}
show(args: any[]): boolean {
if (!super.show(args)) {
return false;
}
if (this.inputs?.length) {
this.inputs.forEach(input => {
input.text = "";
});
}
const config = args[0] as ModalConfig;
this.submitAction = _ => {
this.sanitizeInputs();
const sanitizedName = btoa(encodeURIComponent(this.inputs[0].text));
config.buttonActions[0](sanitizedName);
return true;
};
return true;
}
}

View File

@ -26,6 +26,7 @@ import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { UiHandler } from "#ui/ui-handler"; import { UiHandler } from "#ui/ui-handler";
import { addWindow } from "#ui/ui-theme"; import { addWindow } from "#ui/ui-theme";
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common"; import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next"; import i18next from "i18next";
import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle";
@ -207,6 +208,10 @@ export class RunInfoUiHandler extends UiHandler {
headerText.setOrigin(0, 0); headerText.setOrigin(0, 0);
headerText.setPositionRelative(headerBg, 8, 4); headerText.setPositionRelative(headerBg, 8, 4);
this.runContainer.add(headerText); this.runContainer.add(headerText);
const runName = addTextObject(0, 0, this.runInfo.runNameText, TextStyle.WINDOW);
runName.setOrigin(0, 0);
runName.setPositionRelative(headerBg, 60, 4);
this.runContainer.add(runName);
} }
/** /**
@ -702,10 +707,7 @@ export class RunInfoUiHandler extends UiHandler {
rules.push(i18next.t("challenges:inverseBattle.shortName")); rules.push(i18next.t("challenges:inverseBattle.shortName"));
break; break;
default: { default: {
const localizationKey = Challenges[this.runInfo.challenges[i].id] const localizationKey = toCamelCase(Challenges[this.runInfo.challenges[i].id]);
.split("_")
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
.join("");
rules.push(i18next.t(`challenges:${localizationKey}.name`)); rules.push(i18next.t(`challenges:${localizationKey}.name`));
break; break;
} }

View File

@ -1,12 +1,14 @@
import { GameMode } from "#app/game-mode"; import { GameMode } from "#app/game-mode";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { GameModes } from "#enums/game-modes";
import { TextStyle } from "#enums/text-style"; import { TextStyle } from "#enums/text-style";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts` // biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
import * as Modifier from "#modifiers/modifier"; import * as Modifier from "#modifiers/modifier";
import type { SessionSaveData } from "#system/game-data"; import type { SessionSaveData } from "#system/game-data";
import type { PokemonData } from "#system/pokemon-data"; import type { PokemonData } from "#system/pokemon-data";
import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
import { MessageUiHandler } from "#ui/message-ui-handler"; import { MessageUiHandler } from "#ui/message-ui-handler";
import { RunDisplayMode } from "#ui/run-info-ui-handler"; import { RunDisplayMode } from "#ui/run-info-ui-handler";
import { addTextObject } from "#ui/text"; import { addTextObject } from "#ui/text";
@ -15,7 +17,7 @@ import { fixedInt, formatLargeNumber, getPlayTimeString, isNullOrUndefined } fro
import i18next from "i18next"; import i18next from "i18next";
const SESSION_SLOTS_COUNT = 5; const SESSION_SLOTS_COUNT = 5;
const SLOTS_ON_SCREEN = 3; const SLOTS_ON_SCREEN = 2;
export enum SaveSlotUiMode { export enum SaveSlotUiMode {
LOAD, LOAD,
@ -33,6 +35,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
private uiMode: SaveSlotUiMode; private uiMode: SaveSlotUiMode;
private saveSlotSelectCallback: SaveSlotSelectCallback | null; private saveSlotSelectCallback: SaveSlotSelectCallback | null;
protected manageDataConfig: OptionSelectConfig;
private scrollCursor = 0; private scrollCursor = 0;
@ -101,6 +104,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
processInput(button: Button): boolean { processInput(button: Button): boolean {
const ui = this.getUi(); const ui = this.getUi();
const manageDataOptions: any[] = [];
let success = false; let success = false;
let error = false; let error = false;
@ -109,14 +113,115 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
const originalCallback = this.saveSlotSelectCallback; const originalCallback = this.saveSlotSelectCallback;
if (button === Button.ACTION) { if (button === Button.ACTION) {
const cursor = this.cursor + this.scrollCursor; const cursor = this.cursor + this.scrollCursor;
if (this.uiMode === SaveSlotUiMode.LOAD && !this.sessionSlots[cursor].hasData) { const sessionSlot = this.sessionSlots[cursor];
if (this.uiMode === SaveSlotUiMode.LOAD && !sessionSlot.hasData) {
error = true; error = true;
} else { } else {
switch (this.uiMode) { switch (this.uiMode) {
case SaveSlotUiMode.LOAD: case SaveSlotUiMode.LOAD:
this.saveSlotSelectCallback = null; if (!sessionSlot.malformed) {
manageDataOptions.push({
label: i18next.t("menu:loadGame"),
handler: () => {
globalScene.ui.revertMode();
originalCallback?.(cursor); originalCallback?.(cursor);
return true;
},
keepOpen: false,
});
manageDataOptions.push({
label: i18next.t("saveSlotSelectUiHandler:renameRun"),
handler: () => {
globalScene.ui.revertMode();
ui.setOverlayMode(
UiMode.RENAME_RUN,
{
buttonActions: [
(sanitizedName: string) => {
const name = decodeURIComponent(atob(sanitizedName));
globalScene.gameData.renameSession(cursor, name).then(response => {
if (response[0] === false) {
globalScene.reset(true);
} else {
this.clearSessionSlots();
this.cursorObj = null;
this.populateSessionSlots();
this.setScrollCursor(0);
this.setCursor(0);
ui.revertMode();
ui.showText("", 0);
}
});
},
() => {
ui.revertMode();
},
],
},
"",
);
return true;
},
});
}
this.manageDataConfig = {
xOffset: 0,
yOffset: 48,
options: manageDataOptions,
maxOptions: 4,
};
manageDataOptions.push({
label: i18next.t("saveSlotSelectUiHandler:deleteRun"),
handler: () => {
globalScene.ui.revertMode();
ui.showText(i18next.t("saveSlotSelectUiHandler:deleteData"), null, () => {
ui.setOverlayMode(
UiMode.CONFIRM,
() => {
globalScene.gameData.tryClearSession(cursor).then(response => {
if (response[0] === false) {
globalScene.reset(true);
} else {
this.clearSessionSlots();
this.cursorObj = null;
this.populateSessionSlots();
this.setScrollCursor(0);
this.setCursor(0);
ui.revertMode();
ui.showText("", 0);
}
});
},
() => {
ui.revertMode();
ui.showText("", 0);
},
false,
0,
19,
import.meta.env.DEV ? 300 : 2000,
);
});
return true;
},
keepOpen: false,
});
manageDataOptions.push({
label: i18next.t("menuUiHandler:cancel"),
handler: () => {
globalScene.ui.revertMode();
return true;
},
keepOpen: true,
});
ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, this.manageDataConfig);
break; break;
case SaveSlotUiMode.SAVE: { case SaveSlotUiMode.SAVE: {
const saveAndCallback = () => { const saveAndCallback = () => {
const originalCallback = this.saveSlotSelectCallback; const originalCallback = this.saveSlotSelectCallback;
@ -161,6 +266,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
} }
} else { } else {
this.saveSlotSelectCallback = null; this.saveSlotSelectCallback = null;
ui.showText("", 0);
originalCallback?.(-1); originalCallback?.(-1);
success = true; success = true;
} }
@ -267,33 +373,34 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
this.cursorObj = globalScene.add.container(0, 0); this.cursorObj = globalScene.add.container(0, 0);
const cursorBox = globalScene.add.nineslice( const cursorBox = globalScene.add.nineslice(
0, 0,
0, 15,
"select_cursor_highlight_thick", "select_cursor_highlight_thick",
undefined, undefined,
296, 294,
44, this.sessionSlots[prevSlotIndex ?? 0]?.saveData?.runNameText ? 50 : 60,
6, 6,
6, 6,
6, 6,
6, 6,
); );
const rightArrow = globalScene.add.image(0, 0, "cursor"); const rightArrow = globalScene.add.image(0, 0, "cursor");
rightArrow.setPosition(160, 0); rightArrow.setPosition(160, 15);
rightArrow.setName("rightArrow"); rightArrow.setName("rightArrow");
this.cursorObj.add([cursorBox, rightArrow]); this.cursorObj.add([cursorBox, rightArrow]);
this.sessionSlotsContainer.add(this.cursorObj); this.sessionSlotsContainer.add(this.cursorObj);
} }
const cursorPosition = cursor + this.scrollCursor; const cursorPosition = cursor + this.scrollCursor;
const cursorIncrement = cursorPosition * 56; const cursorIncrement = cursorPosition * 76;
if (this.sessionSlots[cursorPosition] && this.cursorObj) { if (this.sessionSlots[cursorPosition] && this.cursorObj) {
const hasData = this.sessionSlots[cursorPosition].hasData; const session = this.sessionSlots[cursorPosition];
const hasData = session.hasData && !session.malformed;
// If the session slot lacks session data, it does not move from its default, central position. // If the session slot lacks session data, it does not move from its default, central position.
// Only session slots with session data will move leftwards and have a visible arrow. // Only session slots with session data will move leftwards and have a visible arrow.
if (!hasData) { if (!hasData) {
this.cursorObj.setPosition(151, 26 + cursorIncrement); this.cursorObj.setPosition(151, 20 + cursorIncrement);
this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement); this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement);
} else { } else {
this.cursorObj.setPosition(145, 26 + cursorIncrement); this.cursorObj.setPosition(145, 20 + cursorIncrement);
this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement); this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement);
} }
this.setArrowVisibility(hasData); this.setArrowVisibility(hasData);
@ -311,7 +418,8 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
revertSessionSlot(slotIndex: number): void { revertSessionSlot(slotIndex: number): void {
const sessionSlot = this.sessionSlots[slotIndex]; const sessionSlot = this.sessionSlots[slotIndex];
if (sessionSlot) { if (sessionSlot) {
sessionSlot.setPosition(0, slotIndex * 56); const valueHeight = 76;
sessionSlot.setPosition(0, slotIndex * valueHeight);
} }
} }
@ -340,7 +448,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
this.setCursor(this.cursor, prevSlotIndex); this.setCursor(this.cursor, prevSlotIndex);
globalScene.tweens.add({ globalScene.tweens.add({
targets: this.sessionSlotsContainer, targets: this.sessionSlotsContainer,
y: this.sessionSlotsContainerInitialY - 56 * scrollCursor, y: this.sessionSlotsContainerInitialY - 76 * scrollCursor,
duration: fixedInt(325), duration: fixedInt(325),
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
}); });
@ -374,12 +482,14 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
class SessionSlot extends Phaser.GameObjects.Container { class SessionSlot extends Phaser.GameObjects.Container {
public slotId: number; public slotId: number;
public hasData: boolean; public hasData: boolean;
/** Indicates the save slot ran into an error while being loaded */
public malformed: boolean;
private slotWindow: Phaser.GameObjects.NineSlice;
private loadingLabel: Phaser.GameObjects.Text; private loadingLabel: Phaser.GameObjects.Text;
public saveData: SessionSaveData; public saveData: SessionSaveData;
constructor(slotId: number) { constructor(slotId: number) {
super(globalScene, 0, slotId * 56); super(globalScene, 0, slotId * 76);
this.slotId = slotId; this.slotId = slotId;
@ -387,32 +497,89 @@ class SessionSlot extends Phaser.GameObjects.Container {
} }
setup() { setup() {
const slotWindow = addWindow(0, 0, 304, 52); this.slotWindow = addWindow(0, 0, 304, 70);
this.add(slotWindow); this.add(this.slotWindow);
this.loadingLabel = addTextObject(152, 26, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW); this.loadingLabel = addTextObject(152, 33, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
this.loadingLabel.setOrigin(0.5, 0.5); this.loadingLabel.setOrigin(0.5, 0.5);
this.add(this.loadingLabel); this.add(this.loadingLabel);
} }
/**
* Generates a name for sessions that don't have a name yet.
* @param data - The {@linkcode SessionSaveData} being checked
* @returns The default name for the given data.
*/
decideFallback(data: SessionSaveData): string {
let fallbackName = `${GameMode.getModeName(data.gameMode)}`;
switch (data.gameMode) {
case GameModes.CLASSIC:
fallbackName += ` (${globalScene.gameData.gameStats.classicSessionsPlayed + 1})`;
break;
case GameModes.ENDLESS:
case GameModes.SPLICED_ENDLESS:
fallbackName += ` (${globalScene.gameData.gameStats.endlessSessionsPlayed + 1})`;
break;
case GameModes.DAILY: {
const runDay = new Date(data.timestamp).toLocaleDateString();
fallbackName += ` (${runDay})`;
break;
}
case GameModes.CHALLENGE: {
const activeChallenges = data.challenges.filter(c => c.value !== 0);
if (activeChallenges.length === 0) {
break;
}
fallbackName = "";
for (const challenge of activeChallenges.slice(0, 3)) {
if (fallbackName !== "") {
fallbackName += ", ";
}
fallbackName += challenge.toChallenge().getName();
}
if (activeChallenges.length > 3) {
fallbackName += ", ...";
} else if (fallbackName === "") {
// Something went wrong when retrieving the names of the active challenges,
// so fall back to just naming the run "Challenge"
fallbackName = `${GameMode.getModeName(data.gameMode)}`;
}
break;
}
}
return fallbackName;
}
async setupWithData(data: SessionSaveData) { async setupWithData(data: SessionSaveData) {
const hasName = data?.runNameText;
this.remove(this.loadingLabel, true); this.remove(this.loadingLabel, true);
if (hasName) {
const nameLabel = addTextObject(8, 5, data.runNameText, TextStyle.WINDOW);
this.add(nameLabel);
} else {
const fallbackName = this.decideFallback(data);
await globalScene.gameData.renameSession(this.slotId, fallbackName);
const nameLabel = addTextObject(8, 5, fallbackName, TextStyle.WINDOW);
this.add(nameLabel);
}
const gameModeLabel = addTextObject( const gameModeLabel = addTextObject(
8, 8,
5, 19,
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`, `${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
TextStyle.WINDOW, TextStyle.WINDOW,
); );
this.add(gameModeLabel); this.add(gameModeLabel);
const timestampLabel = addTextObject(8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW); const timestampLabel = addTextObject(8, 33, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
this.add(timestampLabel); this.add(timestampLabel);
const playTimeLabel = addTextObject(8, 33, getPlayTimeString(data.playTime), TextStyle.WINDOW); const playTimeLabel = addTextObject(8, 47, getPlayTimeString(data.playTime), TextStyle.WINDOW);
this.add(playTimeLabel); this.add(playTimeLabel);
const pokemonIconsContainer = globalScene.add.container(144, 4); const pokemonIconsContainer = globalScene.add.container(144, 16);
data.party.forEach((p: PokemonData, i: number) => { data.party.forEach((p: PokemonData, i: number) => {
const iconContainer = globalScene.add.container(26 * i, 0); const iconContainer = globalScene.add.container(26 * i, 0);
iconContainer.setScale(0.75); iconContainer.setScale(0.75);
@ -427,13 +594,9 @@ class SessionSlot extends Phaser.GameObjects.Container {
TextStyle.PARTY, TextStyle.PARTY,
{ fontSize: "54px", color: "#f8f8f8" }, { fontSize: "54px", color: "#f8f8f8" },
); );
text.setShadow(0, 0, undefined); text.setShadow(0, 0, undefined).setStroke("#424242", 14).setOrigin(1, 0);
text.setStroke("#424242", 14);
text.setOrigin(1, 0);
iconContainer.add(icon);
iconContainer.add(text);
iconContainer.add([icon, text]);
pokemonIconsContainer.add(iconContainer); pokemonIconsContainer.add(iconContainer);
pokemon.destroy(); pokemon.destroy();
@ -441,7 +604,7 @@ class SessionSlot extends Phaser.GameObjects.Container {
this.add(pokemonIconsContainer); this.add(pokemonIconsContainer);
const modifierIconsContainer = globalScene.add.container(148, 30); const modifierIconsContainer = globalScene.add.container(148, 38);
modifierIconsContainer.setScale(0.5); modifierIconsContainer.setScale(0.5);
let visibleModifierIndex = 0; let visibleModifierIndex = 0;
for (const m of data.modifiers) { for (const m of data.modifiers) {
@ -464,20 +627,31 @@ class SessionSlot extends Phaser.GameObjects.Container {
load(): Promise<boolean> { load(): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
globalScene.gameData.getSession(this.slotId).then(async sessionData => { globalScene.gameData
.getSession(this.slotId)
.then(async sessionData => {
// Ignore the results if the view was exited // Ignore the results if the view was exited
if (!this.active) { if (!this.active) {
return; return;
} }
this.hasData = !!sessionData;
if (!sessionData) { if (!sessionData) {
this.hasData = false;
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty")); this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
resolve(false); resolve(false);
return; return;
} }
this.hasData = true;
this.saveData = sessionData; this.saveData = sessionData;
await this.setupWithData(sessionData); this.setupWithData(sessionData);
resolve(true);
})
.catch(e => {
if (!this.active) {
return;
}
console.warn(`Failed to load session slot #${this.slotId}:`, e);
this.loadingLabel.setText(i18next.t("menu:failedToLoadSession"));
this.hasData = true;
this.malformed = true;
resolve(true); resolve(true);
}); });
}); });

View File

@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
// Return in the format expected by i18next // Return in the format expected by i18next
return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`; return middleKey ? `${topKey}:${middleKey.join(".")}.${t}` : `${topKey}:${t}`;
} }
}) })
.filter(t => t); .filter(t => t);

View File

@ -60,6 +60,7 @@ import { addWindow } from "#ui/ui-theme";
import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler"; import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
import { executeIf } from "#utils/common"; import { executeIf } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";
import { RenameRunFormUiHandler } from "./rename-run-ui-handler";
const transitionModes = [ const transitionModes = [
UiMode.SAVE_SLOT, UiMode.SAVE_SLOT,
@ -98,6 +99,7 @@ const noTransitionModes = [
UiMode.SESSION_RELOAD, UiMode.SESSION_RELOAD,
UiMode.UNAVAILABLE, UiMode.UNAVAILABLE,
UiMode.RENAME_POKEMON, UiMode.RENAME_POKEMON,
UiMode.RENAME_RUN,
UiMode.TEST_DIALOGUE, UiMode.TEST_DIALOGUE,
UiMode.AUTO_COMPLETE, UiMode.AUTO_COMPLETE,
UiMode.ADMIN, UiMode.ADMIN,
@ -168,6 +170,7 @@ export class UI extends Phaser.GameObjects.Container {
new UnavailableModalUiHandler(), new UnavailableModalUiHandler(),
new GameChallengesUiHandler(), new GameChallengesUiHandler(),
new RenameFormUiHandler(), new RenameFormUiHandler(),
new RenameRunFormUiHandler(),
new RunHistoryUiHandler(), new RunHistoryUiHandler(),
new RunInfoUiHandler(), new RunInfoUiHandler(),
new TestDialogueUiHandler(UiMode.TEST_DIALOGUE), new TestDialogueUiHandler(UiMode.TEST_DIALOGUE),

View File

@ -134,7 +134,7 @@ describe("Abilities - Disguise", () => {
}); });
await game.classicMode.startBattle([SpeciesId.FURRET, SpeciesId.MIMIKYU]); await game.classicMode.startBattle([SpeciesId.FURRET, SpeciesId.MIMIKYU]);
const mimikyu = game.scene.getPlayerParty()[1]!; const mimikyu = game.scene.getPlayerParty()[1];
expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.formIndex).toBe(bustedForm);
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);

View File

@ -121,8 +121,8 @@ describe("Abilities - Sheer Force", () => {
await game.classicMode.startBattle([SpeciesId.PIDGEOT]); await game.classicMode.startBattle([SpeciesId.PIDGEOT]);
const pidgeot = game.scene.getPlayerParty()[0]; const pidgeot = game.field.getPlayerPokemon();
const onix = game.scene.getEnemyParty()[0]; const onix = game.field.getEnemyPokemon();
pidgeot.stats[Stat.DEF] = 10000; pidgeot.stats[Stat.DEF] = 10000;
onix.stats[Stat.DEF] = 10000; onix.stats[Stat.DEF] = 10000;

View File

@ -1,7 +1,6 @@
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon } from "#field/pokemon";
import { DamageAnimPhase } from "#phases/damage-anim-phase"; import { DamageAnimPhase } from "#phases/damage-anim-phase";
import { MoveEndPhase } from "#phases/move-end-phase"; import { MoveEndPhase } from "#phases/move-end-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
@ -38,13 +37,13 @@ describe("Abilities - Sturdy", () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
game.move.select(MoveId.CLOSE_COMBAT); game.move.select(MoveId.CLOSE_COMBAT);
await game.phaseInterceptor.to(MoveEndPhase); await game.phaseInterceptor.to(MoveEndPhase);
expect(game.scene.getEnemyParty()[0].hp).toBe(1); expect(game.field.getEnemyPokemon().hp).toBe(1);
}); });
test("Sturdy doesn't activate when user is not at full HP", async () => { test("Sturdy doesn't activate when user is not at full HP", async () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; const enemyPokemon = game.field.getEnemyPokemon();
enemyPokemon.hp = enemyPokemon.getMaxHp() - 1; enemyPokemon.hp = enemyPokemon.getMaxHp() - 1;
game.move.select(MoveId.CLOSE_COMBAT); game.move.select(MoveId.CLOSE_COMBAT);
@ -59,19 +58,7 @@ describe("Abilities - Sturdy", () => {
game.move.select(MoveId.FISSURE); game.move.select(MoveId.FISSURE);
await game.phaseInterceptor.to(MoveEndPhase); await game.phaseInterceptor.to(MoveEndPhase);
const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0]; const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.isFullHp()).toBe(true); expect(enemyPokemon.isFullHp()).toBe(true);
}); });
test("Sturdy is ignored by pokemon with `AbilityId.MOLD_BREAKER`", async () => {
game.override.ability(AbilityId.MOLD_BREAKER);
await game.classicMode.startBattle();
game.move.select(MoveId.CLOSE_COMBAT);
await game.phaseInterceptor.to(DamageAnimPhase);
const enemyPokemon: EnemyPokemon = game.scene.getEnemyParty()[0];
expect(enemyPokemon.hp).toBe(0);
expect(enemyPokemon.isFainted()).toBe(true);
});
}); });

View File

@ -336,7 +336,7 @@ describe("Abilities - Wimp Out", () => {
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerParty()[0].getHpRatio()).toEqual(0.51); expect(game.field.getPlayerPokemon().getHpRatio()).toEqual(0.51);
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.WIMPOD); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.WIMPOD);
}); });
@ -344,8 +344,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out activating should not cancel a double battle", async () => { it("Wimp Out activating should not cancel a double battle", async () => {
game.override.battleStyle("double").enemyAbility(AbilityId.WIMP_OUT).enemyMoveset([MoveId.SPLASH]).enemyLevel(1); game.override.battleStyle("double").enemyAbility(AbilityId.WIMP_OUT).enemyMoveset([MoveId.SPLASH]).enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const enemyLeadPokemon = game.scene.getEnemyParty()[0]; const [enemyLeadPokemon, enemySecPokemon] = game.scene.getEnemyParty();
const enemySecPokemon = game.scene.getEnemyParty()[1];
game.move.select(MoveId.FALSE_SWIPE, 0, BattlerIndex.ENEMY); game.move.select(MoveId.FALSE_SWIPE, 0, BattlerIndex.ENEMY);
game.move.select(MoveId.SPLASH, 1); game.move.select(MoveId.SPLASH, 1);

View File

@ -40,8 +40,7 @@ describe("Abilities - ZERO TO HERO", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.PALAFIN, SpeciesId.PALAFIN]); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.PALAFIN, SpeciesId.PALAFIN]);
const palafin1 = game.scene.getPlayerParty()[1]; const [, palafin1, palafin2] = game.scene.getPlayerParty();
const palafin2 = game.scene.getPlayerParty()[2];
expect(palafin1.formIndex).toBe(heroForm); expect(palafin1.formIndex).toBe(heroForm);
expect(palafin2.formIndex).toBe(heroForm); expect(palafin2.formIndex).toBe(heroForm);
palafin2.hp = 0; palafin2.hp = 0;

View File

@ -64,11 +64,9 @@ describe("Boss Pokemon / Shields", () => {
it("should reduce the number of shields if we are in a double battle", async () => { it("should reduce the number of shields if we are in a double battle", async () => {
game.override.battleStyle("double").startingWave(150); // Floor 150 > 2 shields / 3 health segments game.override.battleStyle("double").startingWave(150); // Floor 150 > 2 shields / 3 health segments
await game.classicMode.startBattle([SpeciesId.MEWTWO]); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; const [boss1, boss2] = game.scene.getEnemyParty();
const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!;
expect(boss1.isBoss()).toBe(true); expect(boss1.isBoss()).toBe(true);
expect(boss1.bossSegments).toBe(2); expect(boss1.bossSegments).toBe(2);
expect(boss2.isBoss()).toBe(true); expect(boss2.isBoss()).toBe(true);
@ -112,7 +110,7 @@ describe("Boss Pokemon / Shields", () => {
// In this test we want to break through 3 shields at once // In this test we want to break through 3 shields at once
const brokenShields = 3; const brokenShields = 3;
const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; const boss1 = game.field.getEnemyPokemon();
const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments; const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments;
const requiredDamageBoss1 = boss1SegmentHp * (1 + Math.pow(2, brokenShields)); const requiredDamageBoss1 = boss1SegmentHp * (1 + Math.pow(2, brokenShields));
expect(boss1.isBoss()).toBe(true); expect(boss1.isBoss()).toBe(true);
@ -124,7 +122,7 @@ describe("Boss Pokemon / Shields", () => {
expect(boss1.bossSegmentIndex).toBe(1); expect(boss1.bossSegmentIndex).toBe(1);
expect(boss1.hp).toBe(boss1.getMaxHp() - toDmgValue(boss1SegmentHp * 3)); expect(boss1.hp).toBe(boss1.getMaxHp() - toDmgValue(boss1SegmentHp * 3));
const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!; const boss2 = game.scene.getEnemyParty()[1];
const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments; const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments;
const requiredDamageBoss2 = boss2SegmentHp * (1 + Math.pow(2, brokenShields)); const requiredDamageBoss2 = boss2SegmentHp * (1 + Math.pow(2, brokenShields));
@ -144,7 +142,7 @@ describe("Boss Pokemon / Shields", () => {
await game.classicMode.startBattle([SpeciesId.MEWTWO]); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
const boss1: EnemyPokemon = game.scene.getEnemyParty()[0]!; const boss1 = game.field.getEnemyPokemon();
const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments; const boss1SegmentHp = boss1.getMaxHp() / boss1.bossSegments;
const singleShieldDamage = Math.ceil(boss1SegmentHp); const singleShieldDamage = Math.ceil(boss1SegmentHp);
expect(boss1.isBoss()).toBe(true); expect(boss1.isBoss()).toBe(true);
@ -167,7 +165,7 @@ describe("Boss Pokemon / Shields", () => {
expect(getTotalStatStageBoosts(boss1)).toBe(totalStatStages); expect(getTotalStatStageBoosts(boss1)).toBe(totalStatStages);
} }
const boss2: EnemyPokemon = game.scene.getEnemyParty()[1]!; const boss2 = game.scene.getEnemyParty()[1];
const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments; const boss2SegmentHp = boss2.getMaxHp() / boss2.bossSegments;
const requiredDamage = boss2SegmentHp * (1 + Math.pow(2, shieldsToBreak - 1)); const requiredDamage = boss2SegmentHp * (1 + Math.pow(2, shieldsToBreak - 1));

View File

@ -34,8 +34,7 @@ describe("Evolution", () => {
it("should keep hidden ability after evolving", async () => { it("should keep hidden ability after evolving", async () => {
await game.classicMode.runToSummon([SpeciesId.EEVEE, SpeciesId.TRAPINCH]); await game.classicMode.runToSummon([SpeciesId.EEVEE, SpeciesId.TRAPINCH]);
const eevee = game.scene.getPlayerParty()[0]; const [eevee, trapinch] = game.scene.getPlayerParty();
const trapinch = game.scene.getPlayerParty()[1];
eevee.abilityIndex = 2; eevee.abilityIndex = 2;
trapinch.abilityIndex = 2; trapinch.abilityIndex = 2;
@ -49,8 +48,7 @@ describe("Evolution", () => {
it("should keep same ability slot after evolving", async () => { it("should keep same ability slot after evolving", async () => {
await game.classicMode.runToSummon([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]); await game.classicMode.runToSummon([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER]);
const bulbasaur = game.scene.getPlayerParty()[0]; const [bulbasaur, charmander] = game.scene.getPlayerParty();
const charmander = game.scene.getPlayerParty()[1];
bulbasaur.abilityIndex = 0; bulbasaur.abilityIndex = 0;
charmander.abilityIndex = 1; charmander.abilityIndex = 1;
@ -80,8 +78,7 @@ describe("Evolution", () => {
nincada.gender = 1; nincada.gender = 1;
await nincada.evolve(pokemonEvolutions[SpeciesId.NINCADA][0], nincada.getSpeciesForm()); await nincada.evolve(pokemonEvolutions[SpeciesId.NINCADA][0], nincada.getSpeciesForm());
const ninjask = game.scene.getPlayerParty()[0]; const [ninjask, shedinja] = game.scene.getPlayerParty();
const shedinja = game.scene.getPlayerParty()[1];
expect(ninjask.abilityIndex).toBe(2); expect(ninjask.abilityIndex).toBe(2);
expect(shedinja.abilityIndex).toBe(1); expect(shedinja.abilityIndex).toBe(1);
expect(ninjask.gender).toBe(1); expect(ninjask.gender).toBe(1);

View File

@ -85,17 +85,14 @@ describe("Spec - Pokemon", () => {
}); });
describe("Get correct fusion type", () => { describe("Get correct fusion type", () => {
let scene: BattleScene;
beforeEach(async () => { beforeEach(async () => {
game.override.enemySpecies(SpeciesId.ZUBAT).starterSpecies(SpeciesId.ABRA).enableStarterFusion(); game.override.enemySpecies(SpeciesId.ZUBAT).starterSpecies(SpeciesId.ABRA).enableStarterFusion();
scene = game.scene;
}); });
it("Fusing two mons with a single type", async () => { it("Fusing two mons with a single type", async () => {
game.override.starterFusionSpecies(SpeciesId.CHARMANDER); game.override.starterFusionSpecies(SpeciesId.CHARMANDER);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = game.field.getPlayerPokemon();
let types = pokemon.getTypes(); let types = pokemon.getTypes();
expect(types[0]).toBe(PokemonType.PSYCHIC); expect(types[0]).toBe(PokemonType.PSYCHIC);
@ -136,7 +133,7 @@ describe("Spec - Pokemon", () => {
it("Fusing two mons with same single type", async () => { it("Fusing two mons with same single type", async () => {
game.override.starterFusionSpecies(SpeciesId.DROWZEE); game.override.starterFusionSpecies(SpeciesId.DROWZEE);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = game.field.getPlayerPokemon();
const types = pokemon.getTypes(); const types = pokemon.getTypes();
expect(types[0]).toBe(PokemonType.PSYCHIC); expect(types[0]).toBe(PokemonType.PSYCHIC);
@ -146,7 +143,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).starterFusionSpecies(SpeciesId.HOUNDOUR); game.override.starterSpecies(SpeciesId.CHARMANDER).starterFusionSpecies(SpeciesId.HOUNDOUR);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = game.field.getPlayerPokemon();
const types = pokemon.getTypes(); const types = pokemon.getTypes();
expect(types[0]).toBe(PokemonType.FIRE); expect(types[0]).toBe(PokemonType.FIRE);
@ -156,7 +153,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).starterFusionSpecies(SpeciesId.CHARMANDER); game.override.starterSpecies(SpeciesId.NUMEL).starterFusionSpecies(SpeciesId.CHARMANDER);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = game.field.getPlayerPokemon();
const types = pokemon.getTypes(); const types = pokemon.getTypes();
expect(types[0]).toBe(PokemonType.FIRE); expect(types[0]).toBe(PokemonType.FIRE);
@ -166,7 +163,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).starterFusionSpecies(SpeciesId.HOUNDOUR); game.override.starterSpecies(SpeciesId.NATU).starterFusionSpecies(SpeciesId.HOUNDOUR);
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const pokemon = scene.getPlayerParty()[0]; const pokemon = game.field.getPlayerPokemon();
let types = pokemon.getTypes(); let types = pokemon.getTypes();
expect(types[0]).toBe(PokemonType.PSYCHIC); expect(types[0]).toBe(PokemonType.PSYCHIC);

View File

@ -33,7 +33,7 @@ describe("Items - Light Ball", () => {
const consoleSpy = vi.spyOn(console, "log"); const consoleSpy = vi.spyOn(console, "log");
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
// Checking console log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called // Checking console log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called
partyMember.getEffectiveStat(Stat.DEF); partyMember.getEffectiveStat(Stat.DEF);
@ -84,7 +84,7 @@ describe("Items - Light Ball", () => {
it("LIGHT_BALL held by PIKACHU", async () => { it("LIGHT_BALL held by PIKACHU", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);
const spAtkStat = partyMember.getStat(Stat.SPATK); const spAtkStat = partyMember.getStat(Stat.SPATK);
@ -113,8 +113,7 @@ describe("Items - Light Ball", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -152,8 +151,7 @@ describe("Items - Light Ball", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -191,7 +189,7 @@ describe("Items - Light Ball", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);
const spAtkStat = partyMember.getStat(Stat.SPATK); const spAtkStat = partyMember.getStat(Stat.SPATK);

View File

@ -33,7 +33,7 @@ describe("Items - Metal Powder", () => {
const consoleSpy = vi.spyOn(console, "log"); const consoleSpy = vi.spyOn(console, "log");
await game.classicMode.startBattle([SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.DITTO]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
// Checking console log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called // Checking console log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called
partyMember.getEffectiveStat(Stat.DEF); partyMember.getEffectiveStat(Stat.DEF);
@ -84,7 +84,7 @@ describe("Items - Metal Powder", () => {
it("METAL_POWDER held by DITTO", async () => { it("METAL_POWDER held by DITTO", async () => {
await game.classicMode.startBattle([SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.DITTO]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const defStat = partyMember.getStat(Stat.DEF); const defStat = partyMember.getStat(Stat.DEF);
@ -107,8 +107,7 @@ describe("Items - Metal Powder", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -140,8 +139,7 @@ describe("Items - Metal Powder", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -173,7 +171,7 @@ describe("Items - Metal Powder", () => {
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]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const defStat = partyMember.getStat(Stat.DEF); const defStat = partyMember.getStat(Stat.DEF);

View File

@ -33,7 +33,7 @@ describe("Items - Quick Powder", () => {
const consoleSpy = vi.spyOn(console, "log"); const consoleSpy = vi.spyOn(console, "log");
await game.classicMode.startBattle([SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.DITTO]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
// Checking console log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called // Checking console log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called
partyMember.getEffectiveStat(Stat.DEF); partyMember.getEffectiveStat(Stat.DEF);
@ -84,7 +84,7 @@ describe("Items - Quick Powder", () => {
it("QUICK_POWDER held by DITTO", async () => { it("QUICK_POWDER held by DITTO", async () => {
await game.classicMode.startBattle([SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.DITTO]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const spdStat = partyMember.getStat(Stat.SPD); const spdStat = partyMember.getStat(Stat.SPD);
@ -107,8 +107,7 @@ describe("Items - Quick Powder", () => {
it("QUICK_POWDER held by fused DITTO (base)", async () => { it("QUICK_POWDER held by fused DITTO (base)", async () => {
await game.classicMode.startBattle([SpeciesId.DITTO, SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.DITTO, SpeciesId.MAROWAK]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -140,8 +139,7 @@ describe("Items - Quick Powder", () => {
it("QUICK_POWDER held by fused DITTO (part)", async () => { it("QUICK_POWDER held by fused DITTO (part)", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.DITTO]); await game.classicMode.startBattle([SpeciesId.MAROWAK, SpeciesId.DITTO]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -173,7 +171,7 @@ describe("Items - Quick Powder", () => {
it("QUICK_POWDER not held by DITTO", async () => { it("QUICK_POWDER not held by DITTO", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.MAROWAK]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const spdStat = partyMember.getStat(Stat.SPD); const spdStat = partyMember.getStat(Stat.SPD);

View File

@ -33,7 +33,7 @@ describe("Items - Thick Club", () => {
const consoleSpy = vi.spyOn(console, "log"); const consoleSpy = vi.spyOn(console, "log");
await game.classicMode.startBattle([SpeciesId.CUBONE]); await game.classicMode.startBattle([SpeciesId.CUBONE]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
// Checking console log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called // Checking console log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called
partyMember.getEffectiveStat(Stat.DEF); partyMember.getEffectiveStat(Stat.DEF);
@ -84,7 +84,7 @@ describe("Items - Thick Club", () => {
it("THICK_CLUB held by CUBONE", async () => { it("THICK_CLUB held by CUBONE", async () => {
await game.classicMode.startBattle([SpeciesId.CUBONE]); await game.classicMode.startBattle([SpeciesId.CUBONE]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);
@ -107,7 +107,7 @@ describe("Items - Thick Club", () => {
it("THICK_CLUB held by MAROWAK", async () => { it("THICK_CLUB held by MAROWAK", async () => {
await game.classicMode.startBattle([SpeciesId.MAROWAK]); await game.classicMode.startBattle([SpeciesId.MAROWAK]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);
@ -130,7 +130,7 @@ describe("Items - Thick Club", () => {
it("THICK_CLUB held by ALOLA_MAROWAK", async () => { it("THICK_CLUB held by ALOLA_MAROWAK", async () => {
await game.classicMode.startBattle([SpeciesId.ALOLA_MAROWAK]); await game.classicMode.startBattle([SpeciesId.ALOLA_MAROWAK]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);
@ -157,8 +157,7 @@ describe("Items - Thick Club", () => {
await game.classicMode.startBattle([species[randSpecies], SpeciesId.PIKACHU]); await game.classicMode.startBattle([species[randSpecies], SpeciesId.PIKACHU]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -194,8 +193,7 @@ describe("Items - Thick Club", () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU, species[randSpecies]]); await game.classicMode.startBattle([SpeciesId.PIKACHU, species[randSpecies]]);
const partyMember = game.scene.getPlayerParty()[0]; const [partyMember, ally] = game.scene.getPlayerParty();
const ally = game.scene.getPlayerParty()[1];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
partyMember.fusionSpecies = ally.species; partyMember.fusionSpecies = ally.species;
@ -227,7 +225,7 @@ describe("Items - Thick Club", () => {
it("THICK_CLUB not held by CUBONE", async () => { it("THICK_CLUB not held by CUBONE", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]); await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const partyMember = game.scene.getPlayerParty()[0]; const partyMember = game.field.getPlayerPokemon();
const atkStat = partyMember.getStat(Stat.ATK); const atkStat = partyMember.getStat(Stat.ATK);

View File

@ -46,7 +46,7 @@ describe("Moves - Dragon Rage", () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
partyPokemon = game.scene.getPlayerParty()[0]; partyPokemon = game.field.getPlayerPokemon();
enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon = game.field.getEnemyPokemon();
}); });

View File

@ -76,10 +76,9 @@ describe("Moves - Dragon Tail", () => {
game.override.battleStyle("double").enemyMoveset(MoveId.SPLASH).enemyAbility(AbilityId.ROUGH_SKIN); game.override.battleStyle("double").enemyMoveset(MoveId.SPLASH).enemyAbility(AbilityId.ROUGH_SKIN);
await game.classicMode.startBattle([SpeciesId.DRATINI, SpeciesId.DRATINI, SpeciesId.WAILORD, SpeciesId.WAILORD]); await game.classicMode.startBattle([SpeciesId.DRATINI, SpeciesId.DRATINI, SpeciesId.WAILORD, SpeciesId.WAILORD]);
const leadPokemon = game.scene.getPlayerParty()[0]!; const leadPokemon = game.field.getPlayerPokemon();
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; const [enemyLeadPokemon, enemySecPokemon] = game.scene.getEnemyParty();
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
game.move.select(MoveId.DRAGON_TAIL, 0, BattlerIndex.ENEMY); game.move.select(MoveId.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
game.move.select(MoveId.SPLASH, 1); game.move.select(MoveId.SPLASH, 1);
@ -105,11 +104,9 @@ describe("Moves - Dragon Tail", () => {
game.override.battleStyle("double").enemyMoveset(MoveId.SPLASH).enemyAbility(AbilityId.ROUGH_SKIN); game.override.battleStyle("double").enemyMoveset(MoveId.SPLASH).enemyAbility(AbilityId.ROUGH_SKIN);
await game.classicMode.startBattle([SpeciesId.DRATINI, SpeciesId.DRATINI, SpeciesId.WAILORD, SpeciesId.WAILORD]); await game.classicMode.startBattle([SpeciesId.DRATINI, SpeciesId.DRATINI, SpeciesId.WAILORD, SpeciesId.WAILORD]);
const leadPokemon = game.scene.getPlayerParty()[0]!; const [leadPokemon, secPokemon] = game.scene.getPlayerParty();
const secPokemon = game.scene.getPlayerParty()[1]!;
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!; const [enemyLeadPokemon, enemySecPokemon] = game.scene.getEnemyParty();
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
game.move.select(MoveId.DRAGON_TAIL, 0, BattlerIndex.ENEMY); game.move.select(MoveId.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
// target the same pokemon, second move should be redirected after first flees // target the same pokemon, second move should be redirected after first flees

View File

@ -42,7 +42,7 @@ describe("Moves - Fissure", () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
partyPokemon = game.scene.getPlayerParty()[0]; partyPokemon = game.field.getPlayerPokemon();
enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon = game.field.getEnemyPokemon();
}); });

View File

@ -80,19 +80,19 @@ describe("Moves - Parting Shot", () => {
// use Memento 3 times to debuff enemy // use Memento 3 times to debuff enemy
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to(FaintPhase);
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.field.getPlayerPokemon().isFainted()).toBe(true);
game.doSelectPartyPokemon(1); game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to(TurnInitPhase, false);
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to(FaintPhase);
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.field.getPlayerPokemon().isFainted()).toBe(true);
game.doSelectPartyPokemon(2); game.doSelectPartyPokemon(2);
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to(TurnInitPhase, false);
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to(FaintPhase);
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.field.getPlayerPokemon().isFainted()).toBe(true);
game.doSelectPartyPokemon(3); game.doSelectPartyPokemon(3);
// set up done // set up done
@ -177,8 +177,8 @@ describe("Moves - Parting Shot", () => {
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
// intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted) // intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted)
await game.killPokemon(game.scene.getPlayerParty()[0]); await game.killPokemon(game.field.getPlayerPokemon());
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.field.getPlayerPokemon().isFainted()).toBe(true);
await game.phaseInterceptor.run(MessagePhase); await game.phaseInterceptor.run(MessagePhase);
game.doSelectPartyPokemon(1); game.doSelectPartyPokemon(1);

View File

@ -133,6 +133,6 @@ describe("Moves - Revival Blessing", () => {
await game.toNextTurn(); await game.toNextTurn();
// If there are incorrectly two switch phases into this slot, the fainted pokemon will end up in slot 3 // If there are incorrectly two switch phases into this slot, the fainted pokemon will end up in slot 3
// Make sure it's still in slot 1 // Make sure it's still in slot 1
expect(game.scene.getEnemyParty()[0]).toBe(enemyFainting); expect(game.field.getEnemyPokemon()).toBe(enemyFainting);
}); });
}); });

View File

@ -44,10 +44,10 @@ describe("Moves - Rollout", () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
const playerPkm = game.scene.getPlayerParty()[0]; const playerPkm = game.field.getPlayerPokemon();
vi.spyOn(playerPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD vi.spyOn(playerPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD
const enemyPkm = game.scene.getEnemyParty()[0]; const enemyPkm = game.field.getEnemyPokemon();
vi.spyOn(enemyPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD vi.spyOn(enemyPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD
vi.spyOn(enemyPkm, "getHeldItems").mockReturnValue([]); //no berries vi.spyOn(enemyPkm, "getHeldItems").mockReturnValue([]); //no berries

View File

@ -6,7 +6,6 @@ import { MoveResult } from "#enums/move-result";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { RandomMoveAttr } from "#moves/move"; import { RandomMoveAttr } from "#moves/move";
import { PokemonMove } from "#moves/pokemon-move";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
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";
@ -40,7 +39,7 @@ describe("Moves - Sketch", () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
// can't use normal moveset override because we need to check moveset changes // can't use normal moveset override because we need to check moveset changes
playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH), new PokemonMove(MoveId.SKETCH)]; game.move.changeMoveset(playerPokemon, [MoveId.SKETCH, MoveId.SKETCH]);
game.move.select(MoveId.SKETCH); game.move.select(MoveId.SKETCH);
await game.phaseInterceptor.to("TurnEndPhase"); await game.phaseInterceptor.to("TurnEndPhase");
@ -62,7 +61,7 @@ describe("Moves - Sketch", () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon(); const enemyPokemon = game.field.getEnemyPokemon();
playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH), new PokemonMove(MoveId.GROWL)]; game.move.changeMoveset(playerPokemon, [MoveId.SKETCH, MoveId.GROWL]);
game.move.select(MoveId.GROWL); game.move.select(MoveId.GROWL);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
@ -88,8 +87,9 @@ describe("Moves - Sketch", () => {
game.override.enemyMoveset([MoveId.METRONOME]); game.override.enemyMoveset([MoveId.METRONOME]);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerPokemon = game.field.getPlayerPokemon(); const playerPokemon = game.field.getPlayerPokemon();
playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH)]; game.move.changeMoveset(playerPokemon, MoveId.SKETCH);
// Opponent uses Metronome -> False Swipe, then player uses Sketch, which should sketch Metronome // Opponent uses Metronome -> False Swipe, then player uses Sketch, which should sketch Metronome
game.move.select(MoveId.SKETCH); game.move.select(MoveId.SKETCH);

View File

@ -48,7 +48,7 @@ describe("Moves - Spikes", () => {
game.doSwitchPokemon(1); game.doSwitchPokemon(1);
await game.toNextTurn(); await game.toNextTurn();
const player = game.scene.getPlayerParty()[0]; const player = game.field.getPlayerPokemon();
expect(player.hp).toBe(player.getMaxHp()); expect(player.hp).toBe(player.getMaxHp());
}); });
@ -62,7 +62,7 @@ describe("Moves - Spikes", () => {
game.move.select(MoveId.ROAR); game.move.select(MoveId.ROAR);
await game.toNextTurn(); await game.toNextTurn();
const enemy = game.scene.getEnemyParty()[0]; const enemy = game.field.getEnemyPokemon();
expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
}); });
@ -77,7 +77,7 @@ describe("Moves - Spikes", () => {
game.forceEnemyToSwitch(); game.forceEnemyToSwitch();
await game.toNextTurn(); await game.toNextTurn();
const enemy = game.scene.getEnemyParty()[0]; const enemy = game.field.getEnemyPokemon();
expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
}); });

View File

@ -50,7 +50,7 @@ describe("Moves - Tackle", () => {
const moveToUse = MoveId.TACKLE; const moveToUse = MoveId.TACKLE;
await game.classicMode.startBattle([SpeciesId.MIGHTYENA]); await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
game.scene.currentBattle.enemyParty[0].stats[Stat.DEF] = 50; game.scene.currentBattle.enemyParty[0].stats[Stat.DEF] = 50;
game.scene.getPlayerParty()[0].stats[Stat.ATK] = 50; game.field.getPlayerPokemon().stats[Stat.ATK] = 50;
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;

View File

@ -72,7 +72,7 @@ describe("Moves - Tera Starstorm", () => {
it("targets both opponents in a double battle when used by Terapagos immediately after terastallizing", async () => { it("targets both opponents in a double battle when used by Terapagos immediately after terastallizing", async () => {
await game.classicMode.startBattle([SpeciesId.TERAPAGOS]); await game.classicMode.startBattle([SpeciesId.TERAPAGOS]);
const terapagos = game.scene.getPlayerParty()[0]; const terapagos = game.field.getPlayerPokemon();
terapagos.isTerastallized = false; terapagos.isTerastallized = false;
game.move.selectWithTera(MoveId.TERA_STARSTORM, 0); game.move.selectWithTera(MoveId.TERA_STARSTORM, 0);
@ -89,7 +89,7 @@ describe("Moves - Tera Starstorm", () => {
it("targets only one opponent in a double battle when used by Terapagos without terastallizing", async () => { it("targets only one opponent in a double battle when used by Terapagos without terastallizing", async () => {
await game.classicMode.startBattle([SpeciesId.TERAPAGOS]); await game.classicMode.startBattle([SpeciesId.TERAPAGOS]);
const terapagos = game.scene.getPlayerParty()[0]; const terapagos = game.field.getPlayerPokemon();
terapagos.isTerastallized = false; terapagos.isTerastallized = false;
game.move.select(MoveId.TERA_STARSTORM, 0, BattlerIndex.ENEMY); game.move.select(MoveId.TERA_STARSTORM, 0, BattlerIndex.ENEMY);
@ -106,8 +106,7 @@ describe("Moves - Tera Starstorm", () => {
it("applies the effects when Terapagos in Stellar Form is fused with another Pokemon", async () => { it("applies the effects when Terapagos in Stellar Form is fused with another Pokemon", async () => {
await game.classicMode.startBattle([SpeciesId.TERAPAGOS, SpeciesId.CHARMANDER, SpeciesId.MAGIKARP]); await game.classicMode.startBattle([SpeciesId.TERAPAGOS, SpeciesId.CHARMANDER, SpeciesId.MAGIKARP]);
const fusionedMon = game.scene.getPlayerParty()[0]; const [fusionedMon, , magikarp] = game.scene.getPlayerParty();
const magikarp = game.scene.getPlayerParty()[2];
// Fuse party members (taken from PlayerPokemon.fuse(...) function) // Fuse party members (taken from PlayerPokemon.fuse(...) function)
fusionedMon.fusionSpecies = magikarp.species; fusionedMon.fusionSpecies = magikarp.species;

View File

@ -7,7 +7,6 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { ShinyRateBoosterModifier } from "#modifiers/modifier"; import { ShinyRateBoosterModifier } from "#modifiers/modifier";
import { PokemonMove } from "#moves/pokemon-move";
import { AnOfferYouCantRefuseEncounter } from "#mystery-encounters/an-offer-you-cant-refuse-encounter"; import { AnOfferYouCantRefuseEncounter } from "#mystery-encounters/an-offer-you-cant-refuse-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
@ -207,9 +206,8 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
it("should award EXP to a pokemon with a move in EXTORTION_MOVES", async () => { it("should award EXP to a pokemon with a move in EXTORTION_MOVES", async () => {
game.override.ability(AbilityId.SYNCHRONIZE); // Not an extortion ability, so we can test extortion move game.override.ability(AbilityId.SYNCHRONIZE); // Not an extortion ability, so we can test extortion move
await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [SpeciesId.ABRA]); await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [SpeciesId.ABRA]);
const party = scene.getPlayerParty(); const abra = game.field.getPlayerPokemon();
const abra = party.find(pkm => pkm.species.speciesId === SpeciesId.ABRA)!; game.move.changeMoveset(abra, MoveId.BEAT_UP);
abra.moveset = [new PokemonMove(MoveId.BEAT_UP)];
const expBefore = abra.exp; const expBefore = abra.exp;
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);

View File

@ -230,7 +230,7 @@ describe("Clowning Around - Mystery Encounter", () => {
// Stop next battle before it runs // Stop next battle before it runs
await game.phaseInterceptor.to(NewBattlePhase, false); await game.phaseInterceptor.to(NewBattlePhase, false);
const leadPokemon = scene.getPlayerParty()[0]; const leadPokemon = game.field.getPlayerPokemon();
expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain); expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain);
}); });
}); });
@ -263,30 +263,30 @@ describe("Clowning Around - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
// Set some moves on party for attack type booster generation // Set some moves on party for attack type booster generation
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.TACKLE), new PokemonMove(MoveId.THIEF)]; game.move.changeMoveset(game.field.getPlayerPokemon(), [MoveId.TACKLE, MoveId.THIEF]);
// 2 Sitrus Berries on lead // 2 Sitrus Berries on lead
scene.modifiers = []; scene.modifiers = [];
let itemType = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType; let itemType = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 2, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 2, itemType);
// 2 Ganlon Berries on lead // 2 Ganlon Berries on lead
itemType = generateModifierType(modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 2, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 2, itemType);
// 5 Golden Punch on lead (ultra) // 5 Golden Punch on lead (ultra)
itemType = generateModifierType(modifierTypes.GOLDEN_PUNCH) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.GOLDEN_PUNCH) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 5, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 5, itemType);
// 5 Lucky Egg on lead (ultra) // 5 Lucky Egg on lead (ultra)
itemType = generateModifierType(modifierTypes.LUCKY_EGG) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.LUCKY_EGG) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 5, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 5, itemType);
// 3 Soothe Bell on lead (great tier, but counted as ultra by this ME) // 3 Soothe Bell on lead (great tier, but counted as ultra by this ME)
itemType = generateModifierType(modifierTypes.SOOTHE_BELL) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.SOOTHE_BELL) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 3, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 3, itemType);
// 5 Soul Dew on lead (rogue) // 5 Soul Dew on lead (rogue)
itemType = generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 5, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 5, itemType);
// 2 Golden Egg on lead (rogue) // 2 Golden Egg on lead (rogue)
itemType = generateModifierType(modifierTypes.GOLDEN_EGG) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.GOLDEN_EGG) as PokemonHeldItemModifierType;
await addItemToPokemon(scene, scene.getPlayerParty()[0], 2, itemType); await addItemToPokemon(scene, game.field.getPlayerPokemon(), 2, itemType);
// 5 Soul Dew on second party pokemon (these should not change) // 5 Soul Dew on second party pokemon (these should not change)
itemType = generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType; itemType = generateModifierType(modifierTypes.SOUL_DEW) as PokemonHeldItemModifierType;
@ -294,7 +294,7 @@ describe("Clowning Around - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
const leadItemsAfter = scene.getPlayerParty()[0].getHeldItems(); const leadItemsAfter = game.field.getPlayerPokemon().getHeldItems();
const ultraCountAfter = leadItemsAfter const ultraCountAfter = leadItemsAfter
.filter(m => m.type.tier === ModifierTier.ULTRA) .filter(m => m.type.tier === ModifierTier.ULTRA)
.reduce((a, b) => a + b.stackCount, 0); .reduce((a, b) => a + b.stackCount, 0);
@ -348,14 +348,14 @@ describe("Clowning Around - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
// Same type moves on lead // Same type moves on lead
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.ICE_BEAM), new PokemonMove(MoveId.SURF)]; game.move.changeMoveset(game.field.getPlayerPokemon(), [MoveId.ICE_BEAM, MoveId.SURF]);
// Different type moves on second // Different type moves on second
scene.getPlayerParty()[1].moveset = [new PokemonMove(MoveId.GRASS_KNOT), new PokemonMove(MoveId.ELECTRO_BALL)]; game.move.changeMoveset(scene.getPlayerParty()[1], [MoveId.GRASS_KNOT, MoveId.ELECTRO_BALL]);
// No moves on third // No moves on third
scene.getPlayerParty()[2].moveset = []; scene.getPlayerParty()[2].moveset = [];
await runMysteryEncounterToEnd(game, 3); await runMysteryEncounterToEnd(game, 3);
const leadTypesAfter = scene.getPlayerParty()[0].getTypes(); const leadTypesAfter = game.field.getPlayerPokemon().getTypes();
const secondaryTypesAfter = scene.getPlayerParty()[1].getTypes(); const secondaryTypesAfter = scene.getPlayerParty()[1].getTypes();
const thirdTypesAfter = scene.getPlayerParty()[2].getTypes(); const thirdTypesAfter = scene.getPlayerParty()[2].getTypes();

View File

@ -6,7 +6,6 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { PokemonMove } from "#moves/pokemon-move";
import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter"; import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
@ -100,7 +99,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
// Make party lead's level arbitrarily high to not get KOed by move // Make party lead's level arbitrarily high to not get KOed by move
const partyLead = scene.getPlayerParty()[0]; const partyLead = game.field.getPlayerPokemon();
partyLead.level = 1000; partyLead.level = 1000;
partyLead.calculateStats(); partyLead.calculateStats();
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
@ -121,7 +120,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
it("should have a Baton in the rewards after battle", async () => { it("should have a Baton in the rewards after battle", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
// Make party lead's level arbitrarily high to not get KOed by move // Make party lead's level arbitrarily high to not get KOed by move
const partyLead = scene.getPlayerParty()[0]; const partyLead = game.field.getPlayerPokemon();
partyLead.level = 1000; partyLead.level = 1000;
partyLead.calculateStats(); partyLead.calculateStats();
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
@ -159,7 +158,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
const phaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); const phaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase");
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
scene.getPlayerParty()[0].moveset = []; game.field.getPlayerPokemon().moveset = [];
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 }); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 });
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof LearnMovePhase).map(p => p[0]); const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof LearnMovePhase).map(p => p[0]);
@ -171,7 +170,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
scene.getPlayerParty()[0].moveset = []; game.field.getPlayerPokemon().moveset = [];
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 }); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 });
expect(leaveEncounterWithoutBattleSpy).toBeCalled(); expect(leaveEncounterWithoutBattleSpy).toBeCalled();
@ -199,7 +198,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
it("should add Oricorio to the party", async () => { it("should add Oricorio to the party", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
const partyCountBefore = scene.getPlayerParty().length; const partyCountBefore = scene.getPlayerParty().length;
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.DRAGON_DANCE)]; game.move.changeMoveset(game.field.getPlayerPokemon(), MoveId.DRAGON_DANCE);
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
const partyCountAfter = scene.getPlayerParty().length; const partyCountAfter = scene.getPlayerParty().length;
@ -238,7 +237,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.DRAGON_DANCE)]; game.move.changeMoveset(game.field.getPlayerPokemon(), MoveId.DRAGON_DANCE);
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
expect(leaveEncounterWithoutBattleSpy).toBeCalled(); expect(leaveEncounterWithoutBattleSpy).toBeCalled();

View File

@ -201,7 +201,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 2 Sitrus berries on party lead // Set 2 Sitrus berries on party lead
scene.modifiers = []; scene.modifiers = [];
const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!; const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!;
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier; const sitrusMod = sitrus.newModifier(game.field.getPlayerPokemon()) as BerryModifier;
sitrusMod.stackCount = 2; sitrusMod.stackCount = 2;
scene.addModifier(sitrusMod, true, false, false, true); scene.addModifier(sitrusMod, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -222,7 +222,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Reviver Seed on party lead // Set 1 Reviver Seed on party lead
scene.modifiers = []; scene.modifiers = [];
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!; const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier; const modifier = revSeed.newModifier(game.field.getPlayerPokemon()) as PokemonInstantReviveModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -248,7 +248,7 @@ describe("Delibird-y - Mystery Encounter", () => {
const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!; const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!;
// Sitrus berries on party // Sitrus berries on party
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier; const sitrusMod = sitrus.newModifier(game.field.getPlayerPokemon()) as BerryModifier;
sitrusMod.stackCount = 2; sitrusMod.stackCount = 2;
scene.addModifier(sitrusMod, true, false, false, true); scene.addModifier(sitrusMod, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -277,7 +277,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Reviver Seed on party lead // Set 1 Reviver Seed on party lead
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!; const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier; const modifier = revSeed.newModifier(game.field.getPlayerPokemon()) as PokemonInstantReviveModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -301,7 +301,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Soul Dew on party lead // Set 1 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]); const modifier = soulDew.newModifier(game.field.getPlayerPokemon());
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -329,7 +329,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Reviver Seed on party lead // Set 1 Reviver Seed on party lead
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!; const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]) as PokemonInstantReviveModifier; const modifier = revSeed.newModifier(game.field.getPlayerPokemon()) as PokemonInstantReviveModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -363,7 +363,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 2 Soul Dew on party lead // Set 2 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 2; modifier.stackCount = 2;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -384,7 +384,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Soul Dew on party lead // Set 1 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -410,7 +410,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Soul Dew on party lead // Set 1 Soul Dew on party lead
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -434,7 +434,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Reviver Seed on party lead // Set 1 Reviver Seed on party lead
scene.modifiers = []; scene.modifiers = [];
const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!; const revSeed = generateModifierType(modifierTypes.REVIVER_SEED)!;
const modifier = revSeed.newModifier(scene.getPlayerParty()[0]); const modifier = revSeed.newModifier(game.field.getPlayerPokemon());
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -463,7 +463,7 @@ describe("Delibird-y - Mystery Encounter", () => {
// Set 1 Soul Dew on party lead // Set 1 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);

View File

@ -6,7 +6,6 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter"; import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
@ -178,7 +177,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty);
// Mock moveset // Mock moveset
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.KNOCK_OFF)]; game.move.changeMoveset(game.field.getPlayerPokemon(), MoveId.KNOCK_OFF);
const item = game.scene.currentBattle.mysteryEncounter!.misc; const item = game.scene.currentBattle.mysteryEncounter!.misc;
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);

View File

@ -105,14 +105,14 @@ describe("Global Trade System - Mystery Encounter", () => {
it("Should trade a Pokemon from the player's party for the first of 3 Pokemon options", async () => { it("Should trade a Pokemon from the player's party for the first of 3 Pokemon options", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
const speciesBefore = scene.getPlayerParty()[0].species.speciesId; const speciesBefore = game.field.getPlayerPokemon().species.speciesId;
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 }); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 });
const speciesAfter = scene.getPlayerParty().at(-1)?.species.speciesId; const speciesAfter = scene.getPlayerParty().at(-1)?.species.speciesId;
expect(speciesAfter).toBeDefined(); expect(speciesAfter).toBeDefined();
expect(speciesBefore).not.toBe(speciesAfter); expect(speciesBefore).not.toBe(speciesAfter);
expect(defaultParty.includes(speciesAfter!)).toBeFalsy(); expect(defaultParty).not.toContain(speciesAfter);
}); });
it("Should trade a Pokemon from the player's party for the second of 3 Pokemon options", async () => { it("Should trade a Pokemon from the player's party for the second of 3 Pokemon options", async () => {
@ -220,7 +220,7 @@ describe("Global Trade System - Mystery Encounter", () => {
// Set 2 Soul Dew on party lead // Set 2 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 2; modifier.stackCount = 2;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -247,7 +247,7 @@ describe("Global Trade System - Mystery Encounter", () => {
// Set 1 Soul Dew on party lead // Set 1 Soul Dew on party lead
scene.modifiers = []; scene.modifiers = [];
const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!; const soulDew = generateModifierType(modifierTypes.SOUL_DEW)!;
const modifier = soulDew.newModifier(scene.getPlayerParty()[0]) as PokemonNatureWeightModifier; const modifier = soulDew.newModifier(game.field.getPlayerPokemon()) as PokemonNatureWeightModifier;
modifier.stackCount = 1; modifier.stackCount = 1;
scene.addModifier(modifier, true, false, false, true); scene.addModifier(modifier, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);

View File

@ -5,7 +5,6 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters"; import { CIVILIZATION_ENCOUNTER_BIOMES } from "#mystery-encounters/mystery-encounters";
@ -110,7 +109,7 @@ describe("Part-Timer - Mystery Encounter", () => {
expect(EncounterPhaseUtils.updatePlayerMoney).toHaveBeenCalledWith(scene.getWaveMoneyAmount(1), true, false); expect(EncounterPhaseUtils.updatePlayerMoney).toHaveBeenCalledWith(scene.getWaveMoneyAmount(1), true, false);
// Expect PP of mon's moves to have been reduced to 2 // Expect PP of mon's moves to have been reduced to 2
const moves = scene.getPlayerParty()[0].moveset; const moves = game.field.getPlayerPokemon().moveset;
for (const move of moves) { for (const move of moves) {
expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2);
} }
@ -233,7 +232,9 @@ describe("Part-Timer - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.PART_TIMER, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.PART_TIMER, defaultParty);
// Mock movesets // Mock movesets
scene.getPlayerParty().forEach(p => (p.moveset = [])); scene.getPlayerParty().forEach(p => {
p.moveset = [];
});
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to(MysteryEncounterPhase, false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
@ -257,14 +258,14 @@ describe("Part-Timer - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.PART_TIMER, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.PART_TIMER, defaultParty);
// Mock moveset // Mock moveset
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.ATTRACT)]; game.move.changeMoveset(game.field.getPlayerPokemon(), MoveId.ATTRACT);
await runMysteryEncounterToEnd(game, 3); await runMysteryEncounterToEnd(game, 3);
expect(EncounterPhaseUtils.updatePlayerMoney).toHaveBeenCalledWith(scene.getWaveMoneyAmount(2.5), true, false); expect(EncounterPhaseUtils.updatePlayerMoney).toHaveBeenCalledWith(scene.getWaveMoneyAmount(2.5), true, false);
// Expect PP of mon's moves to have been reduced to 2 // Expect PP of mon's moves to have been reduced to 2
const moves = scene.getPlayerParty()[0].moveset; const moves = game.field.getPlayerPokemon().moveset;
for (const move of moves) { for (const move of moves) {
expect((move?.getMovePp() ?? 0) - (move?.ppUsed ?? 0)).toBe(2); expect(move.getMovePp() - move.ppUsed).toBe(2);
} }
}); });

View File

@ -11,7 +11,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import type { BerryModifier } from "#modifiers/modifier"; import type { BerryModifier } from "#modifiers/modifier";
import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
@ -214,11 +213,11 @@ describe("Uncommon Breed - Mystery Encounter", () => {
// Berries on party lead // Berries on party lead
const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!; const sitrus = generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS])!;
const sitrusMod = sitrus.newModifier(scene.getPlayerParty()[0]) as BerryModifier; const sitrusMod = sitrus.newModifier(game.field.getPlayerPokemon()) as BerryModifier;
sitrusMod.stackCount = 2; sitrusMod.stackCount = 2;
scene.addModifier(sitrusMod, true, false, false, true); scene.addModifier(sitrusMod, true, false, false, true);
const ganlon = generateModifierType(modifierTypes.BERRY, [BerryType.GANLON])!; const ganlon = generateModifierType(modifierTypes.BERRY, [BerryType.GANLON])!;
const ganlonMod = ganlon.newModifier(scene.getPlayerParty()[0]) as BerryModifier; const ganlonMod = ganlon.newModifier(game.field.getPlayerPokemon()) as BerryModifier;
ganlonMod.stackCount = 3; ganlonMod.stackCount = 3;
scene.addModifier(ganlonMod, true, false, false, true); scene.addModifier(ganlonMod, true, false, false, true);
await scene.updateModifiers(true); await scene.updateModifiers(true);
@ -270,7 +269,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle"); const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty);
// Mock moveset // Mock moveset
scene.getPlayerParty()[0].moveset = [new PokemonMove(MoveId.CHARM)]; game.move.changeMoveset(game.field.getPlayerPokemon(), MoveId.CHARM);
await runMysteryEncounterToEnd(game, 3); await runMysteryEncounterToEnd(game, 3);
expect(leaveEncounterWithoutBattleSpy).toBeCalled(); expect(leaveEncounterWithoutBattleSpy).toBeCalled();

View File

@ -112,7 +112,7 @@ describe("Weird Dream - Mystery Encounter", () => {
it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => { it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
const pokemonPrior = scene.getPlayerParty().map(pokemon => pokemon); const pokemonPrior = scene.getPlayerParty().slice();
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal()); const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);

View File

@ -38,7 +38,7 @@ describe("Form Change Phase", () => {
await game.classicMode.startBattle([SpeciesId.ZACIAN]); await game.classicMode.startBattle([SpeciesId.ZACIAN]);
// Before the form change: Should be Hero form // Before the form change: Should be Hero form
const zacian = game.scene.getPlayerParty()[0]; const zacian = game.field.getPlayerPokemon();
expect(zacian.getFormKey()).toBe("hero-of-many-battles"); expect(zacian.getFormKey()).toBe("hero-of-many-battles");
expect(zacian.getTypes()).toStrictEqual([PokemonType.FAIRY]); expect(zacian.getTypes()).toStrictEqual([PokemonType.FAIRY]);
expect(zacian.calculateBaseStats()).toStrictEqual([92, 120, 115, 80, 115, 138]); expect(zacian.calculateBaseStats()).toStrictEqual([92, 120, 115, 80, 115, 138]);

View File

@ -0,0 +1,82 @@
import * as account from "#app/account";
import * as bypassLoginModule from "#app/global-vars/bypass-login";
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
import type { SessionSaveData } from "#app/system/game-data";
import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id";
import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("System - Rename Run", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([MoveId.SPLASH])
.battleStyle("single")
.enemyAbility(AbilityId.BALL_FETCH)
.enemyMoveset(MoveId.SPLASH);
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
describe("renameSession", () => {
beforeEach(() => {
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(false);
vi.spyOn(account, "updateUserInfo").mockImplementation(async () => [true, 1]);
});
it("should return false if slotId < 0", async () => {
const result = await game.scene.gameData.renameSession(-1, "Named Run");
expect(result).toEqual(false);
});
it("should return false if getSession returns null", async () => {
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue(null as unknown as SessionSaveData);
const result = await game.scene.gameData.renameSession(-1, "Named Run");
expect(result).toEqual(false);
});
it("should return true if bypassLogin is true", async () => {
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
const result = await game.scene.gameData.renameSession(0, "Named Run");
expect(result).toEqual(true);
});
it("should return false if api returns error", async () => {
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("Unknown Error!");
const result = await game.scene.gameData.renameSession(0, "Named Run");
expect(result).toEqual(false);
});
it("should return true if api is succesfull", async () => {
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("");
const result = await game.scene.gameData.renameSession(0, "Named Run");
expect(result).toEqual(true);
expect(account.updateUserInfo).toHaveBeenCalled();
});
});
});

View File

@ -90,10 +90,10 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(2); expect(game.field.getPlayerPokemon().variant).toBe(2);
expect(game.scene.getPlayerParty()[0].gender).toBe(Gender.MALE); expect(game.field.getPlayerPokemon().gender).toBe(Gender.MALE);
}); });
it("Bulbasaur - shiny - variant 2 female hardy overgrow", async () => { it("Bulbasaur - shiny - variant 2 female hardy overgrow", async () => {
@ -151,11 +151,11 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(2); expect(game.field.getPlayerPokemon().variant).toBe(2);
expect(game.scene.getPlayerParty()[0].nature).toBe(Nature.HARDY); expect(game.field.getPlayerPokemon().nature).toBe(Nature.HARDY);
expect(game.scene.getPlayerParty()[0].getAbility().id).toBe(AbilityId.OVERGROW); expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.OVERGROW);
}); });
it("Bulbasaur - shiny - variant 2 female lonely chlorophyl", async () => { it("Bulbasaur - shiny - variant 2 female lonely chlorophyl", async () => {
@ -215,12 +215,12 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(2); expect(game.field.getPlayerPokemon().variant).toBe(2);
expect(game.scene.getPlayerParty()[0].gender).toBe(Gender.FEMALE); expect(game.field.getPlayerPokemon().gender).toBe(Gender.FEMALE);
expect(game.scene.getPlayerParty()[0].nature).toBe(Nature.LONELY); expect(game.field.getPlayerPokemon().nature).toBe(Nature.LONELY);
expect(game.scene.getPlayerParty()[0].getAbility().id).toBe(AbilityId.CHLOROPHYLL); expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.CHLOROPHYLL);
}); });
it("Bulbasaur - shiny - variant 2 female", async () => { it("Bulbasaur - shiny - variant 2 female", async () => {
@ -278,10 +278,10 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(2); expect(game.field.getPlayerPokemon().variant).toBe(2);
expect(game.scene.getPlayerParty()[0].gender).toBe(Gender.FEMALE); expect(game.field.getPlayerPokemon().gender).toBe(Gender.FEMALE);
}); });
it("Bulbasaur - not shiny", async () => { it("Bulbasaur - not shiny", async () => {
@ -339,9 +339,9 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(false); expect(game.field.getPlayerPokemon().shiny).toBe(false);
expect(game.scene.getPlayerParty()[0].variant).toBe(0); expect(game.field.getPlayerPokemon().variant).toBe(0);
}); });
it("Bulbasaur - shiny - variant 1", async () => { it("Bulbasaur - shiny - variant 1", async () => {
@ -401,9 +401,9 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(1); expect(game.field.getPlayerPokemon().variant).toBe(1);
}); });
it("Bulbasaur - shiny - variant 0", async () => { it("Bulbasaur - shiny - variant 0", async () => {
@ -462,9 +462,9 @@ describe("UI - Starter select", () => {
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.field.getPlayerPokemon().shiny).toBe(true);
expect(game.scene.getPlayerParty()[0].variant).toBe(0); expect(game.field.getPlayerPokemon().variant).toBe(0);
}); });
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async () => { it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async () => {
@ -528,7 +528,7 @@ describe("UI - Starter select", () => {
saveSlotSelectUiHandler.processInput(Button.ACTION); saveSlotSelectUiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.CATERPIE); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.CATERPIE);
}); });
it("Check if first pokemon in party is nidoran_m from gen 1 and 2nd row, 4th column (cursor (9+4)-1)", async () => { it("Check if first pokemon in party is nidoran_m from gen 1 and 2nd row, 4th column (cursor (9+4)-1)", async () => {
@ -594,6 +594,6 @@ describe("UI - Starter select", () => {
saveSlotSelectUiHandler.processInput(Button.ACTION); saveSlotSelectUiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.NIDORAN_M); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.NIDORAN_M);
}); });
}); });