Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 8 commits into from
Nov 14, 2024
34 changes: 28 additions & 6 deletions src/battle-scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1233,12 +1233,34 @@ export default class BattleScene extends SceneBase {
newDouble = !!double;
}

if (Overrides.BATTLE_TYPE_OVERRIDE === "double") {
newDouble = true;
}
/* Override battles into single only if not fighting with trainers */
if (newBattleType !== BattleType.TRAINER && Overrides.BATTLE_TYPE_OVERRIDE === "single") {
newDouble = false;
if (!isNullOrUndefined(Overrides.BATTLE_TYPE_OVERRIDE)) {
let doubleOverrideForWave: "single" | "double" | null = null;

switch (Overrides.BATTLE_TYPE_OVERRIDE) {
case "double":
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;
Expand Down
15 changes: 14 additions & 1 deletion src/overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,18 @@ class DefaultOverrides {
/** a specific seed (default: a random string of 24 characters) */
readonly SEED_OVERRIDE: string = "";
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;
DayKev marked this conversation as resolved.
Show resolved Hide resolved
readonly STARTING_WAVE_OVERRIDE: number = 0;
readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null;
Expand Down Expand Up @@ -229,3 +240,5 @@ export default {
...defaultOverrides,
...overrides
} satisfies InstanceType<typeof DefaultOverrides>;

export type BattleStyle = "double" | "single" | "even-doubles" | "odd-doubles";
42 changes: 41 additions & 1 deletion src/test/battle/double_battle.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
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 { TurnInitPhase } from "#app/phases/turn-init-phase";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { StatusEffect } from "#enums/status-effect";
import GameManager from "#test/utils/gameManager";
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", () => {
const DOUBLE_CHANCE = 8; // Normal chance of double battle is 1/8

let phaserGame: Phaser.Game;
let game: GameManager;

Expand Down Expand Up @@ -56,4 +60,40 @@ describe("Double Battles", () => {
await game.phaseInterceptor.to(TurnInitPhase);
expect(game.scene.getPlayerField().filter(p => !p.isFainted())).toHaveLength(2);
}, 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);
});
});
2 changes: 1 addition & 1 deletion src/test/moves/gastro_acid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("Moves - Gastro Acid", () => {
});

it("fails if used on an enemy with an already-suppressed ability", async () => {
game.override.battleType(null);
game.override.battleType("single");

await game.startBattle();

Expand Down
9 changes: 5 additions & 4 deletions src/test/utils/helpers/overridesHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Abilities } from "#app/enums/abilities";
import * as GameMode from "#app/game-mode";
import { GameModes, getGameMode } from "#app/game-mode";
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 { Biome } from "#enums/biome";
import { Moves } from "#enums/moves";
Expand Down Expand Up @@ -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
* @returns `this`
*/
public battleType(battleType: "single" | "double" | null): this {
public battleType(battleType: BattleStyle | null): this {
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;
}

Expand Down
Loading