import { allMoves } from "#app/data/move"; import { Type } from "#enums/type"; import { Weather } from "#app/data/weather"; import type { PlayerPokemon } from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { WeatherType } from "#enums/weather-type"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; describe("Abilities - Libero", () => { 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.battleType("single"); game.override.ability(Abilities.LIBERO); game.override.startingLevel(100); game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset([ Moves.ENDURE, Moves.ENDURE, Moves.ENDURE, Moves.ENDURE ]); }); test( "ability applies and changes a pokemon's type", async () => { game.override.moveset([ Moves.SPLASH ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, ); // Test for Gen9+ functionality, we are using previous funcionality test.skip( "ability applies only once per switch in", async () => { game.override.moveset([ Moves.SPLASH, Moves.AGILITY ]); await game.startBattle([ Species.MAGIKARP, Species.BULBASAUR ]); let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); game.move.select(Moves.AGILITY); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied.filter((a) => a === Abilities.LIBERO)).toHaveLength(1); const leadPokemonType = Type[leadPokemon.getTypes()[0]]; const moveType = Type[allMoves[Moves.AGILITY].type]; expect(leadPokemonType).not.toBe(moveType); await game.toNextTurn(); game.doSwitchPokemon(1); await game.toNextTurn(); game.doSwitchPokemon(1); await game.toNextTurn(); leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.SPLASH); }, ); test( "ability applies correctly even if the pokemon's move has a variable type", async () => { game.override.moveset([ Moves.WEATHER_BALL ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); game.move.select(Moves.WEATHER_BALL); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); expect(leadPokemon.getTypes()).toHaveLength(1); const leadPokemonType = Type[leadPokemon.getTypes()[0]], moveType = Type[Type.FIRE]; expect(leadPokemonType).toBe(moveType); }, ); test( "ability applies correctly even if the type has changed by another ability", async () => { game.override.moveset([ Moves.TACKLE ]); game.override.passiveAbility(Abilities.REFRIGERATE); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); expect(leadPokemon.getTypes()).toHaveLength(1); const leadPokemonType = Type[leadPokemon.getTypes()[0]], moveType = Type[Type.ICE]; expect(leadPokemonType).toBe(moveType); }, ); test( "ability applies correctly even if the pokemon's move calls another move", async () => { game.override.moveset([ Moves.NATURE_POWER ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; game.move.select(Moves.NATURE_POWER); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.AIR_SLASH); }, ); test( "ability applies correctly even if the pokemon's move is delayed / charging", async () => { game.override.moveset([ Moves.DIG ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.DIG); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.DIG); }, ); test( "ability applies correctly even if the pokemon's move misses", async () => { game.override.moveset([ Moves.TACKLE ]); game.override.enemyMoveset(Moves.SPLASH); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); await game.move.forceMiss(); await game.phaseInterceptor.to(TurnEndPhase); const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, ); test( "ability applies correctly even if the pokemon's move is protected against", async () => { game.override.moveset([ Moves.TACKLE ]); game.override.enemyMoveset([ Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, ); test( "ability applies correctly even if the pokemon's move fails because of type immunity", async () => { game.override.moveset([ Moves.TACKLE ]); game.override.enemySpecies(Species.GASTLY); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, ); test( "ability is not applied if pokemon's type is the same as the move's type", async () => { game.override.moveset([ Moves.SPLASH ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [ allMoves[Moves.SPLASH].type ]; game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, ); test( "ability is not applied if pokemon is terastallized", async () => { game.override.moveset([ Moves.SPLASH ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.isTerastallized = true; game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, ); test( "ability is not applied if pokemon uses struggle", async () => { game.override.moveset([ Moves.STRUGGLE ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.STRUGGLE); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, ); test( "ability is not applied if the pokemon's move fails", async () => { game.override.moveset([ Moves.BURN_UP ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.BURN_UP); await game.phaseInterceptor.to(TurnEndPhase); expect(leadPokemon.summonData.abilitiesApplied).not.toContain(Abilities.LIBERO); }, ); test( "ability applies correctly even if the pokemon's Trick-or-Treat fails", async () => { game.override.moveset([ Moves.TRICK_OR_TREAT ]); game.override.enemySpecies(Species.GASTLY); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.TRICK_OR_TREAT); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TRICK_OR_TREAT); }, ); test( "ability applies correctly and the pokemon curses itself", async () => { game.override.moveset([ Moves.CURSE ]); await game.startBattle([ Species.MAGIKARP ]); const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.move.select(Moves.CURSE); await game.phaseInterceptor.to(TurnEndPhase); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.CURSE); expect(leadPokemon.getTag(BattlerTagType.CURSED)).not.toBe(undefined); }, ); }); function testPokemonTypeMatchesDefaultMoveType(pokemon: PlayerPokemon, move: Moves) { expect(pokemon.summonData.abilitiesApplied).toContain(Abilities.LIBERO); expect(pokemon.getTypes()).toHaveLength(1); const pokemonType = Type[pokemon.getTypes()[0]], moveType = Type[allMoves[move].type]; expect(pokemonType).toBe(moveType); }