diff --git a/src/field/pokemon-item-manager.ts b/src/field/pokemon-item-manager.ts index f00cf63f763..58fb6ba4d40 100644 --- a/src/field/pokemon-item-manager.ts +++ b/src/field/pokemon-item-manager.ts @@ -1,18 +1,18 @@ import { allHeldItems } from "#app/modifier/held-items"; -import type { HeldItemType } from "#app/modifier/held-items"; +import type { HeldItems } from "#app/modifier/held-items"; export class PokemonItemManager { - private heldItems: [HeldItemType, number][]; + private heldItems: [HeldItems, number][]; constructor() { this.heldItems = []; } - getHeldItems(): [HeldItemType, number][] { + getHeldItems(): [HeldItems, number][] { return this.heldItems; } - addHeldItem(itemType: HeldItemType, stack: number) { + addHeldItem(itemType: HeldItems, stack: number) { const maxStack = allHeldItems[itemType].getMaxStackCount(); const existing = this.heldItems.find(([type]) => type === itemType); diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 914e6e961e2..f801061b665 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -21,6 +21,7 @@ import { initVouchers } from "#app/system/voucher"; import { Biome } from "#enums/biome"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; import { timedEventManager } from "./global-event-manager"; +import { initHeldItems } from "./modifier/held-items"; export class LoadingScene extends SceneBase { public static readonly KEY = "loading"; @@ -375,6 +376,7 @@ export class LoadingScene extends SceneBase { initSpecies(); initMoves(); initAbilities(); + initHeldItems(); initChallenges(); initMysteryEncounters(); } diff --git a/src/modifier/held-item-pool.ts b/src/modifier/held-item-pool.ts new file mode 100644 index 00000000000..6d54e3561b9 --- /dev/null +++ b/src/modifier/held-item-pool.ts @@ -0,0 +1,69 @@ +/** +import { PlayerPokemon } from "#app/field/pokemon"; +import { randSeedInt } from "#app/utils/common"; +import { HeldItemCategories, HeldItems } from "./held-items"; +import { ModifierTier } from "./modifier-tier"; + +interface HeldItemPool { + [tier: string]: [HeldItems | HeldItemCategories, number][]; +} + +const dailyStarterHeldItemPool: HeldItemPool = { + [ModifierTier.COMMON]: [ + [HeldItemCategories.BASE_STAT_BOOSTER, 1], + [HeldItemCategories.BERRY, 3], + ], + [ModifierTier.GREAT]: [ + [HeldItemCategories.ATTACK_TYPE_BOOSTER, 5], + ], + [ModifierTier.ULTRA]: [ + [HeldItems.REVIVER_SEED, 4], + [HeldItems.SOOTHE_BELL, 1], + [HeldItems.SOUL_DEW, 1], + [HeldItems.GOLDEN_PUNCH, 1], + ], + [ModifierTier.ROGUE]: [ + [HeldItems.GRIP_CLAW, 5], + [HeldItems.BATON, 2], + [HeldItems.FOCUS_BAND, 5], + [HeldItems.QUICK_CLAW, 3], + [HeldItems.KINGS_ROCK, 3], + ], + [ModifierTier.MASTER]: [ + [HeldItems.LEFTOVERS, 1], + [HeldItems.SHELL_BELL, 1], + ], +}; + + + + +export function getDailyRunStarterModifiers(party: PlayerPokemon[]): HeldItems[] { + const ret: HeldItems[] = []; + for (const p of party) { + for (let m = 0; m < 3; m++) { + const tierValue = randSeedInt(64); + + let tier: ModifierTier; + if (tierValue > 25) { + tier = ModifierTier.COMMON; + } else if (tierValue > 12) { + tier = ModifierTier.GREAT; + } else if (tierValue > 4) { + tier = ModifierTier.ULTRA; + } else if (tierValue) { + tier = ModifierTier.ROGUE; + } else { + tier = ModifierTier.MASTER; + } + + const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier)?.type?.newModifier( + p, + ); + ret.push(modifier); + } + } + + return ret; +} +*/ diff --git a/src/modifier/held-items.ts b/src/modifier/held-items.ts index 8200b946011..170004aa024 100644 --- a/src/modifier/held-items.ts +++ b/src/modifier/held-items.ts @@ -5,35 +5,58 @@ import type { NumberHolder } from "#app/utils/common"; import { PokemonType } from "#enums/pokemon-type"; import i18next from "i18next"; -export enum HeldItemType { - NONE, +export const HeldItems = { + NONE: 0x0000, - SITRUS_BERRY = 1, - LEPPA_BERRY, + SITRUS_BERRY: 0x0101, + LEPPA_BERRY: 0x0102, - SILK_SCARF = 101, - BLACK_BELT, - SHARP_BEAK, - POISON_BARB, - SOFT_SAND, - HARD_STONE, - SILVER_POWDER, - SPELL_TAG, - METAL_COAT, - CHARCOAL, - MYSTIC_WATER, - MIRACLE_SEED, - MAGNET, - TWISTED_SPOON, - NEVER_MELT_ICE, - DRAGON_FANG, - BLACK_GLASSES, - FAIRY_FEATHER, -} + SILK_SCARF: 0x0201, + BLACK_BELT: 0x0202, + SHARP_BEAK: 0x0203, + POISON_BARB: 0x0204, + SOFT_SAND: 0x0205, + HARD_STONE: 0x0206, + SILVER_POWDER: 0x0207, + SPELL_TAG: 0x0208, + METAL_COAT: 0x0209, + CHARCOAL: 0x020a, + MYSTIC_WATER: 0x020b, + MIRACLE_SEED: 0x020c, + MAGNET: 0x020d, + TWISTED_SPOON: 0x020e, + NEVER_MELT_ICE: 0x020f, + DRAGON_FANG: 0x0210, + BLACK_GLASSES: 0x0211, + FAIRY_FEATHER: 0x0212, + + REVIVER_SEED: 0x0301, + SOOTHE_BELL: 0x0302, + SOUL_DEW: 0x0303, + GOLDEN_PUNCH: 0x0304, + GRIP_CLAW: 0x0305, + BATON: 0x0306, + FOCUS_BAND: 0x0307, + QUICK_CLAW: 0x0308, + KINGS_ROCK: 0x0309, + LEFTOVERS: 0x030a, + SHELL_BELL: 0x030b, +}; + +export type HeldItems = (typeof HeldItems)[keyof typeof HeldItems]; + +export const HeldItemCategories = { + NONE: 0x0000, + BERRY: 0x0100, + ATTACK_TYPE_BOOSTER: 0x0200, + BASE_STAT_BOOSTER: 0x0400, +}; + +export type HeldItemCategories = (typeof HeldItemCategories)[keyof typeof HeldItemCategories]; export class HeldItem implements Localizable { // public pokemonId: number; - public type: HeldItemType; + public type: HeldItems; public maxStackCount: number; public isTransferable = true; public isStealable = true; @@ -43,7 +66,7 @@ export class HeldItem implements Localizable { public description = ""; public icon = ""; - constructor(type: HeldItemType, maxStackCount = 1) { + constructor(type: HeldItems, maxStackCount = 1) { this.type = type; this.maxStackCount = maxStackCount; @@ -125,36 +148,36 @@ export class HeldItem implements Localizable { } } -interface AttackTypeToHeldItemTypeMap { - [key: number]: HeldItemType; +interface AttackTypeToHeldItemMap { + [key: number]: HeldItems; } -export const attackTypeToHeldItemTypeMap: AttackTypeToHeldItemTypeMap = { - [PokemonType.NORMAL]: HeldItemType.SILK_SCARF, - [PokemonType.FIGHTING]: HeldItemType.BLACK_BELT, - [PokemonType.FLYING]: HeldItemType.SHARP_BEAK, - [PokemonType.POISON]: HeldItemType.POISON_BARB, - [PokemonType.GROUND]: HeldItemType.SOFT_SAND, - [PokemonType.ROCK]: HeldItemType.HARD_STONE, - [PokemonType.BUG]: HeldItemType.SILVER_POWDER, - [PokemonType.GHOST]: HeldItemType.SPELL_TAG, - [PokemonType.STEEL]: HeldItemType.METAL_COAT, - [PokemonType.FIRE]: HeldItemType.CHARCOAL, - [PokemonType.WATER]: HeldItemType.MYSTIC_WATER, - [PokemonType.GRASS]: HeldItemType.MIRACLE_SEED, - [PokemonType.ELECTRIC]: HeldItemType.MAGNET, - [PokemonType.PSYCHIC]: HeldItemType.TWISTED_SPOON, - [PokemonType.ICE]: HeldItemType.NEVER_MELT_ICE, - [PokemonType.DRAGON]: HeldItemType.DRAGON_FANG, - [PokemonType.DARK]: HeldItemType.BLACK_GLASSES, - [PokemonType.FAIRY]: HeldItemType.FAIRY_FEATHER, +export const attackTypeToHeldItem: AttackTypeToHeldItemMap = { + [PokemonType.NORMAL]: HeldItems.SILK_SCARF, + [PokemonType.FIGHTING]: HeldItems.BLACK_BELT, + [PokemonType.FLYING]: HeldItems.SHARP_BEAK, + [PokemonType.POISON]: HeldItems.POISON_BARB, + [PokemonType.GROUND]: HeldItems.SOFT_SAND, + [PokemonType.ROCK]: HeldItems.HARD_STONE, + [PokemonType.BUG]: HeldItems.SILVER_POWDER, + [PokemonType.GHOST]: HeldItems.SPELL_TAG, + [PokemonType.STEEL]: HeldItems.METAL_COAT, + [PokemonType.FIRE]: HeldItems.CHARCOAL, + [PokemonType.WATER]: HeldItems.MYSTIC_WATER, + [PokemonType.GRASS]: HeldItems.MIRACLE_SEED, + [PokemonType.ELECTRIC]: HeldItems.MAGNET, + [PokemonType.PSYCHIC]: HeldItems.TWISTED_SPOON, + [PokemonType.ICE]: HeldItems.NEVER_MELT_ICE, + [PokemonType.DRAGON]: HeldItems.DRAGON_FANG, + [PokemonType.DARK]: HeldItems.BLACK_GLASSES, + [PokemonType.FAIRY]: HeldItems.FAIRY_FEATHER, }; export class AttackTypeBoosterHeldItem extends HeldItem { public moveType: PokemonType; public powerBoost: number; - constructor(type: HeldItemType, maxStackCount = 1, moveType: PokemonType, powerBoost: number) { + constructor(type: HeldItems, maxStackCount = 1, moveType: PokemonType, powerBoost: number) { super(type, maxStackCount); this.moveType = moveType; this.powerBoost = powerBoost; @@ -162,7 +185,7 @@ export class AttackTypeBoosterHeldItem extends HeldItem { } getName(): string { - return i18next.t(`modifierType:AttackTypeBoosterItem.${HeldItemType[this.type]?.toLowerCase()}`); + return i18next.t(`modifierType:AttackTypeBoosterItem.${HeldItems[this.type]?.toLowerCase()}`); } getDescription(): string { @@ -172,7 +195,7 @@ export class AttackTypeBoosterHeldItem extends HeldItem { } getIcon(): string { - return `${HeldItemType[this.type]?.toLowerCase()}`; + return `${HeldItems[this.type]?.toLowerCase()}`; } apply(stackCount: number, moveType: PokemonType, movePower: NumberHolder): void { @@ -192,17 +215,12 @@ export function applyAttackTypeBoosterHeldItem(pokemon: Pokemon, moveType: Pokem } } -type HeldItemMap = { - [key in HeldItemType]: HeldItem; -}; - -export const allHeldItems = {} as HeldItemMap; +export const allHeldItems = {}; export function initHeldItems() { // SILK_SCARF, BLACK_BELT, etc... - for (const [typeKey, heldItemType] of Object.entries(attackTypeToHeldItemTypeMap)) { + for (const [typeKey, heldItemType] of Object.entries(attackTypeToHeldItem)) { const pokemonType = Number(typeKey) as PokemonType; - allHeldItems[heldItemType] = new AttackTypeBoosterHeldItem(heldItemType, 99, pokemonType, 0.2); } } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 9a88d04bf3c..a6e0889679a 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -128,8 +128,8 @@ import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; import { timedEventManager } from "#app/global-event-manager"; -import type { HeldItemType } from "./held-items"; -import { allHeldItems, attackTypeToHeldItemTypeMap } from "./held-items"; +import type { HeldItems } from "./held-items"; +import { allHeldItems, attackTypeToHeldItem } from "./held-items"; const outputModifierData = false; const useMaxWeightForOutput = false; @@ -812,10 +812,10 @@ export class AttackTypeBoosterModifierType { public moveType: PokemonType; public boostPercent: number; - public heldItemId: HeldItemType; + public heldItemId: HeldItems; constructor(moveType: PokemonType, boostPercent: number) { - const heldItemId = attackTypeToHeldItemTypeMap[moveType]; + const heldItemId = attackTypeToHeldItem[moveType]; super( "", allHeldItems[heldItemId].getIcon(), diff --git a/src/modifier/reward-generator.ts b/src/modifier/reward-generator.ts new file mode 100644 index 00000000000..cedecd923bb --- /dev/null +++ b/src/modifier/reward-generator.ts @@ -0,0 +1,120 @@ +import { AttackMove } from "#app/data/moves/move"; +import type Pokemon from "#app/field/pokemon"; +import { PokemonType } from "#enums/pokemon-type"; +import { attackTypeToHeldItem } from "./held-items"; +import { HeldItemReward, type Reward } from "./reward"; + +function getRandomWeightedSelection(weights: Map): T | null { + const totalWeight = Array.from(weights.values()).reduce((sum, weight) => sum + weight, 0); + + if (totalWeight === 0) { + return null; + } + + const randInt = Math.floor(Math.random() * totalWeight); + + let accumulatedWeight = 0; + for (const [item, weight] of weights.entries()) { + accumulatedWeight += weight; + if (randInt < accumulatedWeight) { + return item; + } + } + + return null; +} + +export class RewardGenerator { + options: T[]; + tempWeights: Map; + + constructor(options: T[]) { + this.options = options; + this.tempWeights = new Map(this.options.map(option => [option, 1])); + } + + generate(party: Pokemon[], overrideWeightFunction?: Function) { + const weights = overrideWeightFunction ? overrideWeightFunction(party) : this.weightFunction(party); + + for (const [option, tempWeight] of this.tempWeights.entries()) { + if (tempWeight === 0 && weights.has(option)) { + weights.set(option, 0); + } + } + + const value: T | null = getRandomWeightedSelection(weights); + + if (value) { + this.tempWeights.set(value, 0); + return this.generateReward(value); + } + + return null; + } + + weightFunction(_party: Pokemon[]): Map { + const defaultWeightMap = new Map(); + + this.options.forEach(option => { + defaultWeightMap.set(option, 1); + }); + + return defaultWeightMap; + } + + generateReward(_value: T): Reward | null { + return null; + } +} + +export class AttackTypeBoosterHeldItemRewardGenerator extends RewardGenerator { + constructor() { + //TODO: we can also construct this, but then have to handle options being null + const options = [ + PokemonType.NORMAL, + PokemonType.FIGHTING, + PokemonType.FLYING, + PokemonType.POISON, + PokemonType.GROUND, + PokemonType.ROCK, + PokemonType.BUG, + PokemonType.GHOST, + PokemonType.STEEL, + PokemonType.FIRE, + PokemonType.WATER, + PokemonType.GRASS, + PokemonType.ELECTRIC, + PokemonType.PSYCHIC, + PokemonType.ICE, + PokemonType.DRAGON, + PokemonType.DARK, + PokemonType.FAIRY, + ]; + super(options); + } + + weightFunction(party: Pokemon[]): Map { + const attackMoveTypes = party.flatMap(p => + p + .getMoveset() + .map(m => m.getMove()) + .filter(m => m instanceof AttackMove) + .map(m => m.type), + ); + + const attackMoveTypeWeights = new Map(); + + for (const type of attackMoveTypes) { + const currentWeight = attackMoveTypeWeights.get(type) ?? 0; + if (currentWeight < 3) { + attackMoveTypeWeights.set(type, currentWeight + 1); + } + } + + return attackMoveTypeWeights; + } + + generateReward(value: PokemonType) { + return new HeldItemReward(attackTypeToHeldItem[value]); + } +} diff --git a/src/modifier/reward.ts b/src/modifier/reward.ts index 01ab667a1b1..917e7909cb5 100644 --- a/src/modifier/reward.ts +++ b/src/modifier/reward.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import type { PokeballType } from "#enums/pokeball"; import i18next from "i18next"; -import { allHeldItems, type HeldItem } from "./held-items"; +import { allHeldItems, type HeldItems } from "./held-items"; import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import type Pokemon from "#app/field/pokemon"; @@ -77,8 +77,9 @@ export class PartySelectReward extends Reward { } export class HeldItemReward extends PartySelectReward { - private itemId; - constructor(itemId: HeldItem) { + private itemId: HeldItems; + + constructor(itemId: HeldItems) { super(); this.itemId = itemId; } @@ -103,38 +104,14 @@ export class HeldItemReward extends PartySelectReward { -export class RewardGenerator { - options: number[]; - optionWeights: number[]; - - constructor(options: number[]) { - this.options = options; - } -} -export class PokeballRewardGenerator extends RewardGenerator{ - constructor( - options: PokeballType[], - condition?: (party: Pokemon[], option: number) => boolean, - getOptionWeight?: (party: Pokemon[], option: number) => number, - ) { - super(options); - this.isAvailable = isAvailable; - this.getOptionWeight = getOptionWeight; - } - isAvailable(): boolean { - - } - optionWeights() { - } -} @@ -162,12 +139,6 @@ export class RewardManager { this.rewardPool = rewardPool; } - - - - - - - } - * */ + +*/