From 903c86a9f6cc23c5aabe83f75ce9c670fb6d1443 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Sun, 22 Jun 2025 16:18:10 -0400 Subject: [PATCH] Cleaned up some code --- src/@types/enum-types.ts | 2 +- src/battle-scene.ts | 3 ++ src/data/daily-run.ts | 1 + .../encounters/berries-abound-encounter.ts | 5 +-- .../encounters/bug-type-superfan-encounter.ts | 2 ++ .../encounters/clowning-around-encounter.ts | 1 + .../teleporting-hijinks-encounter.ts | 1 + .../encounters/weird-dream-encounter.ts | 2 ++ .../mystery-encounter-option.ts | 2 ++ .../mystery-encounters/mystery-encounter.ts | 2 ++ .../utils/encounter-phase-utils.ts | 2 ++ .../utils/encounter-pokemon-utils.ts | 4 +++ src/field/arena.ts | 2 ++ src/field/trainer.ts | 5 ++- src/modifier/modifier-type.ts | 7 ++-- src/phases/select-biome-phase.ts | 1 + src/utils/common.ts | 1 + src/utils/enums.ts | 32 +++++++++++++++---- 18 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/@types/enum-types.ts b/src/@types/enum-types.ts index eb2145ed71f..4abf36b4226 100644 --- a/src/@types/enum-types.ts +++ b/src/@types/enum-types.ts @@ -3,7 +3,7 @@ export type EnumOrObject = Record; /** * Utility type to extract the enum values from a `const object`, - * or convert an `enum` object produced by `typeof Enum` into its literal value union. + * or convert an `enum` object produced by `typeof Enum` into the union type representing its values. */ export type EnumValues = E[keyof E]; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 301e054ea2f..3c598a9363c 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2137,6 +2137,7 @@ export default class BattleScene extends SceneBase { ), ] : allSpecies.filter(s => s.isCatchable()); + // TODO: should this use `randSeedItem`? return filteredSpecies[randSeedInt(filteredSpecies.length)]; } @@ -2162,6 +2163,7 @@ export default class BattleScene extends SceneBase { } } + // TODO: should this use `randSeedItem`? return biomes[randSeedInt(biomes.length)]; } @@ -3681,6 +3683,7 @@ export default class BattleScene extends SceneBase { console.log("No Mystery Encounters found, falling back to Mysterious Challengers."); return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS]; } + // TODO: should this use `randSeedItem`? encounter = availableEncounters[randSeedInt(availableEncounters.length)]; // New encounter object to not dirty flags encounter = new MysteryEncounter(encounter); diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 8bf7c5c828a..960de3497a3 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -166,5 +166,6 @@ export function getDailyStartingBiome(): BiomeId { } // Fallback in case something went wrong + // TODO: should this use `randSeedItem`? return biomes[randSeedInt(biomes.length)]; } diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index d86a8439804..e55188fc3c4 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -15,7 +15,7 @@ import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifi import { regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/data/data-lists"; import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { randSeedInt } from "#app/utils/common"; +import { randSeedItem } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; @@ -38,6 +38,7 @@ import i18next from "#app/plugins/i18n"; import { BerryType } from "#enums/berry-type"; import { PERMANENT_STATS, Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; +import { getEnumValues } from "#app/utils/enums"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/berriesAbound"; @@ -311,7 +312,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. .build(); function tryGiveBerry(prioritizedPokemon?: PlayerPokemon) { - const berryType = randSeedInt(Object.keys(BerryType).filter(s => !Number.isNaN(Number(s))).length) as BerryType; + const berryType = randSeedItem(getEnumValues(BerryType)); const berry = generateModifierType(modifierTypes.BERRY, [berryType]) as BerryModifierType; const party = globalScene.getPlayerParty(); 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 df06f40c159..8c10a4f2b2f 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -292,6 +292,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde // Init the moves available for tutor const moveTutorOptions: PokemonMove[] = []; + // TODO: should this use `randSeedItem`? moveTutorOptions.push(new PokemonMove(PHYSICAL_TUTOR_MOVES[randSeedInt(PHYSICAL_TUTOR_MOVES.length)])); moveTutorOptions.push(new PokemonMove(SPECIAL_TUTOR_MOVES[randSeedInt(SPECIAL_TUTOR_MOVES.length)])); moveTutorOptions.push(new PokemonMove(STATUS_TUTOR_MOVES[randSeedInt(STATUS_TUTOR_MOVES.length)])); @@ -389,6 +390,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde specialOptions.push(rareFormChangeModifier); } if (specialOptions.length > 0) { + // TODO: should this use `randSeedItem`? modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); } diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 818318bb499..a8b780667d4 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -139,6 +139,7 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists // Generate random ability for Blacephalon from pool + // TODO: should this use `randSeedItem`? const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)]; encounter.setDialogueToken("ability", allAbilities[ability].name); encounter.misc = { ability }; diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index 9c9232612c6..4a99fc69ae2 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -190,6 +190,7 @@ async function doBiomeTransitionDialogueAndBattleInit() { // Calculate new biome (cannot be current biome) const filteredBiomes = BIOME_CANDIDATES.filter(b => globalScene.arena.biomeType !== b); + // TODO: should this use `randSeedItem`? const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)]; // Show dialogue and transition biome diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 1ba756c7f5d..3316c844287 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -314,6 +314,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit // One random pokemon will get its passive unlocked const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); if (passiveDisabledPokemon?.length > 0) { + // TODO: should this use `randSeedItem`? const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; enablePassiveMon.passive = true; enablePassiveMon.updateInfo(true); @@ -479,6 +480,7 @@ async function doNewTeamPostProcess(transformations: PokemonTransformation[]) { // One random pokemon will get its passive unlocked const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); if (passiveDisabledPokemon?.length > 0) { + // TODO: should this use `randSeedItem`? const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)]; enablePassiveMon.passive = true; await enablePassiveMon.updateInfo(true); diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index 53b53392bb8..999d448e305 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -145,11 +145,13 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption { } if (truePrimaryPool.length > 0) { // always choose from the non-overlapping pokemon first + // TODO: should this use `randSeedItem`? this.primaryPokemon = truePrimaryPool[randSeedInt(truePrimaryPool.length)]; return true; } // if there are multiple overlapping pokemon, we're okay - just choose one and take it out of the supporting pokemon pool if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) { + // TODO: should this use `randSeedItem`? this.primaryPokemon = overlap[randSeedInt(overlap.length)]; this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon); return true; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index fa97a7f4d40..bc0f56b4925 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -385,6 +385,7 @@ export default class MysteryEncounter implements IMysteryEncounter { // If there are multiple overlapping pokemon, we're okay - just choose one and take it out of the primary pokemon pool if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) { // is this working? + // TODO: should this use `randSeedItem`? this.primaryPokemon = overlap[randSeedInt(overlap.length, 0)]; this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon); return true; @@ -395,6 +396,7 @@ export default class MysteryEncounter implements IMysteryEncounter { return false; } // this means we CAN have the same pokemon be a primary and secondary pokemon, so just choose any qualifying one randomly. + // TODO: should this use `randSeedItem`? this.primaryPokemon = qualified[randSeedInt(qualified.length, 0)]; return true; } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index f698f636cac..12328026712 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1099,8 +1099,10 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { if (biomes! && biomes.length > 0) { const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b)); if (specialBiomes.length > 0) { + // TODO: should this use `randSeedItem`? currentBiome = specialBiomes[randSeedInt(specialBiomes.length)]; } else { + // TODO: should this use `randSeedItem`? currentBiome = biomes[randSeedInt(biomes.length)]; } } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index f6ac5b0d38b..32a4aa26915 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -111,20 +111,24 @@ export function getRandomPlayerPokemon( // If there is only 1 legal/unfainted mon left, select from fainted legal mons const faintedLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && p.isFainted()); if (faintedLegalMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(faintedLegalMons.length); chosenPokemon = faintedLegalMons[chosenIndex]; } } if (!chosenPokemon && fullyLegalMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(fullyLegalMons.length); chosenPokemon = fullyLegalMons[chosenIndex]; } if (!chosenPokemon && isAllowed && allowedOnlyMons.length > 0) { + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(allowedOnlyMons.length); chosenPokemon = allowedOnlyMons[chosenIndex]; } if (!chosenPokemon) { // If no other options worked, returns fully random + // TODO: should this use `randSeedItem`? chosenIndex = randSeedInt(party.length); chosenPokemon = party[chosenIndex]; } diff --git a/src/field/arena.ts b/src/field/arena.ts index 8d7e5037852..55f0d6b4f3c 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -152,6 +152,7 @@ export class Arena { if (!tierPool.length) { ret = globalScene.randomSpecies(waveIndex, level); } else { + // TODO: should this use `randSeedItem`? const entry = tierPool[randSeedInt(tierPool.length)]; let species: SpeciesId; if (typeof entry === "number") { @@ -163,6 +164,7 @@ export class Arena { if (level >= levelThreshold) { const speciesIds = entry[levelThreshold]; if (speciesIds.length > 1) { + // TODO: should this use `randSeedItem`? species = speciesIds[randSeedInt(speciesIds.length)]; } else { species = speciesIds[0]; diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 8ac896e2717..a4d125eb9da 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -421,7 +421,8 @@ export default class Trainer extends Phaser.GameObjects.Container { // If useNewSpeciesPool is true, we need to generate a new species from the new species pool, otherwise we generate a random species let species = useNewSpeciesPool - ? getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))]) + ? // TODO: should this use `randSeedItem`? + getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))]) : template.isSameSpecies(index) && index > offset ? getPokemonSpecies( battle.enemyParty[offset].species.getTrainerSpeciesForLevel( @@ -619,6 +620,8 @@ export default class Trainer extends Phaser.GameObjects.Container { if (maxScorePartyMemberIndexes.length > 1) { let rand: number; + // TODO: should this use `randSeedItem`? + globalScene.executeWithSeedOffset( () => (rand = randSeedInt(maxScorePartyMemberIndexes.length)), globalScene.currentBattle.turn << 2, diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 6c5be4399ce..eff5e084ec7 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -103,7 +103,7 @@ import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#app/system import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import PartyUiHandler from "#app/ui/party-ui-handler"; import { getModifierTierTextTint } from "#app/ui/text"; -import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt } from "#app/utils/common"; +import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#app/utils/common"; import { getEnumKeys, getEnumValues } from "#app/utils/enums"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; @@ -1558,6 +1558,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator { if (!tierUniqueCompatibleTms.length) { return null; } + // TODO: should this use `randSeedItem`? const randTmIndex = randSeedInt(tierUniqueCompatibleTms.length); return new TmModifierType(tierUniqueCompatibleTms[randTmIndex]); }); @@ -1611,6 +1612,7 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { return null; } + // TODO: should this use `randSeedItem`? return new EvolutionItemModifierType(evolutionItemPool[randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? }); } @@ -1696,6 +1698,7 @@ export class FormChangeItemModifierTypeGenerator extends ModifierTypeGenerator { return null; } + // TODO: should this use `randSeedItem`? return new FormChangeItemModifierType(formChangeItemPool[randSeedInt(formChangeItemPool.length)]); }); } @@ -1966,7 +1969,7 @@ const modifierTypeInitObj = Object.freeze({ if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) { return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature); } - return new PokemonNatureChangeModifierType(randSeedInt(getEnumValues(Nature).length) as Nature); + return new PokemonNatureChangeModifierType(randSeedItem(getEnumValues(Nature))); }), MYSTICAL_ROCK: () => diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index e8b4946b6d1..679d159bc01 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -56,6 +56,7 @@ export class SelectBiomePhase extends BattlePhase { delay: 1000, }); } else { + // TODO: should this use `randSeedItem`? setNextBiome(biomes[randSeedInt(biomes.length)]); } } else if (biomeLinks.hasOwnProperty(currentBiome)) { diff --git a/src/utils/common.ts b/src/utils/common.ts index 3f13d40c82c..c9029f0fdde 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -9,6 +9,7 @@ export type nil = null | undefined; export const MissingTextureKey = "__MISSING"; // TODO: Draft tests for these utility functions +// TODO: Break up this file /** * Convert a `snake_case` string in any capitalization (such as one from an enum reverse mapping) * into a readable `Title Case` version. diff --git a/src/utils/enums.ts b/src/utils/enums.ts index dcc3ce1d335..1e1e924fe01 100644 --- a/src/utils/enums.ts +++ b/src/utils/enums.ts @@ -1,15 +1,23 @@ -// biome-ignore lint/correctness/noUnusedImports: Used for a JSDoc comment import type { EnumOrObject, EnumValues, TSNumericEnum, NormalEnum } from "#app/@types/enum-types"; /** * Return the string keys of an Enum object, excluding reverse-mapped numbers. * @param enumType - The numeric enum to retrieve keys for. * @returns An ordered array of all of `enumType`'s string keys. + * @example + * enum fruit { + * apple = 1, + * banana = 2, + * cherry = 3, + * orange = 12, + * }; + * + * console.log(getEnumKeys(fruit)); // output: ["apple", "banana", "cherry", "orange"] * @remarks * To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.keys} instead. */ export function getEnumKeys(enumType: TSNumericEnum): (keyof E)[] { - // All enum values are either normal or reverse mapped, so we can retrieve the keys by filtering out all strings. + // All enum values are either normal numbers or reverse mapped strings, so we can retrieve the keys by filtering out numbers. return Object.values(enumType).filter(v => typeof v === "string"); } @@ -17,10 +25,20 @@ export function getEnumKeys(enumType: TSNumericEnum): * Return the numeric values of a numeric Enum object, excluding reverse-mapped strings. * @param enumType - The enum object to retrieve keys for. * @returns An ordered array of all of `enumType`'s number values. + * @example + * enum fruit { + * apple = 1, + * banana = 2, + * cherry = 3, + * orange = 12, + * }; + * + * console.log(getEnumValues(fruit)); // output: [1, 2, 3, 12] + * * @remarks * To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.values} instead. */ -// NB: This does not use `EnumValues` due to variable highlighting in IDEs. +// NB: This does not use `EnumValues` as it messes with variable highlighting in IDEs. export function getEnumValues(enumType: TSNumericEnum): E[keyof E][] { return Object.values(enumType).filter(v => typeof v !== "string") as E[keyof E][]; } @@ -28,7 +46,7 @@ export function getEnumValues(enumType: TSNumericEnum /** * Return the name of the key that matches the given Enum value. * Can be used to emulate Typescript reverse mapping for `const object`s or string enums. - * @param object - The {@linkcode EnumOrObject} to check + * @param object - The {@linkcode NormalEnum} to check. * @param val - The value to get the key of. * @returns The name of the key with the specified value. * @example @@ -36,15 +54,15 @@ export function getEnumValues(enumType: TSNumericEnum * one: 1, * two: 2, * } as const; - * console.log(enumValueToKey(thing, thing.two)); // output: "two" + * console.log(enumValueToKey(thing, 2)); // output: "two" * @throws Error if an invalid enum value is passed to the function * @remarks * If multiple keys map to the same value, the first one (in insertion order) will be retrieved. */ -export function enumValueToKey(object: T, val: T[keyof T]): keyof T { +export function enumValueToKey(object: NormalEnum, val: EnumValues): keyof T { for (const [key, value] of Object.entries(object)) { if (val === value) { - return key as keyof T; + return key; } } throw new Error(`Invalid value passed to \`enumValueToKey\`! Value: ${val}`);