This commit is contained in:
RedstonewolfX 2024-09-18 23:50:51 -07:00 committed by GitHub
commit 2e83236ee7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 278 additions and 29 deletions

View File

@ -1674,7 +1674,12 @@ export default class BattleScene extends SceneBase {
this.scoreText.setVisible(this.gameMode.isDaily); this.scoreText.setVisible(this.gameMode.isDaily);
} }
updateAndShowText(duration: integer): void { /**
* 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 ]; const labels = [ this.luckLabelText, this.luckText ];
labels.forEach(t => t.setAlpha(0)); labels.forEach(t => t.setAlpha(0));
const luckValue = getPartyLuckValue(this.getParty()); const luckValue = getPartyLuckValue(this.getParty());
@ -1685,12 +1690,16 @@ export default class BattleScene extends SceneBase {
this.luckText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); this.luckText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969);
} }
this.luckLabelText.setX((this.game.canvas.width / 6) - 2 - (this.luckText.displayWidth + 2)); 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({ this.tweens.add({
targets: labels, targets: labels,
duration: duration, duration: duration,
alpha: 1, alpha: 1,
onComplete: () => { onComplete: () => {
labels.forEach(t => t.setVisible(true)); labels.forEach(t => t.setVisible(!isDaily));
} }
}); });
} }

View File

@ -1699,7 +1699,8 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24), new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24),
new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)),
new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => {
if (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.EVIOLITE]) { const { gameMode, gameData } = party[0].scene;
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 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; return 0;
@ -1777,7 +1778,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.MULTI_LENS, 18), 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.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.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.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() && party[0].scene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))) ? 1 : 0, 1),
].map(m => { ].map(m => {
m.setTier(ModifierTier.MASTER); return m; m.setTier(ModifierTier.MASTER); return m;
}) })
@ -1969,9 +1970,13 @@ export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool
} }
const tierWeights = [ 768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ]; const tierWeights = [ 768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ];
export const itemPoolChecks: Map<ModifierTypeKeys, boolean> = new Map();
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) { export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) {
const pool = getModifierPoolForType(poolType); const pool = getModifierPoolForType(poolType);
itemPoolChecks.forEach((v, k) => {
itemPoolChecks.set(k, false);
});
const ignoredIndexes = {}; const ignoredIndexes = {};
const modifierTableData = {}; const modifierTableData = {};
@ -2008,6 +2013,9 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
ignoredIndexes[t].push(i++); ignoredIndexes[t].push(i++);
return total; return total;
} }
if (itemPoolChecks.has(modifierType.modifierType.id as ModifierTypeKeys)) {
itemPoolChecks.set(modifierType.modifierType.id as ModifierTypeKeys, true);
}
thresholds.set(total, i++); thresholds.set(total, i++);
return total; return total;
}, 0); }, 0);
@ -2410,8 +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 { export function getPartyLuckValue(party: Pokemon[]): integer {
const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0) if (party[0].scene.gameMode.isDaily) {
return 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); .reduce((total: integer, value: integer) => total += value, 0), 0, 14);
return luck || 0; return luck || 0;
} }

View File

@ -12,6 +12,7 @@ import { type PokeballCounts } from "./battle-scene";
import { Gender } from "./data/gender"; import { Gender } from "./data/gender";
import { Variant } from "./data/variant"; import { Variant } from "./data/variant";
import { type ModifierOverride } from "./modifier/modifier-type"; import { type ModifierOverride } from "./modifier/modifier-type";
import { Unlockables } from "./system/unlockables";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -70,8 +71,10 @@ class DefaultOverrides {
[PokeballType.MASTER_BALL]: 0, [PokeballType.MASTER_BALL]: 0,
}, },
}; };
/** Forces an item to be UNLOCKED */
readonly ITEM_UNLOCK_OVERRIDE: Unlockables[] = [];
/** Set to `true` to show all tutorials */ /** Set to `true` to show all tutorials */
readonly BYPASS_TUTORIAL_SKIP: boolean = false; readonly BYPASS_TUTORIAL_SKIP_OVERRIDE: boolean = false;
// ---------------- // ----------------
// PLAYER OVERRIDES // PLAYER OVERRIDES

View File

@ -76,7 +76,8 @@ export class TitlePhase extends Phase {
this.scene.ui.clearText(); this.scene.ui.clearText();
this.end(); this.end();
}; };
if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { const { gameData } = this.scene;
if (gameData.isUnlocked(Unlockables.ENDLESS_MODE)) {
const options: OptionSelectItem[] = [ const options: OptionSelectItem[] = [
{ {
label: GameMode.getModeName(GameModes.CLASSIC), label: GameMode.getModeName(GameModes.CLASSIC),
@ -100,7 +101,7 @@ export class TitlePhase extends Phase {
} }
} }
]; ];
if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) { if (gameData.isUnlocked(Unlockables.SPLICED_ENDLESS_MODE)) {
options.push({ options.push({
label: GameMode.getModeName(GameModes.SPLICED_ENDLESS), label: GameMode.getModeName(GameModes.SPLICED_ENDLESS),
handler: () => { handler: () => {
@ -220,6 +221,7 @@ export class TitlePhase extends Phase {
const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier())
.concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier()))
.concat([modifierTypes.MAP().withIdFromFunc(modifierTypes.MAP).newModifier()])
.concat(getDailyRunStarterModifiers(party)) .concat(getDailyRunStarterModifiers(party))
.filter((m) => m !== null); .filter((m) => m !== null);

View File

@ -372,6 +372,18 @@ export class GameData {
}; };
} }
/**
* 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.ITEM_UNLOCK_OVERRIDE.includes(unlockable)) {
return true;
}
return this.unlocks[unlockable];
}
public saveSystem(): Promise<boolean> { public saveSystem(): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
this.scene.ui.savingIcon.show(); this.scene.ui.savingIcon.show();

View File

@ -22,15 +22,25 @@ export function applySessionDataPatches(data: SessionSaveData) {
} else if (m.className === "PokemonResetNegativeStatStageModifier") { } else if (m.className === "PokemonResetNegativeStatStageModifier") {
m.className = "ResetNegativeStatStageModifier"; m.className = "ResetNegativeStatStageModifier";
} else if (m.className === "TempBattleStatBoosterModifier") { } else if (m.className === "TempBattleStatBoosterModifier") {
m.className = "TempStatStageBoosterModifier"; // Dire Hit no longer a part of the TempBattleStatBoosterModifierTypeGenerator
m.typeId = "TEMP_STAT_STAGE_BOOSTER"; if (m.typeId !== "DIRE_HIT") {
m.className = "TempStatStageBoosterModifier";
m.typeId = "TEMP_STAT_STAGE_BOOSTER";
// Migration from TempBattleStat to Stat // Migration from TempBattleStat to Stat
const newStat = m.typePregenArgs[0] + 1; const newStat = m.typePregenArgs[0] + 1;
m.typePregenArgs[0] = newStat; m.typePregenArgs[0] = newStat;
// From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ]
m.args = [ newStat, 5, m.args[1] ];
} else {
m.className = "TempCritBoosterModifier";
m.typePregenArgs = [];
// From [ stat, battlesLeft ] to [ maxBattles, battleCount ]
m.args = [ 5, m.args[1] ];
}
// From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ]
m.args = [ newStat, 5, m.args[1] ];
} else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) { } else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) {
let maxBattles: number; let maxBattles: number;
switch (m.typeId) { switch (m.typeId) {
@ -73,7 +83,7 @@ export function applySystemDataPatches(data: SystemSaveData) {
case "1.0.3": case "1.0.3":
case "1.0.4": case "1.0.4":
// --- LEGACY PATCHES --- // --- LEGACY PATCHES ---
if (data.starterData) { if (data.starterData && data.dexData) {
// Migrate ability starter data if empty for caught species // Migrate ability starter data if empty for caught species
Object.keys(data.starterData).forEach(sd => { Object.keys(data.starterData).forEach(sd => {
if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !data.starterData[sd].abilityAttr)) { if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !data.starterData[sd].abilityAttr)) {
@ -104,12 +114,14 @@ export function applySystemDataPatches(data: SystemSaveData) {
// --- PATCHES --- // --- PATCHES ---
// Fix Starter Data // Fix Starter Data
for (const starterId of defaultStarterSpecies) { if (data.starterData && data.dexData) {
if (data.starterData[starterId]?.abilityAttr) { for (const starterId of defaultStarterSpecies) {
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; if (data.starterData[starterId]?.abilityAttr) {
} data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
if (data.dexData[starterId]?.caughtAttr) { }
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE; if (data.dexData[starterId]?.caughtAttr) {
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
}
} }
} }
} }

View File

@ -1,5 +1,15 @@
import { MapModifier } from "#app/modifier/modifier";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "./utils/gameManager"; import GameManager from "./utils/gameManager";
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";
import Overrides from "#app/overrides";
//const TIMEOUT = 20 * 1000;
describe("Daily Mode", () => { describe("Daily Mode", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -28,5 +38,97 @@ describe("Daily Mode", () => {
expect(pkm.level).toBe(20); expect(pkm.level).toBe(20);
expect(pkm.moveset.length).toBeGreaterThan(0); expect(pkm.moveset.length).toBeGreaterThan(0);
}); });
expect(game.scene.getModifiers(MapModifier).length).toBeGreaterThan(0);
}); });
}); });
//*
// 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 () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.startingWave(9)
.startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather
.battleType("single")
.shinyLevel(true)
.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);
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
itemPoolChecks.clear();
});
it("should not have Eviolite and Mini Black Hole available in Classic if not unlocked", async () => {
await game.classicMode.runToSummon();
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);
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
expect(itemPoolChecks.get("EVIOLITE")).toBeFalsy();
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeFalsy();
});
});
it("should have Eviolite and Mini Black Hole available in Daily", async () => {
await game.dailyMode.runToSummon();
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);
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
expect(itemPoolChecks.get("EVIOLITE")).toBeTruthy();
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeTruthy();
});
});
it("should apply luck in Classic Mode", async () => {
await game.classicMode.runToSummon();
const party = game.scene.getParty();
expect(Overrides.SHINY_OVERRIDE).toBeTruthy();
expect(party[0]).toBeDefined();
expect(party[0].getLuck()).toBeGreaterThan(0);
expect(getPartyLuckValue(party)).toBeGreaterThan(0);
});
it("should not apply luck in Daily Run", async () => {
await game.dailyMode.runToSummon();
const party = game.scene.getParty();
expect(party[0]).toBeDefined();
expect(party[0].getLuck()).toBeGreaterThan(0);
expect(getPartyLuckValue(party)).toBe(0);
});
});
//*/

View File

@ -2,6 +2,7 @@ import { GameMode, GameModes, getGameMode } from "#app/game-mode";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import * as Utils from "../utils"; import * as Utils from "../utils";
import GameManager from "./utils/gameManager"; import GameManager from "./utils/gameManager";
describe("game-mode", () => { describe("game-mode", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
let game: GameManager; let game: GameManager;
@ -12,6 +13,7 @@ describe("game-mode", () => {
}); });
afterEach(() => { afterEach(() => {
game.phaseInterceptor.restoreOg(); game.phaseInterceptor.restoreOg();
vi.clearAllMocks();
vi.resetAllMocks(); vi.resetAllMocks();
}); });
beforeEach(() => { beforeEach(() => {

View File

@ -1,9 +1,13 @@
import { Species } from "#app/enums/species";
import { GameModes } from "#app/game-mode"; import { GameModes } from "#app/game-mode";
import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler";
import { Mode } from "#app/ui/ui";
import { Biome } from "#enums/biome";
import { Button } from "#enums/buttons";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { MockClock } from "#test/utils/mocks/mockClock";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { Moves } from "#app/enums/moves";
import { Biome } from "#app/enums/biome";
describe("Reload", () => { describe("Reload", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -50,6 +54,13 @@ describe("Reload", () => {
game.move.select(Moves.KOWTOW_CLEAVE); game.move.select(Moves.KOWTOW_CLEAVE);
await game.phaseInterceptor.to("DamagePhase"); await game.phaseInterceptor.to("DamagePhase");
await game.doKillOpponents(); await game.doKillOpponents();
game.onNextPrompt("SelectBiomePhase", Mode.OPTION_SELECT, () => {
(game.scene.time as MockClock).overrideDelay = null;
const optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
game.scene.time.delayedCall(1010, () => optionSelectUiHandler.processInput(Button.ACTION));
game.endPhase();
(game.scene.time as MockClock).overrideDelay = 1;
});
await game.toNextWave(); await game.toNextWave();
expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase"); expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase");

View File

@ -2,6 +2,7 @@ import { BerryType } from "#app/enums/berry-type";
import { Button } from "#app/enums/buttons"; import { Button } from "#app/enums/buttons";
import { Moves } from "#app/enums/moves"; import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species"; import { Species } from "#app/enums/species";
import { itemPoolChecks } from "#app/modifier/modifier-type";
import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
@ -94,3 +95,47 @@ describe("UI - Transfer Items", () => {
await game.phaseInterceptor.to(SelectModifierPhase); await game.phaseInterceptor.to(SelectModifierPhase);
}, 20000); }, 20000);
}); });
describe.skip("Backup 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);
});
});

View File

@ -10,6 +10,8 @@ import { ModifierOverride } from "#app/modifier/modifier-type";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { vi } from "vitest"; import { vi } from "vitest";
import { GameManagerHelper } from "./gameManagerHelper"; import { GameManagerHelper } from "./gameManagerHelper";
import { Unlockables } from "#app/system/unlockables";
import { Variant } from "#app/data/variant";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -300,6 +302,17 @@ export class OverridesHelper extends GameManagerHelper {
return this; return this;
} }
/**
* 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;
}
/** /**
* Override the items rolled at the end of a battle * Override the items rolled at the end of a battle
* @param items the items to be rolled * @param items the items to be rolled
@ -311,6 +324,25 @@ export class OverridesHelper extends GameManagerHelper {
return this; return this;
} }
/**
* Override player shininess
* @param shininess Whether the player's Pokemon should be shiny.
*/
shinyLevel(shininess: boolean): this {
vi.spyOn(Overrides, "SHINY_OVERRIDE", "get").mockReturnValue(shininess);
this.log(`Set player Pokemon as ${shininess ? "" : "not "}shiny!`);
return this;
}
/**
* Override player shiny variant
* @param variant The player's shiny variant.
*/
variantLevel(variant: Variant): this {
vi.spyOn(Overrides, "VARIANT_OVERRIDE", "get").mockReturnValue(variant);
this.log(`Set player Pokemon's shiny variant to ${variant}!`);
return this;
}
/** /**
* Override the enemy (Pokemon) to have the given amount of health segments * Override the enemy (Pokemon) to have the given amount of health segments
* @param healthSegments the number of segments to give * @param healthSegments the number of segments to give

View File

@ -43,6 +43,7 @@ import { UnavailablePhase } from "#app/phases/unavailable-phase";
import { VictoryPhase } from "#app/phases/victory-phase"; import { VictoryPhase } from "#app/phases/victory-phase";
import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase";
import UI, { Mode } from "#app/ui/ui"; import UI, { Mode } from "#app/ui/ui";
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
import { import {
MysteryEncounterBattlePhase, MysteryEncounterBattlePhase,
MysteryEncounterOptionSelectedPhase, MysteryEncounterOptionSelectedPhase,
@ -122,6 +123,7 @@ export default class PhaseInterceptor {
[EndEvolutionPhase, this.startPhase], [EndEvolutionPhase, this.startPhase],
[LevelCapPhase, this.startPhase], [LevelCapPhase, this.startPhase],
[AttemptRunPhase, this.startPhase], [AttemptRunPhase, this.startPhase],
[SelectBiomePhase, this.startPhase],
[MysteryEncounterPhase, this.startPhase], [MysteryEncounterPhase, this.startPhase],
[MysteryEncounterOptionSelectedPhase, this.startPhase], [MysteryEncounterOptionSelectedPhase, this.startPhase],
[MysteryEncounterBattlePhase, this.startPhase], [MysteryEncounterBattlePhase, this.startPhase],
@ -346,7 +348,8 @@ export default class PhaseInterceptor {
console.log("setMode", `${Mode[mode]} (=${mode})`, args); console.log("setMode", `${Mode[mode]} (=${mode})`, args);
const ret = this.originalSetMode.apply(instance, [mode, ...args]); const ret = this.originalSetMode.apply(instance, [mode, ...args]);
if (!this.phases[currentPhase.constructor.name]) { if (!this.phases[currentPhase.constructor.name]) {
throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list`); throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list --- Add it to PHASES inside of /test/utils/phaseInterceptor.ts`);
} }
if (this.phases[currentPhase.constructor.name].endBySetMode) { if (this.phases[currentPhase.constructor.name].endBySetMode) {
this.inProgress?.callback(); this.inProgress?.callback();

View File

@ -74,11 +74,11 @@ const tutorialHandlers = {
* @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise * @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise
*/ */
export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise<boolean> { export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise<boolean> {
if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP) { if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) {
return false; return false;
} }
if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) { if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) {
return false; return false;
} }

View File

@ -230,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 */ /* 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.showShopOverlay(750 * this.scene.gameSpeed);
this.scene.updateAndShowText(750); this.scene.updateAndShowText(750, this.scene.gameMode.isDaily);
this.scene.updateBiomeWaveText(); this.scene.updateBiomeWaveText();
this.scene.updateMoneyText(); this.scene.updateMoneyText();