[Dev] Add overrides for alternating between single and double battles (#4833)

* [Dev] Add overrides for alternating between single and double battles

* PR feedback

* Add type `DoubleType`

* Fixed Gastro Acid test using `game.override.battleType(null)`

* Changed new type name to `SingleOrDoubleType`

* `SingleOrDoubleType` is now `BattleStyle`

* Update src/test/utils/helpers/overridesHelper.ts

---------

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com>
This commit is contained in:
PigeonBar 2024-11-14 14:40:01 -05:00 committed by GitHub
parent d0d9eb78da
commit 640ac23741
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 13 deletions

View File

@ -1233,12 +1233,34 @@ export default class BattleScene extends SceneBase {
newDouble = !!double; newDouble = !!double;
} }
if (Overrides.BATTLE_TYPE_OVERRIDE === "double") { if (!isNullOrUndefined(Overrides.BATTLE_TYPE_OVERRIDE)) {
newDouble = true; let doubleOverrideForWave: "single" | "double" | null = null;
}
/* Override battles into single only if not fighting with trainers */ switch (Overrides.BATTLE_TYPE_OVERRIDE) {
if (newBattleType !== BattleType.TRAINER && Overrides.BATTLE_TYPE_OVERRIDE === "single") { case "double":
newDouble = false; doubleOverrideForWave = "double";
break;
case "single":
doubleOverrideForWave = "single";
break;
case "even-doubles":
doubleOverrideForWave = (newWaveIndex % 2) ? "single" : "double";
break;
case "odd-doubles":
doubleOverrideForWave = (newWaveIndex % 2) ? "double" : "single";
break;
}
if (doubleOverrideForWave === "double") {
newDouble = true;
}
/**
* Override battles into single only if not fighting with trainers.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/1948 | GitHub Issue #1948}
*/
if (newBattleType !== BattleType.TRAINER && doubleOverrideForWave === "single") {
newDouble = false;
}
} }
const lastBattle = this.currentBattle; const lastBattle = this.currentBattle;

View File

@ -47,7 +47,18 @@ class DefaultOverrides {
/** a specific seed (default: a random string of 24 characters) */ /** a specific seed (default: a random string of 24 characters) */
readonly SEED_OVERRIDE: string = ""; readonly SEED_OVERRIDE: string = "";
readonly WEATHER_OVERRIDE: WeatherType = WeatherType.NONE; readonly WEATHER_OVERRIDE: WeatherType = WeatherType.NONE;
readonly BATTLE_TYPE_OVERRIDE: "double" | "single" | null = null; /**
* If `null`, ignore this override.
*
* If `"single"`, set every non-trainer battle to be a single battle.
*
* If `"double"`, set every battle (including trainer battles) to be a double battle.
*
* If `"even-doubles"`, follow the `"double"` rule on even wave numbers, and follow the `"single"` rule on odd wave numbers.
*
* If `"odd-doubles"`, follow the `"double"` rule on odd wave numbers, and follow the `"single"` rule on even wave numbers.
*/
readonly BATTLE_TYPE_OVERRIDE: BattleStyle | null = null;
readonly STARTING_WAVE_OVERRIDE: number = 0; readonly STARTING_WAVE_OVERRIDE: number = 0;
readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null; readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null;
@ -229,3 +240,5 @@ export default {
...defaultOverrides, ...defaultOverrides,
...overrides ...overrides
} satisfies InstanceType<typeof DefaultOverrides>; } satisfies InstanceType<typeof DefaultOverrides>;
export type BattleStyle = "double" | "single" | "even-doubles" | "odd-doubles";

View File

@ -1,4 +1,6 @@
import { Status } from "#app/data/status-effect"; import { Status } from "#app/data/status-effect";
import { Abilities } from "#enums/abilities";
import { GameModes, getGameMode } from "#app/game-mode";
import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -6,9 +8,11 @@ import { Species } from "#enums/species";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Double Battles", () => { describe("Double Battles", () => {
const DOUBLE_CHANCE = 8; // Normal chance of double battle is 1/8
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
let game: GameManager; let game: GameManager;
@ -56,4 +60,40 @@ describe("Double Battles", () => {
await game.phaseInterceptor.to(TurnInitPhase); await game.phaseInterceptor.to(TurnInitPhase);
expect(game.scene.getPlayerField().filter(p => !p.isFainted())).toHaveLength(2); expect(game.scene.getPlayerField().filter(p => !p.isFainted())).toHaveLength(2);
}, 20000); }, 20000);
it("randomly chooses between single and double battles if there is no battle type override", async () => {
let rngSweepProgress = 0; // Will simulate RNG rolls by slowly increasing from 0 to 1
let doubleCount = 0;
let singleCount = 0;
vi.spyOn(Phaser.Math.RND, "realInRange").mockImplementation((min: number, max: number) => {
return rngSweepProgress * (max - min) + min;
});
game.override.enemyMoveset(Moves.SPLASH)
.moveset(Moves.SPLASH)
.enemyAbility(Abilities.BALL_FETCH)
.ability(Abilities.BALL_FETCH);
// Play through endless, waves 1 to 9, counting number of double battles from waves 2 to 9
await game.classicMode.startBattle([ Species.BULBASAUR ]);
game.scene.gameMode = getGameMode(GameModes.ENDLESS);
for (let i = 0; i < DOUBLE_CHANCE; i++) {
rngSweepProgress = (i + 0.5) / DOUBLE_CHANCE;
game.move.select(Moves.SPLASH);
await game.doKillOpponents();
await game.toNextWave();
if (game.scene.getEnemyParty().length === 1) {
singleCount++;
} else if (game.scene.getEnemyParty().length === 2) {
doubleCount++;
}
}
expect(doubleCount).toBe(1);
expect(singleCount).toBe(DOUBLE_CHANCE - 1);
});
}); });

View File

@ -62,7 +62,7 @@ describe("Moves - Gastro Acid", () => {
}); });
it("fails if used on an enemy with an already-suppressed ability", async () => { it("fails if used on an enemy with an already-suppressed ability", async () => {
game.override.battleType(null); game.override.battleType("single");
await game.startBattle(); await game.startBattle();

View File

@ -4,7 +4,7 @@ import { Abilities } from "#app/enums/abilities";
import * as GameMode from "#app/game-mode"; import * as GameMode from "#app/game-mode";
import { GameModes, getGameMode } from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode";
import { ModifierOverride } from "#app/modifier/modifier-type"; import { ModifierOverride } from "#app/modifier/modifier-type";
import Overrides from "#app/overrides"; import Overrides, { BattleStyle } from "#app/overrides";
import { Unlockables } from "#app/system/unlockables"; import { Unlockables } from "#app/system/unlockables";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -238,13 +238,14 @@ export class OverridesHelper extends GameManagerHelper {
} }
/** /**
* Override the battle type (single or double) * Override the battle type (e.g., single or double).
* @see {@linkcode Overrides.BATTLE_TYPE_OVERRIDE}
* @param battleType battle type to set * @param battleType battle type to set
* @returns `this` * @returns `this`
*/ */
public battleType(battleType: "single" | "double" | null): this { public battleType(battleType: BattleStyle | null): this {
vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType); vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType);
this.log(`Battle type set to ${battleType} only!`); this.log(battleType === null ? "Battle type override disabled!" : `Battle type set to ${battleType}!`);
return this; return this;
} }