diff --git a/public/locales b/public/locales index 7898c0018a7..fa35780fed7 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 7898c0018a70601a6ead76c9dd497ff966cc2e2a +Subproject commit fa35780fed762017c89d1e9ece8a2779dff56c4d diff --git a/src/@types/api/pokerogue-account-api.ts b/src/@types/api/pokerogue-account-api.ts index 7bcdc766664..779592483fb 100644 --- a/src/@types/api/pokerogue-account-api.ts +++ b/src/@types/api/pokerogue-account-api.ts @@ -15,3 +15,10 @@ export interface AccountRegisterRequest { username: string; password: string; } + +export interface AccountChangePwRequest { + password: string; +} +export interface AccountChangePwResponse { + success: boolean; +} diff --git a/src/@types/move-types.ts b/src/@types/move-types.ts index 5f8d7e8777e..ff44c665e48 100644 --- a/src/@types/move-types.ts +++ b/src/@types/move-types.ts @@ -16,7 +16,7 @@ export type * from "#moves/move"; * Map of move subclass names to their respective classes. * Does not include the ChargeMove subclasses. For that, use `ChargingMoveClassMap`. * - * @privateremarks + * @privateRemarks * The `never` field (`declare private _: never`) in some classes is necessary * to ensure typescript does not improperly narrow a failed `is` guard to `never`. * diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 8c8906be2b0..271cde1aaa9 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -242,8 +242,8 @@ export class BattleScene extends SceneBase { public battleStyle: BattleStyle = BattleStyle.SWITCH; /** * Defines whether or not to show type effectiveness hints - * - true: No hints - * - false: Show hints for moves + * - true: Show hints for moves + * - false: No hints */ public typeHints = false; @@ -478,8 +478,8 @@ export class BattleScene extends SceneBase { this.uiContainer = uiContainer; - const overlayWidth = this.game.canvas.width / 6; - const overlayHeight = this.game.canvas.height / 6 - 48; + const overlayWidth = this.scaledCanvas.width; + const overlayHeight = this.scaledCanvas.height - 48; this.fieldOverlay = this.add.rectangle(0, overlayHeight * -1 - 48, overlayWidth, overlayHeight, 0x424242); this.fieldOverlay.setName("rect-field-overlay"); this.fieldOverlay.setOrigin(0, 0); @@ -537,34 +537,29 @@ export class BattleScene extends SceneBase { this.candyBar.setup(); this.fieldUI.add(this.candyBar); - this.biomeWaveText = addTextObject( - this.game.canvas.width / 6 - 2, - 0, - startingWave.toString(), - TextStyle.BATTLE_INFO, - ); + this.biomeWaveText = addTextObject(this.scaledCanvas.width - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO); this.biomeWaveText.setName("text-biome-wave"); this.biomeWaveText.setOrigin(1, 0.5); this.fieldUI.add(this.biomeWaveText); - this.moneyText = addTextObject(this.game.canvas.width / 6 - 2, 0, "", TextStyle.MONEY); + this.moneyText = addTextObject(this.scaledCanvas.width - 2, 0, "", TextStyle.MONEY); this.moneyText.setName("text-money"); this.moneyText.setOrigin(1, 0.5); this.fieldUI.add(this.moneyText); - this.scoreText = addTextObject(this.game.canvas.width / 6 - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); + this.scoreText = addTextObject(this.scaledCanvas.width - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.scoreText.setName("text-score"); this.scoreText.setOrigin(1, 0.5); this.fieldUI.add(this.scoreText); - this.luckText = addTextObject(this.game.canvas.width / 6 - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); + this.luckText = addTextObject(this.scaledCanvas.width - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" }); this.luckText.setName("text-luck"); this.luckText.setOrigin(1, 0.5); this.luckText.setVisible(false); this.fieldUI.add(this.luckText); this.luckLabelText = addTextObject( - this.game.canvas.width / 6 - 2, + this.scaledCanvas.width - 2, 0, i18next.t("common:luckIndicator"), TextStyle.PARTY, @@ -586,10 +581,7 @@ export class BattleScene extends SceneBase { this.spriteSparkleHandler = new PokemonSpriteSparkleHandler(); this.spriteSparkleHandler.setup(); - this.pokemonInfoContainer = new PokemonInfoContainer( - this.game.canvas.width / 6 + 52, - -(this.game.canvas.height / 6) + 66, - ); + this.pokemonInfoContainer = new PokemonInfoContainer(this.scaledCanvas.width + 52, -this.scaledCanvas.height + 66); this.pokemonInfoContainer.setup(); this.fieldUI.add(this.pokemonInfoContainer); @@ -2054,7 +2046,7 @@ export class BattleScene extends SceneBase { } else { this.luckText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969); } - this.luckLabelText.setX(this.game.canvas.width / 6 - 2 - (this.luckText.displayWidth + 2)); + this.luckLabelText.setX(this.scaledCanvas.width - 2 - (this.luckText.displayWidth + 2)); this.tweens.add({ targets: labels, duration: duration, @@ -2088,7 +2080,7 @@ export class BattleScene extends SceneBase { const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible()).length; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; this.biomeWaveText.setY( - -(this.game.canvas.height / 6) + + -this.scaledCanvas.height + (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) + biomeWaveTextHeight / 2, ); @@ -2100,7 +2092,7 @@ export class BattleScene extends SceneBase { const offsetY = (this.scoreText.visible ? this.scoreText : this.moneyText).y + 15; this.partyExpBar.setY(offsetY); this.candyBar.setY(offsetY + 15); - this.ui?.achvBar.setY(this.game.canvas.height / 6 + offsetY); + this.ui?.achvBar.setY(this.scaledCanvas.height + offsetY); } /** diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 0ee1a51a78e..336d45fed66 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1284,7 +1284,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { /** * Set stat stages when the user gets hit by a critical hit * - * @privateremarks + * @privateRemarks * It is the responsibility of the caller to ensure that this ability attribute is only applied * when the user has been hit by a critical hit; such an event is not checked here. * @@ -1768,7 +1768,7 @@ export interface AddSecondStrikeAbAttrParams extends Omit 0) { const lastMoveUsed = moveHistory[moveHistory.length - 1]; - if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) { + if (forbiddenAttackingMoves.includes(lastMoveUsed.move)) { return false; } } // Dragon Tail and Circle Throw switch out Pokémon before the Ability activates. - const fordbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW]; + const forbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW]; if (source) { const enemyMoveHistory = source.getMoveHistory(); if (enemyMoveHistory.length > 0) { const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1]; // Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop. if ( - fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || + forbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) ) { return false; diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index 1298e80c362..d8297636393 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -1641,10 +1641,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [] }, [BiomeId.PLAINS]: { [BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.TWINS ], @@ -1652,10 +1649,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.CILAN, TrainerType.CHILI, TrainerType.CRESS, TrainerType.CHEREN ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.CILAN, TrainerType.CHILI, TrainerType.CRESS, TrainerType.CHEREN ] }, [BiomeId.GRASS]: { [BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.SCHOOL_KID ], @@ -1663,10 +1657,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.ERIKA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.ERIKA ] }, [BiomeId.TALL_GRASS]: { [BiomePoolTier.COMMON]: [], @@ -1674,10 +1665,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.GARDENIA, TrainerType.VIOLA, TrainerType.BRASSIUS ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.GARDENIA, TrainerType.VIOLA, TrainerType.BRASSIUS ] }, [BiomeId.METROPOLIS]: { [BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.CLERK, TrainerType.CYCLIST, TrainerType.OFFICER, TrainerType.WAITER ], @@ -1685,10 +1673,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.ARTIST, TrainerType.RICH_KID ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.WHITNEY, TrainerType.NORMAN, TrainerType.IONO, TrainerType.LARRY ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.WHITNEY, TrainerType.NORMAN, TrainerType.IONO, TrainerType.LARRY ] }, [BiomeId.FOREST]: { [BiomePoolTier.COMMON]: [ TrainerType.RANGER ], @@ -1696,10 +1681,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.BUGSY, TrainerType.BURGH, TrainerType.KATY ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.BUGSY, TrainerType.BURGH, TrainerType.KATY ] }, [BiomeId.SEA]: { [BiomePoolTier.COMMON]: [ TrainerType.SAILOR, TrainerType.SWIMMER ], @@ -1707,10 +1689,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.MARLON ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.MARLON ] }, [BiomeId.SWAMP]: { [BiomePoolTier.COMMON]: [ TrainerType.PARASOL_LADY ], @@ -1718,10 +1697,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.JANINE, TrainerType.ROXIE ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.JANINE, TrainerType.ROXIE ] }, [BiomeId.BEACH]: { [BiomePoolTier.COMMON]: [ TrainerType.FISHERMAN, TrainerType.SAILOR ], @@ -1729,10 +1705,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.MISTY, TrainerType.KOFU ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.MISTY, TrainerType.KOFU ] }, [BiomeId.LAKE]: { [BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.FISHERMAN, TrainerType.PARASOL_LADY ], @@ -1740,21 +1713,15 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.CRASHER_WAKE ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.CRASHER_WAKE ] }, [BiomeId.SEABED]: { - [BiomePoolTier.COMMON]: [], + [BiomePoolTier.COMMON]: [ TrainerType.SWIMMER ], [BiomePoolTier.UNCOMMON]: [], [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.JUAN ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.JUAN ] }, [BiomeId.MOUNTAIN]: { [BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.BLACK_BELT, TrainerType.HIKER ], @@ -1762,10 +1729,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.FALKNER, TrainerType.WINONA, TrainerType.SKYLA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.FALKNER, TrainerType.WINONA, TrainerType.SKYLA ] }, [BiomeId.BADLANDS]: { [BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ], @@ -1773,10 +1737,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.CLAY, TrainerType.GRANT ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.CLAY, TrainerType.GRANT ] }, [BiomeId.CAVE]: { [BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.HIKER ], @@ -1784,10 +1745,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.BROCK, TrainerType.ROXANNE, TrainerType.ROARK ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.BROCK, TrainerType.ROXANNE, TrainerType.ROARK ] }, [BiomeId.DESERT]: { [BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.SCIENTIST ], @@ -1795,10 +1753,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.GORDIE ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.GORDIE ] }, [BiomeId.ICE_CAVE]: { [BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ], @@ -1806,10 +1761,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.PRYCE, TrainerType.BRYCEN, TrainerType.WULFRIC, TrainerType.GRUSHA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.PRYCE, TrainerType.BRYCEN, TrainerType.WULFRIC, TrainerType.GRUSHA ] }, [BiomeId.MEADOW]: { [BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.MUSICIAN, TrainerType.PARASOL_LADY ], @@ -1817,10 +1769,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.LENORA, TrainerType.MILO ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.LENORA, TrainerType.MILO ] }, [BiomeId.POWER_PLANT]: { [BiomePoolTier.COMMON]: [ TrainerType.GUITARIST, TrainerType.WORKER ], @@ -1828,10 +1777,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.VOLKNER, TrainerType.ELESA, TrainerType.CLEMONT ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.VOLKNER, TrainerType.ELESA, TrainerType.CLEMONT ] }, [BiomeId.VOLCANO]: { [BiomePoolTier.COMMON]: [ TrainerType.FIREBREATHER ], @@ -1839,10 +1785,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.BLAINE, TrainerType.FLANNERY, TrainerType.KABU ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.BLAINE, TrainerType.FLANNERY, TrainerType.KABU ] }, [BiomeId.GRAVEYARD]: { [BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC ], @@ -1850,10 +1793,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.MORTY, TrainerType.ALLISTER, TrainerType.RYME ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.MORTY, TrainerType.ALLISTER, TrainerType.RYME ] }, [BiomeId.DOJO]: { [BiomePoolTier.COMMON]: [ TrainerType.BLACK_BELT ], @@ -1861,10 +1801,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.BRAWLY, TrainerType.MAYLENE, TrainerType.KORRINA, TrainerType.BEA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.BRAWLY, TrainerType.MAYLENE, TrainerType.KORRINA, TrainerType.BEA ] }, [BiomeId.FACTORY]: { [BiomePoolTier.COMMON]: [ TrainerType.WORKER ], @@ -1872,10 +1809,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.JASMINE, TrainerType.BYRON ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.JASMINE, TrainerType.BYRON ] }, [BiomeId.RUINS]: { [BiomePoolTier.COMMON]: [ TrainerType.PSYCHIC, TrainerType.SCIENTIST ], @@ -1883,10 +1817,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.SABRINA, TrainerType.TATE, TrainerType.LIZA, TrainerType.TULIP ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.SABRINA, TrainerType.TATE, TrainerType.LIZA, TrainerType.TULIP ] }, [BiomeId.WASTELAND]: { [BiomePoolTier.COMMON]: [ TrainerType.VETERAN ], @@ -1894,10 +1825,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.CLAIR, TrainerType.DRAYDEN, TrainerType.RAIHAN ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.CLAIR, TrainerType.DRAYDEN, TrainerType.RAIHAN ] }, [BiomeId.ABYSS]: { [BiomePoolTier.COMMON]: [], @@ -1905,10 +1833,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.MARNIE ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.MARNIE ] }, [BiomeId.SPACE]: { [BiomePoolTier.COMMON]: [], @@ -1916,10 +1841,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.OLYMPIA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.OLYMPIA ] }, [BiomeId.CONSTRUCTION_SITE]: { [BiomePoolTier.COMMON]: [ TrainerType.OFFICER, TrainerType.WORKER ], @@ -1927,10 +1849,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.LT_SURGE, TrainerType.CHUCK, TrainerType.WATTSON ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.LT_SURGE, TrainerType.CHUCK, TrainerType.WATTSON ] }, [BiomeId.JUNGLE]: { [BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.RANGER ], @@ -1938,10 +1857,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.RAMOS ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.RAMOS ] }, [BiomeId.FAIRY_CAVE]: { [BiomePoolTier.COMMON]: [ TrainerType.BEAUTY ], @@ -1949,10 +1865,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.VALERIE, TrainerType.OPAL, TrainerType.BEDE ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.VALERIE, TrainerType.OPAL, TrainerType.BEDE ] }, [BiomeId.TEMPLE]: { [BiomePoolTier.COMMON]: [], @@ -1960,10 +1873,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.FANTINA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.FANTINA ] }, [BiomeId.SLUM]: { [BiomePoolTier.COMMON]: [ TrainerType.BIKER, TrainerType.OFFICER, TrainerType.ROUGHNECK ], @@ -1971,10 +1881,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.PIERS ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.PIERS ] }, [BiomeId.SNOWY_FOREST]: { [BiomePoolTier.COMMON]: [ TrainerType.SNOW_WORKER ], @@ -1982,10 +1889,7 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.CANDICE, TrainerType.MELONY ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.CANDICE, TrainerType.MELONY ] }, [BiomeId.ISLAND]: { [BiomePoolTier.COMMON]: [ TrainerType.RICH_KID ], @@ -1993,21 +1897,15 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.NESSA ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.NESSA ] }, [BiomeId.LABORATORY]: { - [BiomePoolTier.COMMON]: [], + [BiomePoolTier.COMMON]: [ TrainerType.SCIENTIST ], [BiomePoolTier.UNCOMMON]: [], [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [ TrainerType.GIOVANNI ], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [ TrainerType.GIOVANNI ] }, [BiomeId.END]: { [BiomePoolTier.COMMON]: [], @@ -2015,13 +1913,9 @@ export const biomeTrainerPools: BiomeTrainerPools = { [BiomePoolTier.RARE]: [], [BiomePoolTier.SUPER_RARE]: [], [BiomePoolTier.ULTRA_RARE]: [], - [BiomePoolTier.BOSS]: [], - [BiomePoolTier.BOSS_RARE]: [], - [BiomePoolTier.BOSS_SUPER_RARE]: [], - [BiomePoolTier.BOSS_ULTRA_RARE]: [] + [BiomePoolTier.BOSS]: [] } }; - export function initBiomes() { const pokemonBiomes = [ [ SpeciesId.BULBASAUR, PokemonType.GRASS, PokemonType.POISON, [ diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 2f540f5e1b9..1199ac581a6 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2055,7 +2055,7 @@ export class TruantTag extends AbilityBattlerTag { const lastMove = pokemon.getLastXMoves()[0]; if (!lastMove || lastMove.move === MoveId.NONE) { - // Don't interrupt move if last move was `Moves.NONE` OR no prior move was found + // Don't interrupt move if last move was `MoveId.NONE` OR no prior move was found return true; } @@ -2115,8 +2115,8 @@ export class SlowStartTag extends AbilityBattlerTag { export class HighestStatBoostTag extends AbilityBattlerTag { public declare readonly tagType: HighestStatBoostTagType; - public stat: Stat; - public multiplier: number; + public stat: EffectiveStat = Stat.ATK; + public multiplier = 1.3; constructor(tagType: HighestStatBoostTagType, ability: AbilityId) { super(tagType, ability, BattlerTagLapseType.CUSTOM, 1); @@ -2128,28 +2128,28 @@ export class HighestStatBoostTag extends AbilityBattlerTag { */ public override loadTag(source: BaseBattlerTag & Pick): void { super.loadTag(source); - this.stat = source.stat as Stat; + this.stat = source.stat; this.multiplier = source.multiplier; } onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - let highestStat: EffectiveStat; - EFFECTIVE_STATS.map(s => - pokemon.getEffectiveStat(s, undefined, undefined, undefined, undefined, undefined, undefined, undefined, true), - ).reduce((highestValue: number, value: number, i: number) => { - if (value > highestValue) { - highestStat = EFFECTIVE_STATS[i]; - return value; - } - return highestValue; - }, 0); + const highestStat = EFFECTIVE_STATS.reduce( + (curr: [EffectiveStat, number], stat: EffectiveStat) => { + const value = pokemon.getEffectiveStat(stat, undefined, undefined, true, true, true, false, true, true); + if (value > curr[1]) { + curr[0] = stat; + curr[1] = value; + } + return curr; + }, + [Stat.ATK, 0], + )[0]; - highestStat = highestStat!; // tell TS compiler it's defined! this.stat = highestStat; - this.multiplier = this.stat === Stat.SPD ? 1.5 : 1.3; + this.multiplier = highestStat === Stat.SPD ? 1.5 : 1.3; globalScene.phaseManager.queueMessage( i18next.t("battlerTags:highestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), @@ -2614,7 +2614,7 @@ export class IceFaceBlockDamageTag extends FormBlockDamageTag { */ export class CommandedTag extends SerializableBattlerTag { public override readonly tagType = BattlerTagType.COMMANDED; - public readonly tatsugiriFormKey: string; + public readonly tatsugiriFormKey: string = "curly"; constructor(sourceId: number) { super(BattlerTagType.COMMANDED, BattlerTagLapseType.CUSTOM, 0, MoveId.NONE, sourceId); @@ -2668,7 +2668,7 @@ export class StockpilingTag extends SerializableBattlerTag { super(BattlerTagType.STOCKPILING, BattlerTagLapseType.CUSTOM, 1, sourceMove); } - private onStatStagesChanged: StatStageChangeCallback = (_, statsChanged, statChanges) => { + private onStatStagesChanged(_: Pokemon | null, statsChanged: BattleStat[], statChanges: number[]) { const defChange = statChanges[statsChanged.indexOf(Stat.DEF)] ?? 0; const spDefChange = statChanges[statsChanged.indexOf(Stat.SPDEF)] ?? 0; @@ -2678,7 +2678,11 @@ export class StockpilingTag extends SerializableBattlerTag { if (spDefChange) { this.statChangeCounts[Stat.SPDEF]++; } - }; + + // Removed during bundling; used to ensure this method's signature retains parity + // with the `StatStageChangeCallback` type. + this.onStatStagesChanged satisfies StatStageChangeCallback; + } public override loadTag( source: BaseBattlerTag & Pick, @@ -2718,7 +2722,7 @@ export class StockpilingTag extends SerializableBattlerTag { true, false, true, - this.onStatStagesChanged, + this.onStatStagesChanged.bind(this), ); } } diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 1a1a3774f8f..724d1f302da 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,29 +1,26 @@ import type { FixedBattleConfig } from "#app/battle"; import { getRandomTrainerFunc } from "#app/battle"; import { defaultStarterSpecies } from "#app/constants"; -import { globalScene } from "#app/global-scene"; -import { pokemonEvolutions } from "#balance/pokemon-evolutions"; import { speciesStarterCosts } from "#balance/starters"; -import { pokemonFormChanges } from "#data/pokemon-forms"; import type { PokemonSpecies } from "#data/pokemon-species"; import { BattleType } from "#enums/battle-type"; -import { ChallengeType } from "#enums/challenge-type"; import { Challenges } from "#enums/challenges"; import { TypeColor, TypeShadow } from "#enums/color"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { ModifierTier } from "#enums/modifier-tier"; -import type { MoveId } from "#enums/move-id"; +import { MoveId } from "#enums/move-id"; import type { MoveSourceType } from "#enums/move-source-type"; import { Nature } from "#enums/nature"; import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { TrainerVariant } from "#enums/trainer-variant"; -import type { Pokemon } from "#field/pokemon"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { Trainer } from "#field/trainer"; +import type { ModifierTypeOption } from "#modifiers/modifier-type"; import { PokemonMove } from "#moves/pokemon-move"; import type { DexAttrProps, GameData } from "#system/game-data"; -import { BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common"; +import { type BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common"; import { deepCopy } from "#utils/data"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils"; import { toCamelCase, toSnakeCase } from "#utils/strings"; @@ -341,6 +338,83 @@ export abstract class Challenge { applyFlipStat(_pokemon: Pokemon, _baseStats: number[]) { return false; } + + /** + * An apply function for PARTY_HEAL. Derived classes should alter this. + * @param _status - Whether party healing is enabled or not + * @returns Whether this function did anything + */ + applyPartyHeal(_status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for SHOP. Derived classes should alter this. + * @param _status - Whether the shop is or is not available after a wave + * @returns Whether this function did anything + */ + applyShop(_status: BooleanHolder) { + return false; + } + + /** + * An apply function for POKEMON_ADD_TO_PARTY. Derived classes should alter this. + * @param _pokemon - The pokemon being caught + * @param _status - Whether the pokemon can be added to the party or not + * @return Whether this function did anything + */ + applyPokemonAddToParty(_pokemon: EnemyPokemon, _status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for POKEMON_FUSION. Derived classes should alter this. + * @param _pokemon - The pokemon being checked + * @param _status - Whether the selected pokemon is allowed to fuse or not + * @returns Whether this function did anything + */ + applyPokemonFusion(_pokemon: PlayerPokemon, _status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for POKEMON_MOVE. Derived classes should alter this. + * @param _moveId - The {@linkcode MoveId} being checked + * @param _status - A {@linkcode BooleanHolder} containing the move's usability status + * @returns Whether this function did anything + */ + applyPokemonMove(_moveId: MoveId, _status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for SHOP_ITEM. Derived classes should alter this. + * @param _shopItem - The item being checked + * @param _status - Whether the item should be added to the shop or not + * @returns Whether this function did anything + */ + applyShopItem(_shopItem: ModifierTypeOption | null, _status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for WAVE_REWARD. Derived classes should alter this. + * @param _reward - The reward being checked + * @param _status - Whether the reward should be added to the reward options or not + * @returns Whether this function did anything + */ + applyWaveReward(_reward: ModifierTypeOption | null, _status: BooleanHolder): boolean { + return false; + } + + /** + * An apply function for PREVENT_REVIVE. Derived classes should alter this. + * @param _status - Whether fainting is a permanent status or not + * @returns Whether this function did anything + */ + applyPreventRevive(_status: BooleanHolder): boolean { + return false; + } } type ChallengeCondition = (data: GameData) => boolean; @@ -864,208 +938,108 @@ export class LowerStarterPointsChallenge extends Challenge { } /** - * Apply all challenges that modify starter choice. - * @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE - * @param pokemon {@link PokemonSpecies} The pokemon to check the validity of. - * @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. - * @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon. - * @returns True if any challenge was successfully applied. + * Implements a No Support challenge */ -export function applyChallenges( - challengeType: ChallengeType.STARTER_CHOICE, - pokemon: PokemonSpecies, - valid: BooleanHolder, - dexAttr: DexAttrProps, -): boolean; -/** - * Apply all challenges that modify available total starter points. - * @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS - * @param points {@link NumberHolder} The amount of points you have available. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean; -/** - * Apply all challenges that modify the cost of a starter. - * @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST - * @param species {@link SpeciesId} The pokemon to change the cost of. - * @param points {@link NumberHolder} The cost of the pokemon. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.STARTER_COST, - species: SpeciesId, - cost: NumberHolder, -): boolean; -/** - * Apply all challenges that modify a starter after selection. - * @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY - * @param pokemon {@link Pokemon} The starter pokemon to modify. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pokemon: Pokemon): boolean; -/** - * Apply all challenges that what pokemon you can have in battle. - * @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE - * @param pokemon {@link Pokemon} The pokemon to check the validity of. - * @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.POKEMON_IN_BATTLE, - pokemon: Pokemon, - valid: BooleanHolder, -): boolean; -/** - * Apply all challenges that modify what fixed battles there are. - * @param challengeType {@link ChallengeType} ChallengeType.FIXED_BATTLES - * @param waveIndex {@link Number} The current wave index. - * @param battleConfig {@link FixedBattleConfig} The battle config to modify. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.FIXED_BATTLES, - waveIndex: number, - battleConfig: FixedBattleConfig, -): boolean; -/** - * Apply all challenges that modify type effectiveness. - * @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS - * @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean; -/** - * Apply all challenges that modify what level AI are. - * @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL - * @param level {@link NumberHolder} The generated level of the pokemon. - * @param levelCap {@link Number} The maximum level cap for the current wave. - * @param isTrainer {@link Boolean} Whether this is a trainer pokemon. - * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.AI_LEVEL, - level: NumberHolder, - levelCap: number, - isTrainer: boolean, - isBoss: boolean, -): boolean; -/** - * Apply all challenges that modify how many move slots the AI has. - * @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS - * @param pokemon {@link Pokemon} The pokemon being considered. - * @param moveSlots {@link NumberHolder} The amount of move slots. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.AI_MOVE_SLOTS, - pokemon: Pokemon, - moveSlots: NumberHolder, -): boolean; -/** - * Apply all challenges that modify whether a pokemon has its passive. - * @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS - * @param pokemon {@link Pokemon} The pokemon to modify. - * @param hasPassive {@link BooleanHolder} Whether it has its passive. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.PASSIVE_ACCESS, - pokemon: Pokemon, - hasPassive: BooleanHolder, -): boolean; -/** - * Apply all challenges that modify the game modes settings. - * @param challengeType {@link ChallengeType} ChallengeType.GAME_MODE_MODIFY - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY): boolean; -/** - * Apply all challenges that modify what level a pokemon can access a move. - * @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS - * @param pokemon {@link Pokemon} What pokemon would learn the move. - * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. - * @param move {@link MoveId} The move in question. - * @param level {@link NumberHolder} The level threshold for access. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.MOVE_ACCESS, - pokemon: Pokemon, - moveSource: MoveSourceType, - move: MoveId, - level: NumberHolder, -): boolean; -/** - * Apply all challenges that modify what weight a pokemon gives to move generation - * @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT - * @param pokemon {@link Pokemon} What pokemon would learn the move. - * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. - * @param move {@link MoveId} The move in question. - * @param weight {@link NumberHolder} The weight of the move. - * @returns True if any challenge was successfully applied. - */ -export function applyChallenges( - challengeType: ChallengeType.MOVE_WEIGHT, - pokemon: Pokemon, - moveSource: MoveSourceType, - move: MoveId, - weight: NumberHolder, -): boolean; +export class LimitedSupportChallenge extends Challenge { + constructor() { + super(Challenges.LIMITED_SUPPORT, 3); + } -export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean; - -export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean { - let ret = false; - globalScene.gameMode.challenges.forEach(c => { - if (c.value !== 0) { - switch (challengeType) { - case ChallengeType.STARTER_CHOICE: - ret ||= c.applyStarterChoice(args[0], args[1], args[2]); - break; - case ChallengeType.STARTER_POINTS: - ret ||= c.applyStarterPoints(args[0]); - break; - case ChallengeType.STARTER_COST: - ret ||= c.applyStarterCost(args[0], args[1]); - break; - case ChallengeType.STARTER_MODIFY: - ret ||= c.applyStarterModify(args[0]); - break; - case ChallengeType.POKEMON_IN_BATTLE: - ret ||= c.applyPokemonInBattle(args[0], args[1]); - break; - case ChallengeType.FIXED_BATTLES: - ret ||= c.applyFixedBattle(args[0], args[1]); - break; - case ChallengeType.TYPE_EFFECTIVENESS: - ret ||= c.applyTypeEffectiveness(args[0]); - break; - case ChallengeType.AI_LEVEL: - ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]); - break; - case ChallengeType.AI_MOVE_SLOTS: - ret ||= c.applyMoveSlot(args[0], args[1]); - break; - case ChallengeType.PASSIVE_ACCESS: - ret ||= c.applyPassiveAccess(args[0], args[1]); - break; - case ChallengeType.GAME_MODE_MODIFY: - ret ||= c.applyGameModeModify(); - break; - case ChallengeType.MOVE_ACCESS: - ret ||= c.applyMoveAccessLevel(args[0], args[1], args[2], args[3]); - break; - case ChallengeType.MOVE_WEIGHT: - ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]); - break; - case ChallengeType.FLIP_STAT: - ret ||= c.applyFlipStat(args[0], args[1]); - break; - } + override applyPartyHeal(status: BooleanHolder): boolean { + if (status.value) { + status.value = this.value === 2; + return true; } - }); - return ret; + return false; + } + + override applyShop(status: BooleanHolder): boolean { + if (status.value) { + status.value = this.value === 1; + return true; + } + return false; + } + + static override loadChallenge(source: LimitedSupportChallenge | any): LimitedSupportChallenge { + const newChallenge = new LimitedSupportChallenge(); + newChallenge.value = source.value; + newChallenge.severity = source.severity; + return newChallenge; + } +} + +/** + * Implements a Limited Catch challenge + */ +export class LimitedCatchChallenge extends Challenge { + constructor() { + super(Challenges.LIMITED_CATCH, 1); + } + + override applyPokemonAddToParty(pokemon: EnemyPokemon, status: BooleanHolder): boolean { + if (status.value) { + status.value = pokemon.metWave % 10 === 1; + return true; + } + return false; + } + + static override loadChallenge(source: LimitedCatchChallenge | any): LimitedCatchChallenge { + const newChallenge = new LimitedCatchChallenge(); + newChallenge.value = source.value; + newChallenge.severity = source.severity; + return newChallenge; + } +} + +/** + * Implements a Permanent Faint challenge + */ +export class HardcoreChallenge extends Challenge { + constructor() { + super(Challenges.HARDCORE, 1); + } + + override applyPokemonFusion(pokemon: PlayerPokemon, status: BooleanHolder): boolean { + if (!status.value) { + status.value = pokemon.isFainted(); + return true; + } + return false; + } + + override applyShopItem(shopItem: ModifierTypeOption | null, status: BooleanHolder): boolean { + status.value = shopItem?.type.group !== "revive"; + return true; + } + + override applyWaveReward(reward: ModifierTypeOption | null, status: BooleanHolder): boolean { + return this.applyShopItem(reward, status); + } + + override applyPokemonMove(moveId: MoveId, status: BooleanHolder) { + if (status.value) { + status.value = moveId !== MoveId.REVIVAL_BLESSING; + return true; + } + return false; + } + + override applyPreventRevive(status: BooleanHolder): boolean { + if (!status.value) { + status.value = true; + return true; + } + return false; + } + + static override loadChallenge(source: HardcoreChallenge | any): HardcoreChallenge { + const newChallenge = new HardcoreChallenge(); + newChallenge.value = source.value; + newChallenge.severity = source.severity; + return newChallenge; + } } /** @@ -1089,6 +1063,12 @@ export function copyChallenge(source: Challenge | any): Challenge { return InverseBattleChallenge.loadChallenge(source); case Challenges.FLIP_STAT: return FlipStatChallenge.loadChallenge(source); + case Challenges.LIMITED_CATCH: + return LimitedCatchChallenge.loadChallenge(source); + case Challenges.LIMITED_SUPPORT: + return LimitedSupportChallenge.loadChallenge(source); + case Challenges.HARDCORE: + return HardcoreChallenge.loadChallenge(source); } throw new Error("Unknown challenge copied"); } @@ -1097,87 +1077,13 @@ export const allChallenges: Challenge[] = []; export function initChallenges() { allChallenges.push( + new FreshStartChallenge(), + new HardcoreChallenge(), + new LimitedCatchChallenge(), + new LimitedSupportChallenge(), new SingleGenerationChallenge(), new SingleTypeChallenge(), - new FreshStartChallenge(), new InverseBattleChallenge(), new FlipStatChallenge(), ); } - -/** - * Apply all challenges to the given starter (and form) to check its validity. - * Differs from {@linkcode checkSpeciesValidForChallenge} which only checks form changes. - * @param species - The {@linkcode PokemonSpecies} to check the validity of. - * @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index. - * @param soft - If `true`, allow it if it could become valid through evolution or form change. - * @returns `true` if the species is considered valid. - */ -export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { - if (!soft) { - const isValidForChallenge = new BooleanHolder(true); - applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); - return isValidForChallenge.value; - } - // We check the validity of every evolution and form change, and require that at least one is valid - const speciesToCheck = [species.speciesId]; - while (speciesToCheck.length) { - const checking = speciesToCheck.pop(); - // Linter complains if we don't handle this - if (!checking) { - return false; - } - const checkingSpecies = getPokemonSpecies(checking); - if (checkSpeciesValidForChallenge(checkingSpecies, props, true)) { - return true; - } - if (checking && pokemonEvolutions.hasOwnProperty(checking)) { - pokemonEvolutions[checking].forEach(e => { - // Form check to deal with cases such as Basculin -> Basculegion - // TODO: does this miss anything if checking forms of a stage 2 Pokémon? - if (!e?.preFormKey || e.preFormKey === species.forms[props.formIndex].formKey) { - speciesToCheck.push(e.speciesId); - } - }); - } - } - return false; -} - -/** - * Apply all challenges to the given species (and form) to check its validity. - * Differs from {@linkcode checkStarterValidForChallenge} which also checks evolutions. - * @param species - The {@linkcode PokemonSpecies} to check the validity of. - * @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index. - * @param soft - If `true`, allow it if it could become valid through a form change. - * @returns `true` if the species is considered valid. - */ -function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { - const isValidForChallenge = new BooleanHolder(true); - applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); - if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) { - return isValidForChallenge.value; - } - // If the form in props is valid, return true before checking other form changes - if (soft && isValidForChallenge.value) { - return true; - } - - const result = pokemonFormChanges[species.speciesId].some(f1 => { - // Exclude form changes that require the mon to be on the field to begin with - if (!("item" in f1.trigger)) { - return false; - } - - return species.forms.some((f2, formIndex) => { - if (f1.formKey === f2.formKey) { - const formProps = { ...props, formIndex }; - const isFormValidForChallenge = new BooleanHolder(true); - applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps); - return isFormValidForChallenge.value; - } - return false; - }); - }); - return result; -} diff --git a/src/data/moves/invalid-moves.ts b/src/data/moves/invalid-moves.ts index 6d97d8faf1e..e55eedc29aa 100644 --- a/src/data/moves/invalid-moves.ts +++ b/src/data/moves/invalid-moves.ts @@ -256,7 +256,7 @@ export const noAbilityTypeOverrideMoves: ReadonlySet = new Set([ MoveId.HIDDEN_POWER, ]); -/** Set of all moves that cannot be copied by {@linkcode Moves.SKETCH}. */ +/** Set of all moves that cannot be copied by {@linkcode MoveId.SKETCH}. */ export const invalidSketchMoves: ReadonlySet = new Set([ MoveId.NONE, MoveId.CHATTER, @@ -270,7 +270,7 @@ export const invalidSketchMoves: ReadonlySet = new Set([ MoveId.BREAKNECK_BLITZ__SPECIAL, ]); -/** Set of all moves that cannot be locked into by {@linkcode Moves.ENCORE}. */ +/** Set of all moves that cannot be locked into by {@linkcode MoveId.ENCORE}. */ export const invalidEncoreMoves: ReadonlySet = new Set([ MoveId.MIMIC, MoveId.MIRROR_MOVE, diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index bde5f2977d8..067bd05c2ae 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -22,7 +22,6 @@ import { TypeBoostTag, } from "#data/battler-tags"; import { getBerryEffectFunc } from "#data/berry"; -import { applyChallenges } from "#data/challenge"; import { allAbilities, allMoves } from "#data/data-lists"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-change-triggers"; import { DelayedAttackTag } from "#data/positional-tags/positional-tag"; @@ -93,6 +92,7 @@ import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randS import { getEnumValues } from "#utils/enums"; import { toTitleCase } from "#utils/strings"; import i18next from "i18next"; +import { applyChallenges } from "#utils/challenge-utils"; /** * A function used to conditionally determine execution of a given {@linkcode MoveAttr}. @@ -124,7 +124,7 @@ export abstract class Move implements Localizable { /** * Check if the move is of the given subclass without requiring `instanceof`. * - * ⚠️ Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. For those, + * ! Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. For those, * use {@linkcode isChargingMove} instead. * * @param moveKind - The string name of the move to check against @@ -2587,7 +2587,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } /** - * Applies the effect of {@linkcode Moves.PSYCHO_SHIFT} to its target. + * Applies the effect of {@linkcode MoveId.PSYCHO_SHIFT} to its target. * Psycho Shift takes the user's status effect and passes it onto the target. * The user is then healed after the move has been successfully executed. * @param user - The {@linkcode Pokemon} using the move @@ -2927,7 +2927,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr { /** * Attribute to add the {@linkcode BattlerTagType.BYPASS_SLEEP | BYPASS_SLEEP Battler Tag} for 1 turn to the user before move use. - * Used by {@linkcode Moves.SNORE} and {@linkcode Moves.SLEEP_TALK}. + * Used by {@linkcode MoveId.SNORE} and {@linkcode MoveId.SLEEP_TALK}. */ // TODO: Should this use a battler tag? // TODO: Give this `userSleptOrComatoseCondition` by default @@ -5912,20 +5912,21 @@ export class ProtectAttr extends AddBattlerTagAttr { getCondition(): MoveConditionFunc { return ((user, target, move): boolean => { let timesUsed = 0; - const moveHistory = user.getLastXMoves(); - let turnMove: TurnMove | undefined; - while (moveHistory.length) { - turnMove = moveHistory.shift(); - if (!allMoves[turnMove?.move ?? MoveId.NONE].hasAttr("ProtectAttr") || turnMove?.result !== MoveResult.SUCCESS) { + for (const turnMove of user.getLastXMoves(-1).slice()) { + if ( + // Quick & Wide guard increment the Protect counter without using it for fail chance + !(allMoves[turnMove.move].hasAttr("ProtectAttr") || + [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || + turnMove.result !== MoveResult.SUCCESS + ) { break; } - timesUsed++; + + timesUsed++ } - if (timesUsed) { - return !user.randBattleSeedInt(Math.pow(3, timesUsed)); - } - return true; + + return timesUsed === 0 || user.randBattleSeedInt(Math.pow(3, timesUsed)) === 0; }); } } @@ -6905,13 +6906,19 @@ export class RandomMoveAttr extends CallMoveAttr { * @param move Move being used * @param args Unused */ - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { + // TODO: Move this into the constructor to avoid constructing this every call const moveIds = getEnumValues(MoveId).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : MoveId.NONE); let moveId: MoveId = MoveId.NONE; + const moveStatus = new BooleanHolder(true); do { moveId = this.getMoveOverride() ?? moveIds[user.randBattleSeedInt(moveIds.length)]; + moveStatus.value = moveId !== MoveId.NONE; + if (user.isPlayer()) { + applyChallenges(ChallengeType.POKEMON_MOVE, moveId, moveStatus); + } } - while (moveId === MoveId.NONE); + while (!moveStatus.value); return super.apply(user, target, allMoves[moveId], args); } } @@ -7139,7 +7146,7 @@ export class CopyMoveAttr extends CallMoveAttr { /** * Attribute used for moves that cause the target to repeat their last used move. * - * Used by {@linkcode Moves.INSTRUCT | Instruct}. + * Used by {@linkcode MoveId.INSTRUCT | Instruct}. * @see [Instruct on Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)) */ export class RepeatMoveAttr extends MoveEffectAttr { @@ -7402,7 +7409,7 @@ const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { /** * Attribute to temporarily copy the last move in the target's moveset. - * Used by {@linkcode Moves.MIMIC}. + * Used by {@linkcode MoveId.MIMIC}. */ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -7966,7 +7973,7 @@ export class VariableTargetAttr extends MoveAttr { /** * Attribute to cause the target to move immediately after the user. * - * Used by {@linkcode Moves.AFTER_YOU}. + * Used by {@linkcode MoveId.AFTER_YOU}. */ export class AfterYouAttr extends MoveEffectAttr { /** @@ -10887,7 +10894,7 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted ? 1 : 2) .bitingMove(), new StatusMove(MoveId.COURT_CHANGE, PokemonType.NORMAL, 100, 10, -1, 0, 8) - .attr(SwapArenaTagsAttr, [ ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES ]), + .attr(SwapArenaTagsAttr, [ ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES, ArenaTagType.SAFEGUARD, ArenaTagType.FIRE_GRASS_PLEDGE, ArenaTagType.WATER_FIRE_PLEDGE, ArenaTagType.GRASS_WATER_PLEDGE ]), /* Unused */ new AttackMove(MoveId.MAX_FLARE, PokemonType.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) .target(MoveTarget.NEAR_ENEMY) diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts index d3f68fe9db4..3c96cbea598 100644 --- a/src/data/moves/pokemon-move.ts +++ b/src/data/moves/pokemon-move.ts @@ -1,8 +1,10 @@ import { allMoves } from "#data/data-lists"; -import type { MoveId } from "#enums/move-id"; +import { ChallengeType } from "#enums/challenge-type"; +import { MoveId } from "#enums/move-id"; import type { Pokemon } from "#field/pokemon"; import type { Move } from "#moves/move"; -import { toDmgValue } from "#utils/common"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder, toDmgValue } from "#utils/common"; /** * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. @@ -45,16 +47,18 @@ export class PokemonMove { * @returns Whether this {@linkcode PokemonMove} can be selected by this Pokemon. */ isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean { + const move = this.getMove(); // TODO: Add Sky Drop's 1 turn stall - if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) { - return false; + const usability = new BooleanHolder( + !move.name.endsWith(" (N)") && + (ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) && + // TODO: Review if the `MoveId.NONE` check is even necessary anymore + !(this.moveId !== MoveId.NONE && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)), + ); + if (pokemon.isPlayer()) { + applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability); } - - if (this.getMove().name.endsWith(" (N)")) { - return false; - } - - return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1; + return usability.value; } getMove(): Move { diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index 5b2805f9310..ac3d4def654 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -63,12 +63,12 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. break; case 3: trainerType = TrainerType.MIRA; - spriteKeys = getSpriteKeysFromSpecies(SpeciesId.ALAKAZAM, false, 1); + spriteKeys = getSpriteKeysFromSpecies(SpeciesId.ALAKAZAM); trainerNameKey = "mira"; break; case 4: trainerType = TrainerType.RILEY; - spriteKeys = getSpriteKeysFromSpecies(SpeciesId.LUCARIO, false, 1); + spriteKeys = getSpriteKeysFromSpecies(SpeciesId.LUCARIO); trainerNameKey = "riley"; break; default: @@ -164,8 +164,8 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); setEncounterRewards( { - guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], - guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], + guaranteedModifierTypeFuncs: [modifierTypes.RELIC_GOLD], + guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE], fillRemaining: true, }, [eggOptions], diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index d60ebe690ac..c5553e9bb95 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -46,7 +46,7 @@ import { } from "#mystery-encounters/mystery-encounter-requirements"; import { getRandomPartyMemberFunc, trainerConfigs } from "#trainers/trainer-config"; import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { MoveInfoOverlay } from "#ui/move-info-overlay"; import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#utils/common"; import i18next from "i18next"; @@ -715,15 +715,13 @@ function doBugTypeMoveTutor(): Promise { const moveOptions = globalScene.currentBattle.mysteryEncounter!.misc.moveTutorOptions; await showEncounterDialogue(`${namespace}:battle_won`, `${namespace}:speaker`); - const overlayScale = 1; const moveInfoOverlay = new MoveInfoOverlay({ delayVisibility: false, - scale: overlayScale, onSide: true, right: true, x: 1, - y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1, - width: globalScene.game.canvas.width / 6 - 2, + y: -MoveInfoOverlay.getHeight(true) - 1, + width: globalScene.scaledCanvas.width - 2, }); globalScene.ui.add(moveInfoOverlay); diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 1c85cb7595c..092cc4931af 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -45,7 +45,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { trainerConfigs } from "#trainers/trainer-config"; import { TrainerPartyCompoundTemplate, TrainerPartyTemplate } from "#trainers/trainer-party-template"; -import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler"; import { randSeedInt, randSeedShuffle } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 94006a43837..8dae0eaee3a 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -37,7 +37,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou import { MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { DANCING_MOVES } from "#mystery-encounters/requirement-groups"; import { PokemonData } from "#system/pokemon-data"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 29517ac2531..d90e207cc9a 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -23,12 +23,8 @@ const namespace = "mysteryEncounters/darkDeal"; /** Exclude Ultra Beasts (inludes Cosmog/Solgaleo/Lunala/Necrozma), Paradox (includes Miraidon/Koraidon), Eternatus, and Mythicals */ const excludedBosses = [ - SpeciesId.NECROZMA, - SpeciesId.COSMOG, - SpeciesId.COSMOEM, - SpeciesId.SOLGALEO, - SpeciesId.LUNALA, SpeciesId.ETERNATUS, + /** UBs */ SpeciesId.NIHILEGO, SpeciesId.BUZZWOLE, SpeciesId.PHEROMOSA, @@ -40,6 +36,12 @@ const excludedBosses = [ SpeciesId.NAGANADEL, SpeciesId.STAKATAKA, SpeciesId.BLACEPHALON, + SpeciesId.COSMOG, + SpeciesId.COSMOEM, + SpeciesId.SOLGALEO, + SpeciesId.LUNALA, + SpeciesId.NECROZMA, + /** Paradox */ SpeciesId.GREAT_TUSK, SpeciesId.SCREAM_TAIL, SpeciesId.BRUTE_BONNET, @@ -47,10 +49,10 @@ const excludedBosses = [ SpeciesId.SLITHER_WING, SpeciesId.SANDY_SHOCKS, SpeciesId.ROARING_MOON, - SpeciesId.KORAIDON, SpeciesId.WALKING_WAKE, SpeciesId.GOUGING_FIRE, SpeciesId.RAGING_BOLT, + SpeciesId.KORAIDON, SpeciesId.IRON_TREADS, SpeciesId.IRON_BUNDLE, SpeciesId.IRON_HANDS, @@ -58,22 +60,23 @@ const excludedBosses = [ SpeciesId.IRON_MOTH, SpeciesId.IRON_THORNS, SpeciesId.IRON_VALIANT, - SpeciesId.MIRAIDON, SpeciesId.IRON_LEAVES, SpeciesId.IRON_BOULDER, SpeciesId.IRON_CROWN, + SpeciesId.MIRAIDON, + /** Mythical */ SpeciesId.MEW, SpeciesId.CELEBI, - SpeciesId.DEOXYS, SpeciesId.JIRACHI, - SpeciesId.DARKRAI, + SpeciesId.DEOXYS, SpeciesId.PHIONE, SpeciesId.MANAPHY, - SpeciesId.ARCEUS, + SpeciesId.DARKRAI, SpeciesId.SHAYMIN, + SpeciesId.ARCEUS, SpeciesId.VICTINI, - SpeciesId.MELOETTA, SpeciesId.KELDEO, + SpeciesId.MELOETTA, SpeciesId.GENESECT, SpeciesId.DIANCIE, SpeciesId.HOOPA, @@ -81,9 +84,9 @@ const excludedBosses = [ SpeciesId.MAGEARNA, SpeciesId.MARSHADOW, SpeciesId.ZERAORA, - SpeciesId.ZARUDE, SpeciesId.MELTAN, SpeciesId.MELMETAL, + SpeciesId.ZARUDE, SpeciesId.PECHARUNT, ]; diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 66f50f134dd..85102a01ce1 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -33,7 +33,7 @@ import { MoneyRequirement, } from "#mystery-encounters/mystery-encounter-requirements"; import i18next from "#plugins/i18n"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { randSeedItem } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 9c655e70b8c..0413c3d0e1d 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -18,7 +18,7 @@ import { import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import i18next from "i18next"; /** i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 347092fe0b4..ed49fccf190 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -42,7 +42,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou import { PartySizeRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { PokemonData } from "#system/pokemon-data"; import { MusicPreference } from "#system/settings"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; @@ -564,14 +564,14 @@ function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?: function showTradeBackground() { return new Promise(resolve => { - const tradeContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + const tradeContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); tradeContainer.setName("Trade Background"); const flyByStaticBg = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0, ); flyByStaticBg.setName("Black Background"); diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 235bd322ef8..7c528e2305b 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -52,37 +52,6 @@ class BreederSpeciesEvolution { } const POOL_1_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [ - [SpeciesId.MUNCHLAX, new BreederSpeciesEvolution(SpeciesId.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)], - [ - SpeciesId.HAPPINY, - new BreederSpeciesEvolution(SpeciesId.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), - new BreederSpeciesEvolution(SpeciesId.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE), - ], - [ - SpeciesId.MAGBY, - new BreederSpeciesEvolution(SpeciesId.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), - new BreederSpeciesEvolution(SpeciesId.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE), - ], - [ - SpeciesId.ELEKID, - new BreederSpeciesEvolution(SpeciesId.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), - new BreederSpeciesEvolution(SpeciesId.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE), - ], - [SpeciesId.RIOLU, new BreederSpeciesEvolution(SpeciesId.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)], - [ - SpeciesId.BUDEW, - new BreederSpeciesEvolution(SpeciesId.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), - new BreederSpeciesEvolution(SpeciesId.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE), - ], - [SpeciesId.TOXEL, new BreederSpeciesEvolution(SpeciesId.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)], - [ - SpeciesId.MIME_JR, - new BreederSpeciesEvolution(SpeciesId.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), - new BreederSpeciesEvolution(SpeciesId.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE), - ], -]; - -const POOL_2_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [ [ SpeciesId.PICHU, new BreederSpeciesEvolution(SpeciesId.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), @@ -93,24 +62,63 @@ const POOL_2_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [ new BreederSpeciesEvolution(SpeciesId.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(SpeciesId.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE), ], - [SpeciesId.SMOOCHUM, new BreederSpeciesEvolution(SpeciesId.JYNX, SECOND_STAGE_EVOLUTION_WAVE)], - [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)], - [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)], - [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)], [ SpeciesId.IGGLYBUFF, new BreederSpeciesEvolution(SpeciesId.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(SpeciesId.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE), ], + [ + SpeciesId.TOGEPI, + new BreederSpeciesEvolution(SpeciesId.TOGETIC, FIRST_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.TOGEKISS, FINAL_STAGE_EVOLUTION_WAVE), + ], + [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.TYROGUE, new BreederSpeciesEvolution(SpeciesId.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.SMOOCHUM, new BreederSpeciesEvolution(SpeciesId.JYNX, FIRST_STAGE_EVOLUTION_WAVE)], [ SpeciesId.AZURILL, new BreederSpeciesEvolution(SpeciesId.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(SpeciesId.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE), ], - [SpeciesId.WYNAUT, new BreederSpeciesEvolution(SpeciesId.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)], + [ + SpeciesId.BUDEW, + new BreederSpeciesEvolution(SpeciesId.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE), + ], [SpeciesId.CHINGLING, new BreederSpeciesEvolution(SpeciesId.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)], [SpeciesId.BONSLY, new BreederSpeciesEvolution(SpeciesId.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.MIME_JR, new BreederSpeciesEvolution(SpeciesId.MR_MIME, SECOND_STAGE_EVOLUTION_WAVE)], + [ + SpeciesId.MIME_JR, + new BreederSpeciesEvolution(SpeciesId.GALAR_MR_MIME, SECOND_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE), + ], + [ + SpeciesId.HAPPINY, + new BreederSpeciesEvolution(SpeciesId.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE), + ], [SpeciesId.MANTYKE, new BreederSpeciesEvolution(SpeciesId.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.TOXEL, new BreederSpeciesEvolution(SpeciesId.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)], +]; + +const POOL_2_POKEMON: (SpeciesId | BreederSpeciesEvolution)[][] = [ + [SpeciesId.DITTO], + [ + SpeciesId.ELEKID, + new BreederSpeciesEvolution(SpeciesId.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE), + ], + [ + SpeciesId.MAGBY, + new BreederSpeciesEvolution(SpeciesId.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), + new BreederSpeciesEvolution(SpeciesId.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE), + ], + [SpeciesId.WYNAUT, new BreederSpeciesEvolution(SpeciesId.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.MUNCHLAX, new BreederSpeciesEvolution(SpeciesId.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.RIOLU, new BreederSpeciesEvolution(SpeciesId.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)], + [SpeciesId.AUDINO], ]; /** @@ -502,7 +510,7 @@ function getPartyConfig(): EnemyPartyConfig { shiny: true, variant: 1, nature: Nature.MODEST, - moveSet: [MoveId.MOONBLAST, MoveId.MYSTICAL_FIRE, MoveId.ICE_BEAM, MoveId.THUNDERBOLT], + moveSet: [MoveId.DAZZLING_GLEAM, MoveId.MYSTICAL_FIRE, MoveId.ICE_BEAM, MoveId.THUNDERBOLT], // Make this one have an item gimmick when we have more items/finish implementations ivs: [31, 31, 31, 31, 31, 31], }, { @@ -515,7 +523,7 @@ function getPartyConfig(): EnemyPartyConfig { shiny: true, variant: 2, nature: Nature.BOLD, - moveSet: [MoveId.TRI_ATTACK, MoveId.STORED_POWER, MoveId.TAKE_HEART, MoveId.MOONLIGHT], + moveSet: [MoveId.TRI_ATTACK, MoveId.STORED_POWER, MoveId.CALM_MIND, MoveId.MOONLIGHT], ivs: [31, 31, 31, 31, 31, 31], }, ); diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 393f8a24e51..e56c42a3ee5 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -27,7 +27,7 @@ import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import { PokemonData } from "#system/pokemon-data"; import type { HeldModifierConfig } from "#types/held-modifier-config"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { isNullOrUndefined, randSeedShuffle } from "#utils/common"; import { getEnumValues } from "#utils/enums"; import i18next from "i18next"; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index 452a9a8bb4b..74a36a280d3 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -13,8 +13,9 @@ import { HitHealModifier, PokemonHeldItemModifier, TurnHealModifier } from "#mod import type { PokemonHeldItemModifierType } from "#modifiers/modifier-type"; import { PokemonMove } from "#moves/pokemon-move"; import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; -import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils"; import { + type EnemyPartyConfig, + type EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, @@ -23,8 +24,7 @@ import { transitionMysteryEncounterIntroVisuals, } from "#mystery-encounters/encounter-phase-utils"; import { applyModifierTypeToPlayerPokemon } from "#mystery-encounters/encounter-pokemon-utils"; -import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; -import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; +import { type MysteryEncounter, MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option"; import i18next from "#plugins/i18n"; import { randSeedInt } from "#utils/common"; @@ -200,7 +200,8 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde const encounter = globalScene.currentBattle.mysteryEncounter!; setEncounterRewards({ - guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], + guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], + guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true, }); encounter.startOfBattleEffects.push( diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 1164d2ca7ca..57b066e2ba2 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -47,6 +47,7 @@ const namespace = "mysteryEncounters/weirdDream"; /** Exclude Ultra Beasts, Paradox, Eternatus, and all legendary/mythical/trio pokemon that are below 570 BST */ const EXCLUDED_TRANSFORMATION_SPECIES = [ + SpeciesId.ARCEUS, SpeciesId.ETERNATUS, /** UBs */ SpeciesId.NIHILEGO, @@ -82,20 +83,19 @@ const EXCLUDED_TRANSFORMATION_SPECIES = [ SpeciesId.IRON_BOULDER, SpeciesId.IRON_CROWN, /** These are banned so they don't appear in the < 570 BST pool */ + SpeciesId.PHIONE, + SpeciesId.TYPE_NULL, SpeciesId.COSMOG, + SpeciesId.COSMOEM, SpeciesId.MELTAN, SpeciesId.KUBFU, - SpeciesId.COSMOEM, - SpeciesId.POIPOLE, - SpeciesId.TERAPAGOS, - SpeciesId.TYPE_NULL, - SpeciesId.CALYREX, - SpeciesId.NAGANADEL, SpeciesId.URSHIFU, + SpeciesId.CALYREX, SpeciesId.OGERPON, SpeciesId.OKIDOGI, SpeciesId.MUNKIDORI, SpeciesId.FEZANDIPITI, + SpeciesId.TERAPAGOS, ]; const SUPER_LEGENDARY_BST_THRESHOLD = 600; @@ -226,6 +226,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit modifierTypes.MINT, modifierTypes.MINT, modifierTypes.MINT, + modifierTypes.MINT, ], fillRemaining: false, }); @@ -648,15 +649,15 @@ function getTransformedSpecies( } function doShowDreamBackground() { - const transformationContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + const transformationContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); transformationContainer.name = "Dream Background"; // In case it takes a bit for video to load const transformationStaticBg = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0, ); transformationStaticBg.setName("Black Background"); diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 504310eeabd..6ab2f8dae00 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -156,7 +156,7 @@ export class MysteryEncounterOption implements IMysteryEncounterOption { return true; } console.log( - "Mystery Encounter Edge Case: Requirement not met due to primay pokemon overlapping with support pokemon. There's no valid primary pokemon left.", + "Mystery Encounter Edge Case: Requirement not met due to primary pokemon overlapping with support pokemon. There's no valid primary pokemon left.", ); return false; } diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 47dfe58cace..580fdc2ca38 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -576,7 +576,7 @@ export class MysteryEncounterBuilder implements Partial { */ /** - * @statif Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType + * @static Defines the type of encounter which is used as an identifier, should be tied to a unique MysteryEncounterType * NOTE: if new functions are added to {@linkcode MysteryEncounter} class * @param encounterType * @returns this @@ -605,7 +605,7 @@ export class MysteryEncounterBuilder implements Partial { } /** - * Defines an option + phasefor the encounter. + * Defines an option + phase for the encounter. * Use for easy/streamlined options. * There should be at least 2 options defined and no more than 4. * If complex use {@linkcode MysteryEncounterBuilder.withOption} @@ -627,7 +627,7 @@ export class MysteryEncounterBuilder implements Partial { } /** - * Defines an option + phasefor the encounter. + * Defines an option + phase for the encounter. * Use for easy/streamlined options. * There should be at least 2 options defined and no more than 4. * If complex use {@linkcode MysteryEncounterBuilder.withOption} diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index 1ae0659b29e..54179ee2604 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -6,7 +6,7 @@ import { isNullOrUndefined } from "#utils/common"; import i18next from "i18next"; /** - * Will inject all relevant dialogue tokens that exist in the {@linkcode BattlegScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. + * Will inject all relevant dialogue tokens that exist in the {@linkcode globalScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. * Also adds BBCodeText fragments for colored text, if applicable * @param keyOrString * @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 6b085978b27..b599f923534 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -46,7 +46,7 @@ import type { PokemonData } from "#system/pokemon-data"; import type { TrainerConfig } from "#trainers/trainer-config"; import { trainerConfigs } from "#trainers/trainer-config"; import type { HeldModifierConfig } from "#types/held-modifier-config"; -import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler"; import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common"; diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 19f06707257..7617fb5a89e 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -13,6 +13,7 @@ import { CustomPokemonData } from "#data/pokemon-data"; import type { PokemonSpecies } from "#data/pokemon-species"; import { getStatusEffectCatchRateMultiplier } from "#data/status-effect"; import type { AbilityId } from "#enums/ability-id"; +import { ChallengeType } from "#enums/challenge-type"; import { PlayerGender } from "#enums/player-gender"; import type { PokeballType } from "#enums/pokeball"; import type { PokemonType } from "#enums/pokemon-type"; @@ -33,7 +34,8 @@ import { achvs } from "#system/achv"; import type { PartyOption } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler"; import { SummaryUiMode } from "#ui/summary-ui-handler"; -import { isNullOrUndefined, randSeedInt } from "#utils/common"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder, isNullOrUndefined, randSeedInt } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; @@ -706,6 +708,13 @@ export async function catchPokemon( }); }; Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => { + const addStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); + if (!addStatus.value) { + removePokemon(); + end(); + return; + } if (globalScene.getPlayerParty().length === 6) { const promptRelease = () => { globalScene.ui.showText( diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts index 0eabb0d354a..0bd6af0bb04 100644 --- a/src/data/pokemon/pokemon-data.ts +++ b/src/data/pokemon/pokemon-data.ts @@ -162,6 +162,7 @@ export class PokemonSummonData { if (key === "speciesForm" || key === "fusionSpeciesForm") { this[key] = deserializePokemonSpeciesForm(value); + continue; } if (key === "illusion" && typeof value === "object") { @@ -182,6 +183,7 @@ export class PokemonSummonData { } } this[key] = illusionData as IllusionData; + continue; } if (key === "moveset") { @@ -251,7 +253,6 @@ export class PokemonTempSummonData { * Only currently used for positioning the battle cursor. */ turnCount = 1; - /** * The number of turns this pokemon has spent in the active position since the start of the wave * without switching out. diff --git a/src/enums/challenge-type.ts b/src/enums/challenge-type.ts index d9b1fce3e6e..053bcf92011 100644 --- a/src/enums/challenge-type.ts +++ b/src/enums/challenge-type.ts @@ -65,5 +65,45 @@ export enum ChallengeType { /** * Modifies what the pokemon stats for Flip Stat Mode. */ - FLIP_STAT + FLIP_STAT, + /** + * Challenges which conditionally enable or disable automatic party healing during biome transitions + * @see {@linkcode Challenge.applyPartyHealAvailability} + */ + PARTY_HEAL, + /** + * Challenges which conditionally enable or disable the shop + * @see {@linkcode Challenge.applyShopAvailability} + */ + SHOP, + /** + * Challenges which validate whether a pokemon can be added to the player's party or not + * @see {@linkcode Challenge.applyCatchAvailability} + */ + POKEMON_ADD_TO_PARTY, + /** + * Challenges which validate whether a pokemon is allowed to fuse or not + * @see {@linkcode Challenge.applyFusionAvailability} + */ + POKEMON_FUSION, + /** + * Challenges which validate whether particular moves can or cannot be used + * @see {@linkcode Challenge.applyMoveAvailability} + */ + POKEMON_MOVE, + /** + * Challenges which validate whether particular items are or are not sold in the shop + * @see {@linkcode Challenge.applyShopItems} + */ + SHOP_ITEM, + /** + * Challenges which validate whether particular items will be given as a reward after a wave + * @see {@linkcode Challenge.applyWaveRewards} + */ + WAVE_REWARD, + /** + * Challenges which prevent recovery from fainting + * @see {@linkcode Challenge.applyPermanentFaint} + */ + PREVENT_REVIVE, } diff --git a/src/enums/challenges.ts b/src/enums/challenges.ts index 7b506a61a2f..8d4f4c7a22a 100644 --- a/src/enums/challenges.ts +++ b/src/enums/challenges.ts @@ -6,4 +6,7 @@ export enum Challenges { FRESH_START, INVERSE_BATTLE, FLIP_STAT, + LIMITED_CATCH, + LIMITED_SUPPORT, + HARDCORE, } diff --git a/src/enums/ui-mode.ts b/src/enums/ui-mode.ts index dcf6bd2a238..bc93e747be2 100644 --- a/src/enums/ui-mode.ts +++ b/src/enums/ui-mode.ts @@ -43,5 +43,6 @@ export enum UiMode { TEST_DIALOGUE, AUTO_COMPLETE, ADMIN, - MYSTERY_ENCOUNTER + MYSTERY_ENCOUNTER, + CHANGE_PASSWORD_FORM, } diff --git a/src/field/arena.ts b/src/field/arena.ts index 484450cc5df..ed2f1df9b12 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -145,7 +145,7 @@ export class Arena { ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE; console.log(BiomePoolTier[tier]); - while (!this.pokemonPool[tier].length) { + while (!this.pokemonPool[tier]?.length) { console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`); tier--; } diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 1bbacc19566..112b08631b0 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -30,7 +30,7 @@ export class DamageNumberHandler { const baseScale = target.getSpriteScale() / 6; const damageNumber = addTextObject( target.x, - -(globalScene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, + -globalScene.scaledCanvas.height + target.y - target.getSprite().height / 2, formatStat(amount, true), TextStyle.SUMMARY, ); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fe78994ce93..fe85e92772c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -39,7 +39,6 @@ import { TrappedTag, TypeImmuneTag, } from "#data/battler-tags"; -import { applyChallenges } from "#data/challenge"; import { allAbilities, allMoves } from "#data/data-lists"; import { getLevelTotalExp } from "#data/exp"; import { @@ -148,6 +147,7 @@ import { EnemyBattleInfo } from "#ui/enemy-battle-info"; import type { PartyOption } from "#ui/party-ui-handler"; import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import { PlayerBattleInfo } from "#ui/player-battle-info"; +import { applyChallenges } from "#utils/challenge-utils"; import { BooleanHolder, type Constructor, @@ -725,7 +725,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Load all assets needed for this Pokemon's use in battle - * @param ignoreOverride - Whether to ignore overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `true` + * @param ignoreOverride - Whether to ignore overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `true` * @param useIllusion - Whether to consider this pokemon's active illusion; default `false` * @returns A promise that resolves once all the corresponding assets have been loaded. */ @@ -1032,7 +1032,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Return this Pokemon's {@linkcode PokemonSpeciesForm | SpeciesForm}. - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * and overrides `useIllusion`. * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false`. * @returns This Pokemon's {@linkcode PokemonSpeciesForm}. @@ -1088,7 +1088,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Return the {@linkcode PokemonSpeciesForm | SpeciesForm} of this Pokemon's fusion counterpart. - * @param ignoreOverride - Whether to ignore species overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore species overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @param useIllusion - Whether to consider the species of this Pokemon's illusion; default `false` * @returns The {@linkcode PokemonSpeciesForm} of this Pokemon's fusion counterpart. */ @@ -1659,7 +1659,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Return this Pokemon's {@linkcode Gender}. - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` * @returns the {@linkcode Gender} of this {@linkcode Pokemon}. */ @@ -1675,7 +1675,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Return this Pokemon's fusion's {@linkcode Gender}. - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @param useIllusion - Whether to consider this pokemon's illusion if present; default `false` * @returns The {@linkcode Gender} of this {@linkcode Pokemon}'s fusion. */ @@ -1817,12 +1817,10 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { /** * Return all the {@linkcode PokemonMove}s that make up this Pokemon's moveset. * Takes into account player/enemy moveset overrides (which will also override PP count). - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @returns An array of {@linkcode PokemonMove}, as described above. */ getMoveset(ignoreOverride = false): PokemonMove[] { - const ret = !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; - // Overrides moveset based on arrays specified in overrides.ts let overrideArray: MoveId | Array = this.isPlayer() ? Overrides.MOVESET_OVERRIDE @@ -1838,7 +1836,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { }); } - return ret; + return !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; } /** @@ -1885,7 +1883,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * Evaluate and return this Pokemon's typing. * @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `false` * @param forDefend - Whether this Pokemon is currently receiving an attack; default `false` - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false` * @returns An array of {@linkcode PokemonType}s corresponding to this Pokemon's typing (real or percieved). */ @@ -2008,7 +2006,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * @param type - The {@linkcode PokemonType} to check * @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `true` * @param forDefend - Whether this Pokemon is currently receiving an attack; default `false` - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @returns Whether this Pokemon is of the specified type. */ public isOfType(type: PokemonType, includeTeraType = true, forDefend = false, ignoreOverride = false): boolean { @@ -2021,7 +2019,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * Should rarely be called directly in favor of {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr}, * both of which check both ability slots and account for suppression. * @see {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} are the intended ways to check abilities in most cases - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @returns The non-passive {@linkcode Ability} of this Pokemon. */ public getAbility(ignoreOverride = false): Ability { @@ -2203,7 +2201,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * Accounts for all the various effects which can disable or modify abilities. * @param ability - The {@linkcode Abilities | Ability} to check for * @param canApply - Whether to check if the ability is currently active; default `true` - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @returns Whether this {@linkcode Pokemon} has the given ability */ public hasAbility(ability: AbilityId, canApply = true, ignoreOverride = false): boolean { @@ -2218,7 +2216,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * Accounts for all the various effects which can disable or modify abilities. * @param attrType - The {@linkcode AbAttr | attribute} to check for * @param canApply - Whether to check if the ability is currently active; default `true` - * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false` + * @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false` * @returns Whether this Pokemon has an ability with the given {@linkcode AbAttr}. */ public hasAbilityWithAttr(attrType: AbAttrString, canApply = true, ignoreOverride = false): boolean { @@ -4047,7 +4045,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * @param damage integer * @param ignoreSegments boolean, not currently used * @param preventEndure used to update damage if endure or sturdy - * @param ignoreFaintPhas flag on whether to add FaintPhase if pokemon after applying damage faints + * @param ignoreFaintPhase flag on whether to add FaintPhase if pokemon after applying damage faints * @returns integer representing damage dealt */ damage(damage: number, _ignoreSegments = false, preventEndure = false, ignoreFaintPhase = false): number { @@ -4468,7 +4466,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * Return the most recently executed {@linkcode TurnMove} this {@linkcode Pokemon} has used that is: * - Not {@linkcode MoveId.NONE} * - Non-virtual ({@linkcode MoveUseMode | useMode} < {@linkcode MoveUseMode.INDIRECT}) - * @param ignoreStruggle - Whether to additionally ignore {@linkcode Moves.STRUGGLE}; default `false` + * @param ignoreStruggle - Whether to additionally ignore {@linkcode MoveId.STRUGGLE}; default `false` * @param ignoreFollowUp - Whether to ignore moves with a use type of {@linkcode MoveUseMode.FOLLOW_UP} * (e.g. ones called by Copycat/Mirror Move); default `true`. * @returns The last move this Pokemon has used satisfying the aforementioned conditions, @@ -4560,8 +4558,17 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { } const key = this.species.getCryKey(this.formIndex); - let rate = 0.85; - const cry = globalScene.playSound(key, { rate: rate }) as AnySound; + const crySoundConfig = { rate: 0.85, detune: 0 }; + if (this.isPlayer()) { + // If fainting is permanent, emphasize impact + const preventRevive = new BooleanHolder(false); + applyChallenges(ChallengeType.PREVENT_REVIVE, preventRevive); + if (preventRevive.value) { + crySoundConfig.detune = -100; + crySoundConfig.rate = 0.7; + } + } + const cry = globalScene.playSound(key, crySoundConfig) as AnySound; if (!cry || globalScene.fieldVolume === 0) { callback(); return; @@ -4580,7 +4587,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { delay: fixedInt(delay), repeat: -1, callback: () => { - frameThreshold = sprite.anims.msPerFrame / rate; + frameThreshold = sprite.anims.msPerFrame / crySoundConfig.rate; frameProgress += delay; while (frameProgress > frameThreshold) { if (sprite.anims.duration) { @@ -4590,8 +4597,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { frameProgress -= frameThreshold; } if (cry && !cry.pendingRemove) { - rate *= 0.99; - cry.setRate(rate); + cry.setRate(crySoundConfig.rate * 0.99); } else { faintCryTimer?.destroy(); faintCryTimer = null; @@ -5096,6 +5102,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { */ resetWaveData(): void { this.waveData = new PokemonWaveData(); + this.tempSummonData.waveTurnCount = 1; } resetTera(): void { @@ -5199,38 +5206,38 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { } } - updateFusionPalette(ignoreOveride?: boolean): void { - if (!this.getFusionSpeciesForm(ignoreOveride)) { + updateFusionPalette(ignoreOverride?: boolean): void { + if (!this.getFusionSpeciesForm(ignoreOverride)) { [this.getSprite(), this.getTintSprite()] .filter(s => !!s) .map(s => { - s.pipelineData[`spriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; - s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; + s.pipelineData[`spriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = []; + s.pipelineData[`fusionSpriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = []; }); return; } - const speciesForm = this.getSpeciesForm(ignoreOveride); - const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOveride); + const speciesForm = this.getSpeciesForm(ignoreOverride); + const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride); const spriteKey = speciesForm.getSpriteKey( - this.getGender(ignoreOveride) === Gender.FEMALE, + this.getGender(ignoreOverride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant, ); const backSpriteKey = speciesForm - .getSpriteKey(this.getGender(ignoreOveride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant) + .getSpriteKey(this.getGender(ignoreOverride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant) .replace("pkmn__", "pkmn__back__"); const fusionSpriteKey = fusionSpeciesForm.getSpriteKey( - this.getFusionGender(ignoreOveride) === Gender.FEMALE, + this.getFusionGender(ignoreOverride) === Gender.FEMALE, fusionSpeciesForm.formIndex, this.fusionShiny, this.fusionVariant, ); const fusionBackSpriteKey = fusionSpeciesForm .getSpriteKey( - this.getFusionGender(ignoreOveride) === Gender.FEMALE, + this.getFusionGender(ignoreOverride) === Gender.FEMALE, fusionSpeciesForm.formIndex, this.fusionShiny, this.fusionVariant, @@ -5515,8 +5522,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { [this.getSprite(), this.getTintSprite()] .filter(s => !!s) .map(s => { - s.pipelineData[`spriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = spriteColors; - s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = + s.pipelineData[`spriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = spriteColors; + s.pipelineData[`fusionSpriteColors${ignoreOverride && this.summonData.speciesForm ? "Base" : ""}`] = fusionSpriteColors; }); diff --git a/src/game-mode.ts b/src/game-mode.ts index c5ab120e218..82f7b4fa77f 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -2,8 +2,7 @@ import { FixedBattleConfig } from "#app/battle"; import { CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; -import type { Challenge } from "#data/challenge"; -import { allChallenges, applyChallenges, copyChallenge } from "#data/challenge"; +import { allChallenges, type Challenge, copyChallenge } from "#data/challenge"; import { getDailyStartingBiome } from "#data/daily-run"; import { allSpecies } from "#data/data-lists"; import type { PokemonSpecies } from "#data/pokemon-species"; @@ -14,7 +13,8 @@ import { GameModes } from "#enums/game-modes"; import { SpeciesId } from "#enums/species-id"; import type { Arena } from "#field/arena"; import { classicFixedBattles, type FixedBattleConfigs } from "#trainers/fixed-battle-configs"; -import { isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder, isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common"; import i18next from "i18next"; interface GameModeConfig { @@ -311,6 +311,16 @@ export class GameMode implements GameModeConfig { return this.battleConfig[waveIndex]; } + /** + * Check if the current game mode has the shop enabled or not + * @returns Whether the shop is available in the current mode + */ + public getShopStatus(): boolean { + const status = new BooleanHolder(!this.hasNoShop); + applyChallenges(ChallengeType.SHOP, status); + return status.value; + } + getClearScoreBonus(): number { switch (this.modeId) { case GameModes.CLASSIC: diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index b359ec756e6..46ed7e1e4b5 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -14,6 +14,7 @@ import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-fo import { getStatusEffectDescriptor } from "#data/status-effect"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; +import { ChallengeType } from "#enums/challenge-type"; import { FormChangeItem } from "#enums/form-change-item"; import { ModifierPoolType } from "#enums/modifier-pool-type"; import { ModifierTier } from "#enums/modifier-tier"; @@ -104,7 +105,7 @@ import { TempExtraModifierModifier, TempStatStageBoosterModifier, TerastallizeAccessModifier, - TerrastalizeModifier, + TerastallizeModifier, TmModifier, TurnHealModifier, TurnHeldItemTransferModifier, @@ -116,7 +117,16 @@ import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#types/mo import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler"; import { PartyUiHandler } from "#ui/party-ui-handler"; import { getModifierTierTextTint } from "#ui/text"; -import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#utils/common"; +import { applyChallenges } from "#utils/challenge-utils"; +import { + BooleanHolder, + formatMoney, + isNullOrUndefined, + NumberHolder, + padInt, + randSeedInt, + randSeedItem, +} from "#utils/common"; import { getEnumKeys, getEnumValues } from "#utils/enums"; import { getModifierPoolForType, getModifierType } from "#utils/modifier-utils"; import i18next from "i18next"; @@ -421,7 +431,7 @@ export class TerastallizeModifierType extends PokemonModifierType { super( "", `${PokemonType[teraType].toLowerCase()}_tera_shard`, - (type, args) => new TerrastalizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), + (type, args) => new TerastallizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), (pokemon: PlayerPokemon) => { if ( [pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter( @@ -533,7 +543,9 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType { ); this.selectFilter = (pokemon: PlayerPokemon) => { - if (pokemon.hp) { + const selectStatus = new BooleanHolder(pokemon.hp !== 0); + applyChallenges(ChallengeType.PREVENT_REVIVE, selectStatus); + if (selectStatus.value) { return PartyUiHandler.NoEffectMessage; } return null; @@ -1011,6 +1023,7 @@ class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierTy "modifierType:ModifierType.AllPokemonFullReviveModifierType", (_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false, true), ); + this.group = "revive"; } } @@ -1262,7 +1275,9 @@ export class FusePokemonModifierType extends PokemonModifierType { iconImage, (_type, args) => new FusePokemonModifier(this, (args[0] as PlayerPokemon).id, (args[1] as PlayerPokemon).id), (pokemon: PlayerPokemon) => { - if (pokemon.isFusion()) { + const selectStatus = new BooleanHolder(pokemon.isFusion()); + applyChallenges(ChallengeType.POKEMON_FUSION, pokemon, selectStatus); + if (selectStatus.value) { return PartyUiHandler.NoEffectMessage; } return null; @@ -2574,11 +2589,15 @@ function getModifierTypeOptionWithRetry( ): ModifierTypeOption { allowLuckUpgrades = allowLuckUpgrades ?? true; let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades); + const candidateValidity = new BooleanHolder(true); + applyChallenges(ChallengeType.WAVE_REWARD, candidate, candidateValidity); let r = 0; while ( - existingOptions.length && - ++r < retryCount && - existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length + (existingOptions.length && + ++r < retryCount && + existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group) + .length) || + !candidateValidity.value ) { candidate = getNewModifierTypeOption( party, @@ -2588,6 +2607,7 @@ function getModifierTypeOptionWithRetry( 0, allowLuckUpgrades, ); + applyChallenges(ChallengeType.WAVE_REWARD, candidate, candidateValidity); } return candidate!; } @@ -2648,7 +2668,15 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseC [new ModifierTypeOption(modifierTypeInitObj.FULL_RESTORE(), 0, baseCost * 2.25)], [new ModifierTypeOption(modifierTypeInitObj.SACRED_ASH(), 0, baseCost * 10)], ]; - return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); + + return options + .slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)) + .flat() + .filter(shopItem => { + const status = new BooleanHolder(true); + applyChallenges(ChallengeType.SHOP_ITEM, shopItem, status); + return status.value; + }); } export function getEnemyBuffModifierForWave( diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index b31bee7fc69..6907b6907ca 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -2073,7 +2073,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier { } } -export class TerrastalizeModifier extends ConsumablePokemonModifier { +export class TerastallizeModifier extends ConsumablePokemonModifier { public declare type: TerastallizeModifierType; public teraType: PokemonType; @@ -2084,9 +2084,9 @@ export class TerrastalizeModifier extends ConsumablePokemonModifier { } /** - * Checks if {@linkcode TerrastalizeModifier} should be applied + * Checks if {@linkcode TerastallizeModifier} should be applied * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item - * @returns `true` if the {@linkcode TerrastalizeModifier} should be applied + * @returns `true` if the {@linkcode TerastallizeModifier} should be applied */ override shouldApply(playerPokemon?: PlayerPokemon): boolean { return ( @@ -2098,7 +2098,7 @@ export class TerrastalizeModifier extends ConsumablePokemonModifier { } /** - * Applies {@linkcode TerrastalizeModifier} + * Applies {@linkcode TerastallizeModifier} * @param pokemon The {@linkcode PlayerPokemon} that consumes the item * @returns `true` if hp was restored */ @@ -3875,7 +3875,7 @@ const ModifierClassMap = Object.freeze({ ResetNegativeStatStageModifier, FieldEffectModifier, ConsumablePokemonModifier, - TerrastalizeModifier, + TerastallizeModifier, PokemonHpRestoreModifier, PokemonStatusHealModifier, ConsumablePokemonMoveModifier, diff --git a/src/phase.ts b/src/phase.ts index 46a81dddb6f..eccbf3127e6 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -11,7 +11,7 @@ export abstract class Phase { /** * The string name of the phase, used to identify the phase type for {@linkcode is} * - * @privateremarks + * @privateRemarks * * When implementing a phase, you must set the `phaseName` property to the name of the phase. */ diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index fcddd23dd20..aea39cff294 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -12,6 +12,7 @@ import { } from "#data/pokeball"; import { getStatusEffectCatchRateMultiplier } from "#data/status-effect"; import { BattlerIndex } from "#enums/battler-index"; +import { ChallengeType } from "#enums/challenge-type"; import type { PokeballType } from "#enums/pokeball"; import { StatusEffect } from "#enums/status-effect"; import { UiMode } from "#enums/ui-mode"; @@ -23,6 +24,8 @@ import { achvs } from "#system/achv"; import type { PartyOption } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler"; import { SummaryUiMode } from "#ui/summary-ui-handler"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder } from "#utils/common"; import i18next from "i18next"; // TODO: Refactor and split up to allow for overriding capture chance @@ -287,6 +290,13 @@ export class AttemptCapturePhase extends PokemonPhase { }); }; Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => { + const addStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); + if (!addStatus.value) { + removePokemon(); + end(); + return; + } if (globalScene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) { const promptRelease = () => { globalScene.ui.showText( diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 8d199915385..2dbb74c4a85 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -58,12 +58,6 @@ export class BattleEndPhase extends BattlePhase { globalScene.phaseManager.unshiftNew("GameOverPhase", true); } - for (const pokemon of globalScene.getField()) { - if (pokemon) { - pokemon.tempSummonData.waveTurnCount = 1; - } - } - for (const pokemon of globalScene.getPokemonAllowedInBattle()) { applyAbAttrs("PostBattleAbAttr", { pokemon, victory: this.isVictory }); } diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 016d4ff5d3b..ff9ee7cc197 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -9,6 +9,7 @@ import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleType } from "#enums/battle-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; +import { ChallengeType } from "#enums/challenge-type"; import { Command } from "#enums/command"; import { FieldPosition } from "#enums/field-position"; import { MoveId } from "#enums/move-id"; @@ -21,6 +22,8 @@ import type { MoveTargetSet } from "#moves/move"; import { getMoveTargets } from "#moves/move-utils"; import { FieldPhase } from "#phases/field-phase"; import type { TurnMove } from "#types/turn-move"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder } from "#utils/common"; import i18next from "i18next"; export class CommandPhase extends FieldPhase { @@ -109,7 +112,7 @@ export class CommandPhase extends FieldPhase { * Clear out all unusable moves in front of the currently acting pokemon's move queue. */ // TODO: Refactor move queue handling to ensure that this method is not necessary. - private clearUnusuableMoves(): void { + private clearUnusableMoves(): void { const playerPokemon = this.getPokemon(); const moveQueue = playerPokemon.getMoveQueue(); if (moveQueue.length === 0) { @@ -140,7 +143,7 @@ export class CommandPhase extends FieldPhase { * @returns Whether a queued move was successfully set to be executed. */ private tryExecuteQueuedMove(): boolean { - this.clearUnusuableMoves(); + this.clearUnusableMoves(); const playerPokemon = globalScene.getPlayerField()[this.fieldIndex]; const moveQueue = playerPokemon.getMoveQueue(); @@ -210,16 +213,27 @@ export class CommandPhase extends FieldPhase { const move = user.getMoveset()[cursor]; globalScene.ui.setMode(UiMode.MESSAGE); - // Decides between a Disabled, Not Implemented, or No PP translation message - const errorMessage = user.isMoveRestricted(move.moveId, user) - ? user.getRestrictingTag(move.moveId, user)!.selectionDeniedText(user, move.moveId) - : move.getName().endsWith(" (N)") - ? "battle:moveNotImplemented" - : "battle:moveNoPP"; + // Set the translation key for why the move cannot be selected + let cannotSelectKey: string; + const moveStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.POKEMON_MOVE, move.moveId, moveStatus); + if (!moveStatus.value) { + cannotSelectKey = "battle:moveCannotUseChallenge"; + } else if (move.getPpRatio() === 0) { + cannotSelectKey = "battle:moveNoPP"; + } else if (move.getName().endsWith(" (N)")) { + cannotSelectKey = "battle:moveNotImplemented"; + } else if (user.isMoveRestricted(move.moveId, user)) { + cannotSelectKey = user.getRestrictingTag(move.moveId, user)!.selectionDeniedText(user, move.moveId); + } else { + // TODO: Consider a message that signals a being unusable for an unknown reason + cannotSelectKey = ""; + } + const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator globalScene.ui.showText( - i18next.t(errorMessage, { moveName: moveName }), + i18next.t(cannotSelectKey, { moveName: moveName }), null, () => { globalScene.ui.clearText(); diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 94923ae8c1f..e9d28e0fe2a 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -148,9 +148,9 @@ export class EggHatchPhase extends Phase { this.eggHatchOverlay = globalScene.add.rectangle( 0, - -globalScene.game.canvas.height / 6, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + -globalScene.scaledCanvas.height, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0xffffff, ); this.eggHatchOverlay.setOrigin(0, 0); diff --git a/src/phases/end-card-phase.ts b/src/phases/end-card-phase.ts index b9b383db13d..5ce0ca47b99 100644 --- a/src/phases/end-card-phase.ts +++ b/src/phases/end-card-phase.ts @@ -25,8 +25,8 @@ export class EndCardPhase extends Phase { globalScene.field.add(this.endCard); this.text = addTextObject( - globalScene.game.canvas.width / 12, - globalScene.game.canvas.height / 6 - 16, + globalScene.scaledCanvas.width / 2, + globalScene.scaledCanvas.height - 16, i18next.t("battle:congratulations"), TextStyle.SUMMARY, { fontSize: "128px" }, diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index cad79455af3..ad3db97d520 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -90,16 +90,16 @@ export class EvolutionPhase extends Phase { .setVisible(false); this.evolutionBgOverlay = globalScene.add - .rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0x262626) + .rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height, 0x262626) .setOrigin(0) .setAlpha(0); this.evolutionContainer.add([this.evolutionBaseBg, this.evolutionBgOverlay, this.evolutionBg]); this.evolutionOverlay = globalScene.add.rectangle( 0, - -globalScene.game.canvas.height / 6, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6 - 48, + -globalScene.scaledCanvas.height, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height - 48, 0xffffff, ); this.evolutionOverlay.setOrigin(0).setAlpha(0); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index cd7c7a8f48f..f88f9d0cad1 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -649,7 +649,6 @@ export class MovePhase extends BattlePhase { * Displays the move's usage text to the player as applicable for the move being used. */ public showMoveText(): void { - // No text for Moves.NONE, recharging/2-turn moves or interrupted moves if ( this.move.moveId === MoveId.NONE || this.pokemon.getTag(BattlerTagType.RECHARGING) || @@ -658,7 +657,6 @@ export class MovePhase extends BattlePhase { return; } - // Play message for magic coat reflection // TODO: This should be done by the move... globalScene.phaseManager.queueMessage( i18next.t(isReflected(this.useMode) ? "battle:magicCoatActivated" : "battle:useMove", { diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index 80d8b315102..1030d5eb9d9 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -1,6 +1,8 @@ import { globalScene } from "#app/global-scene"; +import { ChallengeType } from "#enums/challenge-type"; import { BattlePhase } from "#phases/battle-phase"; -import { fixedInt } from "#utils/common"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder, fixedInt } from "#utils/common"; export class PartyHealPhase extends BattlePhase { public readonly phaseName = "PartyHealPhase"; @@ -20,7 +22,14 @@ export class PartyHealPhase extends BattlePhase { globalScene.fadeOutBgm(1000, false); } globalScene.ui.fadeOut(1000).then(() => { + const preventRevive = new BooleanHolder(false); + applyChallenges(ChallengeType.PREVENT_REVIVE, preventRevive); for (const pokemon of globalScene.getPlayerParty()) { + // Prevent reviving fainted pokemon during certain challenges + if (pokemon.isFainted() && preventRevive.value) { + continue; + } + pokemon.hp = pokemon.getMaxHp(); pokemon.resetStatus(true, false, false, true); for (const move of pokemon.moveset) { diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index ab96bf5c45e..4089f0c2852 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -1,11 +1,13 @@ import { globalScene } from "#app/global-scene"; import { biomeLinks, getBiomeName } from "#balance/biomes"; import { BiomeId } from "#enums/biome-id"; +import { ChallengeType } from "#enums/challenge-type"; import { UiMode } from "#enums/ui-mode"; import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier"; import { BattlePhase } from "#phases/battle-phase"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; -import { randSeedInt } from "#utils/common"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder, randSeedInt } from "#utils/common"; export class SelectBiomePhase extends BattlePhase { public readonly phaseName = "SelectBiomePhase"; @@ -20,7 +22,11 @@ export class SelectBiomePhase extends BattlePhase { const setNextBiome = (nextBiome: BiomeId) => { if (nextWaveIndex % 10 === 1) { globalScene.applyModifiers(MoneyInterestModifier, true); - globalScene.phaseManager.unshiftNew("PartyHealPhase", false); + const healStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.PARTY_HEAL, healStatus); + if (healStatus.value) { + globalScene.phaseManager.unshiftNew("PartyHealPhase", false); + } } globalScene.phaseManager.unshiftNew("SwitchBiomePhase", nextBiome); this.end(); diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index d6bd252c77d..ef3fa74bd44 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -1,7 +1,6 @@ import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; import { Phase } from "#app/phase"; -import { applyChallenges } from "#data/challenge"; import { SpeciesFormChangeMoveLearnedTrigger } from "#data/form-change-triggers"; import { Gender } from "#data/gender"; import { ChallengeType } from "#enums/challenge-type"; @@ -10,6 +9,7 @@ import { UiMode } from "#enums/ui-mode"; import { overrideHeldItems, overrideModifiers } from "#modifiers/modifier"; import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; import type { Starter } from "#ui/starter-select-ui-handler"; +import { applyChallenges } from "#utils/challenge-utils"; import { isNullOrUndefined } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 6f0493f707d..15d92ba2812 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -16,7 +16,7 @@ import type { Modifier } from "#modifiers/modifier"; import { getDailyRunStarterModifiers, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; import type { SessionSaveData } from "#system/game-data"; import { vouchers } from "#system/voucher"; -import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#utils/common"; import i18next from "i18next"; diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 4b1a79d7443..c0f4a32d7e1 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -3,10 +3,13 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#data/data-lists"; import { BattleType } from "#enums/battle-type"; import type { BattlerIndex } from "#enums/battler-index"; +import { ChallengeType } from "#enums/challenge-type"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import type { CustomModifierSettings } from "#modifiers/modifier-type"; import { handleMysteryEncounterVictory } from "#mystery-encounters/encounter-phase-utils"; import { PokemonPhase } from "#phases/pokemon-phase"; +import { applyChallenges } from "#utils/challenge-utils"; +import { BooleanHolder } from "#utils/common"; export class VictoryPhase extends PokemonPhase { public readonly phaseName = "VictoryPhase"; @@ -63,7 +66,9 @@ export class VictoryPhase extends PokemonPhase { break; } } - if (globalScene.currentBattle.waveIndex % 10) { + const healStatus = new BooleanHolder(globalScene.currentBattle.waveIndex % 10 === 0); + applyChallenges(ChallengeType.PARTY_HEAL, healStatus); + if (!healStatus.value) { globalScene.phaseManager.pushNew( "SelectModifierPhase", undefined, diff --git a/src/plugins/api/pokerogue-account-api.ts b/src/plugins/api/pokerogue-account-api.ts index 03f522e8dac..22f86413618 100644 --- a/src/plugins/api/pokerogue-account-api.ts +++ b/src/plugins/api/pokerogue-account-api.ts @@ -1,6 +1,7 @@ import { ApiBase } from "#api/api-base"; import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import type { + AccountChangePwRequest, AccountInfoResponse, AccountLoginRequest, AccountLoginResponse, @@ -95,4 +96,19 @@ export class PokerogueAccountApi extends ApiBase { removeCookie(SESSION_ID_COOKIE_NAME); // we are always clearing the cookie. } + + public async changePassword(changePwData: AccountChangePwRequest) { + try { + const response = await this.doPost("/account/changepw", changePwData, "form-urlencoded"); + if (response.ok) { + return null; + } + console.warn("Change password failed!", response.status, response.statusText); + return response.text(); + } catch (err) { + console.warn("Change password failed!", err); + } + + return "Unknown error!"; + } } diff --git a/src/system/achv.ts b/src/system/achv.ts index 69eade02e35..383d07252e6 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -5,6 +5,7 @@ import { FlipStatChallenge, FreshStartChallenge, InverseBattleChallenge, + LimitedCatchChallenge, SingleGenerationChallenge, SingleTypeChallenge, } from "#data/challenge"; @@ -922,6 +923,19 @@ export const achvs = { c.value > 0 && globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0), ).setSecret(), + // TODO: Decide on icon + NUZLOCKE: new ChallengeAchv( + "NUZLOCKE", + "", + "NUZLOCKE.description", + "leaf_stone", + 100, + c => + c instanceof LimitedCatchChallenge && + c.value > 0 && + globalScene.gameMode.challenges.some(c => c.id === Challenges.HARDCORE && c.value > 0) && + globalScene.gameMode.challenges.some(c => c.id === Challenges.FRESH_START && c.value > 0), + ), BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(), }; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index d899afa19ef..ae559072e35 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -11,7 +11,6 @@ import { speciesEggMoves } from "#balance/egg-moves"; import { pokemonPrevolutions } from "#balance/pokemon-evolutions"; import { speciesStarterCosts } from "#balance/starters"; import { ArenaTrapTag } from "#data/arena-tag"; -import { applyChallenges } from "#data/challenge"; import { allMoves, allSpecies } from "#data/data-lists"; import type { Egg } from "#data/egg"; import { pokemonFormChanges } from "#data/pokemon-forms"; @@ -63,6 +62,7 @@ import { VoucherType, vouchers } from "#system/voucher"; import { trainerConfigs } from "#trainers/trainer-config"; import type { DexData, DexEntry } from "#types/dex-data"; import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler"; +import { applyChallenges } from "#utils/challenge-utils"; import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common"; import { decrypt, encrypt } from "#utils/data"; import { getEnumKeys } from "#utils/enums"; diff --git a/src/system/settings/settings-gamepad.ts b/src/system/settings/settings-gamepad.ts index 2e25eda7300..c334159cc3c 100644 --- a/src/system/settings/settings-gamepad.ts +++ b/src/system/settings/settings-gamepad.ts @@ -144,7 +144,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole handler: () => changeGamepadHandler(g), })), { - label: i18next.t("settings:cancelContollerChoice"), + label: i18next.t("settings:cancelControllerChoice"), handler: cancelHandler, }, ], diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 9877f298404..9e711735e7f 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -653,7 +653,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { console.log(this.event.bannerKey); const padding = 5; const showTimer = this.event.eventType !== EventType.NO_TIMER_DISPLAY; - const yPosition = globalScene.game.canvas.height / 6 - padding - (showTimer ? 10 : 0) - (this.event.yOffset ?? 0); + const yPosition = globalScene.scaledCanvas.height - padding - (showTimer ? 10 : 0) - (this.event.yOffset ?? 0); this.banner = new Phaser.GameObjects.Image(globalScene, this.availableWidth / 2, yPosition - padding, key); this.banner.setName("img-event-banner"); this.banner.setOrigin(0.5, 1); diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstract-option-select-ui-handler.ts similarity index 99% rename from src/ui/abstact-option-select-ui-handler.ts rename to src/ui/abstract-option-select-ui-handler.ts index 2fb0159b6ef..b7279bc2d30 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstract-option-select-ui-handler.ts @@ -66,7 +66,7 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler { setup() { const ui = this.getUi(); - this.optionSelectContainer = globalScene.add.container(globalScene.game.canvas.width / 6 - 1, -48); + this.optionSelectContainer = globalScene.add.container(globalScene.scaledCanvas.width - 1, -48); this.optionSelectContainer.setName(`option-select-${this.mode ? UiMode[this.mode] : "UNKNOWN"}`); this.optionSelectContainer.setVisible(false); ui.add(this.optionSelectContainer); @@ -135,7 +135,7 @@ export abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectText.setName("text-option-select"); this.optionSelectTextContainer.add(this.optionSelectText); this.optionSelectContainer.setPosition( - globalScene.game.canvas.width / 6 - 1 - (this.config?.xOffset || 0), + globalScene.scaledCanvas.width - 1 - (this.config?.xOffset || 0), -48 + (this.config?.yOffset || 0), ); this.optionSelectBg.width = Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth()); diff --git a/src/ui/achv-bar.ts b/src/ui/achv-bar.ts index bb1ef95c9de..0f71bcbfde0 100644 --- a/src/ui/achv-bar.ts +++ b/src/ui/achv-bar.ts @@ -21,7 +21,7 @@ export class AchvBar extends Phaser.GameObjects.Container { public shown: boolean; constructor() { - super(globalScene, globalScene.game.canvas.width / 6, 0); + super(globalScene, globalScene.scaledCanvas.width, 0); this.playerGender = globalScene.gameData.gender; } @@ -118,7 +118,7 @@ export class AchvBar extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6 - this.bg.width / 2, + x: globalScene.scaledCanvas.width - this.bg.width / 2, duration: 500, ease: "Sine.easeOut", }); @@ -136,7 +136,7 @@ export class AchvBar extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6, + x: globalScene.scaledCanvas.width, duration: 500, ease: "Sine.easeIn", onComplete: () => { diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index 01fd1d45a61..2c04e24e0f2 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -72,9 +72,9 @@ export class AchvsUiHandler extends MessageUiHandler { const ui = this.getUi(); /** Width of the global canvas / 6 */ - const WIDTH = globalScene.game.canvas.width / 6; + const WIDTH = globalScene.scaledCanvas.width; /** Height of the global canvas / 6 */ - const HEIGHT = globalScene.game.canvas.height / 6; + const HEIGHT = globalScene.scaledCanvas.height; this.mainContainer = globalScene.add.container(1, -HEIGHT + 1); diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index d2a45646690..e243bef342e 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -36,7 +36,7 @@ interface ArenaEffectInfo { /** The enum string representation of the effect */ name: string; /** {@linkcode ArenaEffectType} type of effect */ - effecType: ArenaEffectType; + effectType: ArenaEffectType; /** The maximum duration set by the effect */ maxDuration: number; @@ -246,7 +246,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { // Creates a proxy object to decide which text object needs to be updated let textObject: Phaser.GameObjects.Text; - switch (fieldEffectInfo.effecType) { + switch (fieldEffectInfo.effectType) { case ArenaEffectType.PLAYER: textObject = this.flyoutTextPlayer; break; @@ -300,7 +300,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { const existingTrapTagIndex = isArenaTrapTag ? this.fieldEffectInfo.findIndex( - e => tagAddedEvent.arenaTagType === e.tagType && arenaEffectType === e.effecType, + e => tagAddedEvent.arenaTagType === e.tagType && arenaEffectType === e.effectType, ) : -1; let name: string = getFieldEffectText(ArenaTagType[tagAddedEvent.arenaTagType]); @@ -318,7 +318,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { this.fieldEffectInfo.push({ name, - effecType: arenaEffectType, + effectType: arenaEffectType, maxDuration: tagAddedEvent.duration, duration: tagAddedEvent.duration, tagType: tagAddedEvent.arenaTagType, @@ -353,7 +353,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { ? WeatherType[fieldEffectChangedEvent.newWeatherType] : TerrainType[fieldEffectChangedEvent.newTerrainType], ), - effecType: + effectType: fieldEffectChangedEvent instanceof WeatherChangedEvent ? ArenaEffectType.WEATHER : ArenaEffectType.TERRAIN, maxDuration: fieldEffectChangedEvent.duration, duration: fieldEffectChangedEvent.duration, diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/autocomplete-ui-handler.ts index 6016245c38d..337b17048dc 100644 --- a/src/ui/autocomplete-ui-handler.ts +++ b/src/ui/autocomplete-ui-handler.ts @@ -1,6 +1,6 @@ import { Button } from "#enums/buttons"; import { UiMode } from "#enums/ui-mode"; -import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; +import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler"; export class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { modalContainer: Phaser.GameObjects.Container; diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 67beb0eba84..99dabd893df 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -37,7 +37,7 @@ export class BallUiHandler extends UiHandler { const optionsText = addTextObject(0, 0, optionsTextContent, TextStyle.WINDOW, { align: "right", maxLines: 6 }); const optionsTextWidth = optionsText.displayWidth; this.pokeballSelectContainer = globalScene.add.container( - globalScene.game.canvas.width / 6 - 51 - Math.max(64, optionsTextWidth), + globalScene.scaledCanvas.width - 51 - Math.max(64, optionsTextWidth), -49, ); this.pokeballSelectContainer.setVisible(false); diff --git a/src/ui/base-stats-overlay.ts b/src/ui/base-stats-overlay.ts index e3ba472475a..3b432e13096 100644 --- a/src/ui/base-stats-overlay.ts +++ b/src/ui/base-stats-overlay.ts @@ -16,7 +16,6 @@ interface BaseStatsOverlaySettings { const HEIGHT = 120; const BORDER = 8; -const GLOBAL_SCALE = 6; const shortStats = ["HP", "ATK", "DEF", "SPATK", "SPDEF", "SPD"]; export class BaseStatsOverlay extends Phaser.GameObjects.Container implements InfoToggle { @@ -109,7 +108,7 @@ export class BaseStatsOverlay extends Phaser.GameObjects.Container implements In // width of this element static getWidth(_scale: number): number { - return globalScene.game.canvas.width / GLOBAL_SCALE / 2; + return globalScene.scaledCanvas.width / 2; } // height of this element diff --git a/src/ui/battle-info/player-battle-info.ts b/src/ui/battle-info/player-battle-info.ts index 62a2eddecb9..998f7cbb41f 100644 --- a/src/ui/battle-info/player-battle-info.ts +++ b/src/ui/battle-info/player-battle-info.ts @@ -39,7 +39,7 @@ export class PlayerBattleInfo extends BattleInfo { statOverflow: 1, }, }; - super(Math.floor(globalScene.game.canvas.width / 6) - 10, -72, true, posParams); + super(Math.floor(globalScene.scaledCanvas.width) - 10, -72, true, posParams); this.hpNumbersContainer = globalScene.add.container(-15, 10).setName("container_hp"); @@ -198,11 +198,11 @@ export class PlayerBattleInfo extends BattleInfo { this.lastLevelCapped = isLevelCapped; if (this.lastExp !== pokemon.exp || this.lastLevel !== pokemon.level) { - const durationMultipler = Math.max( + const durationMultiplier = Math.max( Phaser.Tweens.Builders.GetEaseFunction("Cubic.easeIn")(1 - Math.min(pokemon.level - this.lastLevel, 10) / 10), 0.1, ); - await this.updatePokemonExp(pokemon, false, durationMultipler); + await this.updatePokemonExp(pokemon, false, durationMultiplier); } else if (isLevelCapped !== oldLevelCapped) { this.setLevel(pokemon.level); } diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index b58897b9022..2ecca06172b 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -94,7 +94,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { this.levelUpStatsContainer = levelUpStatsContainer; - const levelUpStatsLabelsContent = addTextObject(globalScene.game.canvas.width / 6 - 73, -94, "", TextStyle.WINDOW, { + const levelUpStatsLabelsContent = addTextObject(globalScene.scaledCanvas.width - 73, -94, "", TextStyle.WINDOW, { maxLines: 6, }); let levelUpStatsLabelText = ""; @@ -106,7 +106,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { levelUpStatsLabelsContent.x -= levelUpStatsLabelsContent.displayWidth; const levelUpStatsBg = addWindow( - globalScene.game.canvas.width / 6, + globalScene.scaledCanvas.width, -100, 80 + levelUpStatsLabelsContent.displayWidth, 100, @@ -117,7 +117,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { levelUpStatsContainer.add(levelUpStatsLabelsContent); const levelUpStatsIncrContent = addTextObject( - globalScene.game.canvas.width / 6 - 50, + globalScene.scaledCanvas.width - 50, -94, "+\n+\n+\n+\n+\n+", TextStyle.WINDOW, @@ -128,7 +128,7 @@ export class BattleMessageUiHandler extends MessageUiHandler { this.levelUpStatsIncrContent = levelUpStatsIncrContent; const levelUpStatsValuesContent = addBBCodeTextObject( - globalScene.game.canvas.width / 6 - 7, + globalScene.scaledCanvas.width - 7, -94, "", TextStyle.WINDOW, diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 239b963227b..b29ac3ec520 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -19,7 +19,7 @@ export class CandyBar extends Phaser.GameObjects.Container { public shown: boolean; constructor() { - super(globalScene, globalScene.game.canvas.width / 6, -(globalScene.game.canvas.height / 6) + 15); + super(globalScene, globalScene.scaledCanvas.width, -globalScene.scaledCanvas.height + 15); } setup(): void { @@ -80,7 +80,7 @@ export class CandyBar extends Phaser.GameObjects.Container { this.tween = globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6 - (this.bg.width - 5), + x: globalScene.scaledCanvas.width - (this.bg.width - 5), duration: 500, ease: "Sine.easeOut", onComplete: () => { @@ -111,7 +111,7 @@ export class CandyBar extends Phaser.GameObjects.Container { this.tween = globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6, + x: globalScene.scaledCanvas.width, duration: 500, ease: "Sine.easeIn", onComplete: () => { diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index 4a7ab7641a3..b37ddcc6a5f 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -58,11 +58,11 @@ export class GameChallengesUiHandler extends UiHandler { this.widestTextBox = 0; - this.challengesContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + this.challengesContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1); this.challengesContainer.setName("challenges"); this.challengesContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); @@ -79,7 +79,7 @@ export class GameChallengesUiHandler extends UiHandler { this.challengesContainer.add(bgOverlay); // TODO: Change this back to /9 when adding in difficulty - const headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6, 24); + const headerBg = addWindow(0, 0, globalScene.scaledCanvas.width, 24); headerBg.setName("window-header-bg"); headerBg.setOrigin(0, 0); diff --git a/src/ui/change-password-form-ui-handler.ts b/src/ui/change-password-form-ui-handler.ts new file mode 100644 index 00000000000..eccc67ffb04 --- /dev/null +++ b/src/ui/change-password-form-ui-handler.ts @@ -0,0 +1,124 @@ +import { globalScene } from "#app/global-scene"; +import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; +import { UiMode } from "#enums/ui-mode"; +import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; +import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; +import type { ModalConfig } from "#ui/modal-ui-handler"; +import i18next from "i18next"; + +export class ChangePasswordFormUiHandler extends FormModalUiHandler { + private readonly ERR_PASSWORD: string = "invalid password"; + private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist"; + private readonly ERR_PASSWORD_MISMATCH: string = "password doesn't match"; + + constructor(mode: UiMode | null = null) { + super(mode); + } + + setup(): void { + super.setup(); + } + + override getModalTitle(_config?: ModalConfig): string { + return i18next.t("menu:changePassword"); + } + + override getWidth(_config?: ModalConfig): number { + return 160; + } + + override getMargin(_config?: ModalConfig): [number, number, number, number] { + return [0, 0, 48, 0]; + } + + override getButtonLabels(_config?: ModalConfig): string[] { + return [i18next.t("settings:buttonSubmit"), i18next.t("menu:cancel")]; + } + + override getReadableErrorMessage(error: string): string { + const colonIndex = error?.indexOf(":"); + if (colonIndex > 0) { + error = error.slice(0, colonIndex); + } + switch (error) { + case this.ERR_PASSWORD: + return i18next.t("menu:invalidRegisterPassword"); + case this.ERR_ACCOUNT_EXIST: + return i18next.t("menu:accountNonExistent"); + case this.ERR_PASSWORD_MISMATCH: + return i18next.t("menu:passwordNotMatchingConfirmPassword"); + } + + return super.getReadableErrorMessage(error); + } + + override getInputFieldConfigs(): InputFieldConfig[] { + const inputFieldConfigs: InputFieldConfig[] = []; + inputFieldConfigs.push({ + label: i18next.t("menu:password"), + isPassword: true, + }); + inputFieldConfigs.push({ + label: i18next.t("menu:confirmPassword"), + isPassword: true, + }); + return inputFieldConfigs; + } + + override show(args: [ModalConfig, ...any]): boolean { + if (super.show(args)) { + const config = args[0]; + const originalSubmitAction = this.submitAction; + this.submitAction = () => { + if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + // Prevent overlapping overrides on action modification + this.submitAction = originalSubmitAction; + this.sanitizeInputs(); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); + const onFail = (error: string | null) => { + globalScene.ui.setMode(UiMode.CHANGE_PASSWORD_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.playError(); + }; + const [passwordInput, confirmPasswordInput] = this.inputs; + if (!passwordInput?.text) { + return onFail(this.getReadableErrorMessage("invalid password")); + } + if (passwordInput.text !== confirmPasswordInput.text) { + return onFail(this.ERR_PASSWORD_MISMATCH); + } + + pokerogueApi.account.changePassword({ password: passwordInput.text }).then(error => { + if (!error && originalSubmitAction) { + globalScene.ui.playSelect(); + originalSubmitAction(); + // Only clear inputs if the action was successful + for (const input of this.inputs) { + input.setText(""); + } + } else { + onFail(error); + } + }); + } + }; + // Upon pressing cancel, the inputs should be cleared + const originalCancelAction = this.cancelAction; + this.cancelAction = () => { + globalScene.ui.playSelect(); + for (const input of this.inputs) { + input.setText(""); + } + originalCancelAction?.(); + }; + + return true; + } + + return false; + } + + override clear() { + super.clear(); + this.setMouseCursorStyle("default"); //reset cursor + } +} diff --git a/src/ui/char-sprite.ts b/src/ui/char-sprite.ts index 381421086ff..1ab00291ff6 100644 --- a/src/ui/char-sprite.ts +++ b/src/ui/char-sprite.ts @@ -10,7 +10,7 @@ export class CharSprite extends Phaser.GameObjects.Container { public shown: boolean; constructor() { - super(globalScene, globalScene.game.canvas.width / 6 + 32, -42); + super(globalScene, globalScene.scaledCanvas.width + 32, -42); } setup(): void { @@ -49,7 +49,7 @@ export class CharSprite extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6 - 102, + x: globalScene.scaledCanvas.width - 102, duration: 750, ease: "Cubic.easeOut", onComplete: () => { @@ -95,7 +95,7 @@ export class CharSprite extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6 + 32, + x: globalScene.scaledCanvas.width + 32, duration: 750, ease: "Cubic.easeIn", onComplete: () => { diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 41ff559062a..b702bcd0803 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -42,7 +42,7 @@ export class CommandUiHandler extends UiHandler { ui.add(this.commandsContainer); this.teraButton = globalScene.add.sprite(-32, 15, "button_tera"); - this.teraButton.setName("terrastallize-button"); + this.teraButton.setName("terastallize-button"); this.teraButton.setScale(1.3); this.teraButton.setFrame("fire"); this.teraButton.setPipeline(globalScene.spritePipeline, { diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index b2f35931278..49e88556f1b 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -1,8 +1,8 @@ import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import { UiMode } from "#enums/ui-mode"; -import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; -import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler"; +import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler"; import i18next from "i18next"; export class ConfirmUiHandler extends AbstractOptionSelectUiHandler { @@ -69,7 +69,7 @@ export class ConfirmUiHandler extends AbstractOptionSelectUiHandler { const xOffset = args.length >= 7 && args[6] !== null ? (args[6] as number) : 0; const yOffset = args.length >= 8 && args[7] !== null ? (args[7] as number) : 0; - this.optionSelectContainer.setPosition(globalScene.game.canvas.width / 6 - 1 + xOffset, -48 + yOffset); + this.optionSelectContainer.setPosition(globalScene.scaledCanvas.width - 1 + xOffset, -48 + yOffset); this.setCursor(this.switchCheck ? this.switchCheckCursor : 0); return true; @@ -103,7 +103,7 @@ export class ConfirmUiHandler extends AbstractOptionSelectUiHandler { const xOffset = args.length >= 4 && args[3] !== null ? (args[3] as number) : 0; const yOffset = args.length >= 5 && args[4] !== null ? (args[4] as number) : 0; - this.optionSelectContainer.setPosition(globalScene.game.canvas.width / 6 - 1 + xOffset, -48 + yOffset); + this.optionSelectContainer.setPosition(globalScene.scaledCanvas.width - 1 + xOffset, -48 + yOffset); this.setCursor(this.switchCheck ? this.switchCheckCursor : 0); diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 5dcf05e2606..0287e0ee7a5 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -174,7 +174,7 @@ export class EggGachaUiHandler extends MessageUiHandler { const ui = this.getUi(); - this.eggGachaContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6).setVisible(false); + this.eggGachaContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height).setVisible(false); ui.add(this.eggGachaContainer); const bg = globalScene.add.nineslice(0, 0, "default_bg", undefined, 320, 180, 0, 0, 16, 0).setOrigin(0); @@ -212,7 +212,7 @@ export class EggGachaUiHandler extends MessageUiHandler { this.eggGachaOptionSelectBg = addWindow(0, 0, eggGachaOptionSelectWidth, 16 + 576 * this.scale).setOrigin(1); this.eggGachaOptionsContainer = globalScene.add - .container(globalScene.game.canvas.width / 6, 148) + .container(globalScene.scaledCanvas.width, 148) .add(this.eggGachaOptionSelectBg); this.eggGachaContainer.add(this.eggGachaOptionsContainer); @@ -278,7 +278,7 @@ export class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.add(this.eggGachaOptionsContainer); for (const voucher of getEnumValues(VoucherType)) { - const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * voucher, 0); + const container = globalScene.add.container(globalScene.scaledCanvas.width - 56 * voucher, 0); const bg = addWindow(0, 0, 56, 22).setOrigin(1, 0); container.add(bg); diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 5b2c9d40cfa..6536ef2af80 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -19,7 +19,7 @@ export class EggHatchSceneHandler extends UiHandler { } setup() { - this.eggHatchContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.eggHatchContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); globalScene.fieldUI.add(this.eggHatchContainer); const eggLightraysAnimFrames = globalScene.anims.generateFrameNames("egg_lightrays", { start: 0, end: 3 }); diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index 42f969b9d38..2516903f631 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -36,11 +36,11 @@ export class EggListUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - this.eggListContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6).setVisible(false); + this.eggListContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height).setVisible(false); ui.add(this.eggListContainer); const bgColor = globalScene.add - .rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0x006860) + .rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height, 0x006860) .setOrigin(0); const eggListBg = globalScene.add.image(0, 0, "egg_list_bg").setOrigin(0); @@ -69,9 +69,7 @@ export class EggListUiHandler extends MessageUiHandler { .withUpdateGridCallBack(() => this.updateEggIcons()) .withUpdateSingleElementCallback((i: number) => this.setEggDetails(i)); - this.eggListMessageBoxContainer = globalScene.add - .container(0, globalScene.game.canvas.height / 6) - .setVisible(false); + this.eggListMessageBoxContainer = globalScene.add.container(0, globalScene.scaledCanvas.height).setVisible(false); const eggListMessageBox = addWindow(1, -1, 318, 28).setOrigin(0, 1); this.eggListMessageBoxContainer.add(eggListMessageBox); diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index aa4e8974318..c66075dd910 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -59,11 +59,11 @@ export class EggSummaryUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - this.summaryContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.summaryContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); this.summaryContainer.setVisible(false); ui.add(this.summaryContainer); - this.eggHatchContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.eggHatchContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); this.eggHatchContainer.setVisible(false); ui.add(this.eggHatchContainer); @@ -92,7 +92,7 @@ export class EggSummaryUiHandler extends MessageUiHandler { iconContainerX + numCols * iconSize, iconContainerY + 3, 4, - globalScene.game.canvas.height / 6 - 20, + globalScene.scaledCanvas.height - 20, numRows, ); this.summaryContainer.add(scrollBar); diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index c22cf31faaa..0333594dc36 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -22,7 +22,7 @@ export class EvolutionSceneHandler extends MessageUiHandler { const ui = this.getUi(); - this.evolutionContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.evolutionContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); const messageBg = globalScene.add.sprite(0, 0, "bg", globalScene.windowType).setOrigin(0, 1).setVisible(false); diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 42f8cba5df4..1d856079939 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -105,15 +105,13 @@ export class FightUiHandler extends UiHandler implements InfoToggle { ]); // prepare move overlay - const overlayScale = 1; this.moveInfoOverlay = new MoveInfoOverlay({ delayVisibility: true, - scale: overlayScale, onSide: true, right: true, x: 0, - y: -MoveInfoOverlay.getHeight(overlayScale, true), - width: globalScene.game.canvas.width / 6 + 4, + y: -MoveInfoOverlay.getHeight(true), + width: globalScene.scaledCanvas.width + 4, hideEffectBox: true, hideBg: true, }); diff --git a/src/ui/filter-text.ts b/src/ui/filter-text.ts index ff7119dd778..d5809292c25 100644 --- a/src/ui/filter-text.ts +++ b/src/ui/filter-text.ts @@ -62,7 +62,7 @@ export class FilterText extends Phaser.GameObjects.Container { this.dialogueMessageBox = addWindow( -this.textPadding, 0, - globalScene.game.canvas.width / 6 + this.textPadding * 2, + globalScene.scaledCanvas.width + this.textPadding * 2, 49, false, false, diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 203d98a86c7..5c547465de9 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -19,6 +19,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { protected inputs: InputText[]; protected errorMessage: Phaser.GameObjects.Text; protected submitAction: Function | null; + protected cancelAction: (() => void) | null; protected tween: Phaser.Tweens.Tween; protected formLabels: Phaser.GameObjects.Text[]; @@ -126,22 +127,37 @@ export abstract class FormModalUiHandler extends ModalUiHandler { }); } - show(args: any[]): boolean { + override show(args: any[]): boolean { if (super.show(args)) { this.inputContainers.map(ic => ic.setVisible(true)); const config = args[0] as FormModalConfig; this.submitAction = config.buttonActions.length ? config.buttonActions[0] : null; + this.cancelAction = config.buttonActions[1] ?? null; - if (this.buttonBgs.length) { - this.buttonBgs[0].off("pointerdown"); - this.buttonBgs[0].on("pointerdown", () => { - if (this.submitAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { - this.submitAction(); + // #region: Override button pointerDown + // Override the pointerDown event for the buttonBgs to call the `submitAction` and `cancelAction` + // properties that we set above, allowing their behavior to change after this method terminates + // Some subclasses use this to add behavior to the submit and cancel action + + this.buttonBgs[0].off("pointerdown"); + this.buttonBgs[0].on("pointerdown", () => { + if (this.submitAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + this.submitAction(); + } + }); + const cancelBg = this.buttonBgs[1]; + if (cancelBg) { + cancelBg.off("pointerdown"); + cancelBg.on("pointerdown", () => { + // The seemingly redundant cancelAction check is intentionally left in as a defensive programming measure + if (this.cancelAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + this.cancelAction(); } }); } + //#endregion: Override pointerDown events this.modalContainer.y += 24; this.modalContainer.setAlpha(0); diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 524eaeece86..0f55faba5c4 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -2,7 +2,7 @@ import { pokerogueApi } from "#api/pokerogue-api"; import { globalScene } from "#app/global-scene"; import { TextStyle } from "#enums/text-style"; import { UiMode } from "#enums/ui-mode"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; import type { ModalConfig } from "#ui/modal-ui-handler"; @@ -49,7 +49,7 @@ export class LoginFormUiHandler extends FormModalUiHandler { private buildExternalPartyContainer() { this.externalPartyContainer = globalScene.add.container(0, 0); this.externalPartyContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 12, globalScene.game.canvas.height / 12), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width / 2, globalScene.scaledCanvas.height / 2), Phaser.Geom.Rectangle.Contains, ); this.externalPartyTitle = addTextObject(0, 4, "", TextStyle.SETTINGS_LABEL); diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index fa65cccab2f..da6bc9ced78 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -7,7 +7,7 @@ import { Button } from "#enums/buttons"; import { GameDataType } from "#enums/game-data-type"; import { TextStyle } from "#enums/text-style"; import { UiMode } from "#enums/ui-mode"; -import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { AdminMode, getAdminModeName } from "#ui/admin-ui-handler"; import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; import { BgmBar } from "#ui/bgm-bar"; @@ -96,10 +96,10 @@ export class MenuUiHandler extends MessageUiHandler { ui.bgmBar = this.bgmBar; - this.menuContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + this.menuContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1); this.menuContainer.setName("menu"); this.menuContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); @@ -146,10 +146,10 @@ export class MenuUiHandler extends MessageUiHandler { this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale; this.menuBg = addWindow( - globalScene.game.canvas.width / 6 - (this.optionSelectText.displayWidth + 25), + globalScene.scaledCanvas.width - (this.optionSelectText.displayWidth + 25), 0, this.optionSelectText.displayWidth + 19 + 24 * this.scale, - globalScene.game.canvas.height / 6 - 2, + globalScene.scaledCanvas.height - 2, ); this.menuBg.setOrigin(0, 0); @@ -174,7 +174,7 @@ export class MenuUiHandler extends MessageUiHandler { this.dialogueMessageBox = addWindow( -this.textPadding, 0, - globalScene.game.canvas.width / 6 + this.textPadding * 2, + globalScene.scaledCanvas.width + this.textPadding * 2, 49, false, false, @@ -311,6 +311,17 @@ export class MenuUiHandler extends MessageUiHandler { }, keepOpen: true, }, + { + // Note: i18n key is under `menu`, not `menuUiHandler` to avoid duplication + label: i18next.t("menu:changePassword"), + handler: () => { + ui.setOverlayMode(UiMode.CHANGE_PASSWORD_FORM, { + buttonActions: [() => ui.revertMode(), () => ui.revertMode()], + }); + return true; + }, + keepOpen: true, + }, { label: i18next.t("menuUiHandler:consentPreferences"), handler: () => { diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index 228d80968b9..51a6a21a29c 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -7,7 +7,7 @@ import { UiHandler } from "#ui/ui-handler"; import { addWindow, WindowVariant } from "#ui/ui-theme"; export interface ModalConfig { - buttonActions: Function[]; + buttonActions: ((...args: any[]) => any)[]; } export abstract class ModalUiHandler extends UiHandler { @@ -46,7 +46,7 @@ export abstract class ModalUiHandler extends UiHandler { this.modalContainer = globalScene.add.container(0, 0); this.modalContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); @@ -105,8 +105,8 @@ export abstract class ModalUiHandler extends UiHandler { const overlay = globalScene.add.rectangle( (this.getWidth() + marginLeft + marginRight) / 2, (this.getHeight() + marginTop + marginBottom) / 2, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0, ); overlay.setOrigin(0.5, 0.5); @@ -159,8 +159,8 @@ export abstract class ModalUiHandler extends UiHandler { const width = this.getWidth(config); const height = this.getHeight(config); this.modalContainer.setPosition( - (globalScene.game.canvas.width / 6 - (width + (marginRight - marginLeft))) / 2, - (-globalScene.game.canvas.height / 6 - (height + (marginBottom - marginTop))) / 2, + (globalScene.scaledCanvas.width - (width + (marginRight - marginLeft))) / 2, + (-globalScene.scaledCanvas.height - (height + (marginBottom - marginTop))) / 2, ); this.modalBg.setSize(width, height); diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 16eecf6993d..a070b522050 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -86,10 +86,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { transferButtonText.setOrigin(1, 0); this.transferButtonContainer.add(transferButtonText); - this.checkButtonContainer = globalScene.add.container( - globalScene.game.canvas.width / 6 - 1, - OPTION_BUTTON_YPOSITION, - ); + this.checkButtonContainer = globalScene.add.container(globalScene.scaledCanvas.width - 1, OPTION_BUTTON_YPOSITION); this.checkButtonContainer.setName("use-btn"); this.checkButtonContainer.setVisible(false); ui.add(this.checkButtonContainer); @@ -129,8 +126,8 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { this.lockRarityButtonContainer.add(this.lockRarityButtonText); this.continueButtonContainer = globalScene.add.container( - globalScene.game.canvas.width / 12, - -(globalScene.game.canvas.height / 12), + globalScene.scaledCanvas.width / 2, + -(globalScene.scaledCanvas.height / 2), ); this.continueButtonContainer.setVisible(false); ui.add(this.continueButtonContainer); @@ -146,15 +143,13 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { this.continueButtonContainer.add(continueButtonText); // prepare move overlay - const overlayScale = 1; this.moveInfoOverlay = new MoveInfoOverlay({ delayVisibility: true, - scale: overlayScale, onSide: true, right: true, x: 1, - y: -MoveInfoOverlay.getHeight(overlayScale, true) - 1, - width: globalScene.game.canvas.width / 6 - 2, + y: -MoveInfoOverlay.getHeight(true) - 1, + width: globalScene.scaledCanvas.width - 2, }); ui.add(this.moveInfoOverlay); // register the overlay to receive toggle events @@ -209,20 +204,20 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { this.updateRerollCostText(); const typeOptions = args[1] as ModifierTypeOption[]; - const removeHealShop = globalScene.gameMode.hasNoShop; + const hasShop = globalScene.gameMode.getShopStatus(); const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1)); globalScene.applyModifier(HealShopCostModifier, true, baseShopCost); - const shopTypeOptions = !removeHealShop + const shopTypeOptions = hasShop ? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value) : []; const optionsYOffset = shopTypeOptions.length > SHOP_OPTIONS_ROW_LIMIT ? -SINGLE_SHOP_ROW_YOFFSET : -DOUBLE_SHOP_ROW_YOFFSET; for (let m = 0; m < typeOptions.length; m++) { - const sliceWidth = globalScene.game.canvas.width / 6 / (typeOptions.length + 2); + const sliceWidth = globalScene.scaledCanvas.width / (typeOptions.length + 2); const option = new ModifierOption( sliceWidth * (m + 1) + sliceWidth * 0.5, - -globalScene.game.canvas.height / 12 + optionsYOffset, + -globalScene.scaledCanvas.height / 2 + optionsYOffset, typeOptions[m], ); option.setScale(0.5); @@ -243,10 +238,10 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { row ? SHOP_OPTIONS_ROW_LIMIT : 0, row ? undefined : SHOP_OPTIONS_ROW_LIMIT, ); - const sliceWidth = globalScene.game.canvas.width / 6 / (rowOptions.length + 2); + const sliceWidth = globalScene.scaledCanvas.width / (rowOptions.length + 2); const option = new ModifierOption( sliceWidth * (col + 1) + sliceWidth * 0.5, - -globalScene.game.canvas.height / 12 - globalScene.game.canvas.height / 32 - (42 - (28 * row - 1)), + -globalScene.scaledCanvas.height / 2 - globalScene.game.canvas.height / 32 - (42 - (28 * row - 1)), shopTypeOptions[m], ); option.setScale(0.375); @@ -370,7 +365,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) { this.setRowCursor(0); this.setCursor(2); - } else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) { + } else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && !hasShop) { this.setRowCursor(ShopCursorTarget.REWARDS); this.setCursor(0); } else { @@ -558,27 +553,27 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler { // Continue button when no shop items this.cursorObj.setScale(1.25); this.cursorObj.setPosition( - globalScene.game.canvas.width / 18 + 23, - -globalScene.game.canvas.height / 12 - + globalScene.scaledCanvas.width / 3 + 23, + -globalScene.scaledCanvas.height / 2 - (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2), ); ui.showText(i18next.t("modifierSelectUiHandler:continueNextWaveDescription")); return ret; } - const sliceWidth = globalScene.game.canvas.width / 6 / (options.length + 2); + const sliceWidth = globalScene.scaledCanvas.width / (options.length + 2); if (this.rowCursor < 2) { // Cursor on free items this.cursorObj.setPosition( sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 20, - -globalScene.game.canvas.height / 12 - + -globalScene.scaledCanvas.height / 2 - (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2), ); } else { // Cursor on paying items this.cursorObj.setPosition( sliceWidth * (cursor + 1) + sliceWidth * 0.5 - 16, - -globalScene.game.canvas.height / 12 - + -globalScene.scaledCanvas.height / 2 - globalScene.game.canvas.height / 32 - (-14 + 28 * (this.rowCursor - (this.shopOptionsRows.length - 1))), ); @@ -848,7 +843,7 @@ class ModifierOption extends Phaser.GameObjects.Container { /** * Start the tweens responsible for animating the option's appearance * - * @privateremarks + * @privateRemarks * This method is unusual. It "returns" (one via the actual return, one by via appending to the `promiseHolder` * parameter) two promises. The promise returned by the method resolves once the option's appearance animations have * completed, and is meant to allow callers to synchronize with the completion of the option's appearance animations. diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index f8632eb244e..f98630260db 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -10,17 +10,24 @@ import { fixedInt, getLocalizedSpriteKey } from "#utils/common"; import i18next from "i18next"; export interface MoveInfoOverlaySettings { - delayVisibility?: boolean; // if true, showing the overlay will only set it to active and populate the fields and the handler using this field has to manually call setVisible later. - scale?: number; // scale the box? A scale of 0.5 is recommended - top?: boolean; // should the effect box be on top? - right?: boolean; // should the effect box be on the right? - onSide?: boolean; // should the effect be on the side? ignores top argument if true - //location and width of the component; unaffected by scaling + /** + * If true, showing the overlay will only set it to active and populate the fields + * and the handler using this field has to manually call `setVisible` later. + */ + delayVisibility?: boolean; + /** Whether the effect box should be on top */ + top?: boolean; + /** Whether the effect box should be on the right */ + right?: boolean; + /** Whether the effect box should be on the side. Overrides the `top` param if `true`. */ + onSide?: boolean; + /** `x` position of the component, unaffected by scaling */ x?: number; + /** `y` position of the component, unaffected by scaling */ y?: number; - /** Default is always half the screen, regardless of scale */ + /** Width of the component, unaffected by scaling. Defaults to half the screen width. */ width?: number; - /** Determines whether to display the small secondary box */ + /** Whether to display the small secondary box */ hideEffectBox?: boolean; hideBg?: boolean; } @@ -54,12 +61,11 @@ export class MoveInfoOverlay extends Phaser.GameObjects.Container implements Inf options.top = false; } super(globalScene, options?.x, options?.y); - const scale = options?.scale || 1; // set up the scale - this.setScale(scale); + this.setScale(1); this.options = options || {}; // prepare the description box - const width = (options?.width || MoveInfoOverlay.getWidth(scale)) / scale; // divide by scale as we always want this to be half a window wide + const width = options?.width || MoveInfoOverlay.getWidth(); // we always want this to be half a window wide this.descBg = addWindow( options?.onSide && !options?.right ? EFF_WIDTH : 0, options?.top ? EFF_HEIGHT : 0, @@ -88,19 +94,19 @@ export class MoveInfoOverlay extends Phaser.GameObjects.Container implements Inf y: options?.y || 0, }; if (maskPointOrigin.x < 0) { - maskPointOrigin.x += globalScene.game.canvas.width / GLOBAL_SCALE; + maskPointOrigin.x += globalScene.scaledCanvas.width; } if (maskPointOrigin.y < 0) { - maskPointOrigin.y += globalScene.game.canvas.height / GLOBAL_SCALE; + maskPointOrigin.y += globalScene.scaledCanvas.height; } const moveDescriptionTextMaskRect = globalScene.make.graphics(); moveDescriptionTextMaskRect.fillStyle(0xff0000); moveDescriptionTextMaskRect.fillRect( - maskPointOrigin.x + ((options?.onSide && !options?.right ? EFF_WIDTH : 0) + BORDER) * scale, - maskPointOrigin.y + ((options?.top ? EFF_HEIGHT : 0) + BORDER - 2) * scale, - width - ((options?.onSide ? EFF_WIDTH : 0) - BORDER * 2) * scale, - (DESC_HEIGHT - (BORDER - 2) * 2) * scale, + maskPointOrigin.x + ((options?.onSide && !options?.right ? EFF_WIDTH : 0) + BORDER), + maskPointOrigin.y + ((options?.top ? EFF_HEIGHT : 0) + BORDER - 2), + width - ((options?.onSide ? EFF_WIDTH : 0) - BORDER * 2), + DESC_HEIGHT - (BORDER - 2) * 2, ); moveDescriptionTextMaskRect.setScale(6); const moveDescriptionTextMask = this.createGeometryMask(moveDescriptionTextMaskRect); @@ -233,12 +239,12 @@ export class MoveInfoOverlay extends Phaser.GameObjects.Container implements Inf } // width of this element - static getWidth(_scale: number): number { - return globalScene.game.canvas.width / GLOBAL_SCALE / 2; + static getWidth(): number { + return globalScene.scaledCanvas.width / 2; } // height of this element - static getHeight(scale: number, onSide?: boolean): number { - return (onSide ? Math.max(EFF_HEIGHT, DESC_HEIGHT) : EFF_HEIGHT + DESC_HEIGHT) * scale; + static getHeight(onSide?: boolean): number { + return onSide ? Math.max(EFF_HEIGHT, DESC_HEIGHT) : EFF_HEIGHT + DESC_HEIGHT; } } diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index b6bc464855c..881c375fa8a 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -471,7 +471,7 @@ export class MysteryEncounterUiHandler extends UiHandler { // View Party Button const viewPartyText = addBBCodeTextObject( - globalScene.game.canvas.width / 6, + globalScene.scaledCanvas.width, -24, getBBCodeFrag(i18next.t("mysteryEncounterMessages:view_party_button"), TextStyle.PARTY), TextStyle.PARTY, diff --git a/src/ui/party-exp-bar.ts b/src/ui/party-exp-bar.ts index 952a1f8227a..c9567ceb042 100644 --- a/src/ui/party-exp-bar.ts +++ b/src/ui/party-exp-bar.ts @@ -14,7 +14,7 @@ export class PartyExpBar extends Phaser.GameObjects.Container { public shown: boolean; constructor() { - super(globalScene, globalScene.game.canvas.width / 6, -(globalScene.game.canvas.height / 6) + 15); + super(globalScene, globalScene.scaledCanvas.width, -globalScene.scaledCanvas.height + 15); } setup(): void { @@ -66,7 +66,7 @@ export class PartyExpBar extends Phaser.GameObjects.Container { this.tween = globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6 - (this.bg.width - 5), + x: globalScene.scaledCanvas.width - (this.bg.width - 5), duration: 500 / Math.pow(2, globalScene.expGainsSpeed), ease: "Sine.easeOut", onComplete: () => { @@ -92,7 +92,7 @@ export class PartyExpBar extends Phaser.GameObjects.Container { this.tween = globalScene.tweens.add({ targets: this, - x: globalScene.game.canvas.width / 6, + x: globalScene.scaledCanvas.width, duration: 500, ease: "Sine.easeIn", onComplete: () => { diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index b259316f6fa..ff5e7246a6f 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,7 +1,6 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { pokemonEvolutions } from "#balance/pokemon-evolutions"; -import { applyChallenges } from "#data/challenge"; import { allMoves } from "#data/data-lists"; import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; import { Gender, getGenderColor, getGenderSymbol } from "#data/gender"; @@ -26,6 +25,7 @@ import { MoveInfoOverlay } from "#ui/move-info-overlay"; import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler"; import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text"; import { addWindow } from "#ui/ui-theme"; +import { applyChallenges } from "#utils/challenge-utils"; import { BooleanHolder, getLocalizedSpriteKey, randInt } from "#utils/common"; import { toTitleCase } from "#utils/strings"; import i18next from "i18next"; @@ -311,7 +311,7 @@ export class PartyUiHandler extends MessageUiHandler { this.partyCancelButton = partyCancelButton; - this.optionsContainer = globalScene.add.container(globalScene.game.canvas.width / 6 - 1, -1); + this.optionsContainer = globalScene.add.container(globalScene.scaledCanvas.width - 1, -1); partyContainer.add(this.optionsContainer); this.iconAnimHandler = new PokemonIconAnimHandler(); @@ -323,14 +323,12 @@ export class PartyUiHandler extends MessageUiHandler { this.partyDiscardModeButton = partyDiscardModeButton; - // prepare move overlay. in case it appears to be too big, set the overlayScale to .5 - const overlayScale = 1; + // prepare move overlay this.moveInfoOverlay = new MoveInfoOverlay({ - scale: overlayScale, top: true, x: 1, - y: -MoveInfoOverlay.getHeight(overlayScale) - 1, - width: globalScene.game.canvas.width / 12 - 30, + y: -MoveInfoOverlay.getHeight() - 1, + width: globalScene.scaledCanvas.width / 2 - 30, }); ui.add(this.moveInfoOverlay); @@ -827,6 +825,11 @@ export class PartyUiHandler extends MessageUiHandler { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true); } + // This is processed before the filter result since releasing does not depend on status. + if (option === PartyOption.RELEASE) { + return this.processReleaseOption(pokemon); + } + // If the pokemon is filtered out for this option, we cannot continue const filterResult = this.getFilterResult(option, pokemon); if (filterResult) { @@ -850,10 +853,6 @@ export class PartyUiHandler extends MessageUiHandler { // PartyUiMode.POST_BATTLE_SWITCH (SEND_OUT) // These are the options that need a callback - if (option === PartyOption.RELEASE) { - return this.processReleaseOption(pokemon); - } - if (this.partyUiMode === PartyUiMode.SPLICE) { if (option === PartyOption.SPLICE) { (this.selectCallback as PartyModifierSpliceSelectCallback)(this.transferCursor, this.cursor); diff --git a/src/ui/pokeball-tray.ts b/src/ui/pokeball-tray.ts index 9720aa42090..b1522af0e27 100644 --- a/src/ui/pokeball-tray.ts +++ b/src/ui/pokeball-tray.ts @@ -10,7 +10,7 @@ export class PokeballTray extends Phaser.GameObjects.Container { public shown: boolean; constructor(player: boolean) { - super(globalScene, player ? globalScene.game.canvas.width / 6 : 0, player ? -72 : -144); + super(globalScene, player ? globalScene.scaledCanvas.width : 0, player ? -72 : -144); this.player = player; } @@ -36,7 +36,7 @@ export class PokeballTray extends Phaser.GameObjects.Container { .map((_, i) => globalScene.add.sprite( (this.player ? -83 : 76) + - (globalScene.game.canvas.width / 6) * (this.player ? -1 : 1) + + globalScene.scaledCanvas.width * (this.player ? -1 : 1) + 10 * i * (this.player ? 1 : -1), -8, "pb_tray_ball", @@ -67,7 +67,7 @@ export class PokeballTray extends Phaser.GameObjects.Container { this.bg.alpha = 1; this.balls.forEach((ball, b) => { - ball.x += (globalScene.game.canvas.width / 6 + 104) * (this.player ? 1 : -1); + ball.x += (globalScene.scaledCanvas.width + 104) * (this.player ? 1 : -1); let ballFrame = "ball"; if (b >= party.length) { ballFrame = "empty"; @@ -115,7 +115,7 @@ export class PokeballTray extends Phaser.GameObjects.Container { this.balls.forEach((ball, b) => { globalScene.tweens.add({ targets: ball, - x: `${this.player ? "-" : "+"}=${globalScene.game.canvas.width / 6}`, + x: `${this.player ? "-" : "+"}=${globalScene.scaledCanvas.width}`, duration: 250, delay: b * 100, ease: "Sine.easeIn", diff --git a/src/ui/pokedex-info-overlay.ts b/src/ui/pokedex-info-overlay.ts index 0f2f5fa3dde..9c5876318ec 100644 --- a/src/ui/pokedex-info-overlay.ts +++ b/src/ui/pokedex-info-overlay.ts @@ -7,7 +7,6 @@ import { fixedInt } from "#utils/common"; export interface PokedexInfoOverlaySettings { delayVisibility?: boolean; // if true, showing the overlay will only set it to active and populate the fields and the handler using this field has to manually call setVisible later. - scale?: number; // scale the box? A scale of 0.5 is recommended //location and width of the component; unaffected by scaling x?: number; y?: number; @@ -36,17 +35,15 @@ export class PokedexInfoOverlay extends Phaser.GameObjects.Container implements private maskPointOriginX: number; private maskPointOriginY: number; - public scale: number; public width: number; constructor(options?: PokedexInfoOverlaySettings) { super(globalScene, options?.x, options?.y); - this.scale = options?.scale || 1; // set up the scale - this.setScale(this.scale); + this.setScale(1); this.options = options || {}; // prepare the description box - this.width = (options?.width || PokedexInfoOverlay.getWidth(this.scale)) / this.scale; // divide by scale as we always want this to be half a window wide + this.width = options?.width || PokedexInfoOverlay.getWidth(); // we always want this to be half a window wide this.descBg = addWindow(0, 0, this.width, DESC_HEIGHT); this.descBg.setOrigin(0, 0); this.add(this.descBg); @@ -61,19 +58,19 @@ export class PokedexInfoOverlay extends Phaser.GameObjects.Container implements this.maskPointOriginY = options?.y || 0; if (this.maskPointOriginX < 0) { - this.maskPointOriginX += globalScene.game.canvas.width / GLOBAL_SCALE; + this.maskPointOriginX += globalScene.scaledCanvas.width; } if (this.maskPointOriginY < 0) { - this.maskPointOriginY += globalScene.game.canvas.height / GLOBAL_SCALE; + this.maskPointOriginY += globalScene.scaledCanvas.height; } this.textMaskRect = globalScene.make.graphics(); this.textMaskRect.fillStyle(0xff0000); this.textMaskRect.fillRect( - this.maskPointOriginX + BORDER * this.scale, - this.maskPointOriginY + (BORDER - 2) * this.scale, - this.width - BORDER * 2 * this.scale, - (DESC_HEIGHT - (BORDER - 2) * 2) * this.scale, + this.maskPointOriginX + BORDER, + this.maskPointOriginY + (BORDER - 2), + this.width - BORDER * 2, + DESC_HEIGHT - (BORDER - 2) * 2, ); this.textMaskRect.setScale(6); const textMask = this.createGeometryMask(this.textMaskRect); @@ -111,10 +108,10 @@ export class PokedexInfoOverlay extends Phaser.GameObjects.Container implements this.textMaskRect.clear(); this.textMaskRect.fillStyle(0xff0000); this.textMaskRect.fillRect( - this.maskPointOriginX + BORDER * this.scale, - this.maskPointOriginY + (BORDER - 2) * this.scale + (48 - newHeight), - this.width - BORDER * 2 * this.scale, - (newHeight - (BORDER - 2) * 2) * this.scale, + this.maskPointOriginX + BORDER, + this.maskPointOriginY + (BORDER - 2) + (48 - newHeight), + this.width - BORDER * 2, + newHeight - (BORDER - 2) * 2, ); const updatedMask = this.createGeometryMask(this.textMaskRect); this.desc.setMask(updatedMask); @@ -167,12 +164,12 @@ export class PokedexInfoOverlay extends Phaser.GameObjects.Container implements } // width of this element - static getWidth(_scale: number): number { - return globalScene.game.canvas.width / GLOBAL_SCALE / 2; + static getWidth(): number { + return globalScene.scaledCanvas.width / 2; } // height of this element - static getHeight(scale: number, _onSide?: boolean): number { - return DESC_HEIGHT * scale; + static getHeight(): number { + return DESC_HEIGHT; } } diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 227b86c4d4d..49658d9cfc9 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -46,7 +46,7 @@ import { getVariantIcon, getVariantTint } from "#sprites/variant"; import type { StarterAttributes } from "#system/game-data"; import { SettingKeyboard } from "#system/settings-keyboard"; import type { DexEntry } from "#types/dex-data"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { BaseStatsOverlay } from "#ui/base-stats-overlay"; import { MessageUiHandler } from "#ui/message-ui-handler"; import { MoveInfoOverlay } from "#ui/move-info-overlay"; @@ -299,15 +299,15 @@ export class PokedexPageUiHandler extends MessageUiHandler { const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)) ?? "en"; const textSettings = languageSettings[langSettingKey]; - this.starterSelectContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.starterSelectContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); this.starterSelectContainer.setVisible(false); ui.add(this.starterSelectContainer); const bgColor = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0x006860, ); bgColor.setOrigin(0, 0); @@ -602,7 +602,7 @@ export class PokedexPageUiHandler extends MessageUiHandler { this.filterInstructionsContainer.setVisible(true); this.starterSelectContainer.add(this.filterInstructionsContainer); - this.starterSelectMessageBoxContainer = globalScene.add.container(0, globalScene.game.canvas.height / 6); + this.starterSelectMessageBoxContainer = globalScene.add.container(0, globalScene.scaledCanvas.height); this.starterSelectMessageBoxContainer.setVisible(false); this.starterSelectContainer.add(this.starterSelectMessageBoxContainer); @@ -629,7 +629,7 @@ export class PokedexPageUiHandler extends MessageUiHandler { this.menuContainer = globalScene.add.container(-130, 0); this.menuContainer.setName("menu"); this.menuContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); @@ -659,10 +659,10 @@ export class PokedexPageUiHandler extends MessageUiHandler { this.scale = getTextStyleOptions(TextStyle.WINDOW, globalScene.uiTheme).scale; this.menuBg = addWindow( - globalScene.game.canvas.width / 6 - 83, + globalScene.scaledCanvas.width - 83, 0, this.optionSelectText.displayWidth + 19 + 24 * this.scale, - globalScene.game.canvas.height / 6 - 2, + globalScene.scaledCanvas.height - 2, ); this.menuBg.setOrigin(0, 0); @@ -682,19 +682,16 @@ export class PokedexPageUiHandler extends MessageUiHandler { this.menuContainer.bringToTop(this.baseStatsOverlay); // add the info overlay last to be the top most ui element and prevent the IVs from overlaying this - const overlayScale = 1; this.moveInfoOverlay = new MoveInfoOverlay({ - scale: overlayScale, top: true, x: 1, - y: globalScene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29, + y: globalScene.scaledCanvas.height - MoveInfoOverlay.getHeight() - 29, }); this.starterSelectContainer.add(this.moveInfoOverlay); this.infoOverlay = new PokedexInfoOverlay({ - scale: overlayScale, x: 1, - y: globalScene.game.canvas.height / 6 - PokedexInfoOverlay.getHeight(overlayScale) - 29, + y: globalScene.scaledCanvas.height - PokedexInfoOverlay.getHeight() - 29, }); this.starterSelectContainer.add(this.infoOverlay); @@ -1103,7 +1100,7 @@ export class PokedexPageUiHandler extends MessageUiHandler { this.starterSelectMessageBoxContainer.setY(0); this.message.setY(4); } else { - this.starterSelectMessageBoxContainer.setY(globalScene.game.canvas.height / 6); + this.starterSelectMessageBoxContainer.setY(globalScene.scaledCanvas.height); this.starterSelectMessageBox.setOrigin(0, 1); this.message.setY(singleLine ? -22 : -37); } diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index ab3258a03de..4f606cbcbb0 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -1,7 +1,7 @@ import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; import { UiMode } from "#enums/ui-mode"; import type { PlayerPokemon } from "#field/pokemon"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { FilterTextRow } from "#ui/filter-text"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index cd1dc312f4d..aa2a5cda459 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -33,7 +33,7 @@ import { getVariantIcon, getVariantTint } from "#sprites/variant"; import type { DexAttrProps, StarterAttributes } from "#system/game-data"; import { SettingKeyboard } from "#system/settings-keyboard"; import type { DexEntry } from "#types/dex-data"; -import type { OptionSelectConfig } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler"; import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; import { FilterBar } from "#ui/filter-bar"; import { FilterText, FilterTextRow } from "#ui/filter-text"; @@ -245,15 +245,15 @@ export class PokedexUiHandler extends MessageUiHandler { const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)) ?? "en"; const textSettings = languageSettings[langSettingKey]; - this.starterSelectContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); + this.starterSelectContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); this.starterSelectContainer.setVisible(false); ui.add(this.starterSelectContainer); const bgColor = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + globalScene.scaledCanvas.height, 0x006860, ); bgColor.setOrigin(0, 0); @@ -544,7 +544,7 @@ export class PokedexUiHandler extends MessageUiHandler { this.type2Icon.setOrigin(0, 0); this.starterSelectContainer.add(this.type2Icon); - this.starterSelectMessageBoxContainer = globalScene.add.container(0, globalScene.game.canvas.height / 6); + this.starterSelectMessageBoxContainer = globalScene.add.container(0, globalScene.scaledCanvas.height); this.starterSelectMessageBoxContainer.setVisible(false); this.starterSelectContainer.add(this.starterSelectMessageBoxContainer); @@ -784,7 +784,7 @@ export class PokedexUiHandler extends MessageUiHandler { this.starterSelectMessageBoxContainer.setY(0); this.message.setY(4); } else { - this.starterSelectMessageBoxContainer.setY(globalScene.game.canvas.height / 6); + this.starterSelectMessageBoxContainer.setY(globalScene.scaledCanvas.height); this.starterSelectMessageBox.setOrigin(0, 1); this.message.setY(singleLine ? -22 : -37); } diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index 00aa47ae65d..457c48654a3 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -54,14 +54,14 @@ export class RunHistoryUiHandler extends MessageUiHandler { const loadSessionBg = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - -globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + -globalScene.scaledCanvas.height, 0x006860, ); loadSessionBg.setOrigin(0, 0); this.runSelectContainer.add(loadSessionBg); - this.runContainerInitialY = -globalScene.game.canvas.height / 6 + 8; + this.runContainerInitialY = -globalScene.scaledCanvas.height + 8; this.runsContainer = globalScene.add.container(8, this.runContainerInitialY); this.runSelectContainer.add(this.runsContainer); diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 465e48a45ad..2def302c1d5 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -74,7 +74,7 @@ export class RunInfoUiHandler extends UiHandler { } override async setup() { - this.runContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + this.runContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1); // The import of the modifiersModule is loaded here to sidestep async/await issues. this.modifiersModule = Modifier; this.runContainer.setVisible(false); @@ -120,7 +120,7 @@ export class RunInfoUiHandler extends UiHandler { // Creates Header and adds to this.runContainer this.addHeader(); - this.statsBgWidth = (globalScene.game.canvas.width / 6 - 2) / 3; + this.statsBgWidth = (globalScene.scaledCanvas.width - 2) / 3; // Creates Run Result Container this.runResultContainer = globalScene.add.container(0, 24); @@ -147,7 +147,7 @@ export class RunInfoUiHandler extends UiHandler { this.showParty(true); this.runContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); this.getUi().bringToTop(this.runContainer); @@ -174,7 +174,7 @@ export class RunInfoUiHandler extends UiHandler { * It does not check if the run has any PokemonHeldItemModifiers though. */ private addHeader() { - const headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24); + const headerBg = addWindow(0, 0, globalScene.scaledCanvas.width - 2, 24); headerBg.setOrigin(0, 0); this.runContainer.add(headerBg); if (this.runInfo.modifiers.length !== 0) { @@ -702,11 +702,11 @@ export class RunInfoUiHandler extends UiHandler { rules.push(i18next.t("challenges:inverseBattle.shortName")); break; default: { - const localisationKey = Challenges[this.runInfo.challenges[i].id] + const localizationKey = Challenges[this.runInfo.challenges[i].id] .split("_") .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) .join(""); - rules.push(i18next.t(`challenges:${localisationKey}.name`)); + rules.push(i18next.t(`challenges:${localizationKey}.name`)); break; } } @@ -723,7 +723,7 @@ export class RunInfoUiHandler extends UiHandler { private parsePartyInfo(): void { const party = this.runInfo.party; const currentLanguage = i18next.resolvedLanguage ?? "en"; - const windowHeight = (globalScene.game.canvas.height / 6 - 23) / 6; + const windowHeight = (globalScene.scaledCanvas.height - 23) / 6; party.forEach((p: PokemonData, i: number) => { const pokemonInfoWindow = new RoundRectangle(globalScene, 0, 14, this.statsBgWidth * 2 + 10, windowHeight - 2, 3); @@ -971,8 +971,8 @@ export class RunInfoUiHandler extends UiHandler { endCard.setOrigin(0); endCard.setScale(0.5); const text = addTextObject( - globalScene.game.canvas.width / 12, - globalScene.game.canvas.height / 6 - 16, + globalScene.scaledCanvas.width / 2, + globalScene.scaledCanvas.height - 16, i18next.t("battle:congratulations"), TextStyle.SUMMARY, { fontSize: "128px" }, diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 9da34e672f1..9c2f8488b22 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -54,14 +54,14 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler { const loadSessionBg = globalScene.add.rectangle( 0, 0, - globalScene.game.canvas.width / 6, - -globalScene.game.canvas.height / 6, + globalScene.scaledCanvas.width, + -globalScene.scaledCanvas.height, 0x006860, ); loadSessionBg.setOrigin(0, 0); this.saveSlotSelectContainer.add(loadSessionBg); - this.sessionSlotsContainerInitialY = -globalScene.game.canvas.height / 6 + 8; + this.sessionSlotsContainerInitialY = -globalScene.scaledCanvas.height + 8; this.sessionSlotsContainer = globalScene.add.container(8, this.sessionSlotsContainerInitialY); this.saveSlotSelectContainer.add(this.sessionSlotsContainer); @@ -401,7 +401,7 @@ class SessionSlot extends Phaser.GameObjects.Container { const gameModeLabel = addTextObject( 8, 5, - `${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unkown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`, + `${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`, TextStyle.WINDOW, ); this.add(gameModeLabel); diff --git a/src/ui/saving-icon-handler.ts b/src/ui/saving-icon-handler.ts index 6923017218f..00c8b8b526c 100644 --- a/src/ui/saving-icon-handler.ts +++ b/src/ui/saving-icon-handler.ts @@ -8,7 +8,7 @@ export class SavingIconHandler extends Phaser.GameObjects.Container { private shown: boolean; constructor() { - super(globalScene, globalScene.game.canvas.width / 6 - 4, globalScene.game.canvas.height / 6 - 4); + super(globalScene, globalScene.scaledCanvas.width - 4, globalScene.scaledCanvas.height - 4); } setup(): void { diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/settings/abstract-binding-ui-handler.ts index eb68456a69d..2c8d0eb63ba 100644 --- a/src/ui/settings/abstract-binding-ui-handler.ts +++ b/src/ui/settings/abstract-binding-ui-handler.ts @@ -8,7 +8,7 @@ import { UiHandler } from "#ui/ui-handler"; import { addWindow } from "#ui/ui-theme"; import i18next from "i18next"; -type CancelFn = (succes?: boolean) => boolean; +type CancelFn = (success?: boolean) => boolean; /** * Abstract class for handling UI elements related to button bindings. @@ -73,8 +73,8 @@ export abstract class AbstractBindingUiHandler extends UiHandler { // Setup backgrounds and text objects for UI. this.titleBg = addWindow( - globalScene.game.canvas.width / 6 - this.getWindowWidth(), - -(globalScene.game.canvas.height / 6) + 28 + 21, + globalScene.scaledCanvas.width - this.getWindowWidth(), + -globalScene.scaledCanvas.height + 28 + 21, this.getWindowWidth(), 24, ); @@ -82,8 +82,8 @@ export abstract class AbstractBindingUiHandler extends UiHandler { this.optionSelectContainer.add(this.titleBg); this.actionBg = addWindow( - globalScene.game.canvas.width / 6 - this.getWindowWidth(), - -(globalScene.game.canvas.height / 6) + this.getWindowHeight() + 28 + 21 + 21, + globalScene.scaledCanvas.width - this.getWindowWidth(), + -globalScene.scaledCanvas.height + this.getWindowHeight() + 28 + 21 + 21, this.getWindowWidth(), 24, ); @@ -102,8 +102,8 @@ export abstract class AbstractBindingUiHandler extends UiHandler { this.optionSelectContainer.add(this.timerText); this.optionSelectBg = addWindow( - globalScene.game.canvas.width / 6 - this.getWindowWidth(), - -(globalScene.game.canvas.height / 6) + this.getWindowHeight() + 28, + globalScene.scaledCanvas.width - this.getWindowWidth(), + -globalScene.scaledCanvas.height + this.getWindowHeight() + 28, this.getWindowWidth(), this.getWindowHeight(), ); diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index ee9e990ee2a..b40676fc97c 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -96,11 +96,11 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler { const ui = this.getUi(); this.navigationIcons = {}; - this.settingsContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + this.settingsContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1); this.settingsContainer.setName(`settings-${this.titleSelected}`); this.settingsContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height), Phaser.Geom.Rectangle.Contains, ); @@ -109,15 +109,15 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler { this.optionsBg = addWindow( 0, this.navigationContainer.height, - globalScene.game.canvas.width / 6 - 2, - globalScene.game.canvas.height / 6 - 16 - this.navigationContainer.height - 2, + globalScene.scaledCanvas.width - 2, + globalScene.scaledCanvas.height - 16 - this.navigationContainer.height - 2, ); this.optionsBg.setOrigin(0, 0); this.actionsBg = addWindow( 0, - globalScene.game.canvas.height / 6 - this.navigationContainer.height, - globalScene.game.canvas.width / 6 - 2, + globalScene.scaledCanvas.height - this.navigationContainer.height, + globalScene.scaledCanvas.width - 2, 22, ); this.actionsBg.setOrigin(0, 0); @@ -597,7 +597,7 @@ export abstract class AbstractControlSettingsUiHandler extends UiHandler { // Check if the cursor object exists, if not, create it. if (!this.cursorObj) { - const cursorWidth = globalScene.game.canvas.width / 6 - (this.scrollBar.visible ? 16 : 10); + const cursorWidth = globalScene.scaledCanvas.width - (this.scrollBar.visible ? 16 : 10); this.cursorObj = globalScene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner. this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container. diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 81d733220fc..91d5aec984a 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -56,10 +56,10 @@ export class AbstractSettingsUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - this.settingsContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + this.settingsContainer = globalScene.add.container(1, -globalScene.scaledCanvas.height + 1); this.settingsContainer.setName(`settings-${this.title}`); this.settingsContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6 - 20), + new Phaser.Geom.Rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height - 20), Phaser.Geom.Rectangle.Contains, ); @@ -70,16 +70,16 @@ export class AbstractSettingsUiHandler extends MessageUiHandler { this.optionsBg = addWindow( 0, this.navigationContainer.height, - globalScene.game.canvas.width / 6 - 2, - globalScene.game.canvas.height / 6 - 16 - this.navigationContainer.height - 2, + globalScene.scaledCanvas.width - 2, + globalScene.scaledCanvas.height - 16 - this.navigationContainer.height - 2, ); this.optionsBg.setName("window-options-bg"); this.optionsBg.setOrigin(0, 0); const actionsBg = addWindow( 0, - globalScene.game.canvas.height / 6 - this.navigationContainer.height, - globalScene.game.canvas.width / 6 - 2, + globalScene.scaledCanvas.height - this.navigationContainer.height, + globalScene.scaledCanvas.width - 2, 22, ); actionsBg.setOrigin(0, 0); @@ -375,7 +375,7 @@ export class AbstractSettingsUiHandler extends MessageUiHandler { const ret = super.setCursor(cursor); if (!this.cursorObj) { - const cursorWidth = globalScene.game.canvas.width / 6 - (this.scrollBar.visible ? 16 : 10); + const cursorWidth = globalScene.scaledCanvas.width - (this.scrollBar.visible ? 16 : 10); this.cursorObj = globalScene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.optionsContainer.add(this.cursorObj); diff --git a/src/ui/settings/navigation-menu.ts b/src/ui/settings/navigation-menu.ts index 2f3aa50f7f3..b889ce57b61 100644 --- a/src/ui/settings/navigation-menu.ts +++ b/src/ui/settings/navigation-menu.ts @@ -124,7 +124,7 @@ export class NavigationMenu extends Phaser.GameObjects.Container { */ setup() { const navigationManager = NavigationManager.getInstance(); - const headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24); + const headerBg = addWindow(0, 0, globalScene.scaledCanvas.width - 2, 24); headerBg.setOrigin(0, 0); this.add(headerBg); this.width = headerBg.width; diff --git a/src/ui/settings/option-select-ui-handler.ts b/src/ui/settings/option-select-ui-handler.ts index d7b699e6e50..c989c768244 100644 --- a/src/ui/settings/option-select-ui-handler.ts +++ b/src/ui/settings/option-select-ui-handler.ts @@ -1,5 +1,5 @@ import { UiMode } from "#enums/ui-mode"; -import { AbstractOptionSelectUiHandler } from "#ui/abstact-option-select-ui-handler"; +import { AbstractOptionSelectUiHandler } from "#ui/abstract-option-select-ui-handler"; export class OptionSelectUiHandler extends AbstractOptionSelectUiHandler { constructor(mode: UiMode = UiMode.OPTION_SELECT) { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index dac6bc677a2..15a08e9d0f4 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -16,7 +16,6 @@ import { POKERUS_STARTER_COUNT, speciesStarterCosts, } from "#balance/starters"; -import { applyChallenges, checkStarterValidForChallenge } from "#data/challenge"; import { allAbilities, allMoves, allSpecies } from "#data/data-lists"; import { Egg, getEggTierForSpecies } from "#data/egg"; import { GrowthRate, getGrowthRateColor } from "#data/exp"; @@ -48,7 +47,7 @@ import { achvs } from "#system/achv"; import type { DexAttrProps, StarterAttributes, StarterMoveset } from "#system/game-data"; import { SettingKeyboard } from "#system/settings-keyboard"; import type { DexEntry } from "#types/dex-data"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import { DropDown, DropDownLabel, DropDownOption, DropDownState, DropDownType, SortCriteria } from "#ui/dropdown"; import { FilterBar } from "#ui/filter-bar"; import { MessageUiHandler } from "#ui/message-ui-handler"; @@ -59,6 +58,7 @@ import { StarterContainer } from "#ui/starter-container"; import { StatsContainer } from "#ui/stats-container"; import { addBBCodeTextObject, addTextObject } from "#ui/text"; import { addWindow } from "#ui/ui-theme"; +import { applyChallenges, checkStarterValidForChallenge } from "#utils/challenge-utils"; import { BooleanHolder, fixedInt, @@ -335,6 +335,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { private natureLabel: Phaser.GameObjects.Text; private teraLabel: Phaser.GameObjects.Text; private goFilterLabel: Phaser.GameObjects.Text; + /** Group holding the UI elements appearing in the instructionsContainer */ + /* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group` + private instructionElemGroup: Phaser.GameObjects.Group; + */ private starterSelectMessageBox: Phaser.GameObjects.NineSlice; private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; @@ -407,70 +411,33 @@ export class StarterSelectUiHandler extends MessageUiHandler { const currentLanguage = i18next.resolvedLanguage ?? "en"; const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)) ?? "en"; const textSettings = languageSettings[langSettingKey]; + /** Scaled canvas height */ + const sHeight = globalScene.scaledCanvas.height; + /** Scaled canvas width */ + const sWidth = globalScene.scaledCanvas.width; - this.starterSelectContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); - this.starterSelectContainer.setVisible(false); + this.starterSelectContainer = globalScene.add.container(0, -sHeight).setVisible(false); ui.add(this.starterSelectContainer); - const bgColor = globalScene.add.rectangle( - 0, - 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, - 0x006860, - ); - bgColor.setOrigin(0, 0); - this.starterSelectContainer.add(bgColor); + const bgColor = globalScene.add.rectangle(0, 0, sWidth, sHeight, 0x006860).setOrigin(0); - const starterSelectBg = globalScene.add.image(0, 0, "starter_select_bg"); - starterSelectBg.setOrigin(0, 0); - this.starterSelectContainer.add(starterSelectBg); - - this.shinyOverlay = globalScene.add.image(6, 6, "summary_overlay_shiny"); - this.shinyOverlay.setOrigin(0, 0); - this.shinyOverlay.setVisible(false); - this.starterSelectContainer.add(this.shinyOverlay); + const starterSelectBg = globalScene.add.image(0, 0, "starter_select_bg").setOrigin(0); + this.shinyOverlay = globalScene.add.image(6, 6, "summary_overlay_shiny").setOrigin(0).setVisible(false); const starterContainerWindow = addWindow(speciesContainerX, filterBarHeight + 1, 175, 161); - const starterContainerBg = globalScene.add.image( - speciesContainerX + 1, - filterBarHeight + 2, - "starter_container_bg", - ); - starterContainerBg.setOrigin(0, 0); - this.starterSelectContainer.add(starterContainerBg); - - this.starterSelectContainer.add( - addWindow( - teamWindowX, - teamWindowY - randomSelectionWindowHeight, - teamWindowWidth, - randomSelectionWindowHeight, - true, - ), - ); - this.starterSelectContainer.add(addWindow(teamWindowX, teamWindowY, teamWindowWidth, teamWindowHeight)); - this.starterSelectContainer.add( - addWindow(teamWindowX, teamWindowY + teamWindowHeight, teamWindowWidth, teamWindowWidth, true), - ); - this.starterSelectContainer.add(starterContainerWindow); + const starterContainerBg = globalScene.add + .image(speciesContainerX + 1, filterBarHeight + 2, "starter_container_bg") + .setOrigin(0); // Create and initialise filter bar this.filterBarContainer = globalScene.add.container(0, 0); this.filterBar = new FilterBar(Math.min(speciesContainerX, teamWindowX), 1, 210, filterBarHeight); // gen filter - const genOptions: DropDownOption[] = [ - new DropDownOption(1, new DropDownLabel(i18next.t("starterSelectUiHandler:gen1"))), - new DropDownOption(2, new DropDownLabel(i18next.t("starterSelectUiHandler:gen2"))), - new DropDownOption(3, new DropDownLabel(i18next.t("starterSelectUiHandler:gen3"))), - new DropDownOption(4, new DropDownLabel(i18next.t("starterSelectUiHandler:gen4"))), - new DropDownOption(5, new DropDownLabel(i18next.t("starterSelectUiHandler:gen5"))), - new DropDownOption(6, new DropDownLabel(i18next.t("starterSelectUiHandler:gen6"))), - new DropDownOption(7, new DropDownLabel(i18next.t("starterSelectUiHandler:gen7"))), - new DropDownOption(8, new DropDownLabel(i18next.t("starterSelectUiHandler:gen8"))), - new DropDownOption(9, new DropDownLabel(i18next.t("starterSelectUiHandler:gen9"))), - ]; + const genOptions: DropDownOption[] = Array.from( + { length: 9 }, + (_, i) => new DropDownOption(i + 1, new DropDownLabel(i18next.t(`starterSelectUiHandler:gen${i + 1}`))), + ); const genDropDown: DropDown = new DropDown(0, 0, genOptions, this.updateStarters, DropDownType.HYBRID); this.filterBar.addFilter(DropDownColumn.GEN, i18next.t("filterBar:genFilter"), genDropDown); @@ -493,21 +460,24 @@ export class StarterSelectUiHandler extends MessageUiHandler { ); // caught filter - const shiny1Sprite = globalScene.add.sprite(0, 0, "shiny_icons"); - shiny1Sprite.setOrigin(0.15, 0.2); - shiny1Sprite.setScale(0.6); - shiny1Sprite.setFrame(getVariantIcon(0)); - shiny1Sprite.setTint(getVariantTint(0)); - const shiny2Sprite = globalScene.add.sprite(0, 0, "shiny_icons"); - shiny2Sprite.setOrigin(0.15, 0.2); - shiny2Sprite.setScale(0.6); - shiny2Sprite.setFrame(getVariantIcon(1)); - shiny2Sprite.setTint(getVariantTint(1)); - const shiny3Sprite = globalScene.add.sprite(0, 0, "shiny_icons"); - shiny3Sprite.setOrigin(0.15, 0.2); - shiny3Sprite.setScale(0.6); - shiny3Sprite.setFrame(getVariantIcon(2)); - shiny3Sprite.setTint(getVariantTint(2)); + const shiny1Sprite = globalScene.add + .sprite(0, 0, "shiny_icons") + .setOrigin(0.15, 0.2) + .setScale(0.6) + .setFrame(getVariantIcon(0)) + .setTint(getVariantTint(0)); + const shiny2Sprite = globalScene.add + .sprite(0, 0, "shiny_icons") + .setOrigin(0.15, 0.2) + .setScale(0.6) + .setFrame(getVariantIcon(1)) + .setTint(getVariantTint(1)); + const shiny3Sprite = globalScene.add + .sprite(0, 0, "shiny_icons") + .setOrigin(0.15, 0.2) + .setScale(0.6) + .setFrame(getVariantIcon(2)) + .setTint(getVariantTint(2)); const caughtOptions = [ new DropDownOption("SHINY3", new DropDownLabel("", shiny3Sprite)), @@ -608,8 +578,6 @@ export class StarterSelectUiHandler extends MessageUiHandler { ); this.filterBarContainer.add(this.filterBar); - this.starterSelectContainer.add(this.filterBarContainer); - // Offset the generation filter dropdown to avoid covering the filtered pokemon this.filterBar.offsetHybridFilters(); @@ -625,15 +593,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { tone: [0.0, 0.0, 0.0, 0.0], ignoreTimeTint: true, }); - this.starterSelectContainer.add(this.pokemonSprite); - this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY_DEX_NUM); - this.pokemonNumberText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonNumberText); + this.pokemonNumberText = addTextObject(17, 1, "0000", TextStyle.SUMMARY_DEX_NUM).setOrigin(0); - this.pokemonNameText = addTextObject(6, 112, "", TextStyle.SUMMARY); - this.pokemonNameText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonNameText); + this.pokemonNameText = addTextObject(6, 112, "", TextStyle.SUMMARY).setOrigin(0); this.pokemonGrowthRateLabelText = addTextObject( 8, @@ -641,18 +604,15 @@ export class StarterSelectUiHandler extends MessageUiHandler { i18next.t("starterSelectUiHandler:growthRate"), TextStyle.SUMMARY_ALT, { fontSize: "36px" }, + ) + .setOrigin(0) + .setVisible(false); + + this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.GROWTH_RATE_TYPE, { fontSize: "36px" }).setOrigin( + 0, ); - this.pokemonGrowthRateLabelText.setOrigin(0, 0); - this.pokemonGrowthRateLabelText.setVisible(false); - this.starterSelectContainer.add(this.pokemonGrowthRateLabelText); - this.pokemonGrowthRateText = addTextObject(34, 106, "", TextStyle.GROWTH_RATE_TYPE, { fontSize: "36px" }); - this.pokemonGrowthRateText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonGrowthRateText); - - this.pokemonGenderText = addTextObject(96, 112, "", TextStyle.SUMMARY_ALT); - this.pokemonGenderText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonGenderText); + this.pokemonGenderText = addTextObject(96, 112, "", TextStyle.SUMMARY_ALT).setOrigin(0); this.pokemonUncaughtText = addTextObject( 6, @@ -660,9 +620,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { i18next.t("starterSelectUiHandler:uncaught"), TextStyle.SUMMARY_ALT, { fontSize: "56px" }, - ); - this.pokemonUncaughtText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonUncaughtText); + ).setOrigin(0); // The position should be set per language const starterInfoXPos = textSettings?.starterInfoXPos || 31; @@ -677,19 +635,15 @@ export class StarterSelectUiHandler extends MessageUiHandler { i18next.t("starterSelectUiHandler:ability"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }, - ); - this.pokemonAbilityLabelText.setOrigin(0, 0); - this.pokemonAbilityLabelText.setVisible(false); - - this.starterSelectContainer.add(this.pokemonAbilityLabelText); + ) + .setOrigin(0) + .setVisible(false); this.pokemonAbilityText = addTextObject(starterInfoXPos, 127 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize, - }); - this.pokemonAbilityText.setOrigin(0, 0); - this.pokemonAbilityText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); - - this.starterSelectContainer.add(this.pokemonAbilityText); + }) + .setOrigin(0) + .setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); this.pokemonPassiveLabelText = addTextObject( 6, @@ -697,29 +651,27 @@ export class StarterSelectUiHandler extends MessageUiHandler { i18next.t("starterSelectUiHandler:passive"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }, - ); - this.pokemonPassiveLabelText.setOrigin(0, 0); - this.pokemonPassiveLabelText.setVisible(false); - this.starterSelectContainer.add(this.pokemonPassiveLabelText); + ) + .setOrigin(0) + .setVisible(false); this.pokemonPassiveText = addTextObject(starterInfoXPos, 136 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize, - }); - this.pokemonPassiveText.setOrigin(0, 0); - this.pokemonPassiveText.setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); - this.starterSelectContainer.add(this.pokemonPassiveText); + }) + .setOrigin(0) + .setInteractive(new Phaser.Geom.Rectangle(0, 0, 250, 55), Phaser.Geom.Rectangle.Contains); - this.pokemonPassiveDisabledIcon = globalScene.add.sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_stop"); - this.pokemonPassiveDisabledIcon.setOrigin(0, 0.5); - this.pokemonPassiveDisabledIcon.setScale(0.35); - this.pokemonPassiveDisabledIcon.setVisible(false); - this.starterSelectContainer.add(this.pokemonPassiveDisabledIcon); + this.pokemonPassiveDisabledIcon = globalScene.add + .sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_stop") + .setOrigin(0, 0.5) + .setScale(0.35) + .setVisible(false); - this.pokemonPassiveLockedIcon = globalScene.add.sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_lock"); - this.pokemonPassiveLockedIcon.setOrigin(0, 0.5); - this.pokemonPassiveLockedIcon.setScale(0.42, 0.38); - this.pokemonPassiveLockedIcon.setVisible(false); - this.starterSelectContainer.add(this.pokemonPassiveLockedIcon); + this.pokemonPassiveLockedIcon = globalScene.add + .sprite(starterInfoXPos, 137 + starterInfoYOffset, "icon_lock") + .setOrigin(0, 0.5) + .setScale(0.42, 0.38) + .setVisible(false); this.pokemonNatureLabelText = addTextObject( 6, @@ -727,16 +679,13 @@ export class StarterSelectUiHandler extends MessageUiHandler { i18next.t("starterSelectUiHandler:nature"), TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize }, - ); - this.pokemonNatureLabelText.setOrigin(0, 0); - this.pokemonNatureLabelText.setVisible(false); - this.starterSelectContainer.add(this.pokemonNatureLabelText); + ) + .setOrigin(0) + .setVisible(false); this.pokemonNatureText = addBBCodeTextObject(starterInfoXPos, 145 + starterInfoYOffset, "", TextStyle.SUMMARY_ALT, { fontSize: starterInfoTextSize, - }); - this.pokemonNatureText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonNatureText); + }).setOrigin(0); this.pokemonMoveContainers = []; this.pokemonMoveBgs = []; @@ -746,54 +695,34 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMoveBgs = []; this.pokemonEggMoveLabels = []; - this.valueLimitLabel = addTextObject(teamWindowX + 17, 150, "0/10", TextStyle.STARTER_VALUE_LIMIT); - this.valueLimitLabel.setOrigin(0.5, 0); - this.starterSelectContainer.add(this.valueLimitLabel); - - const startLabel = addTextObject(teamWindowX + 17, 162, i18next.t("common:start"), TextStyle.TOOLTIP_CONTENT); - startLabel.setOrigin(0.5, 0); - this.starterSelectContainer.add(startLabel); - - this.startCursorObj = globalScene.add.nineslice( - teamWindowX + 4, - 160, - "select_cursor", - undefined, - 26, - 15, - 6, - 6, - 6, - 6, + this.valueLimitLabel = addTextObject(teamWindowX + 17, 150, "0/10", TextStyle.STARTER_VALUE_LIMIT).setOrigin( + 0.5, + 0, ); - this.startCursorObj.setVisible(false); - this.startCursorObj.setOrigin(0, 0); - this.starterSelectContainer.add(this.startCursorObj); + + const startLabel = addTextObject( + teamWindowX + 17, + 162, + i18next.t("common:start"), + TextStyle.TOOLTIP_CONTENT, + ).setOrigin(0.5, 0); + + this.startCursorObj = globalScene.add + .nineslice(teamWindowX + 4, 160, "select_cursor", undefined, 26, 15, 6, 6, 6, 6) + .setVisible(false) + .setOrigin(0); const randomSelectLabel = addTextObject( teamWindowX + 17, 23, i18next.t("starterSelectUiHandler:randomize"), TextStyle.TOOLTIP_CONTENT, - ); - randomSelectLabel.setOrigin(0.5, 0); - this.starterSelectContainer.add(randomSelectLabel); + ).setOrigin(0.5, 0); - this.randomCursorObj = globalScene.add.nineslice( - teamWindowX + 4, - 21, - "select_cursor", - undefined, - 26, - 15, - 6, - 6, - 6, - 6, - ); - this.randomCursorObj.setVisible(false); - this.randomCursorObj.setOrigin(0, 0); - this.starterSelectContainer.add(this.randomCursorObj); + this.randomCursorObj = globalScene.add + .nineslice(teamWindowX + 4, 21, "select_cursor", undefined, 26, 15, 6, 6, 6, 6) + .setVisible(false) + .setOrigin(0); const starterSpecies: SpeciesId[] = []; @@ -807,7 +736,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { for (let i = 0; i < POKERUS_STARTER_COUNT; i++) { const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus"); cursorObj.setVisible(false); - cursorObj.setOrigin(0, 0); + cursorObj.setOrigin(0); starterBoxContainer.add(cursorObj); this.pokerusCursorObjs.push(cursorObj); } @@ -816,21 +745,21 @@ export class StarterSelectUiHandler extends MessageUiHandler { for (let i = 0; i < 6; i++) { const cursorObj = globalScene.add.image(0, 0, "select_cursor_highlight"); cursorObj.setVisible(false); - cursorObj.setOrigin(0, 0); + cursorObj.setOrigin(0); starterBoxContainer.add(cursorObj); this.starterCursorObjs.push(cursorObj); } - this.cursorObj = globalScene.add.image(0, 0, "select_cursor"); - this.cursorObj.setOrigin(0, 0); - this.starterIconsCursorObj = globalScene.add.image(289, 64, "select_gen_cursor"); - this.starterIconsCursorObj.setName("starter-icons-cursor"); - this.starterIconsCursorObj.setVisible(false); - this.starterIconsCursorObj.setOrigin(0, 0); - this.starterSelectContainer.add(this.starterIconsCursorObj); + this.cursorObj = globalScene.add.image(0, 0, "select_cursor").setOrigin(0); + this.starterIconsCursorObj = globalScene.add + .image(289, 64, "select_gen_cursor") + .setName("starter-icons-cursor") + .setVisible(false) + .setOrigin(0); starterBoxContainer.add(this.cursorObj); + // TODO: Apply the same logic done in the pokedex to only have 81 containers whose sprites are cycled for (const species of allSpecies) { if (!speciesStarterCosts.hasOwnProperty(species.speciesId) || !species.isObtainable()) { continue; @@ -846,109 +775,74 @@ export class StarterSelectUiHandler extends MessageUiHandler { starterBoxContainer.add(starterContainer); } - this.starterSelectContainer.add(starterBoxContainer); - this.starterIcons = []; for (let i = 0; i < 6; i++) { - const icon = globalScene.add.sprite(teamWindowX + 7, calcStarterIconY(i), "pokemon_icons_0"); - icon.setScale(0.5); - icon.setOrigin(0, 0); - icon.setFrame("unknown"); - this.starterSelectContainer.add(icon); + const icon = globalScene.add + .sprite(teamWindowX + 7, calcStarterIconY(i), "pokemon_icons_0") + .setScale(0.5) + .setOrigin(0) + .setFrame("unknown"); this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE); this.starterIcons.push(icon); } - this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types")); - this.type1Icon.setScale(0.5); - this.type1Icon.setOrigin(0, 0); - this.starterSelectContainer.add(this.type1Icon); + this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types")).setScale(0.5).setOrigin(0); - this.type2Icon = globalScene.add.sprite(26, 98, getLocalizedSpriteKey("types")); - this.type2Icon.setScale(0.5); - this.type2Icon.setOrigin(0, 0); - this.starterSelectContainer.add(this.type2Icon); + this.type2Icon = globalScene.add.sprite(26, 98, getLocalizedSpriteKey("types")).setScale(0.5).setOrigin(0); this.pokemonLuckLabelText = addTextObject(8, 89, i18next.t("common:luckIndicator"), TextStyle.WINDOW_ALT, { fontSize: "56px", - }); - this.pokemonLuckLabelText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonLuckLabelText); + }).setOrigin(0); this.pokemonLuckText = addTextObject( 8 + this.pokemonLuckLabelText.displayWidth + 2, 89, "0", TextStyle.LUCK_VALUE, - { - fontSize: "56px", - }, - ); - this.pokemonLuckText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonLuckText); + { fontSize: "56px" }, + ).setOrigin(0); // Candy icon and count - this.pokemonCandyContainer = globalScene.add.container(4.5, 18); + this.pokemonCandyContainer = globalScene.add + .container(4.5, 18) + .setInteractive(new Phaser.Geom.Rectangle(0, 0, 30, 20), Phaser.Geom.Rectangle.Contains); + this.pokemonCandyIcon = globalScene.add.sprite(0, 0, "candy").setScale(0.5).setOrigin(0); + this.pokemonCandyOverlayIcon = globalScene.add.sprite(0, 0, "candy_overlay").setScale(0.5).setOrigin(0); + this.pokemonCandyDarknessOverlay = globalScene.add + .sprite(0, 0, "candy") + .setScale(0.5) + .setOrigin(0) + .setTint(0x000000) + .setAlpha(0.5); - this.pokemonCandyIcon = globalScene.add.sprite(0, 0, "candy"); - this.pokemonCandyIcon.setScale(0.5); - this.pokemonCandyIcon.setOrigin(0, 0); - this.pokemonCandyContainer.add(this.pokemonCandyIcon); - - this.pokemonCandyOverlayIcon = globalScene.add.sprite(0, 0, "candy_overlay"); - this.pokemonCandyOverlayIcon.setScale(0.5); - this.pokemonCandyOverlayIcon.setOrigin(0, 0); - this.pokemonCandyContainer.add(this.pokemonCandyOverlayIcon); - - this.pokemonCandyDarknessOverlay = globalScene.add.sprite(0, 0, "candy"); - this.pokemonCandyDarknessOverlay.setScale(0.5); - this.pokemonCandyDarknessOverlay.setOrigin(0, 0); - this.pokemonCandyDarknessOverlay.setTint(0x000000); - this.pokemonCandyDarknessOverlay.setAlpha(0.5); - this.pokemonCandyContainer.add(this.pokemonCandyDarknessOverlay); - - this.pokemonCandyCountText = addTextObject(9.5, 0, "x0", TextStyle.WINDOW_ALT, { fontSize: "56px" }); - this.pokemonCandyCountText.setOrigin(0, 0); - this.pokemonCandyContainer.add(this.pokemonCandyCountText); - - this.pokemonCandyContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, 30, 20), Phaser.Geom.Rectangle.Contains); - this.starterSelectContainer.add(this.pokemonCandyContainer); + this.pokemonCandyCountText = addTextObject(9.5, 0, "x0", TextStyle.WINDOW_ALT, { fontSize: "56px" }).setOrigin(0); + this.pokemonCandyContainer.add([ + this.pokemonCandyIcon, + this.pokemonCandyOverlayIcon, + this.pokemonCandyDarknessOverlay, + this.pokemonCandyCountText, + ]); this.pokemonFormText = addTextObject(6, 42, "Form", TextStyle.WINDOW_ALT, { fontSize: "42px", - }); - this.pokemonFormText.setOrigin(0, 0); - this.starterSelectContainer.add(this.pokemonFormText); + }).setOrigin(0); - this.pokemonCaughtHatchedContainer = globalScene.add.container(2, 25); - this.pokemonCaughtHatchedContainer.setScale(0.5); - this.starterSelectContainer.add(this.pokemonCaughtHatchedContainer); + this.pokemonCaughtHatchedContainer = globalScene.add.container(2, 25).setScale(0.5); - const pokemonCaughtIcon = globalScene.add.sprite(1, 0, "items", "pb"); - pokemonCaughtIcon.setOrigin(0, 0); - pokemonCaughtIcon.setScale(0.75); - this.pokemonCaughtHatchedContainer.add(pokemonCaughtIcon); + const pokemonCaughtIcon = globalScene.add.sprite(1, 0, "items", "pb").setOrigin(0).setScale(0.75); - this.pokemonCaughtCountText = addTextObject(24, 4, "0", TextStyle.SUMMARY_ALT); - this.pokemonCaughtCountText.setOrigin(0, 0); - this.pokemonCaughtHatchedContainer.add(this.pokemonCaughtCountText); - - this.pokemonHatchedIcon = globalScene.add.sprite(1, 14, "egg_icons"); - this.pokemonHatchedIcon.setOrigin(0.15, 0.2); - this.pokemonHatchedIcon.setScale(0.8); - this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedIcon); - - this.pokemonShinyIcon = globalScene.add.sprite(14, 76, "shiny_icons"); - this.pokemonShinyIcon.setOrigin(0.15, 0.2); - this.pokemonShinyIcon.setScale(1); - this.pokemonCaughtHatchedContainer.add(this.pokemonShinyIcon); - - this.pokemonHatchedCountText = addTextObject(24, 19, "0", TextStyle.SUMMARY_ALT); - this.pokemonHatchedCountText.setOrigin(0, 0); - this.pokemonCaughtHatchedContainer.add(this.pokemonHatchedCountText); - - this.pokemonMovesContainer = globalScene.add.container(102, 16); - this.pokemonMovesContainer.setScale(0.375); + this.pokemonCaughtCountText = addTextObject(24, 4, "0", TextStyle.SUMMARY_ALT).setOrigin(0); + this.pokemonHatchedIcon = globalScene.add.sprite(1, 14, "egg_icons").setOrigin(0.15, 0.2).setScale(0.8); + this.pokemonShinyIcon = globalScene.add.sprite(14, 76, "shiny_icons").setOrigin(0.15, 0.2).setScale(1); + this.pokemonHatchedCountText = addTextObject(24, 19, "0", TextStyle.SUMMARY_ALT).setOrigin(0); + this.pokemonMovesContainer = globalScene.add.container(102, 16).setScale(0.375); + this.pokemonCaughtHatchedContainer.add([ + pokemonCaughtIcon, + this.pokemonCaughtCountText, + this.pokemonHatchedIcon, + this.pokemonShinyIcon, + this.pokemonHatchedCountText, + ]); for (let m = 0; m < 4; m++) { const moveContainer = globalScene.add.container(0, 14 * m); @@ -962,8 +856,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.pokemonMoveBgs.push(moveBg); this.pokemonMoveLabels.push(moveLabel); - moveContainer.add(moveBg); - moveContainer.add(moveLabel); + moveContainer.add([moveBg, moveLabel]); this.pokemonMoveContainers.push(moveContainer); this.pokemonMovesContainer.add(moveContainer); @@ -974,18 +867,18 @@ export class StarterSelectUiHandler extends MessageUiHandler { 56, "(+0)", TextStyle.MOVE_LABEL, - ); - this.pokemonAdditionalMoveCountLabel.setOrigin(0.5, 0); + ).setOrigin(0.5, 0); this.pokemonMovesContainer.add(this.pokemonAdditionalMoveCountLabel); - this.starterSelectContainer.add(this.pokemonMovesContainer); + this.pokemonEggMovesContainer = globalScene.add.container(102, 85).setScale(0.375); - this.pokemonEggMovesContainer = globalScene.add.container(102, 85); - this.pokemonEggMovesContainer.setScale(0.375); - - const eggMovesLabel = addTextObject(-46, 0, i18next.t("starterSelectUiHandler:eggMoves"), TextStyle.WINDOW_ALT); - eggMovesLabel.setOrigin(0.5, 0); + const eggMovesLabel = addTextObject( + -46, + 0, + i18next.t("starterSelectUiHandler:eggMoves"), + TextStyle.WINDOW_ALT, + ).setOrigin(0.5, 0); this.pokemonEggMovesContainer.add(eggMovesLabel); @@ -1001,143 +894,103 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMoveBgs.push(eggMoveBg); this.pokemonEggMoveLabels.push(eggMoveLabel); - eggMoveContainer.add(eggMoveBg); - eggMoveContainer.add(eggMoveLabel); + eggMoveContainer.add([eggMoveBg, eggMoveLabel]); this.pokemonEggMoveContainers.push(eggMoveContainer); this.pokemonEggMovesContainer.add(eggMoveContainer); } - this.starterSelectContainer.add(this.pokemonEggMovesContainer); - - this.teraIcon = globalScene.add.sprite(85, 63, "button_tera"); - this.teraIcon.setName("terrastallize-icon"); - this.teraIcon.setFrame("fire"); - this.starterSelectContainer.add(this.teraIcon); + this.teraIcon = globalScene.add.sprite(85, 63, "button_tera").setName("terastallize-icon").setFrame("fire"); // The font size should be set per language const instructionTextSize = textSettings.instructionTextSize; - this.instructionsContainer = globalScene.add.container(4, 156); - this.instructionsContainer.setVisible(true); - this.starterSelectContainer.add(this.instructionsContainer); + this.instructionsContainer = globalScene.add.container(4, 156).setVisible(true); + + const iRowX = this.instructionRowX; + const iRowY = this.instructionRowY; + const iRowTextX = iRowX + this.instructionRowTextOffset; // instruction rows that will be pushed into the container dynamically based on need // creating new sprites since they will be added to the scene later - this.shinyIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "R.png", - ); - this.shinyIconElement.setName("sprite-shiny-icon-element"); - this.shinyIconElement.setScale(0.675); - this.shinyIconElement.setOrigin(0.0, 0.0); + this.shinyIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "R.png") + .setName("sprite-shiny-icon-element") + .setScale(0.675) + .setOrigin(0); this.shinyLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleShiny"), TextStyle.INSTRUCTIONS_TEXT, - { fontSize: instructionTextSize }, - ); - this.shinyLabel.setName("text-shiny-label"); + { + fontSize: instructionTextSize, + }, + ).setName("text-shiny-label"); - this.formIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "F.png", - ); - this.formIconElement.setName("sprite-form-icon-element"); - this.formIconElement.setScale(0.675); - this.formIconElement.setOrigin(0.0, 0.0); + this.formIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "F.png") + .setName("sprite-form-icon-element") + .setScale(0.675) + .setOrigin(0); this.formLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleForm"), TextStyle.INSTRUCTIONS_TEXT, - { fontSize: instructionTextSize }, - ); - this.formLabel.setName("text-form-label"); + { + fontSize: instructionTextSize, + }, + ).setName("text-form-label"); - this.genderIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "G.png", - ); - this.genderIconElement.setName("sprite-gender-icon-element"); - this.genderIconElement.setScale(0.675); - this.genderIconElement.setOrigin(0.0, 0.0); + this.genderIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "G.png") + .setName("sprite-gender-icon-element") + .setScale(0.675) + .setOrigin(0); this.genderLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleGender"), TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, - ); - this.genderLabel.setName("text-gender-label"); + ).setName("text-gender-label"); - this.abilityIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "E.png", - ); - this.abilityIconElement.setName("sprite-ability-icon-element"); - this.abilityIconElement.setScale(0.675); - this.abilityIconElement.setOrigin(0.0, 0.0); + this.abilityIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "E.png") + .setName("sprite-ability-icon-element") + .setScale(0.675) + .setOrigin(0); this.abilityLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleAbility"), TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, - ); - this.abilityLabel.setName("text-ability-label"); + ).setName("text-ability-label"); - this.natureIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "N.png", - ); - this.natureIconElement.setName("sprite-nature-icon-element"); - this.natureIconElement.setScale(0.675); - this.natureIconElement.setOrigin(0.0, 0.0); + this.natureIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "N.png") + .setName("sprite-nature-icon-element") + .setScale(0.675) + .setOrigin(0); this.natureLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleNature"), TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, - ); - this.natureLabel.setName("text-nature-label"); + ).setName("text-nature-label"); - this.teraIconElement = new Phaser.GameObjects.Sprite( - globalScene, - this.instructionRowX, - this.instructionRowY, - "keyboard", - "V.png", - ); - this.teraIconElement.setName("sprite-tera-icon-element"); - this.teraIconElement.setScale(0.675); - this.teraIconElement.setOrigin(0.0, 0.0); + this.teraIconElement = new Phaser.GameObjects.Sprite(globalScene, iRowX, iRowY, "keyboard", "V.png") + .setName("sprite-tera-icon-element") + .setScale(0.675) + .setOrigin(0); this.teraLabel = addTextObject( - this.instructionRowX + this.instructionRowTextOffset, - this.instructionRowY, + iRowTextX, + iRowY, i18next.t("starterSelectUiHandler:cycleTera"), TextStyle.INSTRUCTIONS_TEXT, - { fontSize: instructionTextSize }, - ); - this.teraLabel.setName("text-tera-label"); + { + fontSize: instructionTextSize, + }, + ).setName("text-tera-label"); this.goFilterIconElement = new Phaser.GameObjects.Sprite( globalScene, @@ -1145,60 +998,122 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.filterInstructionRowY, "keyboard", "C.png", - ); - this.goFilterIconElement.setName("sprite-goFilter-icon-element"); - this.goFilterIconElement.setScale(0.675); - this.goFilterIconElement.setOrigin(0.0, 0.0); + ) + .setName("sprite-goFilter-icon-element") + .setScale(0.675) + .setOrigin(0); this.goFilterLabel = addTextObject( this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY, i18next.t("starterSelectUiHandler:goFilter"), TextStyle.INSTRUCTIONS_TEXT, { fontSize: instructionTextSize }, - ); - this.goFilterLabel.setName("text-goFilter-label"); + ).setName("text-goFilter-label"); + + /** TODO: Uncomment this and update `this.hideInstructions` once our testing infra supports mocks of `Phaser.GameObject.Group` */ + /* + this.instructionElemGroup = globalScene.add.group([ + this.shinyIconElement, + this.shinyLabel, + this.formIconElement, + this.formLabel, + this.genderIconElement, + this.genderLabel, + this.abilityIconElement, + this.abilityLabel, + this.natureIconElement, + this.natureLabel, + this.teraIconElement, + this.teraLabel, + this.goFilterIconElement, + this.goFilterLabel, + ]); + */ this.hideInstructions(); - this.filterInstructionsContainer = globalScene.add.container(50, 5); - this.filterInstructionsContainer.setVisible(true); - this.starterSelectContainer.add(this.filterInstructionsContainer); + this.filterInstructionsContainer = globalScene.add.container(50, 5).setVisible(true); - this.starterSelectMessageBoxContainer = globalScene.add.container(0, globalScene.game.canvas.height / 6); - this.starterSelectMessageBoxContainer.setVisible(false); - this.starterSelectContainer.add(this.starterSelectMessageBoxContainer); + this.starterSelectMessageBoxContainer = globalScene.add.container(0, sHeight).setVisible(false); - this.starterSelectMessageBox = addWindow(1, -1, 318, 28); - this.starterSelectMessageBox.setOrigin(0, 1); + this.starterSelectMessageBox = addWindow(1, -1, 318, 28).setOrigin(0, 1); this.starterSelectMessageBoxContainer.add(this.starterSelectMessageBox); - this.message = addTextObject(8, 8, "", TextStyle.WINDOW, { maxLines: 2 }); - this.message.setOrigin(0, 0); + this.message = addTextObject(8, 8, "", TextStyle.WINDOW, { maxLines: 2 }).setOrigin(0); this.starterSelectMessageBoxContainer.add(this.message); // arrow icon for the message box this.initPromptSprite(this.starterSelectMessageBoxContainer); - this.statsContainer = new StatsContainer(6, 16); + this.statsContainer = new StatsContainer(6, 16).setVisible(false); globalScene.add.existing(this.statsContainer); - this.statsContainer.setVisible(false); - - this.starterSelectContainer.add(this.statsContainer); - // add the info overlay last to be the top most ui element and prevent the IVs from overlaying this - const overlayScale = 1; this.moveInfoOverlay = new MoveInfoOverlay({ - scale: overlayScale, top: true, x: 1, - y: globalScene.game.canvas.height / 6 - MoveInfoOverlay.getHeight(overlayScale) - 29, + y: globalScene.scaledCanvas.height / 6 - MoveInfoOverlay.getHeight() - 29, }); - this.starterSelectContainer.add(this.moveInfoOverlay); - // Filter bar sits above everything, except the tutorial overlay and message box - this.starterSelectContainer.bringToTop(this.filterBarContainer); + this.starterSelectContainer.add([ + bgColor, + starterSelectBg, + this.shinyOverlay, + starterContainerBg, + addWindow( + teamWindowX, + teamWindowY - randomSelectionWindowHeight, + teamWindowWidth, + randomSelectionWindowHeight, + true, + ), + addWindow(teamWindowX, teamWindowY, teamWindowWidth, teamWindowHeight), + addWindow(teamWindowX, teamWindowY + teamWindowHeight, teamWindowWidth, teamWindowWidth, true), + starterContainerWindow, + this.pokemonSprite, + this.pokemonNumberText, + this.pokemonNameText, + this.pokemonGrowthRateLabelText, + this.pokemonGrowthRateText, + this.pokemonGenderText, + this.pokemonUncaughtText, + this.pokemonAbilityLabelText, + this.pokemonAbilityText, + this.pokemonPassiveLabelText, + this.pokemonPassiveText, + this.pokemonPassiveDisabledIcon, + this.pokemonPassiveLockedIcon, + this.pokemonNatureLabelText, + this.pokemonNatureText, + this.valueLimitLabel, + startLabel, + this.startCursorObj, + randomSelectLabel, + this.randomCursorObj, + this.starterIconsCursorObj, + starterBoxContainer, + ...this.starterIcons, + this.type1Icon, + this.type2Icon, + this.pokemonLuckLabelText, + this.pokemonLuckText, + this.pokemonCandyContainer, + this.pokemonFormText, + this.pokemonCaughtHatchedContainer, + this.pokemonMovesContainer, + this.pokemonEggMovesContainer, + this.teraIcon, + this.instructionsContainer, + this.filterInstructionsContainer, + this.starterSelectMessageBoxContainer, + this.statsContainer, + this.moveInfoOverlay, + // Filter bar sits above everything, except the tutorial overlay and message box. + // Do not put anything below this unless it must appear below the filter bar. + this.filterBarContainer, + ]); + this.initTutorialOverlay(this.starterSelectContainer); this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer); @@ -1390,11 +1305,11 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.starterSelectMessageBox.setSize(318, singleLine ? 28 : 42); if (moveToTop) { - this.starterSelectMessageBox.setOrigin(0, 0); + this.starterSelectMessageBox.setOrigin(0); this.starterSelectMessageBoxContainer.setY(0); this.message.setY(4); } else { - this.starterSelectMessageBoxContainer.setY(globalScene.game.canvas.height / 6); + this.starterSelectMessageBoxContainer.setY(globalScene.scaledCanvas.height); this.starterSelectMessageBox.setOrigin(0, 1); this.message.setY(singleLine ? -22 : -37); } @@ -1924,9 +1839,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { true, ); if (!isDupe && isValidForChallenge && isOverValueLimit) { - const cursorObj = this.starterCursorObjs[this.starterSpecies.length]; - cursorObj.setVisible(true); - cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); + this.starterCursorObjs[this.starterSpecies.length] + .setVisible(true) + .setPosition(this.cursorObj.x, this.cursorObj.y); this.addToParty( this.lastSpecies, this.dexAttrCursor, @@ -2409,9 +2324,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { globalScene.playSound("se/sparkle"); // Cycle tint based on current sprite tint const tint = getVariantTint(newVariant); - this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)); - this.pokemonShinyIcon.setTint(tint); - this.pokemonShinyIcon.setVisible(true); + this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)).setTint(tint).setVisible(true); starterAttributes.shiny = true; } else { @@ -2453,8 +2366,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { }); // Cycle tint based on current sprite tint const tint = getVariantTint(newVariant as Variant); - this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); - this.pokemonShinyIcon.setTint(tint); + this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)).setTint(tint); success = true; } } @@ -2775,56 +2687,64 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.checkIconId(this.starterIcons[index], species, props.female, props.formIndex, props.shiny, props.variant); } - switchMoveHandler(i: number, newMove: MoveId, move: MoveId) { - const speciesId = this.lastSpecies.speciesId; - const existingMoveIndex = this.starterMoveset?.indexOf(newMove)!; // TODO: is this bang correct? - this.starterMoveset![i] = newMove; // TODO: is this bang correct? - if (existingMoveIndex > -1) { - this.starterMoveset![existingMoveIndex] = move; // TODO: is this bang correct? + /** + * Puts a move at the requested index in the current highlighted Pokemon's moveset. + * If the move was already present in the moveset, swap its position with the one at the requested index. + * + * @remarks + * ⚠️ {@linkcode starterMoveset | this.starterMoveset} **must not be null when this method is called** + * @param targetIndex - The index to place the move + * @param newMove - The move to place in the moveset + * @param previousMove - The move that was previously in the spot + */ + switchMoveHandler(targetIndex: number, newMove: MoveId, previousMove: MoveId) { + const starterMoveset = this.starterMoveset; + if (isNullOrUndefined(starterMoveset)) { + console.warn("Trying to update a non-existing moveset"); + return; } - const props: DexAttrProps = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor); + + const speciesId = this.lastSpecies.speciesId; + const existingMoveIndex = starterMoveset.indexOf(newMove); + starterMoveset[targetIndex] = newMove; + if (existingMoveIndex !== -1) { + starterMoveset[existingMoveIndex] = previousMove; + } + const updatedMoveset = starterMoveset.slice() as StarterMoveset; + const formIndex = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor).formIndex; + const starterData = globalScene.gameData.starterData[speciesId]; // species has different forms if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) { - // starterMoveData doesn't have base form moves or is using the single form format - if ( - !globalScene.gameData.starterData[speciesId].moveset || - Array.isArray(globalScene.gameData.starterData[speciesId].moveset) - ) { - globalScene.gameData.starterData[speciesId].moveset = { - [props.formIndex]: this.starterMoveset?.slice(0) as StarterMoveset, - }; - } - const starterMoveData = globalScene.gameData.starterData[speciesId].moveset; - - // starterMoveData doesn't have active form moves - if (!starterMoveData.hasOwnProperty(props.formIndex)) { - globalScene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice( - 0, - ) as StarterMoveset; - } - - // does the species' starter move data have its form's starter moves and has it been updated - if (starterMoveData.hasOwnProperty(props.formIndex)) { - // active form move hasn't been updated - if (starterMoveData[props.formIndex][existingMoveIndex] !== newMove) { - globalScene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice( - 0, - ) as StarterMoveset; - } + // Species has forms with different movesets + if (!starterData.moveset || Array.isArray(starterData.moveset)) { + starterData.moveset = {}; } + starterData.moveset[formIndex] = updatedMoveset; } else { - globalScene.gameData.starterData[speciesId].moveset = this.starterMoveset?.slice(0) as StarterMoveset; + starterData.moveset = updatedMoveset; } this.setSpeciesDetails(this.lastSpecies, { forSeen: false }); - // switch moves of starter if exists - if (this.starterMovesets.length) { - Array.from({ length: this.starterSpecies.length }, (_, i) => { - const starterSpecies = this.starterSpecies[i]; - if (starterSpecies.speciesId === speciesId) { - this.starterMovesets[i] = this.starterMoveset!; // TODO: is this bang correct? - } - }); + this.updateSelectedStarterMoveset(speciesId); + } + + /** + * Update the starter moveset for the given species if it is part of the selected starters. + * + * @remarks + * It is safe to call with a species that is not part of the selected starters. + * + * @param id - The species ID to update the moveset for + */ + private updateSelectedStarterMoveset(id: SpeciesId): void { + if (this.starterMoveset === null) { + return; + } + + for (const [index, species] of this.starterSpecies.entries()) { + if (species.speciesId === id) { + this.starterMovesets[index] = this.starterMoveset; + } } } @@ -2867,12 +2787,14 @@ export class StarterSelectUiHandler extends MessageUiHandler { } else { iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting); } - // @ts-expect-error: TODO can iconPath actually be undefined? - iconElement.setTexture(gamepadType, iconPath); - iconElement.setPosition(this.instructionRowX, this.instructionRowY); - controlLabel.setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY); - iconElement.setVisible(true); - controlLabel.setVisible(true); + // The bang for iconPath is correct as long the cases in the above switch statement handle all `SettingKeyboard` values enabled in touch mode + iconElement + .setTexture(gamepadType, iconPath!) + .setPosition(this.instructionRowX, this.instructionRowY) + .setVisible(true); + controlLabel + .setPosition(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY) + .setVisible(true); this.instructionsContainer.add([iconElement, controlLabel]); this.instructionRowY += 8; if (this.instructionRowY >= 24) { @@ -2896,11 +2818,13 @@ export class StarterSelectUiHandler extends MessageUiHandler { } else { iconPath = globalScene.inputController?.getIconForLatestInputRecorded(iconSetting); } - iconElement.setTexture(gamepadType, iconPath); - iconElement.setPosition(this.filterInstructionRowX, this.filterInstructionRowY); - controlLabel.setPosition(this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY); - iconElement.setVisible(true); - controlLabel.setVisible(true); + iconElement + .setTexture(gamepadType, iconPath) + .setPosition(this.filterInstructionRowX, this.filterInstructionRowY) + .setVisible(true); + controlLabel + .setPosition(this.filterInstructionRowX + this.instructionRowTextOffset, this.filterInstructionRowY) + .setVisible(true); this.filterInstructionsContainer.add([iconElement, controlLabel]); this.filterInstructionRowY += 8; if (this.filterInstructionRowY >= 24) { @@ -2998,8 +2922,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.filteredStarterContainers = []; this.validStarterContainers = []; + // biome-ignore-start lint/nursery/useIterableCallbackReturn: benign this.pokerusCursorObjs.forEach(cursor => cursor.setVisible(false)); this.starterCursorObjs.forEach(cursor => cursor.setVisible(false)); + // biome-ignore-end lint/nursery/useIterableCallbackReturn: benign this.filterBar.updateFilterLabels(); @@ -3022,7 +2948,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { globalScene.gameData.getSpeciesDexAttrProps(species, tempFormProps), true, ); - allFormsValid = allFormsValid || isValidForChallenge; + allFormsValid ||= isValidForChallenge; } } else { const isValidForChallenge = checkStarterValidForChallenge( @@ -3286,6 +3212,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { override destroy(): void { // Without this the reference gets hung up and no startercontainers get GCd this.starterContainers = []; + /* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group` + this.instructionElemGroup.destroy(true); + */ } updateScroll = () => { @@ -3307,28 +3236,28 @@ export class StarterSelectUiHandler extends MessageUiHandler { container.setVisible(false); if (this.pokerusSpecies.includes(container.species)) { - this.pokerusCursorObjs[pokerusCursorIndex].setPosition(pos.x - 1, pos.y + 1); - this.pokerusCursorObjs[pokerusCursorIndex].setVisible(false); + this.pokerusCursorObjs[pokerusCursorIndex].setPosition(pos.x - 1, pos.y + 1).setVisible(false); pokerusCursorIndex++; } if (this.starterSpecies.includes(container.species)) { - this.starterCursorObjs[this.starterSpecies.indexOf(container.species)].setPosition(pos.x - 1, pos.y + 1); - this.starterCursorObjs[this.starterSpecies.indexOf(container.species)].setVisible(false); + this.starterCursorObjs[this.starterSpecies.indexOf(container.species)] + .setPosition(pos.x - 1, pos.y + 1) + .setVisible(false); } return; } container.setVisible(true); if (this.pokerusSpecies.includes(container.species)) { - this.pokerusCursorObjs[pokerusCursorIndex].setPosition(pos.x - 1, pos.y + 1); - this.pokerusCursorObjs[pokerusCursorIndex].setVisible(true); + this.pokerusCursorObjs[pokerusCursorIndex].setPosition(pos.x - 1, pos.y + 1).setVisible(true); pokerusCursorIndex++; } if (this.starterSpecies.includes(container.species)) { - this.starterCursorObjs[this.starterSpecies.indexOf(container.species)].setPosition(pos.x - 1, pos.y + 1); - this.starterCursorObjs[this.starterSpecies.indexOf(container.species)].setVisible(true); + this.starterCursorObjs[this.starterSpecies.indexOf(container.species)] + .setPosition(pos.x - 1, pos.y + 1) + .setVisible(true); } const speciesId = container.species.speciesId; @@ -3404,8 +3333,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { ? (this.starterPreferences[species.speciesId].variant as Variant) : defaultProps.variant; const tint = getVariantTint(variant); - this.pokemonShinyIcon.setFrame(getVariantIcon(variant)); - this.pokemonShinyIcon.setTint(tint); + this.pokemonShinyIcon.setFrame(getVariantIcon(variant)).setTint(tint); this.setSpecies(species); this.updateInstructions(); } @@ -3433,8 +3361,11 @@ export class StarterSelectUiHandler extends MessageUiHandler { } moveStarterIconsCursor(index: number): void { - this.starterIconsCursorObj.x = this.starterIcons[index].x + this.starterIconsCursorXOffset; - this.starterIconsCursorObj.y = this.starterIcons[index].y + this.starterIconsCursorYOffset; + this.starterIconsCursorObj.setPositionRelative( + this.starterIcons[index], + this.starterIconsCursorXOffset, + this.starterIconsCursorYOffset, + ); if (this.starterSpecies.length > 0) { this.starterIconsCursorObj.setVisible(true); this.setSpecies(this.starterSpecies[index]); @@ -3525,9 +3456,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { const colorScheme = starterColors[species.speciesId]; const luck = globalScene.gameData.getDexAttrLuck(this.speciesStarterDexEntry.caughtAttr); - this.pokemonLuckText.setVisible(!!luck); - this.pokemonLuckText.setText(luck.toString()); - this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant)); + this.pokemonLuckText + .setVisible(!!luck) + .setText(luck.toString()) + .setTint(getVariantTint(Math.min(luck - 1, 2) as Variant)); this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible); //Growth translate @@ -3536,10 +3468,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { if (i18next.exists("growth:" + growthAux)) { growthReadable = i18next.t(("growth:" + growthAux) as any); } - this.pokemonGrowthRateText.setText(growthReadable); - - this.pokemonGrowthRateText.setColor(getGrowthRateColor(species.growthRate)); - this.pokemonGrowthRateText.setShadowColor(getGrowthRateColor(species.growthRate, true)); + this.pokemonGrowthRateText + .setText(growthReadable) + .setColor(getGrowthRateColor(species.growthRate)) + .setShadowColor(getGrowthRateColor(species.growthRate, true)); this.pokemonGrowthRateLabelText.setVisible(true); this.pokemonUncaughtText.setVisible(false); this.pokemonAbilityLabelText.setVisible(true); @@ -3557,16 +3489,13 @@ export class StarterSelectUiHandler extends MessageUiHandler { const defaultProps = globalScene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); const variant = defaultProps.variant; const tint = getVariantTint(variant); - this.pokemonShinyIcon.setFrame(getVariantIcon(variant)); - this.pokemonShinyIcon.setTint(tint); - this.pokemonShinyIcon.setVisible(defaultProps.shiny); + this.pokemonShinyIcon.setFrame(getVariantIcon(variant)).setTint(tint).setVisible(defaultProps.shiny); this.pokemonCaughtHatchedContainer.setVisible(true); this.pokemonFormText.setVisible(true); if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) { this.pokemonCaughtHatchedContainer.setY(16); - this.pokemonShinyIcon.setY(135); - this.pokemonShinyIcon.setFrame(getVariantIcon(variant)); + this.pokemonShinyIcon.setY(135).setFrame(getVariantIcon(variant)); [this.pokemonCandyContainer, this.pokemonHatchedIcon, this.pokemonHatchedCountText].map(c => c.setVisible(false), ); @@ -3577,7 +3506,6 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); this.pokemonCandyCountText.setText(`×${globalScene.gameData.starterData[species.speciesId].candyCount}`); - this.pokemonCandyContainer.setVisible(true); this.pokemonFormText.setY(42); this.pokemonHatchedIcon.setVisible(true); this.pokemonHatchedCountText.setVisible(true); @@ -3586,14 +3514,16 @@ export class StarterSelectUiHandler extends MessageUiHandler { const candyCropY = 16 - 16 * (currentFriendship / friendshipCap); this.pokemonCandyDarknessOverlay.setCrop(0, 0, 16, candyCropY); - this.pokemonCandyContainer.on("pointerover", () => { - globalScene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true); - this.activeTooltip = "CANDY"; - }); - this.pokemonCandyContainer.on("pointerout", () => { - globalScene.ui.hideTooltip(); - this.activeTooltip = undefined; - }); + this.pokemonCandyContainer + .setVisible(true) + .on("pointerover", () => { + globalScene.ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true); + this.activeTooltip = "CANDY"; + }) + .on("pointerout", () => { + globalScene.ui.hideTooltip(); + this.activeTooltip = undefined; + }); } // Pause the animation when the species is selected @@ -3650,6 +3580,11 @@ export class StarterSelectUiHandler extends MessageUiHandler { }); } + if (!isNullOrUndefined(props.formIndex)) { + // If switching forms while the pokemon is in the team, update its moveset + this.updateSelectedStarterMoveset(species.speciesId); + } + const speciesForm = getPokemonSpeciesForm(species.speciesId, props.formIndex); this.setTypeIcons(speciesForm.type1, speciesForm.type2); @@ -3850,19 +3785,21 @@ export class StarterSelectUiHandler extends MessageUiHandler { const assetLoadCancelled = new BooleanHolder(false); this.assetLoadCancelled = assetLoadCancelled; + female ??= false; if (shouldUpdateSprite) { - species.loadAssets(female!, formIndex, shiny, variant, true).then(() => { - // TODO: is this bang correct? + species.loadAssets(female, formIndex, shiny, variant, true).then(() => { if (assetLoadCancelled.value) { return; } this.assetLoadCancelled = null; this.speciesLoaded.set(species.speciesId, true); - this.pokemonSprite.play(species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? - this.pokemonSprite.setPipelineData("shiny", shiny); - this.pokemonSprite.setPipelineData("variant", variant); - this.pokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? - this.pokemonSprite.setVisible(!this.statsMode); + // Note: Bangs are correct due to `female ??= false` above + this.pokemonSprite + .play(species.getSpriteKey(female!, formIndex, shiny, variant)) + .setPipelineData("shiny", shiny) + .setPipelineData("variant", variant) + .setPipelineData("spriteKey", species.getSpriteKey(female!, formIndex, shiny, variant)) + .setVisible(!this.statsMode); }); } else { this.pokemonSprite.setVisible(!this.statsMode); @@ -3875,7 +3812,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { const starterSprite = currentFilteredContainer.icon as Phaser.GameObjects.Sprite; starterSprite.setTexture( species.getIconAtlasKey(formIndex, shiny, variant), - species.getIconId(female!, formIndex, shiny, variant), + species.getIconId(female, formIndex, shiny, variant), ); currentFilteredContainer.checkIconId(female, formIndex, shiny, variant); } @@ -3921,9 +3858,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { if (dexEntry.caughtAttr && species.malePercent !== null) { const gender = !female ? Gender.MALE : Gender.FEMALE; - this.pokemonGenderText.setText(getGenderSymbol(gender)); - this.pokemonGenderText.setColor(getGenderColor(gender)); - this.pokemonGenderText.setShadowColor(getGenderColor(gender, true)); + this.pokemonGenderText + .setText(getGenderSymbol(gender)) + .setColor(getGenderColor(gender)) + .setShadowColor(getGenderColor(gender, true)); } else { this.pokemonGenderText.setText(""); } @@ -3935,13 +3873,12 @@ export class StarterSelectUiHandler extends MessageUiHandler { } else { ability = allAbilities[this.lastSpecies.getAbility(abilityIndex!)]; // TODO: is this bang correct? } - this.pokemonAbilityText.setText(ability.name); const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1); - this.pokemonAbilityText.setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD)); - this.pokemonAbilityText.setShadowColor( - this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true), - ); + this.pokemonAbilityText + .setText(ability.name) + .setColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD)) + .setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true)); const passiveAttr = globalScene.gameData.starterData[species.speciesId].passiveAttr; const passiveAbility = allAbilities[this.lastSpecies.getPassiveAbility(formIndex)]; @@ -3968,14 +3905,16 @@ export class StarterSelectUiHandler extends MessageUiHandler { const textStyle = isUnlocked && isEnabled ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GRAY; const textAlpha = isUnlocked && isEnabled ? 1 : 0.5; - this.pokemonPassiveLabelText.setVisible(true); - this.pokemonPassiveLabelText.setColor(this.getTextColor(TextStyle.SUMMARY_ALT)); - this.pokemonPassiveLabelText.setShadowColor(this.getTextColor(TextStyle.SUMMARY_ALT, true)); - this.pokemonPassiveText.setVisible(true); - this.pokemonPassiveText.setText(passiveAbility.name); - this.pokemonPassiveText.setColor(this.getTextColor(textStyle)); - this.pokemonPassiveText.setAlpha(textAlpha); - this.pokemonPassiveText.setShadowColor(this.getTextColor(textStyle, true)); + this.pokemonPassiveLabelText + .setVisible(true) + .setColor(this.getTextColor(TextStyle.SUMMARY_ALT)) + .setShadowColor(this.getTextColor(TextStyle.SUMMARY_ALT, true)); + this.pokemonPassiveText + .setVisible(true) + .setText(passiveAbility.name) + .setColor(this.getTextColor(textStyle)) + .setAlpha(textAlpha) + .setShadowColor(this.getTextColor(textStyle, true)); if (this.activeTooltip === "PASSIVE") { globalScene.ui.editTooltip(`${passiveAbility.name}`, `${passiveAbility.description}`); @@ -3996,10 +3935,10 @@ export class StarterSelectUiHandler extends MessageUiHandler { x: this.pokemonPassiveText.x + this.pokemonPassiveText.displayWidth + 1, y: this.pokemonPassiveText.y + this.pokemonPassiveText.displayHeight / 2, }; - this.pokemonPassiveDisabledIcon.setVisible(isUnlocked && !isEnabled); - this.pokemonPassiveDisabledIcon.setPosition(iconPosition.x, iconPosition.y); - this.pokemonPassiveLockedIcon.setVisible(!isUnlocked); - this.pokemonPassiveLockedIcon.setPosition(iconPosition.x, iconPosition.y); + this.pokemonPassiveDisabledIcon + .setVisible(isUnlocked && !isEnabled) + .setPosition(iconPosition.x, iconPosition.y); + this.pokemonPassiveLockedIcon.setVisible(!isUnlocked).setPosition(iconPosition.x, iconPosition.y); } else if (this.activeTooltip === "PASSIVE") { // No passive and passive tooltip is active > hide it globalScene.ui.hideTooltip(); @@ -4077,8 +4016,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { } } else { this.shinyOverlay.setVisible(false); - this.pokemonNumberText.setColor(this.getTextColor(TextStyle.SUMMARY)); - this.pokemonNumberText.setShadowColor(this.getTextColor(TextStyle.SUMMARY, true)); + this.pokemonNumberText + .setColor(this.getTextColor(TextStyle.SUMMARY)) + .setShadowColor(this.getTextColor(TextStyle.SUMMARY, true)); this.pokemonGenderText.setText(""); this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); @@ -4111,8 +4051,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMovesContainer.setVisible(!!this.speciesStarterDexEntry?.caughtAttr && hasEggMoves); - this.pokemonAdditionalMoveCountLabel.setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`); - this.pokemonAdditionalMoveCountLabel.setVisible(this.speciesStarterMoves.length > 4); + this.pokemonAdditionalMoveCountLabel + .setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`) + .setVisible(this.speciesStarterMoves.length > 4); this.tryUpdateValue(); @@ -4121,14 +4062,12 @@ export class StarterSelectUiHandler extends MessageUiHandler { setTypeIcons(type1: PokemonType | null, type2: PokemonType | null): void { if (type1 !== null) { - this.type1Icon.setVisible(true); - this.type1Icon.setFrame(PokemonType[type1].toLowerCase()); + this.type1Icon.setVisible(true).setFrame(PokemonType[type1].toLowerCase()); } else { this.type1Icon.setVisible(false); } if (type2 !== null) { - this.type2Icon.setVisible(true); - this.type2Icon.setFrame(PokemonType[type2].toLowerCase()); + this.type2Icon.setVisible(true).setFrame(PokemonType[type2].toLowerCase()); } else { this.type2Icon.setVisible(false); } @@ -4146,17 +4085,18 @@ export class StarterSelectUiHandler extends MessageUiHandler { const species = this.starterSpecies[s]; const currentDexAttr = this.getCurrentDexProps(species.speciesId); const props = globalScene.gameData.getSpeciesDexAttrProps(species, currentDexAttr); - this.starterIcons[s].setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)); - this.starterIcons[s].setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); + this.starterIcons[s] + .setTexture(species.getIconAtlasKey(props.formIndex, props.shiny, props.variant)) + .setFrame(species.getIconId(props.female, props.formIndex, props.shiny, props.variant)); this.checkIconId(this.starterIcons[s], species, props.female, props.formIndex, props.shiny, props.variant); if (s >= index) { - this.starterCursorObjs[s].setPosition(this.starterCursorObjs[s + 1].x, this.starterCursorObjs[s + 1].y); - this.starterCursorObjs[s].setVisible(this.starterCursorObjs[s + 1].visible); + this.starterCursorObjs[s] + .setPosition(this.starterCursorObjs[s + 1].x, this.starterCursorObjs[s + 1].y) + .setVisible(this.starterCursorObjs[s + 1].visible); } } this.starterCursorObjs[this.starterSpecies.length].setVisible(false); - this.starterIcons[this.starterSpecies.length].setTexture("pokemon_icons_0"); - this.starterIcons[this.starterSpecies.length].setFrame("unknown"); + this.starterIcons[this.starterSpecies.length].setTexture("pokemon_icons_0").setFrame("unknown"); if (this.starterIconsCursorObj.visible) { if (this.starterIconsCursorIndex === this.starterSpecies.length) { @@ -4177,7 +4117,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { if (this.filteredStarterContainers.length > 0) { // Back to the first Pokemon if there is one this.cursorObj.setVisible(true); - this.setCursor(0 + this.scrollCursor * 9); + this.setCursor(this.scrollCursor * 9); } else { // Back to filters this.filterBarCursor = Math.max(1, this.filterBar.numFilters - 1); @@ -4212,8 +4152,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { break; } if (baseStarterValue - starterValue > 0) { - starter.label.setColor(this.getTextColor(textStyle)); - starter.label.setShadowColor(this.getTextColor(textStyle, true)); + starter.label.setColor(this.getTextColor(textStyle)).setShadowColor(this.getTextColor(textStyle, true)); } } @@ -4232,16 +4171,15 @@ export class StarterSelectUiHandler extends MessageUiHandler { if (newValueStr.startsWith("0.")) { newValueStr = newValueStr.slice(1); } - this.valueLimitLabel.setText(`${newValueStr}/${valueLimit}`); - this.valueLimitLabel.setColor(this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK)); - this.valueLimitLabel.setShadowColor( - this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK, true), - ); + this.valueLimitLabel + .setText(`${newValueStr}/${valueLimit}`) + .setColor(this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK)) + .setShadowColor(this.getTextColor(!overLimit ? TextStyle.TOOLTIP_CONTENT : TextStyle.SUMMARY_PINK, true)); if (overLimit) { globalScene.time.delayedCall(fixedInt(500), () => this.tryUpdateValue()); return false; } - let isPartyValid: boolean = this.isPartyValid(); + let isPartyValid = this.isPartyValid(); if (addingToParty) { const species = this.filteredStarterContainers[this.cursor].species; const isNewPokemonValid = checkStarterValidForChallenge( @@ -4249,7 +4187,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), false, ); - isPartyValid = isPartyValid || isNewPokemonValid; + isPartyValid ||= isNewPokemonValid; } /** @@ -4424,7 +4362,7 @@ export class StarterSelectUiHandler extends MessageUiHandler { globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)), false, ); - canStart = canStart || isValidForChallenge; + canStart ||= isValidForChallenge; } return canStart; } @@ -4536,6 +4474,8 @@ export class StarterSelectUiHandler extends MessageUiHandler { } hideInstructions(): void { + // TODO: uncomment this and delete the rest of the method once our testing infra supports mocks of `Phaser.GameObject.Group` + // this.instructionElemGroup.setVisible(false); this.shinyIconElement.setVisible(false); this.shinyLabel.setVisible(false); this.formIconElement.setVisible(false); @@ -4585,8 +4525,9 @@ export class StarterSelectUiHandler extends MessageUiHandler { console.log( `${species.name}'s icon ${icon.frame.name} does not match getIconId with female: ${female}, formIndex: ${formIndex}, shiny: ${shiny}, variant: ${variant}`, ); - icon.setTexture(species.getIconAtlasKey(formIndex, false, variant)); - icon.setFrame(species.getIconId(female, formIndex, false, variant)); + icon + .setTexture(species.getIconAtlasKey(formIndex, false, variant)) + .setFrame(species.getIconId(female, formIndex, false, variant)); } } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index b51bdfdb157..b6ce0a706f8 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -879,7 +879,7 @@ export class SummaryUiHandler extends UiHandler { !isNullOrUndefined(this.pokemon) ) { const teraIcon = globalScene.add.sprite(123, 26, "button_tera"); - teraIcon.setName("terrastallize-icon"); + teraIcon.setName("terastallize-icon"); teraIcon.setFrame(PokemonType[this.pokemon.getTeraType()].toLowerCase()); profileContainer.add(teraIcon); } diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/test-dialogue-ui-handler.ts index c57c73ca777..4f825ed95ea 100644 --- a/src/ui/test-dialogue-ui-handler.ts +++ b/src/ui/test-dialogue-ui-handler.ts @@ -1,6 +1,6 @@ import { UiMode } from "#enums/ui-mode"; import type { PlayerPokemon } from "#field/pokemon"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import type { InputFieldConfig } from "#ui/form-modal-ui-handler"; import { FormModalUiHandler } from "#ui/form-modal-ui-handler"; import type { ModalConfig } from "#ui/modal-ui-handler"; @@ -13,7 +13,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { setup() { super.setup(); - const flattenKeys = (object?: any, topKey?: string, midleKey?: string[]): Array => { + const flattenKeys = (object?: any, topKey?: string, middleKey?: string[]): Array => { return Object.keys(object ?? {}) .map((t, i) => { const value = Object.values(object)[i]; @@ -23,7 +23,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { // If the value is an object, execute the same process // si el valor es un objeto ejecuta el mismo proceso - return flattenKeys(value, topKey ?? t, topKey ? (midleKey ? [...midleKey, t] : [t]) : undefined).filter( + return flattenKeys(value, topKey ?? t, topKey ? (middleKey ? [...middleKey, t] : [t]) : undefined).filter( t => t.length > 0, ); } @@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler { // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key // Return in the format expected by i18next - return midleKey ? `${topKey}:${midleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`; + return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`; } }) .filter(t => t); diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 66cb69f6a26..ab7a4d020fa 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -36,12 +36,12 @@ export class TitleUiHandler extends OptionSelectUiHandler { const ui = this.getUi(); - this.titleContainer = globalScene.add.container(0, -(globalScene.game.canvas.height / 6)); + this.titleContainer = globalScene.add.container(0, -globalScene.scaledCanvas.height); this.titleContainer.setName("title"); this.titleContainer.setAlpha(0); ui.add(this.titleContainer); - const logo = globalScene.add.image(globalScene.game.canvas.width / 6 / 2, 8, "logo"); + const logo = globalScene.add.image(globalScene.scaledCanvas.width / 2, 8, "logo"); logo.setOrigin(0.5, 0); this.titleContainer.add(logo); @@ -53,7 +53,7 @@ export class TitleUiHandler extends OptionSelectUiHandler { this.playerCountLabel = addTextObject( // Actual y position will be determined after the title menu has been populated with options - globalScene.game.canvas.width / 6 - 2, + globalScene.scaledCanvas.width - 2, 0, `? ${i18next.t("menu:playersOnline")}`, TextStyle.MESSAGE, @@ -131,7 +131,7 @@ export class TitleUiHandler extends OptionSelectUiHandler { if (ret) { // Moving player count to top of the menu - this.playerCountLabel.setY(globalScene.game.canvas.height / 6 - 13 - this.getWindowHeight()); + this.playerCountLabel.setY(globalScene.scaledCanvas.height - 13 - this.getWindowHeight()); this.splashMessage = randItem(getSplashMessages()); this.splashMessageText.setText( diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 4c8f0613122..d5baea07ed5 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -13,6 +13,7 @@ import { BallUiHandler } from "#ui/ball-ui-handler"; import { BattleMessageUiHandler } from "#ui/battle-message-ui-handler"; import type { BgmBar } from "#ui/bgm-bar"; import { GameChallengesUiHandler } from "#ui/challenges-select-ui-handler"; +import { ChangePasswordFormUiHandler } from "#ui/change-password-form-ui-handler"; import { CommandUiHandler } from "#ui/command-ui-handler"; import { ConfirmUiHandler } from "#ui/confirm-ui-handler"; import { EggGachaUiHandler } from "#ui/egg-gacha-ui-handler"; @@ -102,6 +103,7 @@ const noTransitionModes = [ UiMode.ADMIN, UiMode.MYSTERY_ENCOUNTER, UiMode.RUN_INFO, + UiMode.CHANGE_PASSWORD_FORM, ]; export class UI extends Phaser.GameObjects.Container { @@ -121,7 +123,7 @@ export class UI extends Phaser.GameObjects.Container { private overlayActive: boolean; constructor() { - super(globalScene, 0, globalScene.game.canvas.height / 6); + super(globalScene, 0, globalScene.scaledCanvas.height); this.mode = UiMode.MESSAGE; this.modeChain = []; @@ -172,6 +174,7 @@ export class UI extends Phaser.GameObjects.Container { new AutoCompleteUiHandler(), new AdminUiHandler(), new MysteryEncounterUiHandler(), + new ChangePasswordFormUiHandler(), ]; } @@ -180,13 +183,7 @@ export class UI extends Phaser.GameObjects.Container { for (const handler of this.handlers) { handler.setup(); } - this.overlay = globalScene.add.rectangle( - 0, - 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, - 0, - ); + this.overlay = globalScene.add.rectangle(0, 0, globalScene.scaledCanvas.width, globalScene.scaledCanvas.height, 0); this.overlay.setName("rect-ui-overlay"); this.overlay.setOrigin(0, 0); globalScene.uiContainer.add(this.overlay); @@ -437,15 +434,15 @@ export class UI extends Phaser.GameObjects.Container { if (isTouch) { // If we are in the top left quadrant on mobile, move the tooltip to the top right corner if (pointerX <= globalScene.game.canvas.width / 2 && pointerY <= globalScene.game.canvas.height / 2) { - x = globalScene.game.canvas.width / 6 - tooltipWidth - padding; + x = globalScene.scaledCanvas.width - tooltipWidth - padding; } } else { // If the tooltip would go offscreen on the right, or is close to it, move to the left of the cursor - if (x + tooltipWidth + padding > globalScene.game.canvas.width / 6) { + if (x + tooltipWidth + padding > globalScene.scaledCanvas.width) { x = Math.max(padding, pointerX / 6 - tooltipWidth - padding); } // If the tooltip would go offscreen at the bottom, or is close to it, move above the cursor - if (y + tooltipHeight + padding > globalScene.game.canvas.height / 6) { + if (y + tooltipHeight + padding > globalScene.scaledCanvas.height) { y = Math.max(padding, pointerY / 6 - tooltipHeight - padding); } } diff --git a/src/utils/challenge-utils.ts b/src/utils/challenge-utils.ts new file mode 100644 index 00000000000..43297027e04 --- /dev/null +++ b/src/utils/challenge-utils.ts @@ -0,0 +1,409 @@ +import type { FixedBattleConfig } from "#app/battle"; +import { globalScene } from "#app/global-scene"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { pokemonFormChanges } from "#data/pokemon-forms"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { ChallengeType } from "#enums/challenge-type"; +import type { MoveId } from "#enums/move-id"; +import type { MoveSourceType } from "#enums/move-source-type"; +import type { SpeciesId } from "#enums/species-id"; +import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { ModifierTypeOption } from "#modifiers/modifier-type"; +import type { DexAttrProps } from "#system/game-data"; +import { BooleanHolder, type NumberHolder } from "./common"; +import { getPokemonSpecies } from "./pokemon-utils"; + +/** + * @param challengeType - {@linkcode ChallengeType.STARTER_CHOICE} + * @param pokemon - The {@linkcode PokemonSpecies} to check the validity of + * @param valid - A {@linkcode BooleanHolder} holding the checked species' validity; + * will be set to `false` if the species is disallowed + * @param dexAttr - The {@linkcode DexAttrProps | Dex attributes} of the species + * @returns `true` if any challenge was successfully applied + */ +export function applyChallenges( + challengeType: ChallengeType.STARTER_CHOICE, + pokemon: PokemonSpecies, + valid: BooleanHolder, + dexAttr: DexAttrProps, +): boolean; +/** + * Apply all challenges that modify available total starter points. + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS + * @param points {@link NumberHolder} The amount of points you have available. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean; +/** + * Apply all challenges that modify the cost of a starter. + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST + * @param species {@link SpeciesId} The pokemon to change the cost of. + * @param points {@link NumberHolder} The cost of the pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.STARTER_COST, + species: SpeciesId, + cost: NumberHolder, +): boolean; +/** + * Apply all challenges that modify a starter after selection. + * @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY + * @param pokemon {@link Pokemon} The starter pokemon to modify. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pokemon: Pokemon): boolean; +/** + * Apply all challenges that what pokemon you can have in battle. + * @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE + * @param pokemon {@link Pokemon} The pokemon to check the validity of. + * @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.POKEMON_IN_BATTLE, + pokemon: Pokemon, + valid: BooleanHolder, +): boolean; +/** + * Apply all challenges that modify what fixed battles there are. + * @param challengeType {@link ChallengeType} ChallengeType.FIXED_BATTLES + * @param waveIndex {@link Number} The current wave index. + * @param battleConfig {@link FixedBattleConfig} The battle config to modify. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.FIXED_BATTLES, + waveIndex: number, + battleConfig: FixedBattleConfig, +): boolean; +/** + * Apply all challenges that modify type effectiveness. + * @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS + * @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean; +/** + * Apply all challenges that modify what level AI are. + * @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL + * @param level {@link NumberHolder} The generated level of the pokemon. + * @param levelCap {@link Number} The maximum level cap for the current wave. + * @param isTrainer {@link Boolean} Whether this is a trainer pokemon. + * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.AI_LEVEL, + level: NumberHolder, + levelCap: number, + isTrainer: boolean, + isBoss: boolean, +): boolean; +/** + * Apply all challenges that modify how many move slots the AI has. + * @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS + * @param pokemon {@link Pokemon} The pokemon being considered. + * @param moveSlots {@link NumberHolder} The amount of move slots. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.AI_MOVE_SLOTS, + pokemon: Pokemon, + moveSlots: NumberHolder, +): boolean; +/** + * Apply all challenges that modify whether a pokemon has its passive. + * @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS + * @param pokemon {@link Pokemon} The pokemon to modify. + * @param hasPassive {@link BooleanHolder} Whether it has its passive. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.PASSIVE_ACCESS, + pokemon: Pokemon, + hasPassive: BooleanHolder, +): boolean; +/** + * Apply all challenges that modify the game modes settings. + * @param challengeType {@link ChallengeType} ChallengeType.GAME_MODE_MODIFY + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY): boolean; +/** + * Apply all challenges that modify what level a pokemon can access a move. + * @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link MoveId} The move in question. + * @param level {@link NumberHolder} The level threshold for access. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.MOVE_ACCESS, + pokemon: Pokemon, + moveSource: MoveSourceType, + move: MoveId, + level: NumberHolder, +): boolean; +/** + * Apply all challenges that modify what weight a pokemon gives to move generation + * @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT + * @param pokemon {@link Pokemon} What pokemon would learn the move. + * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. + * @param move {@link MoveId} The move in question. + * @param weight {@link NumberHolder} The weight of the move. + * @returns True if any challenge was successfully applied. + */ +export function applyChallenges( + challengeType: ChallengeType.MOVE_WEIGHT, + pokemon: Pokemon, + moveSource: MoveSourceType, + move: MoveId, + weight: NumberHolder, +): boolean; + +export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean; + +/** + * Apply all challenges that conditionally enable or disable automatic party healing during biome transitions + * @param challengeType - {@linkcode ChallengeType.PARTY_HEAL} + * @param status - Whether party healing is enabled or not + * @returns `true` if any challenge was successfully applied, `false` otherwise + */ +export function applyChallenges(challengeType: ChallengeType.PARTY_HEAL, status: BooleanHolder): boolean; + +/** + * Apply all challenges that conditionally enable or disable the shop + * @param challengeType - {@linkcode ChallengeType.SHOP} + * @param status - Whether party healing is enabled or not + * @returns `true` if any challenge was successfully applied, `false` otherwise + */ +export function applyChallenges(challengeType: ChallengeType.SHOP, status: BooleanHolder): boolean; + +/** + * Apply all challenges that validate whether a pokemon can be added to the player's party or not + * @param challengeType - {@linkcode ChallengeType.POKEMON_ADD_TO_PARTY} + * @param pokemon - The pokemon being caught + * @param status - Whether the pokemon can be added to the party or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges( + challengeType: ChallengeType.POKEMON_ADD_TO_PARTY, + pokemon: EnemyPokemon, + status: BooleanHolder, +): boolean; + +/** + * Apply all challenges that validate whether a pokemon is allowed to fuse or not + * @param challengeType - {@linkcode ChallengeType.POKEMON_FUSION} + * @param pokemon - The pokemon being checked + * @param status - Whether the selected pokemon is allowed to fuse or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges( + challengeType: ChallengeType.POKEMON_FUSION, + pokemon: PlayerPokemon, + status: BooleanHolder, +): boolean; + +/** + * Apply all challenges that validate whether particular moves can or cannot be used + * @param challengeType - {@linkcode ChallengeType.POKEMON_MOVE} + * @param moveId - The move being checked + * @param status - Whether the move can be used or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges( + challengeType: ChallengeType.POKEMON_MOVE, + moveId: MoveId, + status: BooleanHolder, +): boolean; + +/** + * Apply all challenges that validate whether particular items are or are not sold in the shop + * @param challengeType - {@linkcode ChallengeType.SHOP_ITEM} + * @param shopItem - The item being checked + * @param status - Whether the item should be added to the shop or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges( + challengeType: ChallengeType.SHOP_ITEM, + shopItem: ModifierTypeOption | null, + status: BooleanHolder, +): boolean; + +/** + * Apply all challenges that validate whether particular items will be given as a reward after a wave + * @param challengeType - {@linkcode ChallengeType.WAVE_REWARD} + * @param reward - The reward being checked + * @param status - Whether the reward should be added to the reward options or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges( + challengeType: ChallengeType.WAVE_REWARD, + reward: ModifierTypeOption | null, + status: BooleanHolder, +): boolean; + +/** + * Apply all challenges that prevent recovery from fainting + * @param challengeType - {@linkcode ChallengeType.PREVENT_REVIVE} + * @param status - Whether fainting is a permanent status or not + * @return `true` if any challenge was sucessfully applied, `false` otherwise + */ +export function applyChallenges(challengeType: ChallengeType.PREVENT_REVIVE, status: BooleanHolder): boolean; + +export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean { + let ret = false; + globalScene.gameMode.challenges.forEach(c => { + if (c.value !== 0) { + switch (challengeType) { + case ChallengeType.STARTER_CHOICE: + ret ||= c.applyStarterChoice(args[0], args[1], args[2]); + break; + case ChallengeType.STARTER_POINTS: + ret ||= c.applyStarterPoints(args[0]); + break; + case ChallengeType.STARTER_COST: + ret ||= c.applyStarterCost(args[0], args[1]); + break; + case ChallengeType.STARTER_MODIFY: + ret ||= c.applyStarterModify(args[0]); + break; + case ChallengeType.POKEMON_IN_BATTLE: + ret ||= c.applyPokemonInBattle(args[0], args[1]); + break; + case ChallengeType.FIXED_BATTLES: + ret ||= c.applyFixedBattle(args[0], args[1]); + break; + case ChallengeType.TYPE_EFFECTIVENESS: + ret ||= c.applyTypeEffectiveness(args[0]); + break; + case ChallengeType.AI_LEVEL: + ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.AI_MOVE_SLOTS: + ret ||= c.applyMoveSlot(args[0], args[1]); + break; + case ChallengeType.PASSIVE_ACCESS: + ret ||= c.applyPassiveAccess(args[0], args[1]); + break; + case ChallengeType.GAME_MODE_MODIFY: + ret ||= c.applyGameModeModify(); + break; + case ChallengeType.MOVE_ACCESS: + ret ||= c.applyMoveAccessLevel(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.MOVE_WEIGHT: + ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]); + break; + case ChallengeType.FLIP_STAT: + ret ||= c.applyFlipStat(args[0], args[1]); + break; + case ChallengeType.PARTY_HEAL: + ret ||= c.applyPartyHeal(args[0]); + break; + case ChallengeType.SHOP: + ret ||= c.applyShop(args[0]); + break; + case ChallengeType.POKEMON_ADD_TO_PARTY: + ret ||= c.applyPokemonAddToParty(args[0], args[1]); + break; + case ChallengeType.POKEMON_FUSION: + ret ||= c.applyPokemonFusion(args[0], args[1]); + break; + case ChallengeType.POKEMON_MOVE: + ret ||= c.applyPokemonMove(args[0], args[1]); + break; + case ChallengeType.SHOP_ITEM: + ret ||= c.applyShopItem(args[0], args[1]); + break; + case ChallengeType.WAVE_REWARD: + ret ||= c.applyWaveReward(args[0], args[1]); + break; + case ChallengeType.PREVENT_REVIVE: + ret ||= c.applyPreventRevive(args[0]); + break; + } + } + }); + return ret; +} + +/** + * Apply all challenges to the given starter (and form) to check its validity. + * Differs from {@linkcode checkSpeciesValidForChallenge} which only checks form changes. + * @param species - The {@linkcode PokemonSpecies} to check the validity of. + * @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index. + * @param soft - If `true`, allow it if it could become valid through evolution or form change. + * @returns `true` if the species is considered valid. + */ +export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { + if (!soft) { + const isValidForChallenge = new BooleanHolder(true); + applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); + return isValidForChallenge.value; + } + // We check the validity of every evolution and form change, and require that at least one is valid + const speciesToCheck = [species.speciesId]; + while (speciesToCheck.length) { + const checking = speciesToCheck.pop(); + // Linter complains if we don't handle this + if (!checking) { + return false; + } + const checkingSpecies = getPokemonSpecies(checking); + if (checkSpeciesValidForChallenge(checkingSpecies, props, true)) { + return true; + } + if (checking && pokemonEvolutions.hasOwnProperty(checking)) { + pokemonEvolutions[checking].forEach(e => { + // Form check to deal with cases such as Basculin -> Basculegion + // TODO: does this miss anything if checking forms of a stage 2 Pokémon? + if (!e?.preFormKey || e.preFormKey === species.forms[props.formIndex].formKey) { + speciesToCheck.push(e.speciesId); + } + }); + } + } + return false; +} + +/** + * Apply all challenges to the given species (and form) to check its validity. + * Differs from {@linkcode checkStarterValidForChallenge} which also checks evolutions. + * @param species - The {@linkcode PokemonSpecies} to check the validity of. + * @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index. + * @param soft - If `true`, allow it if it could become valid through a form change. + * @returns `true` if the species is considered valid. + */ +function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { + const isValidForChallenge = new BooleanHolder(true); + applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); + if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) { + return isValidForChallenge.value; + } + // If the form in props is valid, return true before checking other form changes + if (soft && isValidForChallenge.value) { + return true; + } + + const result = pokemonFormChanges[species.speciesId].some(f1 => { + // Exclude form changes that require the mon to be on the field to begin with + if (!("item" in f1.trigger)) { + return false; + } + + return species.forms.some((f2, formIndex) => { + if (f1.formKey === f2.formKey) { + const formProps = { ...props, formIndex }; + const isFormValidForChallenge = new BooleanHolder(true); + applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps); + return isFormValidForChallenge.value; + } + return false; + }); + }); + return result; +} diff --git a/test/abilities/analytic.test.ts b/test/abilities/analytic.test.ts index d1b9ba4cbbb..cf5a501bbc5 100644 --- a/test/abilities/analytic.test.ts +++ b/test/abilities/analytic.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Analytic", () => { it("should increase damage if the user moves last", async () => { await game.classicMode.startBattle([SpeciesId.ARCEUS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.TACKLE); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/abilities/anger-point.test.ts b/test/abilities/anger-point.test.ts index 84a22449dae..4031ff702a9 100644 --- a/test/abilities/anger-point.test.ts +++ b/test/abilities/anger-point.test.ts @@ -36,7 +36,7 @@ describe("Ability - Anger Point", () => { it("should set the user's attack stage to +6 when hit by a critical hit", async () => { game.override.enemyAbility(AbilityId.ANGER_POINT).moveset(MoveId.FALSE_SWIPE); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); // minimize the enemy's attack stage to ensure it is always set to +6 enemy.setStatStage(Stat.ATK, -6); @@ -53,7 +53,7 @@ describe("Ability - Anger Point", () => { .enemyAbility(AbilityId.ANGER_POINT) .ability(AbilityId.SKILL_LINK); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true); const angerPointSpy = vi.spyOn(PostReceiveCritStatStageChangeAbAttr.prototype, "apply"); game.move.select(MoveId.BULLET_SEED); @@ -68,7 +68,7 @@ describe("Ability - Anger Point", () => { .enemyHasPassiveAbility(true) .moveset(MoveId.FALSE_SWIPE); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true); enemy.setStatStage(Stat.ATK, 6); game.move.select(MoveId.FALSE_SWIPE); diff --git a/test/abilities/beast-boost.test.ts b/test/abilities/beast-boost.test.ts index 193de9b7669..aeb4d854b1a 100644 --- a/test/abilities/beast-boost.test.ts +++ b/test/abilities/beast-boost.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Beast Boost", () => { it("should prefer highest stat to boost its corresponding stat stage by 1 when winning a battle", async () => { await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // Set the pokemon's highest stat to DEF, so it should be picked by Beast Boost vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 1000, 200, 100, 100]); console.log(playerPokemon.stats); @@ -54,7 +54,7 @@ describe("Abilities - Beast Boost", () => { await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // If the opponent uses Guard Split, the pokemon's second highest stat (SPATK) should be chosen vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 201, 200, 100, 100]); @@ -72,7 +72,7 @@ describe("Abilities - Beast Boost", () => { // Order preference follows the order of EFFECTIVE_STAT await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // Set up tie between SPATK, SPDEF, and SPD, where SPATK should win vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 1, 1, 100, 100, 100]); diff --git a/test/abilities/competitive.test.ts b/test/abilities/competitive.test.ts index 1a083705e5c..3c511b85b14 100644 --- a/test/abilities/competitive.test.ts +++ b/test/abilities/competitive.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Competitive", () => { it("lower atk and def by 1 via tickle, then increase spatk by 4 via competitive", async () => { await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); @@ -49,7 +49,7 @@ describe("Abilities - Competitive", () => { game.override.enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.CLOSE_COMBAT); await game.phaseInterceptor.to(TurnInitPhase); @@ -62,7 +62,7 @@ describe("Abilities - Competitive", () => { game.override.startingHeldItems([{ name: "WHITE_HERB" }]); await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); diff --git a/test/abilities/contrary.test.ts b/test/abilities/contrary.test.ts index df72c25054a..e7a1ae1e3d0 100644 --- a/test/abilities/contrary.test.ts +++ b/test/abilities/contrary.test.ts @@ -33,7 +33,7 @@ describe("Abilities - Contrary", () => { it("should invert stat changes when applied", async () => { await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); }); @@ -43,7 +43,7 @@ describe("Abilities - Contrary", () => { game.override.enemyPassiveAbility(AbilityId.CLEAR_BODY).moveset([MoveId.TAIL_WHIP]); await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); @@ -57,7 +57,7 @@ describe("Abilities - Contrary", () => { game.override.enemyPassiveAbility(AbilityId.CLEAR_BODY).enemyMoveset(MoveId.HOWL).moveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); diff --git a/test/abilities/cud-chew.test.ts b/test/abilities/cud-chew.test.ts index 5f15de04cae..f68141096eb 100644 --- a/test/abilities/cud-chew.test.ts +++ b/test/abilities/cud-chew.test.ts @@ -43,7 +43,7 @@ describe("Abilities - Cud Chew", () => { it("stores inside summonData at end of turn", async () => { await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; // needed to allow sitrus procs game.move.select(MoveId.SPLASH); @@ -71,7 +71,7 @@ describe("Abilities - Cud Chew", () => { game.override.enemyMoveset([MoveId.SPLASH, MoveId.HEAL_PULSE]); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); // Dip below half to eat berry farigiraf.hp = farigiraf.getMaxHp() / 2 - 1; @@ -120,7 +120,7 @@ describe("Abilities - Cud Chew", () => { .enemyMoveset(MoveId.TEATIME); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; // needed to allow berry procs game.move.select(MoveId.STUFF_CHEEKS); @@ -148,7 +148,7 @@ describe("Abilities - Cud Chew", () => { it("should reset both arrays on switch", async () => { await game.classicMode.startBattle([SpeciesId.FARIGIRAF, SpeciesId.GIRAFARIG]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; // eat berry turn 1, switch out turn 2 @@ -177,7 +177,7 @@ describe("Abilities - Cud Chew", () => { game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; game.move.select(MoveId.SPLASH); @@ -199,7 +199,7 @@ describe("Abilities - Cud Chew", () => { const apply = vi.spyOn(CudChewConsumeBerryAbAttr.prototype, "apply"); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; game.move.select(MoveId.SPLASH); @@ -225,7 +225,7 @@ describe("Abilities - Cud Chew", () => { game.override.enemyAbility(AbilityId.UNNERVE); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; game.move.select(MoveId.SPLASH); @@ -243,7 +243,7 @@ describe("Abilities - Cud Chew", () => { game.override.enemyMoveset(MoveId.INCINERATE); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = farigiraf.getMaxHp() / 4; game.move.select(MoveId.SPLASH); @@ -262,7 +262,7 @@ describe("Abilities - Cud Chew", () => { .startingHeldItems([]); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); game.move.select(MoveId.BUG_BITE); await game.toNextTurn(); @@ -278,7 +278,7 @@ describe("Abilities - Cud Chew", () => { game.override.passiveAbility(AbilityId.RIPEN); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; game.move.select(MoveId.SPLASH); @@ -294,7 +294,7 @@ describe("Abilities - Cud Chew", () => { game.override.enemyLevel(1); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); - const farigiraf = game.scene.getPlayerPokemon()!; + const farigiraf = game.field.getPlayerPokemon(); farigiraf.hp = 1; game.move.select(MoveId.HYPER_VOICE); @@ -307,7 +307,7 @@ describe("Abilities - Cud Chew", () => { // reload and the berry should still be there await game.reload.reloadSession(); - const farigirafReloaded = game.scene.getPlayerPokemon()!; + const farigirafReloaded = game.field.getPlayerPokemon(); expect(farigirafReloaded.summonData.berriesEatenLast).toEqual([BerryType.SITRUS]); const wave1Hp = farigirafReloaded.hp; diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index bbf1a368573..c651a341c42 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -125,7 +125,7 @@ describe("Abilities - Dancer", () => { game.override.battleStyle("double").moveset(MoveId.SPLASH).enemyMoveset([MoveId.SWORDS_DANCE, MoveId.FAKE_OUT]); await game.classicMode.startBattle([SpeciesId.ORICORIO]); - const oricorio = game.scene.getPlayerPokemon()!; + const oricorio = game.field.getPlayerPokemon(); expect(oricorio).toBeDefined(); // get faked out and copy swords dance diff --git a/test/abilities/defiant.test.ts b/test/abilities/defiant.test.ts index 29c386ff093..f1e62701b9d 100644 --- a/test/abilities/defiant.test.ts +++ b/test/abilities/defiant.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Defiant", () => { it("lower atk and def by 1 via tickle, then increase atk by 4 via defiant", async () => { await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); @@ -48,7 +48,7 @@ describe("Abilities - Defiant", () => { game.override.enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.CLOSE_COMBAT); await game.phaseInterceptor.to(TurnInitPhase); @@ -61,7 +61,7 @@ describe("Abilities - Defiant", () => { game.override.startingHeldItems([{ name: "WHITE_HERB" }]); await game.classicMode.startBattle([SpeciesId.FLYGON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnInitPhase); diff --git a/test/abilities/desolate-land.test.ts b/test/abilities/desolate-land.test.ts index c88f7415bb1..15dd1897864 100644 --- a/test/abilities/desolate-land.test.ts +++ b/test/abilities/desolate-land.test.ts @@ -145,7 +145,7 @@ describe("Abilities - Desolate Land", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN); - vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); + vi.spyOn(game.field.getPlayerPokemon(), "randBattleSeedInt").mockReturnValue(0); vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index bf271c81e4d..4745d6ab609 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -37,7 +37,7 @@ describe("Abilities - Disguise", () => { it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => { await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); const maxHp = mimikyu.getMaxHp(); const disguiseDamage = toDmgValue(maxHp / 8); @@ -54,7 +54,7 @@ describe("Abilities - Disguise", () => { it("doesn't break disguise when attacked with ineffective move", async () => { await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); expect(mimikyu.formIndex).toBe(disguisedForm); @@ -69,7 +69,7 @@ describe("Abilities - Disguise", () => { game.override.moveset([MoveId.SURGING_STRIKES]).enemyLevel(5); await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); const maxHp = mimikyu.getMaxHp(); const disguiseDamage = toDmgValue(maxHp / 8); @@ -91,7 +91,7 @@ describe("Abilities - Disguise", () => { it("takes effects from status moves and damage from status effects", async () => { await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); expect(mimikyu.hp).toBe(mimikyu.getMaxHp()); game.move.select(MoveId.TOXIC_THREAD); @@ -109,7 +109,7 @@ describe("Abilities - Disguise", () => { await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]); - const mimikyu = game.scene.getPlayerPokemon()!; + const mimikyu = game.field.getPlayerPokemon(); const maxHp = mimikyu.getMaxHp(); const disguiseDamage = toDmgValue(maxHp / 8); @@ -154,7 +154,7 @@ describe("Abilities - Disguise", () => { await game.classicMode.startBattle(); - const mimikyu = game.scene.getPlayerPokemon()!; + const mimikyu = game.field.getPlayerPokemon(); expect(mimikyu.formIndex).toBe(bustedForm); @@ -175,7 +175,7 @@ describe("Abilities - Disguise", () => { await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]); - const mimikyu1 = game.scene.getPlayerPokemon()!; + const mimikyu1 = game.field.getPlayerPokemon(); expect(mimikyu1.formIndex).toBe(bustedForm); @@ -190,7 +190,7 @@ describe("Abilities - Disguise", () => { game.override.enemyMoveset([MoveId.ENDURE]); await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); mimikyu.hp = 1; game.move.select(MoveId.SHADOW_SNEAK); @@ -205,7 +205,7 @@ describe("Abilities - Disguise", () => { await game.classicMode.startBattle(); - const mimikyu = game.scene.getEnemyPokemon()!; + const mimikyu = game.field.getEnemyPokemon(); const maxHp = mimikyu.getMaxHp(); const disguiseDamage = toDmgValue(maxHp / 8); @@ -225,6 +225,6 @@ describe("Abilities - Disguise", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.formIndex).toBe(disguisedForm); + expect(game.field.getEnemyPokemon().formIndex).toBe(disguisedForm); }); }); diff --git a/test/abilities/dry-skin.test.ts b/test/abilities/dry-skin.test.ts index 01602150710..ad88c5aa377 100644 --- a/test/abilities/dry-skin.test.ts +++ b/test/abilities/dry-skin.test.ts @@ -35,7 +35,7 @@ describe("Abilities - Dry Skin", () => { it("during sunlight, lose 1/8 of maximum health at the end of each turn", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); // first turn game.move.select(MoveId.SUNNY_DAY); @@ -52,7 +52,7 @@ describe("Abilities - Dry Skin", () => { it("during rain, gain 1/8 of maximum health at the end of each turn", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1; @@ -72,7 +72,7 @@ describe("Abilities - Dry Skin", () => { game.override.moveset([MoveId.FLAMETHROWER]); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const initialHP = 1000; enemy.hp = initialHP; @@ -95,7 +95,7 @@ describe("Abilities - Dry Skin", () => { it("opposing water attacks heal 1/4 of maximum health and deal no damage", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1; @@ -109,7 +109,7 @@ describe("Abilities - Dry Skin", () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1; @@ -123,7 +123,7 @@ describe("Abilities - Dry Skin", () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1; @@ -145,7 +145,7 @@ describe("Abilities - Dry Skin", () => { it("opposing water moves still heal regardless of accuracy check", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.WATER_GUN); enemy.hp = enemy.hp - 1; diff --git a/test/abilities/early-bird.test.ts b/test/abilities/early-bird.test.ts index 97ce02e5e62..a7318fd8c6e 100644 --- a/test/abilities/early-bird.test.ts +++ b/test/abilities/early-bird.test.ts @@ -37,7 +37,7 @@ describe("Abilities - Early Bird", () => { it("reduces Rest's sleep time to 1 turn", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); game.move.select(MoveId.BELLY_DRUM); await game.toNextTurn(); @@ -62,7 +62,7 @@ describe("Abilities - Early Bird", () => { it("reduces 3-turn sleep to 1 turn", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.status = new Status(StatusEffect.SLEEP, 0, 4); game.move.select(MoveId.SPLASH); @@ -81,7 +81,7 @@ describe("Abilities - Early Bird", () => { it("reduces 1-turn sleep to 0 turns", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.status = new Status(StatusEffect.SLEEP, 0, 2); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/flash-fire.test.ts b/test/abilities/flash-fire.test.ts index d9f0e194d9c..a9d837e6e40 100644 --- a/test/abilities/flash-fire.test.ts +++ b/test/abilities/flash-fire.test.ts @@ -39,7 +39,7 @@ describe("Abilities - Flash Fire", () => { game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -50,7 +50,7 @@ describe("Abilities - Flash Fire", () => { game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.PROTECT]); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); game.move.select(MoveId.PROTECT); await game.phaseInterceptor.to(TurnEndPhase); @@ -61,7 +61,7 @@ describe("Abilities - Flash Fire", () => { game.override.enemyMoveset([MoveId.WILL_O_WISP]).moveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.move.forceHit(); @@ -76,7 +76,7 @@ describe("Abilities - Flash Fire", () => { game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH).statusEffect(StatusEffect.FREEZE); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); @@ -95,8 +95,8 @@ describe("Abilities - Flash Fire", () => { game.doSelectPartyPokemon(1); await game.phaseInterceptor.to(TurnEndPhase); - const chansey = game.scene.getPlayerPokemon()!; - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.CHANSEY); + const chansey = game.field.getPlayerPokemon(); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.CHANSEY); expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); }); @@ -107,7 +107,7 @@ describe("Abilities - Flash Fire", () => { .enemyAbility(AbilityId.FLASH_FIRE) .ability(AbilityId.NONE); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); const initialHP = 1000; blissey.hp = initialHP; @@ -137,7 +137,7 @@ describe("Abilities - Flash Fire", () => { .enemySpecies(SpeciesId.BLISSEY); await game.classicMode.startBattle([SpeciesId.RATTATA]); - const blissey = game.scene.getEnemyPokemon()!; + const blissey = game.field.getEnemyPokemon(); const initialHP = 1000; blissey.hp = initialHP; diff --git a/test/abilities/flower-gift.test.ts b/test/abilities/flower-gift.test.ts index 2fc67b098b7..01459cd4e1e 100644 --- a/test/abilities/flower-gift.test.ts +++ b/test/abilities/flower-gift.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Flower Gift", () => { game.move.select(MoveId.SPLASH); - expect(game.scene.getPlayerPokemon()?.formIndex).toBe(OVERCAST_FORM); + expect(game.field.getPlayerPokemon().formIndex).toBe(OVERCAST_FORM); }; /** @@ -169,7 +169,7 @@ describe("Abilities - Flower Gift", () => { game.override.weather(WeatherType.HARSH_SUN); await game.classicMode.startBattle([SpeciesId.CHERRIM]); - const cherrim = game.scene.getPlayerPokemon()!; + const cherrim = game.field.getPlayerPokemon(); expect(cherrim.formIndex).toBe(SUNSHINE_FORM); game.move.select(MoveId.SPLASH); @@ -188,7 +188,7 @@ describe("Abilities - Flower Gift", () => { await game.classicMode.startBattle([SpeciesId.CHERRIM, SpeciesId.MAGIKARP]); - const cherrim = game.scene.getPlayerPokemon()!; + const cherrim = game.field.getPlayerPokemon(); expect(cherrim.formIndex).toBe(SUNSHINE_FORM); @@ -215,7 +215,7 @@ describe("Abilities - Flower Gift", () => { game.override.weather(WeatherType.SUNNY); await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]); - const cherrim = game.scene.getPlayerPokemon()!; + const cherrim = game.field.getPlayerPokemon(); expect(cherrim.formIndex).toBe(SUNSHINE_FORM); diff --git a/test/abilities/flower-veil.test.ts b/test/abilities/flower-veil.test.ts index 46478a3dd9c..44274d86a1b 100644 --- a/test/abilities/flower-veil.test.ts +++ b/test/abilities/flower-veil.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Flower Veil", () => { .moveset([MoveId.REST, MoveId.SPLASH]) .startingHeldItems([{ name: "FLAME_ORB" }]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); game.move.select(MoveId.REST); await game.move.selectEnemyMove(MoveId.TACKLE); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -74,7 +74,7 @@ describe("Abilities - Flower Veil", () => { await game.move.selectEnemyMove(MoveId.YAWN, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase"); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); expect(user.getTag(BattlerTagType.DROWSY)).toBeFalsy(); expect(ally.getTag(BattlerTagType.DROWSY)).toBeFalsy(); }); @@ -87,7 +87,7 @@ describe("Abilities - Flower Veil", () => { game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.THUNDER_WAVE); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); }); it("should not prevent status conditions for a non-grass user and its non-grass allies", async () => { @@ -155,7 +155,7 @@ describe("Abilities - Flower Veil", () => { it("should prevent the drops while retaining the boosts from spicy extract", async () => { game.override.enemyMoveset([MoveId.SPICY_EXTRACT]).moveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); expect(user.getStatStage(Stat.ATK)).toBe(2); diff --git a/test/abilities/forecast.test.ts b/test/abilities/forecast.test.ts index 644927186f4..87d1d20acdb 100644 --- a/test/abilities/forecast.test.ts +++ b/test/abilities/forecast.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Forecast", () => { game.move.select(MoveId.SPLASH); - expect(game.scene.getPlayerPokemon()?.formIndex).toBe(NORMAL_FORM); + expect(game.field.getPlayerPokemon().formIndex).toBe(NORMAL_FORM); }; beforeAll(() => { @@ -186,14 +186,14 @@ describe("Abilities - Forecast", () => { game.move.select(MoveId.RAIN_DANCE); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()?.formIndex).toBe(RAINY_FORM); - expect(game.scene.getEnemyPokemon()?.formIndex).not.toBe(RAINY_FORM); + expect(game.field.getPlayerPokemon().formIndex).toBe(RAINY_FORM); + expect(game.field.getEnemyPokemon().formIndex).not.toBe(RAINY_FORM); }); it("reverts to Normal Form when Forecast is suppressed, changes form to match the weather when it regains it", async () => { game.override.enemyMoveset([MoveId.GASTRO_ACID]).weather(WeatherType.RAIN); await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.PIKACHU]); - const castform = game.scene.getPlayerPokemon()!; + const castform = game.field.getPlayerPokemon(); expect(castform.formIndex).toBe(RAINY_FORM); @@ -233,7 +233,7 @@ describe("Abilities - Forecast", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to(PostSummonPhase); - const castform = game.scene.getPlayerPokemon()!; + const castform = game.field.getPlayerPokemon(); // Damage phase should come first await game.phaseInterceptor.to(DamageAnimPhase); @@ -248,7 +248,7 @@ describe("Abilities - Forecast", () => { game.override.weather(WeatherType.RAIN); await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]); - const castform = game.scene.getPlayerPokemon()!; + const castform = game.field.getPlayerPokemon(); expect(castform.formIndex).toBe(RAINY_FORM); @@ -263,14 +263,14 @@ describe("Abilities - Forecast", () => { it("should trigger player's form change when summoned at the same time as an enemy with a weather changing ability", async () => { game.override.enemyAbility(AbilityId.DROUGHT); await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]); - const castform = game.scene.getPlayerPokemon()!; + const castform = game.field.getPlayerPokemon(); expect(castform.formIndex).toBe(SUNNY_FORM); }); it("should trigger enemy's form change when summoned at the same time as a player with a weather changing ability", async () => { game.override.ability(AbilityId.DROUGHT).enemySpecies(SpeciesId.CASTFORM).enemyAbility(AbilityId.FORECAST); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const castform = game.scene.getEnemyPokemon()!; + const castform = game.field.getEnemyPokemon(); expect(castform.formIndex).toBe(SUNNY_FORM); }); }); diff --git a/test/abilities/good-as-gold.test.ts b/test/abilities/good-as-gold.test.ts index 7fc1ad85b53..c6b6faf8349 100644 --- a/test/abilities/good-as-gold.test.ts +++ b/test/abilities/good-as-gold.test.ts @@ -43,7 +43,7 @@ describe("Abilities - Good As Gold", () => { game.override.enemyMoveset([MoveId.GROWL]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/gorilla-tactics.test.ts b/test/abilities/gorilla-tactics.test.ts index 83e6cdb156e..01acd2295ab 100644 --- a/test/abilities/gorilla-tactics.test.ts +++ b/test/abilities/gorilla-tactics.test.ts @@ -40,7 +40,7 @@ describe("Abilities - Gorilla Tactics", () => { it("boosts the Pokémon's Attack by 50%, but limits the Pokémon to using only one move", async () => { await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]); - const darmanitan = game.scene.getPlayerPokemon()!; + const darmanitan = game.field.getPlayerPokemon(); const initialAtkStat = darmanitan.getStat(Stat.ATK); game.move.select(MoveId.SPLASH); @@ -86,7 +86,7 @@ describe("Abilities - Gorilla Tactics", () => { vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]); - const darmanitan = game.scene.getPlayerPokemon()!; + const darmanitan = game.field.getPlayerPokemon(); game.move.select(MoveId.METRONOME); await game.phaseInterceptor.to("TurnEndPhase"); diff --git a/test/abilities/gulp-missile.test.ts b/test/abilities/gulp-missile.test.ts index faf30adae33..865a319251f 100644 --- a/test/abilities/gulp-missile.test.ts +++ b/test/abilities/gulp-missile.test.ts @@ -52,7 +52,7 @@ describe("Abilities - Gulp Missile", () => { it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => { await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); game.move.select(MoveId.DIVE); await game.toNextTurn(); @@ -66,7 +66,7 @@ describe("Abilities - Gulp Missile", () => { it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => { await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.49); expect(cramorant.getHpRatio()).toBe(0.49); @@ -80,7 +80,7 @@ describe("Abilities - Gulp Missile", () => { it("changes to base form when switched out after Surf or Dive is used", async () => { await game.classicMode.startBattle([SpeciesId.CRAMORANT, SpeciesId.MAGIKARP]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); game.move.select(MoveId.SURF); await game.toNextTurn(); @@ -95,7 +95,7 @@ describe("Abilities - Gulp Missile", () => { it("changes form during Dive's charge turn", async () => { await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); game.move.select(MoveId.DIVE); await game.phaseInterceptor.to("MoveEndPhase"); @@ -108,7 +108,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "damageAndUpdate"); game.move.select(MoveId.SURF); @@ -121,7 +121,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.TAIL_WHIP); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55); game.move.select(MoveId.SURF); @@ -140,8 +140,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const cramorant = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55); @@ -164,8 +164,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const cramorant = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.45); @@ -188,7 +188,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.SURF); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); game.move.select(MoveId.DIVE); await game.phaseInterceptor.to("BerryPhase", false); @@ -201,8 +201,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.TACKLE).enemyAbility(AbilityId.MAGIC_GUARD); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const cramorant = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55); @@ -225,7 +225,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.THUNDERBOLT); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); game.move.select(MoveId.SURF); await game.phaseInterceptor.to("FaintPhase"); @@ -233,7 +233,7 @@ describe("Abilities - Gulp Missile", () => { expect(cramorant.hp).toBe(0); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined(); expect(cramorant.formIndex).toBe(NORMAL_FORM); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.DEF)).toBe(-1); }); it("doesn't trigger if user is behind a substitute", async () => { @@ -244,21 +244,21 @@ describe("Abilities - Gulp Missile", () => { await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + expect(game.field.getPlayerPokemon().formIndex).toBe(GULPING_FORM); game.move.select(MoveId.SUBSTITUTE); await game.move.selectEnemyMove(MoveId.POWER_TRIP); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + expect(game.field.getPlayerPokemon().formIndex).toBe(GULPING_FORM); }); it("cannot be suppressed", async () => { game.override.enemyMoveset(MoveId.GASTRO_ACID); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55); game.move.select(MoveId.SURF); @@ -278,7 +278,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(MoveId.SKILL_SWAP); await game.classicMode.startBattle([SpeciesId.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon()!; + const cramorant = game.field.getPlayerPokemon(); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55); game.move.select(MoveId.SURF); @@ -301,6 +301,6 @@ describe("Abilities - Gulp Missile", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("TurnStartPhase"); - expect(game.scene.getEnemyPokemon()?.hasAbility(AbilityId.GULP_MISSILE)).toBe(false); + expect(game.field.getEnemyPokemon().hasAbility(AbilityId.GULP_MISSILE)).toBe(false); }); }); diff --git a/test/abilities/harvest.test.ts b/test/abilities/harvest.test.ts index d27ee491fed..ec4c02c7b91 100644 --- a/test/abilities/harvest.test.ts +++ b/test/abilities/harvest.test.ts @@ -19,7 +19,7 @@ describe("Abilities - Harvest", () => { let game: GameManager; const getPlayerBerries = () => - game.scene.getModifiers(BerryModifier, true).filter(b => b.pokemonId === game.scene.getPlayerPokemon()?.id); + game.scene.getModifiers(BerryModifier, true).filter(b => b.pokemonId === game.field.getPlayerPokemon().id); /** Check whether the player's Modifiers contains the specified berries and nothing else. */ function expectBerriesContaining(...berries: ModifierOverride[]): void { @@ -64,11 +64,11 @@ describe("Abilities - Harvest", () => { await game.move.selectEnemyMove(MoveId.NUZZLE); await game.phaseInterceptor.to("BerryPhase"); expect(getPlayerBerries()).toHaveLength(0); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toHaveLength(1); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toHaveLength(1); await game.phaseInterceptor.to("TurnEndPhase"); expectBerriesContaining({ name: "BERRY", type: BerryType.LUM, count: 1 }); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); }); it("tracks berries eaten while disabled/not present", async () => { @@ -82,7 +82,7 @@ describe("Abilities - Harvest", () => { .enemyAbility(AbilityId.NEUTRALIZING_GAS); await game.classicMode.startBattle([SpeciesId.MILOTIC]); - const milotic = game.scene.getPlayerPokemon()!; + const milotic = game.field.getPlayerPokemon(); expect(milotic).toBeDefined(); // Chug a few berries without harvest (should get tracked) @@ -122,7 +122,7 @@ describe("Abilities - Harvest", () => { .ability(AbilityId.BALL_FETCH); // don't actually need harvest for this test await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const regieleki = game.scene.getPlayerPokemon()!; + const regieleki = game.field.getPlayerPokemon(); regieleki.hp = 1; game.move.select(MoveId.SPLASH); @@ -150,7 +150,7 @@ describe("Abilities - Harvest", () => { .enemyAbility(AbilityId.COMPOUND_EYES); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const regieleki = game.scene.getPlayerPokemon()!; + const regieleki = game.field.getPlayerPokemon(); regieleki.hp = regieleki.getMaxHp() / 4 + 1; game.move.select(MoveId.SPLASH); @@ -161,7 +161,7 @@ describe("Abilities - Harvest", () => { // ate 1 berry and recovered it expect(regieleki.battleData.berriesEaten).toEqual([]); expect(getPlayerBerries()).toEqual([expect.objectContaining({ berryType: BerryType.PETAYA, stackCount: 1 })]); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1); // heal up so harvest doesn't proc and kill enemy game.move.select(MoveId.EARTHQUAKE); @@ -170,13 +170,13 @@ describe("Abilities - Harvest", () => { await game.toNextWave(); expectBerriesContaining({ name: "BERRY", count: 1, type: BerryType.PETAYA }); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1); await game.reload.reloadSession(); expect(regieleki.battleData.berriesEaten).toEqual([]); expectBerriesContaining({ name: "BERRY", count: 1, type: BerryType.PETAYA }); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1); }); it("cannot restore capped berries", async () => { @@ -187,7 +187,7 @@ describe("Abilities - Harvest", () => { game.override.startingHeldItems(initBerries); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; + const feebas = game.field.getPlayerPokemon(); feebas.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF]; game.move.select(MoveId.SPLASH); @@ -215,7 +215,7 @@ describe("Abilities - Harvest", () => { game.override.startingHeldItems(initBerries); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF]; game.move.select(MoveId.SPLASH); @@ -234,7 +234,7 @@ describe("Abilities - Harvest", () => { await game.move.selectEnemyMove(MoveId.INCINERATE); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); }); it("cannot restore knocked off berries", async () => { @@ -245,7 +245,7 @@ describe("Abilities - Harvest", () => { await game.move.selectEnemyMove(MoveId.KNOCK_OFF); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); }); it("can restore berries eaten by Teatime", async () => { @@ -257,7 +257,7 @@ describe("Abilities - Harvest", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); expectBerriesContaining(...initBerries); }); @@ -271,8 +271,8 @@ describe("Abilities - Harvest", () => { await game.phaseInterceptor.to("BerryPhase"); // pluck triggers harvest for neither side - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); - expect(game.scene.getEnemyPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); + expect(game.field.getEnemyPokemon().battleData.berriesEaten).toEqual([]); expect(getPlayerBerries()).toEqual([]); }); @@ -293,7 +293,7 @@ describe("Abilities - Harvest", () => { await game.phaseInterceptor.to("TurnEndPhase", false); // won't trigger harvest since we didn't lose the berry (it just doesn't ever add it to the array) - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]); expectBerriesContaining(...initBerries); }); @@ -303,7 +303,7 @@ describe("Abilities - Harvest", () => { await game.classicMode.startBattle([SpeciesId.MEOWSCARADA]); // pre damage - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.hp = 1; // steal a sitrus and immediately consume it @@ -326,7 +326,7 @@ describe("Abilities - Harvest", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toBe([]); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toBe([]); expect(getPlayerBerries()).toEqual([]); }); @@ -339,7 +339,7 @@ describe("Abilities - Harvest", () => { game.move.select(MoveId.NATURAL_GIFT); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toHaveLength(0); + expect(game.field.getPlayerPokemon().battleData.berriesEaten).toHaveLength(0); expectBerriesContaining(...initBerries); }); }); diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts index c151836d76d..52f47535bf4 100644 --- a/test/abilities/healer.test.ts +++ b/test/abilities/healer.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Healer", () => { game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); // Only want one magikarp to have the ability vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[AbilityId.HEALER]); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/heatproof.test.ts b/test/abilities/heatproof.test.ts index 63f877a3567..25e9be12809 100644 --- a/test/abilities/heatproof.test.ts +++ b/test/abilities/heatproof.test.ts @@ -40,7 +40,7 @@ describe("Abilities - Heatproof", () => { it("reduces Fire type damage by half", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const initialHP = 1000; enemy.hp = initialHP; @@ -63,7 +63,7 @@ describe("Abilities - Heatproof", () => { game.override.enemyStatusEffect(StatusEffect.BURN).enemySpecies(SpeciesId.ABRA); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.SPLASH); await game.toNextTurn(); diff --git a/test/abilities/honey-gather.test.ts b/test/abilities/honey-gather.test.ts index 8dfd2c189f8..e2f356470ae 100644 --- a/test/abilities/honey-gather.test.ts +++ b/test/abilities/honey-gather.test.ts @@ -62,7 +62,7 @@ describe("Abilities - Honey Gather", () => { game.scene.money = 1000; // something weird is going on with the test framework, so this is required to prevent a crash - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "scene", "get").mockReturnValue(game.scene); // Expects next wave so run must succeed vi.spyOn(Overrides, "RUN_SUCCESS_OVERRIDE", "get").mockReturnValue(true); diff --git a/test/abilities/hustle.test.ts b/test/abilities/hustle.test.ts index 74ee01f02ef..5280df7b9de 100644 --- a/test/abilities/hustle.test.ts +++ b/test/abilities/hustle.test.ts @@ -35,7 +35,7 @@ describe("Abilities - Hustle", () => { it("increases the user's Attack stat by 50%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; + const pikachu = game.field.getPlayerPokemon(); const atk = pikachu.stats[Stat.ATK]; vi.spyOn(pikachu, "getEffectiveStat"); @@ -49,7 +49,7 @@ describe("Abilities - Hustle", () => { it("lowers the accuracy of the user's physical moves by 20%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; + const pikachu = game.field.getPlayerPokemon(); vi.spyOn(pikachu, "getAccuracyMultiplier"); @@ -61,7 +61,7 @@ describe("Abilities - Hustle", () => { it("does not affect non-physical moves", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; + const pikachu = game.field.getPlayerPokemon(); const spatk = pikachu.stats[Stat.SPATK]; vi.spyOn(pikachu, "getEffectiveStat"); @@ -78,8 +78,8 @@ describe("Abilities - Hustle", () => { game.override.startingLevel(100).enemyLevel(30); await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const pikachu = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[MoveId.FISSURE], "calculateBattleAccuracy"); diff --git a/test/abilities/hyper-cutter.test.ts b/test/abilities/hyper-cutter.test.ts index 8a509d026b6..51244532c79 100644 --- a/test/abilities/hyper-cutter.test.ts +++ b/test/abilities/hyper-cutter.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Hyper Cutter", () => { it("only prevents ATK drops", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.OCTOLOCK); await game.toNextTurn(); diff --git a/test/abilities/ice-face.test.ts b/test/abilities/ice-face.test.ts index 9b3020b67a7..2410cd8492f 100644 --- a/test/abilities/ice-face.test.ts +++ b/test/abilities/ice-face.test.ts @@ -44,7 +44,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); @@ -57,7 +57,7 @@ describe("Abilities - Ice Face", () => { game.move.select(MoveId.SURGING_STRIKES); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); // First hit @@ -85,7 +85,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -99,7 +99,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -114,7 +114,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); @@ -134,7 +134,7 @@ describe("Abilities - Ice Face", () => { game.move.select(MoveId.SNOWSCAPE); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon()!; + let eiscue = game.field.getPlayerPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); @@ -146,7 +146,7 @@ describe("Abilities - Ice Face", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to(QuietFormChangePhase); - eiscue = game.scene.getPlayerPokemon()!; + eiscue = game.field.getPlayerPokemon(); expect(eiscue.formIndex).toBe(icefaceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); @@ -158,7 +158,7 @@ describe("Abilities - Ice Face", () => { await game.classicMode.startBattle([SpeciesId.EISCUE]); game.move.select(MoveId.HAIL); - const eiscue = game.scene.getPlayerPokemon()!; + const eiscue = game.field.getPlayerPokemon(); await game.phaseInterceptor.to(QuietFormChangePhase); @@ -179,7 +179,7 @@ describe("Abilities - Ice Face", () => { game.move.select(MoveId.ICE_BEAM); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon()!; + let eiscue = game.field.getPlayerPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); @@ -206,7 +206,7 @@ describe("Abilities - Ice Face", () => { await game.classicMode.startBattle([SpeciesId.EISCUE]); - const eiscue = game.scene.getPlayerPokemon()!; + const eiscue = game.field.getPlayerPokemon(); expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); @@ -229,7 +229,7 @@ describe("Abilities - Ice Face", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.formIndex).toBe(icefaceForm); + expect(game.field.getEnemyPokemon().formIndex).toBe(icefaceForm); }); it("cannot be suppressed", async () => { @@ -241,7 +241,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -257,7 +257,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); @@ -269,10 +269,10 @@ describe("Abilities - Ice Face", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const eiscue = game.scene.getEnemyPokemon()!; + const eiscue = game.field.getEnemyPokemon(); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); - expect(game.scene.getPlayerPokemon()!.hasAbility(AbilityId.TRACE)).toBe(true); + expect(game.field.getPlayerPokemon().hasAbility(AbilityId.TRACE)).toBe(true); }); }); diff --git a/test/abilities/illuminate.test.ts b/test/abilities/illuminate.test.ts index 814b7dc64b7..3cf0bc90702 100644 --- a/test/abilities/illuminate.test.ts +++ b/test/abilities/illuminate.test.ts @@ -33,7 +33,7 @@ describe("Abilities - Illuminate", () => { await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); expect(player.getStatStage(Stat.ACC)).toBe(0); diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index e48cd9e9b78..2343a11cb74 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -35,8 +35,8 @@ describe("Abilities - Illusion", () => { it("creates illusion at the start", async () => { await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.FEEBAS]); - const zoroark = game.scene.getPlayerPokemon()!; - const zorua = game.scene.getEnemyPokemon()!; + const zoroark = game.field.getPlayerPokemon(); + const zorua = game.field.getEnemyPokemon(); expect(!!zoroark.summonData.illusion).equals(true); expect(!!zorua.summonData.illusion).equals(true); @@ -48,7 +48,7 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const zorua = game.scene.getEnemyPokemon()!; + const zorua = game.field.getEnemyPokemon(); expect(!!zorua.summonData.illusion).equals(false); expect(zorua.name).equals("Zorua"); @@ -60,7 +60,7 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const zorua = game.scene.getEnemyPokemon()!; + const zorua = game.field.getEnemyPokemon(); expect(!!zorua.summonData.illusion).equals(false); }); @@ -69,7 +69,7 @@ describe("Abilities - Illusion", () => { game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS); await game.classicMode.startBattle([SpeciesId.KOFFING]); - const zorua = game.scene.getEnemyPokemon()!; + const zorua = game.field.getEnemyPokemon(); expect(!!zorua.summonData.illusion).equals(false); }); @@ -85,15 +85,15 @@ describe("Abilities - Illusion", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.summonData.illusion).toBeFalsy(); + expect(game.field.getPlayerPokemon().summonData.illusion).toBeFalsy(); }); it("causes enemy AI to consider the illusion's type instead of the actual type when considering move effectiveness", async () => { game.override.enemyMoveset([MoveId.FLAMETHROWER, MoveId.PSYCHIC, MoveId.TACKLE]); await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; - const zoroark = game.scene.getPlayerPokemon()!; + const enemy = game.field.getEnemyPokemon(); + const zoroark = game.field.getPlayerPokemon(); const flameThrower = enemy.getMoveset()[0]!.getMove(); const psychic = enemy.getMoveset()[1]!.getMove(); @@ -125,7 +125,7 @@ describe("Abilities - Illusion", () => { await game.move.forceEnemyMove(MoveId.WILL_O_WISP); await game.toEndOfTurn(); - const zoroark = game.scene.getPlayerPokemon()!; + const zoroark = game.field.getPlayerPokemon(); expect(!!zoroark.summonData.illusion).equals(true); }); @@ -143,7 +143,7 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const zoroark = game.scene.getPlayerPokemon()!; + const zoroark = game.field.getPlayerPokemon(); expect(zoroark.summonData.illusion?.name).equals("Axew"); expect(zoroark.getNameToRender(true)).equals("axew nickname"); @@ -155,7 +155,7 @@ describe("Abilities - Illusion", () => { it("breaks when suppressed", async () => { game.override.moveset(MoveId.GASTRO_ACID); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const zorua = game.scene.getEnemyPokemon()!; + const zorua = game.field.getEnemyPokemon(); expect(!!zorua.summonData?.illusion).toBe(true); diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index 9f3678850a1..a093fbbe6c6 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -58,8 +58,8 @@ describe("Abilities - Infiltrator", () => { ])("should bypass the target's $effectName", async ({ tagType, move }) => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const preScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage; @@ -74,8 +74,8 @@ describe("Abilities - Infiltrator", () => { it("should bypass the target's Safeguard", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.scene.arena.addTag(ArenaTagType.SAFEGUARD, 1, MoveId.NONE, enemy.id, ArenaTagSide.ENEMY, true); @@ -90,8 +90,8 @@ describe("Abilities - Infiltrator", () => { it.todo("should bypass the target's Mist", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.scene.arena.addTag(ArenaTagType.MIST, 1, MoveId.NONE, enemy.id, ArenaTagSide.ENEMY, true); @@ -105,8 +105,8 @@ describe("Abilities - Infiltrator", () => { it("should bypass the target's Substitute", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.addTag(BattlerTagType.SUBSTITUTE, 1, MoveId.NONE, enemy.id); diff --git a/test/abilities/intrepid-sword.test.ts b/test/abilities/intrepid-sword.test.ts index 75a1ab14e82..4cd8f5e394b 100644 --- a/test/abilities/intrepid-sword.test.ts +++ b/test/abilities/intrepid-sword.test.ts @@ -32,8 +32,8 @@ describe("Abilities - Intrepid Sword", () => { it("should raise ATK stat stage by 1 on entry", async () => { await game.classicMode.runToSummon([SpeciesId.ZACIAN]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); await game.phaseInterceptor.to(CommandPhase, false); diff --git a/test/abilities/magic-bounce.test.ts b/test/abilities/magic-bounce.test.ts index 3b4185e848f..c15690c3f5d 100644 --- a/test/abilities/magic-bounce.test.ts +++ b/test/abilities/magic-bounce.test.ts @@ -42,7 +42,7 @@ describe("Abilities - Magic Bounce", () => { game.move.use(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should not bounce moves while the target is in the semi-invulnerable state", async () => { @@ -53,7 +53,7 @@ describe("Abilities - Magic Bounce", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should individually bounce back multi-target moves", async () => { @@ -70,12 +70,12 @@ describe("Abilities - Magic Bounce", () => { it("should still bounce back a move that would otherwise fail", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.scene.getEnemyPokemon()?.setStatStage(Stat.ATK, -6); + game.field.getEnemyPokemon().setStatStage(Stat.ATK, -6); game.move.use(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should not bounce back a move that was just bounced", async () => { @@ -85,7 +85,7 @@ describe("Abilities - Magic Bounce", () => { game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should receive the stat change after reflecting a move back to a mirror armor user", async () => { @@ -95,7 +95,7 @@ describe("Abilities - Magic Bounce", () => { game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should not bounce back a move from a mold breaker user", async () => { @@ -105,7 +105,7 @@ describe("Abilities - Magic Bounce", () => { game.move.use(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should bounce back a spread status move against both pokemon", async () => { @@ -156,7 +156,7 @@ describe("Abilities - Magic Bounce", () => { game.move.select(MoveId.CURSE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getTag(BattlerTagType.CURSED)).toBeDefined(); + expect(game.field.getEnemyPokemon().getTag(BattlerTagType.CURSED)).toBeDefined(); }); // TODO: enable when Magic Bounce is fixed to properly reset the hit count @@ -164,8 +164,8 @@ describe("Abilities - Magic Bounce", () => { game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]).enemyMoveset([MoveId.TACKLE, MoveId.GROWL]); // game.override.ability(AbilityId.MOLD_BREAKER); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); // Give the player MOLD_BREAKER for this turn to bypass Magic Bounce. const playerAbilitySpy = game.field.mockAbility(playerPokemon, AbilityId.MOLD_BREAKER); @@ -194,8 +194,8 @@ describe("Abilities - Magic Bounce", () => { .enemyAbility(AbilityId.MAGIC_BOUNCE); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); // turn 1 game.move.select(MoveId.GROWL); @@ -237,7 +237,7 @@ describe("Abilities - Magic Bounce", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM]; - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(stomping_tantrum, "calculateBattlePower"); // Spore gets reflected back onto us @@ -260,35 +260,35 @@ describe("Abilities - Magic Bounce", () => { // Turn 1 - thunder wave immunity test game.move.select(MoveId.THUNDER_WAVE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); // Turn 2 - soundproof immunity test game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should bounce back a move before the accuracy check", async () => { game.override.moveset([MoveId.SPORE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const attacker = game.scene.getPlayerPokemon()!; + const attacker = game.field.getPlayerPokemon(); vi.spyOn(attacker, "getAccuracyMultiplier").mockReturnValue(0.0); game.move.select(MoveId.SPORE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.SLEEP); + expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.SLEEP); }); it("should take the accuracy of the magic bounce user into account", async () => { game.override.moveset([MoveId.SPORE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const opponent = game.scene.getEnemyPokemon()!; + const opponent = game.field.getEnemyPokemon(); vi.spyOn(opponent, "getAccuracyMultiplier").mockReturnValue(0); game.move.select(MoveId.SPORE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); }); it("should always apply the leftmost available target's magic bounce when bouncing moves like sticky webs in doubles", async () => { @@ -332,14 +332,14 @@ describe("Abilities - Magic Bounce", () => { await game.move.selectEnemyMove(MoveId.FLY); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.TOXIC); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); game.override.ability(AbilityId.NO_GUARD); game.move.select(MoveId.CHARM); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-2); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-2); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); }); diff --git a/test/abilities/mimicry.test.ts b/test/abilities/mimicry.test.ts index c04860d1fcd..44416387f6e 100644 --- a/test/abilities/mimicry.test.ts +++ b/test/abilities/mimicry.test.ts @@ -47,44 +47,44 @@ describe("Abilities - Mimicry", () => { }); it("Pokemon should revert back to its original, root type once terrain ends", async () => { - game.override - .moveset([MoveId.SPLASH, MoveId.TRANSFORM]) - .enemyAbility(AbilityId.MIMICRY) - .enemyMoveset([MoveId.SPLASH, MoveId.PSYCHIC_TERRAIN]); + game.override.enemyAbility(AbilityId.MIMICRY); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon(); - game.move.select(MoveId.TRANSFORM); - await game.move.selectEnemyMove(MoveId.PSYCHIC_TERRAIN); + const playerPokemon = game.field.getPlayerPokemon(); + + game.move.use(MoveId.SKILL_SWAP); + await game.move.forceEnemyMove(MoveId.PSYCHIC_TERRAIN); await game.toNextTurn(); - expect(playerPokemon?.getTypes().includes(PokemonType.PSYCHIC)).toBe(true); + + expect(playerPokemon.getTypes()).toEqual([PokemonType.PSYCHIC]); if (game.scene.arena.terrain) { game.scene.arena.terrain.turnsLeft = 1; } - game.move.select(MoveId.SPLASH); - await game.move.selectEnemyMove(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(playerPokemon?.getTypes().includes(PokemonType.ELECTRIC)).toBe(true); + + expect(playerPokemon.getTypes()).toEqual([PokemonType.ELECTRIC]); }); it("If the Pokemon is under the effect of a type-adding move and an equivalent terrain activates, the move's effect disappears", async () => { game.override.enemyMoveset([MoveId.FORESTS_CURSE, MoveId.GRASSY_TERRAIN]); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.FORESTS_CURSE); await game.toNextTurn(); - expect(playerPokemon?.summonData.addedType).toBe(PokemonType.GRASS); + expect(playerPokemon.summonData.addedType).toBe(PokemonType.GRASS); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.GRASSY_TERRAIN); await game.phaseInterceptor.to("TurnEndPhase"); - expect(playerPokemon?.summonData.addedType).toBeNull(); - expect(playerPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true); + expect(playerPokemon.summonData.addedType).toBeNull(); + expect(playerPokemon.getTypes()).toEqual([PokemonType.GRASS]); }); }); diff --git a/test/abilities/mirror-armor.test.ts b/test/abilities/mirror-armor.test.ts index b2f9c9dc8fa..b2bd9be4755 100644 --- a/test/abilities/mirror-armor.test.ts +++ b/test/abilities/mirror-armor.test.ts @@ -40,8 +40,8 @@ describe("Ability - Mirror Armor", () => { game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate, enemy should lose -1 atk game.move.select(MoveId.SPLASH); @@ -56,8 +56,8 @@ describe("Ability - Mirror Armor", () => { game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate, enemy should lose -1 atk game.move.select(MoveId.SPLASH); @@ -112,8 +112,8 @@ describe("Ability - Mirror Armor", () => { game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense game.move.select(MoveId.SPLASH); @@ -153,8 +153,8 @@ describe("Ability - Mirror Armor", () => { game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense game.move.select(MoveId.TICKLE); @@ -171,8 +171,8 @@ describe("Ability - Mirror Armor", () => { game.override.enemyAbility(AbilityId.WHITE_SMOKE).ability(AbilityId.MIRROR_ARMOR); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats game.move.select(MoveId.SPLASH); @@ -189,8 +189,8 @@ describe("Ability - Mirror Armor", () => { game.override.ability(AbilityId.WHITE_SMOKE).enemyAbility(AbilityId.MIRROR_ARMOR); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats game.move.select(MoveId.TICKLE); @@ -207,8 +207,8 @@ describe("Ability - Mirror Armor", () => { game.override.ability(AbilityId.MIRROR_ARMOR); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Enemy uses octolock, player loses stats at end of turn game.move.select(MoveId.SPLASH); @@ -225,8 +225,8 @@ describe("Ability - Mirror Armor", () => { game.override.enemyAbility(AbilityId.MIRROR_ARMOR); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); // Player uses octolock, enemy loses stats at end of turn game.move.select(MoveId.OCTOLOCK); @@ -243,8 +243,8 @@ describe("Ability - Mirror Armor", () => { game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.SPLASH, BattlerIndex.PLAYER); @@ -258,8 +258,8 @@ describe("Ability - Mirror Armor", () => { game.override.ability(AbilityId.MIRROR_ARMOR); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const userPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const userPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.STICKY_WEB, BattlerIndex.PLAYER); diff --git a/test/abilities/moody.test.ts b/test/abilities/moody.test.ts index 80879837bce..d1f8aa2e351 100644 --- a/test/abilities/moody.test.ts +++ b/test/abilities/moody.test.ts @@ -35,7 +35,7 @@ describe("Abilities - Moody", () => { it("should increase one stat stage by 2 and decrease a different stat stage by 1", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.toNextTurn(); @@ -52,7 +52,7 @@ describe("Abilities - Moody", () => { it("should only increase one stat stage by 2 if all stat stages are at -6", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // Set all stat stages to -6 vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(-6)); @@ -70,7 +70,7 @@ describe("Abilities - Moody", () => { it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // Set all stat stages to 6 vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6)); diff --git a/test/abilities/moxie.test.ts b/test/abilities/moxie.test.ts index 28b90042969..042a8ddd058 100644 --- a/test/abilities/moxie.test.ts +++ b/test/abilities/moxie.test.ts @@ -41,7 +41,7 @@ describe("Abilities - Moxie", () => { const moveToUse = MoveId.AERIAL_ACE; await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.MIGHTYENA]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); diff --git a/test/abilities/mummy.test.ts b/test/abilities/mummy.test.ts index e3843f9c112..57105bc3468 100644 --- a/test/abilities/mummy.test.ts +++ b/test/abilities/mummy.test.ts @@ -37,7 +37,7 @@ describe("Abilities - Mummy", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.MUMMY); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.MUMMY); }); it("should not change the enemy's ability hit by a non-contact move", async () => { @@ -47,6 +47,6 @@ describe("Abilities - Mummy", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); }); }); diff --git a/test/abilities/mycelium-might.test.ts b/test/abilities/mycelium-might.test.ts index 41da6b5c693..c3b7b4753b6 100644 --- a/test/abilities/mycelium-might.test.ts +++ b/test/abilities/mycelium-might.test.ts @@ -42,12 +42,12 @@ describe("Abilities - Mycelium Might", () => { * https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24 */ - it("will move last in its priority bracket and ignore protective abilities", async () => { + it("should move last in its priority bracket and ignore protective abilities", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); - const enemyIndex = enemyPokemon?.getBattlerIndex(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = enemyPokemon.getBattlerIndex(); game.move.select(MoveId.BABY_DOLL_EYES); @@ -62,16 +62,16 @@ describe("Abilities - Mycelium Might", () => { await game.phaseInterceptor.to(TurnEndPhase); // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); }); - it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { + it("should still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => { game.override.enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex(); - const enemyIndex = enemyPokemon?.getBattlerIndex(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = enemyPokemon.getBattlerIndex(); game.move.select(MoveId.BABY_DOLL_EYES); @@ -85,14 +85,14 @@ describe("Abilities - Mycelium Might", () => { expect(commandOrder).toEqual([playerIndex, enemyIndex]); await game.phaseInterceptor.to(TurnEndPhase); // Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced. - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); }); - it("will not affect non-status moves", async () => { + it("should not affect non-status moves", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex(); game.move.select(MoveId.QUICK_ATTACK); diff --git a/test/abilities/neutralizing-gas.test.ts b/test/abilities/neutralizing-gas.test.ts index 560b7af72e7..555e5f8a19c 100644 --- a/test/abilities/neutralizing-gas.test.ts +++ b/test/abilities/neutralizing-gas.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Neutralizing Gas", () => { await game.phaseInterceptor.to("TurnEndPhase"); // Intimidate is suppressed, so the attack stat should not be lowered - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should allow the user's passive to activate", async () => { @@ -56,7 +56,7 @@ describe("Abilities - Neutralizing Gas", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(1); }); it.todo("should activate before other abilities", async () => { @@ -68,7 +68,7 @@ describe("Abilities - Neutralizing Gas", () => { await game.phaseInterceptor.to("TurnEndPhase"); // Intimidate is suppressed even when the user's speed is lower - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should activate other abilities when removed", async () => { @@ -79,15 +79,15 @@ describe("Abilities - Neutralizing Gas", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(0); - expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(0); + const enemyPokemon = game.field.getEnemyPokemon(); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); // Enemy removes user's ability, so both abilities are activated - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(1); - expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1); + expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(1); }); it("should not activate the user's other ability when removed", async () => { @@ -95,13 +95,13 @@ describe("Abilities - Neutralizing Gas", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); // Neutralising gas user's passive is still active - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); + const enemyPokemon = game.field.getEnemyPokemon(); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); // Intimidate did not reactivate after neutralizing gas was removed - expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1); + expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); }); it("should only deactivate when all setters are off the field", async () => { @@ -164,7 +164,7 @@ describe("Abilities - Neutralizing Gas", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); - vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); + vi.spyOn(game.field.getPlayerPokemon(), "randBattleSeedInt").mockReturnValue(0); vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0); const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; @@ -178,7 +178,7 @@ describe("Abilities - Neutralizing Gas", () => { game.override.battleStyle("single").ability(AbilityId.NEUTRALIZING_GAS).enemyAbility(AbilityId.DELTA_STREAM); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const weatherChangeAttr = enemy.getAbilityAttrs("PostSummonWeatherChangeAbAttr", false)[0]; const weatherChangeSpy = vi.spyOn(weatherChangeAttr, "apply"); @@ -186,7 +186,7 @@ describe("Abilities - Neutralizing Gas", () => { game.move.select(MoveId.SPLASH); await game.killPokemon(enemy); - await game.killPokemon(game.scene.getPlayerPokemon()!); + await game.killPokemon(game.field.getPlayerPokemon()); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); expect(weatherChangeSpy).not.toHaveBeenCalled(); diff --git a/test/abilities/normal-move-type-change.test.ts b/test/abilities/normal-move-type-change.test.ts index fdf9ef0f9f2..11ea7a2e5f0 100644 --- a/test/abilities/normal-move-type-change.test.ts +++ b/test/abilities/normal-move-type-change.test.ts @@ -58,10 +58,10 @@ describe.each([ it(`should change Normal-type attacks to ${tyName} type and boost their power`, async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const typeSpy = vi.spyOn(playerPokemon, "getMoveType"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemySpy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); const powerSpy = vi.spyOn(allMoves[MoveId.TACKLE], "calculateBattlePower"); @@ -103,10 +103,10 @@ describe.each([ await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const tySpy = vi.spyOn(playerPokemon, "getMoveType"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyEffectivenessSpy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); enemyPokemon.hp = Math.floor(enemyPokemon.getMaxHp() * 0.8); @@ -137,7 +137,7 @@ describe.each([ await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const tySpy = vi.spyOn(playerPokemon, "getMoveType"); game.move.select(move); @@ -149,10 +149,10 @@ describe.each([ it("should affect all hits of a Normal-type multi-hit move", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const tySpy = vi.spyOn(playerPokemon, "getMoveType"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FURY_SWIPES); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -183,7 +183,7 @@ describe.each([ expect(boost, "power boost should be defined").toBeDefined(); const powerSpy = vi.spyOn(testMoveInstance, "calculateBattlePower"); - const typeSpy = vi.spyOn(game.scene.getPlayerPokemon()!, "getMoveType"); + const typeSpy = vi.spyOn(game.field.getPlayerPokemon(), "getMoveType"); game.move.select(MoveId.TACKLE); await game.phaseInterceptor.to("BerryPhase", false); expect(typeSpy, "type was not changed").toHaveLastReturnedWith(ty); diff --git a/test/abilities/oblivious.test.ts b/test/abilities/oblivious.test.ts index 6b9598903c5..02056251992 100644 --- a/test/abilities/oblivious.test.ts +++ b/test/abilities/oblivious.test.ts @@ -39,14 +39,14 @@ describe("Abilities - Oblivious", () => { .moveset(MoveId.SKILL_SWAP) .enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon(); - enemy?.addTag(BattlerTagType.TAUNT); - expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeTruthy(); + const enemy = game.field.getEnemyPokemon(); + enemy.addTag(BattlerTagType.TAUNT); + expect(enemy.getTag(BattlerTagType.TAUNT)).toBeDefined(); game.move.select(MoveId.SKILL_SWAP); await game.phaseInterceptor.to("BerryPhase"); - expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeFalsy(); + expect(enemy.getTag(BattlerTagType.TAUNT)).toBeUndefined(); }); it("should remove infatuation when gained", async () => { @@ -56,14 +56,15 @@ describe("Abilities - Oblivious", () => { .moveset(MoveId.SKILL_SWAP) .enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon(); - vi.spyOn(enemy!, "isOppositeGender").mockReturnValue(true); - enemy?.addTag(BattlerTagType.INFATUATED, 5, MoveId.JUDGMENT, game.scene.getPlayerPokemon()?.id); // sourceID needs to be defined - expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeTruthy(); + + const enemy = game.field.getEnemyPokemon(); + vi.spyOn(enemy, "isOppositeGender").mockReturnValue(true); + enemy.addTag(BattlerTagType.INFATUATED, 5, MoveId.JUDGMENT, game.field.getPlayerPokemon().id); // sourceID needs to be defined + expect(enemy.getTag(BattlerTagType.INFATUATED)).toBeTruthy(); game.move.select(MoveId.SKILL_SWAP); await game.phaseInterceptor.to("BerryPhase"); - expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeFalsy(); + expect(enemy.getTag(BattlerTagType.INFATUATED)).toBeFalsy(); }); }); diff --git a/test/abilities/parental-bond.test.ts b/test/abilities/parental-bond.test.ts index 51c15ea32b1..a72fc82260f 100644 --- a/test/abilities/parental-bond.test.ts +++ b/test/abilities/parental-bond.test.ts @@ -42,8 +42,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); let enemyStartingHp = enemyPokemon.hp; @@ -66,7 +66,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.POWER_UP_PUNCH); @@ -81,7 +81,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BABY_DOLL_EYES); @@ -95,7 +95,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.DOUBLE_HIT); await game.move.forceHit(); @@ -110,7 +110,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SELF_DESTRUCT); @@ -124,7 +124,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.ROLLOUT); await game.move.forceHit(); @@ -139,7 +139,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DRAGON_RAGE); await game.phaseInterceptor.to("BerryPhase", false); @@ -152,8 +152,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.COUNTER); await game.phaseInterceptor.to("DamageAnimPhase"); @@ -185,7 +185,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.EARTHQUAKE); await game.phaseInterceptor.to("DamageAnimPhase", false); @@ -199,7 +199,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.MIND_BLOWN); @@ -218,8 +218,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BURN_UP); @@ -239,7 +239,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.HYPER_BEAM); await game.move.forceHit(); @@ -259,8 +259,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ANCHOR_SHOT); await game.move.forceHit(); @@ -283,8 +283,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SMACK_DOWN); await game.move.forceHit(); @@ -304,7 +304,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.U_TURN); await game.move.forceHit(); @@ -321,8 +321,8 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.WAKE_UP_SLAP); await game.move.forceHit(); @@ -342,7 +342,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.TACKLE); @@ -356,7 +356,7 @@ describe("Abilities - Parental Bond", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.WATER_GUN); @@ -369,7 +369,7 @@ describe("Abilities - Parental Bond", () => { game.override.enemyLevel(1000).moveset(MoveId.FUTURE_SIGHT); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "damageAndUpdate"); game.move.select(MoveId.FUTURE_SIGHT); diff --git a/test/abilities/perish-body.test.ts b/test/abilities/perish-body.test.ts index 251d29abe36..50ba2f86c39 100644 --- a/test/abilities/perish-body.test.ts +++ b/test/abilities/perish-body.test.ts @@ -34,55 +34,55 @@ describe("Abilities - Perish Song", () => { it("should trigger when hit with damaging move", async () => { await game.classicMode.startBattle(); - const cursola = game.scene.getPlayerPokemon(); - const magikarp = game.scene.getEnemyPokemon(); + const cursola = game.field.getPlayerPokemon(); + const magikarp = game.field.getEnemyPokemon(); game.move.select(MoveId.SPLASH); await game.toNextTurn(); - expect(cursola?.summonData.tags[0].turnCount).toBe(3); - expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + expect(cursola.summonData.tags[0].turnCount).toBe(3); + expect(magikarp.summonData.tags[0].turnCount).toBe(3); }); it("should trigger even when fainting", async () => { game.override.enemyLevel(100).startingLevel(1); await game.classicMode.startBattle([SpeciesId.CURSOLA, SpeciesId.FEEBAS]); - const magikarp = game.scene.getEnemyPokemon(); + const magikarp = game.field.getEnemyPokemon(); game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); await game.toNextTurn(); - expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + expect(magikarp.summonData.tags[0].turnCount).toBe(3); }); it("should not activate if attacker already has perish song", async () => { game.override.enemyMoveset([MoveId.PERISH_SONG, MoveId.AQUA_JET, MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.CURSOLA]); - const feebas = game.scene.getPlayerPokemon(); - const magikarp = game.scene.getEnemyPokemon(); + const feebas = game.field.getPlayerPokemon(); + const magikarp = game.field.getEnemyPokemon(); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.PERISH_SONG); await game.toNextTurn(); - expect(feebas?.summonData.tags[0].turnCount).toBe(3); - expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + expect(feebas.summonData.tags[0].turnCount).toBe(3); + expect(magikarp.summonData.tags[0].turnCount).toBe(3); game.doSwitchPokemon(1); await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - const cursola = game.scene.getPlayerPokemon(); - expect(cursola?.summonData.tags.length).toBe(0); - expect(magikarp?.summonData.tags[0].turnCount).toBe(2); + const cursola = game.field.getPlayerPokemon(); + expect(cursola.summonData.tags.length).toBe(0); + expect(magikarp.summonData.tags[0].turnCount).toBe(2); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.AQUA_JET); await game.toNextTurn(); - expect(cursola?.summonData.tags.length).toBe(0); - expect(magikarp?.summonData.tags[0].turnCount).toBe(1); + expect(cursola.summonData.tags.length).toBe(0); + expect(magikarp.summonData.tags[0].turnCount).toBe(1); }); it("should activate if cursola already has perish song, but not reset its counter", async () => { @@ -91,22 +91,22 @@ describe("Abilities - Perish Song", () => { .moveset([MoveId.WHIRLWIND, MoveId.SPLASH]) .startingWave(5); await game.classicMode.startBattle([SpeciesId.CURSOLA]); - const cursola = game.scene.getPlayerPokemon(); + const cursola = game.field.getPlayerPokemon(); game.move.select(MoveId.WHIRLWIND); await game.move.selectEnemyMove(MoveId.PERISH_SONG); await game.toNextTurn(); - const magikarp = game.scene.getEnemyPokemon(); - expect(cursola?.summonData.tags[0].turnCount).toBe(3); - expect(magikarp?.summonData.tags.length).toBe(0); + const magikarp = game.field.getEnemyPokemon(); + expect(cursola.summonData.tags[0].turnCount).toBe(3); + expect(magikarp.summonData.tags.length).toBe(0); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.AQUA_JET); await game.toNextTurn(); - expect(cursola?.summonData.tags[0].turnCount).toBe(2); - expect(magikarp?.summonData.tags.length).toBe(1); - expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + expect(cursola.summonData.tags[0].turnCount).toBe(2); + expect(magikarp.summonData.tags.length).toBe(1); + expect(magikarp.summonData.tags[0].turnCount).toBe(3); }); }); diff --git a/test/abilities/protosynthesis.test.ts b/test/abilities/protosynthesis.test.ts index f4461a562ea..ea2e1e20c17 100644 --- a/test/abilities/protosynthesis.test.ts +++ b/test/abilities/protosynthesis.test.ts @@ -42,10 +42,10 @@ describe("Abilities - Protosynthesis", () => { .startingLevel(100) .enemyLevel(100); await game.classicMode.startBattle([SpeciesId.MEW]); - const mew = game.scene.getPlayerPokemon()!; + const mew = game.field.getPlayerPokemon(); // Nature of starting mon is randomized. We need to fix it to a neutral nature for the automated test. mew.setNature(Nature.HARDY); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const def_before_boost = mew.getEffectiveStat( Stat.DEF, undefined, diff --git a/test/abilities/quick-draw.test.ts b/test/abilities/quick-draw.test.ts index d3914e24268..ce5873af3a8 100644 --- a/test/abilities/quick-draw.test.ts +++ b/test/abilities/quick-draw.test.ts @@ -43,8 +43,8 @@ describe("Abilities - Quick Draw", () => { test("makes pokemon going first in its priority bracket", async () => { await game.classicMode.startBattle(); - const pokemon = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pokemon = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); pokemon.hp = 1; enemy.hp = 1; @@ -65,8 +65,8 @@ describe("Abilities - Quick Draw", () => { async () => { await game.classicMode.startBattle(); - const pokemon = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pokemon = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); pokemon.hp = 1; enemy.hp = 1; @@ -85,8 +85,8 @@ describe("Abilities - Quick Draw", () => { await game.classicMode.startBattle(); - const pokemon = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pokemon = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); pokemon.hp = 1; enemy.hp = 1; diff --git a/test/abilities/sap-sipper.test.ts b/test/abilities/sap-sipper.test.ts index a1c034ab126..95f9841d924 100644 --- a/test/abilities/sap-sipper.test.ts +++ b/test/abilities/sap-sipper.test.ts @@ -45,7 +45,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); @@ -63,7 +63,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(moveToUse); @@ -86,7 +86,7 @@ describe("Abilities - Sap Sipper", () => { expect(game.scene.arena.terrain).toBeDefined(); expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("activate once against multi-hit grass attacks", async () => { @@ -96,7 +96,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); @@ -114,7 +114,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); @@ -140,7 +140,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const initialEnemyHp = enemyPokemon.hp; game.move.select(moveToUse); @@ -156,7 +156,7 @@ describe("Abilities - Sap Sipper", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.LEAF_BLADE); await game.phaseInterceptor.to("MoveEffectPhase"); diff --git a/test/abilities/shield-dust.test.ts b/test/abilities/shield-dust.test.ts index 025b415dbc0..a34056e00e9 100644 --- a/test/abilities/shield-dust.test.ts +++ b/test/abilities/shield-dust.test.ts @@ -38,8 +38,8 @@ describe("Abilities - Shield Dust", () => { it("Shield Dust", async () => { await game.classicMode.startBattle([SpeciesId.PIDGEOT]); - game.scene.getEnemyPokemon()!.stats[Stat.SPDEF] = 10000; - expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0); + game.field.getEnemyPokemon().stats[Stat.SPDEF] = 10000; + expect(game.field.getPlayerPokemon().formIndex).toBe(0); game.move.select(MoveId.AIR_SLASH); diff --git a/test/abilities/shields-down.test.ts b/test/abilities/shields-down.test.ts index 0323a4afbec..98a1cfffa8e 100644 --- a/test/abilities/shields-down.test.ts +++ b/test/abilities/shields-down.test.ts @@ -66,7 +66,7 @@ describe("Abilities - SHIELDS DOWN", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.status).toBe(undefined); + expect(game.field.getPlayerPokemon().status).toBe(undefined); }); test("should still ignore non-volatile status moves used by a pokemon with mold breaker", async () => { @@ -78,7 +78,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.move.selectEnemyMove(MoveId.SPORE); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.status).toBe(undefined); + expect(game.field.getPlayerPokemon().status).toBe(undefined); }); test("should ignore non-volatile secondary status effects", async () => { @@ -89,7 +89,7 @@ describe("Abilities - SHIELDS DOWN", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.status).toBe(undefined); + expect(game.field.getPlayerPokemon().status).toBe(undefined); }); test("should ignore status moves even through mold breaker", async () => { @@ -101,7 +101,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.status).toBe(undefined); + expect(game.field.getPlayerPokemon().status).toBe(undefined); }); // toxic spikes currently does not poison flying types when gravity is in effect @@ -122,9 +122,9 @@ describe("Abilities - SHIELDS DOWN", () => { await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.MINIOR); - expect(game.scene.getPlayerPokemon()!.species.formIndex).toBe(0); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.MINIOR); + expect(game.field.getPlayerPokemon().species.formIndex).toBe(0); + expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.POISON); }); test("should ignore yawn", async () => { @@ -136,7 +136,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.move.selectEnemyMove(MoveId.YAWN); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.findTag(tag => tag.tagType === BattlerTagType.DROWSY)).toBe(undefined); + expect(game.field.getPlayerPokemon().findTag(tag => tag.tagType === BattlerTagType.DROWSY)).toBe(undefined); }); test("should not ignore volatile status effects", async () => { @@ -149,7 +149,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getPlayerPokemon()!.findTag(tag => tag.tagType === BattlerTagType.CONFUSED)).not.toBe(undefined); + expect(game.field.getPlayerPokemon().findTag(tag => tag.tagType === BattlerTagType.CONFUSED)).not.toBe(undefined); }); // the `NoTransformAbilityAbAttr` attribute is not checked anywhere, so this test cannot pass. @@ -162,7 +162,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.move.selectEnemyMove(MoveId.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP); }); test("should not prevent minior from receiving the fainted status effect in trainer battles", async () => { @@ -173,7 +173,7 @@ describe("Abilities - SHIELDS DOWN", () => { .startingWave(5) .enemySpecies(SpeciesId.MINIOR); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const minior = game.scene.getEnemyPokemon()!; + const minior = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); await game.toNextTurn(); diff --git a/test/abilities/simple.test.ts b/test/abilities/simple.test.ts index 39f1b579a19..fe065e37cfe 100644 --- a/test/abilities/simple.test.ts +++ b/test/abilities/simple.test.ts @@ -33,7 +33,7 @@ describe("Abilities - Simple", () => { it("should double stat changes when applied", async () => { await game.classicMode.startBattle([SpeciesId.SLOWBRO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2); }); diff --git a/test/abilities/speed-boost.test.ts b/test/abilities/speed-boost.test.ts index ff0beaf74a0..b087be29460 100644 --- a/test/abilities/speed-boost.test.ts +++ b/test/abilities/speed-boost.test.ts @@ -40,7 +40,7 @@ describe("Abilities - Speed Boost", () => { it("should increase speed by 1 stage at end of turn", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.toNextTurn(); @@ -53,7 +53,7 @@ describe("Abilities - Speed Boost", () => { game.move.select(MoveId.U_TURN); game.doSelectPartyPokemon(1); await game.toNextTurn(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.SPLASH); @@ -69,13 +69,13 @@ describe("Abilities - Speed Boost", () => { game.move.select(MoveId.U_TURN); game.doSelectPartyPokemon(1); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!).toBe(ninjask); + expect(game.field.getPlayerPokemon()).toBe(ninjask); expect(ninjask.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.U_TURN); game.doSelectPartyPokemon(1); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!).toBe(shuckle); + expect(game.field.getPlayerPokemon()).toBe(shuckle); expect(shuckle.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.SPLASH); @@ -88,7 +88,7 @@ describe("Abilities - Speed Boost", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.SPLASH); @@ -109,7 +109,7 @@ describe("Abilities - Speed Boost", () => { await game.phaseInterceptor.to(AttemptRunPhase); await game.toNextTurn(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/stall.test.ts b/test/abilities/stall.test.ts index 71796d376a3..5b4e38f7099 100644 --- a/test/abilities/stall.test.ts +++ b/test/abilities/stall.test.ts @@ -40,8 +40,8 @@ describe("Abilities - Stall", () => { it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex(); game.move.select(MoveId.QUICK_ATTACK); @@ -58,8 +58,8 @@ describe("Abilities - Stall", () => { it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex(); game.move.select(MoveId.TACKLE); @@ -77,8 +77,8 @@ describe("Abilities - Stall", () => { game.override.ability(AbilityId.STALL); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex(); - const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex(); + const playerIndex = game.field.getPlayerPokemon().getBattlerIndex(); + const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex(); game.move.select(MoveId.TACKLE); diff --git a/test/abilities/steely-spirit.test.ts b/test/abilities/steely-spirit.test.ts index 072566fdb96..5fbb11a4bfd 100644 --- a/test/abilities/steely-spirit.test.ts +++ b/test/abilities/steely-spirit.test.ts @@ -39,7 +39,7 @@ describe("Abilities - Steely Spirit", () => { it("increases Steel-type moves' power used by the user and its allies by 50%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon()!; + const enemyToCheck = game.field.getEnemyPokemon(); vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]); @@ -54,7 +54,7 @@ describe("Abilities - Steely Spirit", () => { it("stacks if multiple users with this ability are on the field.", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.PIKACHU]); - const enemyToCheck = game.scene.getEnemyPokemon()!; + const enemyToCheck = game.field.getEnemyPokemon(); game.scene.getPlayerField().forEach(p => { vi.spyOn(p, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]); @@ -74,7 +74,7 @@ describe("Abilities - Steely Spirit", () => { it("does not take effect when suppressed", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon()!; + const enemyToCheck = game.field.getEnemyPokemon(); vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]); expect(boostSource.hasAbility(AbilityId.STEELY_SPIRIT)).toBe(true); diff --git a/test/abilities/super-luck.test.ts b/test/abilities/super-luck.test.ts index a0f5293b036..fd6e3d75f76 100644 --- a/test/abilities/super-luck.test.ts +++ b/test/abilities/super-luck.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Super Luck", () => { it("should increase the user's crit stage by 1", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const critSpy = vi.spyOn(enemy, "getCritStage"); // crit stage is called on enemy game.move.select(MoveId.TACKLE); diff --git a/test/abilities/synchronize.test.ts b/test/abilities/synchronize.test.ts index c95ae1b7828..048f9663db9 100644 --- a/test/abilities/synchronize.test.ts +++ b/test/abilities/synchronize.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Synchronize", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); }); @@ -48,8 +48,8 @@ describe("Abilities - Synchronize", () => { game.move.select(MoveId.THUNDER_WAVE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS); + expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.PARALYSIS); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.PARALYSIS); expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase"); }); @@ -60,8 +60,8 @@ describe("Abilities - Synchronize", () => { await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined(); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP); + expect(game.field.getPlayerPokemon().status?.effect).toBeUndefined(); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP); expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); }); @@ -76,8 +76,8 @@ describe("Abilities - Synchronize", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBeUndefined(); + expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.POISON); + expect(game.field.getEnemyPokemon().status?.effect).toBeUndefined(); expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase"); }); @@ -87,8 +87,8 @@ describe("Abilities - Synchronize", () => { game.move.select(MoveId.THUNDER_WAVE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined(); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS); + expect(game.field.getPlayerPokemon().status?.effect).toBeUndefined(); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.PARALYSIS); expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase"); }); }); diff --git a/test/abilities/tera-shell.test.ts b/test/abilities/tera-shell.test.ts index 385fabe1a54..4183cd4d0a6 100644 --- a/test/abilities/tera-shell.test.ts +++ b/test/abilities/tera-shell.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Tera Shell", () => { it("should change the effectiveness of non-resisted attacks when the source is at full HP", async () => { await game.classicMode.startBattle([SpeciesId.SNORLAX]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SPLASH); @@ -57,7 +57,7 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([SpeciesId.SNORLAX]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SPLASH); @@ -71,7 +71,7 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([SpeciesId.AGGRON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SPLASH); @@ -85,7 +85,7 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SPLASH); @@ -100,7 +100,7 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([SpeciesId.SNORLAX]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SPLASH); diff --git a/test/abilities/trace.test.ts b/test/abilities/trace.test.ts index 4211234a451..3a4809a4e7e 100644 --- a/test/abilities/trace.test.ts +++ b/test/abilities/trace.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Trace", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); }); it("should activate a copied post-summon ability", async () => { @@ -48,6 +48,6 @@ describe("Abilities - Trace", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 686a6c20904..c10dd404ab9 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -64,7 +64,7 @@ describe("Abilities - Unburden", () => { game.override.enemyMoveset(MoveId.FALSE_SWIPE); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -80,7 +80,7 @@ describe("Abilities - Unburden", () => { game.override.enemyMoveset(MoveId.FALSE_SWIPE).startingModifier([{ name: "BERRY_POUCH", count: 5850 }]); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -95,9 +95,9 @@ describe("Abilities - Unburden", () => { it("should activate for the target, and not the stealer, when a berry is stolen", async () => { await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyHeldItemCt = getHeldItemCount(enemyPokemon); const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD); @@ -113,7 +113,7 @@ describe("Abilities - Unburden", () => { it("should activate when an item is knocked off", async () => { await game.classicMode.startBattle([SpeciesId.TREECKO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyHeldItemCt = getHeldItemCount(enemyPokemon); const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD); @@ -129,7 +129,7 @@ describe("Abilities - Unburden", () => { game.override.ability(AbilityId.MAGICIAN).startingHeldItems([]); // Remove player's full stacks of held items so it can steal opponent's held items await game.classicMode.startBattle([SpeciesId.TREECKO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyHeldItemCt = getHeldItemCount(enemyPokemon); const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD); @@ -145,7 +145,7 @@ describe("Abilities - Unburden", () => { game.override.enemyAbility(AbilityId.PICKPOCKET).enemyHeldItems([]); // Remove opponent's full stacks of held items so it can steal player's held items await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -161,7 +161,7 @@ describe("Abilities - Unburden", () => { game.override.moveset(MoveId.THIEF).startingHeldItems([]); // Remove player's full stacks of held items so it can steal opponent's held items await game.classicMode.startBattle([SpeciesId.TREECKO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyHeldItemCt = getHeldItemCount(enemyPokemon); const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD); @@ -177,11 +177,11 @@ describe("Abilities - Unburden", () => { game.override.startingHeldItems([{ name: "GRIP_CLAW", count: 1 }]); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier; vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyHeldItemCt = getHeldItemCount(enemyPokemon); const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD); @@ -197,7 +197,7 @@ describe("Abilities - Unburden", () => { game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS).enemyMoveset(MoveId.FALSE_SWIPE); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -214,7 +214,7 @@ describe("Abilities - Unburden", () => { game.override.moveset(MoveId.STUFF_CHEEKS); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItemCt = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -292,7 +292,7 @@ describe("Abilities - Unburden", () => { game.override.enemyMoveset([MoveId.FALSE_SWIPE, MoveId.WORRY_SEED]); await game.classicMode.startBattle([SpeciesId.PURRLOIN]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -317,7 +317,7 @@ describe("Abilities - Unburden", () => { game.override.startingHeldItems([{ name: "REVIVER_SEED" }]).enemyMoveset([MoveId.WING_ATTACK]); await game.classicMode.startBattle([SpeciesId.TREECKO]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerHeldItems = getHeldItemCount(playerPokemon); const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD); @@ -334,7 +334,7 @@ describe("Abilities - Unburden", () => { game.override.enemyMoveset([MoveId.SPLASH, MoveId.THIEF]); await game.classicMode.startBattle([SpeciesId.TREECKO, SpeciesId.FEEBAS]); - const treecko = game.scene.getPlayerPokemon()!; + const treecko = game.field.getPlayerPokemon(); const treeckoInitialHeldItems = getHeldItemCount(treecko); const initialSpeed = treecko.getStat(Stat.SPD); @@ -348,7 +348,7 @@ describe("Abilities - Unburden", () => { await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!).toBe(treecko); + expect(game.field.getPlayerPokemon()).toBe(treecko); expect(getHeldItemCount(treecko)).toBeLessThan(treeckoInitialHeldItems); expect(treecko.getEffectiveStat(Stat.SPD)).toBe(initialSpeed); }); diff --git a/test/abilities/unseen-fist.test.ts b/test/abilities/unseen-fist.test.ts index 01b573fe66c..3c0fa3f8c60 100644 --- a/test/abilities/unseen-fist.test.ts +++ b/test/abilities/unseen-fist.test.ts @@ -55,7 +55,7 @@ describe("Abilities - Unseen Fist", () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, enemyPokemon.id); game.move.select(MoveId.TACKLE); @@ -77,10 +77,10 @@ async function testUnseenFistHitResult( await game.classicMode.startBattle(); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; diff --git a/test/abilities/volt-absorb.test.ts b/test/abilities/volt-absorb.test.ts index 921d56f075b..9ba76028703 100644 --- a/test/abilities/volt-absorb.test.ts +++ b/test/abilities/volt-absorb.test.ts @@ -42,7 +42,7 @@ describe("Abilities - Volt Absorb", () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); @@ -62,7 +62,7 @@ describe("Abilities - Volt Absorb", () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); enemyPokemon.hp = enemyPokemon.hp - 1; @@ -83,7 +83,7 @@ describe("Abilities - Volt Absorb", () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); enemyPokemon.hp = enemyPokemon.hp - 1; diff --git a/test/abilities/wandering-spirit.test.ts b/test/abilities/wandering-spirit.test.ts index 63c2550b198..44e2cd61236 100644 --- a/test/abilities/wandering-spirit.test.ts +++ b/test/abilities/wandering-spirit.test.ts @@ -38,8 +38,8 @@ describe("Abilities - Wandering Spirit", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.WANDERING_SPIRIT); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.WANDERING_SPIRIT); }); it("should not exchange abilities when hit with a non-contact move", async () => { @@ -49,8 +49,8 @@ describe("Abilities - Wandering Spirit", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.WANDERING_SPIRIT); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.WANDERING_SPIRIT); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); }); it("should activate post-summon abilities", async () => { @@ -60,6 +60,6 @@ describe("Abilities - Wandering Spirit", () => { game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/abilities/wimp-out.test.ts b/test/abilities/wimp-out.test.ts index a1c19a12fd4..1e129f34a19 100644 --- a/test/abilities/wimp-out.test.ts +++ b/test/abilities/wimp-out.test.ts @@ -70,7 +70,7 @@ describe("Abilities - Wimp Out", () => { game.override.passiveAbility(AbilityId.REGENERATOR).startingLevel(5).enemyLevel(100); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const wimpod = game.scene.getPlayerPokemon()!; + const wimpod = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -84,7 +84,7 @@ describe("Abilities - Wimp Out", () => { game.override.enemyAbility(AbilityId.WIMP_OUT); await game.classicMode.startBattle([SpeciesId.GOLISOPOD, SpeciesId.TYRUNT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.hp *= 0.52; game.move.select(MoveId.FALSE_SWIPE); @@ -97,7 +97,7 @@ describe("Abilities - Wimp Out", () => { it("Does not trigger when HP already below half", async () => { await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const wimpod = game.scene.getPlayerPokemon()!; + const wimpod = game.field.getPlayerPokemon(); wimpod.hp = 5; game.move.select(MoveId.SPLASH); @@ -117,7 +117,7 @@ describe("Abilities - Wimp Out", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()!.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); + expect(game.field.getPlayerPokemon().getTag(BattlerTagType.TRAPPED)).toBeUndefined(); expect(game.scene.getPlayerParty()[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined(); confirmSwitch(); }); @@ -130,7 +130,7 @@ describe("Abilities - Wimp Out", () => { game.doSelectPartyPokemon(1); await game.phaseInterceptor.to("TurnEndPhase"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const hasFled = enemyPokemon.switchOutStatus; expect(hasFled).toBe(false); confirmSwitch(); @@ -139,17 +139,17 @@ describe("Abilities - Wimp Out", () => { it("If this Ability does not activate due to being hit by U-turn or Volt Switch, the user of that move will be switched out.", async () => { game.override.startingLevel(190).startingWave(8).enemyMoveset([MoveId.U_TURN]); await game.classicMode.startBattle([SpeciesId.GOLISOPOD, SpeciesId.TYRUNT]); - const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id; + const RIVAL_NINJASK1 = game.field.getEnemyPokemon().id; game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase", false); - expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1); + expect(game.field.getEnemyPokemon().id !== RIVAL_NINJASK1); }); it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", async () => { game.override.startingLevel(69).enemyMoveset([MoveId.DRAGON_TAIL]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const wimpod = game.scene.getPlayerPokemon()!; + const wimpod = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -159,7 +159,7 @@ describe("Abilities - Wimp Out", () => { await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(SpeciesId.WIMPOD); + expect(game.field.getPlayerPokemon().species.speciesId).not.toBe(SpeciesId.WIMPOD); }); it("triggers when recoil damage is taken", async () => { @@ -177,7 +177,7 @@ describe("Abilities - Wimp Out", () => { game.override.moveset([MoveId.SUBSTITUTE]).enemyMoveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const wimpod = game.scene.getPlayerPokemon()!; + const wimpod = game.field.getPlayerPokemon(); wimpod.hp *= 0.52; game.move.select(MoveId.SUBSTITUTE); @@ -208,7 +208,7 @@ describe("Abilities - Wimp Out", () => { .startingHeldItems([{ name: "SHELL_BELL", count: 4 }]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const wimpod = game.scene.getPlayerPokemon()!; + const wimpod = game.field.getPlayerPokemon(); wimpod.damageAndUpdate(toDmgValue(wimpod.getMaxHp() * 0.4)); @@ -219,7 +219,7 @@ describe("Abilities - Wimp Out", () => { expect(game.scene.getPlayerParty()[1]).toBe(wimpod); expect(wimpod.hp).toBeGreaterThan(toDmgValue(wimpod.getMaxHp() / 2)); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.TYRUNT); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.TYRUNT); }, ); @@ -227,7 +227,7 @@ describe("Abilities - Wimp Out", () => { game.override.weather(WeatherType.HAIL).enemyMoveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -240,7 +240,7 @@ describe("Abilities - Wimp Out", () => { game.override.enemyAbility(AbilityId.SHEER_FORCE).enemyMoveset(MoveId.SLUDGE_BOMB).startingLevel(95); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.ENDURE); await game.phaseInterceptor.to("TurnEndPhase"); @@ -252,7 +252,7 @@ describe("Abilities - Wimp Out", () => { game.override.statusEffect(StatusEffect.POISON).enemyMoveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -265,7 +265,7 @@ describe("Abilities - Wimp Out", () => { game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(AbilityId.BAD_DREAMS); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.52; + game.field.getPlayerPokemon().hp *= 0.52; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -277,7 +277,7 @@ describe("Abilities - Wimp Out", () => { it("Wimp Out will activate due to leech seed", async () => { game.override.enemyMoveset([MoveId.LEECH_SEED]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.52; + game.field.getPlayerPokemon().hp *= 0.52; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -289,7 +289,7 @@ describe("Abilities - Wimp Out", () => { it("Wimp Out will activate due to curse damage", async () => { game.override.enemySpecies(SpeciesId.DUSKNOIR).enemyMoveset([MoveId.CURSE]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.52; + game.field.getPlayerPokemon().hp *= 0.52; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -301,7 +301,7 @@ describe("Abilities - Wimp Out", () => { it("Wimp Out will activate due to salt cure damage", async () => { game.override.enemySpecies(SpeciesId.NACLI).enemyMoveset([MoveId.SALT_CURE]).enemyLevel(1); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.7; + game.field.getPlayerPokemon().hp *= 0.7; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -313,7 +313,7 @@ describe("Abilities - Wimp Out", () => { it("Wimp Out will activate due to damaging trap damage", async () => { game.override.enemySpecies(SpeciesId.MAGIKARP).enemyMoveset([MoveId.WHIRLPOOL]).enemyLevel(1); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.55; + game.field.getPlayerPokemon().hp *= 0.55; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -331,14 +331,14 @@ describe("Abilities - Wimp Out", () => { .weather(WeatherType.HAIL) .statusEffect(StatusEffect.POISON); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); expect(game.scene.getPlayerParty()[0].getHpRatio()).toEqual(0.51); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.WIMPOD); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.WIMPOD); }); it("Wimp Out activating should not cancel a double battle", async () => { @@ -369,7 +369,7 @@ describe("Abilities - Wimp Out", () => { .enemyMoveset([MoveId.SPLASH]) .enemyLevel(1); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.THUNDER_PUNCH); game.doSelectPartyPokemon(1); @@ -391,7 +391,7 @@ describe("Abilities - Wimp Out", () => { it("Wimp Out will activate due to Nightmare", async () => { game.override.enemyMoveset([MoveId.NIGHTMARE]).statusEffect(StatusEffect.SLEEP); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.65; + game.field.getPlayerPokemon().hp *= 0.65; game.move.select(MoveId.SPLASH); game.doSelectPartyPokemon(1); @@ -417,13 +417,13 @@ describe("Abilities - Wimp Out", () => { game.override.enemyMoveset(MoveId.BULLET_SEED).enemyAbility(AbilityId.SKILL_LINK); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.ENDURE); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to("TurnEndPhase"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.turnData.hitsLeft).toBe(0); expect(enemyPokemon.turnData.hitCount).toBe(5); confirmSwitch(); @@ -433,13 +433,13 @@ describe("Abilities - Wimp Out", () => { game.override.enemyMoveset(MoveId.TACKLE).enemyHeldItems([{ name: "MULTI_LENS", count: 1 }]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.ENDURE); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to("TurnEndPhase"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.turnData.hitsLeft).toBe(0); expect(enemyPokemon.turnData.hitCount).toBe(2); confirmSwitch(); @@ -448,13 +448,13 @@ describe("Abilities - Wimp Out", () => { game.override.enemyMoveset(MoveId.TACKLE).enemyAbility(AbilityId.PARENTAL_BOND); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - game.scene.getPlayerPokemon()!.hp *= 0.51; + game.field.getPlayerPokemon().hp *= 0.51; game.move.select(MoveId.ENDURE); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to("TurnEndPhase"); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.turnData.hitsLeft).toBe(0); expect(enemyPokemon.turnData.hitCount).toBe(2); confirmSwitch(); @@ -466,7 +466,7 @@ describe("Abilities - Wimp Out", () => { async () => { game.override.moveset([MoveId.SWORDS_DANCE]).enemyMoveset([MoveId.SWAGGER]); await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.hp *= 0.51; playerPokemon.setStatStage(Stat.ATK, 6); playerPokemon.addTag(BattlerTagType.CONFUSED); @@ -486,7 +486,7 @@ describe("Abilities - Wimp Out", () => { game.override.enemyAbility(AbilityId.WIMP_OUT).startingLevel(5850).startingWave(10); await game.classicMode.startBattle([SpeciesId.GOLISOPOD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // Use 2 turns of False Swipe due to opponent's health bar shield game.move.select(MoveId.FALSE_SWIPE); diff --git a/test/abilities/wind-power.test.ts b/test/abilities/wind-power.test.ts index 377a8052e13..5aaec25e19a 100644 --- a/test/abilities/wind-power.test.ts +++ b/test/abilities/wind-power.test.ts @@ -33,7 +33,7 @@ describe("Abilities - Wind Power", () => { it("becomes charged when hit by wind moves", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon()!; + const shiftry = game.field.getEnemyPokemon(); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -47,7 +47,7 @@ describe("Abilities - Wind Power", () => { game.override.ability(AbilityId.WIND_POWER).enemySpecies(SpeciesId.MAGIKARP); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon()!; + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -61,8 +61,8 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_POWER); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon()!; - const shiftry = game.scene.getPlayerPokemon()!; + const magikarp = game.field.getEnemyPokemon(); + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -79,7 +79,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(SpeciesId.MAGIKARP); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon()!; + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); diff --git a/test/abilities/wind-rider.test.ts b/test/abilities/wind-rider.test.ts index be30acb0f64..9480bb437fb 100644 --- a/test/abilities/wind-rider.test.ts +++ b/test/abilities/wind-rider.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Wind Rider", () => { it("takes no damage from wind moves and its ATK stat stage is raised by 1 when hit by one", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon()!; + const shiftry = game.field.getEnemyPokemon(); expect(shiftry.getStatStage(Stat.ATK)).toBe(0); @@ -48,7 +48,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon()!; + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getStatStage(Stat.ATK)).toBe(0); @@ -63,8 +63,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon()!; - const shiftry = game.scene.getPlayerPokemon()!; + const magikarp = game.field.getEnemyPokemon(); + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getStatStage(Stat.ATK)).toBe(0); expect(magikarp.getStatStage(Stat.ATK)).toBe(0); @@ -81,8 +81,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon()!; - const shiftry = game.scene.getPlayerPokemon()!; + const magikarp = game.field.getEnemyPokemon(); + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getStatStage(Stat.ATK)).toBe(0); expect(magikarp.getStatStage(Stat.ATK)).toBe(0); @@ -99,7 +99,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(SpeciesId.MAGIKARP); await game.classicMode.startBattle([SpeciesId.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon()!; + const shiftry = game.field.getPlayerPokemon(); expect(shiftry.getStatStage(Stat.ATK)).toBe(0); expect(shiftry.isFullHp()).toBe(true); diff --git a/test/abilities/zen-mode.test.ts b/test/abilities/zen-mode.test.ts index 24d53bda7b6..f3ea7d78398 100644 --- a/test/abilities/zen-mode.test.ts +++ b/test/abilities/zen-mode.test.ts @@ -38,7 +38,7 @@ describe("Abilities - ZEN MODE", () => { it("shouldn't change form when taking damage if not dropping below 50% HP", async () => { await game.classicMode.startBattle([SpeciesId.DARMANITAN]); - const darmanitan = game.scene.getPlayerPokemon()!; + const darmanitan = game.field.getPlayerPokemon(); expect(darmanitan.formIndex).toBe(baseForm); game.move.select(MoveId.SPLASH); @@ -52,7 +52,7 @@ describe("Abilities - ZEN MODE", () => { it("should change form when falling below 50% HP", async () => { await game.classicMode.startBattle([SpeciesId.DARMANITAN]); - const darmanitan = game.scene.getPlayerPokemon()!; + const darmanitan = game.field.getPlayerPokemon(); darmanitan.hp = darmanitan.getMaxHp() / 2 + 1; expect(darmanitan.formIndex).toBe(baseForm); @@ -65,7 +65,7 @@ describe("Abilities - ZEN MODE", () => { it("should stay zen mode when fainted", async () => { await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]); - const darmanitan = game.scene.getPlayerPokemon()!; + const darmanitan = game.field.getPlayerPokemon(); darmanitan.hp = darmanitan.getMaxHp() / 2 + 1; expect(darmanitan.formIndex).toBe(baseForm); diff --git a/test/abilities/zero-to-hero.test.ts b/test/abilities/zero-to-hero.test.ts index cb0fe75d11b..d9fa3580da2 100644 --- a/test/abilities/zero-to-hero.test.ts +++ b/test/abilities/zero-to-hero.test.ts @@ -62,7 +62,7 @@ describe("Abilities - ZERO TO HERO", () => { it("should swap to Hero form when switching out during a battle", async () => { await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]); - const palafin = game.scene.getPlayerPokemon()!; + const palafin = game.field.getPlayerPokemon(); expect(palafin.formIndex).toBe(baseForm); game.doSwitchPokemon(1); @@ -73,7 +73,7 @@ describe("Abilities - ZERO TO HERO", () => { it("should not swap to Hero form if switching due to faint", async () => { await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]); - const palafin = game.scene.getPlayerPokemon()!; + const palafin = game.field.getPlayerPokemon(); expect(palafin.formIndex).toBe(baseForm); game.move.select(MoveId.SPLASH); @@ -90,7 +90,7 @@ describe("Abilities - ZERO TO HERO", () => { await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]); - const palafin = game.scene.getPlayerPokemon()!; + const palafin = game.field.getPlayerPokemon(); expect(palafin.formIndex).toBe(heroForm); game.move.select(MoveId.SPLASH); diff --git a/test/arena/arena-gravity.test.ts b/test/arena/arena-gravity.test.ts index b08dcf0c9b0..02b49d7711f 100644 --- a/test/arena/arena-gravity.test.ts +++ b/test/arena/arena-gravity.test.ts @@ -84,7 +84,7 @@ describe("Arena - Gravity", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pidgeot = game.scene.getEnemyPokemon()!; + const pidgeot = game.field.getEnemyPokemon(); vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); // Try earthquake on 1st turn (fails!); @@ -113,7 +113,7 @@ describe("Arena - Gravity", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pidgeot = game.scene.getEnemyPokemon()!; + const pidgeot = game.field.getEnemyPokemon(); vi.spyOn(pidgeot, "getAttackTypeEffectiveness"); // Setup Gravity on 1st turn @@ -136,8 +136,8 @@ describe("Arena - Gravity", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const charizard = game.scene.getPlayerPokemon()!; - const snorlax = game.scene.getEnemyPokemon()!; + const charizard = game.field.getPlayerPokemon(); + const snorlax = game.field.getEnemyPokemon(); game.move.select(MoveId.SPLASH); diff --git a/test/arena/psychic-terrain.test.ts b/test/arena/psychic-terrain.test.ts index 82232cd8d05..6d42ed0d3ac 100644 --- a/test/arena/psychic-terrain.test.ts +++ b/test/arena/psychic-terrain.test.ts @@ -42,7 +42,7 @@ describe("Arena - Psychic Terrain", () => { game.move.select(MoveId.DARK_VOID); await game.toEndOfTurn(); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP); }); it("Rain Dance with Prankster is not blocked", async () => { diff --git a/test/arena/weather-hail.test.ts b/test/arena/weather-hail.test.ts index b3ff8de3d42..edc01b3348f 100644 --- a/test/arena/weather-hail.test.ts +++ b/test/arena/weather-hail.test.ts @@ -52,8 +52,8 @@ describe("Weather - Hail", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1)); @@ -66,8 +66,8 @@ describe("Weather - Hail", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1)); diff --git a/test/arena/weather-sandstorm.test.ts b/test/arena/weather-sandstorm.test.ts index f5cf44458c5..69c741d53c5 100644 --- a/test/arena/weather-sandstorm.test.ts +++ b/test/arena/weather-sandstorm.test.ts @@ -51,8 +51,8 @@ describe("Weather - Sandstorm", () => { await game.phaseInterceptor.to("TurnEndPhase"); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1)); @@ -80,11 +80,11 @@ describe("Weather - Sandstorm", () => { it("increases Rock type Pokemon Sp.Def by 50%", async () => { await game.classicMode.startBattle([SpeciesId.ROCKRUFF]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerSpdef = playerPokemon.getStat(Stat.SPDEF); expect(playerPokemon.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(playerSpdef * 1.5)); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemySpdef = enemyPokemon.getStat(Stat.SPDEF); expect(enemyPokemon.getEffectiveStat(Stat.SPDEF)).toBe(enemySpdef); }); diff --git a/test/arena/weather-strong-winds.test.ts b/test/arena/weather-strong-winds.test.ts index 8d2d1ee0a75..1800027f59c 100644 --- a/test/arena/weather-strong-winds.test.ts +++ b/test/arena/weather-strong-winds.test.ts @@ -36,8 +36,8 @@ describe("Weather - Strong Winds", () => { game.override.enemySpecies(SpeciesId.RAYQUAZA); await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pikachu = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); @@ -47,8 +47,8 @@ describe("Weather - Strong Winds", () => { it("electric type move is neutral for flying type pokemon", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pikachu = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDERBOLT); @@ -58,8 +58,8 @@ describe("Weather - Strong Winds", () => { it("ice type move is neutral for flying type pokemon", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pikachu = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.ICE_BEAM); @@ -69,8 +69,8 @@ describe("Weather - Strong Winds", () => { it("rock type move is neutral for flying type pokemon", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const pikachu = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.ROCK_SLIDE); @@ -83,7 +83,7 @@ describe("Weather - Strong Winds", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1; game.move.use(MoveId.SPLASH); diff --git a/test/battle/ability-swap.test.ts b/test/battle/ability-swap.test.ts index 4ce60e1f0b2..6d8ae8b5551 100644 --- a/test/battle/ability-swap.test.ts +++ b/test/battle/ability-swap.test.ts @@ -37,10 +37,10 @@ describe("Test Ability Swapping", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); game.move.select(MoveId.SPLASH); - game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.INTIMIDATE]); + game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.INTIMIDATE]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should remove primal weather when the setter's ability is removed", async () => { @@ -48,7 +48,7 @@ describe("Test Ability Swapping", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); game.move.select(MoveId.SPLASH); - game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.BALL_FETCH]); + game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.BALL_FETCH]); await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.arena.weather?.weatherType).toBeUndefined(); @@ -59,10 +59,10 @@ describe("Test Ability Swapping", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); game.move.select(MoveId.SPLASH); - game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.BALL_FETCH]); + game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.BALL_FETCH]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1); // would be 2 if passive activated again + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(1); // would be 2 if passive activated again }); // Pickup and Honey Gather are special cases as they're the only abilities to be Unsuppressable but not Unswappable @@ -73,6 +73,6 @@ describe("Test Ability Swapping", () => { game.move.select(MoveId.ROLE_PLAY); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/battle/battle-order.test.ts b/test/battle/battle-order.test.ts index 47afb582a5a..0ee23cd6418 100644 --- a/test/battle/battle-order.test.ts +++ b/test/battle/battle-order.test.ts @@ -35,8 +35,8 @@ describe("Battle order", () => { it("opponent faster than player 50 vs 150", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50 vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 @@ -54,8 +54,8 @@ describe("Battle order", () => { it("Player faster than opponent 150 vs 50", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150 vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50 diff --git a/test/battle/battle.test.ts b/test/battle/battle.test.ts index ff5090e5f8d..3dd154cf4eb 100644 --- a/test/battle/battle.test.ts +++ b/test/battle/battle.test.ts @@ -293,7 +293,7 @@ describe("Phase - Battle Phase", () => { .startingHeldItems([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]); await game.classicMode.startBattle(); - game.scene.getPlayerPokemon()!.hp = 1; + game.field.getPlayerPokemon().hp = 1; game.move.select(moveToUse); await game.phaseInterceptor.to(BattleEndPhase); diff --git a/test/battle/damage-calculation.test.ts b/test/battle/damage-calculation.test.ts index ca01830abd0..fac7c624f99 100644 --- a/test/battle/damage-calculation.test.ts +++ b/test/battle/damage-calculation.test.ts @@ -38,10 +38,10 @@ describe("Battle Mechanics - Damage Calculation", () => { it("Tackle deals expected base damage", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); vi.spyOn(playerPokemon, "getEffectiveStat").mockReturnValue(80); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getEffectiveStat").mockReturnValue(90); // expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2 @@ -56,7 +56,7 @@ describe("Battle Mechanics - Damage Calculation", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const aggron = game.scene.getEnemyPokemon()!; + const aggron = game.field.getEnemyPokemon(); game.move.select(MoveId.TACKLE); @@ -75,7 +75,7 @@ describe("Battle Mechanics - Damage Calculation", () => { dmg_redux_modifier.stackCount = 1000; await game.scene.addEnemyModifier(modifierTypes.ENEMY_DAMAGE_REDUCTION().newModifier() as EnemyPersistentModifier); - const aggron = game.scene.getEnemyPokemon()!; + const aggron = game.field.getEnemyPokemon(); game.move.select(MoveId.TACKLE); @@ -89,8 +89,8 @@ describe("Battle Mechanics - Damage Calculation", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const magikarp = game.scene.getPlayerPokemon()!; - const dragonite = game.scene.getEnemyPokemon()!; + const magikarp = game.field.getPlayerPokemon(); + const dragonite = game.field.getEnemyPokemon(); expect(dragonite.getAttackDamage({ source: magikarp, move: allMoves[MoveId.DRAGON_RAGE] }).damage).toBe(40); }); @@ -100,8 +100,8 @@ describe("Battle Mechanics - Damage Calculation", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const magikarp = game.scene.getPlayerPokemon()!; - const aggron = game.scene.getEnemyPokemon()!; + const magikarp = game.field.getPlayerPokemon(); + const aggron = game.field.getEnemyPokemon(); expect(aggron.getAttackDamage({ source: magikarp, move: allMoves[MoveId.FISSURE] }).damage).toBe(aggron.hp); }); @@ -111,7 +111,7 @@ describe("Battle Mechanics - Damage Calculation", () => { await game.classicMode.startBattle([SpeciesId.SHEDINJA]); - const shedinja = game.scene.getPlayerPokemon()!; + const shedinja = game.field.getPlayerPokemon(); game.move.select(MoveId.JUMP_KICK); @@ -126,7 +126,7 @@ describe("Battle Mechanics - Damage Calculation", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const charizard = game.scene.getEnemyPokemon()!; + const charizard = game.field.getEnemyPokemon(); if (charizard.getMaxHp() % 2 === 1) { expect(charizard.hp).toBeGreaterThan(charizard.getMaxHp() / 2); diff --git a/test/battle/inverse-battle.test.ts b/test/battle/inverse-battle.test.ts index 9ba2df9bc44..66a21e80009 100644 --- a/test/battle/inverse-battle.test.ts +++ b/test/battle/inverse-battle.test.ts @@ -43,7 +43,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.THUNDERBOLT); @@ -58,7 +58,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.THUNDERBOLT); @@ -73,7 +73,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.THUNDERBOLT); @@ -89,7 +89,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const charizard = game.scene.getEnemyPokemon()!; + const charizard = game.field.getEnemyPokemon(); const maxHp = charizard.getMaxHp(); const damage_prediction = Math.max(Math.round(charizard.getMaxHp() / 32), 1); @@ -111,7 +111,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -126,7 +126,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = enemy.getMaxHp() - 1; game.move.select(MoveId.WATER_GUN); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -140,7 +140,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.WILL_O_WISP); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -155,7 +155,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.NUZZLE); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -169,7 +169,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -184,7 +184,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - expect(game.scene.getEnemyPokemon()?.waveData.abilitiesApplied).toContain(AbilityId.ANTICIPATION); + expect(game.field.getEnemyPokemon().waveData.abilitiesApplied).toContain(AbilityId.ANTICIPATION); }); it("Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => { @@ -192,7 +192,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); game.move.select(MoveId.CONVERSION_2); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -207,7 +207,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FLYING_PRESS); @@ -222,7 +222,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.TACKLE); @@ -237,7 +237,7 @@ describe("Inverse Battle", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FORESIGHT); diff --git a/test/boss-pokemon.test.ts b/test/boss-pokemon.test.ts index 0a7c71c95e7..b0dfbf19794 100644 --- a/test/boss-pokemon.test.ts +++ b/test/boss-pokemon.test.ts @@ -80,7 +80,7 @@ describe("Boss Pokemon / Shields", () => { await game.classicMode.startBattle([SpeciesId.MEWTWO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const segmentHp = enemyPokemon.getMaxHp() / enemyPokemon.bossSegments; expect(enemyPokemon.isBoss()).toBe(true); expect(enemyPokemon.bossSegments).toBe(3); @@ -191,7 +191,7 @@ describe("Boss Pokemon / Shields", () => { await game.classicMode.startBattle([SpeciesId.MEWTWO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.isBoss()).toBe(true); expect(enemyPokemon.bossSegments).toBe(2); expect(getTotalStatStageBoosts(enemyPokemon)).toBe(0); diff --git a/test/challenges/hardcore.test.ts b/test/challenges/hardcore.test.ts new file mode 100644 index 00000000000..0f4ab1b9f02 --- /dev/null +++ b/test/challenges/hardcore.test.ts @@ -0,0 +1,169 @@ +import { Status } from "#data/status-effect"; +import { AbilityId } from "#enums/ability-id"; +import { Button } from "#enums/buttons"; +import { Challenges } from "#enums/challenges"; +import { MoveId } from "#enums/move-id"; +import { ShopCursorTarget } from "#enums/shop-cursor-target"; +import { SpeciesId } from "#enums/species-id"; +import { StatusEffect } from "#enums/status-effect"; +import { UiMode } from "#enums/ui-mode"; +import { GameManager } from "#test/test-utils/game-manager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Challenges - Hardcore", () => { + 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.challengeMode.addChallenge(Challenges.HARDCORE, 1, 1); + game.override + .battleStyle("single") + .enemySpecies(SpeciesId.VOLTORB) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH) + .moveset(MoveId.SPLASH); + }); + + it("should render Revival Blessing unusable by players only", async () => { + game.override.enemyMoveset(MoveId.REVIVAL_BLESSING).moveset(MoveId.REVIVAL_BLESSING); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + const player = game.field.getPlayerPokemon(); + const revBlessing = player.getMoveset()[0]; + expect(revBlessing.isUsable(player)).toBe(false); + + game.move.select(MoveId.REVIVAL_BLESSING); + await game.toEndOfTurn(); + + // Player struggled due to only move being the unusable Revival Blessing + expect(player).toHaveUsedMove(MoveId.STRUGGLE); + expect(game.field.getEnemyPokemon()).toHaveUsedMove(MoveId.REVIVAL_BLESSING); + }); + + it("prevents REVIVE items in shop and in wave rewards", async () => { + game.override.startingWave(181).startingLevel(200); + await game.challengeMode.startBattle(); + + game.move.select(MoveId.SPLASH); + await game.doKillOpponents(); + + await game.phaseInterceptor.to("SelectModifierPhase"); + expect(game.scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); + const modifierSelectHandler = game.scene.ui.handlers.find( + h => h instanceof ModifierSelectUiHandler, + ) as ModifierSelectUiHandler; + expect( + modifierSelectHandler.options.find(reward => reward.modifierTypeOption.type.group === "revive"), + ).toBeUndefined(); + expect( + modifierSelectHandler.shopOptionsRows.find(row => + row.find(item => item.modifierTypeOption.type.group === "revive"), + ), + ).toBeUndefined(); + }); + + it("prevents the automatic party heal from reviving fainted Pokémon", async () => { + game.override.startingWave(10).startingLevel(200); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF, SpeciesId.WHISMUR]); + + const faintedPokemon = game.scene.getPlayerParty()[1]; + faintedPokemon.hp = 0; + faintedPokemon.status = new Status(StatusEffect.FAINT); + expect(faintedPokemon.isFainted()).toBe(true); + + game.move.select(MoveId.SPLASH); + await game.doKillOpponents(); + + await game.toNextWave(); + + expect(faintedPokemon.isFainted()).toBe(true); + }); + + // TODO: Couldn't figure out how to select party Pokémon + it.skip("prevents fusion with a fainted Pokémon", async () => { + game.override.itemRewards([{ name: "DNA_SPLICERS" }]); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF, SpeciesId.WHISMUR]); + + const faintedPokemon = game.scene.getPlayerParty()[1]; + faintedPokemon.hp = 0; + faintedPokemon.status = new Status(StatusEffect.FAINT); + expect(faintedPokemon.isFainted()).toBe(true); + + game.move.select(MoveId.RAZOR_LEAF); + await game.doKillOpponents(); + + await game.phaseInterceptor.to("SelectModifierPhase"); + game.onNextPrompt( + "SelectModifierPhase", + UiMode.MODIFIER_SELECT, + () => { + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to and select first modifier + handler.setCursor(0); + handler.setRowCursor(ShopCursorTarget.REWARDS); + handler.processInput(Button.ACTION); + + // Go to fainted Pokémon and try to select it + handler.processInput(Button.RIGHT); + handler.processInput(Button.ACTION); + handler.processInput(Button.ACTION); + handler.processInput(Button.ACTION); + + expect(game.scene.getPlayerParty().length).toBe(2); + }, + () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), + true, + ); + }); + + // TODO: Couldn't figure out how to select party Pokémon + it.skip("prevents fainted Pokémon from being revived", async () => { + game.override.itemRewards([{ name: "MAX_REVIVE" }]); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF, SpeciesId.WHISMUR]); + + const faintedPokemon = game.scene.getPlayerParty()[1]; + faintedPokemon.hp = 0; + faintedPokemon.status = new Status(StatusEffect.FAINT); + expect(faintedPokemon.isFainted()).toBe(true); + + game.move.select(MoveId.RAZOR_LEAF); + await game.doKillOpponents(); + + await game.phaseInterceptor.to("SelectModifierPhase"); + game.onNextPrompt( + "SelectModifierPhase", + UiMode.MODIFIER_SELECT, + () => { + const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to and select first modifier + handler.setCursor(0); + handler.setRowCursor(ShopCursorTarget.REWARDS); + handler.processInput(Button.ACTION); + + // Go to fainted Pokémon and try to select it + handler.processInput(Button.RIGHT); + handler.processInput(Button.ACTION); + handler.processInput(Button.ACTION); + handler.processInput(Button.ACTION); + + expect(faintedPokemon.isFainted()).toBe(true); + }, + () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), + true, + ); + }); +}); diff --git a/test/challenges/limited-catch.test.ts b/test/challenges/limited-catch.test.ts new file mode 100644 index 00000000000..80be52df2fb --- /dev/null +++ b/test/challenges/limited-catch.test.ts @@ -0,0 +1,55 @@ +import { AbilityId } from "#enums/ability-id"; +import { Challenges } from "#enums/challenges"; +import { MoveId } from "#enums/move-id"; +import { PokeballType } from "#enums/pokeball"; +import { SpeciesId } from "#enums/species-id"; +import { GameManager } from "#test/test-utils/game-manager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Challenges - Limited Catch", () => { + 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.challengeMode.addChallenge(Challenges.LIMITED_CATCH, 1, 1); + game.override + .battleStyle("single") + .enemySpecies(SpeciesId.VOLTORB) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH) + .startingModifier([{ name: "MASTER_BALL", count: 1 }]); + }); + + it("should allow wild Pokémon to be caught on X1 waves", async () => { + game.override.startingWave(31); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + game.doThrowPokeball(PokeballType.MASTER_BALL); + await game.toEndOfTurn(); + + expect(game.scene.getPlayerParty()).toHaveLength(2); + }); + + it("should prevent Pokémon from being caught on non-X1 waves", async () => { + game.override.startingWave(53); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + game.doThrowPokeball(PokeballType.MASTER_BALL); + await game.toEndOfTurn(); + + expect(game.scene.getPlayerParty()).toHaveLength(1); + }); +}); diff --git a/test/challenges/limited-support.test.ts b/test/challenges/limited-support.test.ts new file mode 100644 index 00000000000..5c0eb2bd420 --- /dev/null +++ b/test/challenges/limited-support.test.ts @@ -0,0 +1,90 @@ +import { AbilityId } from "#enums/ability-id"; +import { Challenges } from "#enums/challenges"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { UiMode } from "#enums/ui-mode"; +import { GameManager } from "#test/test-utils/game-manager"; +import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Challenges - Limited Support", () => { + 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 + .battleStyle("single") + .enemySpecies(SpeciesId.VOLTORB) + .enemyAbility(AbilityId.BALL_FETCH) + .enemyMoveset(MoveId.SPLASH); + }); + + it('should disable the shop in "No Shop"', async () => { + game.override.startingWave(181); + game.challengeMode.addChallenge(Challenges.LIMITED_SUPPORT, 2, 1); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + game.move.use(MoveId.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("SelectModifierPhase"); + + expect(game.scene.ui.getMode()).toBe(UiMode.MODIFIER_SELECT); + const modifierSelectHandler = game.scene.ui.handlers.find( + h => h instanceof ModifierSelectUiHandler, + ) as ModifierSelectUiHandler; + expect(modifierSelectHandler.shopOptionsRows).toHaveLength(0); + }); + + it('should disable the automatic party heal in "No Heal"', async () => { + game.override.startingWave(10); + game.challengeMode.addChallenge(Challenges.LIMITED_SUPPORT, 1, 1); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + const playerPokemon = game.field.getPlayerPokemon(); + playerPokemon.hp /= 2; + + game.move.use(MoveId.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + expect(playerPokemon).not.toHaveFullHp(); + }); + + it('should disable both automatic party healing and shop in "Both"', async () => { + game.override.startingWave(10); + game.challengeMode.addChallenge(Challenges.LIMITED_SUPPORT, 3, 1); + await game.challengeMode.startBattle([SpeciesId.NUZLEAF]); + + const playerPokemon = game.field.getPlayerPokemon(); + playerPokemon.hp /= 2; + + game.move.use(MoveId.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + expect(playerPokemon).not.toHaveFullHp(); + + game.move.use(MoveId.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("SelectModifierPhase"); + + expect(game.scene.ui.getMode()).toBe(UiMode.MODIFIER_SELECT); + const modifierSelectHandler = game.scene.ui.handlers.find( + h => h instanceof ModifierSelectUiHandler, + ) as ModifierSelectUiHandler; + expect(modifierSelectHandler.shopOptionsRows).toHaveLength(0); + }); +}); diff --git a/test/data/status-effect.test.ts b/test/data/status-effect.test.ts index ebfd9066b3c..20e164ce4d7 100644 --- a/test/data/status-effect.test.ts +++ b/test/data/status-effect.test.ts @@ -331,8 +331,8 @@ describe("Status Effects", () => { await game.move.forceStatusActivation(true); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.isFullHp()).toBe(true); - expect(game.scene.getPlayerPokemon()!.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); + expect(game.field.getEnemyPokemon().isFullHp()).toBe(true); + expect(game.field.getPlayerPokemon().getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); }); }); @@ -365,7 +365,7 @@ describe("Status Effects", () => { it("should last the appropriate number of turns", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.status = new Status(StatusEffect.SLEEP, 0, 4); game.move.select(MoveId.SPLASH); @@ -422,7 +422,7 @@ describe("Status Effects", () => { it("should not inflict a 0 HP mon with a status", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MILOTIC]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.hp = 0; expect(player.trySetStatus(StatusEffect.BURN)).toBe(false); diff --git a/test/endless-boss.test.ts b/test/endless-boss.test.ts index 707d164d4d0..2a4ac9ddc7d 100644 --- a/test/endless-boss.test.ts +++ b/test/endless-boss.test.ts @@ -34,10 +34,10 @@ describe("Endless Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); - expect(eternatus?.formIndex).toBe(0); + const eternatus = game.field.getEnemyPokemon(); + expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + expect(eternatus.formIndex).toBe(0); }); it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Endless`, async () => { @@ -46,10 +46,10 @@ describe("Endless Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); - expect(eternatus?.formIndex).toBe(1); + const eternatus = game.field.getEnemyPokemon(); + expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + expect(eternatus.formIndex).toBe(1); }); it(`should spawn a minor boss every ${EndlessBossWave.Minor} waves in END biome in Spliced Endless`, async () => { @@ -58,10 +58,10 @@ describe("Endless Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Minor); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); - expect(eternatus?.formIndex).toBe(0); + const eternatus = game.field.getEnemyPokemon(); + expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + expect(eternatus.formIndex).toBe(0); }); it(`should spawn a major boss every ${EndlessBossWave.Major} waves in END biome in Spliced Endless`, async () => { @@ -70,10 +70,10 @@ describe("Endless Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(EndlessBossWave.Major); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - const eternatus = game.scene.getEnemyPokemon(); - expect(eternatus?.species.speciesId).toBe(SpeciesId.ETERNATUS); - expect(eternatus?.hasPassive()).toBe(false); - expect(eternatus?.formIndex).toBe(1); + const eternatus = game.field.getEnemyPokemon(); + expect(eternatus.species.speciesId).toBe(SpeciesId.ETERNATUS); + expect(eternatus.hasPassive()).toBe(false); + expect(eternatus.formIndex).toBe(1); }); it(`should NOT spawn major or minor boss outside wave ${EndlessBossWave.Minor}s in END biome`, async () => { @@ -81,6 +81,6 @@ describe("Endless Boss", () => { await game.runToFinalBossEncounter([SpeciesId.BIDOOF], GameModes.ENDLESS); expect(game.scene.currentBattle.waveIndex).not.toBe(EndlessBossWave.Minor); - expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(SpeciesId.ETERNATUS); + expect(game.field.getEnemyPokemon().species.speciesId).not.toBe(SpeciesId.ETERNATUS); }); }); diff --git a/test/enemy-command.test.ts b/test/enemy-command.test.ts index 12281179e0d..e7687ae9641 100644 --- a/test/enemy-command.test.ts +++ b/test/enemy-command.test.ts @@ -61,7 +61,7 @@ describe("Enemy Commands - Move Selection", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.aiType = AiType.SMART_RANDOM; const moveChoices: MoveChoiceSet = {}; @@ -85,7 +85,7 @@ describe("Enemy Commands - Move Selection", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.aiType = AiType.SMART_RANDOM; const moveChoices: MoveChoiceSet = {}; diff --git a/test/evolution.test.ts b/test/evolution.test.ts index 172a4e5d3aa..afe557ff2c0 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -64,7 +64,7 @@ describe("Evolution", () => { it("should handle illegal abilityIndex values", async () => { await game.classicMode.runToSummon([SpeciesId.SQUIRTLE]); - const squirtle = game.scene.getPlayerPokemon()!; + const squirtle = game.field.getPlayerPokemon(); squirtle.abilityIndex = 5; await squirtle.evolve(pokemonEvolutions[SpeciesId.SQUIRTLE][0], squirtle.getSpeciesForm()); @@ -74,7 +74,7 @@ describe("Evolution", () => { it("should handle nincada's unique evolution", async () => { await game.classicMode.runToSummon([SpeciesId.NINCADA]); - const nincada = game.scene.getPlayerPokemon()!; + const nincada = game.field.getPlayerPokemon(); nincada.abilityIndex = 2; nincada.metBiome = -1; nincada.gender = 1; @@ -107,12 +107,12 @@ describe("Evolution", () => { await game.classicMode.startBattle([SpeciesId.TOTODILE]); - const totodile = game.scene.getPlayerPokemon()!; + const totodile = game.field.getPlayerPokemon(); const hpBefore = totodile.hp; expect(totodile.hp).toBe(totodile.getMaxHp()); - const golem = game.scene.getEnemyPokemon()!; + const golem = game.field.getEnemyPokemon(); golem.hp = 1; expect(golem.hp).toBe(1); @@ -135,14 +135,14 @@ describe("Evolution", () => { await game.classicMode.startBattle([SpeciesId.CYNDAQUIL]); - const cyndaquil = game.scene.getPlayerPokemon()!; + const cyndaquil = game.field.getPlayerPokemon(); cyndaquil.hp = Math.floor(cyndaquil.getMaxHp() / 2); const hpBefore = cyndaquil.hp; const maxHpBefore = cyndaquil.getMaxHp(); expect(cyndaquil.hp).toBe(Math.floor(cyndaquil.getMaxHp() / 2)); - const golem = game.scene.getEnemyPokemon()!; + const golem = game.field.getEnemyPokemon(); golem.hp = 1; expect(golem.hp).toBe(1); @@ -167,7 +167,7 @@ describe("Evolution", () => { * 1, 2 or 3, it's a 4 family maushold */ await game.classicMode.startBattle([SpeciesId.TANDEMAUS]); // starts us off with a tandemaus - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.level = 25; // tandemaus evolves at level 25 vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold const threeForm = playerPokemon.getEvolution()!; diff --git a/test/field/pokemon.test.ts b/test/field/pokemon.test.ts index baa50556473..02058ad6cb1 100644 --- a/test/field/pokemon.test.ts +++ b/test/field/pokemon.test.ts @@ -28,7 +28,7 @@ describe("Spec - Pokemon", () => { it("should not crash when trying to set status of undefined", async () => { await game.classicMode.runToSummon([SpeciesId.ABRA]); - const pkm = game.scene.getPlayerPokemon()!; + const pkm = game.field.getPlayerPokemon(); expect(pkm).toBeDefined(); expect(pkm.trySetStatus(undefined)).toBe(false); @@ -50,7 +50,7 @@ describe("Spec - Pokemon", () => { }); it("should append a new pokemon by default", async () => { - const zubat = scene.getEnemyPokemon()!; + const zubat = game.field.getEnemyPokemon(); zubat.addToParty(PokeballType.LUXURY_BALL); const party = scene.getPlayerParty(); @@ -62,7 +62,7 @@ describe("Spec - Pokemon", () => { it("should put a new pokemon into the passed slotIndex", async () => { const slotIndex = 1; - const zubat = scene.getEnemyPokemon()!; + const zubat = game.field.getEnemyPokemon(); zubat.addToParty(PokeballType.LUXURY_BALL, slotIndex); const party = scene.getPlayerParty(); @@ -78,7 +78,7 @@ describe("Spec - Pokemon", () => { await game.classicMode.startBattle([SpeciesId.ROTOM]); - const fanRotom = game.scene.getPlayerPokemon()!; + const fanRotom = game.field.getPlayerPokemon(); expect(fanRotom.compatibleTms).not.toContain(MoveId.BLIZZARD); expect(fanRotom.compatibleTms).toContain(MoveId.AIR_SLASH); diff --git a/test/final-boss.test.ts b/test/final-boss.test.ts index 2180979899a..81b5f7816d6 100644 --- a/test/final-boss.test.ts +++ b/test/final-boss.test.ts @@ -41,7 +41,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - expect(game.scene.getEnemyPokemon()!.species.speciesId).toBe(SpeciesId.ETERNATUS); + expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.ETERNATUS); }); it("should NOT spawn Eternatus before wave 200 in END biome", async () => { @@ -50,7 +50,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).not.toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(BiomeId.END); - expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(SpeciesId.ETERNATUS); + expect(game.field.getEnemyPokemon().species.speciesId).not.toBe(SpeciesId.ETERNATUS); }); it("should NOT spawn Eternatus outside of END biome", async () => { @@ -59,13 +59,13 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).not.toBe(BiomeId.END); - expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(SpeciesId.ETERNATUS); + expect(game.field.getEnemyPokemon().species.speciesId).not.toBe(SpeciesId.ETERNATUS); }); it("should initially spawn in regular form without passive & 4 boss segments", async () => { await game.runToFinalBossEncounter([SpeciesId.BIDOOF], GameModes.CLASSIC); - const eternatus = game.scene.getEnemyPokemon()!; + const eternatus = game.field.getEnemyPokemon(); expect(eternatus.formIndex).toBe(0); expect(eternatus.bossSegments).toBe(4); expect(eternatus.bossSegmentIndex).toBe(3); @@ -77,7 +77,7 @@ describe("Final Boss", () => { await game.runToFinalBossEncounter([SpeciesId.KYUREM], GameModes.CLASSIC); // phase 1 - const eternatus = game.scene.getEnemyPokemon()!; + const eternatus = game.field.getEnemyPokemon(); const phase1Hp = eternatus.getMaxHp(); game.move.use(MoveId.DRAGON_PULSE); @@ -100,7 +100,7 @@ describe("Final Boss", () => { await game.runToFinalBossEncounter([SpeciesId.SALAZZLE], GameModes.CLASSIC); // Eternatus phase 1 - const eternatus = game.scene.getEnemyPokemon()!; + const eternatus = game.field.getEnemyPokemon(); const phase1Hp = eternatus.getMaxHp(); game.move.use(MoveId.WILL_O_WISP); diff --git a/test/internals.test.ts b/test/internals.test.ts index bb33b01c265..98262a6db25 100644 --- a/test/internals.test.ts +++ b/test/internals.test.ts @@ -24,7 +24,7 @@ describe("Internals", () => { it("should provide Eevee with 3 defined abilities", async () => { await game.classicMode.runToSummon([SpeciesId.EEVEE]); - const eevee = game.scene.getPlayerPokemon()!; + const eevee = game.field.getPlayerPokemon(); expect(eevee.getSpeciesForm().getAbilityCount()).toBe(3); @@ -35,7 +35,7 @@ describe("Internals", () => { it("should set Eeeve abilityIndex between 0-2", async () => { await game.classicMode.runToSummon([SpeciesId.EEVEE]); - const eevee = game.scene.getPlayerPokemon()!; + const eevee = game.field.getPlayerPokemon(); expect(eevee.abilityIndex).toBeGreaterThanOrEqual(0); expect(eevee.abilityIndex).toBeLessThanOrEqual(2); diff --git a/test/items/dire-hit.test.ts b/test/items/dire-hit.test.ts index fe7fabd3c4c..6d4bc7524eb 100644 --- a/test/items/dire-hit.test.ts +++ b/test/items/dire-hit.test.ts @@ -42,7 +42,7 @@ describe("Items - Dire Hit", () => { it("should raise CRIT stage by 1", async () => { await game.classicMode.startBattle([SpeciesId.GASTLY]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getCritStage"); diff --git a/test/items/eviolite.test.ts b/test/items/eviolite.test.ts index 2e64135d264..9268226661b 100644 --- a/test/items/eviolite.test.ts +++ b/test/items/eviolite.test.ts @@ -28,7 +28,7 @@ describe("Items - Eviolite", () => { it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async () => { await game.classicMode.startBattle([SpeciesId.PICHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { const statValue = new NumberHolder(partyMember.getStat(stat, false)); @@ -49,7 +49,7 @@ describe("Items - Eviolite", () => { it("should not provide a boost for fully evolved, unfused pokemon", async () => { await game.classicMode.startBattle([SpeciesId.RAICHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { const statValue = new NumberHolder(partyMember.getStat(stat, false)); @@ -199,7 +199,7 @@ describe("Items - Eviolite", () => { await game.classicMode.startBattle([randItem(gMaxablePokemon)]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => { const statValue = new NumberHolder(partyMember.getStat(stat, false)); diff --git a/test/items/exp-booster.test.ts b/test/items/exp-booster.test.ts index dd2c8eb0c2b..1740b56693b 100644 --- a/test/items/exp-booster.test.ts +++ b/test/items/exp-booster.test.ts @@ -29,7 +29,7 @@ describe("EXP Modifier Items", () => { game.override.startingHeldItems([{ name: "LUCKY_EGG", count: 3 }, { name: "GOLDEN_EGG" }]); await game.classicMode.startBattle(); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); partyMember.exp = 100; const expHolder = new NumberHolder(partyMember.exp); game.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, expHolder); diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index c38294d07a4..485f35ed10a 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -34,7 +34,7 @@ describe("Items - Leek", () => { it("should raise CRIT stage by 2 when held by FARFETCHD", async () => { await game.classicMode.startBattle([SpeciesId.FARFETCHD]); - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); @@ -48,7 +48,7 @@ describe("Items - Leek", () => { it("should raise CRIT stage by 2 when held by GALAR_FARFETCHD", async () => { await game.classicMode.startBattle([SpeciesId.GALAR_FARFETCHD]); - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); @@ -62,7 +62,7 @@ describe("Items - Leek", () => { it("should raise CRIT stage by 2 when held by SIRFETCHD", async () => { await game.classicMode.startBattle([SpeciesId.SIRFETCHD]); - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); @@ -90,7 +90,7 @@ describe("Items - Leek", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); @@ -118,7 +118,7 @@ describe("Items - Leek", () => { partyMember.fusionGender = ally.gender; partyMember.fusionLuck = ally.luck; - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); @@ -132,7 +132,7 @@ describe("Items - Leek", () => { it("should not raise CRIT stage when held by a Pokemon outside of FARFETCHD line", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const enemyMember = game.scene.getEnemyPokemon()!; + const enemyMember = game.field.getEnemyPokemon(); vi.spyOn(enemyMember, "getCritStage"); diff --git a/test/items/leftovers.test.ts b/test/items/leftovers.test.ts index bed40b1c83a..6ae4094799b 100644 --- a/test/items/leftovers.test.ts +++ b/test/items/leftovers.test.ts @@ -40,7 +40,7 @@ describe("Items - Leftovers", () => { // Make sure leftovers are there expect(game.scene.modifiers[0].type.id).toBe("LEFTOVERS"); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); // We should have full hp expect(leadPokemon.isFullHp()).toBe(true); diff --git a/test/items/multi-lens.test.ts b/test/items/multi-lens.test.ts index e3cf39e4933..b69a07033c9 100644 --- a/test/items/multi-lens.test.ts +++ b/test/items/multi-lens.test.ts @@ -46,7 +46,7 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const spy = vi.spyOn(enemyPokemon, "getAttackDamage"); vi.spyOn(enemyPokemon, "getBaseDamage").mockReturnValue(100); @@ -67,7 +67,7 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.TACKLE); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -79,7 +79,7 @@ describe("Items - Multi Lens", () => { it("should apply secondary effects on each hit", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.TRAILBLAZE); @@ -90,7 +90,7 @@ describe("Items - Multi Lens", () => { it("should not enhance multi-hit moves", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.TACHYON_CUTTER); @@ -120,8 +120,8 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); const spy = vi.spyOn(enemyPokemon, "getAttackDamage"); game.move.select(MoveId.SEISMIC_TOSS); @@ -145,7 +145,7 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SUPER_FANG); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -164,7 +164,7 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SUPER_FANG); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -184,7 +184,7 @@ describe("Items - Multi Lens", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SUPER_FANG); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -196,7 +196,7 @@ describe("Items - Multi Lens", () => { game.override.enemyLevel(1000); await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "damageAndUpdate"); game.move.select(MoveId.FUTURE_SIGHT); diff --git a/test/items/reviver-seed.test.ts b/test/items/reviver-seed.test.ts index 268c5497899..45d41459f06 100644 --- a/test/items/reviver-seed.test.ts +++ b/test/items/reviver-seed.test.ts @@ -51,7 +51,7 @@ describe("Items - Reviver Seed", () => { ])("should activate the holder's reviver seed from a $moveType", async ({ move }) => { game.override.enemyLevel(100).startingLevel(1).enemyMoveset(move); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.hp - 1); const reviverSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier; @@ -66,7 +66,7 @@ describe("Items - Reviver Seed", () => { it("should activate the holder's reviver seed from confusion self-hit", async () => { game.override.enemyLevel(1).startingLevel(100).enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.hp - 1); player.addTag(BattlerTagType.CONFUSED, 3); @@ -95,7 +95,7 @@ describe("Items - Reviver Seed", () => { .moveset(move) .enemyMoveset(MoveId.ENDURE); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.damageAndUpdate(enemy.hp - 1); game.move.select(move); @@ -119,7 +119,7 @@ describe("Items - Reviver Seed", () => { .enemyAbility(AbilityId.LIQUID_OOZE) .enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.GASTLY, SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.hp - 1); const playerSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier; @@ -140,9 +140,9 @@ describe("Items - Reviver Seed", () => { .startingHeldItems([]) // reset held items to nothing so user doesn't revive and not trigger Destiny Bond .enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.hp - 1); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.DESTINY_BOND); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/items/scope-lens.test.ts b/test/items/scope-lens.test.ts index 578b0576aaa..7bc2dd7356b 100644 --- a/test/items/scope-lens.test.ts +++ b/test/items/scope-lens.test.ts @@ -33,7 +33,7 @@ describe("Items - Scope Lens", () => { it("should raise CRIT stage by 1", async () => { await game.classicMode.startBattle([SpeciesId.GASTLY]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getCritStage"); diff --git a/test/items/temp-stat-stage-booster.test.ts b/test/items/temp-stat-stage-booster.test.ts index 806ff20df6c..f95fe553faf 100644 --- a/test/items/temp-stat-stage-booster.test.ts +++ b/test/items/temp-stat-stage-booster.test.ts @@ -41,7 +41,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { it("should provide a x1.3 stat stage multiplier", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getStatStageMultiplier"); @@ -57,7 +57,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getAccuracyMultiplier"); @@ -77,7 +77,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getStatStageMultiplier"); @@ -102,7 +102,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); vi.spyOn(partyMember, "getStatStageMultiplier"); vi.spyOn(partyMember, "getAccuracyMultiplier"); diff --git a/test/items/toxic-orb.test.ts b/test/items/toxic-orb.test.ts index a1888a6aa1d..664ddd437e4 100644 --- a/test/items/toxic-orb.test.ts +++ b/test/items/toxic-orb.test.ts @@ -42,7 +42,7 @@ describe("Items - Toxic orb", () => { it("should badly poison the holder", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); expect(player.getHeldItems()[0].type.id).toBe("TOXIC_ORB"); game.move.select(MoveId.SPLASH); diff --git a/test/moves/ability-ignore-moves.test.ts b/test/moves/ability-ignore-moves.test.ts index 750d8fe2f20..e3a7c7db12f 100644 --- a/test/moves/ability-ignore-moves.test.ts +++ b/test/moves/ability-ignore-moves.test.ts @@ -77,7 +77,7 @@ describe("Moves - Ability-Ignoring Moves", () => { await game.toEndOfTurn(); expect(enemy.isFainted()).toBe(false); - expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0].move).toBe(MoveId.SUNSTEEL_STRIKE); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].move).toBe(MoveId.SUNSTEEL_STRIKE); }); // TODO: Verify this behavior on cart diff --git a/test/moves/alluring-voice.test.ts b/test/moves/alluring-voice.test.ts index dc01cc1a54e..cc89a106c16 100644 --- a/test/moves/alluring-voice.test.ts +++ b/test/moves/alluring-voice.test.ts @@ -38,7 +38,7 @@ describe("Moves - Alluring Voice", () => { it("should confuse the opponent if their stat stages were raised", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.use(MoveId.ALLURING_VOICE); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); diff --git a/test/moves/assist.test.ts b/test/moves/assist.test.ts index 52467c2ba98..ab0a8106457 100644 --- a/test/moves/assist.test.ts +++ b/test/moves/assist.test.ts @@ -51,18 +51,18 @@ describe("Moves - Assist", () => { // Player_2 uses Sketch, copies Swords Dance, Player_1 uses Assist, uses Player_2's Sketched Swords Dance await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(2); // Stat raised from Assist -> Swords Dance + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(2); // Stat raised from Assist -> Swords Dance }); it("should fail if there are no allies", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; + const feebas = game.field.getPlayerPokemon(); game.move.changeMoveset(feebas, [MoveId.ASSIST, MoveId.SKETCH, MoveId.PROTECT, MoveId.DRAGON_TAIL]); game.move.select(MoveId.ASSIST, 0); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should fail if ally has no usable moves and user has usable moves", async () => { @@ -82,7 +82,7 @@ describe("Moves - Assist", () => { game.move.select(MoveId.PROTECT, 1); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should apply secondary effects of a move", async () => { @@ -96,6 +96,6 @@ describe("Moves - Assist", () => { game.move.select(MoveId.ASSIST, 1); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // should receive recoil damage from Wood Hammer + expect(game.field.getPlayerPokemon().isFullHp()).toBeFalsy(); // should receive recoil damage from Wood Hammer }); }); diff --git a/test/moves/astonish.test.ts b/test/moves/astonish.test.ts index 0f7dc526b2d..1b39b1c4fef 100644 --- a/test/moves/astonish.test.ts +++ b/test/moves/astonish.test.ts @@ -42,9 +42,9 @@ describe("Moves - Astonish", () => { test("move effect should cancel the target's move on the turn it applies", async () => { await game.classicMode.startBattle([SpeciesId.MEOWSCARADA]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ASTONISH); diff --git a/test/moves/autotomize.test.ts b/test/moves/autotomize.test.ts index a8a7309e03e..85ea9c3852f 100644 --- a/test/moves/autotomize.test.ts +++ b/test/moves/autotomize.test.ts @@ -38,7 +38,7 @@ describe("Moves - Autotomize", () => { const threeAutotomizeDracozoltWeight = 0.1; await game.classicMode.startBattle([SpeciesId.DRACOZOLT]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getWeight()).toBe(baseDracozoltWeight); game.move.select(MoveId.AUTOTOMIZE); await game.toNextTurn(); @@ -62,7 +62,7 @@ describe("Moves - Autotomize", () => { const autotomizeAegislashWeight = 0.1; await game.classicMode.startBattle([SpeciesId.AEGISLASH]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getWeight()).toBe(baseAegislashWeight); @@ -98,7 +98,7 @@ describe("Moves - Autotomize", () => { const autotomizeLightGroudonWeight = 425; game.override.ability(AbilityId.LIGHT_METAL); await game.classicMode.startBattle([SpeciesId.GROUDON]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getWeight()).toBe(baseLightGroudonWeight); game.move.select(MoveId.AUTOTOMIZE); await game.toNextTurn(); diff --git a/test/moves/baneful-bunker.test.ts b/test/moves/baneful-bunker.test.ts index da2a8e0418a..07fb74ce6a8 100644 --- a/test/moves/baneful-bunker.test.ts +++ b/test/moves/baneful-bunker.test.ts @@ -5,7 +5,7 @@ import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Baneful Bunker", () => { let phaserGame: Phaser.Game; @@ -26,55 +26,51 @@ describe("Moves - Baneful Bunker", () => { game.override .battleStyle("single") - .moveset(MoveId.SLASH) - .enemySpecies(SpeciesId.SNORLAX) + .moveset([MoveId.SLASH, MoveId.FLASH_CANNON]) + .enemySpecies(SpeciesId.TOXAPEX) .enemyAbility(AbilityId.INSOMNIA) .enemyMoveset(MoveId.BANEFUL_BUNKER) .startingLevel(100) .enemyLevel(100); }); - test("should protect the user and poison attackers that make contact", async () => { - await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + function expectProtected() { + expect(game.scene.getEnemyPokemon()?.hp).toBe(game.scene.getEnemyPokemon()?.getMaxHp()); + expect(game.scene.getPlayerPokemon()?.status?.effect).toBe(StatusEffect.POISON); + } + + it("should protect the user and poison attackers that make contact", async () => { + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); game.move.select(MoveId.SLASH); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy(); + + expectProtected(); }); - test("should protect the user and poison attackers that make contact, regardless of accuracy checks", async () => { + + it("should ignore accuracy checks", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; - game.move.select(MoveId.SLASH); - await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); - await game.phaseInterceptor.to("MoveEffectPhase"); - + await game.phaseInterceptor.to("MoveEndPhase"); // baneful bunker await game.move.forceMiss(); + await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy(); + + expectProtected(); }); - test("should not poison attackers that don't make contact", async () => { - game.override.moveset(MoveId.FLASH_CANNON); + it("should block non-contact moves without poisoning attackers", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const charizard = game.field.getPlayerPokemon(); + const toxapex = game.field.getEnemyPokemon(); game.move.select(MoveId.FLASH_CANNON); - await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); - await game.phaseInterceptor.to("MoveEffectPhase"); - - await game.move.forceMiss(); await game.phaseInterceptor.to("BerryPhase", false); - expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeFalsy(); + + expect(toxapex.hp).toBe(toxapex.getMaxHp()); + expect(charizard.status?.effect).toBeUndefined(); }); }); diff --git a/test/moves/baton-pass.test.ts b/test/moves/baton-pass.test.ts index ef1979b223d..f9bd92a63cd 100644 --- a/test/moves/baton-pass.test.ts +++ b/test/moves/baton-pass.test.ts @@ -42,7 +42,7 @@ describe("Moves - Baton Pass", () => { game.move.select(MoveId.NASTY_PLOT); await game.toNextTurn(); - let playerPokemon = game.scene.getPlayerPokemon()!; + let playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2); @@ -52,7 +52,7 @@ describe("Moves - Baton Pass", () => { await game.phaseInterceptor.to("TurnEndPhase"); // assert - playerPokemon = game.scene.getPlayerPokemon()!; + playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.species.speciesId).toEqual(SpeciesId.SHUCKLE); expect(playerPokemon.getStatStage(Stat.SPATK)).toEqual(2); }); @@ -73,7 +73,7 @@ describe("Moves - Baton Pass", () => { await game.phaseInterceptor.to("PostSummonPhase", false); // check buffs are still there - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.SPATK)).toEqual(2); + expect(game.field.getEnemyPokemon().getStatStage(Stat.SPATK)).toEqual(2); // confirm that a switch actually happened. can't use species because I // can't find a way to override trainer parties with more than 1 pokemon species expect(game.phaseInterceptor.log.slice(-4)).toEqual([ @@ -105,7 +105,7 @@ describe("Moves - Baton Pass", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.FIRE_SPIN); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/moves/beak-blast.test.ts b/test/moves/beak-blast.test.ts index 71d2d957bed..4d28e7fd0ab 100644 --- a/test/moves/beak-blast.test.ts +++ b/test/moves/beak-blast.test.ts @@ -40,8 +40,8 @@ describe("Moves - Beak Blast", () => { it("should add a charge effect that burns attackers on contact", async () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BEAK_BLAST); @@ -57,8 +57,8 @@ describe("Moves - Beak Blast", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BEAK_BLAST); @@ -74,8 +74,8 @@ describe("Moves - Beak Blast", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BEAK_BLAST); @@ -91,7 +91,7 @@ describe("Moves - Beak Blast", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.BEAK_BLAST); @@ -104,8 +104,8 @@ describe("Moves - Beak Blast", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.BEAK_BLAST); @@ -120,8 +120,8 @@ describe("Moves - Beak Blast", () => { it("should still burn the enemy if the user is knocked out", async () => { game.override.ability(AbilityId.BALL_FETCH); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; - const user = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); + const user = game.field.getPlayerPokemon(); user.hp = 1; game.move.select(MoveId.BEAK_BLAST); await game.phaseInterceptor.to("BerryPhase", false); @@ -133,7 +133,7 @@ describe("Moves - Beak Blast", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); game.move.select(MoveId.BEAK_BLAST); await game.phaseInterceptor.to("BerryPhase", false); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.status?.effect).not.toBe(StatusEffect.BURN); }); }); diff --git a/test/moves/beat-up.test.ts b/test/moves/beat-up.test.ts index 79d672bd0ed..cfb3d35bed5 100644 --- a/test/moves/beat-up.test.ts +++ b/test/moves/beat-up.test.ts @@ -43,8 +43,8 @@ describe("Moves - Beat Up", () => { SpeciesId.EEVEE, ]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); let enemyStartingHp = enemyPokemon.hp; game.move.select(MoveId.BEAT_UP); @@ -71,7 +71,7 @@ describe("Moves - Beat Up", () => { SpeciesId.EEVEE, ]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.scene.getPlayerParty()[1].trySetStatus(StatusEffect.BURN); diff --git a/test/moves/belly-drum.test.ts b/test/moves/belly-drum.test.ts index 9cbb7adb1ae..827030c14aa 100644 --- a/test/moves/belly-drum.test.ts +++ b/test/moves/belly-drum.test.ts @@ -44,7 +44,7 @@ describe("Moves - BELLY DRUM", () => { test("raises the user's ATK stat stage to its max, at the cost of 1/2 of its maximum HP", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); game.move.select(MoveId.BELLY_DRUM); @@ -57,7 +57,7 @@ describe("Moves - BELLY DRUM", () => { test("will still take effect if an uninvolved stat stage is at max", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); // Here - Stat.ATK -> -3 and Stat.SPATK -> 6 @@ -75,7 +75,7 @@ describe("Moves - BELLY DRUM", () => { test("fails if the pokemon's ATK stat stage is at its maximum", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.setStatStage(Stat.ATK, 6); @@ -89,7 +89,7 @@ describe("Moves - BELLY DRUM", () => { test("fails if the user's health is less than 1/2", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/test/moves/burning-jealousy.test.ts b/test/moves/burning-jealousy.test.ts index 62f41790582..33ff855f31e 100644 --- a/test/moves/burning-jealousy.test.ts +++ b/test/moves/burning-jealousy.test.ts @@ -40,7 +40,7 @@ describe("Moves - Burning Jealousy", () => { it("should burn the opponent if their stat stages were raised", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.BURNING_JEALOUSY); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -53,7 +53,7 @@ describe("Moves - Burning Jealousy", () => { game.override.starterSpecies(0).battleStyle("double"); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.ABRA]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.BURNING_JEALOUSY); game.move.select(MoveId.GROWL, 1); @@ -67,7 +67,7 @@ describe("Moves - Burning Jealousy", () => { game.override.enemySpecies(SpeciesId.DITTO).enemyAbility(AbilityId.IMPOSTER).enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.BURNING_JEALOUSY); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/moves/camouflage.test.ts b/test/moves/camouflage.test.ts index badab8ec4a5..b3f87be4715 100644 --- a/test/moves/camouflage.test.ts +++ b/test/moves/camouflage.test.ts @@ -37,7 +37,7 @@ describe("Moves - Camouflage", () => { it("Camouflage should look at terrain first when selecting a type to change into", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.CAMOUFLAGE); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); diff --git a/test/moves/ceaseless-edge.test.ts b/test/moves/ceaseless-edge.test.ts index 56d7c97ea68..64f4cf15511 100644 --- a/test/moves/ceaseless-edge.test.ts +++ b/test/moves/ceaseless-edge.test.ts @@ -42,7 +42,7 @@ describe("Moves - Ceaseless Edge", () => { test("move should hit and apply spikes", async () => { await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartingHp = enemyPokemon.hp; @@ -64,7 +64,7 @@ describe("Moves - Ceaseless Edge", () => { game.override.startingHeldItems([{ name: "MULTI_LENS" }]); await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartingHp = enemyPokemon.hp; diff --git a/test/moves/chilly-reception.test.ts b/test/moves/chilly-reception.test.ts index 948b42cb3f2..096454132f3 100644 --- a/test/moves/chilly-reception.test.ts +++ b/test/moves/chilly-reception.test.ts @@ -44,7 +44,7 @@ describe("Moves - Chilly Reception", () => { await game.toEndOfTurn(); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW); - expect(game.scene.getPlayerPokemon()).toBe(meowth); + expect(game.field.getPlayerPokemon()).toBe(meowth); expect(slowking.isOnField()).toBe(false); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); expect(game.textInterceptor.logs).toContain( @@ -60,7 +60,7 @@ describe("Moves - Chilly Reception", () => { expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); }); it("should still switch out even if weather cannot be changed", async () => { @@ -85,7 +85,7 @@ describe("Moves - Chilly Reception", () => { expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()).toBe(meowth); + expect(game.field.getPlayerPokemon()).toBe(meowth); expect(slowking.isOnField()).toBe(false); }); @@ -95,7 +95,7 @@ describe("Moves - Chilly Reception", () => { expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.SNOW); - const slowking = game.scene.getPlayerPokemon()!; + const slowking = game.field.getPlayerPokemon(); game.move.select(MoveId.SNOWSCAPE); await game.toNextTurn(); @@ -108,7 +108,7 @@ describe("Moves - Chilly Reception", () => { expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()).toBe(slowking); + expect(game.field.getPlayerPokemon()).toBe(slowking); expect(slowking.getLastXMoves()[0].result).toBe(MoveResult.FAIL); expect(game.textInterceptor.logs).toContain( i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(slowking) }), @@ -126,7 +126,7 @@ describe("Moves - Chilly Reception", () => { await game.toEndOfTurn(); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW); - expect(game.scene.getPlayerPokemon()).toBe(meowth); + expect(game.field.getPlayerPokemon()).toBe(meowth); expect(slowking.isOnField()).toBe(false); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); expect(game.textInterceptor.logs).not.toContain( diff --git a/test/moves/clangorous-soul.test.ts b/test/moves/clangorous-soul.test.ts index 82a0d383f65..2f1c7a18f50 100644 --- a/test/moves/clangorous-soul.test.ts +++ b/test/moves/clangorous-soul.test.ts @@ -41,7 +41,7 @@ describe("Moves - Clangorous Soul", () => { it("raises the user's ATK, DEF, SPATK, SPDEF, and SPD stat stages by 1 each at the cost of 1/3 of its maximum HP", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.move.select(MoveId.CLANGOROUS_SOUL); @@ -58,7 +58,7 @@ describe("Moves - Clangorous Soul", () => { it("will still take effect if one or more of the involved stat stages are not at max", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); //Here - Stat.SPD -> 0 and Stat.SPDEF -> 4 @@ -81,7 +81,7 @@ describe("Moves - Clangorous Soul", () => { it("fails if all stat stages involved are at max", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.setStatStage(Stat.ATK, 6); leadPokemon.setStatStage(Stat.DEF, 6); @@ -103,7 +103,7 @@ describe("Moves - Clangorous Soul", () => { it("fails if the user's health is less than 1/3", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 91e941e2845..bfe4dd2f954 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -45,7 +45,7 @@ describe("Moves - Copycat", () => { game.move.select(MoveId.COPYCAT); // Last successful move should be Swords Dance await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(4); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(4); }); it("should fail when the last move used is not a valid Copycat move", async () => { @@ -58,7 +58,7 @@ describe("Moves - Copycat", () => { game.move.select(MoveId.COPYCAT); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should copy the called move when the last move successfully calls another", async () => { @@ -70,7 +70,7 @@ describe("Moves - Copycat", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); // Player moves first so enemy can copy Swords Dance await game.toNextTurn(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); expect(enemy.getLastXMoves()[0]).toMatchObject({ move: MoveId.SWORDS_DANCE, result: MoveResult.SUCCESS, @@ -87,6 +87,6 @@ describe("Moves - Copycat", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2); + expect(game.field.getEnemyPokemon().getStatStage(Stat.SPDEF)).toBe(-2); }); }); diff --git a/test/moves/court-change.test.ts b/test/moves/court-change.test.ts new file mode 100644 index 00000000000..a27854c12be --- /dev/null +++ b/test/moves/court-change.test.ts @@ -0,0 +1,85 @@ +import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import { GameManager } from "#test/test-utils/game-manager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Move - Court Change", () => { + 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 + .ability(AbilityId.BALL_FETCH) + .criticalHits(false) + .enemyAbility(AbilityId.STURDY) + .startingLevel(100) + .battleStyle("single") + .enemySpecies(SpeciesId.MAGIKARP) + .enemyMoveset(MoveId.SPLASH); + }); + + it("should swap combined Pledge effects to the opposite side", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.REGIELEKI, SpeciesId.SHUCKLE]); + + const regieleki = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + + game.move.use(MoveId.WATER_PLEDGE); + game.move.use(MoveId.GRASS_PLEDGE, 1); + await game.toNextTurn(); + + // enemy team will be in the swamp and slowed + expect(game.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, ArenaTagSide.ENEMY)).toBeDefined(); + expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBe(enemyPokemon.getStat(Stat.SPD) / 4); + + game.move.use(MoveId.COURT_CHANGE); + game.move.use(MoveId.SPLASH, 1); + await game.toEndOfTurn(); + + // own team should now be in the swamp and slowed + expect(game.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, ArenaTagSide.ENEMY)).toBeUndefined(); + expect(game.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, ArenaTagSide.PLAYER)).toBeDefined(); + expect(regieleki.getEffectiveStat(Stat.SPD)).toBe(regieleki.getStat(Stat.SPD) / 4); + }); + + it("should swap safeguard to the enemy side ", async () => { + game.override.enemyMoveset(MoveId.TOXIC_THREAD); + await game.classicMode.startBattle([SpeciesId.NINJASK]); + + const ninjask = game.field.getPlayerPokemon(); + + game.move.use(MoveId.SAFEGUARD); + await game.move.forceEnemyMove(MoveId.TOXIC_THREAD); + await game.toNextTurn(); + + // Ninjask will not be poisoned because of Safeguard + expect(game.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, ArenaTagSide.PLAYER)).toBeDefined(); + expect(ninjask.status?.effect).toBeUndefined(); + + game.move.use(MoveId.COURT_CHANGE); + await game.toEndOfTurn(); + + // Ninjask should now be poisoned due to lack of Safeguard + expect(game.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, ArenaTagSide.PLAYER)).toBeUndefined(); + expect(game.scene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, ArenaTagSide.ENEMY)).toBeDefined(); + expect(ninjask.status?.effect).toBe(StatusEffect.POISON); + }); +}); diff --git a/test/moves/crafty-shield.test.ts b/test/moves/crafty-shield.test.ts index d0c148c1f41..0e067a07214 100644 --- a/test/moves/crafty-shield.test.ts +++ b/test/moves/crafty-shield.test.ts @@ -1,12 +1,14 @@ import { AbilityId } from "#enums/ability-id"; +import { ArenaTagSide } from "#enums/arena-tag-side"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { BerryPhase } from "#phases/berry-phase"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Crafty Shield", () => { let phaserGame: Phaser.Game; @@ -27,68 +29,100 @@ describe("Moves - Crafty Shield", () => { game.override .battleStyle("double") - .moveset([MoveId.CRAFTY_SHIELD, MoveId.SPLASH, MoveId.SWORDS_DANCE]) - .enemySpecies(SpeciesId.SNORLAX) - .enemyMoveset([MoveId.GROWL]) + .enemySpecies(SpeciesId.DUSKNOIR) + .enemyMoveset(MoveId.GROWL) .enemyAbility(AbilityId.INSOMNIA) .startingLevel(100) .enemyLevel(100); }); - test("should protect the user and allies from status moves", async () => { + it("should protect the user and allies from status moves", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const [charizard, blastoise] = game.scene.getPlayerField(); + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.GROWL); + await game.move.forceEnemyMove(MoveId.GROWL); - game.move.select(MoveId.CRAFTY_SHIELD); - game.move.select(MoveId.SPLASH, 1); + await game.phaseInterceptor.to("TurnEndPhase"); - await game.phaseInterceptor.to(BerryPhase, false); - - leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); + expect(charizard.getStatStage(Stat.ATK)).toBe(0); + expect(blastoise.getStatStage(Stat.ATK)).toBe(0); }); - test("should not protect the user and allies from attack moves", async () => { - game.override.enemyMoveset([MoveId.TACKLE]); - + it("should not protect the user and allies from attack moves", async () => { + game.override.enemyMoveset(MoveId.TACKLE); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const [charizard, blastoise] = game.scene.getPlayerField(); - game.move.select(MoveId.CRAFTY_SHIELD); - game.move.select(MoveId.SPLASH, 1); + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER); + await game.move.forceEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("TurnEndPhase"); - await game.phaseInterceptor.to(BerryPhase, false); - - expect(leadPokemon.some(p => p.hp < p.getMaxHp())).toBeTruthy(); + expect(charizard.isFullHp()).toBe(false); + expect(blastoise.isFullHp()).toBe(false); }); - test("should protect the user and allies from moves that ignore other protection", async () => { - game.override.enemySpecies(SpeciesId.DUSCLOPS).enemyMoveset([MoveId.CURSE]); - + it("should not block entry hazards and field-targeted moves", async () => { + game.override.enemyMoveset([MoveId.PERISH_SONG, MoveId.TOXIC_SPIKES]); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const [charizard, blastoise] = game.scene.getPlayerField(); - game.move.select(MoveId.CRAFTY_SHIELD); - game.move.select(MoveId.SPLASH, 1); + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.PERISH_SONG); + await game.move.forceEnemyMove(MoveId.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); - await game.phaseInterceptor.to(BerryPhase, false); - - leadPokemon.forEach(p => expect(p.getTag(BattlerTagType.CURSED)).toBeUndefined()); + expect(game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.PLAYER)).toBeDefined(); + expect(charizard.getTag(BattlerTagType.PERISH_SONG)).toBeDefined(); + expect(blastoise.getTag(BattlerTagType.PERISH_SONG)).toBeDefined(); }); - test("should not block allies' self-targeted moves", async () => { + it("should protect the user and allies from moves that ignore other protection", async () => { + game.override.moveset(MoveId.CURSE); + await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const [charizard, blastoise] = game.scene.getPlayerField(); - game.move.select(MoveId.CRAFTY_SHIELD); - game.move.select(MoveId.SWORDS_DANCE, 1); + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.CURSE, BattlerIndex.PLAYER); + await game.move.forceEnemyMove(MoveId.CURSE, BattlerIndex.PLAYER_2); - await game.phaseInterceptor.to(BerryPhase, false); + await game.toEndOfTurn(); - expect(leadPokemon[0].getStatStage(Stat.ATK)).toBe(0); - expect(leadPokemon[1].getStatStage(Stat.ATK)).toBe(2); + expect(charizard.getTag(BattlerTagType.CURSED)).toBeUndefined(); + expect(blastoise.getTag(BattlerTagType.CURSED)).toBeUndefined(); + + const [dusknoir1, dusknoir2] = game.scene.getEnemyField(); + expect(dusknoir1).toHaveFullHp(); + expect(dusknoir2).toHaveFullHp(); + }); + + it("should not block allies' self or ally-targeted moves", async () => { + await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); + + const [charizard, blastoise] = game.scene.getPlayerField(); + + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER); + game.move.use(MoveId.SWORDS_DANCE, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(charizard.getStatStage(Stat.ATK)).toBe(0); + expect(blastoise.getStatStage(Stat.ATK)).toBe(2); + + game.move.use(MoveId.HOWL, BattlerIndex.PLAYER); + game.move.use(MoveId.CRAFTY_SHIELD, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(charizard.getStatStage(Stat.ATK)).toBe(1); + expect(blastoise.getStatStage(Stat.ATK)).toBe(3); }); }); diff --git a/test/moves/destiny-bond.test.ts b/test/moves/destiny-bond.test.ts index 48bd29fe662..9c397717335 100644 --- a/test/moves/destiny-bond.test.ts +++ b/test/moves/destiny-bond.test.ts @@ -48,15 +48,15 @@ describe("Moves - Destiny Bond", () => { game.override.moveset(moveToUse); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(true); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(true); }); it("should KO the opponent on the next turn", async () => { @@ -65,24 +65,24 @@ describe("Moves - Destiny Bond", () => { game.override.moveset([MoveId.SPLASH, moveToUse]); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); // Turn 1: Enemy uses Destiny Bond and doesn't faint game.move.select(MoveId.SPLASH); await game.setTurnOrder(playerFirst); await game.toNextTurn(); - expect(enemyPokemon?.isFainted()).toBe(false); - expect(playerPokemon?.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(false); // Turn 2: Player KO's the enemy before the enemy's turn game.move.select(moveToUse); await game.setTurnOrder(playerFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(true); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(true); }); it("should fail if used twice in a row", async () => { @@ -91,24 +91,24 @@ describe("Moves - Destiny Bond", () => { game.override.moveset([MoveId.SPLASH, moveToUse]); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); // Turn 1: Enemy uses Destiny Bond and doesn't faint game.move.select(MoveId.SPLASH); await game.setTurnOrder(enemyFirst); await game.toNextTurn(); - expect(enemyPokemon?.isFainted()).toBe(false); - expect(playerPokemon?.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(false); // Turn 2: Enemy should fail Destiny Bond then get KO'd game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(false); }); it("should not KO the opponent if the user dies to weather", async () => { @@ -118,15 +118,15 @@ describe("Moves - Destiny Bond", () => { game.override.moveset(moveToUse).ability(AbilityId.SAND_STREAM); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(false); }); it("should not KO the opponent if the user had another turn", async () => { @@ -135,25 +135,25 @@ describe("Moves - Destiny Bond", () => { game.override.moveset([MoveId.SPORE, moveToUse]); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); // Turn 1: Enemy uses Destiny Bond and doesn't faint game.move.select(MoveId.SPORE); await game.setTurnOrder(enemyFirst); await game.toNextTurn(); - expect(enemyPokemon?.isFainted()).toBe(false); - expect(playerPokemon?.isFainted()).toBe(false); - expect(enemyPokemon?.status?.effect).toBe(StatusEffect.SLEEP); + expect(enemyPokemon.isFainted()).toBe(false); + expect(playerPokemon.isFainted()).toBe(false); + expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP); // Turn 2: Enemy should skip a turn due to sleep, then get KO'd game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(false); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(false); }); it("should not KO an ally", async () => { @@ -171,10 +171,10 @@ describe("Moves - Destiny Bond", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon0?.isFainted()).toBe(false); - expect(enemyPokemon1?.isFainted()).toBe(false); - expect(playerPokemon0?.isFainted()).toBe(true); - expect(playerPokemon1?.isFainted()).toBe(false); + expect(enemyPokemon0.isFainted()).toBe(false); + expect(enemyPokemon1.isFainted()).toBe(false); + expect(playerPokemon0.isFainted()).toBe(true); + expect(playerPokemon1.isFainted()).toBe(false); }); it("should not cause a crash if the user is KO'd by Ceaseless Edge", async () => { @@ -184,15 +184,15 @@ describe("Moves - Destiny Bond", () => { game.override.moveset(moveToUse); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(true); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(true); // Ceaseless Edge spikes effect should still activate const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag; @@ -235,20 +235,20 @@ describe("Moves - Destiny Bond", () => { game.override.moveset(moveToUse).startingHeldItems([{ name: "REVIVER_SEED" }]); await game.classicMode.startBattle(defaultParty); - const enemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(moveToUse); await game.setTurnOrder(enemyFirst); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.isFainted()).toBe(true); - expect(playerPokemon?.isFainted()).toBe(true); + expect(enemyPokemon.isFainted()).toBe(true); + expect(playerPokemon.isFainted()).toBe(true); // Check that the Tackle user's Reviver Seed did not activate const revSeeds = game.scene .getModifiers(PokemonInstantReviveModifier) - .filter(m => m.pokemonId === playerPokemon?.id); + .filter(m => m.pokemonId === playerPokemon.id); expect(revSeeds.length).toBe(1); }); }); diff --git a/test/moves/diamond-storm.test.ts b/test/moves/diamond-storm.test.ts index 9de7409ca18..ffd3c62420e 100644 --- a/test/moves/diamond-storm.test.ts +++ b/test/moves/diamond-storm.test.ts @@ -41,6 +41,6 @@ describe("Moves - Diamond Storm", () => { game.move.select(MoveId.DIAMOND_STORM); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.DEF)).toBe(2); + expect(game.field.getPlayerPokemon().getStatStage(Stat.DEF)).toBe(2); }); }); diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index 2cb8ae05963..fcc593b75da 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -38,8 +38,8 @@ describe("Moves - Dig", () => { it("should make the user semi-invulnerable, then attack over 2 turns", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIG); await game.phaseInterceptor.to("TurnEndPhase"); @@ -62,7 +62,7 @@ describe("Moves - Dig", () => { game.override.moveset([]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.changeMoveset(playerPokemon, MoveId.DIG); game.move.select(MoveId.DIG); @@ -80,8 +80,8 @@ describe("Moves - Dig", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIG); @@ -95,7 +95,7 @@ describe("Moves - Dig", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.DIG); @@ -110,8 +110,8 @@ describe("Moves - Dig", () => { it("should cause the user to take double damage from Earthquake", async () => { await game.classicMode.startBattle([SpeciesId.DONDOZO]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); const preDigEarthquakeDmg = playerPokemon.getAttackDamage({ source: enemyPokemon, diff --git a/test/moves/disable.test.ts b/test/moves/disable.test.ts index 5543c16fecf..9b5763bf90b 100644 --- a/test/moves/disable.test.ts +++ b/test/moves/disable.test.ts @@ -132,7 +132,7 @@ describe("Moves - Disable", () => { vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.ABSORB); await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const playerMon = game.scene.getPlayerPokemon()!; + const playerMon = game.field.getPlayerPokemon(); playerMon.pushMoveHistory({ move: MoveId.SPLASH, targets: [BattlerIndex.ENEMY], useMode: MoveUseMode.NORMAL }); game.scene.currentBattle.lastMove = MoveId.SPLASH; @@ -141,7 +141,7 @@ describe("Moves - Disable", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - const enemyMon = game.scene.getEnemyPokemon()!; + const enemyMon = game.field.getEnemyPokemon(); expect(enemyMon.isMoveRestricted(moveId), `calling move ${MoveId[moveId]} was not disabled`).toBe(true); expect(enemyMon.getLastXMoves(-1)).toHaveLength(2); const calledMove = enemyMon.getLastXMoves()[0].move; diff --git a/test/moves/dive.test.ts b/test/moves/dive.test.ts index c34f3dc54dc..6464cb110b4 100644 --- a/test/moves/dive.test.ts +++ b/test/moves/dive.test.ts @@ -38,8 +38,8 @@ describe("Moves - Dive", () => { it("should make the user semi-invulnerable, then attack over 2 turns", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIVE); @@ -64,8 +64,8 @@ describe("Moves - Dive", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIVE); @@ -79,7 +79,7 @@ describe("Moves - Dive", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.DIVE); @@ -96,8 +96,8 @@ describe("Moves - Dive", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIVE); @@ -113,8 +113,8 @@ describe("Moves - Dive", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DIVE); diff --git a/test/moves/doodle.test.ts b/test/moves/doodle.test.ts index c4380720f20..8b90d120b24 100644 --- a/test/moves/doodle.test.ts +++ b/test/moves/doodle.test.ts @@ -39,7 +39,7 @@ describe("Moves - Doodle", () => { game.move.select(MoveId.DOODLE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); }); it("should copy the opponent's ability to itself and its ally in doubles", async () => { @@ -64,6 +64,6 @@ describe("Moves - Doodle", () => { await game.phaseInterceptor.to("BerryPhase"); // Enemies should have been intimidated twice - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-2); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-2); }); }); diff --git a/test/moves/double-team.test.ts b/test/moves/double-team.test.ts index 9276a0956e8..6d471df2d57 100644 --- a/test/moves/double-team.test.ts +++ b/test/moves/double-team.test.ts @@ -36,8 +36,8 @@ describe("Moves - Double Team", () => { it("raises the user's EVA stat stage by 1", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const ally = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const ally = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getAccuracyMultiplier"); expect(ally.getStatStage(Stat.EVA)).toBe(0); diff --git a/test/moves/dragon-rage.test.ts b/test/moves/dragon-rage.test.ts index 1b850ade488..c90e2b78abd 100644 --- a/test/moves/dragon-rage.test.ts +++ b/test/moves/dragon-rage.test.ts @@ -47,7 +47,7 @@ describe("Moves - Dragon Rage", () => { await game.classicMode.startBattle(); partyPokemon = game.scene.getPlayerParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon()!; + enemyPokemon = game.field.getEnemyPokemon(); }); it("ignores weaknesses", async () => { diff --git a/test/moves/dragon-tail.test.ts b/test/moves/dragon-tail.test.ts index 487647784f7..1cea6f908a0 100644 --- a/test/moves/dragon-tail.test.ts +++ b/test/moves/dragon-tail.test.ts @@ -41,7 +41,7 @@ describe("Moves - Dragon Tail", () => { it("should cause opponent to flee, and not crash", async () => { await game.classicMode.startBattle([SpeciesId.DRATINI]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DRAGON_TAIL); @@ -59,8 +59,8 @@ describe("Moves - Dragon Tail", () => { game.override.enemyAbility(AbilityId.ROUGH_SKIN); await game.classicMode.startBattle([SpeciesId.DRATINI]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.DRAGON_TAIL); @@ -132,7 +132,7 @@ describe("Moves - Dragon Tail", () => { game.override.enemyAbility(AbilityId.SUCTION_CUPS); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.DRAGON_TAIL); await game.phaseInterceptor.to("TurnEndPhase"); @@ -149,7 +149,7 @@ describe("Moves - Dragon Tail", () => { await game.toNextTurn(); // Make sure the enemy switched to a healthy Pokemon - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); expect(enemy).toBeDefined(); expect(enemy.isFullHp()).toBe(true); @@ -171,7 +171,7 @@ describe("Moves - Dragon Tail", () => { await game.toNextTurn(); // Make sure the enemy field is not empty and has a revived Pokemon - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); expect(enemy).toBeDefined(); expect(enemy.hp).toBe(Math.floor(enemy.getMaxHp() / 2)); expect(game.scene.getEnemyField().length).toBe(1); @@ -189,7 +189,7 @@ describe("Moves - Dragon Tail", () => { await game.toNextTurn(); // Make sure the player's field is not empty and has a revived Pokemon - const dratini = game.scene.getPlayerPokemon()!; + const dratini = game.field.getPlayerPokemon(); expect(dratini).toBeDefined(); expect(dratini.hp).toBe(Math.floor(dratini.getMaxHp() / 2)); expect(game.scene.getPlayerField().length).toBe(1); diff --git a/test/moves/electrify.test.ts b/test/moves/electrify.test.ts index 19a2fdfb7a2..10ae2fcd939 100644 --- a/test/moves/electrify.test.ts +++ b/test/moves/electrify.test.ts @@ -36,8 +36,8 @@ describe("Moves - Electrify", () => { it("should convert attacks to Electric type", async () => { await game.classicMode.startBattle([SpeciesId.EXCADRILL]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getMoveType"); game.move.select(MoveId.ELECTRIFY); @@ -54,8 +54,8 @@ describe("Moves - Electrify", () => { await game.classicMode.startBattle([SpeciesId.EXCADRILL]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getPlayerPokemon(); vi.spyOn(enemyPokemon, "getMoveType"); game.move.select(MoveId.ELECTRIFY); diff --git a/test/moves/electro-shot.test.ts b/test/moves/electro-shot.test.ts index e5031fefb3d..4b1303fc930 100644 --- a/test/moves/electro-shot.test.ts +++ b/test/moves/electro-shot.test.ts @@ -38,8 +38,8 @@ describe("Moves - Electro Shot", () => { it("should increase the user's Sp. Atk on the first turn, then attack on the second turn", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ELECTRO_SHOT); @@ -68,8 +68,8 @@ describe("Moves - Electro Shot", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ELECTRO_SHOT); @@ -91,7 +91,7 @@ describe("Moves - Electro Shot", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.ELECTRO_SHOT); diff --git a/test/moves/encore.test.ts b/test/moves/encore.test.ts index 1b0e2ed03f2..0840346c3b1 100644 --- a/test/moves/encore.test.ts +++ b/test/moves/encore.test.ts @@ -39,7 +39,7 @@ describe("Moves - Encore", () => { it("should prevent the target from using any move except the last used move", async () => { await game.classicMode.startBattle([SpeciesId.SNORLAX]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ENCORE); await game.move.selectEnemyMove(MoveId.SPLASH); @@ -67,8 +67,8 @@ describe("Moves - Encore", () => { await game.classicMode.startBattle([SpeciesId.SNORLAX]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); if (delay) { game.move.select(MoveId.SPLASH); @@ -92,23 +92,23 @@ describe("Moves - Encore", () => { game.override.moveset([MoveId.ENCORE, MoveId.TORMENT, MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.ENCORE); await game.setTurnOrder(turnOrder); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.getTag(BattlerTagType.ENCORE)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.ENCORE)).toBeDefined(); await game.toNextTurn(); game.move.select(MoveId.TORMENT); await game.setTurnOrder(turnOrder); await game.phaseInterceptor.to("BerryPhase"); - expect(enemyPokemon?.getTag(BattlerTagType.TORMENT)).toBeDefined(); + expect(enemyPokemon.getTag(BattlerTagType.TORMENT)).toBeDefined(); await game.toNextTurn(); game.move.select(MoveId.SPLASH); await game.setTurnOrder(turnOrder); await game.phaseInterceptor.to("BerryPhase"); - const lastMove = enemyPokemon?.getLastXMoves()[0]; + const lastMove = enemyPokemon.getLastXMoves()[0]; expect(lastMove?.move).toBe(MoveId.STRUGGLE); }); }); diff --git a/test/moves/endure.test.ts b/test/moves/endure.test.ts index 71a16ef4cc1..1bac3c5d6ff 100644 --- a/test/moves/endure.test.ts +++ b/test/moves/endure.test.ts @@ -1,9 +1,10 @@ import { AbilityId } from "#enums/ability-id"; +import { HitResult } from "#enums/hit-result"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Endure", () => { let phaserGame: Phaser.Game; @@ -22,7 +23,7 @@ describe("Moves - Endure", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([MoveId.THUNDER, MoveId.BULLET_SEED, MoveId.TOXIC, MoveId.SHEER_COLD]) + .moveset([MoveId.THUNDER, MoveId.BULLET_SEED, MoveId.SHEER_COLD]) .ability(AbilityId.SKILL_LINK) .startingLevel(100) .battleStyle("single") @@ -32,55 +33,52 @@ describe("Moves - Endure", () => { .enemyMoveset(MoveId.ENDURE); }); - it("should let the pokemon survive with 1 HP", async () => { + it("should let the pokemon survive with 1 HP from attacks", async () => { await game.classicMode.startBattle([SpeciesId.ARCEUS]); game.move.select(MoveId.THUNDER); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.hp).toBe(1); + expect(game.field.getEnemyPokemon().hp).toBe(1); }); - it("should let the pokemon survive with 1 HP when hit with a multihit move", async () => { + it("should let the pokemon survive with 1 HP from multi-strike moves", async () => { await game.classicMode.startBattle([SpeciesId.ARCEUS]); game.move.select(MoveId.BULLET_SEED); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.hp).toBe(1); + expect(game.field.getEnemyPokemon().hp).toBe(1); }); it("should let the pokemon survive against OHKO moves", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.SHEER_COLD); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemy.isFainted()).toBeFalsy(); + expect(enemy.hp).toBe(1); }); // comprehensive indirect damage test copied from Reviver Seed test it.each([ - { moveType: "Damaging Move Chip Damage", move: MoveId.SALT_CURE }, - { moveType: "Chip Damage", move: MoveId.LEECH_SEED }, - { moveType: "Trapping Chip Damage", move: MoveId.WHIRLPOOL }, - { moveType: "Status Effect Damage", move: MoveId.TOXIC }, + { moveType: "Damaging Move Chip", move: MoveId.SALT_CURE }, + { moveType: "Status Move Chip", move: MoveId.LEECH_SEED }, + { moveType: "Partial Trapping move", move: MoveId.WHIRLPOOL }, + { moveType: "Status Effect", move: MoveId.TOXIC }, { moveType: "Weather", move: MoveId.SANDSTORM }, - ])("should not prevent fainting from $moveType", async ({ move }) => { - game.override - .enemyLevel(1) - .startingLevel(100) - .enemySpecies(SpeciesId.MAGIKARP) - .moveset(move) - .enemyMoveset(MoveId.ENDURE); + ])("should not prevent fainting from $moveType Damage", async ({ move }) => { + game.override.moveset(move).enemyLevel(100); await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; - enemy.damageAndUpdate(enemy.hp - 1); + const enemy = game.field.getEnemyPokemon(); + enemy.hp = 2; + // force attack to do 1 dmg (for salt cure) + vi.spyOn(enemy, "getAttackDamage").mockReturnValue({ cancelled: false, result: HitResult.EFFECTIVE, damage: 1 }); game.move.select(move); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemy.isFainted()).toBeTruthy(); + expect(enemy.isFainted()).toBe(true); }); }); diff --git a/test/moves/entrainment.test.ts b/test/moves/entrainment.test.ts index c96cacc8d0b..8d5d69458cb 100644 --- a/test/moves/entrainment.test.ts +++ b/test/moves/entrainment.test.ts @@ -38,7 +38,7 @@ describe("Moves - Entrainment", () => { game.move.select(MoveId.ENTRAINMENT); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.ADAPTABILITY); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.ADAPTABILITY); }); it("should activate post-summon abilities", async () => { @@ -48,6 +48,6 @@ describe("Moves - Entrainment", () => { game.move.select(MoveId.ENTRAINMENT); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/moves/fake-out.test.ts b/test/moves/fake-out.test.ts index 8db73681f05..22ff8e2c304 100644 --- a/test/moves/fake-out.test.ts +++ b/test/moves/fake-out.test.ts @@ -33,7 +33,7 @@ describe("Moves - Fake Out", () => { it("should only work the first turn a pokemon is sent out in a battle", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const corv = game.scene.getEnemyPokemon()!; + const corv = game.field.getEnemyPokemon(); game.move.select(MoveId.FAKE_OUT); await game.toNextTurn(); @@ -52,14 +52,14 @@ describe("Moves - Fake Out", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); // set hp to 1 for easy knockout - game.scene.getEnemyPokemon()!.hp = 1; + game.field.getEnemyPokemon().hp = 1; game.move.select(MoveId.FAKE_OUT); await game.toNextWave(); game.move.select(MoveId.FAKE_OUT); await game.toNextTurn(); - const corv = game.scene.getEnemyPokemon()!; + const corv = game.field.getEnemyPokemon(); expect(corv).toBeDefined(); expect(corv?.hp).toBeLessThan(corv?.getMaxHp()); }); @@ -69,14 +69,14 @@ describe("Moves - Fake Out", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); // set hp to 1 for easy knockout - game.scene.getEnemyPokemon()!.hp = 1; + game.field.getEnemyPokemon().hp = 1; game.move.select(MoveId.FAKE_OUT); await game.toNextWave(); game.move.select(MoveId.FAKE_OUT); await game.toNextTurn(); - const corv = game.scene.getEnemyPokemon()!; + const corv = game.field.getEnemyPokemon(); expect(corv).toBeDefined(); expect(corv.hp).toBeLessThan(corv.getMaxHp()); }); @@ -87,7 +87,7 @@ describe("Moves - Fake Out", () => { game.move.select(MoveId.FAKE_OUT); await game.toNextTurn(); - const corv = game.scene.getEnemyPokemon()!; + const corv = game.field.getEnemyPokemon(); expect(corv.hp).toBeLessThan(corv.getMaxHp()); corv.hp = corv.getMaxHp(); diff --git a/test/moves/false-swipe.test.ts b/test/moves/false-swipe.test.ts index 30547036e69..40408ad523b 100644 --- a/test/moves/false-swipe.test.ts +++ b/test/moves/false-swipe.test.ts @@ -36,8 +36,8 @@ describe("Moves - False Swipe", () => { it("should reduce the target to 1 HP", async () => { await game.classicMode.startBattle([SpeciesId.MILOTIC]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.FALSE_SWIPE); await game.toNextTurn(); diff --git a/test/moves/fell-stinger.test.ts b/test/moves/fell-stinger.test.ts index 9f482202c47..ede70b7af9b 100644 --- a/test/moves/fell-stinger.test.ts +++ b/test/moves/fell-stinger.test.ts @@ -41,7 +41,7 @@ describe("Moves - Fell Stinger", () => { game.override.enemyMoveset([MoveId.DOUBLE_EDGE]); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); await game.phaseInterceptor.to("VictoryPhase"); @@ -53,7 +53,7 @@ describe("Moves - Fell Stinger", () => { game.override.enemyMoveset(MoveId.SPLASH).enemyStatusEffect(StatusEffect.BURN); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); await game.phaseInterceptor.to("VictoryPhase"); @@ -65,7 +65,7 @@ describe("Moves - Fell Stinger", () => { game.override.weather(WeatherType.HAIL); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); @@ -78,7 +78,7 @@ describe("Moves - Fell Stinger", () => { game.override.enemyPassiveAbility(AbilityId.STURDY).enemyAbility(AbilityId.DRY_SKIN).weather(WeatherType.HARSH_SUN); await game.challengeMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); @@ -91,7 +91,7 @@ describe("Moves - Fell Stinger", () => { game.override.enemyAbility(AbilityId.BALL_FETCH).enemyHeldItems([{ name: "REVIVER_SEED" }]); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); await game.phaseInterceptor.to("TurnEndPhase"); @@ -106,7 +106,7 @@ describe("Moves - Fell Stinger", () => { vi.spyOn(fellStinger, "power", "get").mockReturnValue(50000); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const leftEnemy = game.scene.getEnemyField()[0]!; // Turn 1: set Salt Cure, enemy splashes and does nothing @@ -129,7 +129,7 @@ describe("Moves - Fell Stinger", () => { vi.spyOn(allMoves[MoveId.FELL_STINGER], "power", "get").mockReturnValue(50000); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const leftEnemy = game.scene.getEnemyField()[0]!; // Turn 1: set Bind, enemy splashes and does nothing @@ -152,7 +152,7 @@ describe("Moves - Fell Stinger", () => { vi.spyOn(allMoves[MoveId.FELL_STINGER], "power", "get").mockReturnValue(50000); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const leftEnemy = game.scene.getEnemyField()[0]!; // Turn 1: set Leech Seed, enemy splashes and does nothing @@ -173,11 +173,11 @@ describe("Moves - Fell Stinger", () => { game.override.enemyAbility(AbilityId.KLUTZ); await game.classicMode.startBattle([SpeciesId.LEAVANNY]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FELL_STINGER); await game.phaseInterceptor.to("TurnEndPhase"); - expect(leadPokemon?.getStatStage(Stat.ATK)).toBe(3); + expect(leadPokemon.getStatStage(Stat.ATK)).toBe(3); }); }); diff --git a/test/moves/fillet-away.test.ts b/test/moves/fillet-away.test.ts index c4c87e1d00e..e6eb003c9ea 100644 --- a/test/moves/fillet-away.test.ts +++ b/test/moves/fillet-away.test.ts @@ -42,7 +42,7 @@ describe("Moves - FILLET AWAY", () => { test("raises the user's ATK, SPATK, and SPD stat stages by 2 each, at the cost of 1/2 of its maximum HP", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); game.move.select(MoveId.FILLET_AWAY); @@ -57,7 +57,7 @@ describe("Moves - FILLET AWAY", () => { test("still takes effect if one or more of the involved stat stages are not at max", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); //Here - Stat.SPD -> 0 and Stat.SPATK -> 3 @@ -76,7 +76,7 @@ describe("Moves - FILLET AWAY", () => { test("fails if all stat stages involved are at max", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.setStatStage(Stat.ATK, 6); leadPokemon.setStatStage(Stat.SPATK, 6); @@ -94,7 +94,7 @@ describe("Moves - FILLET AWAY", () => { test("fails if the user's health is less than 1/2", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); const hpLost = toDmgValue(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/test/moves/fissure.test.ts b/test/moves/fissure.test.ts index 8a8673811ce..b22b81906a6 100644 --- a/test/moves/fissure.test.ts +++ b/test/moves/fissure.test.ts @@ -43,7 +43,7 @@ describe("Moves - Fissure", () => { await game.classicMode.startBattle(); partyPokemon = game.scene.getPlayerParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon()!; + enemyPokemon = game.field.getEnemyPokemon(); }); it("ignores damage modification from abilities, for example FUR_COAT", async () => { diff --git a/test/moves/flower-shield.test.ts b/test/moves/flower-shield.test.ts index 425d0443ca7..5fc671895d3 100644 --- a/test/moves/flower-shield.test.ts +++ b/test/moves/flower-shield.test.ts @@ -38,8 +38,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(SpeciesId.CHERRIM); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const cherrim = game.scene.getEnemyPokemon()!; - const magikarp = game.scene.getPlayerPokemon()!; + const cherrim = game.field.getEnemyPokemon(); + const magikarp = game.field.getPlayerPokemon(); expect(magikarp.getStatStage(Stat.DEF)).toBe(0); expect(cherrim.getStatStage(Stat.DEF)).toBe(0); @@ -78,8 +78,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(SpeciesId.PARAS).enemyMoveset(MoveId.DIG).enemyLevel(50); await game.classicMode.startBattle([SpeciesId.CHERRIM]); - const paras = game.scene.getEnemyPokemon()!; - const cherrim = game.scene.getPlayerPokemon()!; + const paras = game.field.getEnemyPokemon(); + const cherrim = game.field.getPlayerPokemon(); expect(paras.getStatStage(Stat.DEF)).toBe(0); expect(cherrim.getStatStage(Stat.DEF)).toBe(0); @@ -97,8 +97,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(SpeciesId.MAGIKARP); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; - const ally = game.scene.getPlayerPokemon()!; + const enemy = game.field.getEnemyPokemon(); + const ally = game.field.getPlayerPokemon(); expect(enemy.getStatStage(Stat.DEF)).toBe(0); expect(ally.getStatStage(Stat.DEF)).toBe(0); diff --git a/test/moves/fly.test.ts b/test/moves/fly.test.ts index 3682fce3d0c..dc40b4a439b 100644 --- a/test/moves/fly.test.ts +++ b/test/moves/fly.test.ts @@ -41,8 +41,8 @@ describe("Moves - Fly", () => { it("should make the user semi-invulnerable, then attack over 2 turns", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FLY); @@ -67,8 +67,8 @@ describe("Moves - Fly", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FLY); @@ -82,7 +82,7 @@ describe("Moves - Fly", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.FLY); @@ -99,8 +99,8 @@ describe("Moves - Fly", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FLY); diff --git a/test/moves/focus-punch.test.ts b/test/moves/focus-punch.test.ts index c67053ef7ec..9a76dbec0db 100644 --- a/test/moves/focus-punch.test.ts +++ b/test/moves/focus-punch.test.ts @@ -41,8 +41,8 @@ describe("Moves - Focus Punch", () => { it("should deal damage at the end of turn if uninterrupted", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartingHp = enemyPokemon.hp; @@ -65,8 +65,8 @@ describe("Moves - Focus Punch", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); const enemyStartingHp = enemyPokemon.hp; @@ -89,8 +89,8 @@ describe("Moves - Focus Punch", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FOCUS_PUNCH); diff --git a/test/moves/foresight.test.ts b/test/moves/foresight.test.ts index c3b29d8fabb..7de628ba172 100644 --- a/test/moves/foresight.test.ts +++ b/test/moves/foresight.test.ts @@ -33,7 +33,7 @@ describe("Moves - Foresight", () => { it("should allow Normal and Fighting moves to hit Ghost types", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.QUICK_ATTACK); await game.toNextTurn(); @@ -57,7 +57,7 @@ describe("Moves - Foresight", () => { game.override.enemyMoveset([MoveId.MINIMIZE]); await game.classicMode.startBattle(); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); vi.spyOn(pokemon, "getAccuracyMultiplier"); game.move.select(MoveId.FORESIGHT); diff --git a/test/moves/forests-curse.test.ts b/test/moves/forests-curse.test.ts index 4467a5c4037..db88c1f79c4 100644 --- a/test/moves/forests-curse.test.ts +++ b/test/moves/forests-curse.test.ts @@ -35,13 +35,13 @@ describe("Moves - Forest's Curse", () => { it("will replace the added type from Trick Or Treat", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.TRICK_OR_TREAT); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemyPokemon!.summonData.addedType).toBe(PokemonType.GHOST); + expect(enemyPokemon.summonData.addedType).toBe(PokemonType.GHOST); game.move.select(MoveId.FORESTS_CURSE); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemyPokemon?.summonData.addedType).toBe(PokemonType.GRASS); + expect(enemyPokemon.summonData.addedType).toBe(PokemonType.GRASS); }); }); diff --git a/test/moves/freeze-dry.test.ts b/test/moves/freeze-dry.test.ts index 5d84d6be795..0b22d4f0997 100644 --- a/test/moves/freeze-dry.test.ts +++ b/test/moves/freeze-dry.test.ts @@ -36,7 +36,7 @@ describe("Moves - Freeze-Dry", () => { it("should deal 2x damage to pure water types", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -50,7 +50,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.WINGULL); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -64,7 +64,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.VOLCANION); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -85,7 +85,7 @@ describe("Moves - Freeze-Dry", () => { .moveset([MoveId.SOAK, MoveId.FREEZE_DRY]); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.SOAK); @@ -103,7 +103,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.QUAGSIRE); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FORESTS_CURSE); @@ -120,7 +120,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.SKARMORY); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.teraType = PokemonType.WATER; enemy.isTerastallized = true; vi.spyOn(enemy, "getMoveEffectiveness"); @@ -136,7 +136,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.PELIPPER); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.teraType = PokemonType.FIRE; enemy.isTerastallized = true; vi.spyOn(enemy, "getMoveEffectiveness"); @@ -152,7 +152,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemySpecies(SpeciesId.TERAPAGOS).enemyAbility(AbilityId.TERA_SHELL); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.SOAK); @@ -169,7 +169,7 @@ describe("Moves - Freeze-Dry", () => { game.override.ability(AbilityId.NORMALIZE); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -183,7 +183,7 @@ describe("Moves - Freeze-Dry", () => { game.override.ability(AbilityId.NORMALIZE).enemySpecies(SpeciesId.SHIELDON); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -197,7 +197,7 @@ describe("Moves - Freeze-Dry", () => { game.override.ability(AbilityId.NORMALIZE).enemySpecies(SpeciesId.JELLICENT); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -211,7 +211,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemyMoveset([MoveId.ELECTRIFY]); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -225,7 +225,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemyMoveset([MoveId.ELECTRIFY]).enemySpecies(SpeciesId.GYARADOS); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -239,7 +239,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemyMoveset([MoveId.ELECTRIFY]).enemySpecies(SpeciesId.BARBOACH); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -253,7 +253,7 @@ describe("Moves - Freeze-Dry", () => { game.override.enemyMoveset([MoveId.ELECTRIFY]).enemySpecies(SpeciesId.FLAPPLE); await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -269,7 +269,7 @@ describe("Moves - Freeze-Dry", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -285,7 +285,7 @@ describe("Moves - Freeze-Dry", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -301,7 +301,7 @@ describe("Moves - Freeze-Dry", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); @@ -318,7 +318,7 @@ describe("Moves - Freeze-Dry", () => { await game.challengeMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); game.move.select(MoveId.FREEZE_DRY); diff --git a/test/moves/freezy-frost.test.ts b/test/moves/freezy-frost.test.ts index 8a8a47013ca..e25c29c9302 100644 --- a/test/moves/freezy-frost.test.ts +++ b/test/moves/freezy-frost.test.ts @@ -37,8 +37,8 @@ describe("Moves - Freezy Frost", () => { it("should clear stat changes of user and opponent", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const user = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const user = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.HOWL); await game.toNextTurn(); @@ -56,7 +56,7 @@ describe("Moves - Freezy Frost", () => { it("should clear all stat changes even when enemy uses the move", async () => { game.override.enemyMoveset(MoveId.FREEZY_FROST); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); // Shuckle for slower Howl on first turn so Freezy Frost doesn't affect it. - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); game.move.select(MoveId.HOWL); await game.toNextTurn(); diff --git a/test/moves/fusion-bolt.test.ts b/test/moves/fusion-bolt.test.ts index aa8073c4c1f..9594dcfd15c 100644 --- a/test/moves/fusion-bolt.test.ts +++ b/test/moves/fusion-bolt.test.ts @@ -37,7 +37,7 @@ describe("Moves - Fusion Bolt", () => { it("should not make contact", async () => { await game.classicMode.startBattle([SpeciesId.ZEKROM]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); const initialHp = partyMember.hp; game.move.select(fusionBolt); diff --git a/test/moves/fusion-flare.test.ts b/test/moves/fusion-flare.test.ts index be8ce4eeec4..dd8ae11683d 100644 --- a/test/moves/fusion-flare.test.ts +++ b/test/moves/fusion-flare.test.ts @@ -37,7 +37,7 @@ describe("Moves - Fusion Flare", () => { it("should thaw freeze status condition", async () => { await game.classicMode.startBattle([SpeciesId.RESHIRAM]); - const partyMember = game.scene.getPlayerPokemon()!; + const partyMember = game.field.getPlayerPokemon(); game.move.select(fusionFlare); diff --git a/test/moves/geomancy.test.ts b/test/moves/geomancy.test.ts index b01ad756f9b..df5bb1b4b97 100644 --- a/test/moves/geomancy.test.ts +++ b/test/moves/geomancy.test.ts @@ -37,7 +37,7 @@ describe("Moves - Geomancy", () => { it("should boost the user's stats on the second turn of use", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); const affectedStats: EffectiveStat[] = [Stat.SPATK, Stat.SPDEF, Stat.SPD]; game.move.select(MoveId.GEOMANCY); @@ -58,7 +58,7 @@ describe("Moves - Geomancy", () => { it("should execute over 2 turns between waves", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); const affectedStats: EffectiveStat[] = [Stat.SPATK, Stat.SPDEF, Stat.SPD]; game.move.select(MoveId.GEOMANCY); diff --git a/test/moves/gigaton-hammer.test.ts b/test/moves/gigaton-hammer.test.ts index 6043f9d7f51..e5009310de6 100644 --- a/test/moves/gigaton-hammer.test.ts +++ b/test/moves/gigaton-hammer.test.ts @@ -35,7 +35,7 @@ describe("Moves - Gigaton Hammer", () => { it("can't be used two turns in a row", async () => { await game.classicMode.startBattle(); - const enemy1 = game.scene.getEnemyPokemon()!; + const enemy1 = game.field.getEnemyPokemon(); game.move.select(MoveId.GIGATON_HAMMER); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -49,7 +49,7 @@ describe("Moves - Gigaton Hammer", () => { game.move.select(MoveId.GIGATON_HAMMER); await game.toNextTurn(); - const enemy2 = game.scene.getEnemyPokemon()!; + const enemy2 = game.field.getEnemyPokemon(); expect(enemy2.hp).toBe(enemy2.getMaxHp()); }); @@ -58,7 +58,7 @@ describe("Moves - Gigaton Hammer", () => { game.override.startingWave(4); await game.classicMode.startBattle(); - const enemy1 = game.scene.getEnemyPokemon()!; + const enemy1 = game.field.getEnemyPokemon(); game.move.select(MoveId.GIGATON_HAMMER); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -72,7 +72,7 @@ describe("Moves - Gigaton Hammer", () => { game.move.select(MoveId.GIGATON_HAMMER); await game.toNextTurn(); - const enemy2 = game.scene.getEnemyPokemon()!; + const enemy2 = game.field.getEnemyPokemon(); expect(enemy2.hp).toBeLessThan(enemy2.getMaxHp()); }); diff --git a/test/moves/glaive-rush.test.ts b/test/moves/glaive-rush.test.ts index f20abd68500..c16bd338932 100644 --- a/test/moves/glaive-rush.test.ts +++ b/test/moves/glaive-rush.test.ts @@ -35,7 +35,7 @@ describe("Moves - Glaive Rush", () => { it("takes double damage from attacks", async () => { await game.classicMode.startBattle([SpeciesId.KLINK]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; game.move.select(MoveId.SHADOW_SNEAK); @@ -50,7 +50,7 @@ describe("Moves - Glaive Rush", () => { it("always gets hit by attacks", async () => { await game.classicMode.startBattle([SpeciesId.KLINK]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; allMoves[MoveId.AVALANCHE].accuracy = 0; @@ -63,8 +63,8 @@ describe("Moves - Glaive Rush", () => { game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]).enemyMoveset([MoveId.AVALANCHE]); await game.classicMode.startBattle([SpeciesId.KLINK]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; player.hp = 1000; @@ -83,8 +83,8 @@ describe("Moves - Glaive Rush", () => { game.override.enemyMoveset([MoveId.SHADOW_SNEAK]); await game.classicMode.startBattle([SpeciesId.KLINK]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; player.hp = 1000; @@ -108,8 +108,8 @@ describe("Moves - Glaive Rush", () => { game.override.enemyMoveset([MoveId.SHADOW_SNEAK]); await game.classicMode.startBattle([SpeciesId.KLINK, SpeciesId.FEEBAS]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; allMoves[MoveId.SHADOW_SNEAK].accuracy = 0; @@ -131,8 +131,8 @@ describe("Moves - Glaive Rush", () => { .enemyMoveset([MoveId.GLAIVE_RUSH, MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.KLINK]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.hp = 1000; player.hp = 1000; diff --git a/test/moves/growth.test.ts b/test/moves/growth.test.ts index 5737f32d891..4c892f0dee2 100644 --- a/test/moves/growth.test.ts +++ b/test/moves/growth.test.ts @@ -35,7 +35,7 @@ describe("Moves - Growth", () => { it("should raise SPATK stat stage by 1", async () => { await game.classicMode.startBattle([SpeciesId.MIGHTYENA]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); diff --git a/test/moves/grudge.test.ts b/test/moves/grudge.test.ts index d9e2f4f8320..cc75024bfbf 100644 --- a/test/moves/grudge.test.ts +++ b/test/moves/grudge.test.ts @@ -1,5 +1,6 @@ import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; @@ -59,6 +60,8 @@ describe("Moves - Grudge", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextTurn(); + expect(ratatta).toHaveBattlerTag(BattlerTagType.GRUDGE); + game.move.use(MoveId.GUILLOTINE); await game.move.forceEnemyMove(MoveId.SPLASH); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/moves/guard-split.test.ts b/test/moves/guard-split.test.ts index fa5dc162a6c..316807f68aa 100644 --- a/test/moves/guard-split.test.ts +++ b/test/moves/guard-split.test.ts @@ -36,8 +36,8 @@ describe("Moves - Guard Split", () => { game.override.enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const avgDef = Math.floor((player.getStat(Stat.DEF, false) + enemy.getStat(Stat.DEF, false)) / 2); const avgSpDef = Math.floor((player.getStat(Stat.SPDEF, false) + enemy.getStat(Stat.SPDEF, false)) / 2); @@ -56,8 +56,8 @@ describe("Moves - Guard Split", () => { game.override.enemyMoveset([MoveId.GUARD_SPLIT]); await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const avgDef = Math.floor((player.getStat(Stat.DEF, false) + enemy.getStat(Stat.DEF, false)) / 2); const avgSpDef = Math.floor((player.getStat(Stat.SPDEF, false) + enemy.getStat(Stat.SPDEF, false)) / 2); diff --git a/test/moves/guard-swap.test.ts b/test/moves/guard-swap.test.ts index e7f8ed1df91..4d30c437a66 100644 --- a/test/moves/guard-swap.test.ts +++ b/test/moves/guard-swap.test.ts @@ -36,8 +36,8 @@ describe("Moves - Guard Swap", () => { it("should swap the user's DEF and SPDEF stat stages with the target's", async () => { await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); diff --git a/test/moves/hard-press.test.ts b/test/moves/hard-press.test.ts index f269373e697..70563052b98 100644 --- a/test/moves/hard-press.test.ts +++ b/test/moves/hard-press.test.ts @@ -49,7 +49,7 @@ describe("Moves - Hard Press", () => { it("should return 50 power if target HP ratio is at 50%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); const targetHpRatio = 0.5; - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -62,7 +62,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is at 1%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); const targetHpRatio = 0.01; - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -75,7 +75,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is less than 1%", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); const targetHpRatio = 0.005; - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); diff --git a/test/moves/haze.test.ts b/test/moves/haze.test.ts index 179a06fba09..1e16b499fa2 100644 --- a/test/moves/haze.test.ts +++ b/test/moves/haze.test.ts @@ -36,8 +36,8 @@ describe("Moves - Haze", () => { it("should reset all stat changes of all Pokemon on field", async () => { await game.classicMode.startBattle([SpeciesId.RATTATA]); - const user = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const user = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); expect(user.getStatStage(Stat.ATK)).toBe(0); expect(enemy.getStatStage(Stat.ATK)).toBe(0); diff --git a/test/moves/heal-block.test.ts b/test/moves/heal-block.test.ts index fc814fda4bc..4c8e6395171 100644 --- a/test/moves/heal-block.test.ts +++ b/test/moves/heal-block.test.ts @@ -38,8 +38,8 @@ describe("Moves - Heal Block", () => { it("shouldn't stop damage from HP-drain attacks, just HP restoration", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); player.damageAndUpdate(player.getMaxHp() - 1); @@ -56,8 +56,8 @@ describe("Moves - Heal Block", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.ABSORB); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -70,7 +70,7 @@ describe("Moves - Heal Block", () => { it("should prevent Wish from restoring HP", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.field.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.hp = 1; @@ -94,7 +94,7 @@ describe("Moves - Heal Block", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.getMaxHp() - 1); @@ -107,7 +107,7 @@ describe("Moves - Heal Block", () => { it("should prevent healing from heal-over-time moves", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.getMaxHp() - 1); @@ -123,7 +123,7 @@ describe("Moves - Heal Block", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.getMaxHp() - 1); @@ -138,7 +138,7 @@ describe("Moves - Heal Block", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.damageAndUpdate(player.getMaxHp() - 1); game.move.select(MoveId.SPLASH); diff --git a/test/moves/heart-swap.test.ts b/test/moves/heart-swap.test.ts index e876ec06646..f192f74558d 100644 --- a/test/moves/heart-swap.test.ts +++ b/test/moves/heart-swap.test.ts @@ -36,8 +36,8 @@ describe("Moves - Heart Swap", () => { it("should swap all of the user's stat stages with the target's", async () => { await game.classicMode.startBattle([SpeciesId.MANAPHY]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); diff --git a/test/moves/hyper-beam.test.ts b/test/moves/hyper-beam.test.ts index 510dac5f662..c61d503d627 100644 --- a/test/moves/hyper-beam.test.ts +++ b/test/moves/hyper-beam.test.ts @@ -40,8 +40,8 @@ describe("Moves - Hyper Beam", () => { it("should force the user to recharge on the next turn (and only that turn)", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.HYPER_BEAM); diff --git a/test/moves/imprison.test.ts b/test/moves/imprison.test.ts index ed2213bbc7b..3e971707d6e 100644 --- a/test/moves/imprison.test.ts +++ b/test/moves/imprison.test.ts @@ -33,7 +33,7 @@ describe("Moves - Imprison", () => { it("Pokemon under Imprison cannot use shared moves", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.TRANSFORM); await game.move.selectEnemyMove(MoveId.IMPRISON); @@ -60,7 +60,7 @@ describe("Moves - Imprison", () => { it("Imprison applies to Pokemon switched into Battle", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI, SpeciesId.BULBASAUR]); - const playerPokemon1 = game.scene.getPlayerPokemon()!; + const playerPokemon1 = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.move.selectEnemyMove(MoveId.IMPRISON); @@ -74,7 +74,7 @@ describe("Moves - Imprison", () => { game.doSwitchPokemon(1); await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - const playerPokemon2 = game.scene.getPlayerPokemon()!; + const playerPokemon2 = game.field.getPlayerPokemon(); const imprisonBattlerTag2 = playerPokemon2.getTag(BattlerTagType.IMPRISON); expect(playerPokemon1).not.toEqual(playerPokemon2); expect(imprisonBattlerTag2).toBeDefined(); @@ -84,8 +84,8 @@ describe("Moves - Imprison", () => { game.override.moveset([MoveId.SPLASH, MoveId.IMPRISON]); await game.classicMode.startBattle([SpeciesId.REGIELEKI, SpeciesId.BULBASAUR]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.IMPRISON); await game.move.selectEnemyMove(MoveId.GROWL); await game.toNextTurn(); diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index c34626f5e76..27318105783 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -50,7 +50,7 @@ describe("Moves - Instruct", () => { game.override.moveset(MoveId.INSTRUCT).enemyLevel(1000); // ensures shuckle no die await game.classicMode.startBattle([SpeciesId.AMOONGUSS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.changeMoveset(enemy, MoveId.SONIC_BOOM); game.move.select(MoveId.INSTRUCT); @@ -60,7 +60,7 @@ describe("Moves - Instruct", () => { await game.phaseInterceptor.to("MovePhase"); // enemy attacks us await game.phaseInterceptor.to("MovePhase", false); // instruct let currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; - expect(currentPhase.pokemon).toBe(game.scene.getPlayerPokemon()); + expect(currentPhase.pokemon).toBe(game.field.getPlayerPokemon()); await game.phaseInterceptor.to("MoveEndPhase"); await game.phaseInterceptor.to("MovePhase", false); // enemy repeats move @@ -70,14 +70,14 @@ describe("Moves - Instruct", () => { await game.phaseInterceptor.to("TurnEndPhase", false); instructSuccess(enemy, MoveId.SONIC_BOOM); - expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); + expect(game.field.getPlayerPokemon().getInverseHp()).toBe(40); }); it("should repeat enemy's move through substitute", async () => { game.override.moveset([MoveId.INSTRUCT, MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.AMOONGUSS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.changeMoveset(enemy, [MoveId.SONIC_BOOM, MoveId.SUBSTITUTE]); game.move.select(MoveId.SPLASH); @@ -90,8 +90,8 @@ describe("Moves - Instruct", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("TurnEndPhase", false); - instructSuccess(game.scene.getEnemyPokemon()!, MoveId.SONIC_BOOM); - expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); + instructSuccess(game.field.getEnemyPokemon(), MoveId.SONIC_BOOM); + expect(game.field.getPlayerPokemon().getInverseHp()).toBe(40); }); it("should repeat ally's attack on enemy", async () => { @@ -116,7 +116,7 @@ describe("Moves - Instruct", () => { game.override.moveset(MoveId.INSTRUCT).enemyLevel(5); await game.classicMode.startBattle([SpeciesId.AMOONGUSS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.changeMoveset(enemy, [MoveId.GIGATON_HAMMER, MoveId.BLOOD_MOON]); game.move.select(MoveId.INSTRUCT); @@ -124,7 +124,7 @@ describe("Moves - Instruct", () => { await game.phaseInterceptor.to("BerryPhase"); instructSuccess(enemy, MoveId.GIGATON_HAMMER); - expect(game.scene.getPlayerPokemon()!.turnData.attacksReceived.length).toBe(2); + expect(game.field.getPlayerPokemon().turnData.attacksReceived.length).toBe(2); }); it("should add moves to move queue for copycat", async () => { @@ -141,7 +141,7 @@ describe("Moves - Instruct", () => { instructSuccess(enemy1, MoveId.WATER_GUN); // amoonguss gets hit by water gun thrice; once by original attack, once by instructed use and once by copycat - expect(game.scene.getPlayerPokemon()!.turnData.attacksReceived.length).toBe(3); + expect(game.field.getPlayerPokemon().turnData.attacksReceived.length).toBe(3); }); it("should fail on metronomed moves, even if also in moveset", async () => { @@ -175,16 +175,16 @@ describe("Moves - Instruct", () => { await game.move.forceStatusActivation(false); await game.phaseInterceptor.to("TurnEndPhase", false); - const moveHistory = game.scene.getEnemyPokemon()?.getLastXMoves(-1)!; + const moveHistory = game.field.getEnemyPokemon().getLastXMoves(-1)!; expect(moveHistory.map(m => m.move)).toEqual([MoveId.SONIC_BOOM, MoveId.NONE, MoveId.SONIC_BOOM]); - expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); + expect(game.field.getPlayerPokemon().getInverseHp()).toBe(40); }); it("should not repeat enemy's out of pp move", async () => { game.override.moveset(MoveId.INSTRUCT).enemySpecies(SpeciesId.UNOWN); await game.classicMode.startBattle([SpeciesId.AMOONGUSS]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.changeMoveset(enemyPokemon, MoveId.HIDDEN_POWER); const moveUsed = enemyPokemon.moveset.find(m => m?.moveId === MoveId.HIDDEN_POWER)!; moveUsed.ppUsed = moveUsed.getMovePp() - 1; @@ -258,7 +258,7 @@ describe("Moves - Instruct", () => { game.override.enemyMoveset(MoveId.INSTRUCT).enemySpecies(SpeciesId.UNOWN); await game.classicMode.startBattle([SpeciesId.AMOONGUSS, SpeciesId.TOXICROAK]); - const amoonguss = game.scene.getPlayerPokemon()!; + const amoonguss = game.field.getPlayerPokemon(); game.move.changeMoveset(amoonguss, MoveId.SEED_BOMB); amoonguss.pushMoveHistory({ @@ -284,7 +284,7 @@ describe("Moves - Instruct", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should attempt to call enemy's disabled move, but move use itself should fail", async () => { @@ -310,7 +310,7 @@ describe("Moves - Instruct", () => { game.override.moveset([MoveId.INSTRUCT]); await game.classicMode.startBattle([SpeciesId.AMOONGUSS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.changeMoveset(enemy, MoveId.PROTECT); game.move.select(MoveId.INSTRUCT); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -325,8 +325,8 @@ describe("Moves - Instruct", () => { game.override.moveset([MoveId.INSTRUCT]).enemyMoveset([MoveId.SONIC_BOOM, MoveId.HYPER_BEAM]); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.pushMoveHistory({ move: MoveId.SONIC_BOOM, targets: [BattlerIndex.PLAYER], @@ -353,7 +353,7 @@ describe("Moves - Instruct", () => { game.override.enemyMoveset(MoveId.INSTRUCT); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const regieleki = game.scene.getPlayerPokemon()!; + const regieleki = game.field.getPlayerPokemon(); regieleki.pushMoveHistory({ move: MoveId.ELECTRO_DRIFT, targets: [BattlerIndex.PLAYER], @@ -371,7 +371,7 @@ describe("Moves - Instruct", () => { game.override.enemyMoveset([MoveId.SPLASH, MoveId.WHIRLWIND]).moveset(MoveId.INSTRUCT); await game.classicMode.startBattle([SpeciesId.LUCARIO, SpeciesId.BANETTE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.pushMoveHistory({ move: MoveId.WHIRLWIND, targets: [BattlerIndex.PLAYER], @@ -387,7 +387,7 @@ describe("Moves - Instruct", () => { const instructedMove = enemyPokemon.getLastXMoves(-1)[1]; expect(instructedMove.result).toBe(MoveResult.SUCCESS); expect(instructedMove.move).toBe(MoveId.WHIRLWIND); - expect(game.scene.getPlayerPokemon()?.species.speciesId).toBe(SpeciesId.BANETTE); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.BANETTE); }); it("should respect moves' original priority for psychic terrain", async () => { @@ -559,7 +559,7 @@ describe("Moves - Instruct", () => { .enemyMoveset(MoveId.BULLET_SEED); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const bulbasaur = game.scene.getPlayerPokemon()!; + const bulbasaur = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.toNextTurn(); diff --git a/test/moves/jaw-lock.test.ts b/test/moves/jaw-lock.test.ts index 919e07ece9a..441c74c7356 100644 --- a/test/moves/jaw-lock.test.ts +++ b/test/moves/jaw-lock.test.ts @@ -42,8 +42,8 @@ describe("Moves - Jaw Lock", () => { it("should trap the move's user and target", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.JAW_LOCK); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -63,8 +63,8 @@ describe("Moves - Jaw Lock", () => { game.override.enemyLevel(1); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.JAW_LOCK); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -88,8 +88,8 @@ describe("Moves - Jaw Lock", () => { it("should only trap the user until the target faints", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.JAW_LOCK); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -140,8 +140,8 @@ describe("Moves - Jaw Lock", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.JAW_LOCK); diff --git a/test/moves/last-resort.test.ts b/test/moves/last-resort.test.ts index e6f4faacd09..ca4ed6726ed 100644 --- a/test/moves/last-resort.test.ts +++ b/test/moves/last-resort.test.ts @@ -13,7 +13,7 @@ describe("Moves - Last Resort", () => { let game: GameManager; function expectLastResortFail() { - expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0]).toEqual( + expect(game.field.getPlayerPokemon().getLastXMoves()[0]).toEqual( expect.objectContaining({ move: MoveId.LAST_RESORT, result: MoveResult.FAIL, @@ -45,7 +45,7 @@ describe("Moves - Last Resort", () => { game.override.moveset([MoveId.LAST_RESORT, MoveId.SPLASH, MoveId.GROWL, MoveId.GROWTH]); await game.classicMode.startBattle([SpeciesId.BLISSEY]); - const blissey = game.scene.getPlayerPokemon()!; + const blissey = game.field.getPlayerPokemon(); expect(blissey).toBeDefined(); // Last resort by itself @@ -69,7 +69,7 @@ describe("Moves - Last Resort", () => { blissey.pushMoveHistory({ move: MoveId.GROWTH, targets: [BattlerIndex.PLAYER], useMode: MoveUseMode.NORMAL }); game.move.select(MoveId.LAST_RESORT); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.getLastXMoves()[0]).toEqual( + expect(game.field.getPlayerPokemon().getLastXMoves()[0]).toEqual( expect.objectContaining({ move: MoveId.LAST_RESORT, result: MoveResult.SUCCESS, @@ -114,7 +114,7 @@ describe("Moves - Last Resort", () => { game.move.select(MoveId.SLEEP_TALK); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.getLastXMoves(-1)).toEqual([ + expect(game.field.getPlayerPokemon().getLastXMoves(-1)).toEqual([ expect.objectContaining({ move: MoveId.LAST_RESORT, result: MoveResult.SUCCESS, @@ -136,15 +136,15 @@ describe("Moves - Last Resort", () => { await game.doKillOpponents(); await game.toNextWave(); - const oldMoveHistory = game.scene.getPlayerPokemon()?.summonData.moveHistory; + const oldMoveHistory = game.field.getPlayerPokemon().summonData.moveHistory; await game.reload.reloadSession(); - const newMoveHistory = game.scene.getPlayerPokemon()?.summonData.moveHistory; + const newMoveHistory = game.field.getPlayerPokemon().summonData.moveHistory; expect(oldMoveHistory).toEqual(newMoveHistory); // use last resort and it should kill the karp just fine game.move.select(MoveId.LAST_RESORT); - game.scene.getEnemyPokemon()!.hp = 1; + game.field.getEnemyPokemon().hp = 1; await game.phaseInterceptor.to("TurnEndPhase"); expect(game.isVictory()).toBe(true); diff --git a/test/moves/light-screen.test.ts b/test/moves/light-screen.test.ts index 04f7f59f184..c8282037f20 100644 --- a/test/moves/light-screen.test.ts +++ b/test/moves/light-screen.test.ts @@ -53,8 +53,8 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -72,8 +72,8 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -88,8 +88,8 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -106,8 +106,8 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); expect(mockedDmg).toBe(allMoves[moveToUse].power); diff --git a/test/moves/lucky-chant.test.ts b/test/moves/lucky-chant.test.ts index 1128f79b1d2..7be75631494 100644 --- a/test/moves/lucky-chant.test.ts +++ b/test/moves/lucky-chant.test.ts @@ -37,7 +37,7 @@ describe("Moves - Lucky Chant", () => { game.override.criticalHits(true); await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const charizard = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); expect(charizard).toBeDefined(); const critSpy = vi.spyOn(charizard, "getCriticalHitResult"); // called on the defender (ie player) @@ -59,7 +59,7 @@ describe("Moves - Lucky Chant", () => { game.override.enemyMoveset(MoveId.FLOWER_TRICK); await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const charizard = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); expect(charizard).toBeDefined(); game.move.select(MoveId.SPLASH); @@ -79,7 +79,7 @@ describe("Moves - Lucky Chant", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const charizard = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); expect(charizard).toBeDefined(); game.move.select(MoveId.FOLLOW_ME, BattlerIndex.PLAYER); diff --git a/test/moves/magic-coat.test.ts b/test/moves/magic-coat.test.ts index 2ed09499e95..7c1c703119d 100644 --- a/test/moves/magic-coat.test.ts +++ b/test/moves/magic-coat.test.ts @@ -43,7 +43,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.PROTECT); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getEnemyPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should fail if called again in the same turn due to moves like instruct", async () => { @@ -52,7 +52,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.INSTRUCT); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getEnemyPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should not reflect moves used on the next turn", async () => { @@ -68,7 +68,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.GROWL); await game.move.selectEnemyMove(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should reflect basic status moves", async () => { @@ -77,7 +77,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should individually bounce back multi-target moves when used by both targets in doubles", async () => { @@ -110,13 +110,13 @@ describe("Moves - Magic Coat", () => { it("should still bounce back a move that would otherwise fail", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.scene.getEnemyPokemon()?.setStatStage(Stat.ATK, -6); + game.field.getEnemyPokemon().setStatStage(Stat.ATK, -6); game.override.moveset([MoveId.GROWL]); game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should not bounce back a move that was just bounced", async () => { @@ -143,7 +143,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should still bounce back a move from a mold breaker user", async () => { @@ -153,8 +153,8 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(0); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should only bounce spikes back once when both targets use magic coat in doubles", async () => { @@ -175,7 +175,7 @@ describe("Moves - Magic Coat", () => { game.move.select(MoveId.CURSE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getTag(BattlerTagType.CURSED)).toBeDefined(); + expect(game.field.getEnemyPokemon().getTag(BattlerTagType.CURSED)).toBeDefined(); }); // TODO: encore is failing if the last move was virtual. @@ -186,7 +186,7 @@ describe("Moves - Magic Coat", () => { .enemyAbility(AbilityId.MAGIC_BOUNCE); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // turn 1 game.move.select(MoveId.GROWL); @@ -226,7 +226,7 @@ describe("Moves - Magic Coat", () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM]; - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(stomping_tantrum, "calculateBattlePower"); game.move.select(MoveId.SPORE); @@ -252,34 +252,34 @@ describe("Moves - Magic Coat", () => { // Turn 1 - thunder wave immunity test game.move.select(MoveId.THUNDER_WAVE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); // Turn 2 - soundproof immunity test game.move.select(MoveId.GROWL); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should bounce back a move before the accuracy check", async () => { game.override.moveset([MoveId.SPORE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const attacker = game.scene.getPlayerPokemon()!; + const attacker = game.field.getPlayerPokemon(); vi.spyOn(attacker, "getAccuracyMultiplier").mockReturnValue(0.0); game.move.select(MoveId.SPORE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.SLEEP); + expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.SLEEP); }); it("should take the accuracy of the magic bounce user into account", async () => { game.override.moveset([MoveId.SPORE]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const opponent = game.scene.getEnemyPokemon()!; + const opponent = game.field.getEnemyPokemon(); vi.spyOn(opponent, "getAccuracyMultiplier").mockReturnValue(0); game.move.select(MoveId.SPORE); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.status).toBeUndefined(); + expect(game.field.getPlayerPokemon().status).toBeUndefined(); }); }); diff --git a/test/moves/make-it-rain.test.ts b/test/moves/make-it-rain.test.ts index ab242d0c3a0..a75c1ae59f8 100644 --- a/test/moves/make-it-rain.test.ts +++ b/test/moves/make-it-rain.test.ts @@ -37,7 +37,7 @@ describe("Moves - Make It Rain", () => { it("should only lower SPATK stat stage by 1 once in a double battle", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.MAKE_IT_RAIN); game.move.select(MoveId.SPLASH, 1); @@ -54,8 +54,8 @@ describe("Moves - Make It Rain", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.MAKE_IT_RAIN); @@ -70,7 +70,7 @@ describe("Moves - Make It Rain", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const enemyPokemon = game.scene.getEnemyField(); game.move.select(MoveId.MAKE_IT_RAIN); @@ -85,7 +85,7 @@ describe("Moves - Make It Rain", () => { it("should lower SPATK stat stage by 1 if it only hits the second target", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.MAKE_IT_RAIN); game.move.select(MoveId.SPLASH, 1); diff --git a/test/moves/metronome.test.ts b/test/moves/metronome.test.ts index e39d24c81db..2215c18f451 100644 --- a/test/moves/metronome.test.ts +++ b/test/moves/metronome.test.ts @@ -44,8 +44,8 @@ describe("Moves - Metronome", () => { it("should have one semi-invulnerable turn and deal damage on the second turn when a semi-invulnerable move is called", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(MoveId.DIVE); game.move.select(MoveId.METRONOME); @@ -60,7 +60,7 @@ describe("Moves - Metronome", () => { it("should apply secondary effects of a move", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(MoveId.WOOD_HAMMER); game.move.select(MoveId.METRONOME); @@ -71,7 +71,7 @@ describe("Moves - Metronome", () => { it("should recharge after using recharge move", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(MoveId.HYPER_BEAM); vi.spyOn(allMoves[MoveId.HYPER_BEAM], "accuracy", "get").mockReturnValue(100); @@ -137,7 +137,7 @@ describe("Moves - Metronome", () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(MoveId.ROAR); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.METRONOME); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/moves/miracle-eye.test.ts b/test/moves/miracle-eye.test.ts index 9ed3824fc28..8ea6653eeb4 100644 --- a/test/moves/miracle-eye.test.ts +++ b/test/moves/miracle-eye.test.ts @@ -34,7 +34,7 @@ describe("Moves - Miracle Eye", () => { it("should allow Psychic moves to hit Dark types", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.CONFUSION); await game.toNextTurn(); diff --git a/test/moves/mirror-move.test.ts b/test/moves/mirror-move.test.ts index 0253932026b..50ea4274f49 100644 --- a/test/moves/mirror-move.test.ts +++ b/test/moves/mirror-move.test.ts @@ -56,7 +56,7 @@ describe("Moves - Mirror Move", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2); + expect(game.field.getEnemyPokemon().getStatStage(Stat.SPDEF)).toBe(-2); }); it("should be able to copy status moves", async () => { @@ -67,7 +67,7 @@ describe("Moves - Mirror Move", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should fail if the target has not used any moves", async () => { @@ -77,6 +77,6 @@ describe("Moves - Mirror Move", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); }); diff --git a/test/moves/nightmare.test.ts b/test/moves/nightmare.test.ts index 7b3772d8b71..0b7542f89d1 100644 --- a/test/moves/nightmare.test.ts +++ b/test/moves/nightmare.test.ts @@ -36,7 +36,7 @@ describe("Moves - Nightmare", () => { it("lowers enemy hp by 1/4 each turn while asleep", async () => { await game.classicMode.startBattle([SpeciesId.HYPNO]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const enemyMaxHP = enemyPokemon.hp; game.move.select(MoveId.NIGHTMARE); diff --git a/test/moves/obstruct.test.ts b/test/moves/obstruct.test.ts index 9abc60be129..bc03075b2cc 100644 --- a/test/moves/obstruct.test.ts +++ b/test/moves/obstruct.test.ts @@ -37,8 +37,8 @@ describe("Moves - Obstruct", () => { game.move.select(MoveId.OBSTRUCT); await game.phaseInterceptor.to("BerryPhase"); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); expect(player.isFullHp()).toBe(true); expect(enemy.getStatStage(Stat.DEF)).toBe(-2); @@ -51,8 +51,8 @@ describe("Moves - Obstruct", () => { await game.phaseInterceptor.to("MoveEffectPhase"); await game.move.forceMiss(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); await game.phaseInterceptor.to("TurnEndPhase"); expect(player.isFullHp()).toBe(true); @@ -66,8 +66,8 @@ describe("Moves - Obstruct", () => { game.move.select(MoveId.OBSTRUCT); await game.phaseInterceptor.to("BerryPhase"); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); expect(player.isFullHp()).toBe(true); expect(enemy.getStatStage(Stat.DEF)).toBe(0); @@ -80,7 +80,7 @@ describe("Moves - Obstruct", () => { game.move.select(MoveId.OBSTRUCT); await game.phaseInterceptor.to("BerryPhase"); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); expect(player.getStatStage(Stat.ATK)).toBe(-1); }); @@ -92,6 +92,6 @@ describe("Moves - Obstruct", () => { game.move.select(MoveId.OBSTRUCT); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(0); + expect(game.field.getEnemyPokemon().getStatStage(Stat.DEF)).toBe(0); }); }); diff --git a/test/moves/octolock.test.ts b/test/moves/octolock.test.ts index 09992dd52c7..f51c972f169 100644 --- a/test/moves/octolock.test.ts +++ b/test/moves/octolock.test.ts @@ -37,7 +37,7 @@ describe("Moves - Octolock", () => { it("lowers DEF and SPDEF stat stages of the target Pokemon by 1 each turn", async () => { await game.classicMode.startBattle([SpeciesId.GRAPPLOCT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // use Octolock and advance to init phase of next turn to check for stat changes game.move.select(MoveId.OCTOLOCK); @@ -58,7 +58,7 @@ describe("Moves - Octolock", () => { game.override.enemyAbility(AbilityId.BIG_PECKS); await game.classicMode.startBattle([SpeciesId.GRAPPLOCT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // use Octolock and advance to init phase of next turn to check for stat changes game.move.select(MoveId.OCTOLOCK); @@ -72,7 +72,7 @@ describe("Moves - Octolock", () => { game.override.enemyAbility(AbilityId.WHITE_SMOKE); await game.classicMode.startBattle([SpeciesId.GRAPPLOCT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // use Octolock and advance to init phase of next turn to check for stat changes game.move.select(MoveId.OCTOLOCK); @@ -86,7 +86,7 @@ describe("Moves - Octolock", () => { game.override.enemyAbility(AbilityId.CLEAR_BODY); await game.classicMode.startBattle([SpeciesId.GRAPPLOCT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // use Octolock and advance to init phase of next turn to check for stat changes game.move.select(MoveId.OCTOLOCK); @@ -99,7 +99,7 @@ describe("Moves - Octolock", () => { it("traps the target pokemon", async () => { await game.classicMode.startBattle([SpeciesId.GRAPPLOCT]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // before Octolock - enemy should not be trapped expect(enemyPokemon.findTag(t => t instanceof TrappedTag)).toBeUndefined(); @@ -115,7 +115,7 @@ describe("Moves - Octolock", () => { game.override.enemyMoveset(MoveId.OCTOLOCK); await game.classicMode.startBattle([SpeciesId.GASTLY]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // before Octolock - player should not be trapped expect(playerPokemon.findTag(t => t instanceof TrappedTag)).toBeUndefined(); @@ -132,7 +132,7 @@ describe("Moves - Octolock", () => { it("does not work on pokemon with added ghost type via Trick-or-Treat", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); // before Octolock - pokemon should not be trapped expect(enemy.findTag(t => t instanceof TrappedTag)).toBeUndefined(); diff --git a/test/moves/parting-shot.test.ts b/test/moves/parting-shot.test.ts index 7785bdd3a2f..660edc4565a 100644 --- a/test/moves/parting-shot.test.ts +++ b/test/moves/parting-shot.test.ts @@ -38,7 +38,7 @@ describe("Moves - Parting Shot", () => { game.override.enemySpecies(SpeciesId.POOCHYENA).ability(AbilityId.PRANKSTER); await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); game.move.select(MoveId.PARTING_SHOT); @@ -53,7 +53,7 @@ describe("Moves - Parting Shot", () => { game.override.enemySpecies(SpeciesId.GHOLDENGO).enemyAbility(AbilityId.GOOD_AS_GOLD); await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); game.move.select(MoveId.PARTING_SHOT); @@ -97,7 +97,7 @@ describe("Moves - Parting Shot", () => { // set up done await game.phaseInterceptor.to(TurnInitPhase, false); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6); @@ -120,7 +120,7 @@ describe("Moves - Parting Shot", () => { game.override.enemySpecies(SpeciesId.ALTARIA).enemyAbility(AbilityId.NONE).enemyMoveset([MoveId.MIST]); await game.classicMode.startBattle([SpeciesId.SNORLAX, SpeciesId.MEOWTH]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); game.move.select(MoveId.PARTING_SHOT); @@ -139,7 +139,7 @@ describe("Moves - Parting Shot", () => { game.override.enemySpecies(SpeciesId.TENTACOOL).enemyAbility(AbilityId.CLEAR_BODY); await game.classicMode.startBattle([SpeciesId.SNORLAX, SpeciesId.MEOWTH]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); game.move.select(MoveId.PARTING_SHOT); @@ -157,7 +157,7 @@ describe("Moves - Parting Shot", () => { async () => { await game.classicMode.startBattle([SpeciesId.MURKROW]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon).toBeDefined(); game.move.select(MoveId.PARTING_SHOT); @@ -186,7 +186,7 @@ describe("Moves - Parting Shot", () => { game.move.select(MoveId.PARTING_SHOT); await game.phaseInterceptor.to(BerryPhase, false); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MEOWTH); diff --git a/test/moves/plasma-fists.test.ts b/test/moves/plasma-fists.test.ts index 569fb1d5c0d..fb6e46feffa 100644 --- a/test/moves/plasma-fists.test.ts +++ b/test/moves/plasma-fists.test.ts @@ -60,8 +60,8 @@ describe("Moves - Plasma Fists", () => { await game.classicMode.startBattle([SpeciesId.ONIX]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getMoveType"); game.move.select(MoveId.PLASMA_FISTS); @@ -78,8 +78,8 @@ describe("Moves - Plasma Fists", () => { await game.classicMode.startBattle([SpeciesId.DUSCLOPS]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); vi.spyOn(enemyPokemon, "getMoveType"); game.move.select(MoveId.PLASMA_FISTS); diff --git a/test/moves/pledge-moves.test.ts b/test/moves/pledge-moves.test.ts index c67519d417b..7a9c4112d7d 100644 --- a/test/moves/pledge-moves.test.ts +++ b/test/moves/pledge-moves.test.ts @@ -89,8 +89,8 @@ describe("Moves - Pledge Moves", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FIRE_PLEDGE); diff --git a/test/moves/pollen-puff.test.ts b/test/moves/pollen-puff.test.ts index ecd208f777b..76732a39c43 100644 --- a/test/moves/pollen-puff.test.ts +++ b/test/moves/pollen-puff.test.ts @@ -53,7 +53,7 @@ describe("Moves - Pollen Puff", () => { game.override.moveset([MoveId.POLLEN_PUFF]).ability(AbilityId.PARENTAL_BOND).enemyLevel(100); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const target = game.scene.getEnemyPokemon()!; + const target = game.field.getEnemyPokemon(); game.move.select(MoveId.POLLEN_PUFF); diff --git a/test/moves/powder.test.ts b/test/moves/powder.test.ts index 17f64a95179..cbf70feebe6 100644 --- a/test/moves/powder.test.ts +++ b/test/moves/powder.test.ts @@ -41,7 +41,7 @@ describe("Moves - Powder", () => { game.override.enemyMoveset([]); await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.changeMoveset(enemyPokemon, MoveId.EMBER); game.move.select(MoveId.POWDER); @@ -66,7 +66,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -80,7 +80,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -94,7 +94,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -108,7 +108,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -122,7 +122,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -137,7 +137,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -152,8 +152,8 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); // Turn 1: Roar away 1 opponent game.move.select(MoveId.ROAR, 0, BattlerIndex.ENEMY_2); @@ -184,8 +184,8 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -200,7 +200,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -214,7 +214,7 @@ describe("Moves - Powder", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER); @@ -227,7 +227,7 @@ describe("Moves - Powder", () => { game.override.enemyMoveset([MoveId.FIRE_PLEDGE, MoveId.GRASS_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER, 0, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, 1); @@ -244,7 +244,7 @@ describe("Moves - Powder", () => { game.override.enemyMoveset([MoveId.FIRE_PLEDGE, MoveId.WATER_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER, 0, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, 1); @@ -261,7 +261,7 @@ describe("Moves - Powder", () => { game.override.enemyMoveset([MoveId.FIRE_PLEDGE, MoveId.WATER_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.CHARIZARD]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.POWDER, 0, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, 1); diff --git a/test/moves/power-shift.test.ts b/test/moves/power-shift.test.ts index adfd1f0aaa9..84bf6e230d4 100644 --- a/test/moves/power-shift.test.ts +++ b/test/moves/power-shift.test.ts @@ -32,7 +32,7 @@ describe("Moves - Power Shift", () => { it("switches the user's raw Attack stat with its raw Defense stat", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.setStat(Stat.ATK, 10, false); playerPokemon.setStat(Stat.DEF, 20, false); diff --git a/test/moves/power-split.test.ts b/test/moves/power-split.test.ts index 1eaabf6090d..9af57ebb191 100644 --- a/test/moves/power-split.test.ts +++ b/test/moves/power-split.test.ts @@ -36,8 +36,8 @@ describe("Moves - Power Split", () => { game.override.enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); @@ -56,8 +56,8 @@ describe("Moves - Power Split", () => { game.override.enemyMoveset([MoveId.POWER_SPLIT]); await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); diff --git a/test/moves/power-swap.test.ts b/test/moves/power-swap.test.ts index b25db40e64c..74cf37e33a5 100644 --- a/test/moves/power-swap.test.ts +++ b/test/moves/power-swap.test.ts @@ -36,8 +36,8 @@ describe("Moves - Power Swap", () => { it("should swap the user's ATK and SPATK stat stages with the target's", async () => { await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); diff --git a/test/moves/power-trick.test.ts b/test/moves/power-trick.test.ts index 3ddd27212e1..a8013f1882b 100644 --- a/test/moves/power-trick.test.ts +++ b/test/moves/power-trick.test.ts @@ -37,7 +37,7 @@ describe("Moves - Power Trick", () => { it("swaps the user's ATK and DEF stats", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); const baseATK = player.getStat(Stat.ATK, false); const baseDEF = player.getStat(Stat.DEF, false); @@ -53,7 +53,7 @@ describe("Moves - Power Trick", () => { it("resets initial ATK and DEF stat swap when used consecutively", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); const baseATK = player.getStat(Stat.ATK, false); const baseDEF = player.getStat(Stat.DEF, false); @@ -74,7 +74,7 @@ describe("Moves - Power Trick", () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE]); await game.override.moveset([MoveId.POWER_TRICK, MoveId.BATON_PASS]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.addTag(BattlerTagType.POWER_TRICK); game.move.select(MoveId.BATON_PASS); @@ -82,7 +82,7 @@ describe("Moves - Power Trick", () => { await game.phaseInterceptor.to(TurnEndPhase); - const switchedPlayer = game.scene.getPlayerPokemon()!; + const switchedPlayer = game.field.getPlayerPokemon(); const baseATK = switchedPlayer.getStat(Stat.ATK); const baseDEF = switchedPlayer.getStat(Stat.DEF); @@ -95,14 +95,14 @@ describe("Moves - Power Trick", () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE]); await game.override.moveset([MoveId.POWER_TRICK, MoveId.TRANSFORM]); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); player.addTag(BattlerTagType.POWER_TRICK); game.move.select(MoveId.TRANSFORM); await game.phaseInterceptor.to(TurnEndPhase); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); const baseATK = enemy.getStat(Stat.ATK); const baseDEF = enemy.getStat(Stat.DEF); diff --git a/test/moves/protect.test.ts b/test/moves/protect.test.ts index 7da54f268e0..9a4856d0d99 100644 --- a/test/moves/protect.test.ts +++ b/test/moves/protect.test.ts @@ -1,15 +1,14 @@ -import { ArenaTrapTag } from "#data/arena-tag"; import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; -import { ArenaTagSide } from "#enums/arena-tag-side"; import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; +import { MoveUseMode } from "#enums/move-use-mode"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Protect", () => { let phaserGame: Phaser.Game; @@ -27,90 +26,210 @@ describe("Moves - Protect", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override .battleStyle("single") - .moveset([MoveId.PROTECT]) + .moveset([MoveId.PROTECT, MoveId.SPIKY_SHIELD, MoveId.ENDURE, MoveId.SPLASH]) .enemySpecies(SpeciesId.SNORLAX) .enemyAbility(AbilityId.INSOMNIA) - .enemyMoveset([MoveId.TACKLE]) + .enemyMoveset(MoveId.LUMINA_CRASH) .startingLevel(100) .enemyLevel(100); }); - test("should protect the user from attacks", async () => { + it("should protect the user from attacks and their secondary effects", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); game.move.select(MoveId.PROTECT); - await game.phaseInterceptor.to("BerryPhase", false); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(charizard.getStatStage(Stat.SPDEF)).toBe(0); + expect(charizard); }); - test("should prevent secondary effects from the opponent's attack", async () => { - game.override.enemyMoveset([MoveId.CEASELESS_EDGE]); - vi.spyOn(allMoves[MoveId.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100); - + it.each<{ numTurns: number; chance: number }>([ + { numTurns: 1, chance: 3 }, + { numTurns: 2, chance: 9 }, + { numTurns: 3, chance: 27 }, + { numTurns: 4, chance: 81 }, + ])("should have a 1/$chance success rate after $numTurns successful uses", async ({ numTurns, chance }) => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const charizard = game.scene.getPlayerPokemon()!; + + // mock RNG roll to suceed unless exactly the desired chance is hit + vi.spyOn(charizard, "randBattleSeedInt").mockImplementation(range => (range !== chance ? 0 : 1)); + const conditionSpy = vi.spyOn(allMoves[MoveId.PROTECT]["conditions"][0], "apply"); + + // click protect many times + for (let x = 0; x < numTurns; x++) { + game.move.select(MoveId.PROTECT); + await game.toNextTurn(); + + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + expect(conditionSpy).toHaveLastReturnedWith(true); + } game.move.select(MoveId.PROTECT); + await game.toNextTurn(); - await game.phaseInterceptor.to("BerryPhase", false); - - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeUndefined(); + expect(charizard.hp).toBeLessThan(charizard.getMaxHp()); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(conditionSpy).toHaveLastReturnedWith(false); }); - test("should protect the user from status moves", async () => { - game.override.enemyMoveset([MoveId.CHARM]); - + it("should share fail chance with all move variants", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); + charizard.summonData.moveHistory = [ + { move: MoveId.ENDURE, result: MoveResult.SUCCESS, targets: [BattlerIndex.PLAYER], useMode: MoveUseMode.NORMAL }, + { + move: MoveId.SPIKY_SHIELD, + result: MoveResult.SUCCESS, + targets: [BattlerIndex.PLAYER], + useMode: MoveUseMode.NORMAL, + }, + ]; + // force protect to fail on anything >=2 uses (1/9 chance) + vi.spyOn(charizard, "randBattleSeedInt").mockImplementation(range => (range >= 9 ? 1 : 0)); game.move.select(MoveId.PROTECT); + await game.toNextTurn(); - await game.phaseInterceptor.to("BerryPhase", false); - - expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); - test("should stop subsequent hits of a multi-hit move", async () => { + it("should reset fail chance on move failure", async () => { + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); + + const charizard = game.scene.getPlayerPokemon()!; + // force protect to always fail if RNG roll attempt is made + vi.spyOn(charizard, "randBattleSeedInt").mockReturnValue(1); + + game.move.select(MoveId.PROTECT); + await game.toNextTurn(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + + game.move.select(MoveId.SPIKY_SHIELD); + await game.toNextTurn(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + + game.move.select(MoveId.SPIKY_SHIELD); + await game.toNextTurn(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); + + it("should reset fail chance on using another move", async () => { + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); + + const charizard = game.scene.getPlayerPokemon()!; + // force protect to always fail if RNG roll attempt is made + vi.spyOn(charizard, "randBattleSeedInt").mockReturnValue(1); + + game.move.select(MoveId.PROTECT); + await game.toNextTurn(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + + game.move.select(MoveId.SPLASH); + await game.toNextTurn(); + + game.move.select(MoveId.PROTECT); + await game.toNextTurn(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); + + it("should reset fail chance on starting a new wave", async () => { + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); + + const charizard = game.field.getPlayerPokemon(); + // force protect to always fail if RNG roll attempt is made + vi.spyOn(charizard, "randBattleSeedInt").mockReturnValue(1); + + game.move.select(MoveId.PROTECT); + // Wait until move end phase to kill opponent to ensure protect doesn't fail due to going last + await game.phaseInterceptor.to("MoveEndPhase"); + await game.doKillOpponents(); + await game.toNextWave(); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + + game.move.select(MoveId.SPIKY_SHIELD); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); + + it("should not be blocked by Psychic Terrain", async () => { + game.override.ability(AbilityId.PSYCHIC_SURGE); + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); + + const charizard = game.scene.getPlayerPokemon()!; + game.move.select(MoveId.PROTECT); + await game.toNextTurn(); + + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); + + it("should stop subsequent hits of multi-hit moves", async () => { game.override.enemyMoveset([MoveId.TACHYON_CUTTER]); - await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const charizard = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.PROTECT); - await game.phaseInterceptor.to("BerryPhase", false); - expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); + expect(charizard.hp).toBe(charizard.getMaxHp()); expect(enemyPokemon.turnData.hitCount).toBe(1); }); - test("should fail if the user is the last to move in the turn", async () => { - game.override.enemyMoveset([MoveId.PROTECT]); - + it("should fail if the user moves last in the turn", async () => { + game.override.enemyMoveset(MoveId.PROTECT); await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const charizard = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.PROTECT); - await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); - await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); - expect(leadPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); + + it("should not block Protection-bypassing moves or Future Sight", async () => { + game.override.enemyMoveset([MoveId.FUTURE_SIGHT, MoveId.MIGHTY_CLEAVE, MoveId.SPORE]); + await game.classicMode.startBattle([SpeciesId.AGGRON]); + + const aggron = game.scene.getPlayerPokemon()!; + vi.spyOn(aggron, "randBattleSeedInt").mockReturnValue(0); + + // Turn 1: setup future sight + game.move.select(MoveId.PROTECT); + await game.move.forceEnemyMove(MoveId.FUTURE_SIGHT); + await game.toNextTurn(); + + // Turn 2: mighty cleave + game.move.select(MoveId.PROTECT); + await game.move.forceEnemyMove(MoveId.MIGHTY_CLEAVE); + await game.toNextTurn(); + + expect(aggron.hp).toBeLessThan(aggron.getMaxHp()); + + aggron.hp = aggron.getMaxHp(); + + // turn 3: Future Sight hits + game.move.select(MoveId.PROTECT); + await game.move.forceEnemyMove(MoveId.SPORE); + await game.toNextTurn(); + + expect(aggron.hp).toBeLessThan(aggron.getMaxHp()); + expect(aggron.status?.effect).toBeUndefined(); // check that protect actually worked + }); + + // TODO: Add test + it.todo("should not reset counter when throwing balls"); }); diff --git a/test/moves/psycho-shift.test.ts b/test/moves/psycho-shift.test.ts index f2e762f1381..9e63798773c 100644 --- a/test/moves/psycho-shift.test.ts +++ b/test/moves/psycho-shift.test.ts @@ -37,13 +37,14 @@ describe("Moves - Psycho Shift", () => { it("If Psycho Shift is used on a Pokémon with Synchronize, the user of Psycho Shift will already be afflicted with a status condition when Synchronize activates", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); - expect(enemyPokemon?.status).toBeUndefined(); + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); + expect(playerPokemon.status).toBeDefined(); + expect(enemyPokemon.status).toBeFalsy(); game.move.select(MoveId.PSYCHO_SHIFT); await game.phaseInterceptor.to("TurnEndPhase"); - expect(playerPokemon?.status).toBeNull(); - expect(enemyPokemon?.status).toBeDefined(); + expect(playerPokemon.status).toBeNull(); + expect(enemyPokemon.status).toBeDefined(); }); }); diff --git a/test/moves/purify.test.ts b/test/moves/purify.test.ts index 88e0da802a3..1f3102616b5 100644 --- a/test/moves/purify.test.ts +++ b/test/moves/purify.test.ts @@ -38,8 +38,8 @@ describe("Moves - Purify", () => { test("Purify heals opponent status effect and restores user hp", async () => { await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); + const playerPokemon: PlayerPokemon = game.field.getPlayerPokemon(); playerPokemon.hp = playerPokemon.getMaxHp() - 1; enemyPokemon.status = new Status(StatusEffect.BURN); @@ -55,7 +55,7 @@ describe("Moves - Purify", () => { test("Purify does not heal if opponent doesnt have any status effect", async () => { await game.classicMode.startBattle(); - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon: PlayerPokemon = game.field.getPlayerPokemon(); playerPokemon.hp = playerPokemon.getMaxHp() - 1; const playerInitialHp = playerPokemon.hp; diff --git a/test/moves/quick-guard.test.ts b/test/moves/quick-guard.test.ts index d14eada2445..173d45b412f 100644 --- a/test/moves/quick-guard.test.ts +++ b/test/moves/quick-guard.test.ts @@ -3,10 +3,9 @@ import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; -import { Stat } from "#enums/stat"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Quick Guard", () => { let phaserGame: Phaser.Game; @@ -27,74 +26,72 @@ describe("Moves - Quick Guard", () => { game.override .battleStyle("double") - .moveset([MoveId.QUICK_GUARD, MoveId.SPLASH, MoveId.FOLLOW_ME]) + .moveset([MoveId.QUICK_GUARD, MoveId.SPLASH, MoveId.SPIKY_SHIELD]) .enemySpecies(SpeciesId.SNORLAX) - .enemyMoveset([MoveId.QUICK_ATTACK]) - .enemyAbility(AbilityId.INSOMNIA) + .enemyMoveset(MoveId.QUICK_ATTACK) + .enemyAbility(AbilityId.BALL_FETCH) .startingLevel(100) .enemyLevel(100); }); - test("should protect the user and allies from priority moves", async () => { + it("should protect the user and allies from priority moves", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerField(); - - game.move.select(MoveId.QUICK_GUARD); - game.move.select(MoveId.SPLASH, 1); + const [charizard, blastoise] = game.scene.getPlayerField(); + game.move.select(MoveId.QUICK_GUARD, BattlerIndex.PLAYER); + game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.QUICK_ATTACK, BattlerIndex.PLAYER); + await game.move.forceEnemyMove(MoveId.QUICK_ATTACK, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase", false); - playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(blastoise.hp).toBe(blastoise.getMaxHp()); }); - test("should protect the user and allies from Prankster-boosted moves", async () => { - game.override.enemyAbility(AbilityId.PRANKSTER).enemyMoveset([MoveId.GROWL]); - + it.each<{ name: string; move: MoveId; ability: AbilityId }>([ + { name: "Prankster", move: MoveId.SPORE, ability: AbilityId.PRANKSTER }, + { name: "Gale Wings", move: MoveId.BRAVE_BIRD, ability: AbilityId.GALE_WINGS }, + ])("should protect the user and allies from $name-boosted moves", async ({ move, ability }) => { + game.override.enemyMoveset(move).enemyAbility(ability); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerField(); - - game.move.select(MoveId.QUICK_GUARD); - game.move.select(MoveId.SPLASH, 1); + const [charizard, blastoise] = game.scene.getPlayerField(); + game.move.select(MoveId.QUICK_GUARD, BattlerIndex.PLAYER); + game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(move, BattlerIndex.PLAYER); + await game.move.forceEnemyMove(move, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase", false); - playerPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(blastoise.hp).toBe(blastoise.getMaxHp()); + expect(charizard.status?.effect).toBeUndefined(); + expect(blastoise.status?.effect).toBeUndefined(); }); - test("should stop subsequent hits of a multi-hit priority move", async () => { - game.override.enemyMoveset([MoveId.WATER_SHURIKEN]); - - await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - - const playerPokemon = game.scene.getPlayerField(); - const enemyPokemon = game.scene.getEnemyField(); - - game.move.select(MoveId.QUICK_GUARD); - game.move.select(MoveId.FOLLOW_ME, 1); - - await game.phaseInterceptor.to("BerryPhase", false); - - playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); - enemyPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); - }); - - test("should fail if the user is the last to move in the turn", async () => { - game.override.battleStyle("single").enemyMoveset([MoveId.QUICK_GUARD]); - + it("should increment (but not respect) other protection moves' fail counters", async () => { + game.override.battleStyle("single"); await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const charizard = game.scene.getPlayerPokemon()!; + // force protect to fail on anything >0 uses + vi.spyOn(charizard, "randBattleSeedInt").mockReturnValue(1); game.move.select(MoveId.QUICK_GUARD); + await game.toNextTurn(); - await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); - await game.phaseInterceptor.to("BerryPhase", false); + game.move.select(MoveId.QUICK_GUARD); + await game.toNextTurn(); - expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); - expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + // ignored fail chance + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + + game.move.select(MoveId.SPIKY_SHIELD); + await game.toNextTurn(); + + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); }); diff --git a/test/moves/rage-fist.test.ts b/test/moves/rage-fist.test.ts index 8ed0a71306b..61164b5710c 100644 --- a/test/moves/rage-fist.test.ts +++ b/test/moves/rage-fist.test.ts @@ -65,7 +65,7 @@ describe("Moves - Rage Fist", () => { await game.phaseInterceptor.to("TurnEndPhase"); // hit 8 times, but nothing else - expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(8); + expect(game.field.getPlayerPokemon().battleData.hitCount).toBe(8); expect(move.calculateBattlePower).toHaveLastReturnedWith(350); }); @@ -80,7 +80,7 @@ describe("Moves - Rage Fist", () => { await game.toNextTurn(); // no increase due to substitute - expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(0); + expect(game.field.getPlayerPokemon().battleData.hitCount).toBe(0); // remove substitute and get confused game.move.select(MoveId.TIDY_UP); @@ -95,7 +95,7 @@ describe("Moves - Rage Fist", () => { await game.toNextTurn(); // didn't go up from hitting ourself - expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(0); + expect(game.field.getPlayerPokemon().battleData.hitCount).toBe(0); }); it("should maintain hits recieved between wild waves", async () => { @@ -105,20 +105,20 @@ describe("Moves - Rage Fist", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toNextWave(); - expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(2); + expect(game.field.getPlayerPokemon().battleData.hitCount).toBe(2); game.move.select(MoveId.RAGE_FIST); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("TurnEndPhase"); - expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(4); + expect(game.field.getPlayerPokemon().battleData.hitCount).toBe(4); expect(move.calculateBattlePower).toHaveLastReturnedWith(250); }); it("should reset hits recieved before trainer battles", async () => { await game.classicMode.startBattle([SpeciesId.IRON_HANDS]); - const ironHands = game.scene.getPlayerPokemon()!; + const ironHands = game.field.getPlayerPokemon(); expect(ironHands).toBeDefined(); // beat up a magikarp @@ -184,7 +184,7 @@ describe("Moves - Rage Fist", () => { game.move.select(MoveId.RAGE_FIST); await game.phaseInterceptor.to("MoveEndPhase"); - const charizard = game.scene.getPlayerPokemon()!; + const charizard = game.field.getPlayerPokemon(); expect(charizard).toBeDefined(); expect(charizard.species.speciesId).toBe(SpeciesId.CHARIZARD); expect(move.calculateBattlePower).toHaveLastReturnedWith(150); diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index 228730e9fb6..b8fa2b1ce80 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -52,8 +52,8 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -71,8 +71,8 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -88,8 +88,8 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -105,8 +105,8 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -122,8 +122,8 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); expect(mockedDmg).toBe(allMoves[moveToUse].power); diff --git a/test/moves/relic-song.test.ts b/test/moves/relic-song.test.ts index 62069ac3cd3..c834ccbee24 100644 --- a/test/moves/relic-song.test.ts +++ b/test/moves/relic-song.test.ts @@ -34,7 +34,7 @@ describe("Moves - Relic Song", () => { it("swaps Meloetta's form between Aria and Pirouette", async () => { await game.classicMode.startBattle([SpeciesId.MELOETTA]); - const meloetta = game.scene.getPlayerPokemon()!; + const meloetta = game.field.getPlayerPokemon(); game.move.select(MoveId.RELIC_SONG); await game.toNextTurn(); @@ -51,7 +51,7 @@ describe("Moves - Relic Song", () => { game.challengeMode.addChallenge(Challenges.SINGLE_TYPE, PokemonType.PSYCHIC + 1, 0); await game.challengeMode.startBattle([SpeciesId.MELOETTA]); - const meloetta = game.scene.getPlayerPokemon()!; + const meloetta = game.field.getPlayerPokemon(); expect(meloetta.formIndex).toBe(0); @@ -66,7 +66,7 @@ describe("Moves - Relic Song", () => { game.override.starterForms({ [SpeciesId.MELOETTA]: 1 }).startingWave(10); await game.classicMode.startBattle([SpeciesId.MELOETTA]); - const meloetta = game.scene.getPlayerPokemon()!; + const meloetta = game.field.getPlayerPokemon(); game.move.select(MoveId.SPLASH); await game.doKillOpponents(); diff --git a/test/moves/revival-blessing.test.ts b/test/moves/revival-blessing.test.ts index 89a1996fe03..d14fa89c738 100644 --- a/test/moves/revival-blessing.test.ts +++ b/test/moves/revival-blessing.test.ts @@ -41,7 +41,7 @@ describe("Moves - Revival Blessing", () => { game.doSelectPartyPokemon(1, "SwitchPhase"); await game.toNextTurn(); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); expect(player.species.speciesId).toBe(SpeciesId.MAGIKARP); game.move.select(MoveId.REVIVAL_BLESSING); @@ -82,7 +82,7 @@ describe("Moves - Revival Blessing", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase", false); - const player = game.scene.getPlayerPokemon()!; + const player = game.field.getPlayerPokemon(); expect(player.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); diff --git a/test/moves/role-play.test.ts b/test/moves/role-play.test.ts index af9fc68efa4..ff5d487ddf0 100644 --- a/test/moves/role-play.test.ts +++ b/test/moves/role-play.test.ts @@ -38,7 +38,7 @@ describe("Moves - Role Play", () => { game.move.select(MoveId.ROLE_PLAY); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); }); it("should activate post-summon abilities", async () => { @@ -48,6 +48,6 @@ describe("Moves - Role Play", () => { game.move.select(MoveId.ROLE_PLAY); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/moves/roost.test.ts b/test/moves/roost.test.ts index e4ea09e245c..bb567a41cd0 100644 --- a/test/moves/roost.test.ts +++ b/test/moves/roost.test.ts @@ -49,7 +49,7 @@ describe("Moves - Roost", () => { test("Non flying type uses roost -> no type change, took damage", async () => { await game.classicMode.startBattle([SpeciesId.DUNSPARCE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.ROOST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -73,7 +73,7 @@ describe("Moves - Roost", () => { test("Pure flying type -> becomes normal after roost and takes damage from ground moves -> regains flying", async () => { await game.classicMode.startBattle([SpeciesId.TORNADUS]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.ROOST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -97,7 +97,7 @@ describe("Moves - Roost", () => { test("Dual X/flying type -> becomes type X after roost and takes damage from ground moves -> regains flying", async () => { await game.classicMode.startBattle([SpeciesId.HAWLUCHA]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.ROOST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -122,7 +122,7 @@ describe("Moves - Roost", () => { test("Pokemon with levitate after using roost should lose flying type but still be unaffected by ground moves", async () => { game.override.starterForms({ [SpeciesId.ROTOM]: 4 }); await game.classicMode.startBattle([SpeciesId.ROTOM]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.ROOST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -146,7 +146,7 @@ describe("Moves - Roost", () => { test("A fire/flying type that uses burn up, then roost should be typeless until end of turn", async () => { await game.classicMode.startBattle([SpeciesId.MOLTRES]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.BURN_UP); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -182,7 +182,7 @@ describe("Moves - Roost", () => { test("An electric/flying type that uses double shock, then roost should be typeless until end of turn", async () => { game.override.enemySpecies(SpeciesId.ZEKROM); await game.classicMode.startBattle([SpeciesId.ZAPDOS]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); const playerPokemonStartingHP = playerPokemon.hp; game.move.select(MoveId.DOUBLE_SHOCK); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -223,7 +223,7 @@ describe("Moves - Roost", () => { MoveId.TRICK_OR_TREAT, ]); await game.classicMode.startBattle([SpeciesId.MOLTRES]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.ROOST); await game.phaseInterceptor.to(MoveEffectPhase); diff --git a/test/moves/safeguard.test.ts b/test/moves/safeguard.test.ts index 19b56cd759f..2157f01fee4 100644 --- a/test/moves/safeguard.test.ts +++ b/test/moves/safeguard.test.ts @@ -37,7 +37,7 @@ describe("Moves - Safeguard", () => { it("protects from damaging moves with additional effects", async () => { await game.classicMode.startBattle(); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.NUZZLE); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -48,7 +48,7 @@ describe("Moves - Safeguard", () => { it("protects from status moves", async () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SPORE); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -60,7 +60,7 @@ describe("Moves - Safeguard", () => { it("protects from confusion", async () => { game.override.moveset([MoveId.CONFUSE_RAY]); await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.CONFUSE_RAY); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -89,7 +89,7 @@ describe("Moves - Safeguard", () => { it("protects from Yawn", async () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.YAWN); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); @@ -100,7 +100,7 @@ describe("Moves - Safeguard", () => { it("doesn't protect from already existing Yawn", async () => { await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.YAWN); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -115,7 +115,7 @@ describe("Moves - Safeguard", () => { it("doesn't protect from self-inflicted status from Rest or Flame Orb", async () => { game.override.enemyHeldItems([{ name: "FLAME_ORB" }]); await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.hp = 1; game.move.select(MoveId.SPLASH); diff --git a/test/moves/scale-shot.test.ts b/test/moves/scale-shot.test.ts index 7e5c60577ce..4a3369ce9fc 100644 --- a/test/moves/scale-shot.test.ts +++ b/test/moves/scale-shot.test.ts @@ -42,7 +42,7 @@ describe("Moves - Scale Shot", () => { game.override.enemySpecies(SpeciesId.FORRETRESS); await game.classicMode.startBattle([SpeciesId.MINCCINO]); - const minccino = game.scene.getPlayerPokemon()!; + const minccino = game.field.getPlayerPokemon(); game.move.select(MoveId.SCALE_SHOT); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -70,7 +70,7 @@ describe("Moves - Scale Shot", () => { vi.spyOn(moveToCheck, "calculateBattlePower"); await game.classicMode.startBattle([SpeciesId.MINCCINO]); - const minccino = game.scene.getPlayerPokemon()!; + const minccino = game.field.getPlayerPokemon(); game.move.select(MoveId.SCALE_SHOT); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/test/moves/secret-power.test.ts b/test/moves/secret-power.test.ts index 16090bebcbe..b8f713f59cf 100644 --- a/test/moves/secret-power.test.ts +++ b/test/moves/secret-power.test.ts @@ -43,7 +43,7 @@ describe("Moves - Secret Power", () => { vi.spyOn(allMoves[MoveId.SECRET_POWER], "chance", "get").mockReturnValue(100); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); // No Terrain + BiomeId.VOLCANO --> Burn game.move.select(MoveId.SECRET_POWER); diff --git a/test/moves/shed-tail.test.ts b/test/moves/shed-tail.test.ts index ff2b29e846d..b53af269875 100644 --- a/test/moves/shed-tail.test.ts +++ b/test/moves/shed-tail.test.ts @@ -34,14 +34,14 @@ describe("Moves - Shed Tail", () => { it("transfers a Substitute doll to the switched in Pokemon", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.FEEBAS]); - const magikarp = game.scene.getPlayerPokemon()!; + const magikarp = game.field.getPlayerPokemon(); game.move.select(MoveId.SHED_TAIL); game.doSelectPartyPokemon(1); await game.phaseInterceptor.to("TurnEndPhase", false); - const feebas = game.scene.getPlayerPokemon()!; + const feebas = game.field.getPlayerPokemon(); const substituteTag = feebas.getTag(SubstituteTag); expect(feebas).not.toBe(magikarp); @@ -55,7 +55,7 @@ describe("Moves - Shed Tail", () => { it("should fail if no ally is available to switch in", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const magikarp = game.scene.getPlayerPokemon()!; + const magikarp = game.field.getPlayerPokemon(); expect(game.scene.getPlayerParty().length).toBe(1); game.move.select(MoveId.SHED_TAIL); diff --git a/test/moves/shell-trap.test.ts b/test/moves/shell-trap.test.ts index d2908278d93..5ecad3116af 100644 --- a/test/moves/shell-trap.test.ts +++ b/test/moves/shell-trap.test.ts @@ -133,8 +133,8 @@ describe("Moves - Shell Trap", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SHELL_TRAP); diff --git a/test/moves/simple-beam.test.ts b/test/moves/simple-beam.test.ts index f45c19c690e..a5ad6c5af8f 100644 --- a/test/moves/simple-beam.test.ts +++ b/test/moves/simple-beam.test.ts @@ -37,6 +37,6 @@ describe("Moves - Simple Beam", () => { game.move.select(MoveId.SIMPLE_BEAM); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.SIMPLE); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.SIMPLE); }); }); diff --git a/test/moves/sketch.test.ts b/test/moves/sketch.test.ts index fff9be97e2d..ed010b8a883 100644 --- a/test/moves/sketch.test.ts +++ b/test/moves/sketch.test.ts @@ -38,7 +38,7 @@ describe("Moves - Sketch", () => { it("Sketch should not fail even if a previous Sketch failed to retrieve a valid move and ran out of PP", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // can't use normal moveset override because we need to check moveset changes playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH), new PokemonMove(MoveId.SKETCH)]; @@ -60,8 +60,8 @@ describe("Moves - Sketch", () => { it("Sketch should retrieve the most recent valid move from its target history", async () => { game.override.enemyStatusEffect(StatusEffect.PARALYSIS); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH), new PokemonMove(MoveId.GROWL)]; game.move.select(MoveId.GROWL); @@ -88,7 +88,7 @@ describe("Moves - Sketch", () => { game.override.enemyMoveset([MoveId.METRONOME]); await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.moveset = [new PokemonMove(MoveId.SKETCH)]; // Opponent uses Metronome -> False Swipe, then player uses Sketch, which should sketch Metronome diff --git a/test/moves/skill-swap.test.ts b/test/moves/skill-swap.test.ts index 50a0a232f6e..1b3c0255895 100644 --- a/test/moves/skill-swap.test.ts +++ b/test/moves/skill-swap.test.ts @@ -39,8 +39,8 @@ describe("Moves - Skill Swap", () => { game.move.select(MoveId.SKILL_SWAP); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH); - expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.ADAPTABILITY); + expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH); + expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.ADAPTABILITY); }); it("should activate post-summon abilities", async () => { @@ -51,6 +51,6 @@ describe("Moves - Skill Swap", () => { await game.phaseInterceptor.to("BerryPhase"); // player atk should be -1 after opponent gains intimidate and it activates - expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1); }); }); diff --git a/test/moves/sleep-talk.test.ts b/test/moves/sleep-talk.test.ts index 8593f4b4d07..9e8db2e3615 100644 --- a/test/moves/sleep-talk.test.ts +++ b/test/moves/sleep-talk.test.ts @@ -42,7 +42,7 @@ describe("Moves - Sleep Talk", () => { game.move.select(MoveId.SLEEP_TALK); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should fail if the user has no valid moves", async () => { @@ -51,7 +51,7 @@ describe("Moves - Sleep Talk", () => { game.move.select(MoveId.SLEEP_TALK); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.field.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); it("should call a random valid move if the user is asleep", async () => { @@ -60,7 +60,7 @@ describe("Moves - Sleep Talk", () => { game.move.select(MoveId.SLEEP_TALK); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)); + expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)); }); it("should apply secondary effects of a move", async () => { @@ -70,6 +70,6 @@ describe("Moves - Sleep Talk", () => { game.move.select(MoveId.SLEEP_TALK); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // Wood Hammer recoil effect should be applied + expect(game.field.getPlayerPokemon().isFullHp()).toBeFalsy(); // Wood Hammer recoil effect should be applied }); }); diff --git a/test/moves/solar-beam.test.ts b/test/moves/solar-beam.test.ts index f4347493de5..6a413776972 100644 --- a/test/moves/solar-beam.test.ts +++ b/test/moves/solar-beam.test.ts @@ -38,8 +38,8 @@ describe("Moves - Solar Beam", () => { it("should deal damage in two turns if no weather is active", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SOLAR_BEAM); @@ -66,8 +66,8 @@ describe("Moves - Solar Beam", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SOLAR_BEAM); diff --git a/test/moves/sparkly-swirl.test.ts b/test/moves/sparkly-swirl.test.ts index 6e43211ed50..7cda68438e1 100644 --- a/test/moves/sparkly-swirl.test.ts +++ b/test/moves/sparkly-swirl.test.ts @@ -36,7 +36,7 @@ describe("Moves - Sparkly Swirl", () => { game.override.battleStyle("double").statusEffect(StatusEffect.BURN); await game.classicMode.startBattle([SpeciesId.RATTATA, SpeciesId.RATTATA, SpeciesId.RATTATA]); const [leftPlayer, rightPlayer, partyPokemon] = game.scene.getPlayerParty(); - const leftOpp = game.scene.getEnemyPokemon()!; + const leftOpp = game.field.getEnemyPokemon(); vi.spyOn(leftPlayer, "resetStatus"); vi.spyOn(rightPlayer, "resetStatus"); diff --git a/test/moves/spectral-thief.test.ts b/test/moves/spectral-thief.test.ts index dd988ac667a..45005ccf32d 100644 --- a/test/moves/spectral-thief.test.ts +++ b/test/moves/spectral-thief.test.ts @@ -36,8 +36,8 @@ describe("Moves - Spectral Thief", () => { it("should steal max possible positive stat changes and ignore negative ones.", async () => { await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 6); enemy.setStatStage(Stat.DEF, -6); @@ -69,8 +69,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).enemyLevel(50); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const moveToCheck = allMoves[MoveId.SPECTRAL_THIEF]; const dmgBefore = enemy.getAttackDamage({ source: player, move: moveToCheck }).damage; @@ -88,8 +88,8 @@ describe("Moves - Spectral Thief", () => { game.override.ability(AbilityId.CONTRARY); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 6); @@ -106,8 +106,8 @@ describe("Moves - Spectral Thief", () => { game.override.ability(AbilityId.SIMPLE); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); @@ -124,8 +124,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemyAbility(AbilityId.CLEAR_BODY); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); @@ -142,8 +142,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemyAbility(AbilityId.WHITE_SMOKE); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); @@ -160,8 +160,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemyAbility(AbilityId.HYPER_CUTTER); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); @@ -178,8 +178,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemyMoveset(MoveId.SUBSTITUTE); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); @@ -198,8 +198,8 @@ describe("Moves - Spectral Thief", () => { game.override.enemyMoveset(MoveId.PROTECT); await game.classicMode.startBattle(); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); enemy.setStatStage(Stat.ATK, 3); diff --git a/test/moves/speed-swap.test.ts b/test/moves/speed-swap.test.ts index b67ad68746a..1b8e1d24a61 100644 --- a/test/moves/speed-swap.test.ts +++ b/test/moves/speed-swap.test.ts @@ -36,8 +36,8 @@ describe("Moves - Speed Swap", () => { it("should swap the user's SPD and the target's SPD stats", async () => { await game.classicMode.startBattle([SpeciesId.INDEEDEE]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const playerSpd = player.getStat(Stat.SPD, false); const enemySpd = enemy.getStat(Stat.SPD, false); diff --git a/test/moves/spit-up.test.ts b/test/moves/spit-up.test.ts index 22722cfed2b..8b110b0ea04 100644 --- a/test/moves/spit-up.test.ts +++ b/test/moves/spit-up.test.ts @@ -50,7 +50,7 @@ describe("Moves - Spit Up", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); const stockpilingTag = pokemon.getTag(StockpilingTag)!; @@ -72,7 +72,7 @@ describe("Moves - Spit Up", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); @@ -95,7 +95,7 @@ describe("Moves - Spit Up", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); @@ -117,7 +117,7 @@ describe("Moves - Spit Up", () => { it("fails without stacks", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); @@ -128,7 +128,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SPIT_UP, result: MoveResult.FAIL, - targets: [game.scene.getEnemyPokemon()!.getBattlerIndex()], + targets: [game.field.getEnemyPokemon().getBattlerIndex()], }); expect(spitUp.calculateBattlePower).not.toHaveBeenCalled(); @@ -138,7 +138,7 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (both boosts equal)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); const stockpilingTag = pokemon.getTag(StockpilingTag)!; @@ -155,7 +155,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SPIT_UP, result: MoveResult.SUCCESS, - targets: [game.scene.getEnemyPokemon()!.getBattlerIndex()], + targets: [game.field.getEnemyPokemon().getBattlerIndex()], }); expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); @@ -169,7 +169,7 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (different boosts)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); const stockpilingTag = pokemon.getTag(StockpilingTag)!; @@ -187,7 +187,7 @@ describe("Moves - Spit Up", () => { expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SPIT_UP, result: MoveResult.SUCCESS, - targets: [game.scene.getEnemyPokemon()!.getBattlerIndex()], + targets: [game.field.getEnemyPokemon().getBattlerIndex()], }); expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce(); diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index 2219b95e733..75b2b66560e 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -32,12 +32,12 @@ describe("Moves - Steamroller", () => { game.override.enemySpecies(SpeciesId.DITTO).enemyMoveset(MoveId.MINIMIZE); await game.classicMode.startBattle([SpeciesId.IRON_BOULDER]); - const ditto = game.scene.getEnemyPokemon()!; + const ditto = game.field.getEnemyPokemon(); vi.spyOn(ditto, "getAttackDamage"); ditto.hp = 5000; const steamroller = allMoves[MoveId.STEAMROLLER]; vi.spyOn(steamroller, "calculateBattleAccuracy"); - const ironBoulder = game.scene.getPlayerPokemon()!; + const ironBoulder = game.field.getPlayerPokemon(); vi.spyOn(ironBoulder, "getAccuracyMultiplier"); // Turn 1 game.move.select(MoveId.STEAMROLLER); diff --git a/test/moves/stockpile.test.ts b/test/moves/stockpile.test.ts index 5b170c10fcb..2da1285ace2 100644 --- a/test/moves/stockpile.test.ts +++ b/test/moves/stockpile.test.ts @@ -38,7 +38,7 @@ describe("Moves - Stockpile", () => { it("gains a stockpile stack and raises user's DEF and SPDEF stat stages by 1 on each use, fails at max stacks (3)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); // Unfortunately, Stockpile stacks are not directly queryable (i.e. there is no pokemon.getStockpileStacks()), // we just have to know that they're implemented as a BattlerTag. @@ -78,7 +78,7 @@ describe("Moves - Stockpile", () => { it("gains a stockpile stack even if user's DEF and SPDEF stat stages are at +6", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); user.setStatStage(Stat.DEF, 6); user.setStatStage(Stat.SPDEF, 6); diff --git a/test/moves/struggle.test.ts b/test/moves/struggle.test.ts index 21c4e204974..cba049e7277 100644 --- a/test/moves/struggle.test.ts +++ b/test/moves/struggle.test.ts @@ -35,7 +35,7 @@ describe("Moves - Struggle", () => { game.override.moveset([MoveId.STRUGGLE]).ability(AbilityId.ADAPTABILITY); await game.classicMode.startBattle([SpeciesId.RATTATA]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.STRUGGLE); const stabSpy = vi.spyOn(enemy, "calculateStabMultiplier"); @@ -49,7 +49,7 @@ describe("Moves - Struggle", () => { game.override.moveset([MoveId.STRUGGLE]); await game.classicMode.startBattle([SpeciesId.GASTLY]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.STRUGGLE); const moveEffectivenessSpy = vi.spyOn(enemy, "getMoveEffectiveness"); diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 15fd770805a..89018a8d592 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -49,7 +49,7 @@ describe("Moves - Substitute", () => { it("should cause the user to take damage", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -63,7 +63,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.SKARMORY]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -86,7 +86,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -107,7 +107,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -122,7 +122,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -142,7 +142,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -160,7 +160,7 @@ describe("Moves - Substitute", () => { it("shouldn't block the user's own status moves", async () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -179,7 +179,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); vi.spyOn(leadPokemon, "getMoveEffectiveness"); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -197,7 +197,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); vi.spyOn(leadPokemon, "getMoveEffectiveness"); game.move.select(MoveId.SUBSTITUTE); @@ -233,8 +233,8 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -251,7 +251,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -268,7 +268,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -284,7 +284,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -301,7 +301,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -317,7 +317,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, enemyPokemon.id); const enemyNumItems = enemyPokemon.getHeldItems().length; @@ -334,8 +334,8 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -355,7 +355,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -372,7 +372,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -389,7 +389,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon()!; + const leadPokemon = game.field.getPlayerPokemon(); leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, leadPokemon.id); @@ -400,7 +400,7 @@ describe("Moves - Substitute", () => { await game.phaseInterceptor.to("MovePhase", false); - const switchedPokemon = game.scene.getPlayerPokemon()!; + const switchedPokemon = game.field.getPlayerPokemon(); const subTag = switchedPokemon.getTag(SubstituteTag)!; expect(subTag).toBeDefined(); expect(subTag.hp).toBe(Math.floor((leadPokemon.getMaxHp() * 1) / 4)); @@ -411,7 +411,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SUBSTITUTE); @@ -428,8 +428,8 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); playerPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, playerPokemon.id); @@ -446,7 +446,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, playerPokemon.id); @@ -462,8 +462,8 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); playerPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, playerPokemon.id); @@ -479,8 +479,8 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.BLASTOISE]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); playerPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, playerPokemon.id); @@ -497,7 +497,7 @@ describe("Moves - Substitute", () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, playerPokemon.id); diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index 1ae31517cb6..f896a4c9c77 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -43,7 +43,7 @@ describe("Moves - Swallow", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; @@ -70,7 +70,7 @@ describe("Moves - Swallow", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; @@ -98,7 +98,7 @@ describe("Moves - Swallow", () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 0.0001; @@ -125,7 +125,7 @@ describe("Moves - Swallow", () => { it("fails without stacks", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); @@ -144,7 +144,7 @@ describe("Moves - Swallow", () => { it("decreases stats based on stored values (both boosts equal)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); const stockpilingTag = pokemon.getTag(StockpilingTag)!; @@ -173,7 +173,7 @@ describe("Moves - Swallow", () => { it("lower stat stages based on stored values (different boosts)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); pokemon.addTag(BattlerTagType.STOCKPILING); const stockpilingTag = pokemon.getTag(StockpilingTag)!; diff --git a/test/moves/synchronoise.test.ts b/test/moves/synchronoise.test.ts index 3cfeaf04af9..98178b66d00 100644 --- a/test/moves/synchronoise.test.ts +++ b/test/moves/synchronoise.test.ts @@ -34,8 +34,8 @@ describe("Moves - Synchronoise", () => { it("should consider the user's tera type if it is terastallized", async () => { await game.classicMode.startBattle([SpeciesId.BIDOOF]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); // force the player to be terastallized playerPokemon.teraType = PokemonType.WATER; diff --git a/test/moves/syrup-bomb.test.ts b/test/moves/syrup-bomb.test.ts index 3e5bddf1e7b..75f76595730 100644 --- a/test/moves/syrup-bomb.test.ts +++ b/test/moves/syrup-bomb.test.ts @@ -40,7 +40,7 @@ describe("Moves - SYRUP BOMB", () => { it("decreases the target Pokemon's speed stat once per turn for 3 turns", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const targetPokemon = game.scene.getEnemyPokemon()!; + const targetPokemon = game.field.getEnemyPokemon(); expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); game.move.select(MoveId.SYRUP_BOMB); @@ -65,7 +65,7 @@ describe("Moves - SYRUP BOMB", () => { game.override.enemyAbility(AbilityId.BULLETPROOF); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const targetPokemon = game.scene.getEnemyPokemon()!; + const targetPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.SYRUP_BOMB); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -87,6 +87,6 @@ describe("Moves - SYRUP BOMB", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPD)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.SPD)).toBe(-1); }); }); diff --git a/test/moves/tail-whip.test.ts b/test/moves/tail-whip.test.ts index c872b2535e3..8d2dfbda096 100644 --- a/test/moves/tail-whip.test.ts +++ b/test/moves/tail-whip.test.ts @@ -39,7 +39,7 @@ describe("Moves - Tail whip", () => { const moveToUse = MoveId.TAIL_WHIP; await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.MIGHTYENA]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); game.move.select(moveToUse); diff --git a/test/moves/tailwind.test.ts b/test/moves/tailwind.test.ts index 436c854066f..5c91a37f786 100644 --- a/test/moves/tailwind.test.ts +++ b/test/moves/tailwind.test.ts @@ -81,8 +81,8 @@ describe("Moves - Tailwind", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const ally = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const ally = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); const allySpd = ally.getStat(Stat.SPD); const enemySpd = enemy.getStat(Stat.SPD); diff --git a/test/moves/tar-shot.test.ts b/test/moves/tar-shot.test.ts index 719b5e4c7fe..d8da8c3437e 100644 --- a/test/moves/tar-shot.test.ts +++ b/test/moves/tar-shot.test.ts @@ -36,7 +36,7 @@ describe("Moves - Tar Shot", () => { it("lowers the target's Speed stat by one stage and doubles the effectiveness of Fire-type moves used on the target", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); @@ -57,7 +57,7 @@ describe("Moves - Tar Shot", () => { it("will not double the effectiveness of Fire-type moves used on a target that is already under the effect of Tar Shot (but may still lower its Speed)", async () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); @@ -86,7 +86,7 @@ describe("Moves - Tar Shot", () => { game.override.enemySpecies(SpeciesId.SPRIGATITO); await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); enemy.teraType = PokemonType.GRASS; enemy.isTerastallized = true; @@ -110,7 +110,7 @@ describe("Moves - Tar Shot", () => { game.override.enemySpecies(SpeciesId.SPRIGATITO); await game.classicMode.startBattle([SpeciesId.PIKACHU]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); vi.spyOn(enemy, "getMoveEffectiveness"); diff --git a/test/moves/taunt.test.ts b/test/moves/taunt.test.ts index f16a019ce2c..6ac158d3a8f 100644 --- a/test/moves/taunt.test.ts +++ b/test/moves/taunt.test.ts @@ -33,7 +33,7 @@ describe("Moves - Taunt", () => { it("Pokemon should not be able to use Status Moves", async () => { await game.classicMode.startBattle([SpeciesId.REGIELEKI]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // First turn, Player Pokemon succeeds using Growl without Taunt game.move.select(MoveId.GROWL); diff --git a/test/moves/telekinesis.test.ts b/test/moves/telekinesis.test.ts index 4c452c24ec6..f14c42d1dcc 100644 --- a/test/moves/telekinesis.test.ts +++ b/test/moves/telekinesis.test.ts @@ -37,7 +37,7 @@ describe("Moves - Telekinesis", () => { it("Telekinesis makes the affected vulnerable to most attacking moves regardless of accuracy", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyOpponent = game.scene.getEnemyPokemon()!; + const enemyOpponent = game.field.getEnemyPokemon(); game.move.select(MoveId.TELEKINESIS); await game.phaseInterceptor.to("TurnEndPhase"); @@ -54,7 +54,7 @@ describe("Moves - Telekinesis", () => { it("Telekinesis makes the affected airborne and immune to most Ground-moves", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyOpponent = game.scene.getEnemyPokemon()!; + const enemyOpponent = game.field.getEnemyPokemon(); game.move.select(MoveId.TELEKINESIS); await game.phaseInterceptor.to("TurnEndPhase"); @@ -72,7 +72,7 @@ describe("Moves - Telekinesis", () => { game.override.enemyMoveset(MoveId.TRANSFORM); await game.classicMode.startBattle([SpeciesId.DIGLETT]); - const enemyOpponent = game.scene.getEnemyPokemon()!; + const enemyOpponent = game.field.getEnemyPokemon(); game.move.select(MoveId.TELEKINESIS); await game.phaseInterceptor.to("TurnEndPhase"); @@ -84,7 +84,7 @@ describe("Moves - Telekinesis", () => { it("Moves like Smack Down and 1000 Arrows remove all effects of Telekinesis from the target Pokemon", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemyOpponent = game.scene.getEnemyPokemon()!; + const enemyOpponent = game.field.getEnemyPokemon(); game.move.select(MoveId.TELEKINESIS); await game.phaseInterceptor.to("TurnEndPhase"); @@ -102,8 +102,8 @@ describe("Moves - Telekinesis", () => { game.override.enemyMoveset([MoveId.SPLASH, MoveId.INGRAIN]); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; - const enemyOpponent = game.scene.getEnemyPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); + const enemyOpponent = game.field.getEnemyPokemon(); game.move.select(MoveId.TELEKINESIS); await game.move.selectEnemyMove(MoveId.SPLASH); @@ -134,6 +134,6 @@ describe("Moves - Telekinesis", () => { game.doSelectPartyPokemon(1); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined(); + expect(game.field.getPlayerPokemon().getTag(BattlerTagType.TELEKINESIS)).toBeUndefined(); }); }); diff --git a/test/moves/tera-blast.test.ts b/test/moves/tera-blast.test.ts index 37dd8f53eaf..c23c990ec87 100644 --- a/test/moves/tera-blast.test.ts +++ b/test/moves/tera-blast.test.ts @@ -49,10 +49,10 @@ describe("Moves - Tera Blast", () => { it("changes type to match user's tera type", async () => { game.override.enemySpecies(SpeciesId.FURRET); await game.classicMode.startBattle(); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.teraType = PokemonType.FIGHTING; playerPokemon.isTerastallized = true; @@ -66,7 +66,7 @@ describe("Moves - Tera Blast", () => { it("increases power if user is Stellar tera type", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.teraType = PokemonType.STELLAR; playerPokemon.isTerastallized = true; @@ -80,11 +80,11 @@ describe("Moves - Tera Blast", () => { it("is super effective against terastallized targets if user is Stellar tera type", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.teraType = PokemonType.STELLAR; playerPokemon.isTerastallized = true; - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); enemyPokemon.isTerastallized = true; @@ -98,7 +98,7 @@ describe("Moves - Tera Blast", () => { it("uses the higher ATK for damage calculation", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.stats[Stat.ATK] = 100; playerPokemon.stats[Stat.SPATK] = 1; playerPokemon.isTerastallized = true; @@ -113,7 +113,7 @@ describe("Moves - Tera Blast", () => { it("uses the higher SPATK for damage calculation", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.stats[Stat.ATK] = 1; playerPokemon.stats[Stat.SPATK] = 100; @@ -128,7 +128,7 @@ describe("Moves - Tera Blast", () => { game.override.enemyMoveset([MoveId.CHARM]); await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.stats[Stat.ATK] = 51; playerPokemon.stats[Stat.SPATK] = 50; @@ -146,7 +146,7 @@ describe("Moves - Tera Blast", () => { .starterSpecies(SpeciesId.CUBONE); await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.stats[Stat.ATK] = 50; playerPokemon.stats[Stat.SPATK] = 51; @@ -164,7 +164,7 @@ describe("Moves - Tera Blast", () => { game.override.ability(AbilityId.HUGE_POWER); await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.stats[Stat.ATK] = 50; playerPokemon.stats[Stat.SPATK] = 51; @@ -179,7 +179,7 @@ describe("Moves - Tera Blast", () => { it("causes stat drops if user is Stellar tera type", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); playerPokemon.teraType = PokemonType.STELLAR; playerPokemon.isTerastallized = true; @@ -199,14 +199,14 @@ describe("Moves - Tera Blast", () => { ])("should be $ty type if the user has $ab", async ({ ab_id, ty_id }) => { game.override.ability(ab_id).moveset([MoveId.TERA_BLAST]).enemyAbility(AbilityId.BALL_FETCH); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getMoveType(allMoves[MoveId.TERA_BLAST])).toBe(ty_id); }); it("should not be affected by normalize when the user is terastallized with tera normal", async () => { game.override.moveset([MoveId.TERA_BLAST]).ability(AbilityId.NORMALIZE); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // override the tera state for the pokemon playerPokemon.isTerastallized = true; playerPokemon.teraType = PokemonType.NORMAL; diff --git a/test/moves/tera-starstorm.test.ts b/test/moves/tera-starstorm.test.ts index d13ab3fb3b0..869cf597dde 100644 --- a/test/moves/tera-starstorm.test.ts +++ b/test/moves/tera-starstorm.test.ts @@ -36,7 +36,7 @@ describe("Moves - Tera Starstorm", () => { game.override.battleStyle("single"); await game.classicMode.startBattle([SpeciesId.TERAPAGOS]); - const terapagos = game.scene.getPlayerPokemon()!; + const terapagos = game.field.getPlayerPokemon(); terapagos.isTerastallized = true; vi.spyOn(terapagos, "getMoveType"); diff --git a/test/moves/thousand-arrows.test.ts b/test/moves/thousand-arrows.test.ts index e42e92d35d2..47bdce8476c 100644 --- a/test/moves/thousand-arrows.test.ts +++ b/test/moves/thousand-arrows.test.ts @@ -36,7 +36,7 @@ describe("Moves - Thousand Arrows", () => { it("move should hit and ground Flying-type targets", async () => { await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THOUSAND_ARROWS); @@ -55,7 +55,7 @@ describe("Moves - Thousand Arrows", () => { await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THOUSAND_ARROWS); @@ -74,7 +74,7 @@ describe("Moves - Thousand Arrows", () => { await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); enemyPokemon.addTag(BattlerTagType.FLOATING, undefined, MoveId.MAGNET_RISE); diff --git a/test/moves/throat-chop.test.ts b/test/moves/throat-chop.test.ts index f5e6a978e3d..a4f090b2de1 100644 --- a/test/moves/throat-chop.test.ts +++ b/test/moves/throat-chop.test.ts @@ -34,7 +34,7 @@ describe("Moves - Throat Chop", () => { it("prevents the target from using sound-based moves for two turns", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.GROWL); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); diff --git a/test/moves/thunder-wave.test.ts b/test/moves/thunder-wave.test.ts index 84408ace7e9..9c630852d48 100644 --- a/test/moves/thunder-wave.test.ts +++ b/test/moves/thunder-wave.test.ts @@ -36,7 +36,7 @@ describe("Moves - Thunder Wave", () => { game.override.enemySpecies(SpeciesId.MAGIKARP); await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.move.forceHit(); @@ -49,7 +49,7 @@ describe("Moves - Thunder Wave", () => { game.override.enemySpecies(SpeciesId.DIGLETT); await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.move.forceHit(); @@ -62,7 +62,7 @@ describe("Moves - Thunder Wave", () => { game.override.enemySpecies(SpeciesId.MAGIKARP).enemyStatusEffect(StatusEffect.BURN); await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.move.forceHit(); @@ -75,7 +75,7 @@ describe("Moves - Thunder Wave", () => { game.override.ability(AbilityId.NORMALIZE).enemySpecies(SpeciesId.DIGLETT); await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.move.forceHit(); @@ -88,7 +88,7 @@ describe("Moves - Thunder Wave", () => { game.override.ability(AbilityId.NORMALIZE).enemySpecies(SpeciesId.HAUNTER); await game.classicMode.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon: EnemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.THUNDER_WAVE); await game.move.forceHit(); diff --git a/test/moves/tidy-up.test.ts b/test/moves/tidy-up.test.ts index 8dd74e4ab78..51399552a08 100644 --- a/test/moves/tidy-up.test.ts +++ b/test/moves/tidy-up.test.ts @@ -92,7 +92,7 @@ describe("Moves - Tidy Up", () => { game.move.select(MoveId.TIDY_UP); await game.phaseInterceptor.to(MoveEndPhase); - const pokemon = [game.scene.getPlayerPokemon()!, game.scene.getEnemyPokemon()!]; + const pokemon = [game.field.getPlayerPokemon(), game.field.getEnemyPokemon()]; pokemon.forEach(p => { expect(p).toBeDefined(); expect(p!.getTag(SubstituteTag)).toBeUndefined(); @@ -102,7 +102,7 @@ describe("Moves - Tidy Up", () => { it("user's stats are raised with no traps set", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0); diff --git a/test/moves/torment.test.ts b/test/moves/torment.test.ts index 1ed4529fcbd..cdefd8ef005 100644 --- a/test/moves/torment.test.ts +++ b/test/moves/torment.test.ts @@ -36,7 +36,7 @@ describe("Moves - Torment", () => { it("Pokemon should not be able to use the same move consecutively", async () => { await game.classicMode.startBattle([SpeciesId.CHANSEY]); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); // First turn, Player Pokemon uses Tackle successfully game.move.select(MoveId.TACKLE); diff --git a/test/moves/toxic-spikes.test.ts b/test/moves/toxic-spikes.test.ts index 1315eaa31c3..0a0bf8baefc 100644 --- a/test/moves/toxic-spikes.test.ts +++ b/test/moves/toxic-spikes.test.ts @@ -86,7 +86,7 @@ describe("Moves - Toxic Spikes", () => { it("should be removed if a grounded poison pokemon switches in", async () => { await game.classicMode.runToSummon([SpeciesId.MUK, SpeciesId.PIDGEY]); - const muk = game.scene.getPlayerPokemon()!; + const muk = game.field.getPlayerPokemon(); game.move.select(MoveId.TOXIC_SPIKES); await game.toNextTurn(); diff --git a/test/moves/toxic.test.ts b/test/moves/toxic.test.ts index 13af6cf48fd..8044d2f9a90 100644 --- a/test/moves/toxic.test.ts +++ b/test/moves/toxic.test.ts @@ -37,7 +37,7 @@ describe("Moves - Toxic", () => { game.move.select(MoveId.TOXIC); await game.phaseInterceptor.to("BerryPhase", false); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.TOXIC); }); it("may miss if user is not Poison-type", async () => { @@ -47,7 +47,7 @@ describe("Moves - Toxic", () => { game.move.select(MoveId.TOXIC); await game.phaseInterceptor.to("BerryPhase", false); - expect(game.scene.getEnemyPokemon()!.status).toBeUndefined(); + expect(game.field.getEnemyPokemon().status).toBeUndefined(); }); it("should hit semi-invulnerable targets if user is Poison-type", async () => { @@ -59,7 +59,7 @@ describe("Moves - Toxic", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase", false); - expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC); + expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.TOXIC); }); it("should miss semi-invulnerable targets if user is not Poison-type", async () => { @@ -71,7 +71,7 @@ describe("Moves - Toxic", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase", false); - expect(game.scene.getEnemyPokemon()!.status).toBeUndefined(); + expect(game.field.getEnemyPokemon().status).toBeUndefined(); }); it("moves other than Toxic should not hit semi-invulnerable targets even if user is Poison-type", async () => { @@ -82,7 +82,7 @@ describe("Moves - Toxic", () => { await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.phaseInterceptor.to("BerryPhase", false); - const enemyPokemon = game.scene.getEnemyPokemon()!; + const enemyPokemon = game.field.getEnemyPokemon(); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); }); }); diff --git a/test/moves/trick-or-treat.test.ts b/test/moves/trick-or-treat.test.ts index 095cda873e0..3e32c877b9e 100644 --- a/test/moves/trick-or-treat.test.ts +++ b/test/moves/trick-or-treat.test.ts @@ -35,13 +35,13 @@ describe("Moves - Trick Or Treat", () => { it("will replace added type from Forest's Curse", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); game.move.select(MoveId.FORESTS_CURSE); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemyPokemon!.summonData.addedType).toBe(PokemonType.GRASS); + expect(enemyPokemon.summonData.addedType).toBe(PokemonType.GRASS); game.move.select(MoveId.TRICK_OR_TREAT); await game.phaseInterceptor.to("TurnEndPhase"); - expect(enemyPokemon?.summonData.addedType).toBe(PokemonType.GHOST); + expect(enemyPokemon.summonData.addedType).toBe(PokemonType.GHOST); }); }); diff --git a/test/moves/u-turn.test.ts b/test/moves/u-turn.test.ts index fb34490fdf4..25c333e58e1 100644 --- a/test/moves/u-turn.test.ts +++ b/test/moves/u-turn.test.ts @@ -37,7 +37,7 @@ describe("Moves - U-turn", () => { const playerHp = 1; game.override.ability(AbilityId.REGENERATOR); await game.classicMode.startBattle([SpeciesId.RAICHU, SpeciesId.SHUCKLE]); - game.scene.getPlayerPokemon()!.hp = playerHp; + game.field.getPlayerPokemon().hp = playerHp; // act game.move.select(MoveId.U_TURN); @@ -49,7 +49,7 @@ describe("Moves - U-turn", () => { Math.floor(game.scene.getPlayerParty()[1].getMaxHp() * 0.33 + playerHp), ); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.SHUCKLE); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.SHUCKLE); }); it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => { @@ -63,9 +63,9 @@ describe("Moves - U-turn", () => { await game.phaseInterceptor.to("SwitchPhase", false); // assert - const playerPkm = game.scene.getPlayerPokemon()!; + const playerPkm = game.field.getPlayerPokemon(); expect(playerPkm.hp).not.toEqual(playerPkm.getMaxHp()); - expect(game.scene.getEnemyPokemon()!.waveData.abilityRevealed).toBe(true); // proxy for asserting ability activated + expect(game.field.getEnemyPokemon().waveData.abilityRevealed).toBe(true); // proxy for asserting ability activated expect(playerPkm.species.speciesId).toEqual(SpeciesId.RAICHU); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }); @@ -74,24 +74,24 @@ describe("Moves - U-turn", () => { // arrange game.override.enemyAbility(AbilityId.POISON_POINT); await game.classicMode.startBattle([SpeciesId.RAICHU, SpeciesId.SHUCKLE]); - vi.spyOn(game.scene.getEnemyPokemon()!, "randBattleSeedInt").mockReturnValue(0); + vi.spyOn(game.field.getEnemyPokemon(), "randBattleSeedInt").mockReturnValue(0); // act game.move.select(MoveId.U_TURN); await game.phaseInterceptor.to("SwitchPhase", false); // assert - const playerPkm = game.scene.getPlayerPokemon()!; + const playerPkm = game.field.getPlayerPokemon(); expect(playerPkm.status?.effect).toEqual(StatusEffect.POISON); expect(playerPkm.species.speciesId).toEqual(SpeciesId.RAICHU); - expect(game.scene.getEnemyPokemon()!.waveData.abilityRevealed).toBe(true); // proxy for asserting ability activated + expect(game.field.getEnemyPokemon().waveData.abilityRevealed).toBe(true); // proxy for asserting ability activated expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }); it("still forces a switch if u-turn KO's the opponent", async () => { game.override.startingLevel(1000); // Ensure that U-Turn KO's the opponent await game.classicMode.startBattle([SpeciesId.RAICHU, SpeciesId.SHUCKLE]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); // KO the opponent with U-Turn game.move.select(MoveId.U_TURN); @@ -101,6 +101,6 @@ describe("Moves - U-turn", () => { // Check that U-Turn forced a switch expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.SHUCKLE); + expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.SHUCKLE); }); }); diff --git a/test/moves/upper-hand.test.ts b/test/moves/upper-hand.test.ts index 91f3a34b9c6..b2e8dca2afc 100644 --- a/test/moves/upper-hand.test.ts +++ b/test/moves/upper-hand.test.ts @@ -38,8 +38,8 @@ describe("Moves - Upper Hand", () => { it("should flinch the opponent before they use a priority attack", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; - const magikarp = game.scene.getEnemyPokemon()!; + const feebas = game.field.getPlayerPokemon(); + const magikarp = game.field.getEnemyPokemon(); game.move.select(MoveId.UPPER_HAND); await game.phaseInterceptor.to("BerryPhase"); @@ -57,7 +57,7 @@ describe("Moves - Upper Hand", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; + const feebas = game.field.getPlayerPokemon(); game.move.select(MoveId.UPPER_HAND); await game.phaseInterceptor.to("BerryPhase"); @@ -70,8 +70,8 @@ describe("Moves - Upper Hand", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; - const magikarp = game.scene.getEnemyPokemon()!; + const feebas = game.field.getPlayerPokemon(); + const magikarp = game.field.getEnemyPokemon(); game.move.select(MoveId.UPPER_HAND); await game.phaseInterceptor.to("BerryPhase"); @@ -86,7 +86,7 @@ describe("Moves - Upper Hand", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const feebas = game.scene.getPlayerPokemon()!; + const feebas = game.field.getPlayerPokemon(); game.move.select(MoveId.UPPER_HAND); diff --git a/test/moves/whirlwind.test.ts b/test/moves/whirlwind.test.ts index 2aadb76b019..bbb2afe621a 100644 --- a/test/moves/whirlwind.test.ts +++ b/test/moves/whirlwind.test.ts @@ -48,7 +48,7 @@ describe("Moves - Whirlwind", () => { // Must have a pokemon in the back so that the move misses instead of fails. await game.classicMode.startBattle([SpeciesId.STARAPTOR, SpeciesId.MAGIKARP]); - const staraptor = game.scene.getPlayerPokemon()!; + const staraptor = game.field.getPlayerPokemon(); game.move.select(move); await game.move.selectEnemyMove(MoveId.WHIRLWIND); @@ -56,7 +56,7 @@ describe("Moves - Whirlwind", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(staraptor.findTag(t => t.tagType === BattlerTagType.FLYING)).toBeDefined(); - expect(game.scene.getEnemyPokemon()!.getLastXMoves(1)[0].result).toBe(MoveResult.MISS); + expect(game.field.getEnemyPokemon().getLastXMoves(1)[0].result).toBe(MoveResult.MISS); }); it("should force switches randomly", async () => { @@ -208,7 +208,7 @@ describe("Moves - Whirlwind", () => { // expect the enemy to have at least 4 pokemon, necessary for this check to even work expect(game.scene.getEnemyParty().length, "enemy must have exactly 4 pokemon").toBeGreaterThanOrEqual(4); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); console.log(user.getMoveset(false)); @@ -219,7 +219,7 @@ describe("Moves - Whirlwind", () => { await game.toNextTurn(); // Get the enemy pokemon id so we can check if is the same after switch. - const enemy_id = game.scene.getEnemyPokemon()!.id; + const enemy_id = game.field.getEnemyPokemon().id; // Hit the enemy that fainted with whirlwind. game.move.select(MoveId.WHIRLWIND, 0, BattlerIndex.ENEMY); @@ -231,7 +231,7 @@ describe("Moves - Whirlwind", () => { await game.toNextTurn(); // Expect the enemy pokemon to not have switched out. - expect(game.scene.getEnemyPokemon()!.id).toBe(enemy_id); + expect(game.field.getEnemyPokemon().id).toBe(enemy_id); }); it("should force a wild pokemon to flee", async () => { @@ -242,7 +242,7 @@ describe("Moves - Whirlwind", () => { .ability(AbilityId.BALL_FETCH); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const user = game.scene.getPlayerPokemon()!; + const user = game.field.getPlayerPokemon(); game.move.select(MoveId.WHIRLWIND); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/moves/wide-guard.test.ts b/test/moves/wide-guard.test.ts index e599829e1b9..b45b7327265 100644 --- a/test/moves/wide-guard.test.ts +++ b/test/moves/wide-guard.test.ts @@ -1,11 +1,12 @@ import { AbilityId } from "#enums/ability-id"; +import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; +import { MoveResult } from "#enums/move-result"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; -import { BerryPhase } from "#phases/berry-phase"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Wide Guard", () => { let phaserGame: Phaser.Game; @@ -26,71 +27,84 @@ describe("Moves - Wide Guard", () => { game.override .battleStyle("double") - .moveset([MoveId.WIDE_GUARD, MoveId.SPLASH, MoveId.SURF]) + .moveset([MoveId.WIDE_GUARD, MoveId.SPLASH, MoveId.SURF, MoveId.SPIKY_SHIELD]) .enemySpecies(SpeciesId.SNORLAX) - .enemyMoveset(MoveId.SWIFT) + .enemyMoveset([MoveId.SWIFT, MoveId.GROWL, MoveId.TACKLE]) .enemyAbility(AbilityId.INSOMNIA) .startingLevel(100) .enemyLevel(100); }); - test("should protect the user and allies from multi-target attack moves", async () => { + it("should protect the user and allies from multi-target attack and status moves", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); + const [charizard, blastoise] = game.scene.getPlayerField(); - const leadPokemon = game.scene.getPlayerField(); + game.move.select(MoveId.WIDE_GUARD, BattlerIndex.PLAYER); + game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.SWIFT); + await game.move.forceEnemyMove(MoveId.GROWL); + await game.phaseInterceptor.to("TurnEndPhase"); - game.move.select(MoveId.WIDE_GUARD); - game.move.select(MoveId.SPLASH, 1); - - await game.phaseInterceptor.to(BerryPhase, false); - - leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp())); + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(blastoise.hp).toBe(blastoise.getMaxHp()); + expect(charizard.getStatStage(Stat.ATK)).toBe(0); + expect(blastoise.getStatStage(Stat.ATK)).toBe(0); }); - test("should protect the user and allies from multi-target status moves", async () => { - game.override.enemyMoveset([MoveId.GROWL]); - + it("should not protect the user and allies from single-target moves", async () => { await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const [charizard, blastoise] = game.scene.getPlayerField(); + game.move.select(MoveId.WIDE_GUARD, BattlerIndex.PLAYER); + game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + await game.move.forceEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER); + await game.move.forceEnemyMove(MoveId.TACKLE, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("TurnEndPhase"); - game.move.select(MoveId.WIDE_GUARD); - game.move.select(MoveId.SPLASH, 1); - - await game.phaseInterceptor.to(BerryPhase, false); - - leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0)); + expect(charizard.hp).toBeLessThan(charizard.getMaxHp()); + expect(blastoise.hp).toBeLessThan(blastoise.getMaxHp()); }); - test("should not protect the user and allies from single-target moves", async () => { - game.override.enemyMoveset([MoveId.TACKLE]); + it("should protect the user from its ally's multi-target move", async () => { + game.override.enemyMoveset(MoveId.SPLASH); await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - const leadPokemon = game.scene.getPlayerField(); + const charizard = game.scene.getPlayerPokemon()!; + const [snorlax1, snorlax2] = game.scene.getEnemyField(); - game.move.select(MoveId.WIDE_GUARD); - game.move.select(MoveId.SPLASH, 1); + game.move.select(MoveId.WIDE_GUARD, BattlerIndex.PLAYER); + game.move.select(MoveId.SURF, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("TurnEndPhase"); - await game.phaseInterceptor.to(BerryPhase, false); - - expect(leadPokemon.some(p => p.hp < p.getMaxHp())).toBeTruthy(); + expect(charizard.hp).toBe(charizard.getMaxHp()); + expect(snorlax1.hp).toBeLessThan(snorlax1.getMaxHp()); + expect(snorlax2.hp).toBeLessThan(snorlax2.getMaxHp()); }); - test("should protect the user from its ally's multi-target move", async () => { - game.override.enemyMoveset([MoveId.SPLASH]); + it("should increment (but not respect) other protection moves' fail counters", async () => { + game.override.battleStyle("single"); + await game.classicMode.startBattle([SpeciesId.CHARIZARD]); - await game.classicMode.startBattle([SpeciesId.CHARIZARD, SpeciesId.BLASTOISE]); - - const leadPokemon = game.scene.getPlayerField(); - const enemyPokemon = game.scene.getEnemyField(); + const charizard = game.scene.getPlayerPokemon()!; + // force protect to fail on anything other than a guaranteed success + vi.spyOn(charizard, "randBattleSeedInt").mockReturnValue(1); game.move.select(MoveId.WIDE_GUARD); - game.move.select(MoveId.SURF, 1); + await game.toNextTurn(); - await game.phaseInterceptor.to(BerryPhase, false); + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); - expect(leadPokemon[0].hp).toBe(leadPokemon[0].getMaxHp()); - enemyPokemon.forEach(p => expect(p.hp).toBeLessThan(p.getMaxHp())); + // ignored fail chance + game.move.select(MoveId.WIDE_GUARD); + await game.toNextTurn(); + + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + + game.move.select(MoveId.SPIKY_SHIELD); + await game.toNextTurn(); + + // ignored fail chance + expect(charizard.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); }); diff --git a/test/moves/will-o-wisp.test.ts b/test/moves/will-o-wisp.test.ts index 5cb94c4617c..864413b0ce7 100644 --- a/test/moves/will-o-wisp.test.ts +++ b/test/moves/will-o-wisp.test.ts @@ -36,7 +36,7 @@ describe("Moves - Will-O-Wisp", () => { it("should burn the opponent", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy = game.scene.getEnemyPokemon()!; + const enemy = game.field.getEnemyPokemon(); game.move.select(MoveId.WILL_O_WISP); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 8ec9b4cb345..a48422c459f 100644 --- a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -268,7 +268,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.phaseInterceptor.to(SelectModifierPhase, false); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); - const leadPokemonItems = scene.getPlayerParty()?.[0].getHeldItems() as PokemonHeldItemModifier[]; + const leadPokemonItems = scene.getPlayerParty()[0].getHeldItems() as PokemonHeldItemModifier[]; const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier); expect(item).toBeDefined; }); diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index b66d0e95ad0..3025b08b8b6 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -144,9 +144,9 @@ describe("Fun And Games! - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); - expect(scene.getEnemyPokemon()?.species.speciesId).toBe(SpeciesId.WOBBUFFET); - expect(scene.getEnemyPokemon()?.ivs).toEqual([0, 0, 0, 0, 0, 0]); - expect(scene.getEnemyPokemon()?.nature).toBe(Nature.MILD); + expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET); + expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]); + expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); @@ -206,7 +206,7 @@ describe("Fun And Games! - Mystery Encounter", () => { }); // Skip minigame - const wobbuffet = scene.getEnemyPokemon()!; + const wobbuffet = game.field.getEnemyPokemon(); wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp()); scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); @@ -236,7 +236,7 @@ describe("Fun And Games! - Mystery Encounter", () => { }); // Skip minigame - const wobbuffet = scene.getEnemyPokemon()!; + const wobbuffet = game.field.getEnemyPokemon(); wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp()); scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); @@ -266,7 +266,7 @@ describe("Fun And Games! - Mystery Encounter", () => { }); // Skip minigame - const wobbuffet = scene.getEnemyPokemon()!; + const wobbuffet = game.field.getEnemyPokemon(); wobbuffet.hp = 1; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index e9fcc9797d1..ed0d612e967 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -147,12 +147,13 @@ describe("Weird Dream - Mystery Encounter", () => { const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); + expect(modifierSelectHandler.options.length).toEqual(6); expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("ROGUE_BALL"); expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("MINT"); expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("MINT"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("MINT"); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.id).toEqual("MINT"); + expect(modifierSelectHandler.options[5].modifierTypeOption.type.id).toEqual("MINT"); }); it("should leave encounter without battle", async () => { diff --git a/test/phases/frenzy-move-reset.test.ts b/test/phases/frenzy-move-reset.test.ts index 93406ddd577..feedd1b5865 100644 --- a/test/phases/frenzy-move-reset.test.ts +++ b/test/phases/frenzy-move-reset.test.ts @@ -52,7 +52,7 @@ describe("Frenzy Move Reset", () => { it("should cancel frenzy move if move fails turn 2", async () => { await game.classicMode.startBattle(); - const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemon = game.field.getPlayerPokemon(); game.move.select(MoveId.THRASH); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); diff --git a/test/phases/learn-move-phase.test.ts b/test/phases/learn-move-phase.test.ts index 77902a0d959..3181f9238dd 100644 --- a/test/phases/learn-move-phase.test.ts +++ b/test/phases/learn-move-phase.test.ts @@ -29,7 +29,7 @@ describe("Learn Move Phase", () => { it("If Pokemon has less than 4 moves, its newest move will be added to the lowest empty index", async () => { game.override.moveset([MoveId.SPLASH]); await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const pokemon = game.scene.getPlayerPokemon()!; + const pokemon = game.field.getPlayerPokemon(); const newMovePos = pokemon?.getMoveset().length; game.move.select(MoveId.SPLASH); await game.doKillOpponents(); @@ -43,7 +43,7 @@ describe("Learn Move Phase", () => { it("If a pokemon has 4 move slots filled, the chosen move will be deleted and replaced", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const bulbasaur = game.scene.getPlayerPokemon()!; + const bulbasaur = game.field.getPlayerPokemon(); const prevMoveset = [MoveId.SPLASH, MoveId.ABSORB, MoveId.ACID, MoveId.VINE_WHIP]; const moveSlotNum = 3; @@ -74,7 +74,7 @@ describe("Learn Move Phase", () => { it("selecting the newly deleted move will reject it and keep old moveset", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); - const bulbasaur = game.scene.getPlayerPokemon()!; + const bulbasaur = game.field.getPlayerPokemon(); const prevMoveset = [MoveId.SPLASH, MoveId.ABSORB, MoveId.ACID, MoveId.VINE_WHIP]; game.move.changeMoveset(bulbasaur, [MoveId.SPLASH, MoveId.ABSORB, MoveId.ACID, MoveId.VINE_WHIP]); diff --git a/test/ui/starter-select.test.ts b/test/ui/starter-select.test.ts index a8c6284cf3f..6dc9603c8b3 100644 --- a/test/ui/starter-select.test.ts +++ b/test/ui/starter-select.test.ts @@ -10,7 +10,7 @@ import { EncounterPhase } from "#phases/encounter-phase"; import { SelectStarterPhase } from "#phases/select-starter-phase"; import type { TitlePhase } from "#phases/title-phase"; import { GameManager } from "#test/test-utils/game-manager"; -import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler"; +import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import type { OptionSelectUiHandler } from "#ui/option-select-ui-handler"; import type { SaveSlotSelectUiHandler } from "#ui/save-slot-select-ui-handler"; import type { StarterSelectUiHandler } from "#ui/starter-select-ui-handler";