add unit tests for pokemon salesman

This commit is contained in:
ImperialSympathizer 2024-07-22 16:10:26 -04:00
parent 792af86f39
commit 0c45194892
9 changed files with 193 additions and 9 deletions

View File

@ -94,7 +94,7 @@ export const PokemonSalesmanEncounter: IMysteryEncounter =
encounter.setDialogueToken("purchasePokemon", pokemon.name); encounter.setDialogueToken("purchasePokemon", pokemon.name);
encounter.setDialogueToken("price", price.toString()); encounter.setDialogueToken("price", price.toString());
encounter.misc = { encounter.misc = {
money: price, price: price,
pokemon: pokemon pokemon: pokemon
}; };
@ -118,11 +118,11 @@ export const PokemonSalesmanEncounter: IMysteryEncounter =
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter; const encounter = scene.currentBattle.mysteryEncounter;
const cost = encounter.misc.money; const price = encounter.misc.price;
const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon; const purchasedPokemon = encounter.misc.pokemon as PlayerPokemon;
// Update money // Update money
updatePlayerMoney(scene, -cost, true, false); updatePlayerMoney(scene, -price, true, false);
// Show dialogue // Show dialogue
await showEncounterDialogue(scene, `${namespace}:option:1:selected_dialogue`, `${namespace}:speaker`); await showEncounterDialogue(scene, `${namespace}:option:1:selected_dialogue`, `${namespace}:speaker`);

View File

