From f5b49d812d9858b3541c5ff5a3996598b3675344 Mon Sep 17 00:00:00 2001 From: RedstonewolfX <108761527+RedstonewolfX@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:56:26 -0400 Subject: [PATCH] Write shop test and add new overrides Adds new overrides that allow you to force content to be locked or unlocked These overrides were also added to the OverridesHelper to make them available to tests Adds a new check function for content unlocks, which returns `true` if it is overrode to be unlocked, `false` if it is overrode to be locked, and the unlock data mapped to a Boolean otherwise All existing checks (other than the ones that involve actually unlocking things) for unlockables have been changed to use this Added a pair of new exporting booleans, specifically for my test, that check if Eviolite or Mini Black Hole are in the loot table please forgive my jank --- src/modifier/modifier-type.ts | 16 +++++++-- src/overrides.ts | 5 +++ src/phases/title-phase.ts | 6 ++-- src/system/unlockables.ts | 12 +++++++ src/test/daily_mode.test.ts | 44 +++++++++++++++++++---- src/test/utils/helpers/overridesHelper.ts | 13 +++++++ 6 files changed, 83 insertions(+), 13 deletions(-) diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 20835e745f7..e2c1420565c 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -10,7 +10,7 @@ import PartyUiHandler, { PokemonMoveSelectFilter, PokemonSelectFilter } from ".. import * as Utils from "../utils"; import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName } from "../data/temp-battle-stat"; import { getBerryEffectDescription, getBerryName } from "../data/berry"; -import { Unlockables } from "../system/unlockables"; +import { isUnlocked, Unlockables } from "../system/unlockables"; import { StatusEffect, getStatusEffectDescriptor } from "../data/status-effect"; import { SpeciesFormKey } from "../data/pokemon-species"; import BattleScene from "../battle-scene"; @@ -1597,7 +1597,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { const { gameMode, gameData } = party[0].scene; - if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.unlocks[Unlockables.EVIOLITE])) { + if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && isUnlocked(Unlockables.EVIOLITE, gameData))) { return party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))) && !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier)) ? 10 : 0; } return 0; @@ -1675,7 +1675,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.MULTI_LENS, 18), new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5), new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24), - new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE])) ? 1 : 0, 1), + new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && isUnlocked(Unlockables.MINI_BLACK_HOLE, party[0].scene.gameData))) ? 1 : 0, 1), ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) @@ -1867,9 +1867,13 @@ export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool } const tierWeights = [ 768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ]; +export let poolHasEviolite: boolean = false; +export let poolHasBlackHole: boolean = false; export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) { const pool = getModifierPoolForType(poolType); + poolHasEviolite = false; + poolHasBlackHole = false; const ignoredIndexes = {}; const modifierTableData = {}; @@ -1906,6 +1910,12 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod ignoredIndexes[t].push(i++); return total; } + if (modifierType.modifierType.id === "EVIOLITE") { + poolHasEviolite = true; + } + if (modifierType.modifierType.id === "MINI_BLACK_HOLE") { + poolHasBlackHole = true; + } thresholds.set(total, i++); return total; }, 0); diff --git a/src/overrides.ts b/src/overrides.ts index 8b3d628e05e..811461a3c60 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -13,6 +13,7 @@ import { Gender } from "./data/gender"; import { allSpecies } from "./data/pokemon-species"; // eslint-disable-line @typescript-eslint/no-unused-vars import { Variant } from "./data/variant"; import { type ModifierOverride } from "./modifier/modifier-type"; +import { Unlockables } from "./system/unlockables"; /** * Overrides that are using when testing different in game situations @@ -69,6 +70,10 @@ class DefaultOverrides { [PokeballType.MASTER_BALL]: 0, }, }; + /** Forces an item to be UNLOCKED */ + readonly UNLOCK_OVERRIDE: Unlockables[] = []; + /** Forces an item to be NOT UNLOCKED */ + readonly DISABLE_UNLOCK_OVERRIDE: Unlockables[] = []; // ---------------- // PLAYER OVERRIDES diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index c755f0c0feb..023fd382558 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -8,7 +8,7 @@ import { GameModes, GameMode, getGameMode } from "#app/game-mode.js"; import { regenerateModifierPoolThresholds, ModifierPoolType, modifierTypes, getDailyRunStarterModifiers } from "#app/modifier/modifier-type.js"; import { Phase } from "#app/phase.js"; import { SessionSaveData } from "#app/system/game-data.js"; -import { Unlockables } from "#app/system/unlockables.js"; +import { isUnlocked, Unlockables } from "#app/system/unlockables.js"; import { vouchers } from "#app/system/voucher.js"; import { OptionSelectItem, OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler.js"; import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler.js"; @@ -76,7 +76,7 @@ export class TitlePhase extends Phase { this.scene.ui.clearText(); this.end(); }; - if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { + if (isUnlocked(Unlockables.ENDLESS_MODE, this.scene.gameData)) { const options: OptionSelectItem[] = [ { label: GameMode.getModeName(GameModes.CLASSIC), @@ -100,7 +100,7 @@ export class TitlePhase extends Phase { } } ]; - if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { + if (isUnlocked(Unlockables.SPLICED_ENDLESS_MODE, this.scene.gameData)) { options.push({ label: GameMode.getModeName(GameModes.SPLICED_ENDLESS), handler: () => { diff --git a/src/system/unlockables.ts b/src/system/unlockables.ts index 983909373fd..81f390afdb0 100644 --- a/src/system/unlockables.ts +++ b/src/system/unlockables.ts @@ -1,5 +1,7 @@ import i18next from "i18next"; import { GameMode, GameModes } from "../game-mode"; +import Overrides from "#app/overrides"; +import { GameData } from "./game-data"; export enum Unlockables { ENDLESS_MODE, @@ -8,6 +10,16 @@ export enum Unlockables { EVIOLITE } +export function isUnlocked(unlockable: Unlockables, gameData: GameData): boolean { + if (Overrides.UNLOCK_OVERRIDE.includes(unlockable)) { + return true; + } + if (Overrides.DISABLE_UNLOCK_OVERRIDE.includes(unlockable)) { + return false; + } + return !!gameData.unlocks[Unlockables.MINI_BLACK_HOLE]; +} + export function getUnlockableName(unlockable: Unlockables) { switch (unlockable) { case Unlockables.ENDLESS_MODE: diff --git a/src/test/daily_mode.test.ts b/src/test/daily_mode.test.ts index ebc19c0b6de..6ddabae3285 100644 --- a/src/test/daily_mode.test.ts +++ b/src/test/daily_mode.test.ts @@ -1,6 +1,11 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; import { MapModifier } from "#app/modifier/modifier.js"; +import { SelectModifierPhase } from "../phases/select-modifier-phase"; +import { Moves } from "#app/enums/moves.js"; +import { Abilities } from "#app/enums/abilities.js"; +import { Unlockables } from "#app/system/unlockables.js"; +import { poolHasEviolite, poolHasBlackHole } from "#app/modifier/modifier-type.js"; describe("Daily Mode", () => { let phaserGame: Phaser.Game; @@ -32,13 +37,38 @@ describe("Daily Mode", () => { expect(game.scene.getModifiers(MapModifier).length).toBeGreaterThan(0); }); - /* - Can't figure out how to check the shop's item pool :( describe("Shop modifications", async () => { - const modifierPhase = new SelectModifierPhase(game.scene); - game.scene.unshiftPhase(modifierPhase); - await game.phaseInterceptor.run(SelectModifierPhase); - expect(getModifierThresholdPool(ModifierPoolType.PLAYER)); + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override + .battleType("single") + .startingLevel(200) + .moveset([Moves.SURF]) + .enemyAbility(Abilities.BALL_FETCH) + .startingModifier([{ name: "LOCK_CAPSULE" }]) + .lockUnlockable([Unlockables.MINI_BLACK_HOLE, Unlockables.EVIOLITE]); + }); + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + it("should only allow Mini Black Hole and Eviolite outside of Daily if unlocked", async () => { + await game.classicMode.runToSummon(); + await game.startBattle(); + + game.move.select(Moves.SURF); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(poolHasEviolite).toBeFalsy(); + expect(poolHasBlackHole).toBeFalsy(); + }); + it("should allow Eviolite and Mini Black Hole in shop when in Daily Run", async () => { + await game.dailyMode.runToSummon(); + await game.startBattle(); + + game.move.select(Moves.SURF); + await game.phaseInterceptor.to(SelectModifierPhase, false); + expect(poolHasEviolite).toBeTruthy(); + expect(poolHasBlackHole).toBeTruthy(); + }); }); - */ }); diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index d5eaee003db..505641e7e06 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -10,6 +10,7 @@ import { ModifierOverride } from "#app/modifier/modifier-type.js"; import Overrides from "#app/overrides"; import { vi } from "vitest"; import { GameManagerHelper } from "./gameManagerHelper"; +import { Unlockables } from "#app/system/unlockables.js"; /** * Helper to handle overrides in tests @@ -281,6 +282,18 @@ export class OverridesHelper extends GameManagerHelper { return this; } + unlockUnlockable(unlockable: Unlockables[]) { + vi.spyOn(Overrides, "UNLOCK_OVERRIDE", "get").mockReturnValue(unlockable); + this.log("Temporarily unlocked the following content: ", unlockable); + return this; + } + + lockUnlockable(unlockable: Unlockables[]) { + vi.spyOn(Overrides, "DISABLE_UNLOCK_OVERRIDE", "get").mockReturnValue(unlockable); + this.log("Temporarily re-locked the following content: ", unlockable); + return this; + } + private log(...params: any[]) { console.log("Overrides:", ...params); }