diff --git a/src/field/pokemon-held-item-manager.ts b/src/field/pokemon-held-item-manager.ts index bd6133551bf..c30b660c855 100644 --- a/src/field/pokemon-held-item-manager.ts +++ b/src/field/pokemon-held-item-manager.ts @@ -1,22 +1,7 @@ import { allHeldItems } from "#app/items/all-held-items"; import { isItemInRequested, type HeldItemCategoryId, type HeldItemId } from "#app/enums/held-item-id"; import type { FormChangeItem } from "#enums/form-change-item"; -import type { BASE_STAT_TOTAL_DATA } from "#app/items/held-items/base-stat-total"; -import type { BASE_STAT_FLAT_DATA } from "#app/items/held-items/base-stat-flat"; - -type HELD_ITEM_DATA = BASE_STAT_TOTAL_DATA | BASE_STAT_FLAT_DATA; - -interface HeldItemProperties { - stack: number; - disabled?: boolean; - unstealable?: boolean; //TODO: ensure this is taken into account by stealing effects - cooldown?: number; - data?: HELD_ITEM_DATA; -} - -export type HeldItemPropertyMap = { - [key in HeldItemId]?: HeldItemProperties; -}; +import { isHeldItemSpecs, type HeldItemDataMap, type HeldItemSpecs } from "#app/items/held-item-data-types"; interface FormChangeItemProperties { active: boolean; @@ -27,7 +12,7 @@ export type FormChangeItemPropertyMap = { }; export class PokemonItemManager { - public heldItems: HeldItemPropertyMap; + public heldItems: HeldItemDataMap; public formChangeItems: FormChangeItemPropertyMap; constructor() { @@ -61,14 +46,6 @@ export class PokemonItemManager { return itemType in this.heldItems; } - /* - getItem(itemType: HeldItemId): HeldItemProperties { - if (itemType in this.heldItems) { - return this.heldItems[itemType]; - } - } -*/ - getStack(itemType: HeldItemId): number { const item = this.heldItems[itemType]; return item ? item.stack : 0; @@ -79,7 +56,7 @@ export class PokemonItemManager { return item ? item.stack >= allHeldItems[itemType].getMaxStackCount() : false; } - overrideItems(newItems: HeldItemPropertyMap) { + overrideItems(newItems: HeldItemDataMap) { this.heldItems = newItems; // The following is to allow randomly generated item configs to have stack 0 for (const [item, properties] of Object.entries(this.heldItems)) { @@ -89,7 +66,11 @@ export class PokemonItemManager { } } - add(itemType: HeldItemId, addStack = 1, data?: HELD_ITEM_DATA): boolean { + add(itemType: HeldItemId | HeldItemSpecs, addStack = 1): boolean { + if (isHeldItemSpecs(itemType)) { + return this.addItemWithSpecs(itemType); + } + const maxStack = allHeldItems[itemType].getMaxStackCount(); const item = this.heldItems[itemType]; @@ -100,12 +81,25 @@ export class PokemonItemManager { return true; } } else { - this.heldItems[itemType] = { stack: Math.min(addStack, maxStack), disabled: false, data: data }; + this.heldItems[itemType] = { stack: Math.min(addStack, maxStack) }; return true; } return false; } + addItemWithSpecs(itemSpecs: HeldItemSpecs): boolean { + const id = itemSpecs.id; + const maxStack = allHeldItems[id].getMaxStackCount(); + const item = this.heldItems[id]; + + const tempStack = item?.stack ?? 0; + + this.heldItems[id] = itemSpecs; + this.heldItems[id].stack = Math.min(itemSpecs.stack + tempStack, maxStack); + + return true; + } + remove(itemType: HeldItemId, removeStack = 1, all = false) { const item = this.heldItems[itemType]; diff --git a/src/items/held-item-data-types.ts b/src/items/held-item-data-types.ts new file mode 100644 index 00000000000..ff291b04282 --- /dev/null +++ b/src/items/held-item-data-types.ts @@ -0,0 +1,69 @@ +import type Pokemon from "#app/field/pokemon"; +import type { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; +import type { RewardTier } from "#enums/reward-tier"; +import type { Stat } from "#enums/stat"; + +export interface BASE_STAT_TOTAL_DATA { + statModifier: number; +} + +export interface BASE_STAT_FLAT_DATA { + statModifier: number; + stats: Stat[]; +} + +export type HeldItemExtraData = BASE_STAT_TOTAL_DATA | BASE_STAT_FLAT_DATA; + +export type HeldItemData = { + stack: number; + disabled?: boolean; + unstealable?: boolean; + cooldown?: number; + data?: HeldItemExtraData; +}; + +export type HeldItemDataMap = { + [key in HeldItemId]?: HeldItemData; +}; + +export type HeldItemSpecs = HeldItemData & { + id: HeldItemId; +}; + +export function isHeldItemSpecs(entry: any): entry is HeldItemSpecs { + return typeof entry.id === "number" && "stack" in entry; +} + +export type HeldItemWeights = { + [key in HeldItemId]?: number; +}; + +export type HeldItemWeightFunc = (party: Pokemon[]) => number; + +export type HeldItemCategoryEntry = HeldItemData & { + id: HeldItemCategoryId; + customWeights?: HeldItemWeights; +}; + +export function isHeldItemCategoryEntry(entry: any): entry is HeldItemCategoryEntry { + return isHeldItemCategoryEntry(entry.id) && "customWeights" in entry; +} + +type HeldItemPoolEntry = { + entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs; + weight: number | HeldItemWeightFunc; +}; + +export type HeldItemPool = HeldItemPoolEntry[]; + +export type HeldItemTieredPool = { + [key in RewardTier]?: HeldItemPool; +}; + +type HeldItemConfigurationEntry = { + entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs | HeldItemPool; + count?: number | (() => number); + excluded?: HeldItemId[]; +}; + +export type HeldItemConfiguration = HeldItemConfigurationEntry[]; diff --git a/src/items/held-item-pool.ts b/src/items/held-item-pool.ts index 196f2121325..ab778d68f74 100644 --- a/src/items/held-item-pool.ts +++ b/src/items/held-item-pool.ts @@ -1,110 +1,157 @@ -/* import type { PlayerPokemon } from "#app/field/pokemon"; -import { randSeedInt } from "#app/utils/common"; -import { HeldItemCategoryId, HeldItemId, isCategoryId } from "#enums/held-item-id"; +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 type { PokemonType } from "#enums/pokemon-type"; import { RewardTier } from "#enums/reward-tier"; +import { PERMANENT_STATS } from "#enums/stat"; +import { + type HeldItemPool, + type HeldItemSpecs, + type HeldItemTieredPool, + type HeldItemWeights, + isHeldItemCategoryEntry, +} from "./held-item-data-types"; +import { attackTypeToHeldItem } from "./held-items/attack-type-booster"; +import { permanentStatToHeldItem } from "./held-items/base-stat-booster"; +import { berryTypeToHeldItem } from "./held-items/berry"; -interface HeldItemPoolEntry { - weight: number; - item: HeldItemId | HeldItemCategoryId; -} +export const wildHeldItemPool: HeldItemTieredPool = {}; -export type HeldItemPool = { - [key in RewardTier]?: HeldItemPoolEntry[]; -}; +export const trainerHeldItemPool: HeldItemTieredPool = {}; -const dailyStarterHeldItemPool: HeldItemPool = { - [RewardTier.COMMON]: [ - { item: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }, - { item: HeldItemCategoryId.BERRY, weight: 3 }, - ], - [RewardTier.GREAT]: [{ item: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }], - [RewardTier.ULTRA]: [ - { item: HeldItemId.REVIVER_SEED, weight: 4 }, - { item: HeldItemId.SOOTHE_BELL, weight: 1 }, - { item: HeldItemId.SOUL_DEW, weight: 1 }, - { item: HeldItemId.GOLDEN_PUNCH, weight: 1 }, - ], - [RewardTier.ROGUE]: [ - { item: HeldItemId.GRIP_CLAW, weight: 5 }, - { item: HeldItemId.BATON, weight: 2 }, - { item: HeldItemId.FOCUS_BAND, weight: 5 }, - { item: HeldItemId.QUICK_CLAW, weight: 3 }, - { item: HeldItemId.KINGS_ROCK, weight: 3 }, - ], - [RewardTier.MASTER]: [ - { item: HeldItemId.LEFTOVERS, weight: 1 }, - { item: HeldItemId.SHELL_BELL, weight: 1 }, - ], -}; +export const dailyStarterHeldItemPool: HeldItemTieredPool = {}; -export function getDailyRunStarterHeldItems(party: PlayerPokemon[]): PokemonHeldItemModifier[] { - const ret: HeldItemId[] = []; +export function getDailyRunStarterHeldItems(party: PlayerPokemon[]) { for (const p of party) { for (let m = 0; m < 3; m++) { const tierValue = randSeedInt(64); - let tier: RewardTier; - if (tierValue > 25) { - tier = RewardTier.COMMON; - } else if (tierValue > 12) { - tier = RewardTier.GREAT; - } else if (tierValue > 4) { - tier = RewardTier.ULTRA; - } else if (tierValue) { - tier = RewardTier.ROGUE; - } else { - tier = RewardTier.MASTER; - } + const tier = getDailyRewardTier(tierValue); - const item = getNewHeldItemFromPool(party, dailyStarterHeldItemPool, tier); - ret.push(item); + const item = getNewHeldItemFromPool(dailyStarterHeldItemPool[tier] as HeldItemPool, p); + p.heldItemManager.add(item); } } - - return ret; } -function getNewModifierTypeOption( - party: Pokemon[], - poolType: ModifierPoolType, - baseTier?: ModifierTier, - upgradeCount?: number, - retryCount = 0, - allowLuckUpgrades = true, -): ModifierTypeOption | null { - const player = !poolType; - const pool = getModifierPoolForType(poolType); - const thresholds = getPoolThresholds(poolType); +function getDailyRewardTier(tierValue: number): RewardTier { + if (tierValue > 25) { + return RewardTier.COMMON; + } + if (tierValue > 12) { + return RewardTier.GREAT; + } + if (tierValue > 4) { + return RewardTier.ULTRA; + } + if (tierValue > 0) { + return RewardTier.ROGUE; + } + return RewardTier.MASTER; +} - const tier = determineTier(party, player, baseTier, upgradeCount, retryCount, allowLuckUpgrades); +function pickWeightedIndex(weights: number[]): number { + const totalWeight = weights.reduce((sum, w) => sum + w, 0); - const tierThresholds = Object.keys(thresholds[tier]); - const totalWeight = Number.parseInt(tierThresholds[tierThresholds.length - 1]); - const value = randSeedInt(totalWeight); - let index: number | undefined; - for (const t of tierThresholds) { - const threshold = Number.parseInt(t); - if (value < threshold) { - index = thresholds[tier][threshold]; - break; - } + if (totalWeight <= 0) { + throw new Error("Total weight must be greater than 0."); } - if (index === undefined) { + let r = randSeedFloat() * totalWeight; + + for (let i = 0; i < weights.length; i++) { + if (r < weights[i]) { + return i; + } + r -= weights[i]; + } + + return -1; // TODO: Change to something more appropriate +} + +export function getNewVitaminHeldItem(customWeights: HeldItemWeights = {}): HeldItemId { + const items = PERMANENT_STATS.map(s => permanentStatToHeldItem[s]); + const weights = items.map(t => customWeights[t] ?? t); + return items[pickWeightedIndex(weights)]; +} + +export function getNewBerryHeldItem(customWeights: HeldItemWeights = {}): HeldItemId { + const berryTypes = getEnumValues(BerryType); + const items = berryTypes.map(b => berryTypeToHeldItem[b]); + + const weights = items.map(t => + (customWeights[t] ?? (t === HeldItemId.SITRUS_BERRY || t === HeldItemId.LUM_BERRY || t === HeldItemId.LEPPA_BERRY)) + ? 2 + : 1, + ); + + return items[pickWeightedIndex(weights)]; +} + +export function getNewAttackTypeBoosterHeldItem( + pokemon: Pokemon | Pokemon[], + customWeights: HeldItemWeights = {}, +): HeldItemId | null { + const party = coerceArray(pokemon); + + // TODO: make this consider moves or abilities that change types + const attackMoveTypes = party.flatMap(p => + p + .getMoveset() + .filter(m => m.getMove().is("AttackMove")) + .map(m => m.getMove().type), + ); + if (!attackMoveTypes.length) { return null; } - if (player) { - console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]); - } + const attackMoveTypeWeights = attackMoveTypes.reduce((map, type) => { + const current = map.get(type) ?? 0; + if (current < 3) { + map.set(type, current + 1); + } + return map; + }, new Map()); - const item = pool[tier][index].item; - if (isCategoryId(item)) { - return getNewHeldItemCategoryOption(item); - } - return item; + const types = Array.from(attackMoveTypeWeights.keys()); - // console.log(modifierType, !player ? "(enemy)" : ""); + const weights = types.map(type => customWeights[attackTypeToHeldItem[type]] ?? attackMoveTypeWeights.get(type)!); + + const type = types[pickWeightedIndex(weights)]; + return attackTypeToHeldItem[type]; +} + +export function getNewHeldItemFromCategory( + id: HeldItemCategoryId, + pokemon: Pokemon | Pokemon[], + customWeights: HeldItemWeights = {}, +): HeldItemId | null { + if (id === HeldItemCategoryId.BERRY) { + return getNewBerryHeldItem(customWeights); + } + if (id === HeldItemCategoryId.VITAMIN) { + return getNewVitaminHeldItem(customWeights); + } + if (id === HeldItemCategoryId.TYPE_ATTACK_BOOSTER) { + return getNewAttackTypeBoosterHeldItem(pokemon, customWeights); + } + return null; +} + +function getNewHeldItemFromPool(pool: HeldItemPool, pokemon: Pokemon): HeldItemId | HeldItemSpecs { + const weights = pool.map(p => (typeof p.weight === "function" ? p.weight(coerceArray(pokemon)) : p.weight)); + + const entry = pool[pickWeightedIndex(weights)].entry; + + if (typeof entry === "number") { + return entry as HeldItemId; + } + + if (isHeldItemCategoryEntry(entry)) { + return getNewHeldItemFromCategory(entry.id, pokemon, entry?.customWeights) as HeldItemId; + } + + return entry as HeldItemSpecs; } -*/ diff --git a/src/items/held-items/base-stat-flat.ts b/src/items/held-items/base-stat-flat.ts index 5144b392ad0..f191d8a06b9 100644 --- a/src/items/held-items/base-stat-flat.ts +++ b/src/items/held-items/base-stat-flat.ts @@ -1,7 +1,7 @@ import type Pokemon from "#app/field/pokemon"; import type { HeldItemId } from "#enums/held-item-id"; import { HeldItem, ITEM_EFFECT } from "../held-item"; -import { getStatKey, type Stat } from "#enums/stat"; +import { getStatKey } from "#enums/stat"; import i18next from "i18next"; export interface BASE_STAT_FLAT_PARAMS { @@ -11,11 +11,6 @@ export interface BASE_STAT_FLAT_PARAMS { baseStats: number[]; } -export interface BASE_STAT_FLAT_DATA { - statModifier: number; - stats: Stat[]; -} - /** * Currently used by Old Gateau item */ diff --git a/src/items/held-items/base-stat-total.ts b/src/items/held-items/base-stat-total.ts index 59bc10e3cc3..f5c57c6a685 100644 --- a/src/items/held-items/base-stat-total.ts +++ b/src/items/held-items/base-stat-total.ts @@ -1,6 +1,7 @@ import type Pokemon from "#app/field/pokemon"; import i18next from "i18next"; import { HeldItem, ITEM_EFFECT } from "../held-item"; +import type { BASE_STAT_TOTAL_DATA } from "../held-item-data"; export interface BASE_STAT_TOTAL_PARAMS { /** The pokemon with the item */ @@ -9,10 +10,6 @@ export interface BASE_STAT_TOTAL_PARAMS { baseStats: number[]; } -export interface BASE_STAT_TOTAL_DATA { - statModifier: number; -} - /** * Currently used by Shuckle Juice item */ diff --git a/src/items/init-held-item-pools.ts b/src/items/init-held-item-pools.ts new file mode 100644 index 00000000000..0e6dc7b99e6 --- /dev/null +++ b/src/items/init-held-item-pools.ts @@ -0,0 +1,80 @@ +import { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; +import { RewardTier } from "#enums/reward-tier"; +import { dailyStarterHeldItemPool, trainerHeldItemPool, wildHeldItemPool } from "./held-item-pool"; + +/** + * Initialize the wild held item pool + */ +function initWildHeldItemPool() { + wildHeldItemPool[RewardTier.COMMON] = [{ entry: HeldItemCategoryId.BERRY, weight: 1 }]; + wildHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }]; + wildHeldItemPool[RewardTier.ULTRA] = [ + { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }, + { entry: HeldItemId.WHITE_HERB, weight: 0 }, + ]; + wildHeldItemPool[RewardTier.ROGUE] = [{ entry: HeldItemId.LUCKY_EGG, weight: 4 }]; + wildHeldItemPool[RewardTier.MASTER] = [{ entry: HeldItemId.GOLDEN_EGG, weight: 1 }]; +} + +/** + * Initialize the trainer pokemon held item pool + */ +function initTrainerHeldItemPool() { + trainerHeldItemPool[RewardTier.COMMON] = [ + { entry: HeldItemCategoryId.BERRY, weight: 8 }, + { entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }, + ]; + trainerHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }]; + trainerHeldItemPool[RewardTier.ULTRA] = [ + { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 10 }, + { entry: HeldItemId.WHITE_HERB, weight: 0 }, + ]; + trainerHeldItemPool[RewardTier.ROGUE] = [ + { entry: HeldItemId.FOCUS_BAND, weight: 2 }, + { entry: HeldItemId.LUCKY_EGG, weight: 4 }, + { entry: HeldItemId.QUICK_CLAW, weight: 1 }, + { entry: HeldItemId.GRIP_CLAW, weight: 1 }, + { entry: HeldItemId.WIDE_LENS, weight: 1 }, + ]; + trainerHeldItemPool[RewardTier.MASTER] = [ + { entry: HeldItemId.KINGS_ROCK, weight: 1 }, + { entry: HeldItemId.LEFTOVERS, weight: 1 }, + { entry: HeldItemId.SHELL_BELL, weight: 1 }, + { entry: HeldItemId.SCOPE_LENS, weight: 1 }, + ]; +} + +/** + * Initialize the daily starter held item pool + */ +function initDailyStarterModifierPool() { + dailyStarterHeldItemPool[RewardTier.COMMON] = [ + { entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }, + { entry: HeldItemCategoryId.BERRY, weight: 3 }, + ]; + dailyStarterHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }]; + dailyStarterHeldItemPool[RewardTier.ULTRA] = [ + { entry: HeldItemId.REVIVER_SEED, weight: 4 }, + { entry: HeldItemId.SOOTHE_BELL, weight: 1 }, + { entry: HeldItemId.SOUL_DEW, weight: 1 }, + { entry: HeldItemId.GOLDEN_PUNCH, weight: 1 }, + ]; + dailyStarterHeldItemPool[RewardTier.ROGUE] = [ + { entry: HeldItemId.GRIP_CLAW, weight: 5 }, + { entry: HeldItemId.BATON, weight: 2 }, + { entry: HeldItemId.FOCUS_BAND, weight: 5 }, + { entry: HeldItemId.QUICK_CLAW, weight: 3 }, + { entry: HeldItemId.KINGS_ROCK, weight: 3 }, + ]; + dailyStarterHeldItemPool[RewardTier.MASTER] = [ + { entry: HeldItemId.LEFTOVERS, weight: 1 }, + { entry: HeldItemId.SHELL_BELL, weight: 1 }, + ]; +} + +export function initHeldItemPools() { + // Default held item pools for specific scenarios + initWildHeldItemPool(); + initTrainerHeldItemPool(); + initDailyStarterModifierPool(); +} diff --git a/src/modifier/init-modifier-pools.ts b/src/modifier/init-modifier-pools.ts index 60697333600..3d558097d78 100644 --- a/src/modifier/init-modifier-pools.ts +++ b/src/modifier/init-modifier-pools.ts @@ -1,11 +1,5 @@ import type Pokemon from "#app/field/pokemon"; -import { - dailyStarterModifierPool, - enemyBuffModifierPool, - modifierPool, - trainerModifierPool, - wildModifierPool, -} from "#app/modifier/modifier-pools"; +import { enemyBuffModifierPool, modifierPool } from "#app/modifier/modifier-pools"; import { globalScene } from "#app/global-scene"; import { DoubleBattleChanceBoosterModifier, SpeciesCritBoosterModifier, TurnStatusEffectModifier } from "./modifier"; import { WeightedModifierType } from "./modifier-type"; @@ -27,35 +21,6 @@ import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; // biome-ignore lint/correctness/noUnusedImports: This is used in a tsdoc comment import type { initModifierTypes } from "./modifier-type"; -/** - * Initialize the wild modifier pool - */ -function initWildModifierPool() { - wildModifierPool[ModifierTier.COMMON] = [new WeightedModifierType(modifierTypes.BERRY, 1)].map(m => { - m.setTier(ModifierTier.COMMON); - return m; - }); - wildModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1)].map(m => { - m.setTier(ModifierTier.GREAT); - return m; - }); - wildModifierPool[ModifierTier.ULTRA] = [ - new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10), - new WeightedModifierType(modifierTypes.WHITE_HERB, 0), - ].map(m => { - m.setTier(ModifierTier.ULTRA); - return m; - }); - wildModifierPool[ModifierTier.ROGUE] = [new WeightedModifierType(modifierTypes.LUCKY_EGG, 4)].map(m => { - m.setTier(ModifierTier.ROGUE); - return m; - }); - wildModifierPool[ModifierTier.MASTER] = [new WeightedModifierType(modifierTypes.GOLDEN_EGG, 1)].map(m => { - m.setTier(ModifierTier.MASTER); - return m; - }); -} - /** * Initialize the common modifier pool */ @@ -649,46 +614,6 @@ function initMasterModifierPool() { }); } -function initTrainerModifierPool() { - trainerModifierPool[ModifierTier.COMMON] = [ - new WeightedModifierType(modifierTypes.BERRY, 8), - new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), - ].map(m => { - m.setTier(ModifierTier.COMMON); - return m; - }); - trainerModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3)].map(m => { - m.setTier(ModifierTier.GREAT); - return m; - }); - trainerModifierPool[ModifierTier.ULTRA] = [ - new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10), - new WeightedModifierType(modifierTypes.WHITE_HERB, 0), - ].map(m => { - m.setTier(ModifierTier.ULTRA); - return m; - }); - trainerModifierPool[ModifierTier.ROGUE] = [ - new WeightedModifierType(modifierTypes.FOCUS_BAND, 2), - new WeightedModifierType(modifierTypes.LUCKY_EGG, 4), - new WeightedModifierType(modifierTypes.QUICK_CLAW, 1), - new WeightedModifierType(modifierTypes.GRIP_CLAW, 1), - new WeightedModifierType(modifierTypes.WIDE_LENS, 1), - ].map(m => { - m.setTier(ModifierTier.ROGUE); - return m; - }); - trainerModifierPool[ModifierTier.MASTER] = [ - new WeightedModifierType(modifierTypes.KINGS_ROCK, 1), - new WeightedModifierType(modifierTypes.LEFTOVERS, 1), - new WeightedModifierType(modifierTypes.SHELL_BELL, 1), - new WeightedModifierType(modifierTypes.SCOPE_LENS, 1), - ].map(m => { - m.setTier(ModifierTier.MASTER); - return m; - }); -} - /** * Initialize the enemy buff modifier pool */ @@ -737,51 +662,6 @@ function initEnemyBuffModifierPool() { }); } -/** - * Initialize the daily starter modifier pool - */ -function initDailyStarterModifierPool() { - dailyStarterModifierPool[ModifierTier.COMMON] = [ - new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1), - new WeightedModifierType(modifierTypes.BERRY, 3), - ].map(m => { - m.setTier(ModifierTier.COMMON); - return m; - }); - dailyStarterModifierPool[ModifierTier.GREAT] = [new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5)].map( - m => { - m.setTier(ModifierTier.GREAT); - return m; - }, - ); - dailyStarterModifierPool[ModifierTier.ULTRA] = [ - new WeightedModifierType(modifierTypes.REVIVER_SEED, 4), - new WeightedModifierType(modifierTypes.SOOTHE_BELL, 1), - new WeightedModifierType(modifierTypes.SOUL_DEW, 1), - new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, 1), - ].map(m => { - m.setTier(ModifierTier.ULTRA); - return m; - }); - dailyStarterModifierPool[ModifierTier.ROGUE] = [ - new WeightedModifierType(modifierTypes.GRIP_CLAW, 5), - new WeightedModifierType(modifierTypes.BATON, 2), - new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), - new WeightedModifierType(modifierTypes.QUICK_CLAW, 3), - new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), - ].map(m => { - m.setTier(ModifierTier.ROGUE); - return m; - }); - dailyStarterModifierPool[ModifierTier.MASTER] = [ - new WeightedModifierType(modifierTypes.LEFTOVERS, 1), - new WeightedModifierType(modifierTypes.SHELL_BELL, 1), - ].map(m => { - m.setTier(ModifierTier.MASTER); - return m; - }); -} - /** * Initialize {@linkcode modifierPool} with the initial set of modifier types. * {@linkcode initModifierTypes} MUST be called before this function. @@ -798,7 +678,6 @@ export function initModifierPools() { initWildModifierPool(); initTrainerModifierPool(); initEnemyBuffModifierPool(); - initDailyStarterModifierPool(); } /** diff --git a/src/modifier/modifier-pools.ts b/src/modifier/modifier-pools.ts index 3396dca1f93..db6ebc700e5 100644 --- a/src/modifier/modifier-pools.ts +++ b/src/modifier/modifier-pools.ts @@ -7,10 +7,6 @@ import type { ModifierPool } from "#app/@types/modifier-types"; export const modifierPool: ModifierPool = {}; -export const wildModifierPool: ModifierPool = {}; - -export const trainerModifierPool: ModifierPool = {}; - export const enemyBuffModifierPool: ModifierPool = {}; export const dailyStarterModifierPool: ModifierPool = {}; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 5cbcc293e9d..744b46e9fef 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -46,7 +46,6 @@ import { MoneyRewardModifier, MultipleParticipantExpBonusModifier, PokemonAllMovePpRestoreModifier, - type PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonLevelIncrementModifier, PokemonNatureChangeModifier, @@ -98,12 +97,12 @@ import { HeldItemId } from "#enums/held-item-id"; import { allHeldItems } from "#app/items/all-held-items"; import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; import { attackTypeToHeldItem } from "#app/items/held-items/attack-type-booster"; -import { berryTypeToHeldItem } from "#app/items/held-items/berry"; import { permanentStatToHeldItem, statBoostItems } from "#app/items/held-items/base-stat-booster"; import { SPECIES_STAT_BOOSTER_ITEMS, type SpeciesStatBoosterItemId } from "#app/items/held-items/stat-booster"; import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { getModifierPoolForType, getModifierType } from "#app/utils/modifier-utils"; +import { getModifierPoolForType } from "#app/utils/modifier-utils"; import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types"; +import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#app/items/held-item-pool"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -767,41 +766,14 @@ export class TempStatStageBoosterModifierType extends ModifierType implements Ge } } -export class BerryReward extends HeldItemReward implements GeneratedPersistentModifierType { - private berryType: BerryType; - - constructor(berryType: BerryType) { - const itemId = berryTypeToHeldItem[berryType]; - super(itemId); - - this.berryType = berryType; - this.id = "BERRY"; // needed to prevent harvest item deletion; remove after modifier rework - } - - getPregenArgs(): any[] { - return [this.berryType]; - } -} - class BerryRewardGenerator extends ModifierTypeGenerator { constructor() { super((_party: Pokemon[], pregenArgs?: any[]) => { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in BerryType) { return new BerryReward(pregenArgs[0] as BerryType); } - const berryTypes = getEnumValues(BerryType); - let randBerryType: BerryType; - const rand = randSeedInt(12); - if (rand < 2) { - randBerryType = BerryType.SITRUS; - } else if (rand < 4) { - randBerryType = BerryType.LUM; - } else if (rand < 6) { - randBerryType = BerryType.LEPPA; - } else { - randBerryType = berryTypes[randSeedInt(berryTypes.length - 3) + 2]; - } - return new BerryReward(randBerryType); + const item = getNewBerryHeldItem(); + return new HeldItemReward(item); }); } } @@ -1166,47 +1138,9 @@ class AttackTypeBoosterRewardGenerator extends ModifierTypeGenerator { return new AttackTypeBoosterReward(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT); } - // TODO: make this consider moves or abilities that change types - const attackMoveTypes = party.flatMap(p => - p - .getMoveset() - .map(m => m.getMove()) - .filter(m => m.is("AttackMove")) - .map(m => m.type), - ); - if (!attackMoveTypes.length) { - return null; - } + const item = getNewAttackTypeBoosterHeldItem(party); - const attackMoveTypeWeights = new Map(); - let totalWeight = 0; - for (const t of attackMoveTypes) { - const weight = attackMoveTypeWeights.get(t) ?? 0; - if (weight < 3) { - attackMoveTypeWeights.set(t, weight + 1); - totalWeight++; - } - } - - if (!totalWeight) { - return null; - } - - let type: PokemonType; - - const randInt = randSeedInt(totalWeight); - let weight = 0; - - for (const t of attackMoveTypeWeights.keys()) { - const typeWeight = attackMoveTypeWeights.get(t)!; // guranteed to be defined - if (randInt <= weight + typeWeight) { - type = t; - break; - } - weight += typeWeight; - } - - return new AttackTypeBoosterReward(type!, TYPE_BOOST_ITEM_BOOST_PERCENT); + return item ? new HeldItemReward(item) : null; }); } } @@ -1217,8 +1151,7 @@ class BaseStatBoosterRewardGenerator extends ModifierTypeGenerator { if (pregenArgs) { return new BaseStatBoosterReward(pregenArgs[0]); } - const randStat: PermanentStat = randSeedInt(Stat.SPD + 1); - return new BaseStatBoosterReward(randStat); + return new HeldItemReward(getNewVitaminHeldItem()); }); } }