mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-01-31 13:17:21 +00:00
[Feature] Fully implement Octolock (#2985)
* implement octolock * Add tests
This commit is contained in:
parent
985c24e7bd
commit
0aa5e0d49d
@ -634,6 +634,32 @@ export class IngrainTag extends TrappedTag {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Octolock traps the target pokemon and reduces its DEF and SPDEF by one stage at the
|
||||
* end of each turn.
|
||||
*/
|
||||
export class OctolockTag extends TrappedTag {
|
||||
constructor(sourceId: number) {
|
||||
super(BattlerTagType.OCTOLOCK, BattlerTagLapseType.TURN_END, 1, Moves.OCTOLOCK, sourceId);
|
||||
}
|
||||
|
||||
canAdd(pokemon: Pokemon): boolean {
|
||||
const isOctolocked = pokemon.getTag(BattlerTagType.OCTOLOCK);
|
||||
return !isOctolocked;
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||
const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
if (shouldLapse) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.DEF, BattleStat.SPDEF], -1));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class AquaRingTag extends BattlerTag {
|
||||
constructor() {
|
||||
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined);
|
||||
@ -1662,6 +1688,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
|
||||
return new DestinyBondTag(sourceMove, sourceId);
|
||||
case BattlerTagType.ICE_FACE:
|
||||
return new IceFaceTag(sourceMove);
|
||||
case BattlerTagType.OCTOLOCK:
|
||||
return new OctolockTag(sourceId);
|
||||
case BattlerTagType.NONE:
|
||||
default:
|
||||
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
|
||||
|
@ -7796,8 +7796,7 @@ export function initMoves() {
|
||||
.attr(EatBerryAttr)
|
||||
.target(MoveTarget.ALL),
|
||||
new StatusMove(Moves.OCTOLOCK, Type.FIGHTING, 100, 15, -1, 0, 8)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1)
|
||||
.partial(),
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.OCTOLOCK, false, true, 1),
|
||||
new AttackMove(Moves.BOLT_BEAK, Type.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8)
|
||||
.attr(FirstAttackDoublePowerAttr),
|
||||
new AttackMove(Moves.FISHIOUS_REND, Type.WATER, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8)
|
||||
|
@ -13,6 +13,7 @@ export enum BattlerTagType {
|
||||
ENCORE = "ENCORE",
|
||||
HELPING_HAND = "HELPING_HAND",
|
||||
INGRAIN = "INGRAIN",
|
||||
OCTOLOCK = "OCTOLOCK",
|
||||
AQUA_RING = "AQUA_RING",
|
||||
DROWSY = "DROWSY",
|
||||
TRAPPED = "TRAPPED",
|
||||
|
62
src/test/battlerTags/octolock.test.ts
Normal file
62
src/test/battlerTags/octolock.test.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import Pokemon from "#app/field/pokemon.js";
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import { BattlerTag, BattlerTagLapseType, OctolockTag, TrappedTag } from "#app/data/battler-tags.js";
|
||||
import { StatChangePhase } from "#app/phases.js";
|
||||
import { BattleStat } from "#app/data/battle-stat.js";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||
|
||||
jest.mock("#app/battle-scene.js");
|
||||
|
||||
describe("BattlerTag - OctolockTag", () => {
|
||||
describe("lapse behavior", () => {
|
||||
it("unshifts a StatChangePhase with expected stat changes", { timeout: 10000 }, async () => {
|
||||
const mockPokemon = {
|
||||
scene: new BattleScene(),
|
||||
getBattlerIndex: () => 0,
|
||||
} as Pokemon;
|
||||
|
||||
const subject = new OctolockTag(1);
|
||||
|
||||
vi.spyOn(mockPokemon.scene, "unshiftPhase").mockImplementation(phase => {
|
||||
expect(phase).toBeInstanceOf(StatChangePhase);
|
||||
expect((phase as StatChangePhase)["levels"]).toEqual(-1);
|
||||
expect((phase as StatChangePhase)["stats"]).toEqual([BattleStat.DEF, BattleStat.SPDEF]);
|
||||
});
|
||||
|
||||
subject.lapse(mockPokemon, BattlerTagLapseType.TURN_END);
|
||||
|
||||
expect(mockPokemon.scene.unshiftPhase).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
it ("traps its target (extends TrappedTag)", { timeout: 2000 }, async () => {
|
||||
expect(new OctolockTag(1)).toBeInstanceOf(TrappedTag);
|
||||
});
|
||||
|
||||
it("can be added to pokemon who are not octolocked", { timeout: 2000 }, async => {
|
||||
const mockPokemon = {
|
||||
getTag: vi.fn().mockReturnValue(undefined) as Pokemon["getTag"],
|
||||
} as Pokemon;
|
||||
|
||||
const subject = new OctolockTag(1);
|
||||
|
||||
expect(subject.canAdd(mockPokemon)).toBeTruthy();
|
||||
|
||||
expect(mockPokemon.getTag).toHaveBeenCalledTimes(1);
|
||||
expect(mockPokemon.getTag).toHaveBeenCalledWith(BattlerTagType.OCTOLOCK);
|
||||
});
|
||||
|
||||
it("cannot be added to pokemon who are octolocked", { timeout: 2000 }, async => {
|
||||
const mockPokemon = {
|
||||
getTag: vi.fn().mockReturnValue(new BattlerTag(null, null, null, null)) as Pokemon["getTag"],
|
||||
} as Pokemon;
|
||||
|
||||
const subject = new OctolockTag(1);
|
||||
|
||||
expect(subject.canAdd(mockPokemon)).toBeFalsy();
|
||||
|
||||
expect(mockPokemon.getTag).toHaveBeenCalledTimes(1);
|
||||
expect(mockPokemon.getTag).toHaveBeenCalledWith(BattlerTagType.OCTOLOCK);
|
||||
});
|
||||
});
|
78
src/test/moves/octolock.test.ts
Normal file
78
src/test/moves/octolock.test.ts
Normal file
@ -0,0 +1,78 @@
|
||||
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 { CommandPhase, MoveEndPhase, TurnInitPhase } from "#app/phases";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrappedTag } from "#app/data/battler-tags.js";
|
||||
|
||||
describe("Moves - Octolock", () => {
|
||||
describe("integration tests", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
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, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.OCTOLOCK, Moves.SPLASH]);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NONE);
|
||||
});
|
||||
|
||||
it("Reduces DEf and SPDEF by 1 each turn", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.GRAPPLOCT]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
// use Octolock and advance to init phase of next turn to check for stat changes
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK));
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-1);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-1);
|
||||
|
||||
// take a second turn to make sure stat changes occur again
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.DEF]).toBe(-2);
|
||||
expect(enemyPokemon[0].summonData.battleStats[BattleStat.SPDEF]).toBe(-2);
|
||||
});
|
||||
|
||||
it("Traps the target pokemon", { timeout: 10000 }, async () => {
|
||||
await game.startBattle([Species.GRAPPLOCT]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyField();
|
||||
|
||||
// before Octolock - enemy should not be trapped
|
||||
expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeUndefined();
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.OCTOLOCK));
|
||||
|
||||
// after Octolock - enemy should be trapped
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
expect(enemyPokemon[0].findTag(t => t instanceof TrappedTag)).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user