From 18450a744da6edb6f1c5753577fbab46f52489b7 Mon Sep 17 00:00:00 2001 From: muscode13 Date: Sat, 26 Oct 2024 11:37:59 -0600 Subject: [PATCH] inital wip --- src/data/arena-tag.ts | 34 +++++++++++++++ src/data/battler-tags.ts | 20 +++++++++ src/data/move.ts | 7 ++- src/enums/arena-tag-type.ts | 1 + src/enums/battler-tag-type.ts | 1 + src/test/moves/fairy_lock.test.ts | 71 +++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/test/moves/fairy_lock.test.ts diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index d2c95b7ccdf..0510d23bafd 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1202,6 +1202,38 @@ class GrassWaterPledgeTag extends ArenaTag { } } +export class FairyLockTag extends ArenaTag { + constructor(sourceId: number) { + super(ArenaTagType.FAIRY_LOCK, 1, Moves.FAIRY_LOCK, sourceId); + } + + onAdd(arena: Arena): void { + arena.scene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd")); + } + + onRemove(arena: Arena): void { } + + lapse(arena: Arena): boolean { + // const lockedIn = [...arena.scene.getEnemyField(), ...arena.scene.getPlayerField()]; + // lockedIn?.forEach((p: Pokemon) => { + // p.addTag(BattlerTagType.TIMED_TRAP, 1, Moves.FAIRY_LOCK, this.sourceId) + // }); + // return super.lapse(arena); + + const ret = super.lapse(arena); + + if (!ret) { + const lockedIn = [ ...arena.scene.getEnemyField(), ...arena.scene.getPlayerField() ]; + console.log(lockedIn); + lockedIn?.forEach((p: Pokemon) => { + p.addTag(BattlerTagType.TIMED_TRAP, 1, Moves.FAIRY_LOCK, this.sourceId); + }); + } + + return ret; + } +} + // TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { @@ -1260,6 +1292,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove return new WaterFirePledgeTag(sourceId, side); case ArenaTagType.GRASS_WATER_PLEDGE: return new GrassWaterPledgeTag(sourceId, side); + case ArenaTagType.FAIRY_LOCK: + return new FairyLockTag(sourceId); default: return null; } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index d671c56ab26..6cc19466666 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2777,6 +2777,24 @@ export class PowerTrickTag extends BattlerTag { } } +export class TimedTrap extends TrappedTag { + constructor(turnCount: number, sourceMove: Moves, sourceId: number) { + super(BattlerTagType.TRAPPED, BattlerTagLapseType.TURN_END, turnCount, sourceMove, sourceId); + } + + onAdd(pokemon: Pokemon): void {} + + onRemove(pokemon: Pokemon): void {} + + lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { + const ret = super.lapse(pokemon, lapseType); + if (!ret) { + pokemon.removeTag(BattlerTagType.TRAPPED); + } + return ret; + } + +} /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * @param sourceId - The ID of the pokemon adding the tag @@ -2954,6 +2972,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new TelekinesisTag(sourceMove); case BattlerTagType.POWER_TRICK: return new PowerTrickTag(sourceMove, sourceId); + case BattlerTagType.TIMED_TRAP: + return new TimedTrap(turnCount, sourceMove, sourceId); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/move.ts b/src/data/move.ts index efdd4568927..7c50ff7573b 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -9042,8 +9042,13 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_OTHERS), new StatusMove(Moves.FAIRY_LOCK, Type.FAIRY, -1, 10, -1, 0, 6) .ignoresSubstitute() + .ignoresProtect() .target(MoveTarget.BOTH_SIDES) - .unimplemented(), + .attr(AddArenaTagAttr, ArenaTagType.FAIRY_LOCK, 1, true) + .condition((user, target, move) => { + const turnMove = user.getLastXMoves(1); + return !turnMove.length || turnMove[0].move !== move.id || turnMove[0].result !== MoveResult.SUCCESS; + }), new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6) .attr(ProtectAttr, BattlerTagType.KINGS_SHIELD) .condition(failIfLastCondition), diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index c73f4ec2ae5..1c62ccb14a6 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -28,4 +28,5 @@ export enum ArenaTagType { FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE", WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE", GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE", + FAIRY_LOCK = "FAIRY_LOCK", } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 680dedb93cc..d114842cca8 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -16,6 +16,7 @@ export enum BattlerTagType { AQUA_RING = "AQUA_RING", DROWSY = "DROWSY", TRAPPED = "TRAPPED", + TIMED_TRAP = "TIMED_TRAP", BIND = "BIND", WRAP = "WRAP", FIRE_SPIN = "FIRE_SPIN", diff --git a/src/test/moves/fairy_lock.test.ts b/src/test/moves/fairy_lock.test.ts new file mode 100644 index 00000000000..472bd5ae2e0 --- /dev/null +++ b/src/test/moves/fairy_lock.test.ts @@ -0,0 +1,71 @@ +import { ArenaTagSide } from "#app/data/arena-tag"; +import { ArenaTagType } from "#app/enums/arena-tag-type"; +import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Fairy Lock", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.FAIRY_LOCK, Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("double") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([ Moves.SPLASH, Moves.U_TURN ]); + }); + + it("Applies Fairy Lock tag for one turn, then apply Trapped tag for one turn", async () => { + await game.classicMode.startBattle([ Species.KLEFKI, Species.TYRUNT ]); + const playerPokemon = game.scene.getPlayerField(); + const enemyField = game.scene.getEnemyField(); + + game.move.select(Moves.FAIRY_LOCK); + game.move.select(Moves.SPLASH, 1); + await game.forceEnemyMove(Moves.SPLASH, 1); + await game.forceEnemyMove(Moves.SPLASH, 1); + await game.phaseInterceptor.to("BerryPhase"); + expect(game.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.PLAYER)).toBeDefined(); + expect(game.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.ENEMY)).toBeDefined(); + + await game.toNextTurn(); + + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.SPLASH, 1); + await game.forceEnemyMove(Moves.SPLASH, 1); + await game.phaseInterceptor.to("BerryPhase"); + expect(game.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.PLAYER)).not.toBeDefined(); + expect(game.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.ENEMY)).not.toBeDefined(); + + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(playerPokemon[1].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyField[0].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + expect(enemyField[1].getTag(BattlerTagType.TRAPPED)).toBeDefined(); + + await game.toNextTurn(); + expect(playerPokemon[0].getTag(BattlerTagType.TRAPPED)).not.toBeDefined(); + expect(playerPokemon[1].getTag(BattlerTagType.TRAPPED)).not.toBeDefined(); + expect(enemyField[0].getTag(BattlerTagType.TRAPPED)).not.toBeDefined(); + expect(enemyField[1].getTag(BattlerTagType.TRAPPED)).not.toBeDefined(); + }); +});