mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-06 16:32:16 +02:00
Revamp Crit-Based Unit Tests & Dire Hit
This commit is contained in:
parent
eeb1ff5760
commit
f77bf5351e
@ -746,6 +746,32 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the critical-hit stage considering the move used and the Pokemon
|
||||
* who used it.
|
||||
* @param source the {@linkcode Pokemon} who using the move
|
||||
* @param move the {@linkcode Move} being used
|
||||
* @returns the final critical-hit stage value
|
||||
*/
|
||||
getCritStage(source: Pokemon, move: Move): number {
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critStage);
|
||||
this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||
this.scene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||
const bonusCrit = new Utils.BooleanHolder(false);
|
||||
//@ts-ignore
|
||||
if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
|
||||
if (bonusCrit.value) {
|
||||
critStage.value += 1;
|
||||
}
|
||||
}
|
||||
if (source.getTag(BattlerTagType.CRIT_BOOST)) {
|
||||
critStage.value += 2;
|
||||
}
|
||||
console.log(`crit stage: +${critStage.value}`);
|
||||
return critStage.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates and retrieves the final value of a stat considering any held
|
||||
* items, move effects, opponent abilities, and whether there was a critical
|
||||
@ -2128,22 +2154,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (critOnly.value || critAlways) {
|
||||
isCritical = true;
|
||||
} else {
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
applyMoveAttrs(HighCritAttr, source, this, move, critStage);
|
||||
this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage);
|
||||
this.scene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage);
|
||||
const bonusCrit = new Utils.BooleanHolder(false);
|
||||
//@ts-ignore
|
||||
if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus.
|
||||
if (bonusCrit.value) {
|
||||
critStage.value += 1;
|
||||
}
|
||||
}
|
||||
if (source.getTag(BattlerTagType.CRIT_BOOST)) {
|
||||
critStage.value += 2;
|
||||
}
|
||||
console.log(`crit stage: +${critStage.value}`);
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critStage.value, 3))];
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
|
||||
isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance);
|
||||
if (Overrides.NEVER_CRIT_OVERRIDE) {
|
||||
isCritical = false;
|
||||
|
97
src/test/items/dire_hit.test.ts
Normal file
97
src/test/items/dire_hit.test.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phase from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase.js";
|
||||
import { TempCritBoosterModifier } from "#app/modifier/modifier.js";
|
||||
import { Mode } from "#app/ui/ui.js";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js";
|
||||
import { Button } from "#app/enums/buttons.js";
|
||||
import { CommandPhase } from "#app/phases/command-phase.js";
|
||||
import { NewBattlePhase } from "#app/phases/new-battle-phase.js";
|
||||
import { TurnInitPhase } from "#app/phases/turn-init-phase.js";
|
||||
|
||||
describe("Items - Dire Hit", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phase.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset(SPLASH_ONLY)
|
||||
.moveset([ Moves.POUND ])
|
||||
.startingHeldItems([{ name: "DIRE_HIT" }])
|
||||
.battleType("single")
|
||||
.disableCrits();
|
||||
|
||||
}, 20000);
|
||||
|
||||
it("should raise CRIT stage by 1", async () => {
|
||||
await game.startBattle([
|
||||
Species.GASTLY
|
||||
]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
vi.spyOn(enemyPokemon, "getCritStage");
|
||||
|
||||
game.move.select(Moves.POUND);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemyPokemon.getCritStage).toHaveReturnedWith(1);
|
||||
}, 20000);
|
||||
|
||||
it("should renew how many battles are left of existing DIRE_HIT when picking up new DIRE_HIT", async() => {
|
||||
game.override.itemRewards([{ name: "DIRE_HIT" }]);
|
||||
|
||||
await game.startBattle([
|
||||
Species.PIKACHU
|
||||
]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
await game.doKillOpponents();
|
||||
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
|
||||
const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier) as TempCritBoosterModifier;
|
||||
expect(modifier.getBattlesLeft()).toBe(4);
|
||||
|
||||
// Forced DIRE_HIT to spawn in the first slot with override
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||
// Traverse to first modifier slot
|
||||
handler.processInput(Button.LEFT);
|
||||
handler.processInput(Button.UP);
|
||||
handler.processInput(Button.ACTION);
|
||||
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
// Making sure only one booster is in the modifier list even after picking up another
|
||||
let count = 0;
|
||||
for (const m of game.scene.modifiers) {
|
||||
if (m instanceof TempCritBoosterModifier) {
|
||||
count++;
|
||||
expect((m as TempCritBoosterModifier).getBattlesLeft()).toBe(5);
|
||||
}
|
||||
}
|
||||
expect(count).toBe(1);
|
||||
}, 20000);
|
||||
});
|
@ -1,7 +1,4 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { CritBoosterModifier } from "#app/modifier/modifier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
||||
import * as Utils from "#app/utils";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
@ -26,91 +23,64 @@ describe("Items - Leek", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]);
|
||||
game.override.disableCrits();
|
||||
|
||||
game.override.battleType("single");
|
||||
game.override
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH])
|
||||
.startingHeldItems([{ name: "LEEK" }])
|
||||
.moveset([ Moves.TACKLE ])
|
||||
.disableCrits()
|
||||
.battleType("single");
|
||||
});
|
||||
|
||||
it("LEEK activates in battle correctly", async () => {
|
||||
game.override.startingHeldItems([{ name: "LEEK" }]);
|
||||
game.override.moveset([Moves.POUND]);
|
||||
const consoleSpy = vi.spyOn(console, "log");
|
||||
it("should raise CRIT stage by 2 when held by FARFETCHD", async () => {
|
||||
await game.startBattle([
|
||||
Species.FARFETCHD
|
||||
]);
|
||||
|
||||
game.move.select(Moves.POUND);
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Applied", "Leek", "");
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK held by FARFETCHD", async () => {
|
||||
await game.startBattle([
|
||||
Species.FARFETCHD
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
|
||||
expect(critStage.value).toBe(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK held by GALAR_FARFETCHD", async () => {
|
||||
it("should raise CRIT stage by 2 when held by GALAR_FARFETCHD", async () => {
|
||||
await game.startBattle([
|
||||
Species.GALAR_FARFETCHD
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(critStage.value).toBe(2);
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK held by SIRFETCHD", async () => {
|
||||
it("should raise CRIT stage by 2 when held by SIRFETCHD", async () => {
|
||||
await game.startBattle([
|
||||
Species.SIRFETCHD
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(critStage.value).toBe(2);
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK held by fused FARFETCHD line (base)", async () => {
|
||||
it("should raise CRIT stage by 2 when held by FARFETCHD line fused with Pokemon", async () => {
|
||||
// Randomly choose from the Farfetch'd line
|
||||
const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD];
|
||||
|
||||
@ -119,9 +89,7 @@ describe("Items - Leek", () => {
|
||||
Species.PIKACHU,
|
||||
]);
|
||||
|
||||
const party = game.scene.getParty();
|
||||
const partyMember = party[0];
|
||||
const ally = party[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
@ -132,20 +100,18 @@ describe("Items - Leek", () => {
|
||||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
expect(critStage.value).toBe(2);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK held by fused FARFETCHD line (part)", async () => {
|
||||
it("should raise CRIT stage by 2 when held by Pokemon fused with FARFETCHD line", async () => {
|
||||
// Randomly choose from the Farfetch'd line
|
||||
const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD];
|
||||
|
||||
@ -154,9 +120,7 @@ describe("Items - Leek", () => {
|
||||
species[Utils.randInt(species.length)]
|
||||
]);
|
||||
|
||||
const party = game.scene.getParty();
|
||||
const partyMember = party[0];
|
||||
const ally = party[1];
|
||||
const [ partyMember, ally ] = game.scene.getParty();
|
||||
|
||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||
partyMember.fusionSpecies = ally.species;
|
||||
@ -167,36 +131,31 @@ describe("Items - Leek", () => {
|
||||
partyMember.fusionGender = ally.gender;
|
||||
partyMember.fusionLuck = ally.luck;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
expect(critStage.value).toBe(2);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("LEEK not held by FARFETCHD line", async () => {
|
||||
it("should not raise CRIT stage when held by a Pokemon outside of FARFETCHD line", async () => {
|
||||
await game.startBattle([
|
||||
Species.PIKACHU
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
const enemyMember = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
vi.spyOn(enemyMember, "getCritStage");
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
game.move.select(Moves.TACKLE);
|
||||
|
||||
// Giving Leek to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.LEEK().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
expect(enemyMember.getCritStage).toHaveReturnedWith(0);
|
||||
}, 20000);
|
||||
});
|
||||
|
@ -1,13 +1,10 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { CritBoosterModifier } from "#app/modifier/modifier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import * as Utils from "#app/utils";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phase from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
|
||||
describe("Items - Scope Lens", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -26,47 +23,29 @@ describe("Items - Scope Lens", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override.enemySpecies(Species.MAGIKARP);
|
||||
game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]);
|
||||
game.override.disableCrits();
|
||||
game.override
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset(SPLASH_ONLY)
|
||||
.moveset([ Moves.POUND ])
|
||||
.startingHeldItems([{ name: "SCOPE_LENS" }])
|
||||
.battleType("single")
|
||||
.disableCrits();
|
||||
|
||||
game.override.battleType("single");
|
||||
}, 20000);
|
||||
|
||||
it("SCOPE_LENS activates in battle correctly", async () => {
|
||||
game.override.startingHeldItems([{ name: "SCOPE_LENS" }]);
|
||||
game.override.moveset([Moves.POUND]);
|
||||
const consoleSpy = vi.spyOn(console, "log");
|
||||
it("should raise CRIT stage by 1", async () => {
|
||||
await game.startBattle([
|
||||
Species.GASTLY
|
||||
]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
||||
vi.spyOn(enemyPokemon, "getCritStage");
|
||||
|
||||
game.move.select(Moves.POUND);
|
||||
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||
|
||||
expect(consoleSpy).toHaveBeenCalledWith("Applied", "Scope Lens", "");
|
||||
}, 20000);
|
||||
|
||||
it("SCOPE_LENS held by random pokemon", async () => {
|
||||
await game.startBattle([
|
||||
Species.GASTLY
|
||||
]);
|
||||
|
||||
const partyMember = game.scene.getPlayerPokemon()!;
|
||||
|
||||
// Making sure modifier is not applied without holding item
|
||||
const critStage = new Utils.IntegerHolder(0);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
|
||||
expect(critStage.value).toBe(0);
|
||||
|
||||
// Giving Scope Lens to party member and testing if it applies
|
||||
partyMember.scene.addModifier(modifierTypes.SCOPE_LENS().newModifier(partyMember), true);
|
||||
partyMember.scene.applyModifiers(CritBoosterModifier, true, partyMember, critStage);
|
||||
|
||||
expect(critStage.value).toBe(1);
|
||||
expect(enemyPokemon.getCritStage).toHaveReturnedWith(1);
|
||||
}, 20000);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user