[Enhancement][Unit Test] Refactor + Unit Tests for Light Screen, Reflect and Aurora Veil (#2514)
* refactor copy pastas * add tests * specify arena tag types
This commit is contained in:
parent
d32119f921
commit
464ffe7332
|
@ -86,37 +86,57 @@ export class MistTag extends ArenaTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces the damage of specific move categories in the arena.
|
||||||
|
* @extends ArenaTag
|
||||||
|
*/
|
||||||
export class WeakenMoveScreenTag extends ArenaTag {
|
export class WeakenMoveScreenTag extends ArenaTag {
|
||||||
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) {
|
protected weakenedCategories: MoveCategory[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of the WeakenMoveScreenTag class.
|
||||||
|
*
|
||||||
|
* @param tagType - The type of the arena tag.
|
||||||
|
* @param turnCount - The number of turns the tag is active.
|
||||||
|
* @param sourceMove - The move that created the tag.
|
||||||
|
* @param sourceId - The ID of the source of the tag.
|
||||||
|
* @param side - The side (player or enemy) the tag affects.
|
||||||
|
* @param weakenedCategories - The categories of moves that are weakened by this tag.
|
||||||
|
*/
|
||||||
|
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, weakenedCategories: MoveCategory[]) {
|
||||||
super(tagType, turnCount, sourceMove, sourceId, side);
|
super(tagType, turnCount, sourceMove, sourceId, side);
|
||||||
|
|
||||||
|
this.weakenedCategories = weakenedCategories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the weakening effect to the move.
|
||||||
|
*
|
||||||
|
* @param arena - The arena where the move is applied.
|
||||||
|
* @param args - The arguments for the move application.
|
||||||
|
* @param args[0] - The category of the move.
|
||||||
|
* @param args[1] - A boolean indicating whether it is a double battle.
|
||||||
|
* @param args[2] - An object of type `Utils.NumberHolder` that holds the damage multiplier
|
||||||
|
*
|
||||||
|
* @returns True if the move was weakened, otherwise false.
|
||||||
|
*/
|
||||||
apply(arena: Arena, args: any[]): boolean {
|
apply(arena: Arena, args: any[]): boolean {
|
||||||
if ((args[1] as boolean)) {
|
if (this.weakenedCategories.includes((args[0] as MoveCategory))) {
|
||||||
(args[2] as Utils.NumberHolder).value = 2732/4096;
|
(args[2] as Utils.NumberHolder).value = (args[1] as boolean) ? 2732/4096 : 0.5;
|
||||||
} else {
|
|
||||||
(args[2] as Utils.NumberHolder).value = 0.5;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReflectTag extends WeakenMoveScreenTag {
|
|
||||||
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
|
||||||
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side);
|
|
||||||
}
|
|
||||||
|
|
||||||
apply(arena: Arena, args: any[]): boolean {
|
|
||||||
if ((args[0] as MoveCategory) === MoveCategory.PHYSICAL) {
|
|
||||||
if ((args[1] as boolean)) {
|
|
||||||
(args[2] as Utils.NumberHolder).value = 2732/4096;
|
|
||||||
} else {
|
|
||||||
(args[2] as Utils.NumberHolder).value = 0.5;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces the damage of physical moves.
|
||||||
|
* Used by {@linkcode Moves.REFLECT}
|
||||||
|
*/
|
||||||
|
class ReflectTag extends WeakenMoveScreenTag {
|
||||||
|
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
||||||
|
super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [MoveCategory.PHYSICAL]);
|
||||||
|
}
|
||||||
|
|
||||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
|
@ -125,21 +145,13 @@ class ReflectTag extends WeakenMoveScreenTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces the damage of special moves.
|
||||||
|
* Used by {@linkcode Moves.LIGHT_SCREEN}
|
||||||
|
*/
|
||||||
class LightScreenTag extends WeakenMoveScreenTag {
|
class LightScreenTag extends WeakenMoveScreenTag {
|
||||||
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
||||||
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side);
|
super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [MoveCategory.SPECIAL]);
|
||||||
}
|
|
||||||
|
|
||||||
apply(arena: Arena, args: any[]): boolean {
|
|
||||||
if ((args[0] as MoveCategory) === MoveCategory.SPECIAL) {
|
|
||||||
if ((args[1] as boolean)) {
|
|
||||||
(args[2] as Utils.NumberHolder).value = 2732/4096;
|
|
||||||
} else {
|
|
||||||
(args[2] as Utils.NumberHolder).value = 0.5;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||||
|
@ -149,9 +161,13 @@ class LightScreenTag extends WeakenMoveScreenTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduces the damage of physical and special moves.
|
||||||
|
* Used by {@linkcode Moves.AURORA_VEIL}
|
||||||
|
*/
|
||||||
class AuroraVeilTag extends WeakenMoveScreenTag {
|
class AuroraVeilTag extends WeakenMoveScreenTag {
|
||||||
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
|
||||||
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side);
|
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL]);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(arena: Arena, quiet: boolean = false): void {
|
onAdd(arena: Arena, quiet: boolean = false): void {
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import * as overrides from "#app/overrides";
|
||||||
|
import {
|
||||||
|
TurnEndPhase,
|
||||||
|
} from "#app/phases";
|
||||||
|
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Abilities } from "#app/enums/abilities.js";
|
||||||
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
|
import Move, { allMoves } from "#app/data/move.js";
|
||||||
|
import { NumberHolder } from "#app/utils.js";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag.js";
|
||||||
|
import { WeatherType } from "#app/data/weather.js";
|
||||||
|
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
||||||
|
|
||||||
|
|
||||||
|
describe("Moves - Aurora Veil", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const singleBattleMultiplier = 0.5;
|
||||||
|
const doubleBattleMultiplier = 2732/4096;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||||
|
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.ABSORB, Moves.ROCK_SLIDE, Moves.TACKLE]);
|
||||||
|
vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100);
|
||||||
|
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP);
|
||||||
|
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.AURORA_VEIL, Moves.AURORA_VEIL, Moves.AURORA_VEIL, Moves.AURORA_VEIL]);
|
||||||
|
vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(WeatherType.HAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of physical attacks by half in a single battle", async() => {
|
||||||
|
const moveToUse = Moves.TACKLE;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of physical attacks by a third in a double battle", async() => {
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||||
|
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
|
||||||
|
const moveToUse = Moves.ROCK_SLIDE;
|
||||||
|
await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
game.doAttack(getMovePosition(game.scene, 1, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of special attacks by half in a single battle", async() => {
|
||||||
|
const moveToUse = Moves.ABSORB;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of special attacks by a third in a double battle", async() => {
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||||
|
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
|
||||||
|
const moveToUse = Moves.DAZZLING_GLEAM;
|
||||||
|
await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
game.doAttack(getMovePosition(game.scene, 1, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the damage of a move multiplied by screen's multiplier, Auroa Veil in this case {@linkcode Moves.AURORA_VEIL}.
|
||||||
|
* Please note this does not consider other damage calculations except the screen multiplier.
|
||||||
|
*
|
||||||
|
* @param defender - The defending Pokémon.
|
||||||
|
* @param attacker - The attacking Pokémon.
|
||||||
|
* @param move - The move being used.
|
||||||
|
* @returns The calculated move damage considering any weakening effects.
|
||||||
|
*/
|
||||||
|
const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) => {
|
||||||
|
const multiplierHolder = new NumberHolder(1);
|
||||||
|
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
|
||||||
|
if (defender.scene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side)) {
|
||||||
|
defender.scene.arena.applyTagsForSide(ArenaTagType.AURORA_VEIL, side, move.category, defender.scene.currentBattle.double, multiplierHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return move.power * multiplierHolder.value;
|
||||||
|
};
|
|
@ -0,0 +1,106 @@
|
||||||
|
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import * as overrides from "#app/overrides";
|
||||||
|
import {
|
||||||
|
TurnEndPhase,
|
||||||
|
} from "#app/phases";
|
||||||
|
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Abilities } from "#app/enums/abilities.js";
|
||||||
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
|
import Move, { allMoves } from "#app/data/move.js";
|
||||||
|
import { NumberHolder } from "#app/utils.js";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag.js";
|
||||||
|
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
||||||
|
|
||||||
|
|
||||||
|
describe("Moves - Light Screen", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const singleBattleMultiplier = 0.5;
|
||||||
|
const doubleBattleMultiplier = 2732/4096;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||||
|
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.ABSORB, Moves.DAZZLING_GLEAM, Moves.TACKLE]);
|
||||||
|
vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100);
|
||||||
|
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP);
|
||||||
|
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.LIGHT_SCREEN, Moves.LIGHT_SCREEN, Moves.LIGHT_SCREEN, Moves.LIGHT_SCREEN]);
|
||||||
|
vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of special attacks by half in a single battle", async() => {
|
||||||
|
const moveToUse = Moves.ABSORB;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of special attacks by a third in a double battle", async() => {
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||||
|
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
|
||||||
|
const moveToUse = Moves.DAZZLING_GLEAM;
|
||||||
|
await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
game.doAttack(getMovePosition(game.scene, 1, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not affect physical attacks", async() => {
|
||||||
|
const moveToUse = Moves.TACKLE;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the damage of a move multiplied by screen's multiplier, Light Screen in this case {@linkcode Moves.LIGHT_SCREEN}.
|
||||||
|
* Please note this does not consider other damage calculations except the screen multiplier.
|
||||||
|
*
|
||||||
|
* @param defender - The defending Pokémon.
|
||||||
|
* @param attacker - The attacking Pokémon.
|
||||||
|
* @param move - The move being used.
|
||||||
|
* @returns The calculated move damage considering any weakening effects.
|
||||||
|
*/
|
||||||
|
const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) => {
|
||||||
|
const multiplierHolder = new NumberHolder(1);
|
||||||
|
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
|
||||||
|
if (defender.scene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side)) {
|
||||||
|
defender.scene.arena.applyTagsForSide(ArenaTagType.LIGHT_SCREEN, side, move.category, defender.scene.currentBattle.double, multiplierHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return move.power * multiplierHolder.value;
|
||||||
|
};
|
|
@ -0,0 +1,106 @@
|
||||||
|
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import * as overrides from "#app/overrides";
|
||||||
|
import {
|
||||||
|
TurnEndPhase,
|
||||||
|
} from "#app/phases";
|
||||||
|
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Abilities } from "#app/enums/abilities.js";
|
||||||
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
|
import Move, { allMoves } from "#app/data/move.js";
|
||||||
|
import { NumberHolder } from "#app/utils.js";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag.js";
|
||||||
|
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
||||||
|
|
||||||
|
|
||||||
|
describe("Moves - Reflect", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const singleBattleMultiplier = 0.5;
|
||||||
|
const doubleBattleMultiplier = 2732/4096;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||||
|
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.ABSORB, Moves.ROCK_SLIDE, Moves.TACKLE]);
|
||||||
|
vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100);
|
||||||
|
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP);
|
||||||
|
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.REFLECT, Moves.REFLECT, Moves.REFLECT, Moves.REFLECT]);
|
||||||
|
vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of physical attacks by half in a single battle", async() => {
|
||||||
|
const moveToUse = Moves.TACKLE;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reduces damage of physical attacks by a third in a double battle", async() => {
|
||||||
|
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||||
|
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||||
|
|
||||||
|
const moveToUse = Moves.ROCK_SLIDE;
|
||||||
|
await game.startBattle([Species.SHUCKLE, Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
game.doAttack(getMovePosition(game.scene, 1, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not affect special attacks", async() => {
|
||||||
|
const moveToUse = Moves.ABSORB;
|
||||||
|
await game.startBattle([Species.SHUCKLE]);
|
||||||
|
|
||||||
|
game.doAttack(getMovePosition(game.scene, 0, moveToUse));
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]);
|
||||||
|
|
||||||
|
expect(mockedDmg).toBe(allMoves[moveToUse].power);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the damage of a move multiplied by screen's multiplier, Reflect in this case {@linkcode Moves.REFLECT}.
|
||||||
|
* Please note this does not consider other damage calculations except the screen multiplier.
|
||||||
|
*
|
||||||
|
* @param defender - The defending Pokémon.
|
||||||
|
* @param attacker - The attacking Pokémon.
|
||||||
|
* @param move - The move being used.
|
||||||
|
* @returns The calculated move damage considering any weakening effects.
|
||||||
|
*/
|
||||||
|
const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) => {
|
||||||
|
const multiplierHolder = new NumberHolder(1);
|
||||||
|
const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
|
||||||
|
if (defender.scene.arena.getTagOnSide(ArenaTagType.REFLECT, side)) {
|
||||||
|
defender.scene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, move.category, defender.scene.currentBattle.double, multiplierHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return move.power * multiplierHolder.value;
|
||||||
|
};
|
Loading…
Reference in New Issue