add the strong stuff encounter and more unit tests
This commit is contained in:
parent
f63b3938e4
commit
d810af0b57
|
@ -54,7 +54,7 @@ export function doTrainerExclamation(scene: BattleScene) {
|
|||
}
|
||||
});
|
||||
|
||||
scene.playSound("GEN8- Exclaim.wav", { volume: 0.8 });
|
||||
scene.playSound("GEN8- Exclaim.wav", { volume: 0.7 });
|
||||
}
|
||||
|
||||
export interface EnemyPokemonConfig {
|
||||
|
|
|
@ -207,7 +207,7 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
|
|||
export function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
|
||||
pokemon.getSpeciesForm().baseStats = [...pokemon.getSpeciesForm().baseStats].map(v => {
|
||||
const newVal = Math.floor(v + value);
|
||||
return Math.min(newVal, 1);
|
||||
return Math.max(newVal, 1);
|
||||
});
|
||||
pokemon.calculateStats();
|
||||
pokemon.updateInfo();
|
||||
|
|
|
@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0;
|
|||
*/
|
||||
|
||||
// 1 to 256, set to null to ignore
|
||||
export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256;
|
||||
export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null;
|
||||
export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
|
||||
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.THE_STRONG_STUFF;
|
||||
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null;
|
||||
|
||||
/**
|
||||
* MODIFIER / ITEM OVERRIDES
|
||||
|
|
|
@ -1107,18 +1107,19 @@ export class EncounterPhase extends BattlePhase {
|
|||
|
||||
if (showEncounterMessage) {
|
||||
const introDialogue = this.scene.currentBattle.mysteryEncounter.dialogue.intro;
|
||||
const FIRST_DIALOGUE_PROMPT_DELAY = 750;
|
||||
let i = 0;
|
||||
const showNextDialogue = () => {
|
||||
const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue;
|
||||
const dialogue = introDialogue[i];
|
||||
const title = getEncounterText(this.scene, dialogue.speaker);
|
||||
const text = getEncounterText(this.scene, dialogue.text);
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true);
|
||||
}
|
||||
i++;
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
};
|
||||
|
||||
if (introDialogue.length > 0) {
|
||||
|
|
|
@ -26,10 +26,9 @@ import { BattlerTagLapseType } from "#app/data/battler-tags";
|
|||
* - Queuing of the MysteryEncounterOptionSelectedPhase
|
||||
*/
|
||||
export class MysteryEncounterPhase extends Phase {
|
||||
private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300;
|
||||
optionSelectSettings: OptionSelectSettings;
|
||||
|
||||
private FIRST_DIALOGUE_PROMPT_DELAY = 300;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
|
@ -108,12 +107,12 @@ export class MysteryEncounterPhase extends Phase {
|
|||
title = getEncounterText(this.scene, dialogue.speaker);
|
||||
}
|
||||
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
i++;
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
};
|
||||
|
||||
showNextDialogue();
|
||||
|
@ -420,6 +419,7 @@ export class MysteryEncounterRewardsPhase extends Phase {
|
|||
* - Queuing of the next wave
|
||||
*/
|
||||
export class PostMysteryEncounterPhase extends Phase {
|
||||
private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750;
|
||||
onPostOptionSelect: OptionPhaseCallback;
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
|
@ -462,13 +462,13 @@ export class PostMysteryEncounterPhase extends Phase {
|
|||
title = getEncounterText(this.scene, dialogue.speaker);
|
||||
}
|
||||
|
||||
i++;
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0);
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
this.scene.ui.showText(text, null, nextAction, i === 0 ? 750 : 0, true);
|
||||
this.scene.ui.showText(text, null, nextAction, i === 1 ? this.FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
i++;
|
||||
};
|
||||
|
||||
showNextDialogue();
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
import * as MysteryEncounters 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 { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { runSelectMysteryEncounterOption } from "#test/mystery-encounter/encounterTestUtils";
|
||||
import { SelectModifierPhase } from "#app/phases";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale-encounter";
|
||||
import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
|
||||
const namespace = "mysteryEncounter:departmentStoreSale";
|
||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.PLAINS;
|
||||
const defaultWave = 37;
|
||||
|
||||
describe("Department Store Sale - 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.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWave(true);
|
||||
|
||||
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
|
||||
[Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||
]);
|
||||
CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => {
|
||||
biomeMap.set(biome, [MysteryEncounterType.DEPARTMENT_STORE_SALE]);
|
||||
});
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
|
||||
expect(DepartmentStoreSaleEncounter.encounterType).toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
expect(DepartmentStoreSaleEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||
expect(DepartmentStoreSaleEncounter.dialogue).toBeDefined();
|
||||
expect(DepartmentStoreSaleEncounter.dialogue.intro).toStrictEqual([
|
||||
{ text: `${namespace}:intro` },
|
||||
{
|
||||
speaker: `${namespace}:speaker`,
|
||||
text: `${namespace}:intro_dialogue`,
|
||||
}
|
||||
]);
|
||||
expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||
expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||
expect(DepartmentStoreSaleEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||
expect(DepartmentStoreSaleEncounter.options.length).toBe(4);
|
||||
});
|
||||
|
||||
it("should not spawn outside of CIVILIZATION_ENCOUNTER_BIOMES", async () => {
|
||||
game.override.startingBiome(Biome.VOLCANO);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("Option 1 - TM Shop", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = DepartmentStoreSaleEncounter.options[0];
|
||||
expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:1:label`,
|
||||
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have shop with only TMs", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 1);
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(4);
|
||||
for (const option of modifierSelectHandler.options) {
|
||||
expect(option.modifierTypeOption.type.id).toContain("TM_");
|
||||
}
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 1);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - Vitamin Shop", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = DepartmentStoreSaleEncounter.options[1];
|
||||
expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:2:label`,
|
||||
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have shop with only Vitamins", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 2);
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(3);
|
||||
for (const option of modifierSelectHandler.options) {
|
||||
expect(option.modifierTypeOption.type.id.includes("PP_UP") ||
|
||||
option.modifierTypeOption.type.id.includes("BASE_STAT_BOOSTER")).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 2);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 3 - X Item Shop", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = DepartmentStoreSaleEncounter.options[2];
|
||||
expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:3:label`,
|
||||
buttonTooltip: `${namespace}:option:3:tooltip`,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have shop with only X Items", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 3);
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(5);
|
||||
for (const option of modifierSelectHandler.options) {
|
||||
expect(option.modifierTypeOption.type.id.includes("DIRE_HIT") ||
|
||||
option.modifierTypeOption.type.id.includes("TEMP_STAT_BOOSTER")).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 3);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 4 - Pokeball Shop", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = DepartmentStoreSaleEncounter.options[3];
|
||||
expect(option.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:4:label`,
|
||||
buttonTooltip: `${namespace}:option:4:tooltip`,
|
||||
});
|
||||
});
|
||||
|
||||
it("should have shop with only Pokeballs", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 4);
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(4);
|
||||
for (const option of modifierSelectHandler.options) {
|
||||
expect(option.modifierTypeOption.type.id).toContain("BALL");
|
||||
}
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 4);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -24,7 +24,7 @@ const namespace = "mysteryEncounter:fieryFallout";
|
|||
/** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */
|
||||
const defaultParty = [Species.ARCANINE, Species.NINETALES, Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.VOLCANO;
|
||||
const defaultWave = 45;
|
||||
const defaultWave = 56;
|
||||
|
||||
describe("Fiery Fallout - Mystery Encounter", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -42,7 +42,6 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWave(true);
|
||||
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||
new Map<Biome, MysteryEncounterType[]>([
|
||||
|
@ -54,6 +53,8 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
|
@ -74,7 +75,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
game.override.startingBiome(Biome.MOUNTAIN);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should not run below wave 41", async () => {
|
||||
|
@ -82,7 +83,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
|
@ -96,7 +97,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
it("should initialize fully ", async () => {
|
||||
vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: FieryFalloutEncounter } as Battle);
|
||||
const weatherSpy = vi.spyOn(scene.arena, "trySetWeather").mockReturnValue(true);
|
||||
const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||
const moveInitSpy = vi.spyOn(BattleAnims, "initMoveAnim");
|
||||
const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||
|
||||
const { onInit } = FieryFalloutEncounter;
|
||||
|
@ -130,10 +131,6 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
});
|
||||
|
||||
describe("Option 1 - Fight 2 Volcarona", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = FieryFalloutEncounter.options[0];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
|
@ -180,14 +177,10 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
&& (m as PokemonHeldItemModifier).pokemonId === leadPokemonId, true) as PokemonHeldItemModifier[];
|
||||
const charcoal = leadPokemonItems.find(i => i.type.name === "Charcoal");
|
||||
expect(charcoal).toBeDefined;
|
||||
}, 100000000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - Suffer the weather", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = FieryFalloutEncounter.options[1];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
|
@ -235,10 +228,6 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
|||
});
|
||||
|
||||
describe("Option 3 - use FIRE types", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = FieryFalloutEncounter.options[2];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_SPECIAL);
|
||||
|
|
|
@ -32,7 +32,6 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||
game.override.mysteryEncounterChance(100);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWave(true);
|
||||
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||
new Map<Biome, MysteryEncounterType[]>([
|
||||
|
@ -44,6 +43,8 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
|
@ -99,10 +100,6 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||
});
|
||||
|
||||
describe("Option 1 - Surf", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = LostAtSeaEncounter.options[0];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DISABLED_OR_DEFAULT);
|
||||
|
@ -149,10 +146,6 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||
});
|
||||
|
||||
describe("Option 2 - Fly", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option2 = LostAtSeaEncounter.options[1];
|
||||
|
||||
|
@ -202,10 +195,6 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
|||
});
|
||||
|
||||
describe("Option 3 - Wander aimlessy", () => {
|
||||
beforeEach(async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.LOST_AT_SEA);
|
||||
});
|
||||
|
||||
it("should have the correct properties", () => {
|
||||
const option3 = LostAtSeaEncounter.options[2];
|
||||
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
import * as MysteryEncounters 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 Battle from "#app/battle";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import * as BattleAnims from "#app/data/battle-anims";
|
||||
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { EncounterOptionMode } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounterTestUtils";
|
||||
import { CommandPhase, MovePhase, SelectModifierPhase } from "#app/phases";
|
||||
import { Moves } from "#enums/moves";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import * as Modifiers from "#app/modifier/modifier";
|
||||
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters/the-strong-stuff-encounter";
|
||||
import { Nature } from "#app/data/nature";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { PokemonMove } from "#app/field/pokemon";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
|
||||
const namespace = "mysteryEncounter:theStrongStuff";
|
||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.CAVE;
|
||||
const defaultWave = 45;
|
||||
|
||||
describe("The Strong Stuff - 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.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
|
||||
new Map<Biome, MysteryEncounterType[]>([
|
||||
[Biome.CAVE, [MysteryEncounterType.THE_STRONG_STUFF]],
|
||||
[Biome.MOUNTAIN, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
game.override.mysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
|
||||
expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||
expect(TheStrongStuffEncounter.dialogue).toBeDefined();
|
||||
expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
|
||||
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
|
||||
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
|
||||
expect(TheStrongStuffEncounter.options.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should not spawn outside of CAVE biome", async () => {
|
||||
game.override.startingBiome(Biome.MOUNTAIN);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
});
|
||||
|
||||
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 () => {
|
||||
vi.spyOn(scene, "currentBattle", "get").mockReturnValue({ mysteryEncounter: TheStrongStuffEncounter } as Battle);
|
||||
const moveInitSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||
const moveLoadSpy = vi.spyOn(BattleAnims, "loadMoveAnimAssets");
|
||||
|
||||
const { onInit } = TheStrongStuffEncounter;
|
||||
|
||||
expect(TheStrongStuffEncounter.onInit).toBeDefined();
|
||||
|
||||
const onInitResult = onInit(scene);
|
||||
|
||||
expect(TheStrongStuffEncounter.enemyPartyConfigs).toEqual([
|
||||
{
|
||||
levelAdditiveMultiplier: 1,
|
||||
disableSwitch: true,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
species: getPokemonSpecies(Species.SHUCKLE),
|
||||
isBoss: true,
|
||||
bossSegments: 5,
|
||||
spriteScale: 1.5,
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||
modifierTypes: expect.any(Array),
|
||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||
mysteryEncounterBattleEffects: expect.any(Function)
|
||||
}
|
||||
],
|
||||
}
|
||||
]);
|
||||
await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled());
|
||||
await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled());
|
||||
expect(onInitResult).toBe(true);
|
||||
});
|
||||
|
||||
describe("Option 1 - Power Swap BSTs", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = TheStrongStuffEncounter.options[0];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option1.dialogue).toBeDefined();
|
||||
expect(option1.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:1:label`,
|
||||
buttonTooltip: `${namespace}:option:1:tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option:1:selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should lower stats of highest BST and raise stats for rest of party", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
|
||||
const bstsPrior = scene.getParty().map(p => p.getSpeciesForm().getBaseStatTotal());
|
||||
await runSelectMysteryEncounterOption(game, 1);
|
||||
|
||||
const bstsAfter = scene.getParty().map(p => {
|
||||
return p.getSpeciesForm().getBaseStatTotal();
|
||||
});
|
||||
|
||||
expect(bstsAfter[0]).toEqual(bstsPrior[0] - 20 * 6);
|
||||
expect(bstsAfter[1]).toEqual(bstsPrior[1] + 10 * 6);
|
||||
expect(bstsAfter[2]).toEqual(bstsPrior[2] + 10 * 6);
|
||||
});
|
||||
|
||||
it("should leave encounter without battle", async () => {
|
||||
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 1);
|
||||
|
||||
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - battle the Shuckle", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option1 = TheStrongStuffEncounter.options[1];
|
||||
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT);
|
||||
expect(option1.dialogue).toBeDefined();
|
||||
expect(option1.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}:option:2:label`,
|
||||
buttonTooltip: `${namespace}:option:2:tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}:option:2:selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should start battle against Shuckle", async () => {
|
||||
const phaseSpy = vi.spyOn(scene, "pushPhase");
|
||||
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 2, true);
|
||||
|
||||
const enemyField = scene.getEnemyField();
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||
expect(enemyField.length).toBe(1);
|
||||
expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE);
|
||||
expect(enemyField[0].summonData.battleStats).toEqual([0, 2, 0, 2, 0, 0, 0]);
|
||||
const shuckleItems = scene.getModifiers(Modifiers.BerryModifier, false);
|
||||
expect(shuckleItems.length).toBe(4);
|
||||
expect(shuckleItems.find(m => m.berryType === BerryType.SITRUS)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m.berryType === BerryType.GANLON)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m.berryType === BerryType.APICOT)?.stackCount).toBe(1);
|
||||
expect(shuckleItems.find(m => m.berryType === BerryType.LUM)?.stackCount).toBe(2);
|
||||
expect(enemyField[0].moveset).toEqual([new PokemonMove(Moves.INFESTATION), new PokemonMove(Moves.SALT_CURE), new PokemonMove(Moves.GASTRO_ACID), new PokemonMove(Moves.HEAL_ORDER)]);
|
||||
|
||||
// Should have used moves pre-battle
|
||||
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
|
||||
expect(movePhases.length).toBe(2);
|
||||
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.GASTRO_ACID).length).toBe(1);
|
||||
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.STEALTH_ROCK).length).toBe(1);
|
||||
});
|
||||
|
||||
it("should have Soul Dew in rewards", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
await runSelectMysteryEncounterOption(game, 2, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||
expect(modifierSelectHandler.options.length).toEqual(3);
|
||||
expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("SOUL_DEW");
|
||||
});
|
||||
});
|
||||
});
|
|
@ -243,7 +243,7 @@ describe("Mystery Encounter Utils", () => {
|
|||
arceus.hp = 100;
|
||||
expect(arceus.isAllowedInBattle()).toBe(true);
|
||||
|
||||
koPlayerPokemon(arceus);
|
||||
koPlayerPokemon(scene, arceus);
|
||||
expect(arceus.isAllowedInBattle()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phases";
|
|||
import { OverridesHelper } from "./overridesHelper";
|
||||
import { expect } from "vitest";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { isNullOrUndefined } from "#app/utils";
|
||||
|
||||
/**
|
||||
* Class to manage the game state and transitions between phases.
|
||||
|
@ -151,6 +152,11 @@ export default class GameManager {
|
|||
* @returns A promise that resolves when the EncounterPhase ends.
|
||||
*/
|
||||
async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) {
|
||||
if (!isNullOrUndefined(encounterType)) {
|
||||
this.override.disableTrainerWave(true);
|
||||
this.override.mysteryEncounter(encounterType);
|
||||
}
|
||||
|
||||
await this.runToTitle();
|
||||
|
||||
this.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
|
@ -167,7 +173,7 @@ export default class GameManager {
|
|||
}, () => this.isCurrentPhase(MysteryEncounterPhase), true);
|
||||
|
||||
await this.phaseInterceptor.run(EncounterPhase);
|
||||
if (encounterType) {
|
||||
if (!isNullOrUndefined(encounterType)) {
|
||||
expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||
this.cursor = cursor;
|
||||
}
|
||||
|
||||
this.viewPartyIndex = this.optionsContainer.length - 1;
|
||||
this.viewPartyIndex = this.optionsContainer.list?.length - 1;
|
||||
|
||||
if (!this.cursorObj) {
|
||||
this.cursorObj = this.scene.add.image(0, 0, "cursor");
|
||||
|
@ -294,11 +294,11 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||
|
||||
if (cursor === this.viewPartyIndex) {
|
||||
this.cursorObj.setPosition(246, -17);
|
||||
} else if (this.optionsContainer.length === 3) { // 2 Options
|
||||
} else if (this.optionsContainer.list?.length === 3) { // 2 Options
|
||||
this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 15);
|
||||
} else if (this.optionsContainer.length === 4) { // 3 Options
|
||||
} else if (this.optionsContainer.list?.length === 4) { // 3 Options
|
||||
this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0));
|
||||
} else if (this.optionsContainer.length === 5) { // 4 Options
|
||||
} else if (this.optionsContainer.list?.length === 5) { // 4 Options
|
||||
this.cursorObj.setPosition(-10.5 + (cursor % 2 === 1 ? 100 : 0), 7 + (cursor > 1 ? 16 : 0));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue