From 24bbb0931cf63898617b0ca6898ae1d5487848b8 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer Date: Tue, 24 Sep 2024 13:15:18 -0400 Subject: [PATCH] change return type of isNullOrUndefined --- src/battle-scene.ts | 22 ++++-- src/data/battle-anims.ts | 2 +- .../encounters/bug-type-superfan-encounter.ts | 12 ++-- .../department-store-sale-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../global-trade-system-encounter.ts | 36 +++++----- .../teleporting-hijinks-encounter.ts | 4 +- .../the-pokemon-salesman-encounter.ts | 4 +- .../encounters/training-session-encounter.ts | 15 ++--- .../encounters/uncommon-breed-encounter.ts | 67 +++++++------------ .../encounters/weird-dream-encounter.ts | 12 ++-- .../mystery-encounter-option.ts | 2 +- .../mystery-encounter-requirements.ts | 28 ++++---- .../utils/encounter-dialogue-utils.ts | 2 +- .../utils/encounter-phase-utils.ts | 22 +++--- .../utils/encounter-pokemon-utils.ts | 2 +- src/field/mystery-encounter-intro.ts | 2 +- src/field/pokemon.ts | 4 +- src/phases/select-modifier-phase.ts | 4 +- .../mystery-encounter/encounter-test-utils.ts | 2 +- src/test/utils/gameManager.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 4 +- src/utils.ts | 2 +- 23 files changed, 122 insertions(+), 132 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 75dacadff78..bbae01bf2f6 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2636,8 +2636,7 @@ export default class BattleScene extends SceneBase { modifier = mt.modifier as PokemonHeldItemModifier; modifier.pokemonId = enemyPokemon.id; } - const stackCount = mt.stackCount ?? 1; - modifier.stackCount = stackCount; + modifier.stackCount = mt.stackCount ?? 1; modifier.isTransferable = mt.isTransferable ?? modifier.isTransferable; this.addEnemyModifier(modifier, true); }); @@ -3076,7 +3075,16 @@ export default class BattleScene extends SceneBase { } } - isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean { + /** + * Determines whether a wave should randomly generate a {@linkcode MysteryEncounter}. + * Currently, the only modes that MEs are allowed in are Classic and Challenge. + * Additionally, MEs cannot spawn outside of waves 10-180 in those modes + * + * @param newBattleType + * @param waveIndex + * @param sessionDataEncounterType + */ + private isWaveMysteryEncounter(newBattleType: BattleType, waveIndex: number, sessionDataEncounterType?: MysteryEncounterType): boolean { const [lowestMysteryEncounterWave, highestMysteryEncounterWave] = this.gameMode.getMysteryEncounterLegalWaves(); if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(waveIndex) && waveIndex < highestMysteryEncounterWave && waveIndex > lowestMysteryEncounterWave) { // If ME type is already defined in session data, no need to roll RNG check @@ -3120,10 +3128,10 @@ export default class BattleScene extends SceneBase { getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter { // Loading override or session encounter let encounter: MysteryEncounter | null; - if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE!)) { - encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE!]; + if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) { + encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; } else { - encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType!] : null; + encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null; } // Check for queued encounters first @@ -3166,7 +3174,7 @@ export default class BattleScene extends SceneBase { let tier: MysteryEncounterTier | null = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > greatThreshold ? MysteryEncounterTier.GREAT : tierValue > ultraThreshold ? MysteryEncounterTier.ULTRA : MysteryEncounterTier.ROGUE; if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE)) { - tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE!; + tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE; } let availableEncounters: MysteryEncounter[] = []; diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 7c149ec54d5..62ef8112e6c 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -430,7 +430,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { scene.field.add(moveAnim.bgSprite); const fieldPokemon = scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon(); if (!isNullOrUndefined(priority)) { - scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority!); + scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority); } else if (fieldPokemon?.isOnField()) { scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); } diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 68840943c49..35858b2e6db 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -492,7 +492,7 @@ function getTrainerConfigForWave(waveIndex: number) { .setPartyMemberFunc(3, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex!; + p.formIndex = pool3Mon.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } @@ -515,14 +515,14 @@ function getTrainerConfigForWave(waveIndex: number) { .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex!; + p.formIndex = pool3Mon.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } })) .setPartyMemberFunc(4, getRandomPartyMemberFunc([pool3Mon2.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon2.formIndex)) { - p.formIndex = pool3Mon2.formIndex!; + p.formIndex = pool3Mon2.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } @@ -543,7 +543,7 @@ function getTrainerConfigForWave(waveIndex: number) { .setPartyMemberFunc(2, getRandomPartyMemberFunc(POOL_2_POKEMON, TrainerSlot.TRAINER, true)) .setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex!; + p.formIndex = pool3Mon.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } @@ -566,14 +566,14 @@ function getTrainerConfigForWave(waveIndex: number) { })) .setPartyMemberFunc(2, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex!; + p.formIndex = pool3Mon.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } })) .setPartyMemberFunc(3, getRandomPartyMemberFunc([pool3Mon.species], TrainerSlot.TRAINER, true, p => { if (!isNullOrUndefined(pool3Mon.formIndex)) { - p.formIndex = pool3Mon.formIndex!; + p.formIndex = pool3Mon.formIndex; p.generateAndPopulateMoveset(); p.generateName(); } diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index db25d338a29..88101752eda 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -63,7 +63,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = // Choose TMs const modifiers: ModifierTypeFunc[] = []; let i = 0; - while (i < 4) { + while (i < 6) { // 2/2/1 weight on TM rarity const roll = randSeedInt(5); if (roll < 2) { diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 4f5430b63d9..13f9d926345 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -189,7 +189,7 @@ export const FieryFalloutEncounter: MysteryEncounter = } // Burn random member - const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.NONE); + const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status.effect === StatusEffect.NONE); if (burnable?.length > 0) { const roll = randSeedInt(burnable.length); const chosenPokemon = burnable[roll]; diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 4c3472a1c9e..9da54e60612 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -26,11 +26,15 @@ import { trainerNamePools } from "#app/data/trainer-names"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { Moves } from "#enums/moves"; -import { speciesEggMoves } from "#app/data/egg-moves"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:globalTradeSystem"; +/** Base shiny chance of 512/65536 -> 1/128 odds, affected by events and Shiny Charms. Cannot exceed 1/16 odds. */ +const WONDER_TRADE_SHINY_CHANCE = 512; +/** Max shiny chance of 4096/65536 -> 1/16 odds. */ +const MAX_WONDER_TRADE_SHINY_CHANCE = 4096; + const LEGENDARY_TRADE_POOLS = { 1: [Species.RATTATA, Species.PIDGEY, Species.WEEDLE], 2: [Species.SENTRET, Species.HOOTHOOT, Species.LEDYBA], @@ -224,16 +228,15 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); // Extra shiny roll at 1/128 odds (boosted by events and charms) if (!tradePokemon.shiny) { - const baseShinyChance = 512; - const shinyThreshold = new Utils.IntegerHolder(baseShinyChance); + const shinyThreshold = new Utils.IntegerHolder(WONDER_TRADE_SHINY_CHANCE); if (scene.eventManager.isEventActive()) { shinyThreshold.value *= scene.eventManager.getShinyMultiplier(); } scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold); // Base shiny chance of 512/65536 -> 1/128, affected by events and Shiny Charms - // Maximum shiny chance of 4090/65536 -> 1/16, cannot improve further after that - const shinyChance = Math.min(shinyThreshold.value, 4090); + // Maximum shiny chance of 4096/65536 -> 1/16, cannot improve further after that + const shinyChance = Math.min(shinyThreshold.value, MAX_WONDER_TRADE_SHINY_CHANCE); tradePokemon.trySetShinySeed(shinyChance, false); } @@ -255,17 +258,16 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = // If Pokemon is still not shiny or with HA, give the Pokemon a random Common egg move in its moveset if (!tradePokemon.shiny && (!tradePokemon.species.abilityHidden || tradePokemon.abilityIndex < hiddenIndex)) { - const eggMoves: Moves[] = speciesEggMoves[tradePokemon.getSpeciesForm().getRootSpeciesId()]; - if (eggMoves) { - // Cannot gen the rare egg move, only 1 of the first 3 common moves - const eggMove = eggMoves[randSeedInt(3)]; - if (!tradePokemon.moveset.some(m => m?.moveId === eggMove)) { - if (tradePokemon.moveset.length < 4) { - tradePokemon.moveset.push(new PokemonMove(eggMove)); - } else { - const eggMoveIndex = randSeedInt(4); - tradePokemon.moveset[eggMoveIndex] = new PokemonMove(eggMove); - } + const eggMoves: Moves[] = tradePokemon.getEggMoves(); + + // Cannot gen the rare egg move, only 1 of the first 3 common moves + const eggMove = eggMoves[randSeedInt(3)]; + if (!tradePokemon.moveset.some(m => m?.moveId === eggMove)) { + if (tradePokemon.moveset.length < 4) { + tradePokemon.moveset.push(new PokemonMove(eggMove)); + } else { + const eggMoveIndex = randSeedInt(4); + tradePokemon.moveset[eggMoveIndex] = new PokemonMove(eggMove); } } } @@ -480,7 +482,7 @@ function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?: if (validSpecies?.length > 20) { validSpecies = randSeedShuffle(validSpecies); newSpecies = validSpecies.pop(); - while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) { + while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies)) { newSpecies = validSpecies.pop(); } } else { diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index f5d1891a391..abea725f113 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -39,7 +39,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = .withEncounterTier(MysteryEncounterTier.COMMON) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneRequirement(new WaveModulusRequirement([1, 2, 3], 10)) // Must be in first 3 waves after boss wave - .withSceneRequirement(new MoneyRequirement(undefined, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost + .withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost .withAutoHideIntroVisuals(false) .withCatchAllowed(true) .withIntroSpriteConfigs([ @@ -73,7 +73,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) - .withSceneMoneyRequirement(undefined, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost + .withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, 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 edd687b4270..53e27022195 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -34,7 +34,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_POKEMON_SALESMAN) .withEncounterTier(MysteryEncounterTier.ULTRA) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) - .withSceneRequirement(new MoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay + .withSceneRequirement(new MoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay .withAutoHideIntroVisuals(false) .withIntroSpriteConfigs([ { @@ -114,7 +114,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .withHasDexProgress(true) - .withSceneMoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2 + .withSceneMoneyRequirement(0, MAX_POKEMON_PRICE_MULTIPLIER) // Wave scaling money multiplier of 2 .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index cdf1cef540c..33864c00143 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -324,21 +324,18 @@ export const TrainingSessionEncounter: MysteryEncounter = const abilityIndex = encounter.misc.abilityIndex; if (!!playerPokemon.getFusionSpeciesForm()) { playerPokemon.fusionAbilityIndex = abilityIndex; - if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies!.speciesId)) { - scene.gameData.starterData[playerPokemon.fusionSpecies!.speciesId] + if (!isNullOrUndefined(playerPokemon.fusionSpecies?.speciesId) && speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) { + scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId] .abilityAttr |= - abilityIndex !== 1 || playerPokemon.fusionSpecies!.ability2 + abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2 ? Math.pow(2, playerPokemon.fusionAbilityIndex) : AbilityAttr.ABILITY_HIDDEN; } } else { playerPokemon.abilityIndex = abilityIndex; - if ( - speciesStarters.hasOwnProperty(playerPokemon.species.speciesId) - ) { - scene.gameData.starterData[ - playerPokemon.species.speciesId - ].abilityAttr |= + if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) { + scene.gameData.starterData[playerPokemon.species.speciesId] + .abilityAttr |= abilityIndex !== 1 || playerPokemon.species.ability2 ? Math.pow(2, playerPokemon.abilityIndex) : AbilityAttr.ABILITY_HIDDEN; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 4eade78baa6..c283ff0aa69 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -12,7 +12,6 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { TrainerSlot } from "#app/data/trainer-config"; import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PokemonData from "#app/system/pokemon-data"; -import { speciesEggMoves } from "#app/data/egg-moves"; import { isNullOrUndefined, randSeedInt } from "#app/utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; @@ -53,21 +52,18 @@ export const UncommonBreedEncounter: MysteryEncounter = const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2; const species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true); const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true); - const speciesRootForm = pokemon.species.getRootSpeciesId(); // Pokemon will always have one of its egg moves in its moveset - if (speciesEggMoves.hasOwnProperty(speciesRootForm)) { - const eggMoves: Moves[] = speciesEggMoves[speciesRootForm]; - const eggMoveIndex = randSeedInt(4); - const randomEggMove: Moves = eggMoves[eggMoveIndex]; - encounter.misc = { - eggMove: randomEggMove - }; - if (pokemon.moveset.length < 4) { - pokemon.moveset.push(new PokemonMove(randomEggMove)); - } else { - pokemon.moveset[0] = new PokemonMove(randomEggMove); - } + const eggMoves: Moves[] = pokemon.getEggMoves(); + const eggMoveIndex = randSeedInt(4); + const randomEggMove: Moves = eggMoves[eggMoveIndex]; + encounter.misc = { + eggMove: randomEggMove + }; + if (pokemon.moveset.length < 4) { + pokemon.moveset.push(new PokemonMove(randomEggMove)); + } else { + pokemon.moveset[0] = new PokemonMove(randomEggMove); } encounter.misc.pokemon = pokemon; @@ -198,20 +194,7 @@ export const UncommonBreedEncounter: MysteryEncounter = const pokemon = encounter.misc.pokemon; // Give 1 additional egg move - const previousEggMove = encounter.misc.eggMove; - const speciesRootForm = pokemon.species.getRootSpeciesId(); - if (speciesEggMoves.hasOwnProperty(speciesRootForm)) { - const eggMoves: Moves[] = speciesEggMoves[speciesRootForm]; - let randomEggMove: Moves = eggMoves[randSeedInt(4)]; - while (randomEggMove === previousEggMove) { - randomEggMove = eggMoves[randSeedInt(4)]; - } - if (pokemon.moveset.length < 4) { - pokemon.moveset.push(new PokemonMove(randomEggMove)); - } else { - pokemon.moveset[1] = new PokemonMove(randomEggMove); - } - } + givePokemonExtraEggMove(pokemon, encounter.misc.eggMove); await catchPokemon(scene, pokemon, null, PokeballType.POKEBALL, false); setEncounterRewards(scene, { fillRemaining: true }); @@ -240,20 +223,7 @@ export const UncommonBreedEncounter: MysteryEncounter = const pokemon = encounter.misc.pokemon; // Give 1 additional egg move - const previousEggMove = encounter.misc.eggMove; - const speciesRootForm = pokemon.species.getRootSpeciesId(); - if (speciesEggMoves.hasOwnProperty(speciesRootForm)) { - const eggMoves: Moves[] = speciesEggMoves[speciesRootForm]; - let randomEggMove: Moves = eggMoves[randSeedInt(4)]; - while (randomEggMove === previousEggMove) { - randomEggMove = eggMoves[randSeedInt(4)]; - } - if (pokemon.moveset.length < 4) { - pokemon.moveset.push(new PokemonMove(randomEggMove)); - } else { - pokemon.moveset[1] = new PokemonMove(randomEggMove); - } - } + givePokemonExtraEggMove(pokemon, encounter.misc.eggMove); // Roll IVs a second time pokemon.ivs = pokemon.ivs.map(iv => { @@ -271,3 +241,16 @@ export const UncommonBreedEncounter: MysteryEncounter = .build() ) .build(); + +function givePokemonExtraEggMove(pokemon: EnemyPokemon, previousEggMove: Moves) { + const eggMoves: Moves[] = pokemon.getEggMoves(); + let randomEggMove: Moves = eggMoves[randSeedInt(4)]; + while (randomEggMove === previousEggMove) { + randomEggMove = eggMoves[randSeedInt(4)]; + } + if (pokemon.moveset.length < 4) { + pokemon.moveset.push(new PokemonMove(randomEggMove)); + } else { + pokemon.moveset[1] = new PokemonMove(randomEggMove); + } +} diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 43a3bfd8d3b..33573fbe2d8 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -449,7 +449,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num if (validSpecies?.length > 20) { validSpecies = randSeedShuffle(validSpecies); newSpecies = validSpecies.pop(); - while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies!)) { + while (isNullOrUndefined(newSpecies) || alreadyUsedSpecies.includes(newSpecies)) { newSpecies = validSpecies.pop(); } } else { @@ -459,7 +459,7 @@ function getTransformedSpecies(originalBst: number, bstSearchRange: [number, num } } - return newSpecies!; + return newSpecies; } function doShowDreamBackground(scene: BattleScene) { @@ -554,16 +554,16 @@ function doSideBySideTransformations(scene: BattleScene, transformations: Pokemo async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: PlayerPokemon, speciesRootForm: Species): Promise { let eggMoveIndex: null | number = null; if (speciesEggMoves.hasOwnProperty(speciesRootForm)) { - const eggMoves: Moves[] = speciesEggMoves[speciesRootForm].slice(0); + const eggMoves: Moves[] = newPokemon.getEggMoves().slice(0); const eggMoveIndices = [0, 1, 2, 3]; randSeedShuffle(eggMoveIndices); let randomEggMoveIndex = eggMoveIndices.pop(); - let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex!] : null; + let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; let retries = 0; while (retries < 3 && (!randomEggMove || newPokemon.moveset.some(m => m?.moveId === randomEggMove))) { // If Pokemon already knows this move, roll for another egg move randomEggMoveIndex = eggMoveIndices.pop(); - randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex!] : null; + randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; retries++; } @@ -579,7 +579,7 @@ async function addEggMoveToNewPokemonMoveset(scene: BattleScene, newPokemon: Pla // For pokemon that the player owns (including ones just caught), unlock the egg move if (!isNullOrUndefined(randomEggMoveIndex) && !!scene.gameData.dexData[speciesRootForm].caughtAttr) { - await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex!, true); + await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true); } } } diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 865877445c1..ffae71b9555 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -208,7 +208,7 @@ export class MysteryEncounterOptionBuilder implements Partial= 0 && (this.waveRange?.[0] >= 0 && this.waveRange?.[0] > waveIndex) || (this.waveRange?.[1] >= 0 && this.waveRange?.[1] < waveIndex)) { + if (waveIndex >= 0 && (this.waveRange[0] >= 0 && this.waveRange[0] > waveIndex) || (this.waveRange[1] >= 0 && this.waveRange[1] < waveIndex)) { return false; } } @@ -251,7 +251,7 @@ export class WeatherRequirement extends EncounterSceneRequirement { const currentWeather = scene.arena.weather?.weatherType; let token = ""; if (!isNullOrUndefined(currentWeather)) { - token = WeatherType[currentWeather!].replace("_", " ").toLocaleLowerCase(); + token = WeatherType[currentWeather].replace("_", " ").toLocaleLowerCase(); } return ["weather", token]; } @@ -274,9 +274,9 @@ export class PartySizeRequirement extends EncounterSceneRequirement { } override meetsRequirement(scene: BattleScene): boolean { - if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange?.[0] <= this.partySizeRange?.[1]) { + if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) { const partySize = this.excludeDisallowedPokemon ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length; - if (partySize >= 0 && (this.partySizeRange?.[0] >= 0 && this.partySizeRange?.[0] > partySize) || (this.partySizeRange?.[1] >= 0 && this.partySizeRange?.[1] < partySize)) { + if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) { return false; } } @@ -326,7 +326,7 @@ export class MoneyRequirement extends EncounterSceneRequirement { requiredMoney: number; // Static value scalingMultiplier: number; // Calculates required money based off wave index - constructor(requiredMoney?: number, scalingMultiplier?: number) { + constructor(requiredMoney: number, scalingMultiplier?: number) { super(); this.requiredMoney = requiredMoney ?? 0; this.scalingMultiplier = scalingMultiplier ?? 0; @@ -418,8 +418,8 @@ export class NatureRequirement extends EncounterPokemonRequirement { } override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon!.nature)) { - return ["nature", Nature[pokemon!.nature]]; + if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon.nature)) { + return ["nature", Nature[pokemon.nature]]; } return ["nature", ""]; } @@ -620,7 +620,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { return this.requiredStatusEffect.some((statusEffect) => { if (statusEffect === StatusEffect.NONE) { // StatusEffect.NONE also checks for null or undefined status - return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect; + return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === statusEffect; } else { return pokemon.status?.effect === statusEffect; } @@ -628,12 +628,11 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { }); } else { // for an inverted query, we only want to get the pokemon that don't have ANY of the listed StatusEffects - // return partyPokemon.filter((pokemon) => this.requiredStatusEffect.filter((statusEffect) => pokemon.status?.effect === statusEffect).length === 0); return partyPokemon.filter((pokemon) => { return !this.requiredStatusEffect.some((statusEffect) => { if (statusEffect === StatusEffect.NONE) { // StatusEffect.NONE also checks for null or undefined status - return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect; + return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === statusEffect; } else { return pokemon.status?.effect === statusEffect; } @@ -645,7 +644,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement { override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { const reqStatus = this.requiredStatusEffect.filter((a) => { if (a === StatusEffect.NONE) { - return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon!.status!.effect) || pokemon!.status!.effect === a; + return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status.effect === a; } return pokemon!.status?.effect === a; }); @@ -988,8 +987,9 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement { } override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - if (!isNullOrUndefined(pokemon?.getHpRatio())) { - return ["healthRatio", Math.floor(pokemon!.getHpRatio() * 100).toString() + "%"]; + const hpRatio = pokemon?.getHpRatio(); + if (!isNullOrUndefined(hpRatio)) { + return ["healthRatio", Math.floor(hpRatio * 100).toString() + "%"]; } return ["healthRatio", ""]; } diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index 494a45f69f5..c4d5e47cb05 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -17,7 +17,7 @@ export function getEncounterText(scene: BattleScene, keyOrString?: string, prima return null; } - let textString: string | null = getTextWithDialogueTokens(scene, keyOrString!); + let textString: string | null = getTextWithDialogueTokens(scene, keyOrString); // Can only color the text if a Primary Style is defined // primaryStyle is applied to all text that does not have its own specified style diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index ef4f396b823..ea04241e663 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -135,7 +135,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: scene.currentBattle.trainer.destroy(); } - trainerConfig = partyConfig?.trainerConfig ? partyConfig?.trainerConfig : trainerConfigs[trainerType!]; + trainerConfig = partyTrainerConfig ? partyTrainerConfig : trainerConfigs[trainerType!]; const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle); doubleBattle = doubleTrainer; @@ -166,7 +166,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // This can be amplified or counteracted by setting levelAdditiveModifier in config // levelAdditiveModifier value of 0.5 will halve the modifier scaling, 2 will double it, etc. // Leaving null/undefined will disable level scaling - const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier! : 0; + const mult: number = !isNullOrUndefined(partyConfig.levelAdditiveModifier) ? partyConfig.levelAdditiveModifier : 0; const additive = Math.max(Math.round((scene.currentBattle.waveIndex / 10) * mult), 0); battle.enemyLevels = battle.enemyLevels.map(level => level + additive); @@ -226,7 +226,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set form if (!isNullOrUndefined(config.nickname)) { - enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname!))); + enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname))); } // Generate new id, reset status and HP in case using data source @@ -236,29 +236,29 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set form if (!isNullOrUndefined(config.formIndex)) { - enemyPokemon.formIndex = config.formIndex!; + enemyPokemon.formIndex = config.formIndex; } // Set shiny if (!isNullOrUndefined(config.shiny)) { - enemyPokemon.shiny = config.shiny!; + enemyPokemon.shiny = config.shiny; } // Set Variant if (enemyPokemon.shiny && !isNullOrUndefined(config.variant)) { - enemyPokemon.variant = config.variant!; + enemyPokemon.variant = config.variant; } // Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.) if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) { - enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!; + enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData; } // Set Boss if (config.isBoss) { let segments = !isNullOrUndefined(config.bossSegments) ? config.bossSegments! : scene.getEncounterBossSegments(scene.currentBattle.waveIndex, level, enemySpecies, true); if (!isNullOrUndefined(config.bossSegmentModifier)) { - segments += config.bossSegmentModifier!; + segments += config.bossSegmentModifier; } enemyPokemon.setBoss(true, segments); } @@ -294,18 +294,18 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: // Set ability if (!isNullOrUndefined(config.abilityIndex)) { - enemyPokemon.abilityIndex = config.abilityIndex!; + enemyPokemon.abilityIndex = config.abilityIndex; } // Set gender if (!isNullOrUndefined(config.gender)) { enemyPokemon.gender = config.gender!; - enemyPokemon.summonData.gender = config.gender!; + enemyPokemon.summonData.gender = config.gender; } // Set AI type if (!isNullOrUndefined(config.aiType)) { - enemyPokemon.aiType = config.aiType!; + enemyPokemon.aiType = config.aiType; } // Set moves diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 7277cca0600..d7e596af879 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -218,7 +218,7 @@ export function getRandomSpeciesByStarterTier(starterTiers: number | [number, nu .map(s => [getPokemonSpecies(s[0]), s[1]]); if (types && types.length > 0) { - filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || (!isNullOrUndefined(s[0].type2) && types.includes(s[0].type2!))); + filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || (!isNullOrUndefined(s[0].type2) && types.includes(s[0].type2))); } // If no filtered mons exist at specified starter tiers, will expand starter search range until there are diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 7c58a494699..70588da5d44 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -86,7 +86,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con }; if (!isNullOrUndefined(result.species)) { - const keys = getSpriteKeysFromSpecies(result.species!); + const keys = getSpriteKeysFromSpecies(result.species); result.spriteKey = keys.spriteKey; result.fileRoot = keys.fileRoot; result.isPokemon = true; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 7734f8adec2..bf2c123fa71 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1258,8 +1258,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return allAbilities[Overrides.OPP_ABILITY_OVERRIDE]; } if (this.isFusion()) { - if (!isNullOrUndefined(this.fusionMysteryEncounterPokemonData?.ability) && this.fusionMysteryEncounterPokemonData!.ability !== -1) { - return allAbilities[this.fusionMysteryEncounterPokemonData!.ability]; + if (!isNullOrUndefined(this.fusionMysteryEncounterPokemonData?.ability) && this.fusionMysteryEncounterPokemonData.ability !== -1) { + return allAbilities[this.fusionMysteryEncounterPokemonData.ability]; } else { return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)]; } diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 58fb13ac466..ba55340bd8d 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -252,13 +252,13 @@ export class SelectModifierPhase extends BattlePhase { let multiplier = 1; if (!isNullOrUndefined(this.customModifierSettings?.rerollMultiplier)) { - if (this.customModifierSettings!.rerollMultiplier! < 0) { + if (this.customModifierSettings.rerollMultiplier < 0) { // Completely overrides reroll cost to -1 and early exits return -1; } // Otherwise, continue with custom multiplier - multiplier = this.customModifierSettings!.rerollMultiplier!; + multiplier = this.customModifierSettings.rerollMultiplier; } return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, this.rerollCount) * multiplier, Number.MAX_SAFE_INTEGER); } diff --git a/src/test/mystery-encounter/encounter-test-utils.ts b/src/test/mystery-encounter/encounter-test-utils.ts index a31ee150bfd..9fb2504c02b 100644 --- a/src/test/mystery-encounter/encounter-test-utils.ts +++ b/src/test/mystery-encounter/encounter-test-utils.ts @@ -114,7 +114,7 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN } if (!isNullOrUndefined(secondaryOptionSelect?.pokemonNo)) { - await handleSecondaryOptionSelect(game, secondaryOptionSelect!.pokemonNo, secondaryOptionSelect!.optionNo); + await handleSecondaryOptionSelect(game, secondaryOptionSelect.pokemonNo, secondaryOptionSelect.optionNo); } else { uiHandler.processInput(Button.ACTION); } diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 36423c5e18f..f48fe3ef228 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -195,7 +195,7 @@ export default class GameManager { async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: Species[]) { if (!isNullOrUndefined(encounterType)) { this.override.disableTrainerWaves(); - this.override.mysteryEncounter(encounterType!); + this.override.mysteryEncounter(encounterType); } await this.runToTitle(); diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 08de740e3ec..edff5c25cda 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -95,8 +95,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { super.show(args); this.overrideSettings = args[0] as OptionSelectSettings ?? {}; - const showDescriptionContainer = isNullOrUndefined(this.overrideSettings?.hideDescription) ? true : !this.overrideSettings?.hideDescription; - const slideInDescription = isNullOrUndefined(this.overrideSettings?.slideInDescription) ? true : this.overrideSettings?.slideInDescription; + const showDescriptionContainer = isNullOrUndefined(this.overrideSettings?.hideDescription) ? true : !this.overrideSettings.hideDescription; + const slideInDescription = isNullOrUndefined(this.overrideSettings?.slideInDescription) ? true : this.overrideSettings.slideInDescription; const startingCursorIndex = this.overrideSettings?.startingCursorIndex ?? 0; this.cursorContainer.setVisible(true); diff --git a/src/utils.ts b/src/utils.ts index e526d086316..b029067c8d6 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -584,7 +584,7 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole * Returns if an object is null or undefined * @param object */ -export function isNullOrUndefined(object: any): boolean { +export function isNullOrUndefined(object: any): object is undefined | null { return null === object || undefined === object; }