diff --git a/biome.jsonc b/biome.jsonc index 3385614635c..40301b3e0bc 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -32,7 +32,6 @@ // TODO: these files are too big and complex, ignore them until their respective refactors "src/data/moves/move.ts", "src/data/abilities/ability.ts", - "src/field/pokemon.ts", // this file is just too big: "src/data/balance/tms.ts" @@ -58,7 +57,7 @@ }, "style": { "noVar": "error", - "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome + "useEnumInitializers": "off", // large enums like Moves/Species would make this cumbersome "useBlockStatements": "error", "useConst": "error", "useImportType": "error", @@ -73,9 +72,9 @@ }, "suspicious": { "noDoubleEquals": "error", - // While this would be a nice rule to enable, the current structure of the codebase makes this infeasible + // While this would be a nice rule to enable, the current structure of the codebase makes this infeasible // due to being used for move/ability `args` params and save data-related code. - // This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off. + // This can likely be enabled for all non-utils files once these are eventually reworked, but until then we leave it off. "noExplicitAny": "off", "noAssignInExpressions": "off", "noPrototypeBuiltins": "off", @@ -92,6 +91,19 @@ "noUselessSwitchCase": "off", // Explicit > Implicit "noUselessConstructor": "warn", // TODO: Refactor and make this an error "noBannedTypes": "warn" // TODO: Refactor and make this an error + }, + "nursery": { + "noRestrictedTypes": { + "level": "error", + "options": { + "types": { + "integer": { + "message": "This is an alias for 'number' that can provide false impressions of what values can actually be contained in this variable. Use 'number' instead.", + "use": "number" + } + } + } + } } } }, diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 25798de3b4a..50b9c2da78c 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -3,7 +3,7 @@ import { transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedItem } from "#app/utils/common"; +import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; @@ -88,7 +88,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui const r = randSeedInt(SHINY_MAGIKARP_WEIGHT); - let validEventEncounters = timedEventManager + const validEventEncounters = timedEventManager .getEventEncounters() .filter( s => @@ -111,22 +111,26 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui if ( r === 0 || ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && - (validEventEncounters.length === 0)) + validEventEncounters.length === 0) ) { // If you roll 1%, give shiny Magikarp with random variant species = getPokemonSpecies(Species.MAGIKARP); pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true); - } - else if ( - (validEventEncounters.length > 0 && (r <= EVENT_THRESHOLD || - (isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE))) + } else if ( + validEventEncounters.length > 0 && + (r <= EVENT_THRESHOLD || isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) ) { tries = 0; do { // If you roll 20%, give event encounter with 3 extra shiny rolls and its HA, if it has one const enc = randSeedItem(validEventEncounters); species = getPokemonSpecies(enc.species); - pokemon = new PlayerPokemon(species, 5, species.abilityHidden === Abilities.NONE ? undefined : 2, enc.formIndex); + pokemon = new PlayerPokemon( + species, + 5, + species.abilityHidden === Abilities.NONE ? undefined : 2, + enc.formIndex, + ); pokemon.trySetShinySeed(); pokemon.trySetShinySeed(); pokemon.trySetShinySeed(); @@ -145,15 +149,13 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui pokemon.trySetShinySeed(); pokemon.trySetShinySeed(); pokemon.trySetShinySeed(); - } - else { + } else { // If there's, and this would never happen, no eligible event encounters with a hidden ability, just do Magikarp species = getPokemonSpecies(Species.MAGIKARP); pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true); } } - } - else { + } else { pokemon = new PlayerPokemon(species, 5, 2, species.formIndex); } pokemon.generateAndPopulateMoveset(); diff --git a/src/data/trainers/TrainerPartyTemplate.ts b/src/data/trainers/TrainerPartyTemplate.ts index ccc494218e9..86201589276 100644 --- a/src/data/trainers/TrainerPartyTemplate.ts +++ b/src/data/trainers/TrainerPartyTemplate.ts @@ -224,16 +224,16 @@ export const trainerPartyTemplates = { */ export function getEvilGruntPartyTemplate(): TrainerPartyTemplate { const waveIndex = globalScene.currentBattle?.waveIndex; - if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_1){ + if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_1) { return trainerPartyTemplates.TWO_AVG; } - if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_2){ + if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_2) { return trainerPartyTemplates.THREE_AVG; } - if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_3){ + if (waveIndex <= ClassicFixedBossWaves.EVIL_GRUNT_3) { return trainerPartyTemplates.TWO_AVG_ONE_STRONG; } - if (waveIndex <= ClassicFixedBossWaves.EVIL_ADMIN_1){ + if (waveIndex <= ClassicFixedBossWaves.EVIL_ADMIN_1) { return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger } return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger @@ -251,7 +251,7 @@ export function getGymLeaderPartyTemplate() { switch (gameMode.modeId) { case GameModes.DAILY: if (currentBattle?.waveIndex <= 20) { - return trainerPartyTemplates.GYM_LEADER_2 + return trainerPartyTemplates.GYM_LEADER_2; } return trainerPartyTemplates.GYM_LEADER_3; case GameModes.CHALLENGE: // In the future, there may be a ChallengeType to call here. For now, use classic's. @@ -259,13 +259,15 @@ export function getGymLeaderPartyTemplate() { if (currentBattle?.waveIndex <= 20) { return trainerPartyTemplates.GYM_LEADER_1; // 1 avg 1 strong } - else if (currentBattle?.waveIndex <= 30) { + if (currentBattle?.waveIndex <= 30) { return trainerPartyTemplates.GYM_LEADER_2; // 1 avg 1 strong 1 stronger } - else if (currentBattle?.waveIndex <= 60) { // 50 and 60 + // 50 and 60 + if (currentBattle?.waveIndex <= 60) { return trainerPartyTemplates.GYM_LEADER_3; // 2 avg 1 strong 1 stronger } - else if (currentBattle?.waveIndex <= 90) { // 80 and 90 + // 80 and 90 + if (currentBattle?.waveIndex <= 90) { return trainerPartyTemplates.GYM_LEADER_4; // 3 avg 1 strong 1 stronger } // 110+ diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 35ffa2bd652..64c04b63e78 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5,10 +5,7 @@ import { globalScene } from "#app/global-scene"; import type { Variant } from "#app/sprites/variant"; import { populateVariantColors, variantColorCache } from "#app/sprites/variant"; import { variantData } from "#app/sprites/variant"; -import BattleInfo, { - PlayerBattleInfo, - EnemyBattleInfo, -} from "#app/ui/battle-info"; +import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info"; import type Move from "#app/data/moves/move"; import { HighCritAttr, @@ -50,11 +47,26 @@ import { getPokemonSpecies, getPokemonSpeciesForm, } from "#app/data/pokemon-species"; +import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { - getStarterValueFriendshipCap, - speciesStarterCosts, -} from "#app/data/balance/starters"; -import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor, randSeedIntRange } from "#app/utils/common"; + NumberHolder, + randSeedInt, + getIvsFromId, + BooleanHolder, + randSeedItem, + isNullOrUndefined, + getEnumValues, + toDmgValue, + fixedInt, + rgbaToInt, + rgbHexToRgba, + rgbToHsv, + deltaRgb, + isBetween, + type nil, + type Constructor, + randSeedIntRange, +} from "#app/utils/common"; import type { TypeDamageMultiplier } from "#app/data/type"; import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; @@ -101,11 +113,7 @@ import { FusionSpeciesFormEvolution, validateShedinjaEvo, } from "#app/data/balance/pokemon-evolutions"; -import { - reverseCompatibleTms, - tmSpecies, - tmPoolTiers, -} from "#app/data/balance/tms"; +import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms"; import { BattlerTag, BattlerTagLapseType, @@ -128,11 +136,7 @@ import { type GrudgeTag, } from "../data/battler-tags"; import { WeatherType } from "#enums/weather-type"; -import { - ArenaTagSide, - NoCritTag, - WeakenMoveScreenTag, -} from "#app/data/arena-tag"; +import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag"; import type { SuppressAbilitiesTag } from "#app/data/arena-tag"; import type { Ability } from "#app/data/abilities/ability-class"; import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr"; @@ -171,7 +175,8 @@ import { MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, - CheckTrappedAbAttr, InfiltratorAbAttr, + CheckTrappedAbAttr, + InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, @@ -196,25 +201,18 @@ import type { PartyOption } from "#app/ui/party-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; -import { - EVOLVE_MOVE, - RELEARN_MOVE, -} from "#app/data/balance/pokemon-level-moves"; +import { EVOLVE_MOVE, RELEARN_MOVE } from "#app/data/balance/pokemon-level-moves"; import { achvs } from "#app/system/achv"; import type { StarterDataEntry, StarterMoveset } from "#app/system/game-data"; import { DexAttr } from "#app/system/game-data"; -import { - QuantizerCelebi, - argbFromRgba, - rgbaFromArgb, -} from "@material/material-color-utilities"; +import { QuantizerCelebi, argbFromRgba, rgbaFromArgb } from "@material/material-color-utilities"; import { getNatureStatMultiplier } from "#app/data/nature"; import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, - SpeciesFormChangePostMoveTrigger + SpeciesFormChangePostMoveTrigger, } from "#app/data/pokemon-forms"; import { TerrainType } from "#app/data/terrain"; import type { TrainerSlot } from "#enums/trainer-slot"; @@ -298,10 +296,10 @@ type damageParams = { simulated?: boolean; /** If defined, used in place of calculated effectiveness values */ effectiveness?: number; -} +}; /** Type for the parameters of {@linkcode Pokemon#getBaseDamage | getBaseDamage} */ -type getBaseDamageParams = Omit +type getBaseDamageParams = Omit; /** Type for the parameters of {@linkcode Pokemon#getAttackDamage | getAttackDamage} */ type getAttackDamageParams = Omit; @@ -413,7 +411,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; - this.abilityIndex = abilityIndex ?? this.generateAbilityIndex() + this.abilityIndex = abilityIndex ?? this.generateAbilityIndex(); if (formIndex !== undefined) { this.formIndex = formIndex; @@ -427,8 +425,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (variant !== undefined) { this.variant = variant; } - this.exp = - dataSource?.exp || getLevelTotalExp(this.level, species.growthRate); + this.exp = dataSource?.exp || getLevelTotalExp(this.level, species.growthRate); this.levelExp = dataSource?.levelExp || 0; if (dataSource) { @@ -444,18 +441,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.nickname = dataSource.nickname; this.moveset = dataSource.moveset; this.status = dataSource.status!; // TODO: is this bang correct? - this.friendship = - dataSource.friendship !== undefined - ? dataSource.friendship - : this.species.baseFriendship; + this.friendship = dataSource.friendship ?? this.species.baseFriendship; this.metLevel = dataSource.metLevel || 5; this.luck = dataSource.luck; this.metBiome = dataSource.metBiome; this.metSpecies = - dataSource.metSpecies ?? - (this.metBiome !== -1 - ? this.species.speciesId - : this.species.getRootSpeciesId(true)); + dataSource.metSpecies ?? (this.metBiome !== -1 ? this.species.speciesId : this.species.getRootSpeciesId(true)); this.metWave = dataSource.metWave ?? (this.metBiome === -1 ? -1 : 0); this.pauseEvolutions = dataSource.pauseEvolutions; this.pokerus = !!dataSource.pokerus; @@ -474,9 +465,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.fusionCustomPokemonData = dataSource.fusionCustomPokemonData; this.fusionTeraType = dataSource.fusionTeraType; this.usedTMs = dataSource.usedTMs ?? []; - this.customPokemonData = new CustomPokemonData( - dataSource.customPokemonData, - ); + this.customPokemonData = new CustomPokemonData(dataSource.customPokemonData); this.teraType = dataSource.teraType; this.isTerastallized = dataSource.isTerastallized; this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? []; @@ -489,12 +478,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.formIndex === undefined) { - this.formIndex = globalScene.getSpeciesFormIndex( - species, - this.gender, - this.nature, - this.isPlayer(), - ); + this.formIndex = globalScene.getSpeciesFormIndex(species, this.gender, this.nature, this.isPlayer()); } if (this.shiny === undefined) { @@ -513,19 +497,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.friendship = species.baseFriendship; this.metLevel = level; - this.metBiome = globalScene.currentBattle - ? globalScene.arena.biomeType - : -1; + this.metBiome = globalScene.currentBattle ? globalScene.arena.biomeType : -1; this.metSpecies = species.speciesId; - this.metWave = globalScene.currentBattle - ? globalScene.currentBattle.waveIndex - : -1; + this.metWave = globalScene.currentBattle ? globalScene.currentBattle.waveIndex : -1; this.pokerus = false; if (level > 1) { - const fused = new BooleanHolder( - globalScene.gameMode.isSplicedOnly, - ); + const fused = new BooleanHolder(globalScene.gameMode.isSplicedOnly); if (!fused.value && !this.isPlayer() && !this.hasTrainer()) { globalScene.applyModifier(EnemyFusionChanceModifier, false, fused); } @@ -535,9 +513,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.generateFusionSpecies(); } } - this.luck = - (this.shiny ? this.variant + 1 : 0) + - (this.fusionShiny ? this.fusionVariant + 1 : 0); + this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0); this.fusionLuck = this.luck; this.teraType = randSeedItem(this.getTypes(false, false, true)); @@ -557,15 +533,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!dataSource) { this.calculateStats(); } - } /** - * @param {boolean} useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + * @param useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). */ - getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.summonData.illusion) ? this.summonData.illusion.basePokemon.name : this.name; - const nickname: string = (!useIllusion && this.summonData.illusion) ? this.summonData.illusion.basePokemon.nickname : this.nickname; + getNameToRender(useIllusion = true) { + const name: string = + !useIllusion && this.summonData.illusion ? this.summonData.illusion.basePokemon.name : this.name; + const nickname: string = + !useIllusion && this.summonData.illusion ? this.summonData.illusion.basePokemon.nickname : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -577,12 +554,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - getPokeball(useIllusion = false){ - if(useIllusion){ - return this.summonData.illusion?.pokeball ?? this.pokeball - } else { - return this.pokeball + getPokeball(useIllusion = false) { + if (useIllusion) { + return this.summonData.illusion?.pokeball ?? this.pokeball; } + return this.pokeball; } init(): void { @@ -644,10 +620,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns `true` if the pokemon is fainted */ public isFainted(checkStatus = false): boolean { - return ( - this.hp <= 0 && - (!checkStatus || this.status?.effect === StatusEffect.FAINT) - ); + return this.hp <= 0 && (!checkStatus || this.status?.effect === StatusEffect.FAINT); } /** @@ -665,11 +638,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ public isAllowedInChallenge(): boolean { const challengeAllowed = new BooleanHolder(true); - applyChallenges( - ChallengeType.POKEMON_IN_BATTLE, - this, - challengeAllowed, - ); + applyChallenges(ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed); return challengeAllowed.value; } @@ -690,12 +659,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let ret = 0n; ret |= this.gender !== Gender.FEMALE ? DexAttr.MALE : DexAttr.FEMALE; ret |= !this.shiny ? DexAttr.NON_SHINY : DexAttr.SHINY; - ret |= - this.variant >= 2 - ? DexAttr.VARIANT_3 - : this.variant === 1 - ? DexAttr.VARIANT_2 - : DexAttr.DEFAULT_VARIANT; + ret |= this.variant >= 2 ? DexAttr.VARIANT_3 : this.variant === 1 ? DexAttr.VARIANT_2 : DexAttr.DEFAULT_VARIANT; ret |= globalScene.gameData.getFormAttr(this.formIndex); return ret; } @@ -721,17 +685,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Generate `abilityIndex` based on species and hidden ability if not pre-defined. */ private generateAbilityIndex(): number { - // Roll for hidden ability chance, applying any ability charms for enemy mons - const hiddenAbilityChance = new NumberHolder( - BASE_HIDDEN_ABILITY_CHANCE, - ); + const hiddenAbilityChance = new NumberHolder(BASE_HIDDEN_ABILITY_CHANCE); if (!this.hasTrainer()) { - globalScene.applyModifiers( - HiddenAbilityRateBoosterModifier, - true, - hiddenAbilityChance, - ); + globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } // If the roll succeeded and we have one, use HA; otherwise pick a random ability @@ -744,8 +701,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.species.ability2 !== this.species.ability1 ? randSeedInt(2) : 0; } - - /** * Generate an illusion of the last pokemon in the party, as other wild pokemon in the area. */ @@ -763,7 +718,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { shiny: this.shiny, variant: this.variant, fusionShiny: this.fusionShiny, - fusionVariant: this.fusionVariant + fusionVariant: this.fusionVariant, }, species: speciesId, formIndex: pokemon.formIndex, @@ -771,7 +726,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { pokeball: pokemon.pokeball, fusionFormIndex: pokemon.fusionFormIndex, fusionSpecies: pokemon.fusionSpecies || undefined, - fusionGender: pokemon.fusionGender + fusionGender: pokemon.fusionGender, }; this.name = pokemon.name; @@ -786,7 +741,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.loadAssets(false, true).then(() => this.playAnim()); this.updateInfo(); } else { - const randomIllusion: PokemonSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, this.level); + const randomIllusion: PokemonSpecies = globalScene.arena.randomSpecies( + globalScene.currentBattle.waveIndex, + this.level, + ); this.summonData.illusion = { basePokemon: { @@ -795,12 +753,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { shiny: this.shiny, variant: this.variant, fusionShiny: this.fusionShiny, - fusionVariant: this.fusionVariant + fusionVariant: this.fusionVariant, }, species: randomIllusion.speciesId, formIndex: randomIllusion.formIndex, gender: this.gender, - pokeball: this.pokeball + pokeball: this.pokeball, }; this.name = randomIllusion.name; @@ -812,15 +770,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { breakIllusion(): boolean { if (!this.summonData.illusion) { return false; - } else { - this.name = this.summonData.illusion.basePokemon.name; - this.nickname = this.summonData.illusion.basePokemon.nickname; - this.shiny = this.summonData.illusion.basePokemon.shiny; - this.variant = this.summonData.illusion.basePokemon.variant; - this.fusionVariant = this.summonData.illusion.basePokemon.fusionVariant; - this.fusionShiny = this.summonData.illusion.basePokemon.fusionShiny; - this.summonData.illusion = null; } + this.name = this.summonData.illusion.basePokemon.name; + this.nickname = this.summonData.illusion.basePokemon.nickname; + this.shiny = this.summonData.illusion.basePokemon.shiny; + this.variant = this.summonData.illusion.basePokemon.variant; + this.fusionVariant = this.summonData.illusion.basePokemon.fusionVariant; + this.fusionShiny = this.summonData.illusion.basePokemon.fusionShiny; + this.summonData.illusion = null; if (this.isOnField()) { globalScene.playSound("PRSFX- Transform"); } @@ -841,9 +798,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract getBattlerIndex(): BattlerIndex; /** -   * @param useIllusion - Whether we want the illusion or not. -   */ - async loadAssets(ignoreOverride = true, useIllusion: boolean = false): Promise { + * @param useIllusion - Whether we want the illusion or not. + */ + async loadAssets(ignoreOverride = true, useIllusion = false): Promise { /** Promises that are loading assets and can be run concurrently. */ const loadPromises: Promise[] = []; // Assets for moves @@ -856,7 +813,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), - this.getVariant(useIllusion) + this.getVariant(useIllusion), ), ); @@ -867,15 +824,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } if (this.getFusionSpeciesForm()) { - const fusionFormIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex : this.fusionFormIndex; - const fusionShiny = !useIllusion && this.summonData.illusion?.basePokemon ? this.summonData.illusion.basePokemon.fusionShiny : this.fusionShiny; - const fusionVariant = !useIllusion && this.summonData.illusion?.basePokemon ? this.summonData.illusion.basePokemon.fusionVariant : this.fusionVariant; - loadPromises.push(this.getFusionSpeciesForm(false, useIllusion).loadAssets( - this.getFusionGender(false, useIllusion) === Gender.FEMALE, - fusionFormIndex, - fusionShiny, - fusionVariant - )); + const fusionFormIndex = + useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = + !useIllusion && this.summonData.illusion?.basePokemon + ? this.summonData.illusion.basePokemon.fusionShiny + : this.fusionShiny; + const fusionVariant = + !useIllusion && this.summonData.illusion?.basePokemon + ? this.summonData.illusion.basePokemon.fusionVariant + : this.fusionVariant; + loadPromises.push( + this.getFusionSpeciesForm(false, useIllusion).loadAssets( + this.getFusionGender(false, useIllusion) === Gender.FEMALE, + fusionFormIndex, + fusionShiny, + fusionVariant, + ), + ); globalScene.loadPokemonAtlas( this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride), @@ -883,7 +849,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.isShiny(true)) { - loadPromises.push(populateVariantColors(this, false, ignoreOverride)) + loadPromises.push(populateVariantColors(this, false, ignoreOverride)); if (this.isPlayer()) { loadPromises.push(populateVariantColors(this, true, ignoreOverride)); } @@ -893,7 +859,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // This must be initiated before we queue loading, otherwise the load could have finished before // we reach the line of code that adds the listener, causing a deadlock. - const waitOnLoadPromise = new Promise(resolve => globalScene.load.once(Phaser.Loader.Events.COMPLETE, resolve)); + const waitOnLoadPromise = new Promise(resolve => + globalScene.load.once(Phaser.Loader.Events.COMPLETE, resolve), + ); if (!globalScene.load.isLoading()) { globalScene.load.start(); @@ -965,11 +933,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param useExpSprite should the experimental sprite be used * @param battleSpritePath the filename of the sprite */ - async populateVariantColorCache( - cacheKey: string, - useExpSprite: boolean, - battleSpritePath: string, - ) { + async populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) { const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`; return globalScene .cachedFetch(spritePath) @@ -988,13 +952,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return res.json(); }) .catch(error => { - return this.fallbackVariantColor( - cacheKey, - spritePath, - useExpSprite, - battleSpritePath, - error, - ); + return this.fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error); }) .then(c => { if (!isNullOrUndefined(c)) { @@ -1004,10 +962,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFormKey(): string { - if ( - !this.species.forms.length || - this.species.forms.length <= this.formIndex - ) { + if (!this.species.forms.length || this.species.forms.length <= this.formIndex) { return ""; } return this.species.forms[this.formIndex].formKey; @@ -1017,10 +972,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!this.fusionSpecies) { return null; } - if ( - !this.fusionSpecies.forms.length || - this.fusionSpecies.forms.length <= this.fusionFormIndex - ) { + if (!this.fusionSpecies.forms.length || this.fusionSpecies.forms.length <= this.fusionFormIndex) { return ""; } return this.fusionSpecies.forms[this.fusionFormIndex].formKey; @@ -1032,10 +984,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { - const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace( - /\_{2}/g, - "/", - ); + const spriteId = this.getBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } @@ -1045,7 +994,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, - this.variant + this.variant, ); } @@ -1061,7 +1010,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { formIndex, this.shiny, this.variant, - back + back, ); } @@ -1070,7 +1019,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.summonData.illusion?.basePokemon.shiny ?? this.shiny, - this.summonData.illusion?.basePokemon.variant ?? this.variant + this.summonData.illusion?.basePokemon.variant ?? this.variant, ); } @@ -1084,7 +1033,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, - this.fusionVariant + this.fusionVariant, ); } @@ -1100,7 +1049,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { fusionFormIndex, this.fusionShiny, this.fusionVariant, - back + back, ); } @@ -1108,55 +1057,67 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return `pkmn__${this.getFusionBattleSpriteId(back, ignoreOverride)}`; } - getFusionBattleSpriteAtlasPath( - back?: boolean, - ignoreOverride?: boolean, - ): string { - return this.getFusionBattleSpriteId(back, ignoreOverride).replace( - /\_{2}/g, - "/", - ); + getFusionBattleSpriteAtlasPath(back?: boolean, ignoreOverride?: boolean): string { + return this.getFusionBattleSpriteId(back, ignoreOverride).replace(/\_{2}/g, "/"); } - getIconAtlasKey(ignoreOverride?: boolean, useIllusion: boolean = true): string { - const formIndex = useIllusion && this.summonData.illusion?.formIndex ? this.summonData.illusion?.formIndex : this.formIndex; - const variant = !useIllusion && !!this.summonData.illusion ? this.summonData.illusion?.basePokemon.variant : this.variant; + getIconAtlasKey(ignoreOverride = false, useIllusion = true): string { + // TODO: confirm the correct behavior here (is it intentional that the check fails if `illusion.formIndex` is `0`?) + const formIndex = + useIllusion && this.summonData.illusion?.formIndex ? this.summonData.illusion.formIndex : this.formIndex; + const variant = + !useIllusion && this.summonData.illusion ? this.summonData.illusion.basePokemon.variant : this.variant; return this.getSpeciesForm(ignoreOverride, useIllusion).getIconAtlasKey( formIndex, this.isBaseShiny(useIllusion), - variant + variant, ); } - getFusionIconAtlasKey(ignoreOverride?: boolean, useIllusion: boolean = true): string { - const fusionFormIndex = useIllusion && this.summonData.illusion?.fusionFormIndex ? this.summonData.illusion?.fusionFormIndex : this.fusionFormIndex; - const fusionVariant = !useIllusion && !!this.summonData.illusion ? this.summonData.illusion?.basePokemon.fusionVariant : this.fusionVariant; + getFusionIconAtlasKey(ignoreOverride = false, useIllusion = true): string { + // TODO: confirm the correct behavior here (is it intentional that the check fails if `illusion.fusionFormIndex` is `0`?) + const fusionFormIndex = + useIllusion && this.summonData.illusion?.fusionFormIndex + ? this.summonData.illusion.fusionFormIndex + : this.fusionFormIndex; + const fusionVariant = + !useIllusion && this.summonData.illusion + ? this.summonData.illusion.basePokemon.fusionVariant + : this.fusionVariant; return this.getFusionSpeciesForm(ignoreOverride, useIllusion).getIconAtlasKey( fusionFormIndex, this.isFusionShiny(), - fusionVariant + fusionVariant, ); } - getIconId(ignoreOverride?: boolean, useIllusion: boolean = true): string { - const formIndex = useIllusion && this.summonData.illusion?.formIndex ? this.summonData.illusion?.formIndex : this.formIndex; - const variant = !useIllusion && !!this.summonData.illusion ? this.summonData.illusion?.basePokemon.variant : this.variant; + getIconId(ignoreOverride?: boolean, useIllusion = true): string { + const formIndex = + useIllusion && this.summonData.illusion?.formIndex ? this.summonData.illusion?.formIndex : this.formIndex; + const variant = + !useIllusion && !!this.summonData.illusion ? this.summonData.illusion?.basePokemon.variant : this.variant; return this.getSpeciesForm(ignoreOverride, useIllusion).getIconId( this.getGender(ignoreOverride, useIllusion) === Gender.FEMALE, formIndex, this.isBaseShiny(), - variant + variant, ); } - getFusionIconId(ignoreOverride?: boolean, useIllusion: boolean = true): string { - const fusionFormIndex = useIllusion && this.summonData.illusion?.fusionFormIndex ? this.summonData.illusion?.fusionFormIndex : this.fusionFormIndex; - const fusionVariant = !useIllusion && !!this.summonData.illusion ? this.summonData.illusion?.basePokemon.fusionVariant : this.fusionVariant; + getFusionIconId(ignoreOverride?: boolean, useIllusion = true): string { + const fusionFormIndex = + useIllusion && this.summonData.illusion?.fusionFormIndex + ? this.summonData.illusion?.fusionFormIndex + : this.fusionFormIndex; + const fusionVariant = + !useIllusion && !!this.summonData.illusion + ? this.summonData.illusion?.basePokemon.fusionVariant + : this.fusionVariant; return this.getFusionSpeciesForm(ignoreOverride, useIllusion).getIconId( this.getFusionGender(ignoreOverride, useIllusion) === Gender.FEMALE, fusionFormIndex, this.isFusionShiny(), - fusionVariant + fusionVariant, ); } @@ -1166,12 +1127,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * This overrides `useIllusion` if `true`. * @param useIllusion - `true` to use the speciesForm of the illusion; default `false`. */ - getSpeciesForm(ignoreOverride: boolean = false, useIllusion: boolean = false): PokemonSpeciesForm { + getSpeciesForm(ignoreOverride = false, useIllusion = false): PokemonSpeciesForm { if (!ignoreOverride && this.summonData.speciesForm) { return this.summonData.speciesForm; } - const species: PokemonSpecies = useIllusion && this.summonData.illusion ? getPokemonSpecies(this.summonData.illusion.species) : this.species; + const species: PokemonSpecies = + useIllusion && this.summonData.illusion ? getPokemonSpecies(this.summonData.illusion.species) : this.species; const formIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.formIndex : this.formIndex; if (species.forms && species.forms.length > 0) { @@ -1184,17 +1146,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ - getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionSpecies! : this.fusionSpecies!; - const fusionFormIndex = useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex; + getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion = false): PokemonSpeciesForm { + const fusionSpecies: PokemonSpecies = + useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex = + useIllusion && this.summonData.illusion ? this.summonData.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData.fusionSpeciesForm) { return this.summonData.fusionSpeciesForm; } - if ( - !fusionSpecies?.forms?.length || - fusionFormIndex >= fusionSpecies?.forms.length - ) { + if (!fusionSpecies?.forms?.length || fusionFormIndex >= fusionSpecies?.forms.length) { return fusionSpecies; } return fusionSpecies?.forms[fusionFormIndex]; @@ -1205,9 +1166,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getTintSprite(): Phaser.GameObjects.Sprite | null { - return !this.maskEnabled - ? (this.getAt(1) as Phaser.GameObjects.Sprite) - : this.maskSprite; + return !this.maskEnabled ? (this.getAt(1) as Phaser.GameObjects.Sprite) : this.maskSprite; } getSpriteScale(): number { @@ -1288,20 +1247,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param animConfig {@linkcode String} to pass to {@linkcode Phaser.GameObjects.Sprite.play} * @returns true if the sprite was able to be animated */ - tryPlaySprite( - sprite: Phaser.GameObjects.Sprite, - tintSprite: Phaser.GameObjects.Sprite, - key: string, - ): boolean { + tryPlaySprite(sprite: Phaser.GameObjects.Sprite, tintSprite: Phaser.GameObjects.Sprite, key: string): boolean { // Catch errors when trying to play an animation that doesn't exist try { sprite.play(key); tintSprite.play(key); } catch (error: unknown) { - console.error( - `Couldn't play animation for '${key}'!\nIs the image for this Pokemon missing?\n`, - error, - ); + console.error(`Couldn't play animation for '${key}'!\nIs the image for this Pokemon missing?\n`, error); return false; } @@ -1310,11 +1262,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } playAnim(): void { - this.tryPlaySprite( - this.getSprite(), - this.getTintSprite()!, - this.getBattleSpriteKey(), - ); // TODO: is the bag correct? + this.tryPlaySprite(this.getSprite(), this.getTintSprite()!, this.getBattleSpriteKey()); // TODO: is the bang correct? } getFieldPositionOffset(): [number, number] { @@ -1352,30 +1300,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" const currentPhase = globalScene.getCurrentPhase(); - if ( - currentPhase instanceof MoveEffectPhase && - currentPhase.getPokemon() === this - ) { + if (currentPhase instanceof MoveEffectPhase && currentPhase.getPokemon() === this) { return false; } return true; - } else { - return false; } + return false; } /** If this Pokemon has a Substitute on the field, removes its sprite from the field. */ destroySubstitute(): void { const substitute = this.getTag(SubstituteTag); - if (substitute && substitute.sprite) { + if (substitute?.sprite) { substitute.sprite.destroy(); } } - setFieldPosition( - fieldPosition: FieldPosition, - duration?: number, - ): Promise { + setFieldPosition(fieldPosition: FieldPosition, duration?: number): Promise { return new Promise(resolve => { if (fieldPosition === this.fieldPosition) { resolve(); @@ -1440,10 +1381,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns the numeric value of the desired {@linkcode Stat} */ getStat(stat: PermanentStat, bypassSummonData = true): number { - if ( - !bypassSummonData && - this.summonData.stats[stat] !== 0 - ) { + if (!bypassSummonData && this.summonData.stats[stat] !== 0) { return this.summonData.stats[stat]; } return this.stats[stat]; @@ -1517,9 +1455,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { if (critBoostTag instanceof DragonCheerTag) { - critStage.value += critBoostTag.typesOnAdd.includes(PokemonType.DRAGON) - ? 2 - : 1; + critStage.value += critBoostTag.typesOnAdd.includes(PokemonType.DRAGON) ? 2 : 1; } else { critStage.value += 2; } @@ -1570,57 +1506,37 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ): number { const statValue = new NumberHolder(this.getStat(stat, false)); if (!ignoreHeldItems) { - globalScene.applyModifiers( - StatBoosterModifier, - this.isPlayer(), - this, - stat, - statValue, - ); + globalScene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue); } // The Ruin abilities here are never ignored, but they reveal themselves on summon anyway const fieldApplied = new BooleanHolder(false); for (const pokemon of globalScene.getField(true)) { - applyFieldStatMultiplierAbAttrs( - FieldMultiplyStatAbAttr, - pokemon, - stat, - statValue, - this, - fieldApplied, - simulated, - ); + applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied, simulated); if (fieldApplied.value) { break; } } if (!ignoreAbility) { - applyStatMultiplierAbAttrs( - StatMultiplierAbAttr, - this, - stat, - statValue, - simulated, - ); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue, simulated); } const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { - applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, stat, statValue, simulated, this, move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility); + applyAllyStatMultiplierAbAttrs( + AllyStatMultiplierAbAttr, + ally, + stat, + statValue, + simulated, + this, + move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility, + ); } let ret = statValue.value * - this.getStatStageMultiplier( - stat, - opponent, - move, - ignoreOppAbility, - isCritical, - simulated, - ignoreHeldItems, - ); + this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated, ignoreHeldItems); switch (stat) { case Stat.ATK: @@ -1629,31 +1545,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } break; case Stat.DEF: - if ( - this.isOfType(PokemonType.ICE) && - globalScene.arena.weather?.weatherType === WeatherType.SNOW - ) { + if (this.isOfType(PokemonType.ICE) && globalScene.arena.weather?.weatherType === WeatherType.SNOW) { ret *= 1.5; } break; case Stat.SPATK: break; case Stat.SPDEF: - if ( - this.isOfType(PokemonType.ROCK) && - globalScene.arena.weather?.weatherType === WeatherType.SANDSTORM - ) { + if (this.isOfType(PokemonType.ROCK) && globalScene.arena.weather?.weatherType === WeatherType.SANDSTORM) { ret *= 1.5; } break; - case Stat.SPD: + case Stat.SPD: { const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.TAILWIND, side)) { ret *= 2; } - if ( - globalScene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, side) - ) { + if (globalScene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, side)) { ret >>= 2; } @@ -1663,19 +1571,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.status && this.status.effect === StatusEffect.PARALYSIS) { ret >>= 1; } - if ( - this.getTag(BattlerTagType.UNBURDEN) && - this.hasAbility(Abilities.UNBURDEN) - ) { + if (this.getTag(BattlerTagType.UNBURDEN) && this.hasAbility(Abilities.UNBURDEN)) { ret *= 2; } break; + } } const highestStatBoost = this.findTag( - t => - t instanceof HighestStatBoostTag && - (t as HighestStatBoostTag).stat === stat, + t => t instanceof HighestStatBoostTag && (t as HighestStatBoostTag).stat === stat, ) as HighestStatBoostTag; if (highestStatBoost) { ret *= highestStatBoost.multiplier; @@ -1693,18 +1597,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const baseStats = this.calculateBaseStats(); // Using base stats, calculate and store stats one by one for (const s of PERMANENT_STATS) { - const statHolder = new NumberHolder( - Math.floor((2 * baseStats[s] + this.ivs[s]) * this.level * 0.01), - ); + const statHolder = new NumberHolder(Math.floor((2 * baseStats[s] + this.ivs[s]) * this.level * 0.01)); if (s === Stat.HP) { statHolder.value = statHolder.value + this.level + 10; - globalScene.applyModifier( - PokemonIncrementingStatModifier, - this.isPlayer(), - this, - s, - statHolder, - ); + globalScene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); if (this.hasAbility(Abilities.WONDER_GUARD, false, true)) { statHolder.value = 1; } @@ -1718,37 +1614,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } else { statHolder.value += 5; - const natureStatMultiplier = new NumberHolder( - getNatureStatMultiplier(this.getNature(), s), - ); - globalScene.applyModifier( - PokemonNatureWeightModifier, - this.isPlayer(), - this, - natureStatMultiplier, - ); + const natureStatMultiplier = new NumberHolder(getNatureStatMultiplier(this.getNature(), s)); + globalScene.applyModifier(PokemonNatureWeightModifier, this.isPlayer(), this, natureStatMultiplier); if (natureStatMultiplier.value !== 1) { statHolder.value = Math.max( - Math[natureStatMultiplier.value > 1 ? "ceil" : "floor"]( - statHolder.value * natureStatMultiplier.value, - ), + Math[natureStatMultiplier.value > 1 ? "ceil" : "floor"](statHolder.value * natureStatMultiplier.value), 1, ); } - globalScene.applyModifier( - PokemonIncrementingStatModifier, - this.isPlayer(), - this, - s, - statHolder, - ); + globalScene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); } - statHolder.value = Phaser.Math.Clamp( - statHolder.value, - 1, - Number.MAX_SAFE_INTEGER, - ); + statHolder.value = Phaser.Math.Clamp(statHolder.value, 1, Number.MAX_SAFE_INTEGER); this.setStat(s, statHolder.value); } @@ -1756,32 +1633,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { calculateBaseStats(): number[] { const baseStats = this.getSpeciesForm(true).baseStats.slice(0); - applyChallenges( - ChallengeType.FLIP_STAT, - this, - baseStats, - ); + applyChallenges(ChallengeType.FLIP_STAT, this, baseStats); // Shuckle Juice - globalScene.applyModifiers( - PokemonBaseStatTotalModifier, - this.isPlayer(), - this, - baseStats, - ); + globalScene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats); // Old Gateau - globalScene.applyModifiers( - PokemonBaseStatFlatModifier, - this.isPlayer(), - this, - baseStats, - ); + globalScene.applyModifiers(PokemonBaseStatFlatModifier, this.isPlayer(), this, baseStats); if (this.isFusion()) { const fusionBaseStats = this.getFusionSpeciesForm(true).baseStats; - applyChallenges( - ChallengeType.FLIP_STAT, - this, - fusionBaseStats, - ); + applyChallenges(ChallengeType.FLIP_STAT, this, fusionBaseStats); for (const s of PERMANENT_STATS) { baseStats[s] = Math.ceil((baseStats[s] + fusionBaseStats[s]) / 2); @@ -1792,20 +1651,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } // Vitamins - globalScene.applyModifiers( - BaseStatModifier, - this.isPlayer(), - this, - baseStats, - ); + globalScene.applyModifiers(BaseStatModifier, this.isPlayer(), this, baseStats); return baseStats; } getNature(): Nature { - return this.customPokemonData.nature !== -1 - ? this.customPokemonData.nature - : this.nature; + return this.customPokemonData.nature !== -1 ? this.customPokemonData.nature : this.nature; } setNature(nature: Nature): void { @@ -1840,9 +1692,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getHpRatio(precise = false): number { - return precise - ? this.hp / this.getMaxHp() - : Math.round((this.hp / this.getMaxHp()) * 100) / 100; + return precise ? this.hp / this.getMaxHp() : Math.round((this.hp / this.getMaxHp()) * 100) / 100; } generateGender(): void { @@ -1859,54 +1709,56 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability). + * @param useIllusion - Whether we want the fake or real gender (illusion ability). */ - getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + getGender(ignoreOverride?: boolean, useIllusion = false): Gender { if (useIllusion && this.summonData.illusion) { return this.summonData.illusion.gender; - } else if (!ignoreOverride && !isNullOrUndefined(this.summonData.gender)) { + } + if (!ignoreOverride && !isNullOrUndefined(this.summonData.gender)) { return this.summonData.gender; } return this.gender; } /** - * @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability). + * @param useIllusion - Whether we want the fake or real gender (illusion ability). */ - getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + getFusionGender(ignoreOverride?: boolean, useIllusion = false): Gender { if (useIllusion && this.summonData.illusion?.fusionGender) { return this.summonData.illusion.fusionGender; - } else if (!ignoreOverride && !isNullOrUndefined(this.summonData.fusionGender)) { + } + if (!ignoreOverride && !isNullOrUndefined(this.summonData.fusionGender)) { return this.summonData.fusionGender; } return this.fusionGender; } /** - * @param {boolean} useIllusion - Whether we want the fake or real shininess (illusion ability). + * @param useIllusion - Whether we want the fake or real shininess (illusion ability). */ - isShiny(useIllusion: boolean = false): boolean { + isShiny(useIllusion = false): boolean { if (!useIllusion && this.summonData.illusion) { - return this.summonData.illusion.basePokemon?.shiny || (this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) || false; - } else { - return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); + return !!( + this.summonData.illusion.basePokemon?.shiny || + (this.summonData.illusion.fusionSpecies && this.summonData.illusion.basePokemon?.fusionShiny) + ); } + return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } - isBaseShiny(useIllusion: boolean = false){ + isBaseShiny(useIllusion = false) { if (!useIllusion && this.summonData.illusion) { return !!this.summonData.illusion.basePokemon?.shiny; - } else { - return this.shiny; } + return this.shiny; } - isFusionShiny(useIllusion: boolean = false){ + isFusionShiny(useIllusion = false) { if (!useIllusion && this.summonData.illusion) { return !!this.summonData.illusion.basePokemon?.fusionShiny; - } else { - return this.isFusion(useIllusion) && this.fusionShiny; } + return this.isFusion(useIllusion) && this.fusionShiny; } /** @@ -1914,34 +1766,33 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param useIllusion - Whether we want the fake or real shininess (illusion ability). * @returns `true` if the {@linkcode Pokemon} is shiny and the fusion is shiny as well, `false` otherwise */ - isDoubleShiny(useIllusion: boolean = false): boolean { + isDoubleShiny(useIllusion = false): boolean { if (!useIllusion && this.summonData.illusion?.basePokemon) { - return this.isFusion(false) && this.summonData.illusion.basePokemon.shiny && this.summonData.illusion.basePokemon.fusionShiny; - } else { - return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; + return ( + this.isFusion(false) && + this.summonData.illusion.basePokemon.shiny && + this.summonData.illusion.basePokemon.fusionShiny + ); } + return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } /** - * @param {boolean} useIllusion - Whether we want the fake or real variant (illusion ability). + * @param useIllusion - Whether we want the fake or real variant (illusion ability). */ - getVariant(useIllusion: boolean = false): Variant { + getVariant(useIllusion = false): Variant { if (!useIllusion && this.summonData.illusion) { return !this.isFusion(false) - ? this.summonData.illusion.basePokemon!.variant - : Math.max(this.variant, this.fusionVariant) as Variant; - } else { - return !this.isFusion(true) - ? this.variant - : Math.max(this.variant, this.fusionVariant) as Variant; + ? this.summonData.illusion.basePokemon!.variant + : (Math.max(this.variant, this.fusionVariant) as Variant); } + return !this.isFusion(true) ? this.variant : (Math.max(this.variant, this.fusionVariant) as Variant); } getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { return this.summonData.illusion?.basePokemon?.variant ?? this.variant; } - return this.getVariant(); } @@ -1949,7 +1800,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.luck + (this.isFusion() ? this.fusionLuck : 0); } - isFusion(useIllusion: boolean = false): boolean { + isFusion(useIllusion = false): boolean { if (useIllusion && this.summonData.illusion) { return !!this.summonData.illusion.fusionSpecies; } @@ -1957,12 +1808,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * @param {boolean} useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + * @param useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). */ - getName(useIllusion: boolean = false): string { - return (!useIllusion && this.summonData.illusion?.basePokemon) - ? this.summonData.illusion.basePokemon.name - : this.name; + getName(useIllusion = false): string { + return !useIllusion && this.summonData.illusion?.basePokemon + ? this.summonData.illusion.basePokemon.name + : this.name; } /** @@ -1982,22 +1833,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ hasSpecies(species: Species, formKey?: string): boolean { if (isNullOrUndefined(formKey)) { - return ( - this.species.speciesId === species || - this.fusionSpecies?.speciesId === species - ); + return this.species.speciesId === species || this.fusionSpecies?.speciesId === species; } - return (this.species.speciesId === species && this.getFormKey() === formKey) || (this.fusionSpecies?.speciesId === species && this.getFusionFormKey() === formKey); + return ( + (this.species.speciesId === species && this.getFormKey() === formKey) || + (this.fusionSpecies?.speciesId === species && this.getFusionFormKey() === formKey) + ); } abstract isBoss(): boolean; getMoveset(ignoreOverride?: boolean): PokemonMove[] { - const ret = - !ignoreOverride && this.summonData.moveset - ? this.summonData.moveset - : this.moveset; + const ret = !ignoreOverride && this.summonData.moveset ? this.summonData.moveset : this.moveset; // Overrides moveset based on arrays specified in overrides.ts let overrideArray: Moves | Array = this.isPlayer() @@ -2012,10 +1860,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } overrideArray.forEach((move: Moves, index: number) => { const ppUsed = this.moveset[index]?.ppUsed ?? 0; - this.moveset[index] = new PokemonMove( - move, - Math.min(ppUsed, allMoves[move].pp), - ); + this.moveset[index] = new PokemonMove(move, Math.min(ppUsed, allMoves[move].pp)); }); } @@ -2032,9 +1877,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getUnlockedEggMoves(): Moves[] { const moves: Moves[] = []; const species = - this.metSpecies in speciesEggMoves - ? this.metSpecies - : this.getSpeciesForm(true).getRootSpeciesId(true); + this.metSpecies in speciesEggMoves ? this.metSpecies : this.getSpeciesForm(true).getRootSpeciesId(true); if (species in speciesEggMoves) { for (let i = 0; i < 4; i++) { if (globalScene.gameData.starterData[species].eggMoves & (1 << i)) { @@ -2056,17 +1899,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ public getLearnableLevelMoves(): Moves[] { let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]); - if ( - this.metBiome === -1 && - !globalScene.gameMode.isFreshStartChallenge() && - !globalScene.gameMode.isDaily - ) { + if (this.metBiome === -1 && !globalScene.gameMode.isFreshStartChallenge() && !globalScene.gameMode.isDaily) { levelMoves = this.getUnlockedEggMoves().concat(levelMoves); } if (Array.isArray(this.usedTMs) && this.usedTMs.length > 0) { - levelMoves = this.usedTMs - .filter(m => !levelMoves.includes(m)) - .concat(levelMoves); + levelMoves = this.usedTMs.filter(m => !levelMoves.includes(m)).concat(levelMoves); } levelMoves = levelMoves.filter(lm => !this.moveset.some(m => m.moveId === lm)); return levelMoves; @@ -2082,9 +1919,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ public getTypes( includeTeraType = false, - forDefend: boolean = false, - ignoreOverride: boolean = false, - useIllusion: boolean = false + forDefend = false, + ignoreOverride = false, + useIllusion = false, ): PokemonType[] { const types: PokemonType[] = []; @@ -2099,7 +1936,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } if (!types.length || !includeTeraType) { - if ( !ignoreOverride && this.summonData.types && @@ -2144,10 +1980,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { secondType = fusionType1; } - if ( - secondType === PokemonType.UNKNOWN && - isNullOrUndefined(fusionType2) - ) { + if (secondType === PokemonType.UNKNOWN && isNullOrUndefined(fusionType2)) { // If second pokemon was monotype and shared its primary type secondType = customTypes && @@ -2186,11 +2019,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // check type added to Pokemon from moves like Forest's Curse or Trick Or Treat - if ( - !ignoreOverride && - this.summonData.addedType && - !types.includes(this.summonData.addedType) - ) { + if (!ignoreOverride && this.summonData.addedType && !types.includes(this.summonData.addedType)) { types.push(this.summonData.addedType); } @@ -2210,15 +2039,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false` * @returns `true` if the Pokemon's type matches */ - public isOfType( - type: PokemonType, - includeTeraType = true, - forDefend = false, - ignoreOverride = false, - ): boolean { - return this.getTypes(includeTeraType, forDefend, ignoreOverride).some( - t => t === type, - ); + public isOfType(type: PokemonType, includeTeraType = true, forDefend = false, ignoreOverride = false): boolean { + return this.getTypes(includeTeraType, forDefend, ignoreOverride).some(t => t === type); } /** @@ -2240,27 +2062,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return allAbilities[Overrides.OPP_ABILITY_OVERRIDE]; } if (this.isFusion()) { - if ( - !isNullOrUndefined(this.fusionCustomPokemonData?.ability) && - this.fusionCustomPokemonData.ability !== -1 - ) { + if (!isNullOrUndefined(this.fusionCustomPokemonData?.ability) && this.fusionCustomPokemonData.ability !== -1) { return allAbilities[this.fusionCustomPokemonData.ability]; } - return allAbilities[ - this.getFusionSpeciesForm(ignoreOverride).getAbility( - this.fusionAbilityIndex, - ) - ]; + return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)]; } - if ( - !isNullOrUndefined(this.customPokemonData.ability) && - this.customPokemonData.ability !== -1 - ) { + if (!isNullOrUndefined(this.customPokemonData.ability) && this.customPokemonData.ability !== -1) { return allAbilities[this.customPokemonData.ability]; } - let abilityId = this.getSpeciesForm(ignoreOverride).getAbility( - this.abilityIndex, - ); + let abilityId = this.getSpeciesForm(ignoreOverride).getAbility(this.abilityIndex); if (abilityId === Abilities.NONE) { abilityId = this.species.ability1; } @@ -2281,10 +2091,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) { return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE]; } - if ( - !isNullOrUndefined(this.customPokemonData.passive) && - this.customPokemonData.passive !== -1 - ) { + if (!isNullOrUndefined(this.customPokemonData.passive) && this.customPokemonData.passive !== -1) { return allAbilities[this.customPokemonData.passive]; } @@ -2308,9 +2115,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const abilityAttrs: T[] = []; if (!canApply || this.canApplyAbility()) { - abilityAttrs.push( - ...this.getAbility(ignoreOverride).getAttrs(attrType), - ); + abilityAttrs.push(...this.getAbility(ignoreOverride).getAttrs(attrType)); } if (!canApply || this.canApplyAbility(true)) { @@ -2360,11 +2165,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } if ( - ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || - Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) && + ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) && this.isPlayer()) || - ((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || - Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) && + ((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE || Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) && !this.isPlayer()) ) { return true; @@ -2401,35 +2204,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } const arena = globalScene?.arena; - if ( - arena.ignoreAbilities && - arena.ignoringEffectSource !== this.getBattlerIndex() && - ability.isIgnorable - ) { + if (arena.ignoreAbilities && arena.ignoringEffectSource !== this.getBattlerIndex() && ability.isIgnorable) { return false; } - if ( - this.summonData.abilitySuppressed && - ability.isSuppressable - ) { + if (this.summonData.abilitySuppressed && ability.isSuppressable) { return false; } - const suppressAbilitiesTag = arena.getTag( - ArenaTagType.NEUTRALIZING_GAS, - ) as SuppressAbilitiesTag; + const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; const suppressOffField = ability.hasAttr(PreSummonAbAttr); - if ( - (this.isOnField() || suppressOffField) && - suppressAbilitiesTag && - !suppressAbilitiesTag.isBeingRemoved() - ) { - const thisAbilitySuppressing = ability.hasAttr( - PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, - ); - const hasSuppressingAbility = this.hasAbilityWithAttr( - PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, - false, - ); + if ((this.isOnField() || suppressOffField) && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) { + const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr); + const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false); // Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas // (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized) // If the ability itself is neutralizing gas, don't suppress it (handled through arena tag) @@ -2441,10 +2226,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } } - return ( - (this.hp > 0 || ability.isBypassFaint) && - !ability.conditions.find(condition => !condition(this)) - ); + return (this.hp > 0 || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); } /** @@ -2456,22 +2238,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreOverride Whether to ignore ability changing effects; default `false` * @returns `true` if the ability is present and active */ - public hasAbility( - ability: Abilities, - canApply = true, - ignoreOverride = false, - ): boolean { - if ( - this.getAbility(ignoreOverride).id === ability && - (!canApply || this.canApplyAbility()) - ) { + public hasAbility(ability: Abilities, canApply = true, ignoreOverride = false): boolean { + if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) { return true; } - if ( - this.getPassiveAbility().id === ability && - this.hasPassive() && - (!canApply || this.canApplyAbility(true)) - ) { + if (this.getPassiveAbility().id === ability && this.hasPassive() && (!canApply || this.canApplyAbility(true))) { return true; } return false; @@ -2487,22 +2258,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreOverride Whether to ignore ability changing effects; default `false` * @returns `true` if an ability with the given {@linkcode AbAttr} is present and active */ - public hasAbilityWithAttr( - attrType: Constructor, - canApply = true, - ignoreOverride = false, - ): boolean { - if ( - (!canApply || this.canApplyAbility()) && - this.getAbility(ignoreOverride).hasAttr(attrType) - ) { + public hasAbilityWithAttr(attrType: Constructor, canApply = true, ignoreOverride = false): boolean { + if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) { return true; } - if ( - this.hasPassive() && - (!canApply || this.canApplyAbility(true)) && - this.getPassiveAbility().hasAttr(attrType) - ) { + if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType)) { return true; } return false; @@ -2535,10 +2295,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return PokemonType.STELLAR; } if (this.hasSpecies(Species.OGERPON)) { - const ogerponForm = - this.species.speciesId === Species.OGERPON - ? this.formIndex - : this.fusionFormIndex; + const ogerponForm = this.species.speciesId === Species.OGERPON ? this.formIndex : this.fusionFormIndex; switch (ogerponForm) { case 0: case 4: @@ -2578,10 +2335,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param simulated - If `true`, applies abilities via simulated calls. * @returns `true` if the pokemon is trapped */ - public isTrapped( - trappedAbMessages: string[] = [], - simulated = true, - ): boolean { + public isTrapped(trappedAbMessages: string[] = [], simulated = true): boolean { const commandedTag = this.getTag(BattlerTagType.COMMANDED); if (commandedTag?.getSourcePokemon()?.isActive(true)) { return true; @@ -2596,22 +2350,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective * Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger */ - const opposingFieldUnfiltered = this.isPlayer() - ? globalScene.getEnemyField() - : globalScene.getPlayerField(); - const opposingField = opposingFieldUnfiltered.filter( - enemyPkm => enemyPkm.switchOutStatus === false, - ); + const opposingFieldUnfiltered = this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField(); + const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false); for (const opponent of opposingField) { - applyCheckTrappedAbAttrs( - CheckTrappedAbAttr, - opponent, - trappedByAbility, - this, - trappedAbMessages, - simulated, - ); + applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated); } const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -2633,26 +2376,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveTypeHolder = new NumberHolder(move.type); applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); - applyPreAttackAbAttrs( - MoveTypeChangeAbAttr, - this, - null, - move, - simulated, - moveTypeHolder - ); + applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder); // If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type, // then bypass the check for ion deluge and electrify - if (this.isTerastallized && (move.id === Moves.TERA_BLAST || move.id === Moves.TERA_STARSTORM && moveTypeHolder.value === PokemonType.STELLAR)) { + if ( + this.isTerastallized && + (move.id === Moves.TERA_BLAST || + (move.id === Moves.TERA_STARSTORM && moveTypeHolder.value === PokemonType.STELLAR)) + ) { return moveTypeHolder.value as PokemonType; } - globalScene.arena.applyTags( - ArenaTagType.ION_DELUGE, - simulated, - moveTypeHolder, - ); + globalScene.arena.applyTags(ArenaTagType.ION_DELUGE, simulated, moveTypeHolder); if (this.getTag(BattlerTagType.ELECTRIFIED)) { moveTypeHolder.value = PokemonType.ELECTRIC; } @@ -2677,7 +2413,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ignoreAbility = false, simulated = true, cancelled?: BooleanHolder, - useIllusion: boolean = false + useIllusion = false, ): TypeDamageMultiplier { if (!isNullOrUndefined(this.turnData?.moveEffectiveness)) { return this.turnData?.moveEffectiveness; @@ -2689,28 +2425,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveType = source.getMoveType(move); const typeMultiplier = new NumberHolder( - move.category !== MoveCategory.STATUS || - move.hasAttr(RespectAttackTypeImmunityAttr) - ? this.getAttackTypeEffectiveness( - moveType, - source, - false, - simulated, - move, - useIllusion - ) - : 1); - - applyMoveAttrs( - VariableMoveTypeMultiplierAttr, - source, - this, - move, - typeMultiplier, + move.category !== MoveCategory.STATUS || move.hasAttr(RespectAttackTypeImmunityAttr) + ? this.getAttackTypeEffectiveness(moveType, source, false, simulated, move, useIllusion) + : 1, ); - if ( - this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t)) - ) { + + applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); + if (this.getTypes(true, true).find(t => move.isTypeImmune(source, this, t))) { typeMultiplier.value = 0; } @@ -2720,52 +2441,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelledHolder = cancelled ?? new BooleanHolder(false); if (!ignoreAbility) { - applyPreDefendAbAttrs( - TypeImmunityAbAttr, - this, - source, - move, - cancelledHolder, - simulated, - typeMultiplier, - ); + applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); if (!cancelledHolder.value) { - applyPreDefendAbAttrs( - MoveImmunityAbAttr, - this, - source, - move, - cancelledHolder, - simulated, - typeMultiplier, - ); + applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); } if (!cancelledHolder.value) { - const defendingSidePlayField = this.isPlayer() - ? globalScene.getPlayerField() - : globalScene.getEnemyField(); + const defendingSidePlayField = this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); defendingSidePlayField.forEach(p => - applyPreDefendAbAttrs( - FieldPriorityMoveImmunityAbAttr, - p, - source, - move, - cancelledHolder, - ), + applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelledHolder), ); } } - const immuneTags = this.findTags( - tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType, - ); + const immuneTags = this.findTags(tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType); for (const tag of immuneTags) { - if ( - move && - !move.getAttrs(HitsTagAttr).some(attr => attr.tagType === tag.tagType) - ) { + if (move && !move.getAttrs(HitsTagAttr).some(attr => attr.tagType === tag.tagType)) { typeMultiplier.value = 0; break; } @@ -2773,27 +2465,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Apply Tera Shell's effect to attacks after all immunities are accounted for if (!ignoreAbility && move.category !== MoveCategory.STATUS) { - applyPreDefendAbAttrs( - FullHpResistTypeAbAttr, - this, - source, - move, - cancelledHolder, - simulated, - typeMultiplier, - ); + applyPreDefendAbAttrs(FullHpResistTypeAbAttr, this, source, move, cancelledHolder, simulated, typeMultiplier); } - if ( - move.category === MoveCategory.STATUS && - move.hitsSubstitute(source, this) - ) { + if (move.category === MoveCategory.STATUS && move.hitsSubstitute(source, this)) { typeMultiplier.value = 0; } - return ( - !cancelledHolder.value ? typeMultiplier.value : 0 - ) as TypeDamageMultiplier; + return (!cancelledHolder.value ? typeMultiplier.value : 0) as TypeDamageMultiplier; } /** @@ -2809,10 +2488,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getAttackTypeEffectiveness( moveType: PokemonType, source?: Pokemon, - ignoreStrongWinds: boolean = false, - simulated: boolean = true, + ignoreStrongWinds = false, + simulated = true, move?: Move, - useIllusion: boolean = false + useIllusion = false, ): TypeDamageMultiplier { if (moveType === PokemonType.STELLAR) { return this.isTerastallized ? 2 : 1; @@ -2822,10 +2501,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Handle flying v ground type immunity without removing flying type so effective types are still effective // Related to https://github.com/pagefaultgames/pokerogue/issues/524 - if ( - moveType === PokemonType.GROUND && - (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY)) - ) { + if (moveType === PokemonType.GROUND && (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY))) { const flyingIndex = types.indexOf(PokemonType.FLYING); if (flyingIndex > -1) { types.splice(flyingIndex, 1); @@ -2834,37 +2510,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let multiplier = types .map(defType => { - const multiplier = new NumberHolder( - getTypeDamageMultiplier(moveType, defType), - ); - applyChallenges( - ChallengeType.TYPE_EFFECTIVENESS, - multiplier, - ); + const multiplier = new NumberHolder(getTypeDamageMultiplier(moveType, defType)); + applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier); if (move) { - applyMoveAttrs( - VariableMoveTypeChartAttr, - null, - this, - move, - multiplier, - defType, - ); + applyMoveAttrs(VariableMoveTypeChartAttr, null, this, move, multiplier, defType); } if (source) { const ignoreImmunity = new BooleanHolder(false); - if ( - source.isActive(true) && - source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr) - ) { - applyAbAttrs( - IgnoreTypeImmunityAbAttr, - source, - ignoreImmunity, - simulated, - moveType, - defType, - ); + if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) { + applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType); } if (ignoreImmunity.value) { if (multiplier.value === 0) { @@ -2872,9 +2526,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - const exposedTags = this.findTags( - tag => tag instanceof ExposedTag, - ) as ExposedTag[]; + const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[]; if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) { if (multiplier.value === 0) { return 1; @@ -2885,13 +2537,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }) .reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; - const typeMultiplierAgainstFlying = new NumberHolder( - getTypeDamageMultiplier(moveType, PokemonType.FLYING), - ); - applyChallenges( - ChallengeType.TYPE_EFFECTIVENESS, - typeMultiplierAgainstFlying, - ); + const typeMultiplierAgainstFlying = new NumberHolder(getTypeDamageMultiplier(moveType, PokemonType.FLYING)); + applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, typeMultiplierAgainstFlying); // Handle strong winds lowering effectiveness of types super effective against pure flying if ( !ignoreStrongWinds && @@ -2920,27 +2567,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const enemyTypes = opponent.getTypes(true, true, false, true); /** Is this Pokemon faster than the opponent? */ const outspeed = - (this.isActive(true) - ? this.getEffectiveStat(Stat.SPD, opponent) - : this.getStat(Stat.SPD, false)) >= + (this.isActive(true) ? this.getEffectiveStat(Stat.SPD, opponent) : this.getStat(Stat.SPD, false)) >= opponent.getEffectiveStat(Stat.SPD, this); /** * Based on how effective this Pokemon's types are offensively against the opponent's types. * This score is increased by 25 percent if this Pokemon is faster than the opponent. */ - let atkScore = opponent.getAttackTypeEffectiveness(types[0], this, false, true, undefined, true) * (outspeed ? 1.25 : 1); + let atkScore = + opponent.getAttackTypeEffectiveness(types[0], this, false, true, undefined, true) * (outspeed ? 1.25 : 1); /** * Based on how effectively this Pokemon defends against the opponent's types. * This score cannot be higher than 4. */ - let defScore = - 1 / - Math.max(this.getAttackTypeEffectiveness(enemyTypes[0], opponent), 0.25); + let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0], opponent), 0.25); if (types.length > 1) { atkScore *= opponent.getAttackTypeEffectiveness(types[1], this); } if (enemyTypes.length > 1) { - defScore *= (1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25)); + defScore *= + 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25); } /** * Based on this Pokemon's HP ratio compared to that of the opponent. @@ -2964,14 +2609,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - if ( - this.isFusion() && - this.fusionSpecies && - pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId) - ) { - const fusionEvolutions = pokemonEvolutions[ - this.fusionSpecies.speciesId - ].map(e => new FusionSpeciesFormEvolution(this.species.speciesId, e)); + if (this.isFusion() && this.fusionSpecies && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { + const fusionEvolutions = pokemonEvolutions[this.fusionSpecies.speciesId].map( + e => new FusionSpeciesFormEvolution(this.species.speciesId, e), + ); for (const fe of fusionEvolutions) { if (fe.validate(this)) { return fe; @@ -3002,10 +2643,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!startingLevel) { startingLevel = this.level; } - if ( - learnSituation === LearnMoveSituation.EVOLUTION_FUSED && - this.fusionSpecies - ) { + if (learnSituation === LearnMoveSituation.EVOLUTION_FUSED && this.fusionSpecies) { // For fusion evolutions, get ONLY the moves of the component mon that evolved levelMoves = this.getFusionSpeciesForm(true) .getLevelMoves() @@ -3025,10 +2663,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); for (let e = 0; e < evolutionChain.length; e++) { // TODO: Might need to pass specific form index in simulated evolution chain - const speciesLevelMoves = getPokemonSpeciesForm( - evolutionChain[e][0], - this.formIndex, - ).getLevelMoves(); + const speciesLevelMoves = getPokemonSpeciesForm(evolutionChain[e][0], this.formIndex).getLevelMoves(); if (includeRelearnerMoves) { levelMoves.push(...speciesLevelMoves); } else { @@ -3036,9 +2671,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ...speciesLevelMoves.filter( lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || - ((!e || lm[0] > 1) && - (e === evolutionChain.length - 1 || - lm[0] <= evolutionChain[e + 1][1])), + ((!e || lm[0] > 1) && (e === evolutionChain.length - 1 || lm[0] <= evolutionChain[e + 1][1])), ), ); } @@ -3053,19 +2686,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { lm[0] > 0, ); } - if ( - this.fusionSpecies && - learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE - ) { + if (this.fusionSpecies && learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE) { // For fusion evolutions, get ONLY the moves of the component mon that evolved if (simulateEvolutionChain) { - const fusionEvolutionChain = - this.fusionSpecies.getSimulatedEvolutionChain( - this.level, - this.hasTrainer(), - this.isBoss(), - this.isPlayer(), - ); + const fusionEvolutionChain = this.fusionSpecies.getSimulatedEvolutionChain( + this.level, + this.hasTrainer(), + this.isBoss(), + this.isPlayer(), + ); for (let e = 0; e < fusionEvolutionChain.length; e++) { // TODO: Might need to pass specific form index in simulated evolution chain const speciesLevelMoves = getPokemonSpeciesForm( @@ -3075,9 +2704,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (includeRelearnerMoves) { levelMoves.push( ...speciesLevelMoves.filter( - lm => - (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || - lm[0] !== EVOLVE_MOVE, + lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || lm[0] !== EVOLVE_MOVE, ), ); } else { @@ -3086,8 +2713,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { lm => (includeEvolutionMoves && lm[0] === EVOLVE_MOVE) || ((!e || lm[0] > 1) && - (e === fusionEvolutionChain.length - 1 || - lm[0] <= fusionEvolutionChain[e + 1][1])), + (e === fusionEvolutionChain.length - 1 || lm[0] <= fusionEvolutionChain[e + 1][1])), ), ); } @@ -3106,9 +2732,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } } - levelMoves.sort((lma: [number, number], lmb: [number, number]) => - lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0, - ); + levelMoves.sort((lma: [number, number], lmb: [number, number]) => (lma[0] > lmb[0] ? 1 : lma[0] < lmb[0] ? -1 : 0)); /** * Filter out moves not within the correct level range(s) @@ -3120,10 +2744,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const isRelearner = level < startingLevel; const allowedEvolutionMove = level === 0 && includeEvolutionMoves; - return ( - !(level > this.level) && - (includeRelearnerMoves || !isRelearner || allowedEvolutionMove) - ); + return !(level > this.level) && (includeRelearnerMoves || !isRelearner || allowedEvolutionMove); }); /** @@ -3189,10 +2810,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ trySetShiny(thresholdOverride?: number): boolean { // Shiny Pokemon should not spawn in the end biome in endless - if ( - globalScene.gameMode.isEndless && - globalScene.arena.biomeType === Biome.END - ) { + if (globalScene.gameMode.isEndless && globalScene.arena.biomeType === Biome.END) { return false; } @@ -3212,11 +2830,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } if (!this.hasTrainer()) { - globalScene.applyModifiers( - ShinyRateBoosterModifier, - true, - shinyThreshold, - ); + globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); } } else { shinyThreshold.value = thresholdOverride; @@ -3241,10 +2855,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride} * @returns `true` if the Pokemon has been set as a shiny, `false` otherwise */ - public trySetShinySeed( - thresholdOverride?: number, - applyModifiersToOverride?: boolean, - ): boolean { + public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean { if (!this.shiny) { const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); if (thresholdOverride === undefined || applyModifiersToOverride) { @@ -3254,13 +2865,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (timedEventManager.isEventActive()) { shinyThreshold.value *= timedEventManager.getShinyMultiplier(); } - globalScene.applyModifiers( - ShinyRateBoosterModifier, - true, - shinyThreshold, - ); - } - else { + globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); + } else { shinyThreshold.value = thresholdOverride; } @@ -3270,8 +2876,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.shiny) { this.variant = this.variant ?? 0; this.variant = Math.max(this.generateShinyVariant(), this.variant) as Variant; // Don't set a variant lower than the current one - this.luck = - this.variant + 1 + (this.fusionShiny ? this.fusionVariant + 1 : 0); + this.luck = this.variant + 1 + (this.fusionShiny ? this.fusionVariant + 1 : 0); this.initShinySparkle(); } @@ -3297,8 +2902,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Checks if there is no variant data for both the index or index with form if ( !this.shiny || - (!variantData.hasOwnProperty(variantDataIndex) && - !variantData.hasOwnProperty(this.species.speciesId)) + (!variantData.hasOwnProperty(variantDataIndex) && !variantData.hasOwnProperty(this.species.speciesId)) ) { return 0; } @@ -3328,10 +2932,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Ability Charm to {@linkcode thresholdOverride} * @returns `true` if the Pokemon has been set to have its hidden ability, `false` otherwise */ - public tryRerollHiddenAbilitySeed( - thresholdOverride?: number, - applyModifiersToOverride?: boolean, - ): boolean { + public tryRerollHiddenAbilitySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean { if (!this.species.abilityHidden) { return false; } @@ -3341,11 +2942,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { haThreshold.value = thresholdOverride; } if (!this.hasTrainer()) { - globalScene.applyModifiers( - HiddenAbilityRateBoosterModifier, - true, - haThreshold, - ); + globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, haThreshold); } } else { haThreshold.value = thresholdOverride; @@ -3359,15 +2956,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } public generateFusionSpecies(forStarter?: boolean): void { - const hiddenAbilityChance = new NumberHolder( - BASE_HIDDEN_ABILITY_CHANCE, - ); + const hiddenAbilityChance = new NumberHolder(BASE_HIDDEN_ABILITY_CHANCE); if (!this.hasTrainer()) { - globalScene.applyModifiers( - HiddenAbilityRateBoosterModifier, - true, - hiddenAbilityChance, - ); + globalScene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance); } const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); @@ -3390,30 +2981,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let fusionOverride: PokemonSpecies | undefined = undefined; - if ( - forStarter && - this instanceof PlayerPokemon && - Overrides.STARTER_FUSION_SPECIES_OVERRIDE - ) { - fusionOverride = getPokemonSpecies( - Overrides.STARTER_FUSION_SPECIES_OVERRIDE, - ); - } else if ( - this instanceof EnemyPokemon && - Overrides.OPP_FUSION_SPECIES_OVERRIDE - ) { + if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { + fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); + } else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) { fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE); } this.fusionSpecies = fusionOverride ?? - globalScene.randomSpecies( - globalScene.currentBattle?.waveIndex || 0, - this.level, - false, - filter, - true, - ); + globalScene.randomSpecies(globalScene.currentBattle?.waveIndex || 0, this.level, false, filter, true); this.fusionAbilityIndex = this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 @@ -3465,10 +3041,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let movePool: [Moves, number][] = []; const allLevelMoves = this.getLevelMoves(1, true, true); if (!allLevelMoves) { - console.warn( - "Error encountered trying to generate moveset for:", - this.species.name, - ); + console.warn("Error encountered trying to generate moveset for:", this.species.name); return; } @@ -3483,13 +3056,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { weight = 50; } // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight. Trainers use actual relearn moves. - if (weight === 1 && allMoves[levelMove[1]].power >= 80 || weight === RELEARN_MOVE && this.hasTrainer()) { + if ((weight === 1 && allMoves[levelMove[1]].power >= 80) || (weight === RELEARN_MOVE && this.hasTrainer())) { weight = 40; } - if ( - !movePool.some(m => m[0] === levelMove[1]) && - !allMoves[levelMove[1]].name.endsWith(" (N)") - ) { + if (!movePool.some(m => m[0] === levelMove[1]) && !allMoves[levelMove[1]].name.endsWith(" (N)")) { movePool.push([levelMove[1], weight]); } } @@ -3510,30 +3080,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { compatible = true; break; } - } else if ( - p === this.species.speciesId || - (this.fusionSpecies && p === this.fusionSpecies.speciesId) - ) { + } else if (p === this.species.speciesId || (this.fusionSpecies && p === this.fusionSpecies.speciesId)) { compatible = true; break; } } - if ( - compatible && - !movePool.some(m => m[0] === moveId) && - !allMoves[moveId].name.endsWith(" (N)") - ) { + if (compatible && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) { if (tmPoolTiers[moveId] === ModifierTier.COMMON && this.level >= 15) { movePool.push([moveId, 4]); - } else if ( - tmPoolTiers[moveId] === ModifierTier.GREAT && - this.level >= 30 - ) { + } else if (tmPoolTiers[moveId] === ModifierTier.GREAT && this.level >= 30) { movePool.push([moveId, 8]); - } else if ( - tmPoolTiers[moveId] === ModifierTier.ULTRA && - this.level >= 50 - ) { + } else if (tmPoolTiers[moveId] === ModifierTier.ULTRA && this.level >= 50) { movePool.push([moveId, 14]); } } @@ -3543,10 +3100,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.level >= 60) { for (let i = 0; i < 3; i++) { const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i]; - if ( - !movePool.some(m => m[0] === moveId) && - !allMoves[moveId].name.endsWith(" (N)") - ) { + if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) { movePool.push([moveId, 40]); } } @@ -3562,17 +3116,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.fusionSpecies) { for (let i = 0; i < 3; i++) { - const moveId = - speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][i]; - if ( - !movePool.some(m => m[0] === moveId) && - !allMoves[moveId].name.endsWith(" (N)") - ) { + const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][i]; + if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) { movePool.push([moveId, 40]); } } - const moveId = - speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3]; + const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3]; // No rare egg moves before e4 if ( this.level >= 170 && @@ -3591,35 +3140,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr) && !allMoves[m[0]].hasAttr(HpSplitAttr)); } // No one gets Memento or Final Gambit - movePool = movePool.filter( - m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit), - ); + movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit)); if (this.hasTrainer()) { // Trainers never get OHKO moves movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(OneHitKOAttr)); // Half the weight of self KO moves - movePool = movePool.map(m => [ - m[0], - m[1] * (allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1), - ]); + movePool = movePool.map(m => [m[0], m[1] * (allMoves[m[0]].hasAttr(SacrificialAttr) ? 0.5 : 1)]); // Trainers get a weight bump to stat buffing moves movePool = movePool.map(m => [ m[0], - m[1] * - (allMoves[m[0]] - .getAttrs(StatStageChangeAttr) - .some(a => a.stages > 1 && a.selfTarget) - ? 1.25 - : 1), + m[1] * (allMoves[m[0]].getAttrs(StatStageChangeAttr).some(a => a.stages > 1 && a.selfTarget) ? 1.25 : 1), ]); // Trainers get a weight decrease to multiturn moves movePool = movePool.map(m => [ m[0], - m[1] * - (!!allMoves[m[0]].isChargingMove() || - !!allMoves[m[0]].hasAttr(RechargeAttr) - ? 0.7 - : 1), + m[1] * (!!allMoves[m[0]].isChargingMove() || !!allMoves[m[0]].hasAttr(RechargeAttr) ? 0.7 : 1), ]); } @@ -3627,10 +3162,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Caps max power at 90 to avoid something like hyper beam ruining the stats. // This is a pretty soft weighting factor, although it is scaled with the weight multiplier. const maxPower = Math.min( - movePool.reduce( - (v, m) => Math.max(allMoves[m[0]].calculateEffectivePower(), v), - 40, - ), + movePool.reduce((v, m) => Math.max(allMoves[m[0]].calculateEffectivePower(), v), 40), 90, ); movePool = movePool.map(m => [ @@ -3638,10 +3170,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { m[1] * (allMoves[m[0]].category === MoveCategory.STATUS ? 1 - : Math.max( - Math.min(allMoves[m[0]].calculateEffectivePower() / maxPower, 1), - 0.5, - )), + : Math.max(Math.min(allMoves[m[0]].calculateEffectivePower() / maxPower, 1), 0.5)), ]); // Weight damaging moves against the lower stat. This uses a non-linear relationship. @@ -3649,16 +3178,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // One third weight at ~1.58x higher, one quarter weight at ~1.73x higher, one fifth at ~1.87x, and one tenth at ~2.35x higher. const atk = this.getStat(Stat.ATK); const spAtk = this.getStat(Stat.SPATK); - const worseCategory: MoveCategory = - atk > spAtk ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL; - const statRatio = - worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk; + const worseCategory: MoveCategory = atk > spAtk ? MoveCategory.SPECIAL : MoveCategory.PHYSICAL; + const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk; movePool = movePool.map(m => [ m[0], - m[1] * - (allMoves[m[0]].category === worseCategory - ? Math.min(Math.pow(statRatio, 3) * 1.3, 1) - : 1), + m[1] * (allMoves[m[0]].category === worseCategory ? Math.min(Math.pow(statRatio, 3) * 1.3, 1) : 1), ]); /** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */ @@ -3666,16 +3190,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.isBoss()) { weightMultiplier += 0.4; } - const baseWeights: [Moves, number][] = movePool.map(m => [ - m[0], - Math.ceil(Math.pow(m[1], weightMultiplier) * 100), - ]); + const baseWeights: [Moves, number][] = movePool.map(m => [m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100)]); // All Pokemon force a STAB move first const stabMovePool = baseWeights.filter( - m => - allMoves[m[0]].category !== MoveCategory.STATUS && - this.isOfType(allMoves[m[0]].type), + m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type), ); if (stabMovePool.length) { @@ -3688,41 +3207,32 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.moveset.push(new PokemonMove(stabMovePool[index][0], 0, 0)); } - while ( - baseWeights.length > this.moveset.length && - this.moveset.length < 4 - ) { + while (baseWeights.length > this.moveset.length && this.moveset.length < 4) { if (this.hasTrainer()) { // Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier. // Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights get 20x if STAB. // Status moves remain unchanged on weight, this encourages 1-2 movePool = baseWeights - .filter(m => !this.moveset.some( - mo => - m[0] === mo.moveId || - (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)) // Only one self-KO move allowed - )) + .filter( + m => + !this.moveset.some( + mo => + m[0] === mo.moveId || + (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)), // Only one self-KO move allowed + ), + ) .map(m => { let ret: number; if ( this.moveset.some( - mo => - mo.getMove().category !== MoveCategory.STATUS && - mo.getMove().type === allMoves[m[0]].type, + mo => mo.getMove().category !== MoveCategory.STATUS && mo.getMove().type === allMoves[m[0]].type, ) ) { ret = Math.ceil(Math.sqrt(m[1])); } else if (allMoves[m[0]].category !== MoveCategory.STATUS) { ret = Math.ceil( (m[1] / - Math.max( - Math.pow( - 4, - this.moveset.filter(mo => (mo.getMove().power ?? 0) > 1) - .length, - ) / 8, - 0.5, - )) * + Math.max(Math.pow(4, this.moveset.filter(mo => (mo.getMove().power ?? 0) > 1).length) / 8, 0.5)) * (this.isOfType(allMoves[m[0]].type) ? 20 : 1), ); } else { @@ -3732,11 +3242,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); } else { // Non-trainer pokemon just use normal weights - movePool = baseWeights.filter(m => !this.moveset.some( - mo => - m[0] === mo.moveId || - (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)) // Only one self-KO move allowed - )); + movePool = baseWeights.filter( + m => + !this.moveset.some( + mo => + m[0] === mo.moveId || + (allMoves[m[0]].hasAttr(SacrificialAttr) && mo.getMove().hasAttr(SacrificialAttr)), // Only one self-KO move allowed + ), + ); } const totalWeight = movePool.reduce((v, m) => v + m[1], 0); let rand = randSeedInt(totalWeight); @@ -3753,18 +3266,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { !globalScene.currentBattle?.isBattleMysteryEncounter() || !globalScene.currentBattle?.mysteryEncounter ) { - globalScene.triggerPokemonFormChange( - this, - SpeciesFormChangeMoveLearnedTrigger, - ); + globalScene.triggerPokemonFormChange(this, SpeciesFormChangeMoveLearnedTrigger); } } public trySelectMove(moveIndex: number, ignorePp?: boolean): boolean { - const move = - this.getMoveset().length > moveIndex - ? this.getMoveset()[moveIndex] - : null; + const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null; return move?.isUsable(this, ignorePp) ?? false; } @@ -3773,11 +3280,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const otherBattleInfo = globalScene.fieldUI .getAll() .slice(0, 4) - .filter( - ui => - ui instanceof BattleInfo && - (ui as BattleInfo) instanceof PlayerBattleInfo === this.isPlayer(), - ) + .filter(ui => ui instanceof BattleInfo && (ui as BattleInfo) instanceof PlayerBattleInfo === this.isPlayer()) .find(() => true); if (!otherBattleInfo || !this.getFieldIndex()) { globalScene.fieldUI.sendToBack(this.battleInfo); @@ -3785,10 +3288,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } else { globalScene.fieldUI.moveAbove(this.battleInfo, otherBattleInfo); } - this.battleInfo.setX( - this.battleInfo.x + - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198), - ); + this.battleInfo.setX(this.battleInfo.x + (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198)); this.battleInfo.setVisible(true); if (this.isPlayer()) { this.battleInfo.expMaskRect.x += 150; @@ -3804,7 +3304,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { hideInfo(): Promise { return new Promise(resolve => { - if (this.battleInfo && this.battleInfo.visible) { + if (this.battleInfo?.visible) { globalScene.tweens.add({ targets: [this.battleInfo, this.battleInfo.expMaskRect], x: this.isPlayer() ? "+=150" : `-=${!this.isBoss() ? 150 : 246}`, @@ -3815,10 +3315,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.battleInfo.expMaskRect.x -= 150; } this.battleInfo.setVisible(false); - this.battleInfo.setX( - this.battleInfo.x - - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198), - ); + this.battleInfo.setX(this.battleInfo.x - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198)); resolve(); }, }); @@ -3865,25 +3362,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const maxExpLevel = globalScene.getMaxExpLevel(ignoreLevelCap); const initialExp = this.exp; this.exp += exp; - while ( - this.level < maxExpLevel && - this.exp >= getLevelTotalExp(this.level + 1, this.species.growthRate) - ) { + while (this.level < maxExpLevel && this.exp >= getLevelTotalExp(this.level + 1, this.species.growthRate)) { this.level++; } if (this.level >= maxExpLevel) { - console.log( - initialExp, - this.exp, - getLevelTotalExp(this.level, this.species.growthRate), - ); - this.exp = Math.max( - getLevelTotalExp(this.level, this.species.growthRate), - initialExp, - ); + console.log(initialExp, this.exp, getLevelTotalExp(this.level, this.species.growthRate)); + this.exp = Math.max(getLevelTotalExp(this.level, this.species.growthRate), initialExp); } - this.levelExp = - this.exp - getLevelTotalExp(this.level, this.species.growthRate); + this.levelExp = this.exp - getLevelTotalExp(this.level, this.species.growthRate); } /** @@ -3897,7 +3383,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getOpponent(targetIndex: number): Pokemon | null { const ret = this.getOpponents()[targetIndex]; - if (ret.summonData) { // TODO: why does this check for summonData and can we remove it? + // TODO: why does this check for summonData and can we remove it? + if (ret.summonData) { return ret; } return null; @@ -3909,11 +3396,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param onField - whether to also check if the pokemon is currently on the field (defaults to true) */ getOpponents(onField = true): Pokemon[] { - return ( - (this.isPlayer() - ? globalScene.getEnemyField() - : globalScene.getPlayerField()) as Pokemon[] - ).filter(p => p.isActive(onField)); + return ((this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField()) as Pokemon[]).filter(p => + p.isActive(onField), + ); } getOpponentDescriptor(): string { @@ -3921,17 +3406,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (opponents.length === 1) { return opponents[0].name; } - return this.isPlayer() - ? i18next.t("arenaTag:opposingTeam") - : i18next.t("arenaTag:yourTeam"); + return this.isPlayer() ? i18next.t("arenaTag:opposingTeam") : i18next.t("arenaTag:yourTeam"); } getAlly(): Pokemon | undefined { - return ( - this.isPlayer() - ? globalScene.getPlayerField() - : globalScene.getEnemyField() - )[this.getFieldIndex() ? 0 : 1]; + return (this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField())[this.getFieldIndex() ? 0 : 1]; } /** @@ -3940,9 +3419,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns An array of Pokémon on the allied field. */ getAlliedField(): Pokemon[] { - return this instanceof PlayerPokemon - ? globalScene.getPlayerField() - : globalScene.getEnemyField(); + return this instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); } /** @@ -3985,37 +3462,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } if (!ignoreOppAbility) { - applyAbAttrs( - IgnoreOpponentStatStagesAbAttr, - opponent, - null, - simulated, - stat, - ignoreStatStage, - ); + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage); } if (move) { - applyMoveAttrs( - IgnoreOpponentStatStagesAttr, - this, - opponent, - move, - ignoreStatStage, - ); + applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage); } } if (!ignoreStatStage.value) { - const statStageMultiplier = new NumberHolder( - Math.max(2, 2 + statStage.value) / Math.max(2, 2 - statStage.value), - ); + const statStageMultiplier = new NumberHolder(Math.max(2, 2 + statStage.value) / Math.max(2, 2 - statStage.value)); if (!ignoreHeldItems) { - globalScene.applyModifiers( - TempStatStageBoosterModifier, - this.isPlayer(), - stat, - statStageMultiplier, - ); + globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), stat, statStageMultiplier); } return Math.min(statStageMultiplier.value, 4); } @@ -4039,47 +3496,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const userAccStage = new NumberHolder(this.getStatStage(Stat.ACC)); - const targetEvaStage = new NumberHolder( - target.getStatStage(Stat.EVA), - ); + const targetEvaStage = new NumberHolder(target.getStatStage(Stat.EVA)); const ignoreAccStatStage = new BooleanHolder(false); const ignoreEvaStatStage = new BooleanHolder(false); - applyAbAttrs( - IgnoreOpponentStatStagesAbAttr, - target, - null, - false, - Stat.ACC, - ignoreAccStatStage, - ); - applyAbAttrs( - IgnoreOpponentStatStagesAbAttr, - this, - null, - false, - Stat.EVA, - ignoreEvaStatStage, - ); - applyMoveAttrs( - IgnoreOpponentStatStagesAttr, - this, - target, - sourceMove, - ignoreEvaStatStage, - ); + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, target, null, false, Stat.ACC, ignoreAccStatStage); + applyAbAttrs(IgnoreOpponentStatStagesAbAttr, this, null, false, Stat.EVA, ignoreEvaStatStage); + applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, target, sourceMove, ignoreEvaStatStage); - globalScene.applyModifiers( - TempStatStageBoosterModifier, - this.isPlayer(), - Stat.ACC, - userAccStage, - ); + globalScene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage); - userAccStage.value = ignoreAccStatStage.value - ? 0 - : Math.min(userAccStage.value, 6); + userAccStage.value = ignoreAccStatStage.value ? 0 : Math.min(userAccStage.value, 6); targetEvaStage.value = ignoreEvaStatStage.value ? 0 : targetEvaStage.value; if (target.findTag(t => t instanceof ExposedTag)) { @@ -4094,22 +3522,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : 3 / (3 + Math.min(targetEvaStage.value - userAccStage.value, 6)); } - applyStatMultiplierAbAttrs( - StatMultiplierAbAttr, - this, - Stat.ACC, - accuracyMultiplier, - false, - sourceMove, - ); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, Stat.ACC, accuracyMultiplier, false, sourceMove); const evasionMultiplier = new NumberHolder(1); - applyStatMultiplierAbAttrs( - StatMultiplierAbAttr, - target, - Stat.EVA, - evasionMultiplier, - ); + applyStatMultiplierAbAttrs(StatMultiplierAbAttr, target, Stat.EVA, evasionMultiplier); const ally = this.getAlly(); if (!isNullOrUndefined(ally)) { @@ -4135,8 +3551,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param simulated - if `true`, suppresses changes to game state during calculation (defaults to `true`). * @returns The move's base damage against this Pokemon when used by the source Pokemon. */ - getBaseDamage( - { + getBaseDamage({ source, move, moveCategory, @@ -4145,8 +3560,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ignoreAllyAbility = false, ignoreSourceAllyAbility = false, isCritical = false, - simulated = true}: getBaseDamageParams - ): number { + simulated = true, + }: getBaseDamageParams): number { const isPhysical = moveCategory === MoveCategory.PHYSICAL; /** A base damage multiplier based on the source's level */ @@ -4195,25 +3610,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * The attack's base damage, as determined by the source's level, move power * and Attack stat as well as this Pokemon's Defense stat */ - const baseDamage = - (levelMultiplier * power * sourceAtk.value) / targetDef.value / 50 + 2; + const baseDamage = (levelMultiplier * power * sourceAtk.value) / targetDef.value / 50 + 2; /** Debug message for non-simulated calls (i.e. when damage is actually dealt) */ if (!simulated) { - console.log( - "base damage", - baseDamage, - move.name, - power, - sourceAtk.value, - targetDef.value, - ); + console.log("base damage", baseDamage, move.name, power, sourceAtk.value, targetDef.value); } return baseDamage; } - /** Determine the STAB multiplier for a move used against this pokemon. * * @param source - The attacking {@linkcode Pokemon} @@ -4237,31 +3643,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { stabMultiplier.value += 0.5; } - applyMoveAttrs( - CombinedPledgeStabBoostAttr, - source, - this, - move, - stabMultiplier, - ); + applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier); if (!ignoreSourceAbility) { applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier); } - if ( - source.isTerastallized && - sourceTeraType === moveType && - moveType !== PokemonType.STELLAR - ) { + if (source.isTerastallized && sourceTeraType === moveType && moveType !== PokemonType.STELLAR) { stabMultiplier.value += 0.5; } if ( source.isTerastallized && source.getTeraType() === PokemonType.STELLAR && - (!source.stellarTypesBoosted.includes(moveType) || - source.hasSpecies(Species.TERAPAGOS)) + (!source.stellarTypesBoosted.includes(moveType) || source.hasSpecies(Species.TERAPAGOS)) ) { stabMultiplier.value += matchesSourceType ? 0.5 : 0.2; } @@ -4282,31 +3677,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param effectiveness If defined, used in place of calculated effectiveness values * @returns The {@linkcode DamageCalculationResult} */ - getAttackDamage( - { - source, - move, - ignoreAbility = false, - ignoreSourceAbility = false, - ignoreAllyAbility = false, - ignoreSourceAllyAbility = false, - isCritical = false, - simulated = true, - effectiveness}: getAttackDamageParams, - ): DamageCalculationResult { + getAttackDamage({ + source, + move, + ignoreAbility = false, + ignoreSourceAbility = false, + ignoreAllyAbility = false, + ignoreSourceAllyAbility = false, + isCritical = false, + simulated = true, + effectiveness, + }: getAttackDamageParams): DamageCalculationResult { const damage = new NumberHolder(0); - const defendingSide = this.isPlayer() - ? ArenaTagSide.PLAYER - : ArenaTagSide.ENEMY; + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const variableCategory = new NumberHolder(move.category); - applyMoveAttrs( - VariableMoveCategoryAttr, - source, - this, - move, - variableCategory, - ); + applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory); const moveCategory = variableCategory.value as MoveCategory; /** The move's type after type-changing effects are applied */ @@ -4322,13 +3708,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * Note that the source's abilities are not ignored here */ - const typeMultiplier = effectiveness ?? this.getMoveEffectiveness( - source, - move, - ignoreAbility, - simulated, - cancelled, - ); + const typeMultiplier = + effectiveness ?? this.getMoveEffectiveness(source, move, ignoreAbility, simulated, cancelled); const isPhysical = moveCategory === MoveCategory.PHYSICAL; @@ -4336,21 +3717,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const arenaAttackTypeMultiplier = new NumberHolder( globalScene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()), ); - applyMoveAttrs( - IgnoreWeatherTypeDebuffAttr, - source, - this, - move, - arenaAttackTypeMultiplier, - ); + applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier); const isTypeImmune = typeMultiplier * arenaAttackTypeMultiplier.value === 0; if (cancelled.value || isTypeImmune) { return { cancelled: cancelled.value, - result: - move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT, + result: move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT, damage: 0, }; } @@ -4368,9 +3742,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { null, multiLensMultiplier, ); - fixedDamage.value = toDmgValue( - fixedDamage.value * multiLensMultiplier.value, - ); + fixedDamage.value = toDmgValue(fixedDamage.value * multiLensMultiplier.value); return { cancelled: false, @@ -4447,10 +3819,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * A multiplier for random damage spread in the range [0.85, 1] * This is always 1 for simulated calls. */ - const randomMultiplier = simulated - ? 1 - : this.randBattleSeedIntRange(85, 100) / 100; - + const randomMultiplier = simulated ? 1 : this.randBattleSeedIntRange(85, 100) / 100; /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ const stabMultiplier = this.calculateStabMultiplier(source, move, ignoreSourceAbility, simulated); @@ -4465,12 +3834,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { const burnDamageReductionCancelled = new BooleanHolder(false); if (!ignoreSourceAbility) { - applyAbAttrs( - BypassBurnDamageReductionAbAttr, - source, - burnDamageReductionCancelled, - simulated, - ); + applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, simulated); } if (!burnDamageReductionCancelled.value) { burnMultiplier = 0.5; @@ -4483,13 +3847,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Critical hits should bypass screens if (!isCritical) { globalScene.arena.applyTagsForSide( - WeakenMoveScreenTag, - defendingSide, - simulated, - source, - moveCategory, - screenMultiplier, - ); + WeakenMoveScreenTag, + defendingSide, + simulated, + source, + moveCategory, + screenMultiplier, + ); } /** @@ -4534,14 +3898,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Doubles damage if the attacker has Tinted Lens and is using a resisted move */ if (!ignoreSourceAbility) { - applyPreAttackAbAttrs( - DamageBoostAbAttr, - source, - this, - move, - simulated, - damage, - ); + applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage); } /** Apply the enemy's Damage and Resistance tokens */ @@ -4554,28 +3911,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */ if (!ignoreAbility) { - applyPreDefendAbAttrs( - ReceivedMoveDamageMultiplierAbAttr, - this, - source, - move, - cancelled, - simulated, - damage, - ); + applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage); const ally = this.getAlly(); /** Additionally apply friend guard damage reduction if ally has it. */ if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) { - applyPreDefendAbAttrs( - AlliedFieldDamageReductionAbAttr, - ally, - source, - move, - cancelled, - simulated, - damage, - ); + applyPreDefendAbAttrs(AlliedFieldDamageReductionAbAttr, ally, source, move, cancelled, simulated, damage); } } @@ -4583,15 +3924,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); if (this.isFullHp() && !ignoreAbility) { - applyPreDefendAbAttrs( - PreDefendFullHpEndureAbAttr, - this, - source, - move, - cancelled, - false, - damage, - ); + applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage); } // debug message for when damage is applied (i.e. not simulated) @@ -4620,8 +3953,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param move - The {@linkcode Move} being used * @param simulated - If `true`, suppresses changes to game state during calculation (defaults to `true`) * @returns whether the move critically hits the pokemon - */ - getCriticalHitResult(source: Pokemon, move: Move, simulated: boolean = true): boolean { + */ + getCriticalHitResult(source: Pokemon, move: Move, simulated = true): boolean { const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide); if (noCritTag || Overrides.NEVER_CRIT_OVERRIDE || move.hasAttr(FixedDamageAttr)) { @@ -4635,16 +3968,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(CritOnlyAttr, source, this, move, isCritical); applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move); if (!isCritical.value) { - const critChance = [24, 8, 2, 1][ - Math.max(0, Math.min(this.getCritStage(source, move), 3)) - ]; + const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))]; isCritical.value = critChance === 1 || !globalScene.randBattleSeedInt(critChance); } applyAbAttrs(BlockCritAbAttr, this, null, simulated, isCritical); return isCritical.value; - } /** @@ -4655,12 +3985,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreFaintPhas 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 { + damage(damage: number, _ignoreSegments = false, preventEndure = false, ignoreFaintPhase = false): number { if (this.isFainted()) { return 0; } @@ -4676,12 +4001,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { surviveDamage.value = this.lapseTag(BattlerTagType.ENDURE_TOKEN); } if (!surviveDamage.value) { - globalScene.applyModifiers( - SurviveDamageModifier, - this.isPlayer(), - this, - surviveDamage, - ); + globalScene.applyModifiers(SurviveDamageModifier, this.isPlayer(), this, surviveDamage); } if (surviveDamage.value) { damage = this.hp - 1; @@ -4699,9 +4019,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Once the MoveEffectPhase is over (and calls it's .end() function, shiftPhase() will reset the PhaseQueueSplice via clearPhaseQueueSplice() ) */ globalScene.setPhaseQueueSplice(); - globalScene.unshiftPhase( - new FaintPhase(this.getBattlerIndex(), preventEndure), - ); + globalScene.unshiftPhase(new FaintPhase(this.getBattlerIndex(), preventEndure)); this.destroySubstitute(); this.lapseTag(BattlerTagType.COMMANDED); } @@ -4720,39 +4038,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage() * @returns integer of damage done */ - damageAndUpdate(damage: number, + damageAndUpdate( + damage: number, { result = HitResult.EFFECTIVE, isCritical = false, ignoreSegments = false, ignoreFaintPhase = false, source = undefined, - }: - { - result?: DamageResult, - isCritical?: boolean, - ignoreSegments?: boolean, - ignoreFaintPhase?: boolean, - source?: Pokemon, - } = {} + }: { + result?: DamageResult; + isCritical?: boolean; + ignoreSegments?: boolean; + ignoreFaintPhase?: boolean; + source?: Pokemon; + } = {}, ): number { - const isIndirectDamage = [ HitResult.INDIRECT, HitResult.INDIRECT_KO ].includes(result); - const damagePhase = new DamageAnimPhase( - this.getBattlerIndex(), - damage, - result as DamageResult, - isCritical - ); + const isIndirectDamage = [HitResult.INDIRECT, HitResult.INDIRECT_KO].includes(result); + const damagePhase = new DamageAnimPhase(this.getBattlerIndex(), damage, result as DamageResult, isCritical); globalScene.unshiftPhase(damagePhase); if (this.switchOutStatus && source) { damage = 0; } - damage = this.damage( - damage, - ignoreSegments, - isIndirectDamage, - ignoreFaintPhase, - ); + damage = this.damage(damage, ignoreSegments, isIndirectDamage, ignoreFaintPhase); // Ensure the battle-info bar's HP is updated, though only if the battle info is visible // TODO: When battle-info UI is refactored, make this only update the HP bar if (this.battleInfo.visible) { @@ -4765,15 +4073,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Multi-hits are handled in move-effect-phase.ts for PostDamageAbAttr */ if (!source || source.turnData.hitCount <= 1) { - applyPostDamageAbAttrs( - PostDamageAbAttr, - this, - damage, - this.hasPassive(), - false, - [], - source, - ); + applyPostDamageAbAttrs(PostDamageAbAttr, this, damage, this.hasPassive(), false, [], source); } return damage; } @@ -4789,13 +4089,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isMax(): boolean { - const maxForms = [ SpeciesFormKey.GIGANTAMAX, SpeciesFormKey.GIGANTAMAX_RAPID, SpeciesFormKey.GIGANTAMAX_SINGLE, SpeciesFormKey.ETERNAMAX ] as string[]; - return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); + const maxForms = [ + SpeciesFormKey.GIGANTAMAX, + SpeciesFormKey.GIGANTAMAX_RAPID, + SpeciesFormKey.GIGANTAMAX_SINGLE, + SpeciesFormKey.ETERNAMAX, + ] as string[]; + return ( + maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)) + ); } isMega(): boolean { - const megaForms = [ SpeciesFormKey.MEGA, SpeciesFormKey.MEGA_X, SpeciesFormKey.MEGA_Y, SpeciesFormKey.PRIMAL ] as string[]; - return megaForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && megaForms.includes(this.getFusionFormKey()!)); + const megaForms = [ + SpeciesFormKey.MEGA, + SpeciesFormKey.MEGA_X, + SpeciesFormKey.MEGA_Y, + SpeciesFormKey.PRIMAL, + ] as string[]; + return ( + megaForms.includes(this.getFormKey()) || + (!!this.getFusionFormKey() && megaForms.includes(this.getFusionFormKey()!)) + ); } canAddTag(tagType: BattlerTagType): boolean { @@ -4806,35 +4121,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const stubTag = new BattlerTag(tagType, 0, 0); const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs( - BattlerTagImmunityAbAttr, - this, - stubTag, - cancelled, - true, - ); + applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, stubTag, cancelled, true); const userField = this.getAlliedField(); userField.forEach(pokemon => - applyPreApplyBattlerTagAbAttrs( - UserFieldBattlerTagImmunityAbAttr, - pokemon, - stubTag, - cancelled, - true, - this, - ), + applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, stubTag, cancelled, true, this), ); return !cancelled.value; } - addTag( - tagType: BattlerTagType, - turnCount = 0, - sourceMove?: Moves, - sourceId?: number, - ): boolean { + addTag(tagType: BattlerTagType, turnCount = 0, sourceMove?: Moves, sourceId?: number): boolean { const existingTag = this.getTag(tagType); if (existingTag) { existingTag.onOverlap(this); @@ -4844,25 +4141,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? const cancelled = new BooleanHolder(false); - applyPreApplyBattlerTagAbAttrs( - BattlerTagImmunityAbAttr, - this, - newTag, - cancelled, - ); + applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, newTag, cancelled); if (cancelled.value) { return false; } for (const pokemon of this.getAlliedField()) { - applyPreApplyBattlerTagAbAttrs( - UserFieldBattlerTagImmunityAbAttr, - pokemon, - newTag, - cancelled, - false, - this - ); + applyPreApplyBattlerTagAbAttrs(UserFieldBattlerTagImmunityAbAttr, pokemon, newTag, cancelled, false, this); if (cancelled.value) { return false; } @@ -4910,14 +4195,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const tags = this.summonData.tags; const tag = tags.find(t => t.tagType === tagType); if (!tag) { - return false + return false; } if (!tag.lapse(this, BattlerTagLapseType.CUSTOM)) { tag.onRemove(this); tags.splice(tags.indexOf(tag), 1); } - return true + return true; } /** @@ -4931,8 +4216,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { .filter( t => lapseType === BattlerTagLapseType.FAINT || - (t.lapseTypes.some(lType => lType === lapseType) && - !t.lapse(this, lapseType)), + (t.lapseTypes.some(lType => lType === lapseType) && !t.lapse(this, lapseType)), ) .forEach(t => { t.onRemove(this); @@ -4976,7 +4260,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (t.sourceId === sourceId) { t.sourceId = newSourceId; } - }) + }); } /** @@ -5036,21 +4320,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * @see {@linkcode MoveRestrictionBattlerTag} */ - isMoveTargetRestricted( - moveId: Moves, - user: Pokemon, - target: Pokemon, - ): boolean { - for (const tag of this.findTags( - t => t instanceof MoveRestrictionBattlerTag, - )) { - if ( - (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted( - moveId, - user, - target, - ) - ) { + isMoveTargetRestricted(moveId: Moves, user: Pokemon, target: Pokemon): boolean { + for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { + if ((tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) { return (tag as MoveRestrictionBattlerTag) !== null; } } @@ -5065,26 +4337,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param target - {@linkcode Pokemon} the target of the move, optional and used when the target is a factor in the move's restricted status * @returns The first tag on this Pokemon that restricts the move, or `null` if the move is not restricted. */ - getRestrictingTag( - moveId: Moves, - user?: Pokemon, - target?: Pokemon, - ): MoveRestrictionBattlerTag | null { - for (const tag of this.findTags( - t => t instanceof MoveRestrictionBattlerTag, - )) { + getRestrictingTag(moveId: Moves, user?: Pokemon, target?: Pokemon): MoveRestrictionBattlerTag | null { + for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId, user)) { return tag as MoveRestrictionBattlerTag; } - if ( - user && - target && - (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted( - moveId, - user, - target, - ) - ) { + if (user && target && (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) { return tag as MoveRestrictionBattlerTag; } } @@ -5114,9 +4372,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getLastXMoves(moveCount = 1): TurnMove[] { const moveHistory = this.getMoveHistory(); if (moveCount >= 0) { - return moveHistory - .slice(Math.max(moveHistory.length - moveCount, 0)) - .reverse(); + return moveHistory.slice(Math.max(moveHistory.length - moveCount, 0)).reverse(); } return moveHistory.slice(0).reverse(); } @@ -5142,39 +4398,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.loadAssets().then(() => { this.calculateStats(); globalScene.updateModifiers(this.isPlayer(), true); - Promise.all([this.updateInfo(), globalScene.updateFieldScale()]).then( - () => resolve(), - ); + Promise.all([this.updateInfo(), globalScene.updateFieldScale()]).then(() => resolve()); }); }); } - cry( - soundConfig?: Phaser.Types.Sound.SoundConfig, - sceneOverride?: BattleScene, - ): AnySound { + cry(soundConfig?: Phaser.Types.Sound.SoundConfig, sceneOverride?: BattleScene): AnySound { const scene = sceneOverride ?? globalScene; // TODO: is `sceneOverride` needed? const cry = this.getSpeciesForm(undefined, true).cry(soundConfig); let duration = cry.totalDuration * 1000; - if ( - this.fusionSpecies && - this.getSpeciesForm(undefined, true) !== this.getFusionSpeciesForm(undefined, true) - ) { + if (this.fusionSpecies && this.getSpeciesForm(undefined, true) !== this.getFusionSpeciesForm(undefined, true)) { let fusionCry = this.getFusionSpeciesForm(undefined, true).cry(soundConfig, true); duration = Math.min(duration, fusionCry.totalDuration * 1000); fusionCry.destroy(); scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { try { - SoundFade.fadeOut( - scene, - cry, - fixedInt(Math.ceil(duration * 0.2)), - ); + SoundFade.fadeOut(scene, cry, fixedInt(Math.ceil(duration * 0.2))); fusionCry = this.getFusionSpeciesForm(undefined, true).cry( - Object.assign( - { seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, - soundConfig, - ), + Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig), ); SoundFade.fadeIn( scene, @@ -5194,10 +4435,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // biome-ignore lint: there are a ton of issues.. faintCry(callback: Function): void { - if ( - this.fusionSpecies && - this.getSpeciesForm() !== this.getFusionSpeciesForm() - ) { + if (this.fusionSpecies && this.getSpeciesForm() !== this.getFusionSpeciesForm()) { return this.fusionFaintCry(callback); } @@ -5217,32 +4455,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sprite.anims.pause(); tintSprite?.anims.pause(); - let faintCryTimer: Phaser.Time.TimerEvent | null = - globalScene.time.addEvent({ - delay: fixedInt(delay), - repeat: -1, - callback: () => { - frameThreshold = sprite.anims.msPerFrame / rate; - frameProgress += delay; - while (frameProgress > frameThreshold) { - if (sprite.anims.duration) { - sprite.anims.nextFrame(); - tintSprite?.anims.nextFrame(); - } - frameProgress -= frameThreshold; + let faintCryTimer: Phaser.Time.TimerEvent | null = globalScene.time.addEvent({ + delay: fixedInt(delay), + repeat: -1, + callback: () => { + frameThreshold = sprite.anims.msPerFrame / rate; + frameProgress += delay; + while (frameProgress > frameThreshold) { + if (sprite.anims.duration) { + sprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } - if (cry && !cry.pendingRemove) { - rate *= 0.99; - cry.setRate(rate); - } else { - faintCryTimer?.destroy(); - faintCryTimer = null; - if (callback) { - callback(); - } + frameProgress -= frameThreshold; + } + if (cry && !cry.pendingRemove) { + rate *= 0.99; + cry.setRate(rate); + } else { + faintCryTimer?.destroy(); + faintCryTimer = null; + if (callback) { + callback(); } - }, - }); + } + }, + }); // Failsafe globalScene.time.delayedCall(fixedInt(3000), () => { @@ -5302,61 +4539,53 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sprite.anims.pause(); tintSprite?.anims.pause(); - let faintCryTimer: Phaser.Time.TimerEvent | null = - globalScene.time.addEvent({ - delay: fixedInt(delay), - repeat: -1, - callback: () => { - ++i; - frameThreshold = sprite.anims.msPerFrame / rate; - frameProgress += delay; - while (frameProgress > frameThreshold) { - if (sprite.anims.duration) { - sprite.anims.nextFrame(); - tintSprite?.anims.nextFrame(); - } - frameProgress -= frameThreshold; + let faintCryTimer: Phaser.Time.TimerEvent | null = globalScene.time.addEvent({ + delay: fixedInt(delay), + repeat: -1, + callback: () => { + ++i; + frameThreshold = sprite.anims.msPerFrame / rate; + frameProgress += delay; + while (frameProgress > frameThreshold) { + if (sprite.anims.duration) { + sprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } - if (i === transitionIndex && fusionCryKey) { - SoundFade.fadeOut( - globalScene, - cry, - fixedInt(Math.ceil((duration / rate) * 0.2)), - ); - fusionCry = globalScene.playSound( - fusionCryKey, - Object.assign({ - seek: Math.max(fusionCry.totalDuration * 0.4, 0), - rate: rate, - }), - ); - SoundFade.fadeIn( - globalScene, - fusionCry, - fixedInt(Math.ceil((duration / rate) * 0.2)), - globalScene.masterVolume * globalScene.fieldVolume, - 0, - ); + frameProgress -= frameThreshold; + } + if (i === transitionIndex && fusionCryKey) { + SoundFade.fadeOut(globalScene, cry, fixedInt(Math.ceil((duration / rate) * 0.2))); + fusionCry = globalScene.playSound( + fusionCryKey, + Object.assign({ + seek: Math.max(fusionCry.totalDuration * 0.4, 0), + rate: rate, + }), + ); + SoundFade.fadeIn( + globalScene, + fusionCry, + fixedInt(Math.ceil((duration / rate) * 0.2)), + globalScene.masterVolume * globalScene.fieldVolume, + 0, + ); + } + rate *= 0.99; + if (cry && !cry.pendingRemove) { + cry.setRate(rate); + } + if (fusionCry && !fusionCry.pendingRemove) { + fusionCry.setRate(rate); + } + if ((!cry || cry.pendingRemove) && (!fusionCry || fusionCry.pendingRemove)) { + faintCryTimer?.destroy(); + faintCryTimer = null; + if (callback) { + callback(); } - rate *= 0.99; - if (cry && !cry.pendingRemove) { - cry.setRate(rate); - } - if (fusionCry && !fusionCry.pendingRemove) { - fusionCry.setRate(rate); - } - if ( - (!cry || cry.pendingRemove) && - (!fusionCry || fusionCry.pendingRemove) - ) { - faintCryTimer?.destroy(); - faintCryTimer = null; - if (callback) { - callback(); - } - } - }, - }); + } + }, + }); // Failsafe globalScene.time.delayedCall(fixedInt(3000), () => { @@ -5379,8 +4608,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isOppositeGender(pokemon: Pokemon): boolean { return ( this.gender !== Gender.GENDERLESS && - pokemon.gender === - (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE) + pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE) ); } @@ -5388,11 +4616,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!effect || quiet) { return; } - const message = effect && this.status?.effect === effect - ? getStatusEffectOverlapText(effect ?? StatusEffect.NONE, getPokemonNameWithAffix(this)) - : i18next.t("abilityTriggers:moveImmunity", { - pokemonNameWithAffix: getPokemonNameWithAffix(this), - }); + const message = + effect && this.status?.effect === effect + ? getStatusEffectOverlapText(effect ?? StatusEffect.NONE, getPokemonNameWithAffix(this)) + : i18next.t("abilityTriggers:moveImmunity", { + pokemonNameWithAffix: getPokemonNameWithAffix(this), + }); globalScene.queueMessage(message); } @@ -5417,11 +4646,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.queueImmuneMessage(quiet, effect); return false; } - if ( - this.isGrounded() && - !ignoreField && - globalScene.arena.terrain?.terrainType === TerrainType.MISTY - ) { + if (this.isGrounded() && !ignoreField && globalScene.arena.terrain?.terrainType === TerrainType.MISTY) { this.queueImmuneMessage(quiet, effect); return false; } @@ -5431,7 +4656,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { switch (effect) { case StatusEffect.POISON: - case StatusEffect.TOXIC: + case StatusEffect.TOXIC: { // Check if the Pokemon is immune to Poison/Toxic or if the source pokemon is canceling the immunity const poisonImmunity = types.map(defType => { // Check if the Pokemon is not immune to Poison/Toxic @@ -5442,20 +4667,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity const cancelImmunity = new BooleanHolder(false); if (sourcePokemon) { - applyAbAttrs( - IgnoreTypeStatusEffectImmunityAbAttr, - sourcePokemon, - cancelImmunity, - false, - effect, - defType, - ); + applyAbAttrs(IgnoreTypeStatusEffectImmunityAbAttr, sourcePokemon, cancelImmunity, false, effect, defType); if (cancelImmunity.value) { return false; } } - return true; + return true; }); if (this.isOfType(PokemonType.POISON) || this.isOfType(PokemonType.STEEL)) { @@ -5465,6 +4683,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } break; + } case StatusEffect.PARALYSIS: if (this.isOfType(PokemonType.ELECTRIC)) { this.queueImmuneMessage(quiet, effect); @@ -5472,10 +4691,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } break; case StatusEffect.SLEEP: - if ( - this.isGrounded() && - globalScene.arena.terrain?.terrainType === TerrainType.ELECTRIC - ) { + if (this.isGrounded() && globalScene.arena.terrain?.terrainType === TerrainType.ELECTRIC) { this.queueImmuneMessage(quiet, effect); return false; } @@ -5485,9 +4701,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.isOfType(PokemonType.ICE) || (!ignoreField && globalScene?.arena?.weather?.weatherType && - [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes( - globalScene.arena.weather.weatherType, - )) + [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(globalScene.arena.weather.weatherType)) ) { this.queueImmuneMessage(quiet, effect); return false; @@ -5502,13 +4716,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const cancelled = new BooleanHolder(false); - applyPreSetStatusAbAttrs( - StatusEffectImmunityAbAttr, - this, - effect, - cancelled, - quiet, - ); + applyPreSetStatusAbAttrs(StatusEffectImmunityAbAttr, this, effect, cancelled, quiet); if (cancelled.value) { return false; } @@ -5519,8 +4727,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { pokemon, effect, cancelled, - quiet, this, sourcePokemon, - ) + quiet, + this, + sourcePokemon, + ); if (cancelled.value) { break; } @@ -5530,15 +4740,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - if ( - sourcePokemon && - sourcePokemon !== this && - this.isSafeguarded(sourcePokemon) - ) { - if(!quiet){ - globalScene.queueMessage( - i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) - })); + if (sourcePokemon && sourcePokemon !== this && this.isSafeguarded(sourcePokemon)) { + if (!quiet) { + globalScene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) })); } return false; } @@ -5579,13 +4783,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.resetStatus(false); } globalScene.unshiftPhase( - new ObtainStatusEffectPhase( - this.getBattlerIndex(), - effect, - turnsRemaining, - sourceText, - sourcePokemon, - ), + new ObtainStatusEffectPhase(this.getBattlerIndex(), effect, turnsRemaining, sourceText, sourcePokemon), ); return true; } @@ -5671,9 +4869,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns `true` if this Pokemon is protected by Safeguard; `false` otherwise. */ isSafeguarded(attacker: Pokemon): boolean { - const defendingSide = this.isPlayer() - ? ArenaTagSide.PLAYER - : ArenaTagSide.ENEMY; + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { const bypassed = new BooleanHolder(false); if (attacker) { @@ -5691,18 +4887,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public fieldSetup(resetSummonData?: boolean): void { this.setSwitchOutStatus(false); if (globalScene) { - globalScene.triggerPokemonFormChange( - this, - SpeciesFormChangePostMoveTrigger, - true, - ); + globalScene.triggerPokemonFormChange(this, SpeciesFormChangePostMoveTrigger, true); } // If this Pokemon has a Substitute when loading in, play an animation to add its sprite if (this.getTag(SubstituteTag)) { - globalScene.triggerPokemonBattleAnim( - this, - PokemonAnimType.SUBSTITUTE_ADD, - ); + globalScene.triggerPokemonBattleAnim(this, PokemonAnimType.SUBSTITUTE_ADD); this.getTag(SubstituteTag)!.sourceInFocus = false; } @@ -5732,7 +4921,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.summonData = new PokemonSummonData(); this.tempSummonData = new PokemonTempSummonData(); - this.summonData.illusion = illusion + this.summonData.illusion = illusion; this.updateInfo(); } @@ -5740,7 +4929,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Reset a {@linkcode Pokemon}'s per-battle {@linkcode PokemonBattleData | battleData}, * as well as any transient {@linkcode PokemonWaveData | waveData} for the current wave. * Should be called once per arena transition (new biome/trainer battle/Mystery Encounter). - */ + */ resetBattleAndWaveData(): void { this.battleData = new PokemonBattleData(); this.resetWaveData(); @@ -5761,10 +4950,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.stellarTypesBoosted = []; if (wasTerastallized) { this.updateSpritePipelineData(); - globalScene.triggerPokemonFormChange( - this, - SpeciesFormChangeLapseTeraTrigger, - ); + globalScene.triggerPokemonFormChange(this, SpeciesFormChangeLapseTeraTrigger); } } @@ -5782,18 +4968,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { try { this.getSprite().play(this.getBattleSpriteKey()); } catch (err: unknown) { - console.error( - `Failed to play animation for ${this.getBattleSpriteKey()}`, - err, - ); + console.error(`Failed to play animation for ${this.getBattleSpriteKey()}`, err); } try { this.getTintSprite()?.play(this.getBattleSpriteKey()); } catch (err: unknown) { - console.error( - `Failed to play animation for ${this.getBattleSpriteKey()}`, - err, - ); + console.error(`Failed to play animation for ${this.getBattleSpriteKey()}`, err); } } @@ -5844,9 +5024,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.x * this.parentContainer.scale + this.parentContainer.x, this.y * this.parentContainer.scale + this.parentContainer.y, ); - this.maskSprite?.setScale( - this.getSpriteScale() * this.parentContainer.scale, - ); + this.maskSprite?.setScale(this.getSpriteScale() * this.parentContainer.scale); this.maskEnabled = true; } } @@ -5872,12 +5050,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { [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${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; + s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = []; }); return; } @@ -5892,12 +5066,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.variant, ); const backSpriteKey = speciesForm - .getSpriteKey( - this.getGender(ignoreOveride) === Gender.FEMALE, - speciesForm.formIndex, - this.shiny, - this.variant, - ) + .getSpriteKey(this.getGender(ignoreOveride) === Gender.FEMALE, speciesForm.formIndex, this.shiny, this.variant) .replace("pkmn__", "pkmn__back__"); const fusionSpriteKey = fusionSpeciesForm.getSpriteKey( this.getFusionGender(ignoreOveride) === Gender.FEMALE, @@ -5940,40 +5109,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const spriteColors: number[][] = []; const pixelData: Uint8ClampedArray[] = []; - [canvas, backCanvas, fusionCanvas, fusionBackCanvas].forEach( - (canv: HTMLCanvasElement, c: number) => { - const context = canv.getContext("2d"); - const frame = [ - sourceFrame, - sourceBackFrame, - fusionFrame, - fusionBackFrame, - ][c]; - canv.width = frame.width; - canv.height = frame.height; + [canvas, backCanvas, fusionCanvas, fusionBackCanvas].forEach((canv: HTMLCanvasElement, c: number) => { + const context = canv.getContext("2d"); + const frame = [sourceFrame, sourceBackFrame, fusionFrame, fusionBackFrame][c]; + canv.width = frame.width; + canv.height = frame.height; - if (context) { - context.drawImage( - [sourceImage, sourceBackImage, fusionImage, fusionBackImage][c], - frame.cutX, - frame.cutY, - frame.width, - frame.height, - 0, - 0, - frame.width, - frame.height, - ); - const imageData = context.getImageData( - frame.cutX, - frame.cutY, - frame.width, - frame.height, - ); - pixelData.push(imageData.data); - } - }, - ); + if (context) { + context.drawImage( + [sourceImage, sourceBackImage, fusionImage, fusionBackImage][c], + frame.cutX, + frame.cutY, + frame.width, + frame.height, + 0, + 0, + frame.width, + frame.height, + ); + const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + pixelData.push(imageData.data); + } + }); for (let f = 0; f < 2; f++) { const variantColors = variantColorCache[!f ? spriteKey : backSpriteKey]; @@ -5982,9 +5139,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Object.keys(variantColors[this.variant]).forEach(k => { variantColorSet.set( rgbaToInt(Array.from(Object.values(rgbHexToRgba(k)))), - Array.from( - Object.values(rgbHexToRgba(variantColors[this.variant][k])), - ), + Array.from(Object.values(rgbHexToRgba(variantColors[this.variant][k]))), ); }); } @@ -6014,9 +5169,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const pixelColors: number[] = []; for (let f = 0; f < 2; f++) { for (let i = 0; i < pixelData[f].length; i += 4) { - const total = pixelData[f] - .slice(i, i + 3) - .reduce((total: number, value: number) => total + value, 0); + const total = pixelData[f].slice(i, i + 3).reduce((total: number, value: number) => total + value, 0); if (!total) { continue; } @@ -6033,29 +5186,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const fusionPixelColors: number[] = []; for (let f = 0; f < 2; f++) { - const variantColors = - variantColorCache[!f ? fusionSpriteKey : fusionBackSpriteKey]; + const variantColors = variantColorCache[!f ? fusionSpriteKey : fusionBackSpriteKey]; const variantColorSet = new Map(); - if ( - this.fusionShiny && - variantColors && - variantColors[this.fusionVariant] - ) { + if (this.fusionShiny && variantColors && variantColors[this.fusionVariant]) { for (const k of Object.keys(variantColors[this.fusionVariant])) { variantColorSet.set( rgbaToInt(Array.from(Object.values(rgbHexToRgba(k)))), - Array.from( - Object.values( - rgbHexToRgba(variantColors[this.fusionVariant][k]), - ), - ), + Array.from(Object.values(rgbHexToRgba(variantColors[this.fusionVariant][k]))), ); } } for (let i = 0; i < pixelData[2 + f].length; i += 4) { - const total = pixelData[2 + f] - .slice(i, i + 3) - .reduce((total: number, value: number) => total + value, 0); + const total = pixelData[2 + f].slice(i, i + 3).reduce((total: number, value: number) => total + value, 0); if (!total) { continue; } @@ -6103,101 +5245,94 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { paletteColors = paletteColors!; // erroneously tell TS compiler that paletteColors is defined! fusionPaletteColors = fusionPaletteColors!; // mischievously misinform TS compiler that fusionPaletteColors is defined! - const [palette, fusionPalette] = [paletteColors, fusionPaletteColors].map( - paletteColors => { - let keys = Array.from(paletteColors.keys()).sort( - (a: number, b: number) => - paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1, - ); - let rgbaColors: Map; - let hsvColors: Map; + const [palette, fusionPalette] = [paletteColors, fusionPaletteColors].map(paletteColors => { + let keys = Array.from(paletteColors.keys()).sort((a: number, b: number) => + paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1, + ); + let rgbaColors: Map; + let hsvColors: Map; - const mappedColors = new Map(); + const mappedColors = new Map(); - do { - mappedColors.clear(); + do { + mappedColors.clear(); - rgbaColors = keys.reduce((map: Map, k: number) => { - map.set(k, Object.values(rgbaFromArgb(k))); - return map; - }, new Map()); - hsvColors = Array.from(rgbaColors.keys()).reduce( - (map: Map, k: number) => { - const rgb = rgbaColors.get(k)!.slice(0, 3); - map.set(k, rgbToHsv(rgb[0], rgb[1], rgb[2])); - return map; - }, - new Map(), - ); + rgbaColors = keys.reduce((map: Map, k: number) => { + map.set(k, Object.values(rgbaFromArgb(k))); + return map; + }, new Map()); + hsvColors = Array.from(rgbaColors.keys()).reduce((map: Map, k: number) => { + const rgb = rgbaColors.get(k)!.slice(0, 3); + map.set(k, rgbToHsv(rgb[0], rgb[1], rgb[2])); + return map; + }, new Map()); - for (let c = keys.length - 1; c >= 0; c--) { - const hsv = hsvColors.get(keys[c])!; - for (let c2 = 0; c2 < c; c2++) { - const hsv2 = hsvColors.get(keys[c2])!; - const diff = Math.abs(hsv[0] - hsv2[0]); - if (diff < 30 || diff >= 330) { - if (mappedColors.has(keys[c])) { - mappedColors.get(keys[c])!.push(keys[c2]); - } else { - mappedColors.set(keys[c], [keys[c2]]); - } - break; + for (let c = keys.length - 1; c >= 0; c--) { + const hsv = hsvColors.get(keys[c])!; + for (let c2 = 0; c2 < c; c2++) { + const hsv2 = hsvColors.get(keys[c2])!; + const diff = Math.abs(hsv[0] - hsv2[0]); + if (diff < 30 || diff >= 330) { + if (mappedColors.has(keys[c])) { + mappedColors.get(keys[c])!.push(keys[c2]); + } else { + mappedColors.set(keys[c], [keys[c2]]); } + break; + } + } + } + + mappedColors.forEach((values: number[], key: number) => { + const keyColor = rgbaColors.get(key)!; + const valueColors = values.map(v => rgbaColors.get(v)!); + const color = keyColor.slice(0); + let count = paletteColors.get(key)!; + for (const value of values) { + const valueCount = paletteColors.get(value); + if (!valueCount) { + continue; + } + count += valueCount; + } + + for (let c = 0; c < 3; c++) { + color[c] *= paletteColors.get(key)! / count; + values.forEach((value: number, i: number) => { + if (paletteColors.has(value)) { + const valueCount = paletteColors.get(value)!; + color[c] += valueColors[i][c] * (valueCount / count); + } + }); + color[c] = Math.round(color[c]); + } + + paletteColors.delete(key); + for (const value of values) { + paletteColors.delete(value); + if (mappedColors.has(value)) { + mappedColors.delete(value); } } - mappedColors.forEach((values: number[], key: number) => { - const keyColor = rgbaColors.get(key)!; - const valueColors = values.map(v => rgbaColors.get(v)!); - const color = keyColor.slice(0); - let count = paletteColors.get(key)!; - for (const value of values) { - const valueCount = paletteColors.get(value); - if (!valueCount) { - continue; - } - count += valueCount; - } - - for (let c = 0; c < 3; c++) { - color[c] *= paletteColors.get(key)! / count; - values.forEach((value: number, i: number) => { - if (paletteColors.has(value)) { - const valueCount = paletteColors.get(value)!; - color[c] += valueColors[i][c] * (valueCount / count); - } - }); - color[c] = Math.round(color[c]); - } - - paletteColors.delete(key); - for (const value of values) { - paletteColors.delete(value); - if (mappedColors.has(value)) { - mappedColors.delete(value); - } - } - - paletteColors.set( - argbFromRgba({ - r: color[0], - g: color[1], - b: color[2], - a: color[3], - }), - count, - ); - }); - - keys = Array.from(paletteColors.keys()).sort( - (a: number, b: number) => - paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1, + paletteColors.set( + argbFromRgba({ + r: color[0], + g: color[1], + b: color[2], + a: color[3], + }), + count, ); - } while (mappedColors.size); + }); - return keys.map(c => Object.values(rgbaFromArgb(c))); - }, - ); + keys = Array.from(paletteColors.keys()).sort((a: number, b: number) => + paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1, + ); + } while (mappedColors.size); + + return keys.map(c => Object.values(rgbaFromArgb(c))); + }); const paletteDeltas: number[][] = []; @@ -6220,10 +5355,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const ratio = easeFunc(delta / 255); const color = [0, 0, 0, fusionSpriteColors[sc][3]]; for (let c = 0; c < 3; c++) { - color[c] = Math.round( - fusionSpriteColors[sc][c] * ratio + - fusionPalette[paletteIndex][c] * (1 - ratio), - ); + color[c] = Math.round(fusionSpriteColors[sc][c] * ratio + fusionPalette[paletteIndex][c] * (1 - ratio)); } fusionSpriteColors[sc] = color; } @@ -6232,12 +5364,9 @@ export default 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" : ""}` - ] = fusionSpriteColors; + s.pipelineData[`spriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = spriteColors; + s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData.speciesForm ? "Base" : ""}`] = + fusionSpriteColors; }); canvas.remove(); @@ -6257,9 +5386,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) */ randBattleSeedInt(range: number, min = 0): number { - return globalScene.currentBattle - ? globalScene.randBattleSeedInt(range, min) - : randSeedInt(range, min); + return globalScene.currentBattle ? globalScene.randBattleSeedInt(range, min) : randSeedInt(range, min); } /** @@ -6269,9 +5396,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns a random integer between {@linkcode min} and {@linkcode max} inclusive */ randBattleSeedIntRange(min: number, max: number): number { - return globalScene.currentBattle - ? globalScene.randBattleSeedInt(max - min + 1, min) - : randSeedIntRange(min, max); + return globalScene.currentBattle ? globalScene.randBattleSeedInt(max - min + 1, min) : randSeedIntRange(min, max); } /** @@ -6299,11 +5424,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Trigger abilities that activate upon leaving the field applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, this); this.setSwitchOutStatus(true); - globalScene.triggerPokemonFormChange( - this, - SpeciesFormChangeActiveTrigger, - true, - ); + globalScene.triggerPokemonFormChange(this, SpeciesFormChangeActiveTrigger, true); globalScene.field.remove(this, destroy); } @@ -6325,10 +5446,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { hasSameAbilityInRootForm(abilityIndex: number): boolean { const currentAbilityIndex = this.abilityIndex; const rootForm = getPokemonSpecies(this.species.getRootSpeciesId()); - return ( - rootForm.getAbility(abilityIndex) === - rootForm.getAbility(currentAbilityIndex) - ); + return rootForm.getAbility(abilityIndex) === rootForm.getAbility(currentAbilityIndex); } /** @@ -6357,23 +5475,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param forBattle If `false`, do not trigger in-battle effects (such as Unburden) from losing the item. For example, set this to `false` if the Pokemon is giving away the held item for a Mystery Encounter. Default is `true`. * @returns `true` if the item was removed successfully, `false` otherwise. */ - public loseHeldItem( - heldItem: PokemonHeldItemModifier, - forBattle = true, - ): boolean { + public loseHeldItem(heldItem: PokemonHeldItemModifier, forBattle = true): boolean { if (heldItem.pokemonId !== -1 && heldItem.pokemonId !== this.id) { return false; } - heldItem.stackCount--; - if (heldItem.stackCount <= 0) { - globalScene.removeModifier(heldItem, !this.isPlayer()); - } - if (forBattle) { - applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false); - } + heldItem.stackCount--; + if (heldItem.stackCount <= 0) { + globalScene.removeModifier(heldItem, !this.isPlayer()); + } + if (forBattle) { + applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false); + } - return true; + return true; } /** @@ -6382,11 +5497,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param berryType The type of berry being eaten. * @param updateHarvest Whether to track the berry for harvest; default `true`. */ - public recordEatenBerry(berryType: BerryType, updateHarvest: boolean = true) { + public recordEatenBerry(berryType: BerryType, updateHarvest = true) { this.battleData.hasEatenBerry = true; if (updateHarvest) { // Only track for harvest if we actually consumed the berry - this.battleData.berriesEaten.push(berryType) + this.battleData.berriesEaten.push(berryType); } this.turnData.berriesEaten.push(berryType); } @@ -6407,20 +5522,7 @@ export class PlayerPokemon extends Pokemon { nature?: Nature, dataSource?: Pokemon | PokemonData, ) { - super( - 106, - 148, - species, - level, - abilityIndex, - formIndex, - gender, - shiny, - variant, - ivs, - nature, - dataSource, - ); + super(106, 148, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); if (Overrides.STATUS_OVERRIDE) { this.status = new Status(Overrides.STATUS_OVERRIDE, 0, 4); @@ -6483,17 +5585,13 @@ export class PlayerPokemon extends Pokemon { if (Array.isArray(p)) { const [pkm, form] = p; if ( - (pkm === this.species.speciesId || - (this.fusionSpecies && pkm === this.fusionSpecies.speciesId)) && + (pkm === this.species.speciesId || (this.fusionSpecies && pkm === this.fusionSpecies.speciesId)) && form === this.getFormKey() ) { compatible = true; break; } - } else if ( - p === this.species.speciesId || - (this.fusionSpecies && p === this.fusionSpecies.speciesId) - ) { + } else if (p === this.species.speciesId || (this.fusionSpecies && p === this.fusionSpecies.speciesId)) { compatible = true; break; } @@ -6511,8 +5609,7 @@ export class PlayerPokemon extends Pokemon { if ( !this.getSpeciesForm().validateStarterMoveset( moveset, - globalScene.gameData.starterData[this.species.getRootSpeciesId()] - .eggMoves, + globalScene.gameData.starterData[this.species.getRootSpeciesId()].eggMoves, ) ) { return false; @@ -6538,18 +5635,10 @@ export class PlayerPokemon extends Pokemon { UiMode.PARTY, PartyUiMode.FAINT_SWITCH, this.getFieldIndex(), - (slotIndex: number, option: PartyOption) => { - if ( - slotIndex >= globalScene.currentBattle.getBattlerCount() && - slotIndex < 6 - ) { + (slotIndex: number, _option: PartyOption) => { + if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { globalScene.prependToPhase( - new SwitchSummonPhase( - switchType, - this.getFieldIndex(), - slotIndex, - false, - ), + new SwitchSummonPhase(switchType, this.getFieldIndex(), slotIndex, false), MoveEndPhase, ); } @@ -6563,23 +5652,13 @@ export class PlayerPokemon extends Pokemon { addFriendship(friendship: number): void { if (friendship > 0) { const starterSpeciesId = this.species.getRootSpeciesId(); - const fusionStarterSpeciesId = - this.isFusion() && this.fusionSpecies - ? this.fusionSpecies.getRootSpeciesId() - : 0; + const fusionStarterSpeciesId = this.isFusion() && this.fusionSpecies ? this.fusionSpecies.getRootSpeciesId() : 0; const starterData = [ globalScene.gameData.starterData[starterSpeciesId], - fusionStarterSpeciesId - ? globalScene.gameData.starterData[fusionStarterSpeciesId] - : null, + fusionStarterSpeciesId ? globalScene.gameData.starterData[fusionStarterSpeciesId] : null, ].filter(d => !!d); const amount = new NumberHolder(friendship); - globalScene.applyModifier( - PokemonFriendshipBoosterModifier, - true, - this, - amount, - ); + globalScene.applyModifier(PokemonFriendshipBoosterModifier, true, this, amount); const candyFriendshipMultiplier = globalScene.gameMode.isClassic ? timedEventManager.getClassicFriendshipMultiplier() : 1; @@ -6588,11 +5667,7 @@ export class PlayerPokemon extends Pokemon { ? 1.5 // Divide candy gain for fusions by 1.5 during events : 2 // 2 for fusions outside events : 1; // 1 for non-fused mons - const starterAmount = new NumberHolder( - Math.floor( - (amount.value * candyFriendshipMultiplier) / fusionReduction, - ), - ); + const starterAmount = new NumberHolder(Math.floor((amount.value * candyFriendshipMultiplier) / fusionReduction)); // Add friendship to this PlayerPokemon this.friendship = Math.min(this.friendship + amount.value, 255); @@ -6601,14 +5676,9 @@ export class PlayerPokemon extends Pokemon { } // Add to candy progress for this mon's starter species and its fused species (if it has one) starterData.forEach((sd: StarterDataEntry, i: number) => { - const speciesId = !i - ? starterSpeciesId - : (fusionStarterSpeciesId as Species); + const speciesId = !i ? starterSpeciesId : (fusionStarterSpeciesId as Species); sd.friendship = (sd.friendship || 0) + starterAmount.value; - if ( - sd.friendship >= - getStarterValueFriendshipCap(speciesStarterCosts[speciesId]) - ) { + if (sd.friendship >= getStarterValueFriendshipCap(speciesStarterCosts[speciesId])) { globalScene.gameData.addStarterCandy(getPokemonSpecies(speciesId), 1); sd.friendship = 0; } @@ -6619,9 +5689,7 @@ export class PlayerPokemon extends Pokemon { } } - getPossibleEvolution( - evolution: SpeciesFormEvolution | null, - ): Promise { + getPossibleEvolution(evolution: SpeciesFormEvolution | null): Promise { if (!evolution) { return new Promise(resolve => resolve(this)); } @@ -6636,9 +5704,7 @@ export class PlayerPokemon extends Pokemon { this.fusionFormIndex = evolution.evoFormKey !== null ? Math.max( - evolutionSpecies.forms.findIndex( - f => f.formKey === evolution.evoFormKey, - ), + evolutionSpecies.forms.findIndex(f => f.formKey === evolution.evoFormKey), 0, ) : this.fusionFormIndex; @@ -6660,9 +5726,7 @@ export class PlayerPokemon extends Pokemon { const formIndex = evolution.evoFormKey !== null && !isFusion ? Math.max( - evolutionSpecies.forms.findIndex( - f => f.formKey === evolution.evoFormKey, - ), + evolutionSpecies.forms.findIndex(f => f.formKey === evolution.evoFormKey), 0, ) : this.formIndex; @@ -6683,10 +5747,7 @@ export class PlayerPokemon extends Pokemon { }); } - evolve( - evolution: SpeciesFormEvolution | null, - preEvolution: PokemonSpeciesForm, - ): Promise { + evolve(evolution: SpeciesFormEvolution | null, preEvolution: PokemonSpeciesForm): Promise { if (!evolution) { return new Promise(resolve => resolve()); } @@ -6702,10 +5763,9 @@ export class PlayerPokemon extends Pokemon { } if (evolution.preFormKey !== null) { const formIndex = Math.max( - (!isFusion || !this.fusionSpecies - ? this.species - : this.fusionSpecies - ).forms.findIndex(f => f.formKey === evolution.evoFormKey), + (!isFusion || !this.fusionSpecies ? this.species : this.fusionSpecies).forms.findIndex( + f => f.formKey === evolution.evoFormKey, + ), 0, ); if (!isFusion) { @@ -6720,18 +5780,12 @@ export class PlayerPokemon extends Pokemon { const preEvoAbilityCount = preEvolution.getAbilityCount(); if ([0, 1, 2].includes(this.abilityIndex)) { // Handles cases where a Pokemon with 3 abilities evolves into a Pokemon with 2 abilities (ie: Eevee -> any Eeveelution) - if ( - this.abilityIndex === 2 && - preEvoAbilityCount === 3 && - abilityCount === 2 - ) { + if (this.abilityIndex === 2 && preEvoAbilityCount === 3 && abilityCount === 2) { this.abilityIndex = 1; } } else { // Prevent pokemon with an illegal ability value from breaking things - console.warn( - "this.abilityIndex is somehow an illegal value, please report this", - ); + console.warn("this.abilityIndex is somehow an illegal value, please report this"); console.warn(this.abilityIndex); this.abilityIndex = 0; } @@ -6740,17 +5794,11 @@ export class PlayerPokemon extends Pokemon { const abilityCount = this.getFusionSpeciesForm().getAbilityCount(); const preEvoAbilityCount = preEvolution.getAbilityCount(); if ([0, 1, 2].includes(this.fusionAbilityIndex)) { - if ( - this.fusionAbilityIndex === 2 && - preEvoAbilityCount === 3 && - abilityCount === 2 - ) { + if (this.fusionAbilityIndex === 2 && preEvoAbilityCount === 3 && abilityCount === 2) { this.fusionAbilityIndex = 1; } } else { - console.warn( - "this.fusionAbilityIndex is somehow an illegal value, please report this", - ); + console.warn("this.fusionAbilityIndex is somehow an illegal value, please report this"); console.warn(this.fusionAbilityIndex); this.fusionAbilityIndex = 0; } @@ -6764,20 +5812,15 @@ export class PlayerPokemon extends Pokemon { }); }; if (preEvolution.speciesId === Species.GIMMIGHOUL) { - const evotracker = this.getHeldItems().find(m => m instanceof EvoTrackerModifier); - if (!isNullOrUndefined(evotracker)) { + const evotracker = this.getHeldItems().filter(m => m instanceof EvoTrackerModifier)[0] ?? null; + if (evotracker) { globalScene.removeModifier(evotracker); } } if (!globalScene.gameMode.isDaily || this.metBiome > -1) { - globalScene.gameData.updateSpeciesDexIvs( - this.species.speciesId, - this.ivs, - ); + globalScene.gameData.updateSpeciesDexIvs(this.species.speciesId, this.ivs); globalScene.gameData.setPokemonSeen(this, false); - globalScene.gameData - .setPokemonCaught(this, false) - .then(() => updateAndResolve()); + globalScene.gameData.setPokemonCaught(this, false).then(() => updateAndResolve()); } else { updateAndResolve(); } @@ -6788,10 +5831,7 @@ export class PlayerPokemon extends Pokemon { const isFusion = evolution instanceof FusionSpeciesFormEvolution; const evoSpecies = !isFusion ? this.species : this.fusionSpecies; - if ( - evoSpecies?.speciesId === Species.NINCADA && - evolution.speciesId === Species.NINJASK - ) { + if (evoSpecies?.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) { const newEvolution = pokemonEvolutions[evoSpecies.speciesId][1]; if (validateShedinjaEvo()) { @@ -6826,12 +5866,7 @@ export class PlayerPokemon extends Pokemon { newPokemon.usedTMs = this.usedTMs; globalScene.getPlayerParty().push(newPokemon); - newPokemon.evolve( - !isFusion - ? newEvolution - : new FusionSpeciesFormEvolution(this.id, newEvolution), - evoSpecies, - ); + newPokemon.evolve(!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution), evoSpecies); const modifiers = globalScene.findModifiers( m => m instanceof PokemonHeldItemModifier && m.pokemonId === this.id, true, @@ -6892,9 +5927,7 @@ export class PlayerPokemon extends Pokemon { }; if (!globalScene.gameMode.isDaily || this.metBiome > -1) { globalScene.gameData.setPokemonSeen(this, false); - globalScene.gameData - .setPokemonCaught(this, false) - .then(() => updateAndResolve()); + globalScene.gameData.setPokemonCaught(this, false).then(() => updateAndResolve()); } else { updateAndResolve(); } @@ -6928,8 +5961,7 @@ export class PlayerPokemon extends Pokemon { // Store the average HP% that each Pokemon has const maxHp = this.getMaxHp(); - const newHpPercent = - (pokemon.hp / pokemon.getMaxHp() + this.hp / maxHp) / 2; + const newHpPercent = (pokemon.hp / pokemon.getMaxHp() + this.hp / maxHp) / 2; this.generateName(); this.calculateStats(); @@ -6960,15 +5992,7 @@ export class PlayerPokemon extends Pokemon { true, ) as PokemonHeldItemModifier[]; for (const modifier of fusedPartyMemberHeldModifiers) { - globalScene.tryTransferHeldItemModifier( - modifier, - this, - false, - modifier.getStackCount(), - true, - true, - false, - ); + globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false); } globalScene.updateModifiers(true, true); globalScene.removePartyMemberModifiers(fusedPartyMemberIndex); @@ -6976,11 +6000,7 @@ export class PlayerPokemon extends Pokemon { const newPartyMemberIndex = globalScene.getPlayerParty().indexOf(this); pokemon .getMoveset(true) - .map((m: PokemonMove) => - globalScene.unshiftPhase( - new LearnMovePhase(newPartyMemberIndex, m.getMove().id), - ), - ); + .map((m: PokemonMove) => globalScene.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id))); pokemon.destroy(); this.updateFusionPalette(); } @@ -7082,17 +6102,13 @@ export class EnemyPokemon extends Pokemon { } } - this.luck = - (this.shiny ? this.variant + 1 : 0) + - (this.fusionShiny ? this.fusionVariant + 1 : 0); + this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0); let prevolution: Species; let speciesId = species.speciesId; while ((prevolution = pokemonPrevolutions[speciesId])) { const evolution = pokemonEvolutions[prevolution].find( - pe => - pe.speciesId === speciesId && - (!pe.evoFormKey || pe.evoFormKey === this.getFormKey()), + pe => pe.speciesId === speciesId && (!pe.evoFormKey || pe.evoFormKey === this.getFormKey()), ); speciesId = prevolution; } @@ -7107,8 +6123,7 @@ export class EnemyPokemon extends Pokemon { } } - this.aiType = - boss || this.hasTrainer() ? AiType.SMART : AiType.SMART_RANDOM; + this.aiType = boss || this.hasTrainer() ? AiType.SMART : AiType.SMART_RANDOM; } initBattleInfo(): void { @@ -7132,12 +6147,7 @@ export class EnemyPokemon extends Pokemon { if (boss) { this.bossSegments = bossSegments || - globalScene.getEncounterBossSegments( - globalScene.currentBattle.waveIndex, - this.level, - this.species, - true, - ); + globalScene.getEncounterBossSegments(globalScene.currentBattle.waveIndex, this.level, this.species, true); this.bossSegmentIndex = this.bossSegments - 1; } else { this.bossSegments = 0; @@ -7191,12 +6201,14 @@ export class EnemyPokemon extends Pokemon { const queuedMove = moveQueue[0]; if (queuedMove) { const moveIndex = this.getMoveset().findIndex(m => m.moveId === queuedMove.move); - if ((moveIndex > -1 && this.getMoveset()[moveIndex].isUsable(this, queuedMove.ignorePP)) || queuedMove.virtual) { + if ( + (moveIndex > -1 && this.getMoveset()[moveIndex].isUsable(this, queuedMove.ignorePP)) || + queuedMove.virtual + ) { return queuedMove; - } else { - this.getMoveQueue().shift(); - return this.getNextMove(); } + this.getMoveQueue().shift(); + return this.getNextMove(); } } @@ -7220,11 +6232,13 @@ export class EnemyPokemon extends Pokemon { } } switch (this.aiType) { - case AiType.RANDOM: // No enemy should spawn with this AI type in-game + // No enemy should spawn with this AI type in-game + case AiType.RANDOM: { const moveId = movePool[globalScene.randBattleSeedInt(movePool.length)].moveId; return { move: moveId, targets: this.getNextTargets(moveId) }; + } case AiType.SMART_RANDOM: - case AiType.SMART: + case AiType.SMART: { /** * Search this Pokemon's move pool for moves that will KO an opposing target. * If there are any moves that can KO an opponent (i.e. a player Pokemon), @@ -7245,20 +6259,14 @@ export class EnemyPokemon extends Pokemon { .targets.map(ind => fieldPokemon[ind]) .filter(p => this.isPlayer() !== p.isPlayer()); // Only considers critical hits for crit-only moves or when this Pokemon is under the effect of Laser Focus - const isCritical = - move.hasAttr(CritOnlyAttr) || - !!this.getTag(BattlerTagType.ALWAYS_CRIT); + const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT); return ( move.category !== MoveCategory.STATUS && moveTargets.some(p => { const doesNotFail = move.applyConditions(this, p, move) || - [ - Moves.SUCKER_PUNCH, - Moves.UPPER_HAND, - Moves.THUNDERCLAP, - ].includes(move.id); + [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id); return ( doesNotFail && p.getAttackDamage({ @@ -7269,8 +6277,7 @@ export class EnemyPokemon extends Pokemon { ignoreAllyAbility: !p.getAlly()?.waveData.abilityRevealed, ignoreSourceAllyAbility: false, isCritical, - } - ).damage >= p.hp + }).damage >= p.hp ); }) ); @@ -7286,7 +6293,7 @@ export class EnemyPokemon extends Pokemon { * For more information on how benefit scores are calculated, see `docs/enemy-ai.md`. */ const moveScores = movePool.map(() => 0); - const moveTargets = Object.fromEntries(movePool.map(m => [ m.moveId, this.getNextTargets(m.moveId) ])); + const moveTargets = Object.fromEntries(movePool.map(m => [m.moveId, this.getNextTargets(m.moveId)])); for (const m in movePool) { const pokemonMove = movePool[m]; const move = pokemonMove.getMove(); @@ -7307,8 +6314,7 @@ export class EnemyPokemon extends Pokemon { */ let targetScore = move.getUserBenefitScore(this, target, move) + - move.getTargetBenefitScore(this, target, move) * - (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1); + move.getTargetBenefitScore(this, target, move) * (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1); if (Number.isNaN(targetScore)) { console.error(`Move ${move.name} returned score of NaN`); targetScore = 0; @@ -7318,27 +6324,23 @@ export class EnemyPokemon extends Pokemon { * target score to -20 */ if ( - (move.name.endsWith(" (N)") || - !move.applyConditions(this, target, move)) && - ![ - Moves.SUCKER_PUNCH, - Moves.UPPER_HAND, - Moves.THUNDERCLAP, - ].includes(move.id) + (move.name.endsWith(" (N)") || !move.applyConditions(this, target, move)) && + ![Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id) ) { targetScore = -20; } else if (move instanceof AttackMove) { /** * Attack moves are given extra multipliers to their base benefit score based on * the move's type effectiveness against the target and whether the move is a STAB move. - */ + */ const effectiveness = target.getMoveEffectiveness( this, move, !target.waveData.abilityRevealed, undefined, undefined, - true); + true, + ); if (target.isPlayer() !== this.isPlayer()) { targetScore *= effectiveness; @@ -7377,18 +6379,14 @@ export class EnemyPokemon extends Pokemon { let r = 0; if (this.aiType === AiType.SMART_RANDOM) { // Has a 5/8 chance to select the best move, and a 3/8 chance to advance to the next best move (and repeat this roll) - while ( - r < sortedMovePool.length - 1 && - globalScene.randBattleSeedInt(8) >= 5 - ) { + while (r < sortedMovePool.length - 1 && globalScene.randBattleSeedInt(8) >= 5) { r++; } } else if (this.aiType === AiType.SMART) { // The chance to advance to the next best move increases when the compared moves' scores are closer to each other. while ( r < sortedMovePool.length - 1 && - moveScores[movePool.indexOf(sortedMovePool[r + 1])] / - moveScores[movePool.indexOf(sortedMovePool[r])] >= + moveScores[movePool.indexOf(sortedMovePool[r + 1])] / moveScores[movePool.indexOf(sortedMovePool[r])] >= 0 && globalScene.randBattleSeedInt(100) < Math.round( @@ -7400,8 +6398,14 @@ export class EnemyPokemon extends Pokemon { r++; } } - console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName())); + console.log( + movePool.map(m => m.getName()), + moveScores, + r, + sortedMovePool.map(m => m.getName()), + ); return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] }; + } } } @@ -7418,9 +6422,7 @@ export class EnemyPokemon extends Pokemon { */ getNextTargets(moveId: Moves): BattlerIndex[] { const moveTargets = getMoveTargets(this, moveId); - const targets = globalScene - .getField(true) - .filter(p => moveTargets.targets.indexOf(p.getBattlerIndex()) > -1); + const targets = globalScene.getField(true).filter(p => moveTargets.targets.indexOf(p.getBattlerIndex()) > -1); // If the move is multi-target, return all targets' indexes if (moveTargets.multiple) { return targets.map(p => p.getBattlerIndex()); @@ -7434,8 +6436,7 @@ export class EnemyPokemon extends Pokemon { */ const benefitScores = targets.map(p => [ p.getBattlerIndex(), - move.getTargetBenefitScore(this, p, move) * - (p.isPlayer() === this.isPlayer() ? 1 : -1), + move.getTargetBenefitScore(this, p, move) * (p.isPlayer() === this.isPlayer() ? 1 : -1), ]); const sortedBenefitScores = benefitScores.slice(0); @@ -7466,9 +6467,7 @@ export class EnemyPokemon extends Pokemon { } // Remove any targets whose weights are less than half the max of the target weights from consideration - const benefitCutoffIndex = targetWeights.findIndex( - s => s < targetWeights[0] / 2, - ); + const benefitCutoffIndex = targetWeights.findIndex(s => s < targetWeights[0] / 2); if (benefitCutoffIndex > -1) { targetWeights = targetWeights.slice(0, benefitCutoffIndex); } @@ -7527,12 +6526,7 @@ export class EnemyPokemon extends Pokemon { return 0; } - damage( - damage: number, - ignoreSegments = false, - preventEndure = false, - ignoreFaintPhase = false, - ): number { + damage(damage: number, ignoreSegments = false, preventEndure = false, ignoreFaintPhase = false): number { if (this.isFainted()) { return 0; } @@ -7551,16 +6545,13 @@ export class EnemyPokemon extends Pokemon { while ( segmentsBypassed < this.bossSegmentIndex && this.canBypassBossSegments(segmentsBypassed + 1) && - damage - hpRemainder >= - Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1)) + damage - hpRemainder >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1)) ) { segmentsBypassed++; //console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))); } - damage = toDmgValue( - this.hp - hpThreshold + segmentSize * segmentsBypassed, - ); + damage = toDmgValue(this.hp - hpThreshold + segmentSize * segmentsBypassed); clearedBossSegmentIndex = s - segmentsBypassed; } break; @@ -7575,12 +6566,7 @@ export class EnemyPokemon extends Pokemon { } } - const ret = super.damage( - damage, - ignoreSegments, - preventEndure, - ignoreFaintPhase, - ); + const ret = super.damage(damage, ignoreSegments, preventEndure, ignoreFaintPhase); if (this.isBoss()) { if (ignoreSegments) { @@ -7614,17 +6600,10 @@ export class EnemyPokemon extends Pokemon { * @param segmentIndex index of the segment to get down to (0 = no shield left, 1 = 1 shield left, etc.) */ handleBossSegmentCleared(segmentIndex: number): void { - while ( - this.bossSegmentIndex > 0 && - segmentIndex - 1 < this.bossSegmentIndex - ) { + while (this.bossSegmentIndex > 0 && segmentIndex - 1 < this.bossSegmentIndex) { // Filter out already maxed out stat stages and weigh the rest based on existing stats - const leftoverStats = EFFECTIVE_STATS.filter( - (s: EffectiveStat) => this.getStatStage(s) < 6, - ); - const statWeights = leftoverStats.map((s: EffectiveStat) => - this.getStat(s, false), - ); + const leftoverStats = EFFECTIVE_STATS.filter((s: EffectiveStat) => this.getStatStage(s) < 6); + const statWeights = leftoverStats.map((s: EffectiveStat) => this.getStat(s, false)); let boostedStat: EffectiveStat; const statThresholds: number[] = []; @@ -7656,14 +6635,7 @@ export class EnemyPokemon extends Pokemon { } globalScene.unshiftPhase( - new StatStageChangePhase( - this.getBattlerIndex(), - true, - [boostedStat!], - stages, - true, - true, - ), + new StatStageChangePhase(this.getBattlerIndex(), true, [boostedStat!], stages, true, true), ); this.bossSegmentIndex--; } @@ -7717,11 +6689,7 @@ export class EnemyPokemon extends Pokemon { newPokemon.setVisible(false); ret = newPokemon; - globalScene.triggerPokemonFormChange( - newPokemon, - SpeciesFormChangeActiveTrigger, - true, - ); + globalScene.triggerPokemonFormChange(newPokemon, SpeciesFormChangeActiveTrigger, true); } return ret; @@ -7761,7 +6729,7 @@ interface IllusionData { /** The fusionGender of the illusion if it's a fusion */ fusionGender?: Gender; /** The level of the illusion (not used currently) */ - level?: number + level?: number; } export interface TurnMove { @@ -7810,7 +6778,7 @@ export class PokemonSummonData { /** Data pertaining to this pokemon's illusion. */ public illusion: IllusionData | null = null; - public illusionBroken: boolean = false; + public illusionBroken = false; /** Array containing all berries eaten in the last turn; used by {@linkcode Abilities.CUD_CHEW} */ public berriesEatenLast: BerryType[] = []; @@ -7847,15 +6815,15 @@ export class PokemonSummonData { } } - // TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added +// TODO: Merge this inside `summmonData` but exclude from save if/when a save data serializer is added export class PokemonTempSummonData { /** * The number of turns this pokemon has spent without switching out. * Only currently used for positioning the battle cursor. */ - turnCount: number = 1; + turnCount = 1; - /** + /** * The number of turns this pokemon has spent in the active position since the start of the wave * without switching out. * Reset on switch and new wave, but not stored in `SummonData` to avoid being written to the save file. @@ -7864,7 +6832,6 @@ export class PokemonTempSummonData { * {@linkcode Moves.FAKE_OUT | Fake Out} and {@linkcode Moves.FIRST_IMPRESSION | First Impression}). */ waveTurnCount = 1; - } /** @@ -7875,7 +6842,7 @@ export class PokemonBattleData { /** Counter tracking direct hits this Pokemon has received during this battle; used for {@linkcode Moves.RAGE_FIST} */ public hitCount = 0; /** Whether this Pokemon has eaten a berry this battle; used for {@linkcode Moves.BELCH} */ - public hasEatenBerry: boolean = false; + public hasEatenBerry = false; /** Array containing all berries eaten and not yet recovered during this current battle; used by {@linkcode Abilities.HARVEST} */ public berriesEaten: BerryType[] = []; @@ -7899,7 +6866,7 @@ export class PokemonWaveData { * A set of all the abilities this {@linkcode Pokemon} has used in this wave. * Used to track once per battle conditions, as well as (hopefully) by the updated AI for move effectiveness. */ - public abilitiesApplied: Set = new Set; + public abilitiesApplied: Set = new Set(); /** Whether the pokemon's ability has been revealed or not */ public abilityRevealed = false; } @@ -7939,8 +6906,8 @@ export class PokemonTurnData { * All berries eaten by this pokemon in this turn. * Saved into {@linkcode PokemonSummonData | SummonData} by {@linkcode Abilities.CUD_CHEW} on turn end. * @see {@linkcode PokemonSummonData.berriesEatenLast} - */ - public berriesEaten: BerryType[] = [] + */ + public berriesEaten: BerryType[] = []; } export enum AiType { @@ -8017,13 +6984,7 @@ export class PokemonMove { */ public maxPpOverride?: number; - constructor( - moveId: Moves, - ppUsed = 0, - ppUp = 0, - virtual = false, - maxPpOverride?: number, - ) { + constructor(moveId: Moves, ppUsed = 0, ppUp = 0, virtual = false, maxPpOverride?: number) { this.moveId = moveId; this.ppUsed = ppUsed; this.ppUp = ppUp; @@ -8035,21 +6996,13 @@ export class PokemonMove { * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. * - * @param {Pokemon} pokemon {@linkcode Pokemon} that would be using this move - * @param {boolean} ignorePp If `true`, skips the PP check - * @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) + * @param pokemon - {@linkcode Pokemon} that would be using this move + * @param ignorePp - If `true`, skips the PP check + * @param ignoreRestrictionTags - If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. */ - isUsable( - pokemon: Pokemon, - ignorePp = false, - ignoreRestrictionTags = false, - ): boolean { - if ( - this.moveId && - !ignoreRestrictionTags && - pokemon.isMoveRestricted(this.moveId, pokemon) - ) { + isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean { + if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) { return false; } @@ -8057,9 +7010,7 @@ export class PokemonMove { return false; } - return ( - ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1 - ); + return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1; } getMove(): Move { @@ -8070,15 +7021,12 @@ export class PokemonMove { * Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp} * @param count Amount of PP to use */ - usePp(count: number = 1) { + usePp(count = 1) { this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp()); } getMovePp(): number { - return ( - this.maxPpOverride || - this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5) - ); + return this.maxPpOverride || this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5); } getPpRatio(): number { @@ -8095,12 +7043,6 @@ export class PokemonMove { * @returns A valid {@linkcode PokemonMove} object */ static loadMove(source: PokemonMove | any): PokemonMove { - return new PokemonMove( - source.moveId, - source.ppUsed, - source.ppUp, - source.virtual, - source.maxPpOverride, - ); + return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.virtual, source.maxPpOverride); } } diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index 598d9109abc..428acaf9ed4 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -24,7 +24,7 @@ export class RevivalBlessingPhase extends BattlePhase { UiMode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.user.getFieldIndex(), - (slotIndex: integer, _option: PartyOption) => { + (slotIndex: number, _option: PartyOption) => { if (slotIndex >= 0 && slotIndex < 6) { const pokemon = globalScene.getPlayerParty()[slotIndex]; if (!pokemon || !pokemon.isFainted()) { diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 714a9b39771..5c48cf55753 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -151,9 +151,9 @@ export default class LoginFormUiHandler extends FormModalUiHandler { // Prevent overlapping overrides on action modification this.submitAction = originalLoginAction; this.sanitizeInputs(); - globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); const onFail = error => { - globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); globalScene.ui.playError(); }; if (!this.inputs[0].text) { @@ -243,7 +243,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { }, }); } - globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: options, delay: 1000, }); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index f6105c51ee3..74d4e5bcb17 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -52,6 +52,7 @@ export enum PartyUiMode { * Indicates that the party UI is open because of a start-of-encounter optional * switch. This type of switch can be cancelled. */ + // TODO: Rename to PRE_BATTLE_SWITCH POST_BATTLE_SWITCH, /** * Indicates that the party UI is open because of the move Revival Blessing. @@ -356,6 +357,522 @@ export default class PartyUiHandler extends MessageUiHandler { return true; } + private processSummaryOption(pokemon: Pokemon): boolean { + const ui = this.getUi(); + ui.playSelect(); + ui.setModeWithoutClear(UiMode.SUMMARY, pokemon).then(() => this.clearOptions()); + return true; + } + + private processPokedexOption(pokemon: Pokemon): boolean { + const ui = this.getUi(); + ui.playSelect(); + const attributes = { + shiny: pokemon.shiny, + variant: pokemon.variant, + form: pokemon.formIndex, + female: pokemon.gender === Gender.FEMALE, + }; + ui.setOverlayMode(UiMode.POKEDEX_PAGE, pokemon.species, attributes).then(() => this.clearOptions()); + return true; + } + + private processUnpauseEvolutionOption(pokemon: Pokemon): boolean { + const ui = this.getUi(); + this.clearOptions(); + ui.playSelect(); + pokemon.pauseEvolutions = !pokemon.pauseEvolutions; + this.showText( + i18next.t(pokemon.pauseEvolutions ? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", { + pokemonName: getPokemonNameWithAffix(pokemon, false), + }), + undefined, + () => this.showText("", 0), + null, + true, + ); + return true; + } + + private processUnspliceOption(pokemon: PlayerPokemon): boolean { + const ui = this.getUi(); + this.clearOptions(); + ui.playSelect(); + this.showText( + i18next.t("partyUiHandler:unspliceConfirmation", { + fusionName: pokemon.fusionSpecies?.name, + pokemonName: pokemon.getName(), + }), + null, + () => { + ui.setModeWithoutClear( + UiMode.CONFIRM, + () => { + const fusionName = pokemon.getName(); + pokemon.unfuse().then(() => { + this.clearPartySlots(); + this.populatePartySlots(); + ui.setMode(UiMode.PARTY); + this.showText( + i18next.t("partyUiHandler:wasReverted", { + fusionName: fusionName, + pokemonName: pokemon.getName(false), + }), + undefined, + () => { + ui.setMode(UiMode.PARTY); + this.showText("", 0); + }, + null, + true, + ); + }); + }, + () => { + ui.setMode(UiMode.PARTY); + this.showText("", 0); + }, + ); + }, + ); + return true; + } + + private processReleaseOption(pokemon: Pokemon): boolean { + const ui = this.getUi(); + this.clearOptions(); + ui.playSelect(); + // In release mode, we do not ask for confirmation when clicking release. + if (this.partyUiMode === PartyUiMode.RELEASE) { + this.doRelease(this.cursor); + return true; + } + if (this.cursor >= globalScene.currentBattle.getBattlerCount() || !pokemon.isAllowedInBattle()) { + this.blockInput = true; + this.showText( + i18next.t("partyUiHandler:releaseConfirmation", { + pokemonName: getPokemonNameWithAffix(pokemon, false), + }), + null, + () => { + this.blockInput = false; + ui.setModeWithoutClear( + UiMode.CONFIRM, + () => { + ui.setMode(UiMode.PARTY); + this.doRelease(this.cursor); + }, + () => { + ui.setMode(UiMode.PARTY); + this.showText("", 0); + }, + ); + }, + ); + } else { + this.showText(i18next.t("partyUiHandler:releaseInBattle"), null, () => this.showText("", 0), null, true); + } + return true; + } + + private processRenameOption(pokemon: Pokemon): boolean { + const ui = this.getUi(); + this.clearOptions(); + ui.playSelect(); + ui.setModeWithoutClear( + UiMode.RENAME_POKEMON, + { + buttonActions: [ + (nickname: string) => { + ui.playSelect(); + pokemon.nickname = nickname; + pokemon.updateInfo(); + this.clearPartySlots(); + this.populatePartySlots(); + ui.setMode(UiMode.PARTY); + }, + () => { + ui.setMode(UiMode.PARTY); + }, + ], + }, + pokemon, + ); + return true; + } + + // TODO: Does this need to check that selectCallback exists? + private processTransferOption(): boolean { + const ui = this.getUi(); + if (this.transferCursor !== this.cursor) { + if (this.transferAll) { + this.getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor]).forEach( + (_, i, array) => { + const invertedIndex = array.length - 1 - i; + (this.selectCallback as PartyModifierTransferSelectCallback)( + this.transferCursor, + invertedIndex, + this.transferQuantitiesMax[invertedIndex], + this.cursor, + ); + }, + ); + } else { + (this.selectCallback as PartyModifierTransferSelectCallback)( + this.transferCursor, + this.transferOptionCursor, + this.transferQuantities[this.transferOptionCursor], + this.cursor, + ); + } + } + this.clearTransfer(); + this.clearOptions(); + ui.playSelect(); + return true; + } + + // TODO: This will be largely changed with the modifier rework + private processModifierTransferModeInput(pokemon: PlayerPokemon) { + const ui = this.getUi(); + const option = this.options[this.optionsCursor]; + + if (option === PartyOption.TRANSFER) { + return this.processTransferOption(); + } + + // TODO: Revise this condition + if (!this.transferMode) { + this.startTransfer(); + + let ableToTransferText: string; + for (let p = 0; p < globalScene.getPlayerParty().length; p++) { + // this for look goes through each of the party pokemon + const newPokemon = globalScene.getPlayerParty()[p]; + // this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon `p` + // this returns `undefined` if the new pokemon doesn't have the item at all, otherwise it returns the `pokemonHeldItemModifier` for that item + const matchingModifier = globalScene.findModifier( + m => + m instanceof PokemonHeldItemModifier && + m.pokemonId === newPokemon.id && + m.matchType(this.getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor]), + ) as PokemonHeldItemModifier; + const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us + if (p !== this.transferCursor) { + // this skips adding the able/not able labels on the pokemon doing the transfer + if (matchingModifier) { + // if matchingModifier exists then the item exists on the new pokemon + if (matchingModifier.getMaxStackCount() === matchingModifier.stackCount) { + // checks to see if the stack of items is at max stack; if so, set the description label to "Not able" + ableToTransferText = i18next.t("partyUiHandler:notAble"); + } else { + // if the pokemon isn't at max stack, make the label "Able" + ableToTransferText = i18next.t("partyUiHandler:able"); + } + } else { + // if matchingModifier doesn't exist, that means the pokemon doesn't have any of the item, and we need to show "Able" + ableToTransferText = i18next.t("partyUiHandler:able"); + } + } else { + // this else relates to the transfer pokemon. We set the text to be blank so there's no "Able"/"Not able" text + ableToTransferText = ""; + } + partySlot.slotHpBar.setVisible(false); + partySlot.slotHpOverlay.setVisible(false); + partySlot.slotHpText.setVisible(false); + partySlot.slotDescriptionLabel.setText(ableToTransferText); + partySlot.slotDescriptionLabel.setVisible(true); + } + this.clearOptions(); + ui.playSelect(); + return true; + } + return false; + } + + // TODO: Might need to check here for when this.transferMode is active. + private processModifierTransferModeLeftRightInput(button: Button) { + let success = false; + const option = this.options[this.optionsCursor]; + if (button === Button.LEFT) { + /** Decrease quantity for the current item and update UI */ + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + this.transferQuantities[option] = + this.transferQuantities[option] === 1 + ? this.transferQuantitiesMax[option] + : this.transferQuantities[option] - 1; + this.updateOptions(); + success = this.setCursor( + this.optionsCursor, + ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */ + } + } + + if (button === Button.RIGHT) { + /** Increase quantity for the current item and update UI */ + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + this.transferQuantities[option] = + this.transferQuantities[option] === this.transferQuantitiesMax[option] + ? 1 + : this.transferQuantities[option] + 1; + this.updateOptions(); + success = this.setCursor( + this.optionsCursor, + ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */ + } + } + return success; + } + + // TODO: Might need to check here for when this.transferMode is active. + private processModifierTransferModeUpDownInput(button: Button.UP | Button.DOWN) { + let success = false; + const option = this.options[this.optionsCursor]; + + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + if (option !== PartyOption.ALL) { + this.transferQuantities[option] = this.transferQuantitiesMax[option]; + } + this.updateOptions(); + } + success = this.moveOptionCursor(button); + + return success; + } + + private moveOptionCursor(button: Button.UP | Button.DOWN): boolean { + if (button === Button.UP) { + return this.setCursor(this.optionsCursor ? this.optionsCursor - 1 : this.options.length - 1); + } + return this.setCursor(this.optionsCursor < this.options.length - 1 ? this.optionsCursor + 1 : 0); + } + + private processRememberMoveModeInput(pokemon: PlayerPokemon) { + const ui = this.getUi(); + const option = this.options[this.optionsCursor]; + + // clear overlay on cancel + this.moveInfoOverlay.clear(); + const filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); + if (filterResult === null) { + this.selectCallback?.(this.cursor, option); + this.clearOptions(); + } else { + this.clearOptions(); + this.showText(filterResult as string, undefined, () => this.showText("", 0), undefined, true); + } + ui.playSelect(); + return true; + } + + private processRememberMoveModeUpDownInput(button: Button.UP | Button.DOWN) { + let success = false; + + success = this.moveOptionCursor(button); + + // show move description + const option = this.options[this.optionsCursor]; + const pokemon = globalScene.getPlayerParty()[this.cursor]; + const move = allMoves[pokemon.getLearnableLevelMoves()[option]]; + if (move) { + this.moveInfoOverlay.show(move); + } else { + // or hide the overlay, in case it's the cancel button + this.moveInfoOverlay.clear(); + } + + return success; + } + + private getTransferrableItemsFromPokemon(pokemon: PlayerPokemon) { + return globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id, + ) as PokemonHeldItemModifier[]; + } + + private getFilterResult(option: number, pokemon: PlayerPokemon): string | null { + let filterResult: string | null; + if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { + filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); + if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) { + filterResult = this.FilterChallengeLegal(pokemon); + } + if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { + filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]); + } + } else { + filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)( + pokemon, + this.getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor])[ + this.transferOptionCursor + ], + ); + } + return filterResult; + } + + private processActionButtonForOptions(option: PartyOption) { + const ui = this.getUi(); + if (option === PartyOption.CANCEL) { + return this.processOptionMenuInput(Button.CANCEL); + } + + // If the input has been already processed we are done, otherwise move on until the correct option is found + const pokemon = globalScene.getPlayerParty()[this.cursor]; + + // TODO: Careful about using success for the return values here. Find a better way + // PartyOption.ALL, and options specific to the mode (held items) + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + return this.processModifierTransferModeInput(pokemon); + } + + // options specific to the mode (moves) + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + return this.processRememberMoveModeInput(pokemon); + } + + // These are the options that do not involve a callback + if (option === PartyOption.SUMMARY) { + return this.processSummaryOption(pokemon); + } + if (option === PartyOption.POKEDEX) { + return this.processPokedexOption(pokemon); + } + if (option === PartyOption.UNPAUSE_EVOLUTION) { + return this.processUnpauseEvolutionOption(pokemon); + } + if (option === PartyOption.UNSPLICE) { + return this.processUnspliceOption(pokemon); + } + if (option === PartyOption.RENAME) { + return this.processRenameOption(pokemon); + } + // This is only relevant for PartyUiMode.CHECK + // TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check? + if ( + option >= PartyOption.FORM_CHANGE_ITEM && + globalScene.getCurrentPhase() instanceof SelectModifierPhase && + this.partyUiMode === PartyUiMode.CHECK + ) { + const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); + const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; + modifier.active = !modifier.active; + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true); + } + + // If the pokemon is filtered out for this option, we cannot continue + const filterResult = this.getFilterResult(option, pokemon); + if (filterResult) { + this.clearOptions(); + this.showText(filterResult as string, undefined, () => this.showText("", 0), undefined, true); + return true; + } + + // For what modes is a selectCallback needed? + // PartyUiMode.SELECT (SELECT) + // PartyUiMode.RELEASE (RELEASE) + // PartyUiMode.FAINT_SWITCH (SEND_OUT or PASS_BATON (?)) + // PartyUiMode.REVIVAL_BLESSING (REVIVE) + // PartyUiMode.MODIFIER_TRANSFER (held items, and ALL) + // PartyUiMode.CHECK --- no specific option, only relevant on cancel? + // PartyUiMode.SPLICE (SPLICE) + // PartyUiMode.MOVE_MODIFIER (MOVE_1, MOVE_2, MOVE_3, MOVE_4) + // PartyUiMode.TM_MODIFIER (TEACH) + // PartyUiMode.REMEMBER_MOVE_MODIFIER (no specific option, callback is invoked when selecting a move) + // PartyUiMode.MODIFIER (APPLY option) + // 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); + this.clearTransfer(); + } else if (option === PartyOption.APPLY) { + this.startTransfer(); + } + this.clearOptions(); + ui.playSelect(); + return true; + } + + // This is used when switching out using the Pokemon command (possibly holding a Baton held item). In this case there is no callback. + if ( + (option === PartyOption.PASS_BATON || option === PartyOption.SEND_OUT) && + this.partyUiMode === PartyUiMode.SWITCH + ) { + this.clearOptions(); + (globalScene.getCurrentPhase() as CommandPhase).handleCommand( + Command.POKEMON, + this.cursor, + option === PartyOption.PASS_BATON, + ); + } + + if ( + [ + PartyOption.SEND_OUT, // When sending out at the start of battle, or due to an effect + PartyOption.PASS_BATON, // When passing the baton due to the Baton Pass move + PartyOption.REVIVE, + PartyOption.APPLY, + PartyOption.TEACH, + PartyOption.MOVE_1, + PartyOption.MOVE_2, + PartyOption.MOVE_3, + PartyOption.MOVE_4, + PartyOption.SELECT, + ].includes(option) && + this.selectCallback + ) { + this.clearOptions(); + const selectCallback = this.selectCallback; + this.selectCallback = null; + selectCallback(this.cursor, option); + return true; + } + + return false; + } + + private processOptionMenuInput(button: Button) { + const ui = this.getUi(); + const option = this.options[this.optionsCursor]; + + // Button.CANCEL has no special behavior for any option + if (button === Button.CANCEL) { + this.clearOptions(); + ui.playSelect(); + return true; + } + + if (button === Button.ACTION) { + return this.processActionButtonForOptions(option); + } + + if (button === Button.UP || button === Button.DOWN) { + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + return this.processModifierTransferModeUpDownInput(button); + } + + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + return this.processRememberMoveModeUpDownInput(button); + } + + return this.moveOptionCursor(button); + } + + if (button === Button.LEFT || button === Button.RIGHT) { + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + return this.processModifierTransferModeLeftRightInput(button); + } + } + + return false; + } + processInput(button: Button): boolean { const ui = this.getUi(); @@ -375,463 +892,120 @@ export default class PartyUiHandler extends MessageUiHandler { return false; } - let success = false; - if (this.optionsMode) { - const option = this.options[this.optionsCursor]; - if (button === Button.ACTION) { - const pokemon = globalScene.getPlayerParty()[this.cursor]; - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode && option !== PartyOption.CANCEL) { - this.startTransfer(); - - let ableToTransfer: string; - for (let p = 0; p < globalScene.getPlayerParty().length; p++) { - // this for look goes through each of the party pokemon - const newPokemon = globalScene.getPlayerParty()[p]; - // this next line gets all of the transferable items from pokemon [p]; it does this by getting all the held modifiers that are transferable and checking to see if they belong to pokemon [p] - const getTransferrableItemsFromPokemon = (newPokemon: PlayerPokemon) => - globalScene.findModifiers( - m => - m instanceof PokemonHeldItemModifier && - (m as PokemonHeldItemModifier).isTransferable && - (m as PokemonHeldItemModifier).pokemonId === newPokemon.id, - ) as PokemonHeldItemModifier[]; - // this next bit checks to see if the the selected item from the original transfer pokemon exists on the new pokemon [p]; this returns undefined if the new pokemon doesn't have the item at all, otherwise it returns the pokemonHeldItemModifier for that item - const matchingModifier = globalScene.findModifier( - m => - m instanceof PokemonHeldItemModifier && - m.pokemonId === newPokemon.id && - m.matchType(getTransferrableItemsFromPokemon(pokemon)[this.transferOptionCursor]), - ) as PokemonHeldItemModifier; - const partySlot = this.partySlots.filter(m => m.getPokemon() === newPokemon)[0]; // this gets pokemon [p] for us - if (p !== this.transferCursor) { - // this skips adding the able/not able labels on the pokemon doing the transfer - if (matchingModifier) { - // if matchingModifier exists then the item exists on the new pokemon - if (matchingModifier.getMaxStackCount() === matchingModifier.stackCount) { - // checks to see if the stack of items is at max stack; if so, set the description label to "Not able" - ableToTransfer = i18next.t("partyUiHandler:notAble"); - } else { - // if the pokemon isn't at max stack, make the label "Able" - ableToTransfer = i18next.t("partyUiHandler:able"); - } - } else { - // if matchingModifier doesn't exist, that means the pokemon doesn't have any of the item, and we need to show "Able" - ableToTransfer = i18next.t("partyUiHandler:able"); - } - } else { - // this else relates to the transfer pokemon. We set the text to be blank so there's no "Able"/"Not able" text - ableToTransfer = ""; - } - partySlot.slotHpBar.setVisible(false); - partySlot.slotHpOverlay.setVisible(false); - partySlot.slotHpText.setVisible(false); - partySlot.slotDescriptionLabel.setText(ableToTransfer); - partySlot.slotDescriptionLabel.setVisible(true); - } - - this.clearOptions(); - ui.playSelect(); - return true; - } - if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && option !== PartyOption.CANCEL) { - // clear overlay on cancel - this.moveInfoOverlay.clear(); - const filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); - if (filterResult === null) { - this.selectCallback?.(this.cursor, option); - this.clearOptions(); - } else { - this.clearOptions(); - this.showText(filterResult as string, undefined, () => this.showText("", 0), undefined, true); - } - ui.playSelect(); - return true; - } - if ( - ![ - PartyOption.SUMMARY, - PartyOption.POKEDEX, - PartyOption.UNPAUSE_EVOLUTION, - PartyOption.UNSPLICE, - PartyOption.RELEASE, - PartyOption.CANCEL, - PartyOption.RENAME, - ].includes(option) || - (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE) - ) { - let filterResult: string | null; - const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) => - globalScene.findModifiers( - m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id, - ) as PokemonHeldItemModifier[]; - if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { - filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); - if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) { - filterResult = this.FilterChallengeLegal(pokemon); - } - if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { - filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]); - } - } else { - filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)( - pokemon, - getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor])[ - this.transferOptionCursor - ], - ); - } - if (filterResult === null) { - if (this.partyUiMode !== PartyUiMode.SPLICE) { - this.clearOptions(); - } - if (this.selectCallback && this.partyUiMode !== PartyUiMode.CHECK) { - if (option === PartyOption.TRANSFER) { - if (this.transferCursor !== this.cursor) { - if (this.transferAll) { - getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor]).forEach( - (_, i, array) => { - const invertedIndex = array.length - 1 - i; - (this.selectCallback as PartyModifierTransferSelectCallback)( - this.transferCursor, - invertedIndex, - this.transferQuantitiesMax[invertedIndex], - this.cursor, - ); - }, - ); - } else { - (this.selectCallback as PartyModifierTransferSelectCallback)( - this.transferCursor, - this.transferOptionCursor, - this.transferQuantities[this.transferOptionCursor], - this.cursor, - ); - } - } - this.clearTransfer(); - } else if (this.partyUiMode === PartyUiMode.SPLICE) { - if (option === PartyOption.SPLICE) { - (this.selectCallback as PartyModifierSpliceSelectCallback)(this.transferCursor, this.cursor); - this.clearTransfer(); - } else { - this.startTransfer(); - } - this.clearOptions(); - } else if (option === PartyOption.RELEASE) { - this.doRelease(this.cursor); - } else { - const selectCallback = this.selectCallback; - this.selectCallback = null; - selectCallback(this.cursor, option); - } - } else { - if ( - option >= PartyOption.FORM_CHANGE_ITEM && - globalScene.getCurrentPhase() instanceof SelectModifierPhase - ) { - if (this.partyUiMode === PartyUiMode.CHECK) { - const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); - const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; - modifier.active = !modifier.active; - globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger, false, true); - } - } else if (this.cursor) { - (globalScene.getCurrentPhase() as CommandPhase).handleCommand( - Command.POKEMON, - this.cursor, - option === PartyOption.PASS_BATON, - ); - } - } - if ( - this.partyUiMode !== PartyUiMode.MODIFIER && - this.partyUiMode !== PartyUiMode.TM_MODIFIER && - this.partyUiMode !== PartyUiMode.MOVE_MODIFIER - ) { - ui.playSelect(); - } - return true; - } - this.clearOptions(); - this.showText(filterResult as string, undefined, () => this.showText("", 0), undefined, true); - } else if (option === PartyOption.SUMMARY) { - ui.playSelect(); - ui.setModeWithoutClear(UiMode.SUMMARY, pokemon).then(() => this.clearOptions()); - return true; - } else if (option === PartyOption.POKEDEX) { - ui.playSelect(); - const attributes = { - shiny: pokemon.shiny, - variant: pokemon.variant, - form: pokemon.formIndex, - female: pokemon.gender === Gender.FEMALE, - }; - ui.setOverlayMode(UiMode.POKEDEX_PAGE, pokemon.species, attributes).then(() => this.clearOptions()); - return true; - } else if (option === PartyOption.UNPAUSE_EVOLUTION) { - this.clearOptions(); - ui.playSelect(); - pokemon.pauseEvolutions = !pokemon.pauseEvolutions; - this.showText( - i18next.t( - pokemon.pauseEvolutions ? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", - { pokemonName: getPokemonNameWithAffix(pokemon, false) }, - ), - undefined, - () => this.showText("", 0), - null, - true, - ); - } else if (option === PartyOption.UNSPLICE) { - this.clearOptions(); - ui.playSelect(); - this.showText( - i18next.t("partyUiHandler:unspliceConfirmation", { - fusionName: pokemon.fusionSpecies?.name, - pokemonName: pokemon.getName(), - }), - null, - () => { - ui.setModeWithoutClear( - UiMode.CONFIRM, - () => { - const fusionName = pokemon.getName(); - pokemon.unfuse().then(() => { - this.clearPartySlots(); - this.populatePartySlots(); - ui.setMode(UiMode.PARTY); - this.showText( - i18next.t("partyUiHandler:wasReverted", { - fusionName: fusionName, - pokemonName: pokemon.getName(false), - }), - undefined, - () => { - ui.setMode(UiMode.PARTY); - this.showText("", 0); - }, - null, - true, - ); - }); - }, - () => { - ui.setMode(UiMode.PARTY); - this.showText("", 0); - }, - ); - }, - ); - } else if (option === PartyOption.RELEASE) { - this.clearOptions(); - ui.playSelect(); - if (this.cursor >= globalScene.currentBattle.getBattlerCount() || !pokemon.isAllowedInBattle()) { - this.blockInput = true; - this.showText( - i18next.t("partyUiHandler:releaseConfirmation", { - pokemonName: getPokemonNameWithAffix(pokemon, false), - }), - null, - () => { - this.blockInput = false; - ui.setModeWithoutClear( - UiMode.CONFIRM, - () => { - ui.setMode(UiMode.PARTY); - this.doRelease(this.cursor); - }, - () => { - ui.setMode(UiMode.PARTY); - this.showText("", 0); - }, - ); - }, - ); - } else { - this.showText(i18next.t("partyUiHandler:releaseInBattle"), null, () => this.showText("", 0), null, true); - } - return true; - } else if (option === PartyOption.RENAME) { - this.clearOptions(); - ui.playSelect(); - ui.setModeWithoutClear( - UiMode.RENAME_POKEMON, - { - buttonActions: [ - (nickname: string) => { - ui.playSelect(); - pokemon.nickname = nickname; - pokemon.updateInfo(); - this.clearPartySlots(); - this.populatePartySlots(); - ui.setMode(UiMode.PARTY); - }, - () => { - ui.setMode(UiMode.PARTY); - }, - ], - }, - pokemon, - ); - return true; - } else if (option === PartyOption.CANCEL) { - return this.processInput(Button.CANCEL); - } else if (option === PartyOption.SELECT) { - ui.playSelect(); - return true; - } - } else if (button === Button.CANCEL) { - this.clearOptions(); + let success = false; + success = this.processOptionMenuInput(button); + if (success) { ui.playSelect(); - return true; + } + return success; + } + + if (button === Button.ACTION) { + return this.processPartyActionInput(); + } + + if (button === Button.CANCEL) { + return this.processPartyCancelInput(); + } + + if (button === Button.UP || button === Button.DOWN || button === Button.RIGHT || button === Button.LEFT) { + return this.processPartyDirectionalInput(button); + } + + return false; + } + + private allowCancel(): boolean { + return !(this.partyUiMode === PartyUiMode.FAINT_SWITCH || this.partyUiMode === PartyUiMode.REVIVAL_BLESSING); + } + + private processPartyActionInput(): boolean { + const ui = this.getUi(); + if (this.cursor < 6) { + if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) { + /** Initialize item quantities for the selected Pokemon */ + const itemModifiers = globalScene.findModifiers( + m => + m instanceof PokemonHeldItemModifier && + m.isTransferable && + m.pokemonId === globalScene.getPlayerParty()[this.cursor].id, + ) as PokemonHeldItemModifier[]; + this.transferQuantities = itemModifiers.map(item => item.getStackCount()); + this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount()); + } + this.showOptions(); + ui.playSelect(); + } + // Pressing return button + if (this.cursor === 6) { + if (!this.allowCancel()) { + ui.playError(); } else { - switch (button) { - case Button.LEFT: - /** Decrease quantity for the current item and update UI */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - this.transferQuantities[option] = - this.transferQuantities[option] === 1 - ? this.transferQuantitiesMax[option] - : this.transferQuantities[option] - 1; - this.updateOptions(); - success = this.setCursor( - this.optionsCursor, - ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */ - } - break; - case Button.RIGHT: - /** Increase quantity for the current item and update UI */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - this.transferQuantities[option] = - this.transferQuantities[option] === this.transferQuantitiesMax[option] - ? 1 - : this.transferQuantities[option] + 1; - this.updateOptions(); - success = this.setCursor( - this.optionsCursor, - ); /** Place again the cursor at the same position. Necessary, otherwise the cursor disappears */ - } - break; - case Button.UP: - /** If currently selecting items to transfer, reset quantity selection */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - if (option !== PartyOption.ALL) { - this.transferQuantities[option] = this.transferQuantitiesMax[option]; - } - this.updateOptions(); - } - success = this.setCursor( - this.optionsCursor ? this.optionsCursor - 1 : this.options.length - 1, - ); /** Move cursor */ - break; - case Button.DOWN: - /** If currently selecting items to transfer, reset quantity selection */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - if (option !== PartyOption.ALL) { - this.transferQuantities[option] = this.transferQuantitiesMax[option]; - } - this.updateOptions(); - } - success = this.setCursor( - this.optionsCursor < this.options.length - 1 ? this.optionsCursor + 1 : 0, - ); /** Move cursor */ - break; - } - - // show move description - if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { - const option = this.options[this.optionsCursor]; - const pokemon = globalScene.getPlayerParty()[this.cursor]; - const move = allMoves[pokemon.getLearnableLevelMoves()[option]]; - if (move) { - this.moveInfoOverlay.show(move); - } else { - // or hide the overlay, in case it's the cancel button - this.moveInfoOverlay.clear(); - } - } + return this.processInput(Button.CANCEL); } - } else { - if (button === Button.ACTION) { - if (this.cursor < 6) { - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) { - /** Initialize item quantities for the selected Pokemon */ - const itemModifiers = globalScene.findModifiers( - m => - m instanceof PokemonHeldItemModifier && - m.isTransferable && - m.pokemonId === globalScene.getPlayerParty()[this.cursor].id, - ) as PokemonHeldItemModifier[]; - this.transferQuantities = itemModifiers.map(item => item.getStackCount()); - this.transferQuantitiesMax = itemModifiers.map(item => item.getStackCount()); - } - this.showOptions(); - ui.playSelect(); - } else if (this.partyUiMode === PartyUiMode.FAINT_SWITCH || this.partyUiMode === PartyUiMode.REVIVAL_BLESSING) { - ui.playError(); - } else { - return this.processInput(Button.CANCEL); - } - return true; + } + return true; + } + + private processPartyCancelInput(): boolean { + const ui = this.getUi(); + if ( + (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) && + this.transferMode + ) { + this.clearTransfer(); + ui.playSelect(); + } else if (this.allowCancel()) { + if (this.selectCallback) { + const selectCallback = this.selectCallback; + this.selectCallback = null; + selectCallback(6, PartyOption.CANCEL); + ui.playSelect(); + } else { + ui.setMode(UiMode.COMMAND, this.fieldIndex); + ui.playSelect(); } - if (button === Button.CANCEL) { - if ( - (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) && - this.transferMode - ) { - this.clearTransfer(); - ui.playSelect(); - } else if (this.partyUiMode !== PartyUiMode.FAINT_SWITCH && this.partyUiMode !== PartyUiMode.REVIVAL_BLESSING) { - if (this.selectCallback) { - const selectCallback = this.selectCallback; - this.selectCallback = null; - selectCallback(6, PartyOption.CANCEL); - ui.playSelect(); - } else { - ui.setMode(UiMode.COMMAND, this.fieldIndex); - ui.playSelect(); - } + } + return true; + } + + private processPartyDirectionalInput(button: Button.UP | Button.DOWN | Button.LEFT | Button.RIGHT): boolean { + const ui = this.getUi(); + const slotCount = this.partySlots.length; + const battlerCount = globalScene.currentBattle.getBattlerCount(); + + let success = false; + switch (button) { + case Button.UP: + success = this.setCursor(this.cursor ? (this.cursor < 6 ? this.cursor - 1 : slotCount - 1) : 6); + break; + case Button.DOWN: + success = this.setCursor(this.cursor < 6 ? (this.cursor < slotCount - 1 ? this.cursor + 1 : 6) : 0); + break; + case Button.LEFT: + if (this.cursor >= battlerCount && this.cursor <= 6) { + success = this.setCursor(0); } - - return true; - } - - const slotCount = this.partySlots.length; - const battlerCount = globalScene.currentBattle.getBattlerCount(); - - switch (button) { - case Button.UP: - success = this.setCursor(this.cursor ? (this.cursor < 6 ? this.cursor - 1 : slotCount - 1) : 6); + break; + case Button.RIGHT: + if (slotCount === battlerCount) { + success = this.setCursor(6); break; - case Button.DOWN: - success = this.setCursor(this.cursor < 6 ? (this.cursor < slotCount - 1 ? this.cursor + 1 : 6) : 0); + } + if (battlerCount >= 2 && slotCount > battlerCount && this.getCursor() === 0 && this.lastCursor === 1) { + success = this.setCursor(2); break; - case Button.LEFT: - if (this.cursor >= battlerCount && this.cursor <= 6) { - success = this.setCursor(0); - } + } + if (slotCount > battlerCount && this.cursor < battlerCount) { + success = this.setCursor(this.lastCursor < 6 ? this.lastCursor || battlerCount : battlerCount); break; - case Button.RIGHT: - if (slotCount === battlerCount) { - success = this.setCursor(6); - break; - } - if (battlerCount >= 2 && slotCount > battlerCount && this.getCursor() === 0 && this.lastCursor === 1) { - success = this.setCursor(2); - break; - } - if (slotCount > battlerCount && this.cursor < battlerCount) { - success = this.setCursor(this.lastCursor < 6 ? this.lastCursor || battlerCount : battlerCount); - break; - } - } + } } if (success) { ui.playSelect(); } - return success; } @@ -860,64 +1034,66 @@ export default class PartyUiHandler extends MessageUiHandler { } setCursor(cursor: number): boolean { - let changed: boolean; - if (this.optionsMode) { - changed = this.optionsCursor !== cursor; - let isScroll = false; - if (changed && this.optionsScroll) { - if (Math.abs(cursor - this.optionsCursor) === this.options.length - 1) { - this.optionsScrollCursor = cursor ? this.optionsScrollTotal - 8 : 0; - this.updateOptions(); - } else { - const isDown = cursor && cursor > this.optionsCursor; - if (isDown) { - if (this.options[cursor] === PartyOption.SCROLL_DOWN) { - isScroll = true; - this.optionsScrollCursor++; - } - } else { - if (!cursor && this.optionsScrollCursor) { - isScroll = true; - this.optionsScrollCursor--; - } - } - if (isScroll && this.optionsScrollCursor === 1) { - this.optionsScrollCursor += isDown ? 1 : -1; - } - } + return this.setOptionsCursor(cursor); + } + const changed = this.cursor !== cursor; + if (changed) { + this.lastCursor = this.cursor; + this.cursor = cursor; + if (this.lastCursor < 6) { + this.partySlots[this.lastCursor].deselect(); + } else if (this.lastCursor === 6) { + this.partyCancelButton.deselect(); } - if (isScroll) { + if (cursor < 6) { + this.partySlots[cursor].select(); + } else if (cursor === 6) { + this.partyCancelButton.select(); + } + } + return changed; + } + + private setOptionsCursor(cursor: number): boolean { + const changed = this.optionsCursor !== cursor; + let isScroll = false; + if (changed && this.optionsScroll) { + if (Math.abs(cursor - this.optionsCursor) === this.options.length - 1) { + this.optionsScrollCursor = cursor ? this.optionsScrollTotal - 8 : 0; this.updateOptions(); } else { - this.optionsCursor = cursor; - } - if (!this.optionsCursorObj) { - this.optionsCursorObj = globalScene.add.image(0, 0, "cursor"); - this.optionsCursorObj.setOrigin(0, 0); - this.optionsContainer.add(this.optionsCursorObj); - } - this.optionsCursorObj.setPosition( - 8 - this.optionsBg.displayWidth, - -19 - 16 * (this.options.length - 1 - this.optionsCursor), - ); - } else { - changed = this.cursor !== cursor; - if (changed) { - this.lastCursor = this.cursor; - this.cursor = cursor; - if (this.lastCursor < 6) { - this.partySlots[this.lastCursor].deselect(); - } else if (this.lastCursor === 6) { - this.partyCancelButton.deselect(); + const isDown = cursor && cursor > this.optionsCursor; + if (isDown) { + if (this.options[cursor] === PartyOption.SCROLL_DOWN) { + isScroll = true; + this.optionsScrollCursor++; + } + } else { + if (!cursor && this.optionsScrollCursor) { + isScroll = true; + this.optionsScrollCursor--; + } } - if (cursor < 6) { - this.partySlots[cursor].select(); - } else if (cursor === 6) { - this.partyCancelButton.select(); + if (isScroll && this.optionsScrollCursor === 1) { + this.optionsScrollCursor += isDown ? 1 : -1; } } } + if (isScroll) { + this.updateOptions(); + } else { + this.optionsCursor = cursor; + } + if (!this.optionsCursorObj) { + this.optionsCursorObj = globalScene.add.image(0, 0, "cursor"); + this.optionsCursorObj.setOrigin(0, 0); + this.optionsContainer.add(this.optionsCursorObj); + } + this.optionsCursorObj.setPosition( + 8 - this.optionsBg.displayWidth, + -19 - 16 * (this.options.length - 1 - this.optionsCursor), + ); return changed; } @@ -984,149 +1160,81 @@ export default class PartyUiHandler extends MessageUiHandler { this.setCursor(0); } - updateOptions(): void { - const pokemon = globalScene.getPlayerParty()[this.cursor]; + private allowBatonModifierSwitch(): boolean { + return !!( + this.partyUiMode !== PartyUiMode.FAINT_SWITCH && + globalScene.findModifier( + m => + m instanceof SwitchEffectTransferModifier && + m.pokemonId === globalScene.getPlayerField()[this.fieldIndex].id, + ) + ); + } - const learnableLevelMoves = - this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER ? pokemon.getLearnableLevelMoves() : []; + // TODO: add FORCED_SWITCH (and perhaps also BATON_PASS_SWITCH) to the modes + private isBatonPassMove(): boolean { + const moveHistory = globalScene.getPlayerField()[this.fieldIndex].getMoveHistory(); + return !!( + this.partyUiMode === PartyUiMode.FAINT_SWITCH && + moveHistory.length && + allMoves[moveHistory[moveHistory.length - 1].move].getAttrs(ForceSwitchOutAttr)[0]?.isBatonPass() && + moveHistory[moveHistory.length - 1].result === MoveResult.SUCCESS + ); + } - if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && learnableLevelMoves?.length) { + private getItemModifiers(pokemon: Pokemon): PokemonHeldItemModifier[] { + return ( + (globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id, + ) as PokemonHeldItemModifier[]) ?? [] + ); + } + + private updateOptionsWithRememberMoveModifierMode(pokemon): void { + const learnableMoves = pokemon.getLearnableLevelMoves(); + for (let m = 0; m < learnableMoves.length; m++) { + this.options.push(m); + } + if (learnableMoves?.length) { // show the move overlay with info for the first move - this.moveInfoOverlay.show(allMoves[learnableLevelMoves[0]]); + this.moveInfoOverlay.show(allMoves[learnableMoves[0]]); } + } - const itemModifiers = - this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER - ? (globalScene.findModifiers( - m => m instanceof PokemonHeldItemModifier && m.isTransferable && m.pokemonId === pokemon.id, - ) as PokemonHeldItemModifier[]) - : []; - - if (this.options.length) { - this.options.splice(0, this.options.length); - this.optionsContainer.removeAll(true); - this.eraseOptionsCursor(); + private updateOptionsWithMoveModifierMode(pokemon): void { + // MOVE_1, MOVE_2, MOVE_3, MOVE_4 + for (let m = 0; m < pokemon.moveset.length; m++) { + this.options.push(PartyOption.MOVE_1 + m); } + } - let formChangeItemModifiers: PokemonFormChangeItemModifier[] | undefined; + private updateOptionsWithModifierTransferMode(pokemon): void { + const itemModifiers = this.getItemModifiers(pokemon); + for (let im = 0; im < itemModifiers.length; im++) { + this.options.push(im); + } + if (itemModifiers.length > 1) { + this.options.push(PartyOption.ALL); + } + } + + private addCommonOptions(pokemon): void { + this.options.push(PartyOption.SUMMARY); + this.options.push(PartyOption.POKEDEX); + this.options.push(PartyOption.RENAME); if ( - this.partyUiMode !== PartyUiMode.MOVE_MODIFIER && - this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER && - (this.transferMode || this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER) + pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || + (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)) ) { - switch (this.partyUiMode) { - case PartyUiMode.SWITCH: - case PartyUiMode.FAINT_SWITCH: - case PartyUiMode.POST_BATTLE_SWITCH: - if (this.cursor >= globalScene.currentBattle.getBattlerCount()) { - const allowBatonModifierSwitch = - this.partyUiMode !== PartyUiMode.FAINT_SWITCH && - globalScene.findModifier( - m => - m instanceof SwitchEffectTransferModifier && - (m as SwitchEffectTransferModifier).pokemonId === globalScene.getPlayerField()[this.fieldIndex].id, - ); - - const moveHistory = globalScene.getPlayerField()[this.fieldIndex].getMoveHistory(); - const isBatonPassMove = - this.partyUiMode === PartyUiMode.FAINT_SWITCH && - moveHistory.length && - allMoves[moveHistory[moveHistory.length - 1].move].getAttrs(ForceSwitchOutAttr)[0]?.isBatonPass() && - moveHistory[moveHistory.length - 1].result === MoveResult.SUCCESS; - - // isBatonPassMove and allowBatonModifierSwitch shouldn't ever be true - // at the same time, because they both explicitly check for a mutually - // exclusive partyUiMode. But better safe than sorry. - this.options.push( - isBatonPassMove && !allowBatonModifierSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT, - ); - if (allowBatonModifierSwitch && !isBatonPassMove) { - // the BATON modifier gives an extra switch option for - // pokemon-command switches, allowing buffs to be optionally passed - this.options.push(PartyOption.PASS_BATON); - } - } - break; - case PartyUiMode.REVIVAL_BLESSING: - this.options.push(PartyOption.REVIVE); - break; - case PartyUiMode.MODIFIER: - this.options.push(PartyOption.APPLY); - break; - case PartyUiMode.TM_MODIFIER: - this.options.push(PartyOption.TEACH); - break; - case PartyUiMode.MODIFIER_TRANSFER: - this.options.push(PartyOption.TRANSFER); - break; - case PartyUiMode.SPLICE: - if (this.transferMode) { - if (this.cursor !== this.transferCursor) { - this.options.push(PartyOption.SPLICE); - } - } else { - this.options.push(PartyOption.APPLY); - } - break; - case PartyUiMode.RELEASE: - this.options.push(PartyOption.RELEASE); - break; - case PartyUiMode.CHECK: - if (globalScene.getCurrentPhase() instanceof SelectModifierPhase) { - formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); - for (let i = 0; i < formChangeItemModifiers.length; i++) { - this.options.push(PartyOption.FORM_CHANGE_ITEM + i); - } - } - break; - case PartyUiMode.SELECT: - this.options.push(PartyOption.SELECT); - break; - } - - this.options.push(PartyOption.SUMMARY); - this.options.push(PartyOption.POKEDEX); - this.options.push(PartyOption.RENAME); - - if ( - pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || - (pokemon.isFusion() && - pokemon.fusionSpecies && - pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)) - ) { - this.options.push(PartyOption.UNPAUSE_EVOLUTION); - } - - if (this.partyUiMode === PartyUiMode.SWITCH) { - if (pokemon.isFusion()) { - this.options.push(PartyOption.UNSPLICE); - } - this.options.push(PartyOption.RELEASE); - } else if (this.partyUiMode === PartyUiMode.SPLICE && pokemon.isFusion()) { - this.options.push(PartyOption.UNSPLICE); - } - } else if (this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { - for (let m = 0; m < pokemon.moveset.length; m++) { - this.options.push(PartyOption.MOVE_1 + m); - } - } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { - const learnableMoves = pokemon.getLearnableLevelMoves(); - for (let m = 0; m < learnableMoves.length; m++) { - this.options.push(m); - } - } else { - for (let im = 0; im < itemModifiers.length; im++) { - this.options.push(im); - } - if (itemModifiers.length > 1) { - this.options.push(PartyOption.ALL); - } + this.options.push(PartyOption.UNPAUSE_EVOLUTION); } + } + private addCancelAndScrollOptions(): void { this.optionsScrollTotal = this.options.length; - let optionStartIndex = this.optionsScrollCursor; - let optionEndIndex = Math.min( + const optionStartIndex = this.optionsScrollCursor; + const optionEndIndex = Math.min( this.optionsScrollTotal, optionStartIndex + (!optionStartIndex || this.optionsScrollCursor + 8 >= this.optionsScrollTotal ? 8 : 7), ); @@ -1145,14 +1253,122 @@ export default class PartyUiHandler extends MessageUiHandler { } this.options.push(PartyOption.CANCEL); + } + + updateOptions(): void { + const pokemon = globalScene.getPlayerParty()[this.cursor]; + + if (this.options.length) { + this.options.splice(0, this.options.length); + this.optionsContainer.removeAll(true); + this.eraseOptionsCursor(); + } + + switch (this.partyUiMode) { + case PartyUiMode.MOVE_MODIFIER: + this.updateOptionsWithMoveModifierMode(pokemon); + break; + case PartyUiMode.REMEMBER_MOVE_MODIFIER: + this.updateOptionsWithRememberMoveModifierMode(pokemon); + break; + case PartyUiMode.MODIFIER_TRANSFER: + if (!this.transferMode) { + this.updateOptionsWithModifierTransferMode(pokemon); + } else { + this.options.push(PartyOption.TRANSFER); + this.addCommonOptions(pokemon); + } + break; + // TODO: This still needs to be broken up. + // It could use a rework differentiating different kind of switches + // to treat baton passing separately from switching on faint. + case PartyUiMode.SWITCH: + case PartyUiMode.FAINT_SWITCH: + case PartyUiMode.POST_BATTLE_SWITCH: + if (this.cursor >= globalScene.currentBattle.getBattlerCount()) { + const allowBatonModifierSwitch = this.allowBatonModifierSwitch(); + const isBatonPassMove = this.isBatonPassMove(); + + // isBatonPassMove and allowBatonModifierSwitch shouldn't ever be true + // at the same time, because they both explicitly check for a mutually + // exclusive partyUiMode. But better safe than sorry. + this.options.push( + isBatonPassMove && !allowBatonModifierSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT, + ); + if (allowBatonModifierSwitch && !isBatonPassMove) { + // the BATON modifier gives an extra switch option for + // pokemon-command switches, allowing buffs to be optionally passed + this.options.push(PartyOption.PASS_BATON); + } + } + this.addCommonOptions(pokemon); + if (this.partyUiMode === PartyUiMode.SWITCH) { + if (pokemon.isFusion()) { + this.options.push(PartyOption.UNSPLICE); + } + this.options.push(PartyOption.RELEASE); + } + break; + case PartyUiMode.REVIVAL_BLESSING: + this.options.push(PartyOption.REVIVE); + this.addCommonOptions(pokemon); + break; + case PartyUiMode.MODIFIER: + this.options.push(PartyOption.APPLY); + this.addCommonOptions(pokemon); + break; + case PartyUiMode.TM_MODIFIER: + this.options.push(PartyOption.TEACH); + this.addCommonOptions(pokemon); + break; + case PartyUiMode.SPLICE: + if (this.transferMode) { + if (this.cursor !== this.transferCursor) { + this.options.push(PartyOption.SPLICE); + } + } else { + this.options.push(PartyOption.APPLY); + } + this.addCommonOptions(pokemon); + if (pokemon.isFusion()) { + this.options.push(PartyOption.UNSPLICE); + } + break; + case PartyUiMode.RELEASE: + this.options.push(PartyOption.RELEASE); + this.addCommonOptions(pokemon); + break; + case PartyUiMode.CHECK: + if (globalScene.getCurrentPhase() instanceof SelectModifierPhase) { + const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); + for (let i = 0; i < formChangeItemModifiers.length; i++) { + this.options.push(PartyOption.FORM_CHANGE_ITEM + i); + } + } + this.addCommonOptions(pokemon); + break; + case PartyUiMode.SELECT: + this.options.push(PartyOption.SELECT); + this.addCommonOptions(pokemon); + break; + } + + // Generic, these are applied to all Modes + this.addCancelAndScrollOptions(); + + this.updateOptionsWindow(); + } + + private updateOptionsWindow(): void { + const pokemon = globalScene.getPlayerParty()[this.cursor]; this.optionsBg = addWindow(0, 0, 0, 16 * this.options.length + 13); this.optionsBg.setOrigin(1, 1); this.optionsContainer.add(this.optionsBg); - optionStartIndex = 0; - optionEndIndex = this.options.length; + const optionStartIndex = 0; + const optionEndIndex = this.options.length; let widestOptionWidth = 0; const optionTexts: BBCodeText[] = []; @@ -1185,6 +1401,7 @@ export default class PartyUiHandler extends MessageUiHandler { } break; default: + const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); if (formChangeItemModifiers && option >= PartyOption.FORM_CHANGE_ITEM) { const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; optionName = `${modifier.active ? i18next.t("partyUiHandler:DEACTIVATE") : i18next.t("partyUiHandler:ACTIVATE")} ${modifier.type.name}`; @@ -1200,6 +1417,7 @@ export default class PartyUiHandler extends MessageUiHandler { break; } } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + const learnableLevelMoves = pokemon.getLearnableLevelMoves(); const move = learnableLevelMoves[option]; optionName = allMoves[move].name; altText = !pokemon @@ -1209,6 +1427,7 @@ export default class PartyUiHandler extends MessageUiHandler { } else if (option === PartyOption.ALL) { optionName = i18next.t("partyUiHandler:ALL"); } else { + const itemModifiers = this.getItemModifiers(pokemon); const itemModifier = itemModifiers[option]; optionName = itemModifier.type.name; } @@ -1222,6 +1441,7 @@ export default class PartyUiHandler extends MessageUiHandler { optionText.setOrigin(0, 0); /** For every item that has stack bigger than 1, display the current quantity selection */ + const itemModifiers = this.getItemModifiers(pokemon); const itemModifier = itemModifiers[option]; if ( this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER &&