pokerogue/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts
ImperialSympathizer acb2b66be4
[Feature] Add Mystery Encounters to the game (#3938)
* add .github/workflows/mystery-event.yml

* update mystery-event.yml

* mystery encounters: resolve review comments:

Lost at Sea:
-fix typo in handlePokemonGuidingYouPhase function

Mysterious Chest:
- remove obsolete commented code

mystery-encounter.ts
- remove unused `onDone` field from MysteryEncounterBuilder

* fix typo in CanLearnMoveRequirementOptions

* remove redundance from Pokemon.isAllowedInBattle()

* chore: jsdoc formatting

* fix lost-at-sea tests

* add fallback for biomeMysteryEncounters if empty

* lost-at-sea-encounter: fix and extend tests

* move "battle:fainted" into `koPlayerPokemon`

* add retries to quick-draw tests

* fix lost-at-sea-encounter tests

* clean up battle animation logic

* Update and rename mystery-event.yml to mystery-events.yml

* Update mystery-events.yml

* Fix typo

* Update mystery-events.yml

Fix debug runs

* clean up unit tests and utils

* attach github issues to all encounter jsdocs

* start dialogue refactor

* update sleeping snorlax encounter

* migrate encounters dialogue to new format

* cleanup and add jsdocs

* finish fiery fallout encounter

* fix unit test breaks

* add skeleton tests to fiery fallout

* commit latest test changes

* finish unit tests for fiery fallout

* bug fix for empty modifier shop

* stash working changes

* stash changes

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/test/utils/overridesHelper.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Update src/data/battle-anims.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* nit updates and cleanup

* Update src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* add jsdocs and more cleanup

* add more jsdoc

* add the strong stuff encounter

* add the strong stuff encounter and more unit tests

* cleanup container length checks in ME ui

* add retries to tests

* add retries to tests

* fix trainer wave disable override

* add shuckle juice modifier

* add dialogue bug fixes

* add dialogue bug fixes

* add pokemon salesman encounter and affects pokedex UI display

* add unit tests for pokemon salesman

* temp stash

* add offer you can't refuse

* add unit tests for offer you can't refuse encounter

* remove unnecessary prompt handlers

* add tests for disabled encounter options

* add delibird-y encounter

* add delibird-y encounter

* add absolute avarice encounter

* finish absolute avarice encounter

* add unit tests and enhancements for item overrides in tests

* fix unit test

* cleanup absolute avarice PR

* small bug fixes with latest sync from main

* update visuals loading for safari and stat trainer visuals

* update visuals loading for safari and stat trainer visuals

* update a trainer's test encounter and add unit tests

* add Trash to Treasure encounter

* clean up trash to treasure encounter

* clean up trash to treasure encounter

* add berries abound encounter

* start clowning around encounter

* first implementation pass at clowning around

* add unit tests for clowning around

* add unit tests for clowning around

* clean up ME unit tests

* clean up unit tests

* update unit tests

* add part timer and dancing lessons encounters

* add unit tests for Dancing Lessons and Part-Timer

* reordered biome list and adjusted redirection for project and labels

* Add Weird Dream encounter and slight reworks to Berries Abound/Fight or Flight

* adjusting yml to match new labels

* fix yml whoopsie

* Expanded 'Weird Dream' banlist and fixed a bug with the BST bump range

* adds Winstrate Challenge mystery encounter

* small cleanup for winstrates

* add unit tests for Winstrate Challenge

* fix pokemon not returning after winstrate battle

* commit latest beta merge updates

* fix ME null checks and unit tests with beta update

* fix ME null checks and unit tests with beta update

* MEs to pokerogue beta branch

* test dialogue changes

* test patch fix

* test patch fix

* test patch fix

* adds teleporting hijinks encounter

* add unit tests for Teleporting Hijinks

* small change to teleporting hijinks dialogue

* migrate ME translations to json

* add retries to berries-abound.Option1: should reward the player with X berries based on wave

* add missing ME dialogue back in

* revert template changes

* add ME unique trainer dialogue to both dialogue jsons

* fix hanging comma in json

* fix broken imports

* resolve lint issues

* fix flaky test

* balance tweaks to a few MEs, updates to bug superfan

* add unit tests for Bug-Type Superfan and clean up dialogue

* Adds Fun and Games mystery encounter

* add unit tests for Fun and Games encounter

* update jsdoc

* small ME balance changes

* small ME balance changes

* Adds Uncommon Breed ME and misc. ME bug fixes

* Update getFinalSessionData() to collect Mystery Encounter data

* adds GTS encounter

* various ME bug fixes and balance changes

* latest ME bug fixes

* clean up GTS Encounter and add unit tests

* small cleanup to MEs branch

* add BGM music names for ME music

* bug fixes and balance changes for MEs

* ME data schema updates

* balance changes and bug fixes to MEs

* balance changes and bug fixes to MEs

* update tests for MEs

* add jsdoc to party exp function

* dialogue updates and test fixes for MEs

* dialogue updates and test fixes for MEs

* PR suggestions and fixees

* stash PR feedback and bugfixes

* fix all tests for MEs and cleanup

* PR feedback

* update flaky ME test

* update tests, bug fix MEs, and sprite assets

* remove unintentional console log

* re-enable stubbed function for Phaser text styling

* handle undefined introVisuals properly

* PR feedback from NightKev

* disable Uncommon Breed tests

* locales updates and bug fixes for safari zone

* more PR feedback and update field trip with Rarer Candy

* fix unit test

* Change how reroll button gets disabled in Modifier Shop Phase

* update continue button text logic

* Update src/ui/modifier-select-ui-handler.ts

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>

* fix money formatting and some nits

* more nits

* more nits

* update ME tsdocs with links

* update ME tsdocs with links

---------

Co-authored-by: Felix Staud <felix.staud@headwire.com>
Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
Co-authored-by: ImperialSympathizer <imperialsympathizer@gmail.com>
Co-authored-by: InnocentGameDev <asdargmng@gmail.com>
Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com>
2024-09-14 03:05:58 +01:00

206 lines
8.9 KiB
TypeScript

import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
import { Biome } from "#app/enums/biome";
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
import { Species } from "#app/enums/species";
import GameManager from "#app/test/utils/gameManager";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils";
import BattleScene from "#app/battle-scene";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
import { ATrainersTestEncounter } from "#app/data/mystery-encounters/encounters/a-trainers-test-encounter";
import { EggTier } from "#enums/egg-type";
import { CommandPhase } from "#app/phases/command-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { PartyHealPhase } from "#app/phases/party-heal-phase";
const namespace = "mysteryEncounter:aTrainersTest";
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
const defaultBiome = Biome.CAVE;
const defaultWave = 45;
describe("A Trainer's Test - Mystery Encounter", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
let scene: BattleScene;
beforeAll(() => {
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
});
beforeEach(async () => {
game = new GameManager(phaserGame);
scene = game.scene;
game.override.mysteryEncounterChance(100);
game.override.startingWave(defaultWave);
game.override.startingBiome(defaultBiome);
game.override.disableTrainerWaves();
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
[Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
]);
HUMAN_TRANSITABLE_BIOMES.forEach(biome => {
biomeMap.set(biome, [MysteryEncounterType.A_TRAINERS_TEST]);
});
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap);
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
vi.clearAllMocks();
vi.resetAllMocks();
});
it("should have the correct properties", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
expect(ATrainersTestEncounter.encounterType).toBe(MysteryEncounterType.A_TRAINERS_TEST);
expect(ATrainersTestEncounter.encounterTier).toBe(MysteryEncounterTier.ROGUE);
expect(ATrainersTestEncounter.dialogue).toBeDefined();
expect(ATrainersTestEncounter.dialogue.intro).toBeDefined();
expect(ATrainersTestEncounter.dialogue.intro?.[0].speaker).toBeDefined();
expect(ATrainersTestEncounter.dialogue.intro?.[0].text).toBeDefined();
expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`);
expect(ATrainersTestEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`);
expect(ATrainersTestEncounter.options.length).toBe(2);
});
it("should not run below wave 10", async () => {
game.override.startingWave(9);
await game.runToMysteryEncounter();
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.A_TRAINERS_TEST);
});
it("should not run above wave 179", async () => {
game.override.startingWave(181);
await game.runToMysteryEncounter();
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
});
it("should initialize fully ", async () => {
initSceneWithoutEncounterPhase(scene, defaultParty);
scene.currentBattle.mysteryEncounter = ATrainersTestEncounter;
const { onInit } = ATrainersTestEncounter;
expect(ATrainersTestEncounter.onInit).toBeDefined();
ATrainersTestEncounter.populateDialogueTokensFromRequirements(scene);
const onInitResult = onInit!(scene);
expect(ATrainersTestEncounter.dialogueTokens?.statTrainerName).toBeDefined();
expect(ATrainersTestEncounter.misc.trainerType).toBeDefined();
expect(ATrainersTestEncounter.misc.trainerNameKey).toBeDefined();
expect(ATrainersTestEncounter.misc.trainerEggDescription).toBeDefined();
expect(ATrainersTestEncounter.dialogue.intro).toBeDefined();
expect(ATrainersTestEncounter.options[1].dialogue?.selected).toBeDefined();
expect(onInitResult).toBe(true);
});
describe("Option 1 - Accept the Challenge", () => {
it("should have the correct properties", () => {
const option = ATrainersTestEncounter.options[0];
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
expect(option.dialogue).toBeDefined();
expect(option.dialogue!.buttonLabel).toStrictEqual(`${namespace}.option.1.label`);
expect(option.dialogue!.buttonTooltip).toStrictEqual(`${namespace}.option.1.tooltip`);
});
it("Should start battle against the trainer", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField();
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(enemyField.length).toBe(1);
expect(scene.currentBattle.trainer).toBeDefined();
expect(["buck", "cheryl", "marley", "mira", "riley"].includes(scene.currentBattle.trainer!.config.name.toLowerCase())).toBeTruthy();
expect(enemyField[0]).toBeDefined();
});
it("Should reward the player with an Epic or Legendary egg", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
const eggsBefore = scene.gameData.eggs;
expect(eggsBefore).toBeDefined();
const eggsBeforeLength = eggsBefore.length;
await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();
expect(eggsBeforeLength + 1).toBe(eggsAfter.length);
const eggTier = eggsAfter[eggsAfter.length - 1].tier;
expect(eggTier === EggTier.ULTRA || eggTier === EggTier.MASTER).toBeTruthy();
});
});
describe("Option 2 - Decline the Challenge", () => {
beforeEach(() => {
// Mock sound object
vi.spyOn(scene, "playSoundWithoutBgm").mockImplementation(() => {
return {
totalDuration: 1,
destroy: () => null
} as any;
});
});
it("should have the correct properties", () => {
const option = ATrainersTestEncounter.options[1];
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
expect(option.dialogue).toBeDefined();
expect(option.dialogue?.buttonLabel).toStrictEqual(`${namespace}.option.2.label`);
expect(option.dialogue?.buttonTooltip).toStrictEqual(`${namespace}.option.2.tooltip`);
});
it("Should fully heal the party", async () => {
const phaseSpy = vi.spyOn(scene, "unshiftPhase");
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
await runMysteryEncounterToEnd(game, 2);
const partyHealPhases = phaseSpy.mock.calls.filter(p => p[0] instanceof PartyHealPhase).map(p => p[0]);
expect(partyHealPhases.length).toBe(1);
});
it("Should reward the player with a Rare egg", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
const eggsBefore = scene.gameData.eggs;
expect(eggsBefore).toBeDefined();
const eggsBeforeLength = eggsBefore.length;
await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined();
expect(eggsBeforeLength + 1).toBe(eggsAfter.length);
const eggTier = eggsAfter[eggsAfter.length - 1].tier;
expect(eggTier).toBe(EggTier.GREAT);
});
it("should leave encounter without battle", async () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty);
await runMysteryEncounterToEnd(game, 2);
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
});
});
});