2024-09-13 19:14:10 -07:00
import { BattlerIndex } from "#app/battle" ;
2025-03-07 19:10:29 -06:00
import { PokemonType } from "#enums/pokemon-type" ;
2024-08-22 06:49:33 -07:00
import { BattlerTagType } from "#app/enums/battler-tag-type" ;
import { Moves } from "#app/enums/moves" ;
import { Species } from "#app/enums/species" ;
import { MoveEffectPhase } from "#app/phases/move-effect-phase" ;
import { TurnEndPhase } from "#app/phases/turn-end-phase" ;
2025-02-22 22:52:07 -06:00
import GameManager from "#test/testUtils/gameManager" ;
2024-07-25 16:10:38 -07:00
import Phaser from "phaser" ;
import { afterEach , beforeAll , beforeEach , describe , expect , test } from "vitest" ;
2024-06-23 09:48:49 -07:00
describe ( "Moves - Roost" , ( ) = > {
let phaserGame : Phaser.Game ;
let game : GameManager ;
beforeAll ( ( ) = > {
phaserGame = new Phaser . Game ( {
type : Phaser . HEADLESS ,
} ) ;
} ) ;
afterEach ( ( ) = > {
game . phaseInterceptor . restoreOg ( ) ;
} ) ;
beforeEach ( ( ) = > {
game = new GameManager ( phaserGame ) ;
2024-07-25 14:48:48 -07:00
game . override . battleType ( "single" ) ;
2024-09-12 12:27:47 -07:00
game . override . enemySpecies ( Species . RELICANTH ) ;
2024-07-25 15:29:19 -07:00
game . override . startingLevel ( 100 ) ;
2024-09-13 19:14:10 -07:00
game . override . enemyLevel ( 100 ) ;
2024-09-12 12:27:47 -07:00
game . override . enemyMoveset ( Moves . EARTHQUAKE ) ;
2025-03-09 16:13:25 -05:00
game . override . moveset ( [ Moves . ROOST , Moves . BURN_UP , Moves . DOUBLE_SHOCK ] ) ;
2024-06-23 09:48:49 -07:00
} ) ;
2024-09-12 12:27:47 -07:00
/ * *
* Roost ' s behavior should be defined as :
* The pokemon loses its flying type for a turn . If the pokemon was ungroundd solely due to being a flying type , it will be grounded until end of turn .
* 1 . Pure Flying type pokemon - > become normal type until end of turn
* 2 . Dual Flying / X type pokemon - > become type X until end of turn
* 3 . Pokemon that use burn up into roost ( ex . Moltres ) - > become flying due to burn up , then typeless until end of turn after using roost
* 4 . If a pokemon is afflicted with Forest ' s Curse or Trick or treat , dual type pokemon will become 3 type pokemon after the flying type is regained
* Pure flying types become ( Grass or Ghost ) and then back to flying / ( Grass or Ghost ) ,
* and pokemon post Burn up become ( )
* 5 . If a pokemon is also ungrounded due to other reasons ( such as levitate ) , it will stay ungrounded post roost , despite not being flying type .
* 6 . Non flying types using roost ( such as dunsparce ) are already grounded , so this move will only heal and have no other effects .
* /
2025-03-09 16:13:25 -05:00
test ( "Non flying type uses roost -> no type change, took damage" , async ( ) = > {
await game . classicMode . startBattle ( [ Species . DUNSPARCE ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be normal type, and NOT flying type
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . NORMAL ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Lose HP, still normal type
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBeLessThan ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . NORMAL ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
} ) ;
test ( "Pure flying type -> becomes normal after roost and takes damage from ground moves -> regains flying" , async ( ) = > {
await game . classicMode . startBattle ( [ Species . TORNADUS ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be normal type, and NOT flying type
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . NORMAL ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeFalsy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should have lost HP and is now back to being pure flying
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBeLessThan ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . NORMAL ) . toBeFalsy ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
2024-09-12 12:27:47 -07:00
2025-03-09 16:13:25 -05:00
test ( "Dual X/flying type -> becomes type X after roost and takes damage from ground moves -> regains flying" , async ( ) = > {
await game . classicMode . startBattle ( [ Species . HAWLUCHA ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be pure fighting type and grounded
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FIGHTING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should have lost HP and is now back to being fighting/flying
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBeLessThan ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FIGHTING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes [ 1 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
test ( "Pokemon with levitate after using roost should lose flying type but still be unaffected by ground moves" , async ( ) = > {
game . override . starterForms ( { [ Species . ROTOM ] : 4 } ) ;
await game . classicMode . startBattle ( [ Species . ROTOM ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be pure eletric type and grounded
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . ELECTRIC ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should have lost HP and is now back to being electric/flying
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBe ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . ELECTRIC ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes [ 1 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
test ( "A fire/flying type that uses burn up, then roost should be typeless until end of turn" , async ( ) = > {
await game . classicMode . startBattle ( [ Species . MOLTRES ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . BURN_UP ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be pure flying type after burn up
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be typeless type after roost and is grounded
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . getTag ( BattlerTagType . ROOSTED ) ) . toBeDefined ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . UNKNOWN ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should go back to being pure flying and have taken damage from earthquake, and is ungrounded again
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBeLessThan ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
test ( "An electric/flying type that uses double shock, then roost should be typeless until end of turn" , async ( ) = > {
game . override . enemySpecies ( Species . ZEKROM ) ;
await game . classicMode . startBattle ( [ Species . ZAPDOS ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
const playerPokemonStartingHP = playerPokemon . hp ;
game . move . select ( Moves . DOUBLE_SHOCK ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be pure flying type after burn up
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
game . move . select ( Moves . ROOST ) ;
await game . setTurnOrder ( [ BattlerIndex . PLAYER , BattlerIndex . ENEMY ] ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
// Should only be typeless type after roost and is grounded
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . getTag ( BattlerTagType . ROOSTED ) ) . toBeDefined ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . UNKNOWN ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should go back to being pure flying and have taken damage from earthquake, and is ungrounded again
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemon . hp ) . toBeLessThan ( playerPokemonStartingHP ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FLYING ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
test ( "Dual Type Pokemon afflicted with Forests Curse/Trick or Treat and post roost will become dual type and then become 3 type at end of turn" , async ( ) = > {
game . override . enemyMoveset ( [
Moves . TRICK_OR_TREAT ,
Moves . TRICK_OR_TREAT ,
Moves . TRICK_OR_TREAT ,
Moves . TRICK_OR_TREAT ,
] ) ;
await game . classicMode . startBattle ( [ Species . MOLTRES ] ) ;
const playerPokemon = game . scene . getPlayerPokemon ( ) ! ;
game . move . select ( Moves . ROOST ) ;
await game . phaseInterceptor . to ( MoveEffectPhase ) ;
let playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes [ 0 ] === PokemonType . FIRE ) . toBeTruthy ( ) ;
expect ( playerPokemonTypes . length === 1 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeTruthy ( ) ;
await game . phaseInterceptor . to ( TurnEndPhase ) ;
// Should be fire/flying/ghost
playerPokemonTypes = playerPokemon . getTypes ( ) ;
expect ( playerPokemonTypes . filter ( type = > type === PokemonType . FLYING ) ) . toHaveLength ( 1 ) ;
expect ( playerPokemonTypes . filter ( type = > type === PokemonType . FIRE ) ) . toHaveLength ( 1 ) ;
expect ( playerPokemonTypes . filter ( type = > type === PokemonType . GHOST ) ) . toHaveLength ( 1 ) ;
expect ( playerPokemonTypes . length === 3 ) . toBeTruthy ( ) ;
expect ( playerPokemon . isGrounded ( ) ) . toBeFalsy ( ) ;
} ) ;
2024-06-23 09:48:49 -07:00
} ) ;