@ -316,6 +316,7 @@ export function initCustomMovesForEncounter(scene: BattleScene, moves: Moves | M
* @param scene - Battle Scene * @param scene - Battle Scene
* @param changeValue * @param changeValue
* @param playSound * @param playSound
* @param showMessage
*/ */
export function updatePlayerMoney(scene: BattleScene, changeValue: number, playSound: boolean = true, showMessage: boolean = true) { export function updatePlayerMoney(scene: BattleScene, changeValue: number, playSound: boolean = true, showMessage: boolean = true) {
scene.money = Math.min(Math.max(scene.money + changeValue, 0), Number.MAX_SAFE_INTEGER); scene.money = Math.min(Math.max(scene.money + changeValue, 0), Number.MAX_SAFE_INTEGER);

View File

@ -173,6 +173,9 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
} }
}); });
// Load dex progress icon
this.scene.loadAtlas("encounter_radar", "mystery-encounters");
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => { this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
this.spriteConfigs.every((config) => { this.spriteConfigs.every((config) => {
if (config.isItem) { if (config.isItem) {

View File

@ -273,8 +273,6 @@ export class LoadingScene extends SceneBase {
this.loadAtlas("xbox", "inputs"); this.loadAtlas("xbox", "inputs");
this.loadAtlas("keyboard", "inputs"); this.loadAtlas("keyboard", "inputs");
this.loadAtlas("encounter_radar", "mystery-encounters");
this.loadSe("select"); this.loadSe("select");
this.loadSe("menu_open"); this.loadSe("menu_open");
this.loadSe("hit"); this.loadSe("hit");

View File

@ -117,9 +117,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0;
*/ */
// 1 to 256, set to null to ignore // 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_TIER_OVERRIDE: MysteryEncounterTier = null;
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.POKEMON_SALESMAN; export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null;
/** /**
* MODIFIER / ITEM OVERRIDES * MODIFIER / ITEM OVERRIDES

View File

@ -0,0 +1,171 @@
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 * 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 BattleScene from "#app/battle-scene";
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter";
import { PlayerPokemon } from "#app/field/pokemon";
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
import { PokemonSalesmanEncounter } from "#app/data/mystery-encounters/encounters/pokemon-salesman-encounter";
const namespace = "mysteryEncounter:pokemonSalesman";
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
const defaultBiome = Biome.CAVE;
const defaultWave = 45;
describe("The Pokemon Salesman - 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);
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
[Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
]);
HUMAN_TRANSITABLE_BIOMES.forEach(biome => {
biomeMap.set(biome, [MysteryEncounterType.POKEMON_SALESMAN]);
});
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.POKEMON_SALESMAN, defaultParty);
expect(PokemonSalesmanEncounter.encounterType).toBe(MysteryEncounterType.POKEMON_SALESMAN);
expect(PokemonSalesmanEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA);
expect(PokemonSalesmanEncounter.dialogue).toBeDefined();
expect(PokemonSalesmanEncounter.dialogue.intro).toStrictEqual([
{ text: `${namespace}:intro` },
{ speaker: "mysteryEncounter:pokemonSalesman:speaker", text: "mysteryEncounter:pokemonSalesman:intro_dialogue" }
]);
expect(PokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.title).toBe(`${namespace}:title`);
expect(PokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.description).toBe(`${namespace}:description`);
expect(PokemonSalesmanEncounter.dialogue.encounterOptionsDialogue.query).toBe(`${namespace}:query`);
expect(PokemonSalesmanEncounter.options.length).toBe(2);
});
it("should not spawn outside of HUMAN_TRANSITABLE_BIOMES", async () => {
game.override.startingBiome(Biome.VOLCANO);
await game.runToMysteryEncounter();
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.POKEMON_SALESMAN);
});
it("should not run below wave 10", async () => {
game.override.startingWave(9);
await game.runToMysteryEncounter();
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.POKEMON_SALESMAN);
});
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: PokemonSalesmanEncounter } as Battle);
const { onInit } = PokemonSalesmanEncounter;
expect(PokemonSalesmanEncounter.onInit).toBeDefined();
const onInitResult = onInit(scene);
expect(PokemonSalesmanEncounter.dialogueTokens?.purchasePokemon).toBeDefined();
expect(PokemonSalesmanEncounter.dialogueTokens?.price).toBeDefined();
expect(PokemonSalesmanEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy();
expect(PokemonSalesmanEncounter.misc?.price?.toString()).toBe(PokemonSalesmanEncounter.dialogueTokens?.price);
expect(onInitResult).toBe(true);
});
describe("Option 1 - Purchase the pokemon", () => {
it("should have the correct properties", () => {
const option1 = PokemonSalesmanEncounter.options[0];
expect(option1.optionMode).toBe(EncounterOptionMode.DEFAULT_OR_SPECIAL);
expect(option1.dialogue).toBeDefined();
expect(option1.dialogue).toStrictEqual({
buttonLabel: `${namespace}:option:1:label`,
buttonTooltip: `${namespace}:option:1:tooltip`,
selected: [
{
text: `${namespace}:option:1:selected_message`,
},
],
});
});
it("Should update the player's money properly", async () => {
const initialMoney = 20000;
scene.money = initialMoney;
const updateMoneySpy = vi.spyOn(EncounterPhaseUtils, "updatePlayerMoney");
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
await runSelectMysteryEncounterOption(game, 1);
const price = scene.currentBattle.mysteryEncounter.misc.price;
expect(updateMoneySpy).toHaveBeenCalledWith(scene, -price, true, false);
expect(scene.money).toBe(initialMoney - price);
});
it("Should add the Pokemon to the party", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
const initialPartySize = scene.getParty().length;
const pokemonName = scene.currentBattle.mysteryEncounter.misc.pokemon.name;
await runSelectMysteryEncounterOption(game, 1);
expect(scene.getParty().length).toBe(initialPartySize + 1);
expect(scene.getParty().find(p => p.name === pokemonName) instanceof PlayerPokemon).toBeTruthy();
});
it("should leave encounter without battle", async () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
await runSelectMysteryEncounterOption(game, 1);
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
});
});
describe("Option 2 - Leave", () => {
it("should leave encounter without battle", async () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.POKEMON_SALESMAN, defaultParty);
await runSelectMysteryEncounterOption(game, 2);
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
});
});
});

View File

@ -22,6 +22,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { PokemonMove } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
import { PokemonBaseStatTotalModifier } from "#app/modifier/modifier";
const namespace = "mysteryEncounter:theStrongStuff"; const namespace = "mysteryEncounter:theStrongStuff";
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
@ -153,7 +154,9 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
const bstsAfter = scene.getParty().map(p => { const bstsAfter = scene.getParty().map(p => {
return p.getSpeciesForm().getBaseStatTotal(); const baseStats = p.getSpeciesForm().baseStats.slice(0);
scene.applyModifiers(PokemonBaseStatTotalModifier, true, p, baseStats);
return baseStats.reduce((a, b) => a + b);
}); });
expect(bstsAfter[0]).toEqual(bstsPrior[0] - 20 * 6); expect(bstsAfter[0]).toEqual(bstsPrior[0] - 20 * 6);

View File

@ -298,7 +298,7 @@ describe("Mystery Encounter Utils", () => {
const spy = vi.spyOn(game.scene.ui, "showDialogue"); const spy = vi.spyOn(game.scene.ui, "showDialogue");
showEncounterDialogue(scene, "mysteryEncounter:unit_test_dialogue", "mysteryEncounter:unit_test_dialogue"); showEncounterDialogue(scene, "mysteryEncounter:unit_test_dialogue", "mysteryEncounter:unit_test_dialogue");
expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", "valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, undefined, 0, 0); expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", "valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, expect.any(Function), 0);
}); });
}); });

View File

@ -248,6 +248,14 @@ export default class MockText {
}; };
} }
disableInteractive() {
// Disables interaction with this Game Object.
}
clearTint() {
// Clears tint on this Game Object.
}
add(obj) { add(obj) {
// Adds a child to this Game Object. // Adds a child to this Game Object.
this.list.push(obj); this.list.push(obj);