mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-28 12:32:44 +02:00
Revamp telekinesis tests; fix up stuff
This commit is contained in:
parent
e59bf5254f
commit
06b6143931
@ -1096,13 +1096,11 @@ export class GravityTag extends ArenaTag {
|
||||
onAdd(_arena: Arena): void {
|
||||
globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnAdd"));
|
||||
globalScene.getField(true).forEach(pokemon => {
|
||||
if (pokemon !== null) {
|
||||
pokemon.removeTag(BattlerTagType.FLOATING);
|
||||
pokemon.removeTag(BattlerTagType.TELEKINESIS);
|
||||
if (pokemon.getTag(BattlerTagType.FLYING)) {
|
||||
pokemon.addTag(BattlerTagType.INTERRUPTED);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -660,6 +660,10 @@ export class FlinchedTag extends BattlerTag {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag to cancel the target's action when knocked out of a flying move by Smack Down or Gravity.
|
||||
*/
|
||||
// TODO: This is not a very good way to cancel a semi invulnerable turn
|
||||
export class InterruptedTag extends BattlerTag {
|
||||
constructor(sourceMove: MoveId) {
|
||||
super(BattlerTagType.INTERRUPTED, BattlerTagLapseType.PRE_MOVE, 0, sourceMove);
|
||||
|
@ -5299,13 +5299,11 @@ export class VariableMoveTypeMultiplierAttr extends MoveAttr {
|
||||
}
|
||||
|
||||
export class NeutralDamageAgainstFlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!target.getTag(BattlerTagType.IGNORE_FLYING)) {
|
||||
const multiplier = args[0] as NumberHolder;
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder]): boolean {
|
||||
if (!target.isGrounded(true) && target.isOfType(PokemonType.FLYING)) {
|
||||
const multiplier = args[0];
|
||||
// When a flying type is hit, the first hit is always 1x multiplier.
|
||||
if (target.isOfType(PokemonType.FLYING)) {
|
||||
multiplier.value = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -5634,8 +5632,8 @@ export class LeechSeedAttr extends AddBattlerTagAttr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the appropriate battler tag for Smack Down and Thousand arrows
|
||||
* @extends AddBattlerTagAttr
|
||||
* Attribute to add the {@linkcode BattlerTagType.IGNORE_FLYING | IGNORE_FLYING} battler tag to the target
|
||||
* and remove any prior sources of ungroundedness.
|
||||
*/
|
||||
export class FallDownAttr extends AddBattlerTagAttr {
|
||||
constructor() {
|
||||
@ -5644,20 +5642,30 @@ export class FallDownAttr extends AddBattlerTagAttr {
|
||||
|
||||
/**
|
||||
* Adds Grounded Tag to the target and checks if fallDown message should be displayed
|
||||
* @param user the {@linkcode Pokemon} using the move
|
||||
* @param target the {@linkcode Pokemon} targeted by the move
|
||||
* @param move the {@linkcode Move} invoking this effect
|
||||
* @param user - The {@linkcode Pokemon} using the move
|
||||
* @param target - The {@linkcode Pokemon} targeted by the move
|
||||
* @param move - The {@linkcode Move} invoking this effect
|
||||
* @param args n/a
|
||||
* @returns `true` if the effect successfully applies; `false` otherwise
|
||||
* @returns Whether the target was successfully brought down to earth.
|
||||
*/
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
// Smack down and similar only add their tag if the pokemon is already ungrounded without considering semi-invulnerability
|
||||
if (!target.isGrounded(true)) {
|
||||
// Smack down and similar only add their tag if the pokemon is already ungrounded
|
||||
// TODO: Does this work if the target is semi-invulnerable?
|
||||
if (target.isGrounded(true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the target's flying/floating state and telekinesis, interrupting fly/bounce's activation.
|
||||
// TODO: This is not a good way to remove flying...
|
||||
target.removeTag(BattlerTagType.FLOATING);
|
||||
target.removeTag(BattlerTagType.TELEKINESIS);
|
||||
if (target.getTag(BattlerTagType.FLYING)) {
|
||||
target.addTag(BattlerTagType.INTERRUPTED);
|
||||
}
|
||||
|
||||
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fallDown", { targetPokemonName: getPokemonNameWithAffix(target) }));
|
||||
return super.apply(user, target, move, args);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -7975,6 +7983,7 @@ const phaseForcedSlower = (phase: MovePhase, target: Pokemon, trickRoom: boolean
|
||||
return phase.isForcedLast() && slower;
|
||||
};
|
||||
|
||||
// TODO: This needs to become unselectable, not merely fail
|
||||
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !globalScene.arena.getTag(ArenaTagType.GRAVITY);
|
||||
|
||||
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
|
||||
@ -9280,6 +9289,7 @@ export function initMoves() {
|
||||
.attr(RandomMovesetMoveAttr, invalidAssistMoves, true),
|
||||
new SelfStatusMove(MoveId.INGRAIN, PokemonType.GRASS, -1, 20, -1, 0, 3)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true)
|
||||
// NB: We add the tag directly to avoid removing Telekinesis' accuracy boost
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, true)
|
||||
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLOATING ], true),
|
||||
new AttackMove(MoveId.SUPERPOWER, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3)
|
||||
@ -9907,8 +9917,6 @@ export function initMoves() {
|
||||
.unimplemented(),
|
||||
new AttackMove(MoveId.SMACK_DOWN, PokemonType.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, -1, 0, 5)
|
||||
.attr(FallDownAttr)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
||||
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ])
|
||||
.attr(HitsTagAttr, BattlerTagType.FLYING)
|
||||
.makesContact(false)
|
||||
// TODO: Confirm if Smack Down & Thousand Arrows will ground semi-invulnerable Pokemon with No Guard, etc.
|
||||
@ -10364,8 +10372,6 @@ export function initMoves() {
|
||||
.attr(FallDownAttr)
|
||||
.attr(HitsTagAttr, BattlerTagType.FLYING)
|
||||
.attr(HitsTagAttr, BattlerTagType.FLOATING)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
||||
.attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ])
|
||||
.makesContact(false)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||
// TODO: Confirm if Smack Down & Thousand Arrows will ground semi-invulnerable Pokemon with No Guard, etc.
|
||||
|
@ -2464,7 +2464,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
// Handle flying v ground type immunity without removing flying type so effective types are still effective
|
||||
// Related to https://github.com/pagefaultgames/pokerogue/issues/524
|
||||
//
|
||||
if (moveType === PokemonType.GROUND && this.isGrounded(true)) {
|
||||
if (moveType === PokemonType.GROUND && this.isGrounded()) {
|
||||
const flyingIndex = types.indexOf(PokemonType.FLYING);
|
||||
if (flyingIndex > -1) {
|
||||
types.splice(flyingIndex, 1);
|
||||
|
@ -42,8 +42,8 @@ describe("Terrain -", () => {
|
||||
.disableCrits()
|
||||
.enemySpecies(SpeciesId.SNOM)
|
||||
.enemyAbility(AbilityId.STURDY)
|
||||
.ability(AbilityId.NO_GUARD)
|
||||
.passiveAbility(ability)
|
||||
.ability(ability)
|
||||
.passiveAbility(AbilityId.NO_GUARD)
|
||||
.enemyPassiveAbility(AbilityId.LEVITATE);
|
||||
});
|
||||
|
||||
@ -55,27 +55,31 @@ describe("Terrain -", () => {
|
||||
const powerSpy = vi.spyOn(allMoves[move], "calculateBattlePower");
|
||||
game.move.use(move);
|
||||
await game.move.forceEnemyMove(move);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
// Player grounded attack got boost while enemy ungrounded attack didn't
|
||||
expect(powerSpy).toHaveLastReturnedWith(allMoves[move].power);
|
||||
expect(powerSpy).toHaveNthReturnedWith(2, allMoves[move].power * 1.3);
|
||||
// Player grounded attack got boosted while enemy ungrounded attack didn't
|
||||
expect(powerSpy).toHaveLastReturnedWith(allMoves[move].power * 1.3);
|
||||
expect(powerSpy).toHaveNthReturnedWith(1, allMoves[move].power);
|
||||
});
|
||||
|
||||
it(`should change Terrain Pulse into a ${typeStr}-type move and double its base power`, async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
|
||||
|
||||
const blissey = game.field.getPlayerPokemon();
|
||||
const powerSpy = vi.spyOn(allMoves[MoveId.TERRAIN_PULSE], "calculateBattlePower");
|
||||
const typeSpy = vi.spyOn(blissey, "getMoveType");
|
||||
const playerTypeSpy = vi.spyOn(game.field.getPlayerPokemon(), "getMoveType");
|
||||
const enemyTypeSpy = vi.spyOn(game.field.getEnemyPokemon(), "getMoveType");
|
||||
|
||||
game.move.use(move);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
game.move.use(MoveId.TERRAIN_PULSE);
|
||||
await game.move.forceEnemyMove(MoveId.TERRAIN_PULSE);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
// player grounded terrain pulse was boosted & type converted; enemy ungrounded one wasn't
|
||||
expect(powerSpy).toHaveLastReturnedWith(allMoves[move].power * 2.6); // 2 * 1.3
|
||||
expect(typeSpy).toHaveLastReturnedWith(type);
|
||||
expect(playerTypeSpy).toHaveLastReturnedWith(type);
|
||||
expect(powerSpy).toHaveNthReturnedWith(1, allMoves[move].power);
|
||||
expect(enemyTypeSpy).toHaveNthReturnedWith(1, allMoves[MoveId.TERRAIN_PULSE].type);
|
||||
});
|
||||
});
|
||||
|
||||
@ -169,7 +173,7 @@ describe("Terrain -", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: Enable tests after terrain-msg branch is merged
|
||||
// TODO: Enable suites after terrain-fail-msg branch is merged
|
||||
describe.skip("Electric Terrain", () => {
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
@ -234,8 +238,7 @@ describe("Terrain -", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Enable tests after terrain-msg branch is merged
|
||||
describe("Misty Terrain", () => {
|
||||
describe.skip("Misty Terrain", () => {
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
@ -8,6 +7,9 @@ import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import type { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { HitCheckResult } from "#enums/hit-check-result";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
|
||||
describe("Moves - Telekinesis", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -26,114 +28,129 @@ describe("Moves - Telekinesis", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([MoveId.TELEKINESIS, MoveId.TACKLE, MoveId.MUD_SHOT, MoveId.SMACK_DOWN])
|
||||
.battleStyle("single")
|
||||
.enemySpecies(SpeciesId.SNORLAX)
|
||||
.enemyLevel(60)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyMoveset([MoveId.SPLASH]);
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
});
|
||||
|
||||
it("Telekinesis makes the affected vulnerable to most attacking moves regardless of accuracy", async () => {
|
||||
it("should cause opposing non-OHKO moves to always hit the target", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemyOpponent = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(MoveId.TELEKINESIS);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
const player = game.field.getPlayerPokemon();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.use(MoveId.TELEKINESIS);
|
||||
await game.toNextTurn();
|
||||
vi.spyOn(allMoves[MoveId.TACKLE], "accuracy", "get").mockReturnValue(0);
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.isFullHp()).toBe(false);
|
||||
|
||||
expect(enemy.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemy.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
|
||||
game.move.use(MoveId.TACKLE);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.move.forceMiss();
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(enemy.hp).toBeLessThan(enemy.getMaxHp());
|
||||
expect(player.getLastXMoves()[0].result).toBe(MoveResult.MISS);
|
||||
});
|
||||
|
||||
it("Telekinesis makes the affected airborne and immune to most Ground-moves", async () => {
|
||||
it("should render the target immune to Ground-moves and terrain", async () => {
|
||||
game.override.ability(AbilityId.ELECTRIC_SURGE);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemyOpponent = game.scene.getEnemyPokemon()!;
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.select(MoveId.TELEKINESIS);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
game.move.use(MoveId.TELEKINESIS);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.use(MoveId.EARTHQUAKE);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
const hitSpy = vi.spyOn(game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase, "hitCheck");
|
||||
|
||||
await game.toNextTurn();
|
||||
vi.spyOn(allMoves[MoveId.MUD_SHOT], "accuracy", "get").mockReturnValue(100);
|
||||
game.move.select(MoveId.MUD_SHOT);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.isFullHp()).toBe(true);
|
||||
|
||||
expect(enemy.hp).toBe(enemy.getMaxHp());
|
||||
expect(hitSpy).toHaveLastReturnedWith([HitCheckResult.NO_EFFECT, 0]);
|
||||
|
||||
game.move.use(MoveId.SPORE);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(enemy.status?.effect).toBe(StatusEffect.SLEEP);
|
||||
});
|
||||
|
||||
it("Telekinesis can still affect Pokemon that have been transformed into invalid Pokemon", async () => {
|
||||
game.override.enemyMoveset(MoveId.TRANSFORM);
|
||||
it("should still affect enemies transformed into invalid Pokemon", async () => {
|
||||
game.override.enemyAbility(AbilityId.IMPOSTER);
|
||||
await game.classicMode.startBattle([SpeciesId.DIGLETT]);
|
||||
|
||||
const enemyOpponent = game.scene.getEnemyPokemon()!;
|
||||
const enemyOpponent = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.use(MoveId.TELEKINESIS);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(MoveId.TELEKINESIS);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
expect(enemyOpponent.summonData.speciesForm?.speciesId).toBe(SpeciesId.DIGLETT);
|
||||
});
|
||||
|
||||
it("Moves like Smack Down and 1000 Arrows remove all effects of Telekinesis from the target Pokemon", async () => {
|
||||
it.each<{ name: string; move: MoveId }>([
|
||||
{ name: "Smack Down", move: MoveId.SMACK_DOWN },
|
||||
{ name: "Thousand Arrows", move: MoveId.THOUSAND_ARROWS },
|
||||
])("should be removed when hit by $name", async ({ move }) => {
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemyOpponent = game.scene.getEnemyPokemon()!;
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.select(MoveId.TELEKINESIS);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
|
||||
await game.toNextTurn();
|
||||
game.move.select(MoveId.SMACK_DOWN);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
|
||||
game.move.use(move);
|
||||
await game.toNextTurn();
|
||||
expect(enemy.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined();
|
||||
expect(enemy.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("Ingrain will remove the floating effect of Telekinesis, but not the 100% hit", async () => {
|
||||
game.override.enemyMoveset([MoveId.SPLASH, MoveId.INGRAIN]);
|
||||
it("should become grounded when Ingrain is used, but not remove the guaranteed hit effect", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
const enemyOpponent = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(MoveId.TELEKINESIS);
|
||||
await game.move.selectEnemyMove(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
const playerPokemon = game.field.getPlayerPokemon();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.use(MoveId.TELEKINESIS);
|
||||
await game.toNextTurn();
|
||||
vi.spyOn(allMoves[MoveId.MUD_SHOT], "accuracy", "get").mockReturnValue(0);
|
||||
game.move.select(MoveId.MUD_SHOT);
|
||||
await game.move.selectEnemyMove(MoveId.INGRAIN);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.INGRAIN)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
|
||||
game.move.use(MoveId.MUD_SHOT);
|
||||
await game.move.forceEnemyMove(MoveId.INGRAIN);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
await game.move.forceMiss();
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(enemy.getTag(BattlerTagType.TELEKINESIS)).toBeDefined();
|
||||
expect(enemy.getTag(BattlerTagType.INGRAIN)).toBeDefined();
|
||||
expect(enemy.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemy.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
expect(enemy.isGrounded()).toBe(false);
|
||||
expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||
});
|
||||
|
||||
it("should not be baton passed onto a mega gengar", async () => {
|
||||
it("should not be baton passable onto a mega gengar", async () => {
|
||||
game.override
|
||||
.moveset([MoveId.BATON_PASS])
|
||||
.enemyMoveset([MoveId.TELEKINESIS])
|
||||
.starterForms({ [SpeciesId.GENGAR]: 1 });
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.GENGAR]);
|
||||
game.move.select(MoveId.BATON_PASS);
|
||||
|
||||
game.move.use(MoveId.BATON_PASS);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
expect(game.scene.getPlayerPokemon()!.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined();
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(game.field.getPlayerPokemon().getTag(BattlerTagType.TELEKINESIS)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -26,11 +26,11 @@ describe("Moves - Thousand Arrows", () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleStyle("single")
|
||||
.enemySpecies(SpeciesId.SHUCKLE)
|
||||
.enemySpecies(SpeciesId.EELEKTROSS)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100)
|
||||
.enemyLevel(50)
|
||||
.ability(AbilityId.COMPOUND_EYES)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyAbility(AbilityId.STURDY)
|
||||
.moveset(MoveId.THOUSAND_ARROWS)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
});
|
||||
@ -39,32 +39,35 @@ describe("Moves - Thousand Arrows", () => {
|
||||
game.override.enemySpecies(SpeciesId.ARCHEOPS);
|
||||
await game.classicMode.startBattle([SpeciesId.ILLUMISE]);
|
||||
|
||||
const shuckle = game.scene.getEnemyPokemon()!;
|
||||
expect(shuckle.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
const archeops = game.scene.getEnemyPokemon()!;
|
||||
expect(archeops.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
|
||||
game.move.select(MoveId.THOUSAND_ARROWS);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
const hitSpy = vi.spyOn(game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase, "hitCheck");
|
||||
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(hitSpy).toHaveReturnedWith([expect.anything(), 1]);
|
||||
expect(shuckle.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(shuckle.hp).toBeLessThan(shuckle.getMaxHp());
|
||||
expect(archeops.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(archeops.isGrounded()).toBe(true);
|
||||
expect(archeops.hp).toBeLessThan(archeops.getMaxHp());
|
||||
});
|
||||
|
||||
it("should hit and ground targets with Levitate", async () => {
|
||||
game.override.enemyAbility(AbilityId.LEVITATE);
|
||||
it("should hit and ground targets with Levitate without affecting effectiveness", async () => {
|
||||
game.override.enemyPassiveAbility(AbilityId.LEVITATE);
|
||||
await game.classicMode.startBattle([SpeciesId.ILLUMISE]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
const eelektross = game.scene.getEnemyPokemon()!;
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
|
||||
game.move.select(MoveId.THOUSAND_ARROWS);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
const hitSpy = vi.spyOn(game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase, "hitCheck");
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp());
|
||||
expect(hitSpy).toHaveReturnedWith([expect.anything(), 2]);
|
||||
});
|
||||
|
||||
it("should hit and ground targets under the effects of Magnet Rise", async () => {
|
||||
@ -76,19 +79,19 @@ describe("Moves - Thousand Arrows", () => {
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
// ensure magnet rise suceeeded before getting knocked down
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
expect(enemyPokemon.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
const eelektross = game.field.getEnemyPokemon();
|
||||
expect(eelektross.getTag(BattlerTagType.FLOATING)).toBeDefined();
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(enemyPokemon.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
expect(eelektross.getTag(BattlerTagType.FLOATING)).toBeUndefined();
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp());
|
||||
});
|
||||
|
||||
it.each<{ name: string; move: MoveId }>([
|
||||
{ name: "Fly", move: MoveId.FLY },
|
||||
{ name: "Bounce", move: MoveId.BOUNCE },
|
||||
])("should cancel the target's Fly", async ({ move }) => {
|
||||
])("should hit through and cancel the target's $name", async ({ move }) => {
|
||||
await game.classicMode.startBattle([SpeciesId.ILLUMISE]);
|
||||
|
||||
game.move.select(MoveId.THOUSAND_ARROWS);
|
||||
@ -96,18 +99,19 @@ describe("Moves - Thousand Arrows", () => {
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
// Fly should've worked... until we smacked them
|
||||
const shuckle = game.field.getEnemyPokemon();
|
||||
expect(shuckle.getTag(BattlerTagType.FLYING)).toBeDefined();
|
||||
expect(shuckle.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
// Fly should've worked until hit
|
||||
const eelektross = game.field.getEnemyPokemon();
|
||||
expect(eelektross.getTag(BattlerTagType.FLYING)).toBeDefined();
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
expect(shuckle.getTag(BattlerTagType.FLYING)).toBeUndefined();
|
||||
expect(shuckle.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(shuckle.hp).toBeLessThan(shuckle.getMaxHp());
|
||||
await game.toEndOfTurn();
|
||||
expect(eelektross.getTag(BattlerTagType.FLYING)).toBeUndefined();
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp());
|
||||
});
|
||||
|
||||
it("should NOT ground semi-invulnerable targets unless already ungrounded", async () => {
|
||||
// TODO: verify behavior
|
||||
it.todo("should not ground semi-invulnerable targets unless already ungrounded", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.ILLUMISE]);
|
||||
|
||||
game.move.select(MoveId.THOUSAND_ARROWS);
|
||||
@ -115,10 +119,10 @@ describe("Moves - Thousand Arrows", () => {
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
const shuckle = game.field.getEnemyPokemon();
|
||||
expect(shuckle.isGrounded()).toBe(false);
|
||||
expect(shuckle.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
expect(shuckle.hp).toBeLessThan(shuckle.getMaxHp());
|
||||
const eelektross = game.field.getEnemyPokemon();
|
||||
expect(eelektross.isGrounded()).toBe(false);
|
||||
expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp());
|
||||
});
|
||||
|
||||
// TODO: Sky drop is currently unimplemented
|
||||
|
Loading…
Reference in New Issue
Block a user