diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 9787185b6ae..8665f1e97e5 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1674,6 +1674,11 @@ export default class BattleScene extends SceneBase { this.scoreText.setVisible(this.gameMode.isDaily); } + /** + * Displays the current luck value. + * @param duration The time for this label to fade in, if it is not already visible. + * @param isDaily If true, hides the label. (This is done because Luck does not apply in Daily Mode anymore) + */ updateAndShowText(duration: number, isDaily?: boolean): void { const labels = [ this.luckLabelText, this.luckText ]; labels.forEach(t => t.setAlpha(0)); @@ -1685,6 +1690,10 @@ export default class BattleScene extends SceneBase { this.luckText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); } this.luckLabelText.setX((this.game.canvas.width / 6) - 2 - (this.luckText.displayWidth + 2)); + if (isDaily) { + // Hide luck label + labels.forEach(t => t.setVisible(false)); + } this.tweens.add({ targets: labels, duration: duration, diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index c5e7ea8739e..859779977d5 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1700,7 +1700,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { const { gameMode, gameData } = party[0].scene; - if (party[0].scene.gameMode.isDaily || gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { + if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { 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; @@ -2418,11 +2418,16 @@ export class ModifierTypeOption { } } +/** + * Calculates the team's luck value. + * @param party The player's party. + * @returns A value between 0 and 14, or 0 if the player is in Daily Run mode. + */ export function getPartyLuckValue(party: Pokemon[]): integer { if (party[0].scene.gameMode.isDaily) { return 0; } - const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) + const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? (p.scene.gameMode.isDaily ? 0 : p.getLuck()) : 0) .reduce((total: integer, value: integer) => total += value, 0), 0, 14); return luck || 0; } diff --git a/src/overrides.ts b/src/overrides.ts index dc9de8990d0..6b5d89d9bdc 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -72,9 +72,9 @@ class DefaultOverrides { }, }; /** Forces an item to be UNLOCKED */ - readonly UNLOCK_OVERRIDE: Unlockables[] = []; + readonly ITEM_UNLOCK_OVERRIDE: Unlockables[] = []; /** Set to `true` to show all tutorials */ - readonly BYPASS_TUTORIAL_SKIP: boolean = false; + readonly BYPASS_TUTORIAL_SKIP_OVERRIDE: boolean = false; // ---------------- // PLAYER OVERRIDES diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 2c7084e808f..b23a5ec0c89 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -76,7 +76,8 @@ export class TitlePhase extends Phase { this.scene.ui.clearText(); this.end(); }; - if (this.scene.gameData.isUnlocked(Unlockables.ENDLESS_MODE)) { + const { gameData } = this.scene; + if (gameData.isUnlocked(Unlockables.ENDLESS_MODE)) { const options: OptionSelectItem[] = [ { label: GameMode.getModeName(GameModes.CLASSIC), @@ -100,7 +101,7 @@ export class TitlePhase extends Phase { } } ]; - if (this.scene.gameData.isUnlocked(Unlockables.SPLICED_ENDLESS_MODE)) { + if (gameData.isUnlocked(Unlockables.SPLICED_ENDLESS_MODE)) { options.push({ label: GameMode.getModeName(GameModes.SPLICED_ENDLESS), handler: () => { diff --git a/src/system/game-data.ts b/src/system/game-data.ts index d8af753f230..22a793e9aca 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -371,8 +371,14 @@ export class GameData { unlockPity: this.unlockPity.slice(0) }; } + + /** + * Checks if an `Unlockable` has been unlocked. + * @param unlockable The Unlockable to check + * @returns `true` if the player has unlocked this `Unlockable` or an override has enabled it + */ public isUnlocked(unlockable: Unlockables): boolean { - if (Overrides.UNLOCK_OVERRIDE.includes(unlockable)) { + if (Overrides.ITEM_UNLOCK_OVERRIDE.includes(unlockable)) { return true; } return this.unlocks[unlockable]; diff --git a/src/test/daily_mode.test.ts b/src/test/daily_mode.test.ts index 47142769c23..1b868cab116 100644 --- a/src/test/daily_mode.test.ts +++ b/src/test/daily_mode.test.ts @@ -1,9 +1,12 @@ import { MapModifier } from "#app/modifier/modifier"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "./utils/gameManager"; -//import { Abilities } from "#app/enums/abilities"; -//import { Moves } from "#app/enums/moves"; -//import { itemPoolChecks } from "#app/modifier/modifier-type"; +import { Moves } from "#app/enums/moves"; +import { getPartyLuckValue, itemPoolChecks } from "#app/modifier/modifier-type"; +import { Biome } from "#app/enums/biome"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { Mode } from "#app/ui/ui"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; //const TIMEOUT = 20 * 1000; @@ -38,7 +41,7 @@ describe("Daily Mode", () => { }); }); -/* +//* // Need to figure out how to properly start a battle // Need to fix eviolite - test keeps insisting it is not in loot table, even though Mini Black Hole (which is using the exact same condition) is describe("Shop modifications", async () => { @@ -54,10 +57,14 @@ describe("Shop modifications", async () => { game = new GameManager(phaserGame); game.override + .startingWave(9) + .startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather .battleType("single") - .startingLevel(200) - .moveset([Moves.SURF]) - .enemyAbility(Abilities.BALL_FETCH); + .startingLevel(100) // Avoid levelling up + .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() + .disableTrainerWaves() + .moveset([Moves.KOWTOW_CLEAVE]) + .enemyMoveset(Moves.SPLASH); itemPoolChecks.set("EVIOLITE", false); itemPoolChecks.set("MINI_BLACK_HOLE", false); }); @@ -66,5 +73,24 @@ describe("Shop modifications", async () => { game.phaseInterceptor.restoreOg(); itemPoolChecks.clear(); }); + + it("should do literally anything please god im begging you", async () => { + await game.dailyMode.runToSummon(); + const party = game.scene.getParty(); + expect(party[0]).toBeDefined(); + party[0].shiny = true; + expect(getPartyLuckValue(party)).toBe(0); + expect(itemPoolChecks.get("EVIOLITE")).toBeDefined(); + expect(itemPoolChecks.get("EVIOLITE")).toBeFalsy(); + expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined(); + expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeFalsy(); + game.move.select(Moves.KOWTOW_CLEAVE); + await game.phaseInterceptor.to("DamagePhase"); + await game.doKillOpponents(); + await game.phaseInterceptor.to(BattleEndPhase); + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); + }); + }); }); //*/ diff --git a/src/test/game-mode.test.ts b/src/test/game-mode.test.ts index 4032fc54911..75e76ac3b9d 100644 --- a/src/test/game-mode.test.ts +++ b/src/test/game-mode.test.ts @@ -2,8 +2,10 @@ import { GameMode, GameModes, getGameMode } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import * as Utils from "../utils"; import GameManager from "./utils/gameManager"; -//import { getPartyLuckValue } from "#app/modifier/modifier-type"; -//import { Species } from "#app/enums/species"; +import { getPartyLuckValue } from "#app/modifier/modifier-type"; +import { Species } from "#app/enums/species"; +import { getPokemonSpecies } from "#app/data/pokemon-species"; + describe("game-mode", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -14,6 +16,7 @@ describe("game-mode", () => { }); afterEach(() => { game.phaseInterceptor.restoreOg(); + vi.clearAllMocks(); vi.resetAllMocks(); }); beforeEach(() => { @@ -43,12 +46,7 @@ describe("game-mode", () => { expect(classicGameMode.isWaveTrainer(19, arena)).toBeFalsy(); }); }); - /* - // Need to figure out how to properly start a battle - describe("Luck Check", async () => { - let classicGameMode: GameMode; - let dailyGameMode: GameMode; - + describe.skip("Luck Check", async () => { beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -61,24 +59,29 @@ describe("game-mode", () => { beforeEach(() => { game = new GameManager(phaserGame); - classicGameMode = getGameMode(GameModes.CLASSIC); - dailyGameMode = getGameMode(GameModes.DAILY); }); it("applies luck in Classic", () => { game.override .shinyLevel(true, 2); game.classicMode.startBattle([Species.PICHU]); + game.scene.addPlayerPokemon(getPokemonSpecies(Species.PICHU), 5, undefined, undefined, undefined, true, 2); const party = game.scene.getParty(); - expect(getPartyLuckValue(party)).toBe(3); + const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) + .reduce((total: integer, value: integer) => total += value, 0), 0, 14); + expect(luck).toBeGreaterThan(0); + expect(getPartyLuckValue(party)).toBeGreaterThan(0); }); it("does not apply luck in Daily Runs", () => { game.override .shinyLevel(true, 2); game.dailyMode.startBattle(); + game.scene.addPlayerPokemon(getPokemonSpecies(Species.PICHU), 5, undefined, undefined, undefined, true, 2); const party = game.scene.getParty(); + const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) + .reduce((total: integer, value: integer) => total += value, 0), 0, 14); + expect(luck).toBeGreaterThan(0); expect(getPartyLuckValue(party)).toBe(0); }); }); - //*/ }); diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts index f7dea463574..f952cf4c59c 100644 --- a/src/test/ui/transfer-item.test.ts +++ b/src/test/ui/transfer-item.test.ts @@ -2,6 +2,7 @@ import { BerryType } from "#app/enums/berry-type"; import { Button } from "#app/enums/buttons"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; +import { itemPoolChecks } from "#app/modifier/modifier-type"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; @@ -94,3 +95,47 @@ describe("UI - Transfer Items", () => { await game.phaseInterceptor.to(SelectModifierPhase); }, 20000); }); + +describe("Test", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .startingLevel(100) + .startingWave(1) + .startingHeldItems([ + { name: "BERRY", count: 1, type: BerryType.SITRUS }, + { name: "BERRY", count: 2, type: BerryType.APICOT }, + { name: "BERRY", count: 2, type: BerryType.LUM }, + ]) + .moveset([Moves.DRAGON_CLAW]) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset([Moves.SPLASH]); + itemPoolChecks.set("EVIOLITE", false); + itemPoolChecks.set("MINI_BLACK_HOLE", false); + }); + it("should run", async () => { + await game.classicMode.startBattle([Species.RAYQUAZA, Species.RAYQUAZA, Species.RAYQUAZA]); + + game.move.select(Moves.DRAGON_CLAW); + + game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); + }); + + await game.phaseInterceptor.to(BattleEndPhase); + }); +}); diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index 0640d6c41ba..52b23d112bf 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -302,8 +302,13 @@ export class OverridesHelper extends GameManagerHelper { return this; } - unlockUnlockable(unlockable: Unlockables[]) { - vi.spyOn(Overrides, "UNLOCK_OVERRIDE", "get").mockReturnValue(unlockable); + /** + * Gives the player access to an Unlockable. + * @param unlockable The Unlockable to enable. + * @returns this + */ + enableUnlockable(unlockable: Unlockables[]) { + vi.spyOn(Overrides, "ITEM_UNLOCK_OVERRIDE", "get").mockReturnValue(unlockable); this.log("Temporarily unlocked the following content: ", unlockable); return this; } diff --git a/src/tutorial.ts b/src/tutorial.ts index 18d8291d227..3934ffee57f 100644 --- a/src/tutorial.ts +++ b/src/tutorial.ts @@ -74,11 +74,11 @@ const tutorialHandlers = { * @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise */ export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise { - if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP) { + if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) { return false; } - if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) { + if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) { return false; } diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index af4f0905b6d..521162b8842 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -13,7 +13,6 @@ import * as Utils from "./../utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import { GameModes } from "#app/game-mode"; import { IntegerHolder } from "./../utils"; import Phaser from "phaser"; @@ -231,7 +230,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { /* Multiplies the appearance duration by the speed parameter so that it is always constant, and avoids "flashbangs" at game speed x5 */ this.scene.showShopOverlay(750 * this.scene.gameSpeed); - this.scene.updateAndShowText(750, this.scene.gameMode.modeId === GameModes.DAILY); + this.scene.updateAndShowText(750, this.scene.gameMode.isDaily); this.scene.updateBiomeWaveText(); this.scene.updateMoneyText();