diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index f60ada2c6e9..eb8fdc4a85e 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -1,6 +1,5 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { - generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, @@ -12,8 +11,6 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate"; import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; import { RewardTier } from "#enums/reward-tier"; -import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { globalScene } from "#app/global-scene"; @@ -24,10 +21,7 @@ import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { getPokemonSpecies } from "#app/utils/pokemon-utils"; import { AbilityId } from "#enums/ability-id"; -import { - applyAbilityOverrideToPokemon, - applyModifierTypeToPlayerPokemon, -} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; +import { applyAbilityOverrideToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { PokemonType } from "#enums/pokemon-type"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -38,8 +32,6 @@ import i18next from "i18next"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { BerryModifier } from "#app/modifier/modifier"; -import { BerryType } from "#enums/berry-type"; import { BattlerIndex } from "#enums/battler-index"; import { MoveId } from "#enums/move-id"; import { EncounterBattleAnim } from "#app/data/battle-anims"; @@ -49,7 +41,10 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { EncounterAnim } from "#enums/encounter-anims"; import { Challenges } from "#enums/challenges"; import { MoveUseMode } from "#enums/move-use-mode"; -import { allAbilities, modifierTypes } from "#app/data/data-lists"; +import { allAbilities } from "#app/data/data-lists"; +import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id"; +import { getHeldItemTier } from "#app/items/held-item-tiers"; +import { assignItemsFromConfiguration } from "#app/items/held-item-pool"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/clowningAround"; @@ -283,16 +278,16 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder const party = globalScene.getPlayerParty(); let mostHeldItemsPokemon = party[0]; - let count = mostHeldItemsPokemon - .getHeldItems() - .filter(m => m.isTransferable && !(m instanceof BerryModifier)) - .reduce((v, m) => v + m.stackCount, 0); + let count = mostHeldItemsPokemon.heldItemManager + .getTransferableHeldItems() + .filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY)) + .reduce((v, m) => v + mostHeldItemsPokemon.heldItemManager.getStack(m), 0); for (const pokemon of party) { - const nextCount = pokemon - .getHeldItems() - .filter(m => m.isTransferable && !(m instanceof BerryModifier)) - .reduce((v, m) => v + m.stackCount, 0); + const nextCount = pokemon.heldItemManager + .getTransferableHeldItems() + .filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY)) + .reduce((v, m) => v + pokemon.heldItemManager.getStack(m), 0); if (nextCount > count) { mostHeldItemsPokemon = pokemon; count = nextCount; @@ -301,16 +296,31 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender()); - const items = mostHeldItemsPokemon.getHeldItems(); + const items = mostHeldItemsPokemon.heldItemManager + .getTransferableHeldItems() + .filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY)); // Shuffles Berries (if they have any) + const oldBerries = mostHeldItemsPokemon.heldItemManager + .getHeldItems() + .filter(m => isItemInCategory(m, HeldItemCategoryId.BERRY)); + let numBerries = 0; - for (const m of items.filter(m => m instanceof BerryModifier)) { - numBerries += m.stackCount; - globalScene.removeModifier(m); + for (const berry of oldBerries) { + const stack = mostHeldItemsPokemon.heldItemManager.getStack(berry); + numBerries += stack; + mostHeldItemsPokemon.heldItemManager.remove(berry, stack); } - generateItemsOfTier(mostHeldItemsPokemon, numBerries, "Berries"); + assignItemsFromConfiguration( + [ + { + entry: HeldItemCategoryId.BERRY, + count: numBerries, + }, + ], + mostHeldItemsPokemon, + ); // Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm) // For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier @@ -318,20 +328,36 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder let numUltra = 0; let numRogue = 0; - for (const m of items.filter(m => m.isTransferable && !(m instanceof BerryModifier))) { - const type = m.type.withTierFromPool(ModifierPoolType.PLAYER, party); - const tier = type.tier ?? RewardTier.ULTRA; - if (type.id === "GOLDEN_EGG" || tier === RewardTier.ROGUE) { - numRogue += m.stackCount; - globalScene.removeModifier(m); - } else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === RewardTier.ULTRA) { - numUltra += m.stackCount; - globalScene.removeModifier(m); + for (const m of items) { + const tier = getHeldItemTier(m) ?? RewardTier.ULTRA; + const stack = mostHeldItemsPokemon.heldItemManager.getStack(m); + if (tier === RewardTier.ROGUE) { + numRogue += stack; + } else if (tier === RewardTier.ULTRA) { + numUltra += stack; } + mostHeldItemsPokemon.heldItemManager.remove(m, stack); } - generateItemsOfTier(mostHeldItemsPokemon, numUltra, RewardTier.ULTRA); - generateItemsOfTier(mostHeldItemsPokemon, numRogue, RewardTier.ROGUE); + assignItemsFromConfiguration( + [ + { + entry: ultraPool, + count: numUltra, + }, + ], + mostHeldItemsPokemon, + ); + + assignItemsFromConfiguration( + [ + { + entry: roguePool, + count: numRogue, + }, + ], + mostHeldItemsPokemon, + ); }) .withOptionPhase(async () => { leaveEncounterWithoutBattle(true); @@ -487,68 +513,21 @@ function onYesAbilitySwap(resolve) { selectPokemonForOption(onPokemonSelected, onPokemonNotSelected); } -function generateItemsOfTier(pokemon: PlayerPokemon, numItems: number, tier: RewardTier | "Berries") { - // These pools have to be defined at runtime so that modifierTypes exist - // Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon - // This is to prevent "over-generating" a random item of a certain type during item swaps - const ultraPool = [ - [modifierTypes.REVIVER_SEED, 1], - [modifierTypes.GOLDEN_PUNCH, 5], - [modifierTypes.ATTACK_TYPE_BOOSTER, 99], - [modifierTypes.QUICK_CLAW, 3], - [modifierTypes.WIDE_LENS, 3], - ]; +const ultraPool = [ + { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 1 }, + { entry: HeldItemId.REVIVER_SEED, weight: 1 }, + { entry: HeldItemId.GOLDEN_PUNCH, weight: 1 }, + { entry: HeldItemId.QUICK_CLAW, weight: 1 }, + { entry: HeldItemId.WIDE_LENS, weight: 1 }, +]; - const roguePool = [ - [modifierTypes.LEFTOVERS, 4], - [modifierTypes.SHELL_BELL, 4], - [modifierTypes.SOUL_DEW, 10], - [modifierTypes.SCOPE_LENS, 1], - [modifierTypes.BATON, 1], - [modifierTypes.FOCUS_BAND, 5], - [modifierTypes.KINGS_ROCK, 3], - [modifierTypes.GRIP_CLAW, 5], - ]; - - const berryPool = [ - [BerryType.APICOT, 3], - [BerryType.ENIGMA, 2], - [BerryType.GANLON, 3], - [BerryType.LANSAT, 3], - [BerryType.LEPPA, 2], - [BerryType.LIECHI, 3], - [BerryType.LUM, 2], - [BerryType.PETAYA, 3], - [BerryType.SALAC, 2], - [BerryType.SITRUS, 2], - [BerryType.STARF, 3], - ]; - - let pool: any[]; - if (tier === "Berries") { - pool = berryPool; - } else { - pool = tier === RewardTier.ULTRA ? ultraPool : roguePool; - } - - for (let i = 0; i < numItems; i++) { - if (pool.length === 0) { - // Stop generating new items if somehow runs out of items to spawn - return; - } - const randIndex = randSeedInt(pool.length); - const newItemType = pool[randIndex]; - let newMod: PokemonHeldItemModifierType; - if (tier === "Berries") { - newMod = generateModifierType(modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType; - } else { - newMod = generateModifierType(newItemType[0]) as PokemonHeldItemModifierType; - } - applyModifierTypeToPlayerPokemon(pokemon, newMod); - // Decrement max stacks and remove from pool if at max - newItemType[1]--; - if (newItemType[1] <= 0) { - pool.splice(randIndex, 1); - } - } -} +const roguePool = [ + { entry: HeldItemId.LEFTOVERS, weight: 1 }, + { entry: HeldItemId.SHELL_BELL, weight: 1 }, + { entry: HeldItemId.SOUL_DEW, weight: 1 }, + { entry: HeldItemId.SCOPE_LENS, weight: 1 }, + { entry: HeldItemId.BATON, weight: 1 }, + { entry: HeldItemId.FOCUS_BAND, weight: 1 }, + { entry: HeldItemId.KINGS_ROCK, weight: 1 }, + { entry: HeldItemId.GRIP_CLAW, weight: 1 }, +]; 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 4a84d62c688..433c4d423f2 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -46,9 +46,10 @@ import type { PokeballType } from "#enums/pokeball"; import { doShinySparkleAnim } from "#app/field/anims"; import { TrainerType } from "#enums/trainer-type"; import { timedEventManager } from "#app/global-event-manager"; -import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id"; +import { HeldItemCategoryId, type HeldItemId, isItemInCategory } from "#enums/held-item-id"; import { allHeldItems } from "#app/items/all-held-items"; import { RewardTier } from "#enums/reward-tier"; +import { getHeldItemTier } from "#app/items/held-item-tiers"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/globalTradeSystem"; @@ -419,7 +420,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; // Check tier of the traded item, the received item will be one tier up - let tier = globalTradeItemTiers[heldItemId] ?? RewardTier.GREAT; + let tier = getHeldItemTier(heldItemId) ?? RewardTier.GREAT; // Increment tier by 1 if (tier < RewardTier.MASTER) { @@ -975,38 +976,3 @@ function generateRandomTraderName() { const trainerNames = trainerNameString.split(" & "); return trainerNames[randInt(trainerNames.length)]; } - -const globalTradeItemTiers = { - [HeldItemCategoryId.BERRY]: RewardTier.COMMON, - - [HeldItemCategoryId.BASE_STAT_BOOST]: RewardTier.GREAT, - [HeldItemId.WHITE_HERB]: RewardTier.GREAT, - [HeldItemId.METAL_POWDER]: RewardTier.GREAT, - [HeldItemId.QUICK_POWDER]: RewardTier.GREAT, - [HeldItemId.DEEP_SEA_SCALE]: RewardTier.GREAT, - [HeldItemId.DEEP_SEA_TOOTH]: RewardTier.GREAT, - - [HeldItemCategoryId.TYPE_ATTACK_BOOSTER]: RewardTier.ULTRA, - [HeldItemId.REVIVER_SEED]: RewardTier.ULTRA, - [HeldItemId.LIGHT_BALL]: RewardTier.ULTRA, - [HeldItemId.EVIOLITE]: RewardTier.ULTRA, - [HeldItemId.QUICK_CLAW]: RewardTier.ULTRA, - [HeldItemId.MYSTICAL_ROCK]: RewardTier.ULTRA, - [HeldItemId.WIDE_LENS]: RewardTier.ULTRA, - [HeldItemId.GOLDEN_PUNCH]: RewardTier.ULTRA, - [HeldItemId.TOXIC_ORB]: RewardTier.ULTRA, - [HeldItemId.FLAME_ORB]: RewardTier.ULTRA, - [HeldItemId.LUCKY_EGG]: RewardTier.ULTRA, - - [HeldItemId.FOCUS_BAND]: RewardTier.ROGUE, - [HeldItemId.KINGS_ROCK]: RewardTier.ROGUE, - [HeldItemId.LEFTOVERS]: RewardTier.ROGUE, - [HeldItemId.SHELL_BELL]: RewardTier.ROGUE, - [HeldItemId.GRIP_CLAW]: RewardTier.ROGUE, - [HeldItemId.SOUL_DEW]: RewardTier.ROGUE, - [HeldItemId.BATON]: RewardTier.ROGUE, - [HeldItemId.GOLDEN_EGG]: RewardTier.ULTRA, - - [HeldItemId.MINI_BLACK_HOLE]: RewardTier.MASTER, - [HeldItemId.MULTI_LENS]: RewardTier.MASTER, -}; diff --git a/src/enums/held-item-id.ts b/src/enums/held-item-id.ts index e371662f7a2..823cc614949 100644 --- a/src/enums/held-item-id.ts +++ b/src/enums/held-item-id.ts @@ -123,7 +123,7 @@ export type HeldItemCategoryId = (typeof HeldItemCategoryId)[keyof typeof HeldIt const ITEM_CATEGORY_MASK = 0xFF00 -function getHeldItemCategory(itemId: HeldItemId): HeldItemCategoryId { +export function getHeldItemCategory(itemId: HeldItemId): HeldItemCategoryId { return itemId & ITEM_CATEGORY_MASK; } diff --git a/src/items/held-item-data-types.ts b/src/items/held-item-data-types.ts index 4144d3b7732..099f0f2ee06 100644 --- a/src/items/held-item-data-types.ts +++ b/src/items/held-item-data-types.ts @@ -50,7 +50,7 @@ export function isHeldItemCategoryEntry(entry: any): entry is HeldItemCategoryEn } type HeldItemPoolEntry = { - entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs; + entry: HeldItemId | HeldItemCategoryId | HeldItemCategoryEntry | HeldItemSpecs; weight: number | HeldItemWeightFunc; }; @@ -65,7 +65,7 @@ export type HeldItemTieredPool = { }; type HeldItemConfigurationEntry = { - entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs | HeldItemPool; + entry: HeldItemId | HeldItemCategoryId | HeldItemCategoryEntry | HeldItemSpecs | HeldItemPool; count?: number | (() => number); }; diff --git a/src/items/held-item-pool.ts b/src/items/held-item-pool.ts index 69b06058a9b..7041abbf579 100644 --- a/src/items/held-item-pool.ts +++ b/src/items/held-item-pool.ts @@ -2,7 +2,7 @@ import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { coerceArray, getEnumValues, randSeedFloat, randSeedInt } from "#app/utils/common"; import { BerryType } from "#enums/berry-type"; -import { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; +import { HeldItemCategoryId, HeldItemId, isCategoryId } from "#enums/held-item-id"; import { HeldItemPoolType } from "#enums/modifier-pool-type"; import type { PokemonType } from "#enums/pokemon-type"; import { RewardTier } from "#enums/reward-tier"; @@ -263,6 +263,9 @@ function getNewHeldItemFromPool(pool: HeldItemPool, pokemon: Pokemon, party?: Po const entry = pool[pickWeightedIndex(weights)].entry; if (typeof entry === "number") { + if (isCategoryId(entry)) { + return getNewHeldItemFromCategory(entry, party ?? pokemon, {}, pokemon) as HeldItemId; + } return entry as HeldItemId; } @@ -273,12 +276,24 @@ function getNewHeldItemFromPool(pool: HeldItemPool, pokemon: Pokemon, party?: Po return entry as HeldItemSpecs; } +function assignItemsFromCategory(id: HeldItemCategoryId, pokemon: Pokemon, count: number) { + for (let i = 1; i <= count; i++) { + const newItem = getNewHeldItemFromCategory(id, pokemon, {}, pokemon); + if (newItem) { + pokemon.heldItemManager.add(newItem); + } + } +} + export function assignItemsFromConfiguration(config: HeldItemConfiguration, pokemon: Pokemon) { config.forEach(item => { const { entry, count } = item; const actualCount = typeof count === "function" ? count() : (count ?? 1); if (typeof entry === "number") { + if (isCategoryId(entry)) { + assignItemsFromCategory(entry, pokemon, actualCount); + } pokemon.heldItemManager.add(entry, actualCount); } @@ -287,12 +302,7 @@ export function assignItemsFromConfiguration(config: HeldItemConfiguration, poke } if (isHeldItemCategoryEntry(entry)) { - for (let i = 1; i <= actualCount; i++) { - const newItem = getNewHeldItemFromCategory(entry.id, pokemon, entry?.customWeights, pokemon); - if (newItem) { - pokemon.heldItemManager.add(newItem); - } - } + assignItemsFromCategory(entry.id, pokemon, actualCount); } if (isHeldItemPool(entry)) { diff --git a/src/items/held-item-tiers.ts b/src/items/held-item-tiers.ts new file mode 100644 index 00000000000..fb340998198 --- /dev/null +++ b/src/items/held-item-tiers.ts @@ -0,0 +1,47 @@ +import { getHeldItemCategory, HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; +import { RewardTier } from "#enums/reward-tier"; + +export const heldItemTiers = { + [HeldItemCategoryId.BERRY]: RewardTier.COMMON, + + [HeldItemCategoryId.BASE_STAT_BOOST]: RewardTier.GREAT, + [HeldItemId.WHITE_HERB]: RewardTier.GREAT, + [HeldItemId.METAL_POWDER]: RewardTier.GREAT, + [HeldItemId.QUICK_POWDER]: RewardTier.GREAT, + [HeldItemId.DEEP_SEA_SCALE]: RewardTier.GREAT, + [HeldItemId.DEEP_SEA_TOOTH]: RewardTier.GREAT, + [HeldItemId.SOOTHE_BELL]: RewardTier.GREAT, + + [HeldItemCategoryId.TYPE_ATTACK_BOOSTER]: RewardTier.ULTRA, + [HeldItemId.REVIVER_SEED]: RewardTier.ULTRA, + [HeldItemId.LIGHT_BALL]: RewardTier.ULTRA, + [HeldItemId.EVIOLITE]: RewardTier.ULTRA, + [HeldItemId.QUICK_CLAW]: RewardTier.ULTRA, + [HeldItemId.MYSTICAL_ROCK]: RewardTier.ULTRA, + [HeldItemId.WIDE_LENS]: RewardTier.ULTRA, + [HeldItemId.GOLDEN_PUNCH]: RewardTier.ULTRA, + [HeldItemId.TOXIC_ORB]: RewardTier.ULTRA, + [HeldItemId.FLAME_ORB]: RewardTier.ULTRA, + [HeldItemId.LUCKY_EGG]: RewardTier.ULTRA, + + [HeldItemId.FOCUS_BAND]: RewardTier.ROGUE, + [HeldItemId.KINGS_ROCK]: RewardTier.ROGUE, + [HeldItemId.LEFTOVERS]: RewardTier.ROGUE, + [HeldItemId.SHELL_BELL]: RewardTier.ROGUE, + [HeldItemId.GRIP_CLAW]: RewardTier.ROGUE, + [HeldItemId.SOUL_DEW]: RewardTier.ROGUE, + [HeldItemId.BATON]: RewardTier.ROGUE, + [HeldItemId.GOLDEN_EGG]: RewardTier.ULTRA, + + [HeldItemId.MINI_BLACK_HOLE]: RewardTier.MASTER, + [HeldItemId.MULTI_LENS]: RewardTier.MASTER, +}; + +export function getHeldItemTier(item: HeldItemId): RewardTier | undefined { + let tier = heldItemTiers[item]; + if (!tier) { + const category = getHeldItemCategory(item); + tier = heldItemTiers[category]; + } + return tier; +}