[Bug] Fix daily run generated movesets (#3519)

* Fix daily run generated movesets

* add unit-tests for `fix-daily-run` (#3)

* add `daily` and `classic` helper. split `runToSummon`

there is now a `classicMode.runToSummon` which represents the old one used.
There is now a `dailyMode.runToSummon` too

* add daily_mode.test.ts

covers the occured issue

---------

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
This commit is contained in:
NightKev 2024-08-13 13:48:56 -07:00 committed by GitHub
parent 39f617d58d
commit 0d3fcd82bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 139 additions and 50 deletions

View File

@ -3255,10 +3255,13 @@ export class PlayerPokemon extends Pokemon {
this.variant = Overrides.VARIANT_OVERRIDE; this.variant = Overrides.VARIANT_OVERRIDE;
} }
} }
if (!dataSource) { if (!dataSource) {
if (this.scene.gameMode.isDaily) {
this.generateAndPopulateMoveset();
} else {
this.moveset = []; this.moveset = [];
} }
}
this.generateCompatibleTms(); this.generateCompatibleTms();
} }

View File

@ -39,7 +39,7 @@ describe("Ability Timing", () => {
it("should trigger after switch check", async() => { it("should trigger after switch check", async() => {
initI18n(); initI18n();
i18next.changeLanguage("en"); i18next.changeLanguage("en");
await game.runToSummon([Species.EEVEE, Species.FEEBAS]); await game.classicMode.runToSummon([Species.EEVEE, Species.FEEBAS]);
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
game.setMode(Mode.MESSAGE); game.setMode(Mode.MESSAGE);

View File

@ -39,7 +39,7 @@ describe("Abilities - Intimidate", () => {
}); });
it("single - wild with switch", async () => { it("single - wild with switch", async () => {
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -69,7 +69,7 @@ describe("Abilities - Intimidate", () => {
it("single - boss should only trigger once then switch", async () => { it("single - boss should only trigger once then switch", async () => {
game.override.startingWave(10); game.override.startingWave(10);
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -98,7 +98,7 @@ describe("Abilities - Intimidate", () => {
it("single - trainer should only trigger once with switch", async () => { it("single - trainer should only trigger once with switch", async () => {
game.override.startingWave(5); game.override.startingWave(5);
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -128,7 +128,7 @@ describe("Abilities - Intimidate", () => {
it("double - trainer should only trigger once per pokemon", async () => { it("double - trainer should only trigger once per pokemon", async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.startingWave(5); game.override.startingWave(5);
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -154,7 +154,7 @@ describe("Abilities - Intimidate", () => {
it("double - wild: should only trigger once per pokemon", async () => { it("double - wild: should only trigger once per pokemon", async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.startingWave(3); game.override.startingWave(3);
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -180,7 +180,7 @@ describe("Abilities - Intimidate", () => {
it("double - boss: should only trigger once per pokemon", async () => { it("double - boss: should only trigger once per pokemon", async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.startingWave(10); game.override.startingWave(10);
await game.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]);
game.onNextPrompt( game.onNextPrompt(
"CheckSwitchPhase", "CheckSwitchPhase",
Mode.CONFIRM, Mode.CONFIRM,
@ -334,7 +334,7 @@ describe("Abilities - Intimidate", () => {
it("double - wild vs only 1 on player side", async () => { it("double - wild vs only 1 on player side", async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.startingWave(3); game.override.startingWave(3);
await game.runToSummon([Species.MIGHTYENA]); await game.classicMode.runToSummon([Species.MIGHTYENA]);
await game.phaseInterceptor.to(CommandPhase, false); await game.phaseInterceptor.to(CommandPhase, false);
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);

View File

@ -30,7 +30,7 @@ describe("Abilities - Intrepid Sword", () => {
}); });
it("INTREPID SWORD on player", async() => { it("INTREPID SWORD on player", async() => {
await game.runToSummon([ await game.classicMode.runToSummon([
Species.ZACIAN, Species.ZACIAN,
]); ]);
await game.phaseInterceptor.to(CommandPhase, false); await game.phaseInterceptor.to(CommandPhase, false);

View File

@ -0,0 +1,32 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "./utils/gameManager";
describe("Daily Mode", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
beforeEach(() => {
game = new GameManager(phaserGame);
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
it("should initialize properly", async () => {
await game.dailyMode.runToSummon();
const party = game.scene.getParty();
expect(party).toHaveLength(3);
party.forEach(pkm => {
expect(pkm.level).toBe(20);
expect(pkm.moveset.length).toBeGreaterThan(0);
});
});
});

View File

@ -32,7 +32,7 @@ describe("Evolution", () => {
}); });
it("should keep hidden ability after evolving", async () => { it("should keep hidden ability after evolving", async () => {
await game.runToSummon([Species.EEVEE, Species.TRAPINCH]); await game.classicMode.runToSummon([Species.EEVEE, Species.TRAPINCH]);
const eevee = game.scene.getParty()[0]; const eevee = game.scene.getParty()[0];
const trapinch = game.scene.getParty()[1]; const trapinch = game.scene.getParty()[1];
@ -47,7 +47,7 @@ describe("Evolution", () => {
}, TIMEOUT); }, TIMEOUT);
it("should keep same ability slot after evolving", async () => { it("should keep same ability slot after evolving", async () => {
await game.runToSummon([Species.BULBASAUR, Species.CHARMANDER]); await game.classicMode.runToSummon([Species.BULBASAUR, Species.CHARMANDER]);
const bulbasaur = game.scene.getParty()[0]; const bulbasaur = game.scene.getParty()[0];
const charmander = game.scene.getParty()[1]; const charmander = game.scene.getParty()[1];
@ -62,7 +62,7 @@ describe("Evolution", () => {
}, TIMEOUT); }, TIMEOUT);
it("should handle illegal abilityIndex values", async () => { it("should handle illegal abilityIndex values", async () => {
await game.runToSummon([Species.SQUIRTLE]); await game.classicMode.runToSummon([Species.SQUIRTLE]);
const squirtle = game.scene.getPlayerPokemon()!; const squirtle = game.scene.getPlayerPokemon()!;
squirtle.abilityIndex = 5; squirtle.abilityIndex = 5;
@ -72,7 +72,7 @@ describe("Evolution", () => {
}, TIMEOUT); }, TIMEOUT);
it("should handle nincada's unique evolution", async () => { it("should handle nincada's unique evolution", async () => {
await game.runToSummon([Species.NINCADA]); await game.classicMode.runToSummon([Species.NINCADA]);
const nincada = game.scene.getPlayerPokemon()!; const nincada = game.scene.getPlayerPokemon()!;
nincada.abilityIndex = 2; nincada.abilityIndex = 2;

View File

@ -21,7 +21,7 @@ describe("Spec - Pokemon", () => {
}); });
it("should not crash when trying to set status of undefined", async () => { it("should not crash when trying to set status of undefined", async () => {
await game.runToSummon([Species.ABRA]); await game.classicMode.runToSummon([Species.ABRA]);
const pkm = game.scene.getPlayerPokemon()!; const pkm = game.scene.getPlayerPokemon()!;
expect(pkm).toBeDefined(); expect(pkm).toBeDefined();

View File

@ -23,7 +23,7 @@ describe("Internals", () => {
}); });
it("should provide Eevee with 3 defined abilities", async () => { it("should provide Eevee with 3 defined abilities", async () => {
await game.runToSummon([Species.EEVEE]); await game.classicMode.runToSummon([Species.EEVEE]);
const eevee = game.scene.getPlayerPokemon()!; const eevee = game.scene.getPlayerPokemon()!;
expect(eevee.getSpeciesForm().getAbilityCount()).toBe(3); expect(eevee.getSpeciesForm().getAbilityCount()).toBe(3);
@ -34,7 +34,7 @@ describe("Internals", () => {
}); });
it("should set Eeeve abilityIndex between 0-2", async () => { it("should set Eeeve abilityIndex between 0-2", async () => {
await game.runToSummon([Species.EEVEE]); await game.classicMode.runToSummon([Species.EEVEE]);
const eevee = game.scene.getPlayerPokemon()!; const eevee = game.scene.getPlayerPokemon()!;
expect(eevee.abilityIndex).toBeGreaterThanOrEqual(0); expect(eevee.abilityIndex).toBeGreaterThanOrEqual(0);

View File

@ -88,7 +88,7 @@ async function checkTargetMultiplier(game: GameManager, attackMove: Moves, killA
async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAlly: boolean, killSecondEnemy: boolean, shouldDecreased: boolean, ability?: Abilities) { async function checkDamageDecrease(game: GameManager, attackMove: Moves, killAlly: boolean, killSecondEnemy: boolean, shouldDecreased: boolean, ability?: Abilities) {
// Tested combination on first turn, 1v1 on second turn // Tested combination on first turn, 1v1 on second turn
await game.runToSummon([Species.EEVEE, Species.EEVEE]); await game.classicMode.runToSummon([Species.EEVEE, Species.EEVEE]);
if (ability !== undefined) { if (ability !== undefined) {
game.scene.getPlayerField()[1].abilityIndex = ability; game.scene.getPlayerField()[1].abilityIndex = ability;

View File

@ -39,7 +39,7 @@ describe("Moves - Spikes", () => {
// player set spikes on the field and do splash for 3 turns // player set spikes on the field and do splash for 3 turns
// opponent do splash for 4 turns // opponent do splash for 4 turns
// nobody should take damage // nobody should take damage
await game.runToSummon([ await game.classicMode.runToSummon([
Species.MIGHTYENA, Species.MIGHTYENA,
Species.POOCHYENA, Species.POOCHYENA,
]); ]);
@ -64,7 +64,7 @@ describe("Moves - Spikes", () => {
// player set spikes on the field and switch back to back // player set spikes on the field and switch back to back
// opponent do splash for 2 turns // opponent do splash for 2 turns
// nobody should take damage // nobody should take damage
await game.runToSummon([ await game.classicMode.runToSummon([
Species.MIGHTYENA, Species.MIGHTYENA,
Species.POOCHYENA, Species.POOCHYENA,
]); ]);
@ -87,7 +87,7 @@ describe("Moves - Spikes", () => {
// player set spikes on the field and do splash for 3 turns // player set spikes on the field and do splash for 3 turns
// opponent do splash for 4 turns // opponent do splash for 4 turns
// nobody should take damage // nobody should take damage
await game.runToSummon([ await game.classicMode.runToSummon([
Species.MIGHTYENA, Species.MIGHTYENA,
Species.POOCHYENA, Species.POOCHYENA,
]); ]);
@ -107,7 +107,7 @@ describe("Moves - Spikes", () => {
// turn 1: player set spikes, opponent do splash // turn 1: player set spikes, opponent do splash
// turn 2: player do splash, opponent switch pokemon // turn 2: player do splash, opponent switch pokemon
// opponent pokemon should trigger spikes and lose HP // opponent pokemon should trigger spikes and lose HP
await game.runToSummon([ await game.classicMode.runToSummon([
Species.MIGHTYENA, Species.MIGHTYENA,
Species.POOCHYENA, Species.POOCHYENA,
]); ]);

View File

@ -23,12 +23,13 @@ import { Species } from "#enums/species";
import { Button } from "#enums/buttons"; import { Button } from "#enums/buttons";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js";
import { OverridesHelper } from "./overridesHelper"; import { OverridesHelper } from "./helpers/overridesHelper";
import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js"; import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type.js";
import overrides from "#app/overrides.js";
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js";
import { MoveHelper } from "./moveHelper"; import { MoveHelper } from "./helpers/moveHelper";
import { vi } from "vitest"; import { vi } from "vitest";
import { ClassicModeHelper } from "./helpers/classicModeHelper";
import { DailyModeHelper } from "./helpers/dailyModeHelper";
/** /**
* Class to manage the game state and transitions between phases. * Class to manage the game state and transitions between phases.
@ -41,6 +42,8 @@ export default class GameManager {
public inputsHandler: InputsHandler; public inputsHandler: InputsHandler;
public readonly override: OverridesHelper; public readonly override: OverridesHelper;
public readonly move: MoveHelper; public readonly move: MoveHelper;
public readonly classicMode: ClassicModeHelper;
public readonly dailyMode: DailyModeHelper;
/** /**
* Creates an instance of GameManager. * Creates an instance of GameManager.
@ -58,6 +61,8 @@ export default class GameManager {
this.gameWrapper.setScene(this.scene); this.gameWrapper.setScene(this.scene);
this.override = new OverridesHelper(this); this.override = new OverridesHelper(this);
this.move = new MoveHelper(this); this.move = new MoveHelper(this);
this.classicMode = new ClassicModeHelper(this);
this.dailyMode = new DailyModeHelper(this);
} }
/** /**
@ -118,28 +123,6 @@ export default class GameManager {
} }
/**
* Runs the game to the summon phase.
* @param species - Optional array of species to summon.
* @returns A promise that resolves when the summon phase is reached.
*/
async runToSummon(species?: Species[]) {
await this.runToTitle();
this.onNextPrompt("TitlePhase", Mode.TITLE, () => {
this.scene.gameMode = getGameMode(GameModes.CLASSIC);
const starters = generateStarter(this.scene, species);
const selectStarterPhase = new SelectStarterPhase(this.scene);
this.scene.pushPhase(new EncounterPhase(this.scene, false));
selectStarterPhase.initBattle(starters);
});
await this.phaseInterceptor.run(EncounterPhase);
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) {
this.removeEnemyHeldItems();
}
}
/** /**
* Helper function to run to the final boss encounter as it's a bit tricky due to extra dialogue * Helper function to run to the final boss encounter as it's a bit tricky due to extra dialogue
* Also handles Major/Minor bosses from endless modes * Also handles Major/Minor bosses from endless modes
@ -178,7 +161,7 @@ export default class GameManager {
* @returns A promise that resolves when the battle is started. * @returns A promise that resolves when the battle is started.
*/ */
async startBattle(species?: Species[]) { async startBattle(species?: Species[]) {
await this.runToSummon(species); await this.classicMode.runToSummon(species);
this.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { this.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
this.setMode(Mode.MESSAGE); this.setMode(Mode.MESSAGE);

View File

@ -0,0 +1,35 @@
import { Species } from "#app/enums/species.js";
import { GameModes, getGameMode } from "#app/game-mode.js";
import overrides from "#app/overrides.js";
import { EncounterPhase, SelectStarterPhase } from "#app/phases.js";
import { Mode } from "#app/ui/ui.js";
import { generateStarter } from "../gameManagerUtils";
import { GameManagerHelper } from "./gameManagerHelper";
/**
* Helper to handle classic mode specifics
*/
export class ClassicModeHelper extends GameManagerHelper {
/**
* Runs the classic game to the summon phase.
* @param species - Optional array of species to summon.
* @returns A promise that resolves when the summon phase is reached.
*/
async runToSummon(species?: Species[]) {
await this.game.runToTitle();
this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
this.game.scene.gameMode = getGameMode(GameModes.CLASSIC);
const starters = generateStarter(this.game.scene, species);
const selectStarterPhase = new SelectStarterPhase(this.game.scene);
this.game.scene.pushPhase(new EncounterPhase(this.game.scene, false));
selectStarterPhase.initBattle(starters);
});
await this.game.phaseInterceptor.run(EncounterPhase);
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) {
this.game.removeEnemyHeldItems();
}
}
}

View File

@ -0,0 +1,36 @@
import { Button } from "#app/enums/buttons.js";
import overrides from "#app/overrides.js";
import { EncounterPhase, TitlePhase } from "#app/phases.js";
import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler.js";
import { Mode } from "#app/ui/ui.js";
import { GameManagerHelper } from "./gameManagerHelper";
/**
* Helper to handle daily mode specifics
*/
export class DailyModeHelper extends GameManagerHelper {
/**
* Runs the daily game to the summon phase.
* @returns A promise that resolves when the summon phase is reached.
*/
async runToSummon() {
await this.game.runToTitle();
this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
const titlePhase = new TitlePhase(this.game.scene);
titlePhase.initDailyRun();
});
this.game.onNextPrompt("TitlePhase", Mode.SAVE_SLOT, () => {
const uihandler = this.game.scene.ui.getHandler<SaveSlotSelectUiHandler>();
uihandler.processInput(Button.ACTION); // select first slot. that's fine
});
await this.game.phaseInterceptor.run(EncounterPhase);
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) {
this.game.removeEnemyHeldItems();
}
}
}

View File

@ -1,4 +1,4 @@
import GameManager from "./gameManager"; import GameManager from "../gameManager";
/** /**
* Base class for defining all game helpers. * Base class for defining all game helpers.