diff --git a/index.css b/index.css index 62ad6266d30..e7439639d3a 100644 --- a/index.css +++ b/index.css @@ -183,7 +183,7 @@ input:-internal-autofill-selected { /* Show #apadStats only in battle and shop */ #touchControls:not([data-ui-mode="COMMAND"]):not([data-ui-mode="FIGHT"]):not( [data-ui-mode="BALL"] - ):not([data-ui-mode="TARGET_SELECT"]):not([data-ui-mode="MODIFIER_SELECT"]) + ):not([data-ui-mode="TARGET_SELECT"]):not([data-ui-mode="REWARD_SELECT"]) #apadStats { display: none; } diff --git a/src/@types/locales.ts b/src/@types/locales.ts index 3b5a1477e19..af3d5fcc596 100644 --- a/src/@types/locales.ts +++ b/src/@types/locales.ts @@ -24,15 +24,15 @@ export interface AbilityTranslationEntries { [key: string]: AbilityTranslationEntry; } -export interface ModifierTypeTranslationEntry { +export interface RewardTranslationEntry { name?: string; description?: string; extra?: SimpleTranslationEntries; } -export interface ModifierTypeTranslationEntries { - ModifierType: { [key: string]: ModifierTypeTranslationEntry }; - SpeciesBoosterItem: { [key: string]: ModifierTypeTranslationEntry }; +export interface RewardTranslationEntries { + Reward: { [key: string]: RewardTranslationEntry }; + SpeciesBoosterItem: { [key: string]: RewardTranslationEntry }; AttackTypeBoosterItem: SimpleTranslationEntries; TempStatStageBoosterItem: SimpleTranslationEntries; BaseStatBoosterItem: SimpleTranslationEntries; diff --git a/src/@types/modifier-types.ts b/src/@types/modifier-types.ts deleted file mode 100644 index 28b39d1a151..00000000000 --- a/src/@types/modifier-types.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Intentionally re-exports `ModifierConstructorMap` from `modifier.ts` - -import type { Pokemon } from "#field/pokemon"; -import type { ModifierConstructorMap } from "#modifiers/modifier"; -import type { ModifierType, WeightedModifierType } from "#modifiers/modifier-type"; - -export type ModifierTypeFunc = () => ModifierType; -export type WeightedModifierTypeWeightFunc = (party: Pokemon[], rerollCount?: number) => number; - -export type { ModifierConstructorMap } from "#modifiers/modifier"; - -/** - * Map of modifier names to their respective instance types - */ -export type ModifierInstanceMap = { - [K in keyof ModifierConstructorMap]: InstanceType; -}; - -/** - * Union type of all modifier constructors. - */ -export type ModifierClass = ModifierConstructorMap[keyof ModifierConstructorMap]; - -/** - * Union type of all modifier names as strings. - */ -export type ModifierString = keyof ModifierConstructorMap; - -export type ModifierPool = { - [tier: string]: WeightedModifierType[]; -}; diff --git a/src/@types/rewards.ts b/src/@types/rewards.ts new file mode 100644 index 00000000000..ad938a60164 --- /dev/null +++ b/src/@types/rewards.ts @@ -0,0 +1,23 @@ +import type { HeldItemId } from "#enums/held-item-id"; +import type { RewardId } from "#enums/reward-id"; +import type { TrainerItemId } from "#enums/trainer-item-id"; +import type { Pokemon } from "#field/pokemon"; +import type { Reward, RewardGenerator } from "#items/reward"; + +export type RewardFunc = () => Reward | RewardGenerator; +export type WeightedRewardWeightFunc = (party: Pokemon[], rerollCount?: number) => number; + +export type RewardPoolId = RewardId | HeldItemId | TrainerItemId; + +export type RewardPoolEntry = { + id: RewardPoolId; + weight: number | WeightedRewardWeightFunc; +}; + +export type RewardPool = { + [tier: string]: RewardPoolEntry[]; +}; + +export interface RewardPoolWeights { + [tier: string]: number[]; +} diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b3a92da41e2..74906adfd72 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -53,8 +53,8 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { ExpNotification } from "#enums/exp-notification"; import { FormChangeItem } from "#enums/form-change-item"; import { GameModes } from "#enums/game-modes"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; -import { HeldItemPoolType, ModifierPoolType } from "#enums/modifier-pool-type"; import { MoneyFormat } from "#enums/money-format"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; @@ -65,6 +65,7 @@ import { PlayerGender } from "#enums/player-gender"; import { PokeballType } from "#enums/pokeball"; import type { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PokemonType } from "#enums/pokemon-type"; +import { HeldItemPoolType, RewardPoolType } from "#enums/reward-pool-type"; import { ShopCursorTarget } from "#enums/shop-cursor-target"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; @@ -82,9 +83,10 @@ import { PokemonSpriteSparkleHandler } from "#field/pokemon-sprite-sparkle-handl import { Trainer } from "#field/trainer"; import { applyHeldItems } from "#items/all-held-items"; import { type ApplyTrainerItemsParams, applyTrainerItems } from "#items/apply-trainer-items"; -import { HeldItemEffect } from "#items/held-item"; import type { HeldItemConfiguration } from "#items/held-item-data-types"; import { assignEnemyHeldItemsForWave, assignItemsFromConfiguration } from "#items/held-item-pool"; +import type { Reward } from "#items/reward"; +import { getRewardPoolForType } from "#items/reward-pool-utils"; import { type EnemyAttackStatusEffectChanceTrainerItem, TrainerItemEffect } from "#items/trainer-item"; import { isTrainerItemPool, @@ -94,15 +96,6 @@ import { } from "#items/trainer-item-data-types"; import { TrainerItemManager } from "#items/trainer-item-manager"; import { getNewTrainerItemFromPool } from "#items/trainer-item-pool"; -import type { Modifier } from "#modifiers/modifier"; -import { - ConsumableModifier, - ConsumablePokemonModifier, - FusePokemonModifier, - PokemonHpRestoreModifier, - RememberMoveModifier, -} from "#modifiers/modifier"; -import { getLuckString, getLuckTextTint, getPartyLuckValue } from "#modifiers/modifier-type"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data"; import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters"; @@ -112,7 +105,7 @@ import { hasExpSprite } from "#sprites/sprite-utils"; import type { Variant } from "#sprites/variant"; import { clearVariantData, variantData } from "#sprites/variant"; import type { Achv } from "#system/achv"; -import { achvs, HeldItemAchv, ModifierAchv, MoneyAchv } from "#system/achv"; +import { achvs, HeldItemAchv, MoneyAchv } from "#system/achv"; import { GameData } from "#system/game-data"; import { initGameSpeed } from "#system/game-speed"; import type { PokemonData } from "#system/pokemon-data"; @@ -148,7 +141,7 @@ import { } from "#utils/common"; import { deepMergeSpriteData } from "#utils/data"; import { getEnumValues } from "#utils/enums"; -import { getModifierPoolForType } from "#utils/modifier-utils"; +import { getLuckString, getLuckTextTint, getPartyLuckValue } from "#utils/party"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; import Phaser from "phaser"; @@ -267,7 +260,7 @@ export class BattleScene extends SceneBase { public arena: Arena; public gameMode: GameMode; public score: number; - public lockModifierTiers: boolean; + public lockRarityTiers: boolean; public trainer: Phaser.GameObjects.Sprite; public lastEnemyTrainer: Trainer | null; public currentBattle: Battle; @@ -480,12 +473,12 @@ export class BattleScene extends SceneBase { this.enemyTrainerItems = new TrainerItemManager(); this.itemBar = new ItemBar(); - this.itemBar.setName("modifier-bar"); + this.itemBar.setName("item-bar"); this.add.existing(this.itemBar); uiContainer.add(this.itemBar); this.enemyItemBar = new ItemBar(true); - this.enemyItemBar.setName("enemy-modifier-bar"); + this.enemyItemBar.setName("enemy-item-bar"); this.add.existing(this.enemyItemBar); uiContainer.add(this.enemyItemBar); @@ -854,9 +847,9 @@ export class BattleScene extends SceneBase { } /** - * Returns the ModifierBar of this scene, which is declared private and therefore not accessible elsewhere + * Returns the ItemBar of this scene, which is declared private and therefore not accessible elsewhere * @param isEnemy - Whether to return the enemy modifier bar instead of the player bar; default `false` - * @returns The {@linkcode ModifierBar} for the given side of the field + * @returns The {@linkcode ItemBar} for the given side of the field */ getItemBar(isEnemy = false): ItemBar { return isEnemy ? this.enemyItemBar : this.itemBar; @@ -1167,7 +1160,7 @@ export class BattleScene extends SceneBase { this.score = 0; this.money = 0; - this.lockModifierTiers = false; + this.lockRarityTiers = false; this.pokeballCounts = Object.fromEntries( getEnumValues(PokeballType) @@ -1243,12 +1236,12 @@ export class BattleScene extends SceneBase { ...allSpecies, ...allMoves, ...allAbilities, - ...getEnumValues(ModifierPoolType) - .map(mpt => getModifierPoolForType(mpt)) + ...getEnumValues(RewardPoolType) + .map(mpt => getRewardPoolForType(mpt)) .flatMap(mp => Object.values(mp) .flat() - .map(mt => mt.modifierType) + .map(mt => mt.reward) .filter(mt => "localize" in mt) .map(lpb => lpb as unknown as Localizable), ), @@ -1987,11 +1980,11 @@ export class BattleScene extends SceneBase { }); } - showEnemyModifierBar(): void { + showEnemyItemBar(): void { this.enemyItemBar.setVisible(true); } - hideEnemyModifierBar(): void { + hideEnemyItemBar(): void { this.enemyItemBar.setVisible(false); } @@ -2085,11 +2078,11 @@ export class BattleScene extends SceneBase { } updateUIPositions(): void { - const enemyModifierCount = this.enemyItemBar.totalVisibleLength; + const enemyItemCount = this.enemyItemBar.totalVisibleLength; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; this.biomeWaveText.setY( -(this.game.canvas.height / 6) + - (enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) + + (enemyItemCount ? (enemyItemCount <= 12 ? 15 : 24) : 0) + biomeWaveTextHeight / 2, ); this.moneyText.setY(this.biomeWaveText.y + 10); @@ -2641,56 +2634,19 @@ export class BattleScene extends SceneBase { applyTrainerItems(effect, this.trainerItems, params); } - addModifier(modifier: Modifier | null, playSound?: boolean, instant?: boolean, cost?: number): boolean { - // We check against modifier.type to stop a bug related to loading in a pokemon that has a form change item, which prior to some patch - // that changed form change modifiers worked, had previously set the `type` field to null. - // TODO: This is not the right place to check for this; it should ideally go in a session migrator. - if (!modifier || !modifier.type) { + applyReward(reward: T, params: Parameters[0], playSound?: boolean): boolean { + const soundName = reward.soundName; + + if (playSound && !this.sound.get(soundName)) { + this.playSound(soundName); + } + + if (!reward.shouldApply(params)) { return false; } - let success = false; - const soundName = modifier.type.soundName; - this.validateAchvs(ModifierAchv, modifier); - if (modifier instanceof ConsumableModifier) { - if (playSound && !this.sound.get(soundName)) { - this.playSound(soundName); - } - if (modifier instanceof ConsumablePokemonModifier) { - for (const p in this.party) { - const pokemon = this.party[p]; - - const args: unknown[] = []; - if (modifier instanceof PokemonHpRestoreModifier) { - if (!(modifier as PokemonHpRestoreModifier).fainted) { - const hpRestoreMultiplier = new NumberHolder(1); - this.applyPlayerItems(TrainerItemEffect.HEALING_BOOSTER, { numberHolder: hpRestoreMultiplier }); - args.push(hpRestoreMultiplier.value); - } else { - args.push(1); - } - } else if (modifier instanceof FusePokemonModifier) { - args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon); - } else if (modifier instanceof RememberMoveModifier && !isNullOrUndefined(cost)) { - args.push(cost); - } - - if (modifier.shouldApply(pokemon, ...args)) { - const result = modifier.apply(pokemon, ...args); - success ||= result; - } - } - - this.party.map(p => p.updateInfo(instant)); - } else { - const args = [this]; - if (modifier.shouldApply(...args)) { - const result = modifier.apply(...args); - success ||= result; - } - } - } - return success; + reward.apply(params); + return true; } addHeldItem(heldItemId: HeldItemId, pokemon: Pokemon, amount = 1, playSound?: boolean, ignoreUpdate?: boolean) { @@ -2864,7 +2820,7 @@ export class BattleScene extends SceneBase { } let count = 0; for (let c = 0; c < chances; c++) { - if (!randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) { + if (!randSeedInt(this.gameMode.getEnemyItemChance(isBoss))) { count++; } } @@ -2887,7 +2843,7 @@ export class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy pokemon of {@linkcode PersistentModifier} type + * Removes all items from enemy pokemon and trainers */ clearEnemyItems(): void { this.enemyTrainerItems.clearItems(); @@ -2911,7 +2867,7 @@ export class BattleScene extends SceneBase { this.updateUIPositions(); } - setModifiersVisible(visible: boolean) { + setItemsVisible(visible: boolean) { [this.itemBar, this.enemyItemBar].map(m => m.setVisible(visible)); } diff --git a/src/battle.ts b/src/battle.ts index f2d2b5b78fb..709e7a23164 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -10,15 +10,14 @@ import type { MoveId } from "#enums/move-id"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { PokeballType } from "#enums/pokeball"; -import { RewardTier } from "#enums/reward-tier"; import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { TrainerVariant } from "#enums/trainer-variant"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { Trainer } from "#field/trainer"; +import type { CustomRewardSettings } from "#items/reward-pool-utils"; import { TrainerItemEffect } from "#items/trainer-item"; -import type { CustomModifierSettings } from "#modifiers/modifier-type"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import i18next from "#plugins/i18n"; import { MusicPreference } from "#system/settings"; @@ -481,7 +480,7 @@ export class FixedBattleConfig { public getTrainer: GetTrainerFunc; public getEnemyParty: GetEnemyPartyFunc; public seedOffsetWaveIndex: number; - public customModifierRewardSettings?: CustomModifierSettings; + public customRewardSettings?: CustomRewardSettings; setBattleType(battleType: BattleType): FixedBattleConfig { this.battleType = battleType; @@ -508,8 +507,8 @@ export class FixedBattleConfig { return this; } - setCustomModifierRewards(customModifierRewardSettings: CustomModifierSettings) { - this.customModifierRewardSettings = customModifierRewardSettings; + setCustomRewards(customRewardSettings: CustomRewardSettings) { + this.customRewardSettings = customRewardSettings; return this; } } diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts index b6317e8edd4..d24bd28de1a 100644 --- a/src/data/balance/tms.ts +++ b/src/data/balance/tms.ts @@ -1,4 +1,4 @@ -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; @@ -68591,324 +68591,324 @@ function transposeTmSpecies(): SpeciesTmMoves { export const speciesTmMoves: SpeciesTmMoves = transposeTmSpecies(); interface TmPoolTiers { - [key: number]: RewardTier + [key: number]: RarityTier } export const tmPoolTiers: TmPoolTiers = { - [MoveId.MEGA_PUNCH]: RewardTier.GREAT, - [MoveId.PAY_DAY]: RewardTier.ULTRA, - [MoveId.FIRE_PUNCH]: RewardTier.GREAT, - [MoveId.ICE_PUNCH]: RewardTier.GREAT, - [MoveId.THUNDER_PUNCH]: RewardTier.GREAT, - [MoveId.SWORDS_DANCE]: RewardTier.COMMON, - [MoveId.CUT]: RewardTier.COMMON, - [MoveId.FLY]: RewardTier.COMMON, - [MoveId.MEGA_KICK]: RewardTier.GREAT, - [MoveId.BODY_SLAM]: RewardTier.GREAT, - [MoveId.TAKE_DOWN]: RewardTier.GREAT, - [MoveId.DOUBLE_EDGE]: RewardTier.ULTRA, - [MoveId.PIN_MISSILE]: RewardTier.COMMON, - [MoveId.ROAR]: RewardTier.COMMON, - [MoveId.FLAMETHROWER]: RewardTier.ULTRA, - [MoveId.HYDRO_PUMP]: RewardTier.ULTRA, - [MoveId.SURF]: RewardTier.ULTRA, - [MoveId.ICE_BEAM]: RewardTier.ULTRA, - [MoveId.BLIZZARD]: RewardTier.ULTRA, - [MoveId.PSYBEAM]: RewardTier.GREAT, - [MoveId.HYPER_BEAM]: RewardTier.ULTRA, - [MoveId.LOW_KICK]: RewardTier.COMMON, - [MoveId.COUNTER]: RewardTier.COMMON, - [MoveId.STRENGTH]: RewardTier.GREAT, - [MoveId.SOLAR_BEAM]: RewardTier.ULTRA, - [MoveId.FIRE_SPIN]: RewardTier.COMMON, - [MoveId.THUNDERBOLT]: RewardTier.ULTRA, - [MoveId.THUNDER_WAVE]: RewardTier.COMMON, - [MoveId.THUNDER]: RewardTier.ULTRA, - [MoveId.EARTHQUAKE]: RewardTier.ULTRA, - [MoveId.DIG]: RewardTier.GREAT, - [MoveId.TOXIC]: RewardTier.GREAT, - [MoveId.PSYCHIC]: RewardTier.ULTRA, - [MoveId.AGILITY]: RewardTier.COMMON, - [MoveId.NIGHT_SHADE]: RewardTier.COMMON, - [MoveId.SCREECH]: RewardTier.COMMON, - [MoveId.DOUBLE_TEAM]: RewardTier.COMMON, - [MoveId.CONFUSE_RAY]: RewardTier.COMMON, - [MoveId.LIGHT_SCREEN]: RewardTier.COMMON, - [MoveId.HAZE]: RewardTier.COMMON, - [MoveId.REFLECT]: RewardTier.COMMON, - [MoveId.FOCUS_ENERGY]: RewardTier.COMMON, - [MoveId.METRONOME]: RewardTier.COMMON, - [MoveId.SELF_DESTRUCT]: RewardTier.GREAT, - [MoveId.FIRE_BLAST]: RewardTier.ULTRA, - [MoveId.WATERFALL]: RewardTier.GREAT, - [MoveId.SWIFT]: RewardTier.COMMON, - [MoveId.AMNESIA]: RewardTier.COMMON, - [MoveId.DREAM_EATER]: RewardTier.GREAT, - [MoveId.LEECH_LIFE]: RewardTier.ULTRA, - [MoveId.FLASH]: RewardTier.COMMON, - [MoveId.EXPLOSION]: RewardTier.GREAT, - [MoveId.REST]: RewardTier.COMMON, - [MoveId.ROCK_SLIDE]: RewardTier.GREAT, - [MoveId.TRI_ATTACK]: RewardTier.ULTRA, - [MoveId.SUPER_FANG]: RewardTier.COMMON, - [MoveId.SUBSTITUTE]: RewardTier.COMMON, - [MoveId.THIEF]: RewardTier.GREAT, - [MoveId.SNORE]: RewardTier.COMMON, - [MoveId.CURSE]: RewardTier.COMMON, - [MoveId.REVERSAL]: RewardTier.COMMON, - [MoveId.SPITE]: RewardTier.COMMON, - [MoveId.PROTECT]: RewardTier.COMMON, - [MoveId.SCARY_FACE]: RewardTier.COMMON, - [MoveId.SLUDGE_BOMB]: RewardTier.GREAT, - [MoveId.MUD_SLAP]: RewardTier.COMMON, - [MoveId.SPIKES]: RewardTier.COMMON, - [MoveId.ICY_WIND]: RewardTier.GREAT, - [MoveId.OUTRAGE]: RewardTier.ULTRA, - [MoveId.SANDSTORM]: RewardTier.COMMON, - [MoveId.GIGA_DRAIN]: RewardTier.ULTRA, - [MoveId.ENDURE]: RewardTier.COMMON, - [MoveId.CHARM]: RewardTier.COMMON, - [MoveId.FALSE_SWIPE]: RewardTier.COMMON, - [MoveId.SWAGGER]: RewardTier.COMMON, - [MoveId.STEEL_WING]: RewardTier.GREAT, - [MoveId.ATTRACT]: RewardTier.COMMON, - [MoveId.SLEEP_TALK]: RewardTier.COMMON, - [MoveId.HEAL_BELL]: RewardTier.COMMON, - [MoveId.RETURN]: RewardTier.ULTRA, - [MoveId.FRUSTRATION]: RewardTier.COMMON, - [MoveId.SAFEGUARD]: RewardTier.COMMON, - [MoveId.PAIN_SPLIT]: RewardTier.COMMON, - [MoveId.MEGAHORN]: RewardTier.ULTRA, - [MoveId.BATON_PASS]: RewardTier.COMMON, - [MoveId.ENCORE]: RewardTier.COMMON, - [MoveId.IRON_TAIL]: RewardTier.GREAT, - [MoveId.METAL_CLAW]: RewardTier.COMMON, - [MoveId.SYNTHESIS]: RewardTier.GREAT, - [MoveId.HIDDEN_POWER]: RewardTier.GREAT, - [MoveId.RAIN_DANCE]: RewardTier.COMMON, - [MoveId.SUNNY_DAY]: RewardTier.COMMON, - [MoveId.CRUNCH]: RewardTier.GREAT, - [MoveId.PSYCH_UP]: RewardTier.COMMON, - [MoveId.SHADOW_BALL]: RewardTier.ULTRA, - [MoveId.FUTURE_SIGHT]: RewardTier.GREAT, - [MoveId.ROCK_SMASH]: RewardTier.COMMON, - [MoveId.WHIRLPOOL]: RewardTier.COMMON, - [MoveId.BEAT_UP]: RewardTier.COMMON, - [MoveId.UPROAR]: RewardTier.GREAT, - [MoveId.HEAT_WAVE]: RewardTier.ULTRA, - [MoveId.HAIL]: RewardTier.COMMON, - [MoveId.TORMENT]: RewardTier.COMMON, - [MoveId.WILL_O_WISP]: RewardTier.COMMON, - [MoveId.FACADE]: RewardTier.GREAT, - [MoveId.FOCUS_PUNCH]: RewardTier.COMMON, - [MoveId.NATURE_POWER]: RewardTier.COMMON, - [MoveId.CHARGE]: RewardTier.COMMON, - [MoveId.TAUNT]: RewardTier.COMMON, - [MoveId.HELPING_HAND]: RewardTier.COMMON, - [MoveId.TRICK]: RewardTier.COMMON, - [MoveId.SUPERPOWER]: RewardTier.ULTRA, - [MoveId.RECYCLE]: RewardTier.COMMON, - [MoveId.REVENGE]: RewardTier.GREAT, - [MoveId.BRICK_BREAK]: RewardTier.GREAT, - [MoveId.KNOCK_OFF]: RewardTier.GREAT, - [MoveId.ENDEAVOR]: RewardTier.COMMON, - [MoveId.SKILL_SWAP]: RewardTier.COMMON, - [MoveId.IMPRISON]: RewardTier.COMMON, - [MoveId.SECRET_POWER]: RewardTier.COMMON, - [MoveId.DIVE]: RewardTier.GREAT, - [MoveId.FEATHER_DANCE]: RewardTier.COMMON, - [MoveId.BLAZE_KICK]: RewardTier.GREAT, - [MoveId.HYPER_VOICE]: RewardTier.ULTRA, - [MoveId.BLAST_BURN]: RewardTier.ULTRA, - [MoveId.HYDRO_CANNON]: RewardTier.ULTRA, - [MoveId.WEATHER_BALL]: RewardTier.COMMON, - [MoveId.FAKE_TEARS]: RewardTier.COMMON, - [MoveId.AIR_CUTTER]: RewardTier.GREAT, - [MoveId.OVERHEAT]: RewardTier.ULTRA, - [MoveId.ROCK_TOMB]: RewardTier.GREAT, - [MoveId.METAL_SOUND]: RewardTier.COMMON, - [MoveId.COSMIC_POWER]: RewardTier.COMMON, - [MoveId.SIGNAL_BEAM]: RewardTier.GREAT, - [MoveId.SAND_TOMB]: RewardTier.COMMON, - [MoveId.MUDDY_WATER]: RewardTier.GREAT, - [MoveId.BULLET_SEED]: RewardTier.GREAT, - [MoveId.AERIAL_ACE]: RewardTier.GREAT, - [MoveId.ICICLE_SPEAR]: RewardTier.GREAT, - [MoveId.IRON_DEFENSE]: RewardTier.GREAT, - [MoveId.DRAGON_CLAW]: RewardTier.ULTRA, - [MoveId.FRENZY_PLANT]: RewardTier.ULTRA, - [MoveId.BULK_UP]: RewardTier.COMMON, - [MoveId.BOUNCE]: RewardTier.GREAT, - [MoveId.MUD_SHOT]: RewardTier.GREAT, - [MoveId.POISON_TAIL]: RewardTier.GREAT, - [MoveId.COVET]: RewardTier.GREAT, - [MoveId.MAGICAL_LEAF]: RewardTier.GREAT, - [MoveId.CALM_MIND]: RewardTier.GREAT, - [MoveId.LEAF_BLADE]: RewardTier.ULTRA, - [MoveId.DRAGON_DANCE]: RewardTier.GREAT, - [MoveId.ROCK_BLAST]: RewardTier.GREAT, - [MoveId.WATER_PULSE]: RewardTier.GREAT, - [MoveId.ROOST]: RewardTier.GREAT, - [MoveId.GRAVITY]: RewardTier.COMMON, - [MoveId.GYRO_BALL]: RewardTier.COMMON, - [MoveId.BRINE]: RewardTier.GREAT, - [MoveId.PLUCK]: RewardTier.GREAT, - [MoveId.TAILWIND]: RewardTier.GREAT, - [MoveId.U_TURN]: RewardTier.GREAT, - [MoveId.CLOSE_COMBAT]: RewardTier.ULTRA, - [MoveId.PAYBACK]: RewardTier.COMMON, - [MoveId.ASSURANCE]: RewardTier.COMMON, - [MoveId.EMBARGO]: RewardTier.COMMON, - [MoveId.FLING]: RewardTier.COMMON, - [MoveId.GASTRO_ACID]: RewardTier.GREAT, - [MoveId.POWER_SWAP]: RewardTier.COMMON, - [MoveId.GUARD_SWAP]: RewardTier.COMMON, - [MoveId.WORRY_SEED]: RewardTier.GREAT, - [MoveId.TOXIC_SPIKES]: RewardTier.GREAT, - [MoveId.FLARE_BLITZ]: RewardTier.ULTRA, - [MoveId.AURA_SPHERE]: RewardTier.GREAT, - [MoveId.ROCK_POLISH]: RewardTier.COMMON, - [MoveId.POISON_JAB]: RewardTier.GREAT, - [MoveId.DARK_PULSE]: RewardTier.GREAT, - [MoveId.AQUA_TAIL]: RewardTier.GREAT, - [MoveId.SEED_BOMB]: RewardTier.GREAT, - [MoveId.AIR_SLASH]: RewardTier.GREAT, - [MoveId.X_SCISSOR]: RewardTier.GREAT, - [MoveId.BUG_BUZZ]: RewardTier.GREAT, - [MoveId.DRAGON_PULSE]: RewardTier.GREAT, - [MoveId.POWER_GEM]: RewardTier.GREAT, - [MoveId.DRAIN_PUNCH]: RewardTier.GREAT, - [MoveId.VACUUM_WAVE]: RewardTier.COMMON, - [MoveId.FOCUS_BLAST]: RewardTier.GREAT, - [MoveId.ENERGY_BALL]: RewardTier.GREAT, - [MoveId.BRAVE_BIRD]: RewardTier.ULTRA, - [MoveId.EARTH_POWER]: RewardTier.ULTRA, - [MoveId.GIGA_IMPACT]: RewardTier.GREAT, - [MoveId.NASTY_PLOT]: RewardTier.COMMON, - [MoveId.AVALANCHE]: RewardTier.GREAT, - [MoveId.SHADOW_CLAW]: RewardTier.GREAT, - [MoveId.THUNDER_FANG]: RewardTier.GREAT, - [MoveId.ICE_FANG]: RewardTier.GREAT, - [MoveId.FIRE_FANG]: RewardTier.GREAT, - [MoveId.PSYCHO_CUT]: RewardTier.GREAT, - [MoveId.ZEN_HEADBUTT]: RewardTier.GREAT, - [MoveId.FLASH_CANNON]: RewardTier.GREAT, - [MoveId.ROCK_CLIMB]: RewardTier.GREAT, - [MoveId.DEFOG]: RewardTier.COMMON, - [MoveId.TRICK_ROOM]: RewardTier.COMMON, - [MoveId.DRACO_METEOR]: RewardTier.ULTRA, - [MoveId.LEAF_STORM]: RewardTier.ULTRA, - [MoveId.POWER_WHIP]: RewardTier.ULTRA, - [MoveId.CROSS_POISON]: RewardTier.GREAT, - [MoveId.GUNK_SHOT]: RewardTier.ULTRA, - [MoveId.IRON_HEAD]: RewardTier.GREAT, - [MoveId.STONE_EDGE]: RewardTier.ULTRA, - [MoveId.STEALTH_ROCK]: RewardTier.COMMON, - [MoveId.GRASS_KNOT]: RewardTier.ULTRA, - [MoveId.BUG_BITE]: RewardTier.GREAT, - [MoveId.CHARGE_BEAM]: RewardTier.GREAT, - [MoveId.HONE_CLAWS]: RewardTier.COMMON, - [MoveId.WONDER_ROOM]: RewardTier.COMMON, - [MoveId.PSYSHOCK]: RewardTier.GREAT, - [MoveId.VENOSHOCK]: RewardTier.GREAT, - [MoveId.MAGIC_ROOM]: RewardTier.COMMON, - [MoveId.SMACK_DOWN]: RewardTier.COMMON, - [MoveId.SLUDGE_WAVE]: RewardTier.GREAT, - [MoveId.HEAVY_SLAM]: RewardTier.GREAT, - [MoveId.ELECTRO_BALL]: RewardTier.GREAT, - [MoveId.FLAME_CHARGE]: RewardTier.GREAT, - [MoveId.LOW_SWEEP]: RewardTier.GREAT, - [MoveId.ACID_SPRAY]: RewardTier.COMMON, - [MoveId.FOUL_PLAY]: RewardTier.ULTRA, - [MoveId.ROUND]: RewardTier.COMMON, - [MoveId.ECHOED_VOICE]: RewardTier.COMMON, - [MoveId.STORED_POWER]: RewardTier.COMMON, - [MoveId.ALLY_SWITCH]: RewardTier.COMMON, - [MoveId.SCALD]: RewardTier.GREAT, - [MoveId.HEX]: RewardTier.GREAT, - [MoveId.SKY_DROP]: RewardTier.GREAT, - [MoveId.INCINERATE]: RewardTier.GREAT, - [MoveId.QUASH]: RewardTier.COMMON, - [MoveId.ACROBATICS]: RewardTier.GREAT, - [MoveId.RETALIATE]: RewardTier.GREAT, - [MoveId.WATER_PLEDGE]: RewardTier.GREAT, - [MoveId.FIRE_PLEDGE]: RewardTier.GREAT, - [MoveId.GRASS_PLEDGE]: RewardTier.GREAT, - [MoveId.VOLT_SWITCH]: RewardTier.GREAT, - [MoveId.STRUGGLE_BUG]: RewardTier.COMMON, - [MoveId.BULLDOZE]: RewardTier.GREAT, - [MoveId.FROST_BREATH]: RewardTier.GREAT, - [MoveId.DRAGON_TAIL]: RewardTier.GREAT, - [MoveId.WORK_UP]: RewardTier.COMMON, - [MoveId.ELECTROWEB]: RewardTier.GREAT, - [MoveId.WILD_CHARGE]: RewardTier.GREAT, - [MoveId.DRILL_RUN]: RewardTier.GREAT, - [MoveId.RAZOR_SHELL]: RewardTier.GREAT, - [MoveId.HEAT_CRASH]: RewardTier.GREAT, - [MoveId.TAIL_SLAP]: RewardTier.GREAT, - [MoveId.HURRICANE]: RewardTier.ULTRA, - [MoveId.SNARL]: RewardTier.COMMON, - [MoveId.PHANTOM_FORCE]: RewardTier.ULTRA, - [MoveId.PETAL_BLIZZARD]: RewardTier.GREAT, - [MoveId.DISARMING_VOICE]: RewardTier.GREAT, - [MoveId.DRAINING_KISS]: RewardTier.GREAT, - [MoveId.GRASSY_TERRAIN]: RewardTier.COMMON, - [MoveId.MISTY_TERRAIN]: RewardTier.COMMON, - [MoveId.PLAY_ROUGH]: RewardTier.GREAT, - [MoveId.CONFIDE]: RewardTier.COMMON, - [MoveId.MYSTICAL_FIRE]: RewardTier.GREAT, - [MoveId.EERIE_IMPULSE]: RewardTier.COMMON, - [MoveId.VENOM_DRENCH]: RewardTier.COMMON, - [MoveId.ELECTRIC_TERRAIN]: RewardTier.COMMON, - [MoveId.DAZZLING_GLEAM]: RewardTier.ULTRA, - [MoveId.INFESTATION]: RewardTier.COMMON, - [MoveId.POWER_UP_PUNCH]: RewardTier.GREAT, - [MoveId.DARKEST_LARIAT]: RewardTier.GREAT, - [MoveId.HIGH_HORSEPOWER]: RewardTier.ULTRA, - [MoveId.SOLAR_BLADE]: RewardTier.GREAT, - [MoveId.THROAT_CHOP]: RewardTier.GREAT, - [MoveId.POLLEN_PUFF]: RewardTier.GREAT, - [MoveId.PSYCHIC_TERRAIN]: RewardTier.COMMON, - [MoveId.LUNGE]: RewardTier.GREAT, - [MoveId.SPEED_SWAP]: RewardTier.COMMON, - [MoveId.SMART_STRIKE]: RewardTier.GREAT, - [MoveId.BRUTAL_SWING]: RewardTier.GREAT, - [MoveId.AURORA_VEIL]: RewardTier.COMMON, - [MoveId.PSYCHIC_FANGS]: RewardTier.GREAT, - [MoveId.STOMPING_TANTRUM]: RewardTier.GREAT, - [MoveId.LIQUIDATION]: RewardTier.ULTRA, - [MoveId.BODY_PRESS]: RewardTier.ULTRA, - [MoveId.BREAKING_SWIPE]: RewardTier.GREAT, - [MoveId.STEEL_BEAM]: RewardTier.ULTRA, - [MoveId.EXPANDING_FORCE]: RewardTier.GREAT, - [MoveId.STEEL_ROLLER]: RewardTier.COMMON, - [MoveId.SCALE_SHOT]: RewardTier.ULTRA, - [MoveId.METEOR_BEAM]: RewardTier.GREAT, - [MoveId.MISTY_EXPLOSION]: RewardTier.COMMON, - [MoveId.GRASSY_GLIDE]: RewardTier.COMMON, - [MoveId.RISING_VOLTAGE]: RewardTier.COMMON, - [MoveId.TERRAIN_PULSE]: RewardTier.COMMON, - [MoveId.SKITTER_SMACK]: RewardTier.GREAT, - [MoveId.BURNING_JEALOUSY]: RewardTier.GREAT, - [MoveId.LASH_OUT]: RewardTier.GREAT, - [MoveId.POLTERGEIST]: RewardTier.ULTRA, - [MoveId.CORROSIVE_GAS]: RewardTier.COMMON, - [MoveId.COACHING]: RewardTier.COMMON, - [MoveId.FLIP_TURN]: RewardTier.COMMON, - [MoveId.TRIPLE_AXEL]: RewardTier.COMMON, - [MoveId.DUAL_WINGBEAT]: RewardTier.COMMON, - [MoveId.SCORCHING_SANDS]: RewardTier.GREAT, - [MoveId.TERA_BLAST]: RewardTier.GREAT, - [MoveId.ICE_SPINNER]: RewardTier.GREAT, - [MoveId.SNOWSCAPE]: RewardTier.COMMON, - [MoveId.POUNCE]: RewardTier.COMMON, - [MoveId.TRAILBLAZE]: RewardTier.COMMON, - [MoveId.CHILLING_WATER]: RewardTier.COMMON, - [MoveId.HARD_PRESS]: RewardTier.GREAT, - [MoveId.DRAGON_CHEER]: RewardTier.COMMON, - [MoveId.ALLURING_VOICE]: RewardTier.GREAT, - [MoveId.TEMPER_FLARE]: RewardTier.GREAT, - [MoveId.SUPERCELL_SLAM]: RewardTier.GREAT, - [MoveId.PSYCHIC_NOISE]: RewardTier.GREAT, - [MoveId.UPPER_HAND]: RewardTier.COMMON, + [MoveId.MEGA_PUNCH]: RarityTier.GREAT, + [MoveId.PAY_DAY]: RarityTier.ULTRA, + [MoveId.FIRE_PUNCH]: RarityTier.GREAT, + [MoveId.ICE_PUNCH]: RarityTier.GREAT, + [MoveId.THUNDER_PUNCH]: RarityTier.GREAT, + [MoveId.SWORDS_DANCE]: RarityTier.COMMON, + [MoveId.CUT]: RarityTier.COMMON, + [MoveId.FLY]: RarityTier.COMMON, + [MoveId.MEGA_KICK]: RarityTier.GREAT, + [MoveId.BODY_SLAM]: RarityTier.GREAT, + [MoveId.TAKE_DOWN]: RarityTier.GREAT, + [MoveId.DOUBLE_EDGE]: RarityTier.ULTRA, + [MoveId.PIN_MISSILE]: RarityTier.COMMON, + [MoveId.ROAR]: RarityTier.COMMON, + [MoveId.FLAMETHROWER]: RarityTier.ULTRA, + [MoveId.HYDRO_PUMP]: RarityTier.ULTRA, + [MoveId.SURF]: RarityTier.ULTRA, + [MoveId.ICE_BEAM]: RarityTier.ULTRA, + [MoveId.BLIZZARD]: RarityTier.ULTRA, + [MoveId.PSYBEAM]: RarityTier.GREAT, + [MoveId.HYPER_BEAM]: RarityTier.ULTRA, + [MoveId.LOW_KICK]: RarityTier.COMMON, + [MoveId.COUNTER]: RarityTier.COMMON, + [MoveId.STRENGTH]: RarityTier.GREAT, + [MoveId.SOLAR_BEAM]: RarityTier.ULTRA, + [MoveId.FIRE_SPIN]: RarityTier.COMMON, + [MoveId.THUNDERBOLT]: RarityTier.ULTRA, + [MoveId.THUNDER_WAVE]: RarityTier.COMMON, + [MoveId.THUNDER]: RarityTier.ULTRA, + [MoveId.EARTHQUAKE]: RarityTier.ULTRA, + [MoveId.DIG]: RarityTier.GREAT, + [MoveId.TOXIC]: RarityTier.GREAT, + [MoveId.PSYCHIC]: RarityTier.ULTRA, + [MoveId.AGILITY]: RarityTier.COMMON, + [MoveId.NIGHT_SHADE]: RarityTier.COMMON, + [MoveId.SCREECH]: RarityTier.COMMON, + [MoveId.DOUBLE_TEAM]: RarityTier.COMMON, + [MoveId.CONFUSE_RAY]: RarityTier.COMMON, + [MoveId.LIGHT_SCREEN]: RarityTier.COMMON, + [MoveId.HAZE]: RarityTier.COMMON, + [MoveId.REFLECT]: RarityTier.COMMON, + [MoveId.FOCUS_ENERGY]: RarityTier.COMMON, + [MoveId.METRONOME]: RarityTier.COMMON, + [MoveId.SELF_DESTRUCT]: RarityTier.GREAT, + [MoveId.FIRE_BLAST]: RarityTier.ULTRA, + [MoveId.WATERFALL]: RarityTier.GREAT, + [MoveId.SWIFT]: RarityTier.COMMON, + [MoveId.AMNESIA]: RarityTier.COMMON, + [MoveId.DREAM_EATER]: RarityTier.GREAT, + [MoveId.LEECH_LIFE]: RarityTier.ULTRA, + [MoveId.FLASH]: RarityTier.COMMON, + [MoveId.EXPLOSION]: RarityTier.GREAT, + [MoveId.REST]: RarityTier.COMMON, + [MoveId.ROCK_SLIDE]: RarityTier.GREAT, + [MoveId.TRI_ATTACK]: RarityTier.ULTRA, + [MoveId.SUPER_FANG]: RarityTier.COMMON, + [MoveId.SUBSTITUTE]: RarityTier.COMMON, + [MoveId.THIEF]: RarityTier.GREAT, + [MoveId.SNORE]: RarityTier.COMMON, + [MoveId.CURSE]: RarityTier.COMMON, + [MoveId.REVERSAL]: RarityTier.COMMON, + [MoveId.SPITE]: RarityTier.COMMON, + [MoveId.PROTECT]: RarityTier.COMMON, + [MoveId.SCARY_FACE]: RarityTier.COMMON, + [MoveId.SLUDGE_BOMB]: RarityTier.GREAT, + [MoveId.MUD_SLAP]: RarityTier.COMMON, + [MoveId.SPIKES]: RarityTier.COMMON, + [MoveId.ICY_WIND]: RarityTier.GREAT, + [MoveId.OUTRAGE]: RarityTier.ULTRA, + [MoveId.SANDSTORM]: RarityTier.COMMON, + [MoveId.GIGA_DRAIN]: RarityTier.ULTRA, + [MoveId.ENDURE]: RarityTier.COMMON, + [MoveId.CHARM]: RarityTier.COMMON, + [MoveId.FALSE_SWIPE]: RarityTier.COMMON, + [MoveId.SWAGGER]: RarityTier.COMMON, + [MoveId.STEEL_WING]: RarityTier.GREAT, + [MoveId.ATTRACT]: RarityTier.COMMON, + [MoveId.SLEEP_TALK]: RarityTier.COMMON, + [MoveId.HEAL_BELL]: RarityTier.COMMON, + [MoveId.RETURN]: RarityTier.ULTRA, + [MoveId.FRUSTRATION]: RarityTier.COMMON, + [MoveId.SAFEGUARD]: RarityTier.COMMON, + [MoveId.PAIN_SPLIT]: RarityTier.COMMON, + [MoveId.MEGAHORN]: RarityTier.ULTRA, + [MoveId.BATON_PASS]: RarityTier.COMMON, + [MoveId.ENCORE]: RarityTier.COMMON, + [MoveId.IRON_TAIL]: RarityTier.GREAT, + [MoveId.METAL_CLAW]: RarityTier.COMMON, + [MoveId.SYNTHESIS]: RarityTier.GREAT, + [MoveId.HIDDEN_POWER]: RarityTier.GREAT, + [MoveId.RAIN_DANCE]: RarityTier.COMMON, + [MoveId.SUNNY_DAY]: RarityTier.COMMON, + [MoveId.CRUNCH]: RarityTier.GREAT, + [MoveId.PSYCH_UP]: RarityTier.COMMON, + [MoveId.SHADOW_BALL]: RarityTier.ULTRA, + [MoveId.FUTURE_SIGHT]: RarityTier.GREAT, + [MoveId.ROCK_SMASH]: RarityTier.COMMON, + [MoveId.WHIRLPOOL]: RarityTier.COMMON, + [MoveId.BEAT_UP]: RarityTier.COMMON, + [MoveId.UPROAR]: RarityTier.GREAT, + [MoveId.HEAT_WAVE]: RarityTier.ULTRA, + [MoveId.HAIL]: RarityTier.COMMON, + [MoveId.TORMENT]: RarityTier.COMMON, + [MoveId.WILL_O_WISP]: RarityTier.COMMON, + [MoveId.FACADE]: RarityTier.GREAT, + [MoveId.FOCUS_PUNCH]: RarityTier.COMMON, + [MoveId.NATURE_POWER]: RarityTier.COMMON, + [MoveId.CHARGE]: RarityTier.COMMON, + [MoveId.TAUNT]: RarityTier.COMMON, + [MoveId.HELPING_HAND]: RarityTier.COMMON, + [MoveId.TRICK]: RarityTier.COMMON, + [MoveId.SUPERPOWER]: RarityTier.ULTRA, + [MoveId.RECYCLE]: RarityTier.COMMON, + [MoveId.REVENGE]: RarityTier.GREAT, + [MoveId.BRICK_BREAK]: RarityTier.GREAT, + [MoveId.KNOCK_OFF]: RarityTier.GREAT, + [MoveId.ENDEAVOR]: RarityTier.COMMON, + [MoveId.SKILL_SWAP]: RarityTier.COMMON, + [MoveId.IMPRISON]: RarityTier.COMMON, + [MoveId.SECRET_POWER]: RarityTier.COMMON, + [MoveId.DIVE]: RarityTier.GREAT, + [MoveId.FEATHER_DANCE]: RarityTier.COMMON, + [MoveId.BLAZE_KICK]: RarityTier.GREAT, + [MoveId.HYPER_VOICE]: RarityTier.ULTRA, + [MoveId.BLAST_BURN]: RarityTier.ULTRA, + [MoveId.HYDRO_CANNON]: RarityTier.ULTRA, + [MoveId.WEATHER_BALL]: RarityTier.COMMON, + [MoveId.FAKE_TEARS]: RarityTier.COMMON, + [MoveId.AIR_CUTTER]: RarityTier.GREAT, + [MoveId.OVERHEAT]: RarityTier.ULTRA, + [MoveId.ROCK_TOMB]: RarityTier.GREAT, + [MoveId.METAL_SOUND]: RarityTier.COMMON, + [MoveId.COSMIC_POWER]: RarityTier.COMMON, + [MoveId.SIGNAL_BEAM]: RarityTier.GREAT, + [MoveId.SAND_TOMB]: RarityTier.COMMON, + [MoveId.MUDDY_WATER]: RarityTier.GREAT, + [MoveId.BULLET_SEED]: RarityTier.GREAT, + [MoveId.AERIAL_ACE]: RarityTier.GREAT, + [MoveId.ICICLE_SPEAR]: RarityTier.GREAT, + [MoveId.IRON_DEFENSE]: RarityTier.GREAT, + [MoveId.DRAGON_CLAW]: RarityTier.ULTRA, + [MoveId.FRENZY_PLANT]: RarityTier.ULTRA, + [MoveId.BULK_UP]: RarityTier.COMMON, + [MoveId.BOUNCE]: RarityTier.GREAT, + [MoveId.MUD_SHOT]: RarityTier.GREAT, + [MoveId.POISON_TAIL]: RarityTier.GREAT, + [MoveId.COVET]: RarityTier.GREAT, + [MoveId.MAGICAL_LEAF]: RarityTier.GREAT, + [MoveId.CALM_MIND]: RarityTier.GREAT, + [MoveId.LEAF_BLADE]: RarityTier.ULTRA, + [MoveId.DRAGON_DANCE]: RarityTier.GREAT, + [MoveId.ROCK_BLAST]: RarityTier.GREAT, + [MoveId.WATER_PULSE]: RarityTier.GREAT, + [MoveId.ROOST]: RarityTier.GREAT, + [MoveId.GRAVITY]: RarityTier.COMMON, + [MoveId.GYRO_BALL]: RarityTier.COMMON, + [MoveId.BRINE]: RarityTier.GREAT, + [MoveId.PLUCK]: RarityTier.GREAT, + [MoveId.TAILWIND]: RarityTier.GREAT, + [MoveId.U_TURN]: RarityTier.GREAT, + [MoveId.CLOSE_COMBAT]: RarityTier.ULTRA, + [MoveId.PAYBACK]: RarityTier.COMMON, + [MoveId.ASSURANCE]: RarityTier.COMMON, + [MoveId.EMBARGO]: RarityTier.COMMON, + [MoveId.FLING]: RarityTier.COMMON, + [MoveId.GASTRO_ACID]: RarityTier.GREAT, + [MoveId.POWER_SWAP]: RarityTier.COMMON, + [MoveId.GUARD_SWAP]: RarityTier.COMMON, + [MoveId.WORRY_SEED]: RarityTier.GREAT, + [MoveId.TOXIC_SPIKES]: RarityTier.GREAT, + [MoveId.FLARE_BLITZ]: RarityTier.ULTRA, + [MoveId.AURA_SPHERE]: RarityTier.GREAT, + [MoveId.ROCK_POLISH]: RarityTier.COMMON, + [MoveId.POISON_JAB]: RarityTier.GREAT, + [MoveId.DARK_PULSE]: RarityTier.GREAT, + [MoveId.AQUA_TAIL]: RarityTier.GREAT, + [MoveId.SEED_BOMB]: RarityTier.GREAT, + [MoveId.AIR_SLASH]: RarityTier.GREAT, + [MoveId.X_SCISSOR]: RarityTier.GREAT, + [MoveId.BUG_BUZZ]: RarityTier.GREAT, + [MoveId.DRAGON_PULSE]: RarityTier.GREAT, + [MoveId.POWER_GEM]: RarityTier.GREAT, + [MoveId.DRAIN_PUNCH]: RarityTier.GREAT, + [MoveId.VACUUM_WAVE]: RarityTier.COMMON, + [MoveId.FOCUS_BLAST]: RarityTier.GREAT, + [MoveId.ENERGY_BALL]: RarityTier.GREAT, + [MoveId.BRAVE_BIRD]: RarityTier.ULTRA, + [MoveId.EARTH_POWER]: RarityTier.ULTRA, + [MoveId.GIGA_IMPACT]: RarityTier.GREAT, + [MoveId.NASTY_PLOT]: RarityTier.COMMON, + [MoveId.AVALANCHE]: RarityTier.GREAT, + [MoveId.SHADOW_CLAW]: RarityTier.GREAT, + [MoveId.THUNDER_FANG]: RarityTier.GREAT, + [MoveId.ICE_FANG]: RarityTier.GREAT, + [MoveId.FIRE_FANG]: RarityTier.GREAT, + [MoveId.PSYCHO_CUT]: RarityTier.GREAT, + [MoveId.ZEN_HEADBUTT]: RarityTier.GREAT, + [MoveId.FLASH_CANNON]: RarityTier.GREAT, + [MoveId.ROCK_CLIMB]: RarityTier.GREAT, + [MoveId.DEFOG]: RarityTier.COMMON, + [MoveId.TRICK_ROOM]: RarityTier.COMMON, + [MoveId.DRACO_METEOR]: RarityTier.ULTRA, + [MoveId.LEAF_STORM]: RarityTier.ULTRA, + [MoveId.POWER_WHIP]: RarityTier.ULTRA, + [MoveId.CROSS_POISON]: RarityTier.GREAT, + [MoveId.GUNK_SHOT]: RarityTier.ULTRA, + [MoveId.IRON_HEAD]: RarityTier.GREAT, + [MoveId.STONE_EDGE]: RarityTier.ULTRA, + [MoveId.STEALTH_ROCK]: RarityTier.COMMON, + [MoveId.GRASS_KNOT]: RarityTier.ULTRA, + [MoveId.BUG_BITE]: RarityTier.GREAT, + [MoveId.CHARGE_BEAM]: RarityTier.GREAT, + [MoveId.HONE_CLAWS]: RarityTier.COMMON, + [MoveId.WONDER_ROOM]: RarityTier.COMMON, + [MoveId.PSYSHOCK]: RarityTier.GREAT, + [MoveId.VENOSHOCK]: RarityTier.GREAT, + [MoveId.MAGIC_ROOM]: RarityTier.COMMON, + [MoveId.SMACK_DOWN]: RarityTier.COMMON, + [MoveId.SLUDGE_WAVE]: RarityTier.GREAT, + [MoveId.HEAVY_SLAM]: RarityTier.GREAT, + [MoveId.ELECTRO_BALL]: RarityTier.GREAT, + [MoveId.FLAME_CHARGE]: RarityTier.GREAT, + [MoveId.LOW_SWEEP]: RarityTier.GREAT, + [MoveId.ACID_SPRAY]: RarityTier.COMMON, + [MoveId.FOUL_PLAY]: RarityTier.ULTRA, + [MoveId.ROUND]: RarityTier.COMMON, + [MoveId.ECHOED_VOICE]: RarityTier.COMMON, + [MoveId.STORED_POWER]: RarityTier.COMMON, + [MoveId.ALLY_SWITCH]: RarityTier.COMMON, + [MoveId.SCALD]: RarityTier.GREAT, + [MoveId.HEX]: RarityTier.GREAT, + [MoveId.SKY_DROP]: RarityTier.GREAT, + [MoveId.INCINERATE]: RarityTier.GREAT, + [MoveId.QUASH]: RarityTier.COMMON, + [MoveId.ACROBATICS]: RarityTier.GREAT, + [MoveId.RETALIATE]: RarityTier.GREAT, + [MoveId.WATER_PLEDGE]: RarityTier.GREAT, + [MoveId.FIRE_PLEDGE]: RarityTier.GREAT, + [MoveId.GRASS_PLEDGE]: RarityTier.GREAT, + [MoveId.VOLT_SWITCH]: RarityTier.GREAT, + [MoveId.STRUGGLE_BUG]: RarityTier.COMMON, + [MoveId.BULLDOZE]: RarityTier.GREAT, + [MoveId.FROST_BREATH]: RarityTier.GREAT, + [MoveId.DRAGON_TAIL]: RarityTier.GREAT, + [MoveId.WORK_UP]: RarityTier.COMMON, + [MoveId.ELECTROWEB]: RarityTier.GREAT, + [MoveId.WILD_CHARGE]: RarityTier.GREAT, + [MoveId.DRILL_RUN]: RarityTier.GREAT, + [MoveId.RAZOR_SHELL]: RarityTier.GREAT, + [MoveId.HEAT_CRASH]: RarityTier.GREAT, + [MoveId.TAIL_SLAP]: RarityTier.GREAT, + [MoveId.HURRICANE]: RarityTier.ULTRA, + [MoveId.SNARL]: RarityTier.COMMON, + [MoveId.PHANTOM_FORCE]: RarityTier.ULTRA, + [MoveId.PETAL_BLIZZARD]: RarityTier.GREAT, + [MoveId.DISARMING_VOICE]: RarityTier.GREAT, + [MoveId.DRAINING_KISS]: RarityTier.GREAT, + [MoveId.GRASSY_TERRAIN]: RarityTier.COMMON, + [MoveId.MISTY_TERRAIN]: RarityTier.COMMON, + [MoveId.PLAY_ROUGH]: RarityTier.GREAT, + [MoveId.CONFIDE]: RarityTier.COMMON, + [MoveId.MYSTICAL_FIRE]: RarityTier.GREAT, + [MoveId.EERIE_IMPULSE]: RarityTier.COMMON, + [MoveId.VENOM_DRENCH]: RarityTier.COMMON, + [MoveId.ELECTRIC_TERRAIN]: RarityTier.COMMON, + [MoveId.DAZZLING_GLEAM]: RarityTier.ULTRA, + [MoveId.INFESTATION]: RarityTier.COMMON, + [MoveId.POWER_UP_PUNCH]: RarityTier.GREAT, + [MoveId.DARKEST_LARIAT]: RarityTier.GREAT, + [MoveId.HIGH_HORSEPOWER]: RarityTier.ULTRA, + [MoveId.SOLAR_BLADE]: RarityTier.GREAT, + [MoveId.THROAT_CHOP]: RarityTier.GREAT, + [MoveId.POLLEN_PUFF]: RarityTier.GREAT, + [MoveId.PSYCHIC_TERRAIN]: RarityTier.COMMON, + [MoveId.LUNGE]: RarityTier.GREAT, + [MoveId.SPEED_SWAP]: RarityTier.COMMON, + [MoveId.SMART_STRIKE]: RarityTier.GREAT, + [MoveId.BRUTAL_SWING]: RarityTier.GREAT, + [MoveId.AURORA_VEIL]: RarityTier.COMMON, + [MoveId.PSYCHIC_FANGS]: RarityTier.GREAT, + [MoveId.STOMPING_TANTRUM]: RarityTier.GREAT, + [MoveId.LIQUIDATION]: RarityTier.ULTRA, + [MoveId.BODY_PRESS]: RarityTier.ULTRA, + [MoveId.BREAKING_SWIPE]: RarityTier.GREAT, + [MoveId.STEEL_BEAM]: RarityTier.ULTRA, + [MoveId.EXPANDING_FORCE]: RarityTier.GREAT, + [MoveId.STEEL_ROLLER]: RarityTier.COMMON, + [MoveId.SCALE_SHOT]: RarityTier.ULTRA, + [MoveId.METEOR_BEAM]: RarityTier.GREAT, + [MoveId.MISTY_EXPLOSION]: RarityTier.COMMON, + [MoveId.GRASSY_GLIDE]: RarityTier.COMMON, + [MoveId.RISING_VOLTAGE]: RarityTier.COMMON, + [MoveId.TERRAIN_PULSE]: RarityTier.COMMON, + [MoveId.SKITTER_SMACK]: RarityTier.GREAT, + [MoveId.BURNING_JEALOUSY]: RarityTier.GREAT, + [MoveId.LASH_OUT]: RarityTier.GREAT, + [MoveId.POLTERGEIST]: RarityTier.ULTRA, + [MoveId.CORROSIVE_GAS]: RarityTier.COMMON, + [MoveId.COACHING]: RarityTier.COMMON, + [MoveId.FLIP_TURN]: RarityTier.COMMON, + [MoveId.TRIPLE_AXEL]: RarityTier.COMMON, + [MoveId.DUAL_WINGBEAT]: RarityTier.COMMON, + [MoveId.SCORCHING_SANDS]: RarityTier.GREAT, + [MoveId.TERA_BLAST]: RarityTier.GREAT, + [MoveId.ICE_SPINNER]: RarityTier.GREAT, + [MoveId.SNOWSCAPE]: RarityTier.COMMON, + [MoveId.POUNCE]: RarityTier.COMMON, + [MoveId.TRAILBLAZE]: RarityTier.COMMON, + [MoveId.CHILLING_WATER]: RarityTier.COMMON, + [MoveId.HARD_PRESS]: RarityTier.GREAT, + [MoveId.DRAGON_CHEER]: RarityTier.COMMON, + [MoveId.ALLURING_VOICE]: RarityTier.GREAT, + [MoveId.TEMPER_FLARE]: RarityTier.GREAT, + [MoveId.SUPERCELL_SLAM]: RarityTier.GREAT, + [MoveId.PSYCHIC_NOISE]: RarityTier.GREAT, + [MoveId.UPPER_HAND]: RarityTier.COMMON, }; diff --git a/src/data/challenge.ts b/src/data/challenge.ts index efd28f8ed5d..f4106726e79 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -16,7 +16,7 @@ import type { MoveId } from "#enums/move-id"; import type { MoveSourceType } from "#enums/move-source-type"; import { Nature } from "#enums/nature"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { TrainerVariant } from "#enums/trainer-variant"; @@ -457,13 +457,13 @@ export class SingleGenerationChallenge extends Challenge { .setBattleType(BattleType.TRAINER) .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) .setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true)) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - RewardTier.ROGUE, - RewardTier.ROGUE, - RewardTier.ULTRA, - RewardTier.ULTRA, - RewardTier.ULTRA, + .setCustomRewards({ + guaranteedRarityTiers: [ + RarityTier.ROGUE, + RarityTier.ROGUE, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.ULTRA, ], allowLuckUpgrades: false, }); @@ -474,14 +474,14 @@ export class SingleGenerationChallenge extends Challenge { .setBattleType(BattleType.TRAINER) .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) .setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true)) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - RewardTier.ROGUE, - RewardTier.ROGUE, - RewardTier.ULTRA, - RewardTier.ULTRA, - RewardTier.ULTRA, - RewardTier.ULTRA, + .setCustomRewards({ + guaranteedRarityTiers: [ + RarityTier.ROGUE, + RarityTier.ROGUE, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.ULTRA, ], allowLuckUpgrades: false, }); diff --git a/src/data/data-lists.ts b/src/data/data-lists.ts index 786adb35e5c..85f5a0e6b20 100644 --- a/src/data/data-lists.ts +++ b/src/data/data-lists.ts @@ -3,8 +3,8 @@ import type { PokemonSpecies } from "#data/pokemon-species"; import type { HeldItemId } from "#enums/held-item-id"; import type { TrainerItemId } from "#enums/trainer-item-id"; import type { HeldItem } from "#items/held-item"; +import type { Rewards } from "#items/reward"; import type { TrainerItem } from "#items/trainer-item"; -import type { ModifierTypes } from "#modifiers/modifier-type"; import type { Move } from "#moves/move"; export const allAbilities: Ability[] = []; @@ -15,4 +15,4 @@ export const allHeldItems: Record = {}; export const allTrainerItems: Record = {}; // TODO: Figure out what this is used for and provide an appropriate tsdoc comment -export const modifierTypes = {} as ModifierTypes; +export const allRewards = {} as Rewards; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 476d0455e5e..653fcc1d0e6 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -69,7 +69,7 @@ import { MoveUsedEvent } from "#events/battle-scene"; import type { EnemyPokemon, Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; import { BerryHeldItem, berryTypeToHeldItem } from "#items/berry"; -import { HeldItemEffect } from "#items/held-item"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { TrainerItemEffect } from "#items/trainer-item"; import { applyMoveAttrs } from "#moves/apply-attrs"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves"; @@ -856,7 +856,7 @@ export abstract class Move implements Localizable { if (!this.hasAttr("TypelessAttr")) { globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power); applyHeldItems(HeldItemEffect.ATTACK_TYPE_BOOST, { - pokemon: source, + pokemon: source, moveType: typeChangeHolder.value, movePower: power, }); @@ -2636,14 +2636,14 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { } const stolenItem = heldItems[user.randBattleSeedInt(heldItems.length)]; - + if (!globalScene.tryTransferHeldItem(stolenItem, target, user, false)) { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", - { pokemonName: getPokemonNameWithAffix(user), - targetName: getPokemonNameWithAffix(target), + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", + { pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[stolenItem].name } )); @@ -2719,16 +2719,16 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { globalScene.updateItems(target.isPlayer()); if (this.berriesOnly) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem", + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name })); } else { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem", + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name })); } return true; } - + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { const heldItems = target.getHeldItems(); return heldItems.length ? 5 : 0; @@ -2775,7 +2775,7 @@ export class EatBerryAttr extends MoveEffectAttr { // check for berry pouch preservation globalScene.applyPlayerItems(TrainerItemEffect.PRESERVE_BERRY, {pokemon: pokemon, doPreserve: preserve}); if (!preserve.value) { - this.reduceBerryModifier(pokemon); + this.reduceBerryItem(pokemon); } // Don't update harvest for berries preserved via Berry pouch (no item dupes lol) @@ -2788,7 +2788,7 @@ export class EatBerryAttr extends MoveEffectAttr { return target.getHeldItems().filter(m => isItemInCategory(m, HeldItemCategoryId.BERRY)); } - reduceBerryModifier(target: Pokemon) { + reduceBerryItem(target: Pokemon) { if (this.chosenBerry) { target.loseHeldItem(this.chosenBerry); } @@ -2850,7 +2850,7 @@ export class StealEatBerryAttr extends EatBerryAttr { applyAbAttrs("PostItemLostAbAttr", {pokemon: target}); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: allHeldItems[this.chosenBerry].name }); globalScene.phaseManager.queueMessage(message); - this.reduceBerryModifier(target); + this.reduceBerryItem(target); this.eatBerry(user, target); return true; diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index c081c06b890..57a78f5f9d0 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -1,12 +1,12 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import type { IEggOptions } from "#data/egg"; import { EggSourceType } from "#enums/egg-source-types"; import { EggTier } from "#enums/egg-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; @@ -150,7 +150,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. }, async () => { const encounter = globalScene.currentBattle.mysteryEncounter!; - // Battle the stat trainer for an Egg and great rewards + // Battle the stat trainer for an Egg and great allRewards const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; await transitionMysteryEncounterIntroVisuals(); @@ -164,8 +164,8 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); setEncounterRewards( { - guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], - guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ULTRA], + guaranteedRewardFuncs: [allRewards.SACRED_ASH], + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ULTRA], fillRemaining: true, }, [eggOptions], diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index 0053bb6b9ff..0a9a776472e 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -1,7 +1,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { speciesStarterCosts } from "#balance/starters"; -import { allTrainerItems, modifierTypes } from "#data/data-lists"; +import { allRewards, allTrainerItems } from "#data/data-lists"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -136,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB }) .withOptionPhase(async () => { // Give the player a Shiny Charm - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.SHINY_CHARM); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.SHINY_CHARM); leaveEncounterWithoutBattle(true); }) .build(), @@ -184,7 +184,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 8f05b7031ee..ed581c8f544 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -1,22 +1,22 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { RewardPoolType } from "#enums/reward-pool-type"; import { PERMANENT_STATS, Stat } from "#enums/stat"; import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import { berryTypeToHeldItem } from "#items/berry"; -import type { ModifierTypeOption } from "#modifiers/modifier-type"; -import { regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import type { RewardOption } from "#items/reward"; +import { generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils"; +import { generateRewardOption } from "#items/reward-utils"; import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { - generateModifierTypeOption, getRandomEncounterSpecies, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, @@ -88,7 +88,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. : globalScene.currentBattle.waveIndex > 40 ? 4 : 2; - regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0); + generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), globalScene.getPlayerParty(), 0); encounter.misc = { numBerries }; const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon); @@ -159,20 +159,16 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. } }; - const shopOptions: ModifierTypeOption[] = []; + const shopOptions: RewardOption[] = []; for (let i = 0; i < 5; i++) { // Generate shop berries - const mod = generateModifierTypeOption(modifierTypes.BERRY); + const mod = generateRewardOption(allRewards.BERRY); if (mod) { shopOptions.push(mod); } } - setEncounterRewards( - { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, - undefined, - doBerryRewards, - ); + setEncounterRewards({ guaranteedRewardOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); }, ) @@ -190,10 +186,10 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1); const numBerries: number = encounter.misc.numBerries; - const shopOptions: ModifierTypeOption[] = []; + const shopOptions: RewardOption[] = []; for (let i = 0; i < 5; i++) { // Generate shop berries - const mod = generateModifierTypeOption(modifierTypes.BERRY); + const mod = generateRewardOption(allRewards.BERRY); if (mod) { shopOptions.push(mod); } @@ -246,7 +242,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. }; setEncounterRewards( { - guaranteedModifierTypeOptions: shopOptions, + guaranteedRewardOptions: shopOptions, fillRemaining: false, }, undefined, @@ -279,7 +275,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterRewards( { - guaranteedModifierTypeOptions: shopOptions, + guaranteedRewardOptions: shopOptions, fillRemaining: false, }, undefined, @@ -301,7 +297,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, 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 b92891821df..b526be72ea0 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -1,6 +1,6 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { allHeldItems, allMoves, modifierTypes } from "#data/data-lists"; +import { allHeldItems, allMoves, allRewards } from "#data/data-lists"; import { HeldItemId } from "#enums/held-item-id"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -8,18 +8,18 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerType } from "#enums/trainer-type"; import type { PlayerPokemon, Pokemon } from "#field/pokemon"; -import type { ModifierTypeOption } from "#modifiers/modifier-type"; +import type { RewardOption } from "#items/reward"; +import { generateRewardOption } from "#items/reward-utils"; import { PokemonMove } from "#moves/pokemon-move"; import { getEncounterText, showEncounterDialogue } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { - generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectOptionThenPokemon, @@ -285,7 +285,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde moveTutorOptions, }; - // Assigns callback that teaches move before continuing to rewards + // Assigns callback that teaches move before continuing to allRewards encounter.onRewards = doBugTypeMoveTutor; setEncounterRewards({ fillRemaining: true }); @@ -305,7 +305,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde // Player shows off their bug types const encounter = globalScene.currentBattle.mysteryEncounter!; - // Player gets different rewards depending on the number of bug types they have + // Player gets different allRewards depending on the number of bug types they have const numBugTypes = globalScene.getPlayerParty().filter(p => p.isOfType(PokemonType.BUG, true)).length; const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes, @@ -314,7 +314,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde if (numBugTypes < 2) { setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], + guaranteedRewardFuncs: [allRewards.SUPER_LURE, allRewards.GREAT_BALL], fillRemaining: false, }); encounter.selectedOption!.dialogue!.selected = [ @@ -325,7 +325,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde ]; } else if (numBugTypes < 4) { setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL], + guaranteedRewardFuncs: [allRewards.QUICK_CLAW, allRewards.MAX_LURE, allRewards.ULTRA_BALL], fillRemaining: false, }); encounter.selectedOption!.dialogue!.selected = [ @@ -336,7 +336,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde ]; } else if (numBugTypes < 6) { setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL], + guaranteedRewardFuncs: [allRewards.GRIP_CLAW, allRewards.MAX_LURE, allRewards.ROGUE_BALL], fillRemaining: false, }); encounter.selectedOption!.dialogue!.selected = [ @@ -348,38 +348,38 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde } else { // If the player has any evolution/form change items that are valid for their party, // spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball - const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(modifierTypes.MASTER_BALL)!]; - const specialOptions: ModifierTypeOption[] = []; + const rewardOptions: RewardOption[] = [generateRewardOption(allRewards.MASTER_BALL)!]; + const specialOptions: RewardOption[] = []; if (!globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) { - modifierOptions.push(generateModifierTypeOption(modifierTypes.MEGA_BRACELET)!); + rewardOptions.push(generateRewardOption(allRewards.MEGA_BRACELET)!); } if (!globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) { - modifierOptions.push(generateModifierTypeOption(modifierTypes.DYNAMAX_BAND)!); + rewardOptions.push(generateRewardOption(allRewards.DYNAMAX_BAND)!); } - const nonRareEvolutionModifier = generateModifierTypeOption(modifierTypes.EVOLUTION_ITEM); - if (nonRareEvolutionModifier) { - specialOptions.push(nonRareEvolutionModifier); + const nonRareEvolutionReward = generateRewardOption(allRewards.EVOLUTION_ITEM); + if (nonRareEvolutionReward) { + specialOptions.push(nonRareEvolutionReward); } - const rareEvolutionModifier = generateModifierTypeOption(modifierTypes.RARE_EVOLUTION_ITEM); - if (rareEvolutionModifier) { - specialOptions.push(rareEvolutionModifier); + const rareEvolutionReward = generateRewardOption(allRewards.RARE_EVOLUTION_ITEM); + if (rareEvolutionReward) { + specialOptions.push(rareEvolutionReward); } - const formChangeModifier = generateModifierTypeOption(modifierTypes.FORM_CHANGE_ITEM); - if (formChangeModifier) { - specialOptions.push(formChangeModifier); + const formChangeReward = generateRewardOption(allRewards.FORM_CHANGE_ITEM); + if (formChangeReward) { + specialOptions.push(formChangeReward); } - const rareFormChangeModifier = generateModifierTypeOption(modifierTypes.RARE_FORM_CHANGE_ITEM); - if (rareFormChangeModifier) { - specialOptions.push(rareFormChangeModifier); + const rareFormChangeReward = generateRewardOption(allRewards.RARE_FORM_CHANGE_ITEM); + if (rareFormChangeReward) { + specialOptions.push(rareFormChangeReward); } if (specialOptions.length > 0) { // TODO: should this use `randSeedItem`? - modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); + rewardOptions.push(specialOptions[randSeedInt(specialOptions.length)]); } setEncounterRewards({ - guaranteedModifierTypeOptions: modifierOptions, + guaranteedRewardOptions: rewardOptions, fillRemaining: false, }); encounter.selectedOption!.dialogue!.selected = [ @@ -465,12 +465,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde chosenPokemon.loseHeldItem(lostItem, false); globalScene.updateItems(true); - const bugNet = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; - bugNet.type.tier = RewardTier.ROGUE; + const bugNet = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; + bugNet.type.tier = RarityTier.ROGUE; setEncounterRewards({ - guaranteedModifierTypeOptions: [bugNet], - guaranteedModifierTypeFuncs: [modifierTypes.REVIVER_SEED], + guaranteedRewardOptions: [bugNet], + guaranteedRewardFuncs: [allRewards.REVIVER_SEED], fillRemaining: false, }); leaveEncounterWithoutBattle(true); @@ -744,7 +744,7 @@ function doBugTypeMoveTutor(): Promise { ); } - // Complete battle and go to rewards + // Complete battle and go to allRewards resolve(); }); } diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index b17f70d150f..de9e9b0f3b1 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -16,13 +16,13 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { UiMode } from "#enums/ui-mode"; import type { PlayerPokemon } from "#field/pokemon"; +import { getHeldItemTier } from "#items/held-item-default-tiers"; import { assignItemsFromConfiguration } from "#items/held-item-pool"; -import { getHeldItemTier } from "#items/held-item-tiers"; import { PokemonMove } from "#moves/pokemon-move"; import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; @@ -329,11 +329,11 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder let numRogue = 0; for (const m of items) { - const tier = getHeldItemTier(m) ?? RewardTier.ULTRA; + const tier = getHeldItemTier(m) ?? RarityTier.ULTRA; const stack = mostHeldItemsPokemon.heldItemManager.getStack(m); - if (tier === RewardTier.ROGUE) { + if (tier === RarityTier.ROGUE) { numRogue += stack; - } else if (tier === RewardTier.ULTRA) { + } else if (tier === RarityTier.ULTRA) { numUltra += stack; } mostHeldItemsPokemon.heldItemManager.remove(m, stack); diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index febe171c8b8..56b21490f29 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -1,7 +1,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { EncounterBattleAnim } from "#data/battle-anims"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; @@ -219,7 +219,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder await hideOricorioPokemon(); setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.BATON], + guaranteedRewardFuncs: [allRewards.BATON], fillRemaining: true, }); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]); diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 2d6d0deeed0..aede0c92168 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -1,6 +1,6 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { Challenges } from "#enums/challenges"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -160,7 +160,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE .withOptionPhase(async () => { // Give the player 5 Rogue Balls const encounter = globalScene.currentBattle.mysteryEncounter!; - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.ROGUE_BALL); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.ROGUE_BALL); // Start encounter with random legendary (7-10 starter strength) that has level additive // If this is a mono-type challenge, always ensure the required type is filtered for @@ -204,7 +204,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 8508b4644af..d0cd73f1bc6 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -1,7 +1,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import { allHeldItems, modifierTypes } from "#data/data-lists"; +import { allHeldItems, allRewards } from "#data/data-lists"; import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -34,7 +34,7 @@ const namespace = "mysteryEncounters/delibirdy"; /** Berries only */ const OPTION_2_ALLOWED_HELD_ITEMS = [HeldItemCategoryId.BERRY, HeldItemId.REVIVER_SEED]; -/** Disallowed items are berries, Reviver Seeds, and Vitamins (form change items and fusion items are not PokemonHeldItemModifiers) */ +/** Disallowed items are berries, Reviver Seeds, and Vitamins */ const OPTION_3_DISALLOWED_HELD_ITEMS = [HeldItemCategoryId.BERRY, HeldItemId.REVIVER_SEED]; const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; @@ -61,10 +61,10 @@ const doEventReward = () => { return !fullStack; }); if (candidates.length > 0) { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes[randSeedItem(candidates)]); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards[randSeedItem(candidates)]); } else { // At max stacks, give a Voucher instead - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.VOUCHER); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.VOUCHER); } } }; @@ -168,7 +168,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with // At max stacks, give the first party pokemon a Shell Bell instead backupOption(); } else { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.AMULET_COIN); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.AMULET_COIN); doEventReward(); } @@ -238,7 +238,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with // At max stacks, give the first party pokemon a Shell Bell instead backupOption(); } else { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.CANDY_JAR); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.CANDY_JAR); doEventReward(); } } else { @@ -249,7 +249,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with // At max stacks, give the first party pokemon a Shell Bell instead backupOption(); } else { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.BERRY_POUCH); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.BERRY_POUCH); doEventReward(); } } @@ -321,7 +321,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with // At max stacks, give the first party pokemon a Shell Bell instead backupOption(); } else { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.HEALING_CHARM); + globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.HEALING_CHARM); doEventReward(); } 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 a45c5301a3e..28de326cf46 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -1,12 +1,12 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { SpeciesId } from "#enums/species-id"; import { leaveEncounterWithoutBattle, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter"; -import type { ModifierTypeFunc } from "#types/modifier-types"; +import type { RewardFunc } from "#types/rewards"; import { randSeedInt } from "#utils/common"; /** i18n namespace for encounter */ @@ -59,23 +59,23 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu }, async () => { // Choose TMs - const modifiers: ModifierTypeFunc[] = []; + const rewards: RewardFunc[] = []; let i = 0; while (i < 5) { // 2/2/1 weight on TM rarity const roll = randSeedInt(5); if (roll < 2) { - modifiers.push(modifierTypes.TM_COMMON); + rewards.push(allRewards.TM_COMMON); } else if (roll < 4) { - modifiers.push(modifierTypes.TM_GREAT); + rewards.push(allRewards.TM_GREAT); } else { - modifiers.push(modifierTypes.TM_ULTRA); + rewards.push(allRewards.TM_ULTRA); } i++; } setEncounterRewards({ - guaranteedModifierTypeFuncs: modifiers, + guaranteedRewardFuncs: rewards, fillRemaining: false, }); leaveEncounterWithoutBattle(); @@ -88,21 +88,21 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu }, async () => { // Choose Vitamins - const modifiers: ModifierTypeFunc[] = []; + const rewards: RewardFunc[] = []; let i = 0; while (i < 3) { // 2/1 weight on base stat booster vs PP Up const roll = randSeedInt(3); if (roll === 0) { - modifiers.push(modifierTypes.PP_UP); + rewards.push(allRewards.PP_UP); } else { - modifiers.push(modifierTypes.BASE_STAT_BOOSTER); + rewards.push(allRewards.BASE_STAT_BOOSTER); } i++; } setEncounterRewards({ - guaranteedModifierTypeFuncs: modifiers, + guaranteedRewardFuncs: rewards, fillRemaining: false, }); leaveEncounterWithoutBattle(); @@ -115,21 +115,21 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu }, async () => { // Choose X Items - const modifiers: ModifierTypeFunc[] = []; + const rewards: RewardFunc[] = []; let i = 0; while (i < 5) { // 4/1 weight on base stat booster vs Dire Hit const roll = randSeedInt(5); if (roll === 0) { - modifiers.push(modifierTypes.DIRE_HIT); + rewards.push(allRewards.DIRE_HIT); } else { - modifiers.push(modifierTypes.TEMP_STAT_STAGE_BOOSTER); + rewards.push(allRewards.TEMP_STAT_STAGE_BOOSTER); } i++; } setEncounterRewards({ - guaranteedModifierTypeFuncs: modifiers, + guaranteedRewardFuncs: rewards, fillRemaining: false, }); leaveEncounterWithoutBattle(); @@ -142,25 +142,25 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu }, async () => { // Choose Pokeballs - const modifiers: ModifierTypeFunc[] = []; + const rewards: RewardFunc[] = []; let i = 0; while (i < 4) { // 10/30/20/5 weight on pokeballs const roll = randSeedInt(65); if (roll < 10) { - modifiers.push(modifierTypes.POKEBALL); + rewards.push(allRewards.POKEBALL); } else if (roll < 40) { - modifiers.push(modifierTypes.GREAT_BALL); + rewards.push(allRewards.GREAT_BALL); } else if (roll < 60) { - modifiers.push(modifierTypes.ULTRA_BALL); + rewards.push(allRewards.ULTRA_BALL); } else { - modifiers.push(modifierTypes.ROGUE_BALL); + rewards.push(allRewards.ROGUE_BALL); } i++; } setEncounterRewards({ - guaranteedModifierTypeFuncs: modifiers, + guaranteedRewardFuncs: rewards, fillRemaining: false, }); leaveEncounterWithoutBattle(); diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 9c655e70b8c..35485fba083 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -1,15 +1,15 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { MoveCategory } from "#enums/move-category"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Stat } from "#enums/stat"; import type { PlayerPokemon } from "#field/pokemon"; +import { generateRewardOption } from "#items/reward-utils"; import type { PokemonMove } from "#moves/pokemon-move"; import { - generateModifierTypeOption, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterExp, @@ -96,15 +96,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with const encounter = globalScene.currentBattle.mysteryEncounter!; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!, - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!, - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, - generateModifierTypeOption(modifierTypes.DIRE_HIT)!, - generateModifierTypeOption(modifierTypes.RARER_CANDY)!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, + generateRewardOption(allRewards.DIRE_HIT)!, + generateRewardOption(allRewards.RARER_CANDY)!, ]; setEncounterRewards({ - guaranteedModifierTypeOptions: modifiers, + guaranteedRewardOptions: modifiers, fillRemaining: false, }); } @@ -144,15 +144,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with const encounter = globalScene.currentBattle.mysteryEncounter!; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!, - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!, - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, - generateModifierTypeOption(modifierTypes.DIRE_HIT)!, - generateModifierTypeOption(modifierTypes.RARER_CANDY)!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, + generateRewardOption(allRewards.DIRE_HIT)!, + generateRewardOption(allRewards.RARER_CANDY)!, ]; setEncounterRewards({ - guaranteedModifierTypeOptions: modifiers, + guaranteedRewardOptions: modifiers, fillRemaining: false, }); } @@ -192,15 +192,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with const encounter = globalScene.currentBattle.mysteryEncounter!; if (encounter.misc.correctMove) { const modifiers = [ - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!, - generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, - generateModifierTypeOption(modifierTypes.GREAT_BALL)!, - generateModifierTypeOption(modifierTypes.IV_SCANNER)!, - generateModifierTypeOption(modifierTypes.RARER_CANDY)!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!, + generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, + generateRewardOption(allRewards.GREAT_BALL)!, + generateRewardOption(allRewards.IV_SCANNER)!, + generateRewardOption(allRewards.RARER_CANDY)!, ]; setEncounterRewards({ - guaranteedModifierTypeOptions: modifiers, + guaranteedRewardOptions: modifiers, fillRemaining: false, }); } diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 94422518b25..a3d1da01351 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -250,7 +250,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w } } - // No rewards + // No allRewards leaveEncounterWithoutBattle(true); }, ) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 49848918bd7..adc614fb9d6 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -1,14 +1,16 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RewardPoolType } from "#enums/reward-pool-type"; +import { RarityTier } from "#enums/reward-tier"; +import { TrainerItemId } from "#enums/trainer-item-id"; import type { Pokemon } from "#field/pokemon"; -import type { ModifierTypeOption } from "#modifiers/modifier-type"; -import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; +import type { RewardOption, TrainerItemReward } from "#items/reward"; +import { generatePlayerRewardOptions, generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils"; +import { isTmReward } from "#items/reward-utils"; import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { @@ -89,18 +91,18 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. // Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER const tier = globalScene.currentBattle.waveIndex > 160 - ? RewardTier.MASTER + ? RarityTier.MASTER : globalScene.currentBattle.waveIndex > 120 - ? RewardTier.ROGUE + ? RarityTier.ROGUE : globalScene.currentBattle.waveIndex > 40 - ? RewardTier.ULTRA - : RewardTier.GREAT; - regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0); - let item: ModifierTypeOption | null = null; - // TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward - while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") { - item = getPlayerModifierTypeOptions(1, globalScene.getPlayerParty(), [], { - guaranteedModifierTiers: [tier], + ? RarityTier.ULTRA + : RarityTier.GREAT; + generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), globalScene.getPlayerParty(), 0); + let item: RewardOption | null = null; + // TMs and Candy Jar excluded from possible allRewards as they're too swingy in value for a singular item reward + while (!item || isTmReward(item.type) || (item.type as TrainerItemReward).itemId === TrainerItemId.CANDY_JAR) { + item = generatePlayerRewardOptions(1, globalScene.getPlayerParty(), [], { + guaranteedRarityTiers: [tier], allowLuckUpgrades: false, })[0]; } @@ -151,9 +153,9 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. async () => { // Pick battle // Pokemon will randomly boost 1 stat by 2 stages - const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; + const item = globalScene.currentBattle.mysteryEncounter!.misc as RewardOption; setEncounterRewards({ - guaranteedModifierTypeOptions: [item], + guaranteedRewardOptions: [item], fillRemaining: false, }); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); @@ -175,9 +177,9 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. .withOptionPhase(async () => { // Pick steal const encounter = globalScene.currentBattle.mysteryEncounter!; - const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; + const item = globalScene.currentBattle.mysteryEncounter!.misc as RewardOption; setEncounterRewards({ - guaranteedModifierTypeOptions: [item], + guaranteedRewardOptions: [item], fillRemaining: false, }); @@ -199,7 +201,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 6aee41bc91b..8fb63b16085 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -1,7 +1,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers"; import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; import { FieldPosition } from "#enums/field-position"; @@ -160,7 +160,7 @@ export const FunAndGamesEncounter: MysteryEncounter = MysteryEncounterBuilder.wi ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp await transitionMysteryEncounterIntroVisuals(true, true); leaveEncounterWithoutBattle(true); return true; @@ -281,21 +281,21 @@ function handleNextTurn() { if (healthRatio < 0.03) { // Grand prize setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.MULTI_LENS], + guaranteedRewardFuncs: [allRewards.MULTI_LENS], fillRemaining: false, }); resultMessageKey = `${namespace}:best_result`; } else if (healthRatio < 0.15) { // 2nd prize setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.SCOPE_LENS], + guaranteedRewardFuncs: [allRewards.SCOPE_LENS], fillRemaining: false, }); resultMessageKey = `${namespace}:great_result`; } else if (healthRatio < 0.33) { // 3rd prize setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.WIDE_LENS], + guaranteedRewardFuncs: [allRewards.WIDE_LENS], fillRemaining: false, }); resultMessageKey = `${namespace}:good_result`; 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 0cccba42a49..c296bd36f73 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -8,22 +8,23 @@ import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; import type { PokemonSpecies } from "#data/pokemon-species"; import { getTypeRgb } from "#data/type"; import { HeldItemCategoryId, type HeldItemId, isItemInCategory } from "#enums/held-item-id"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { PokeballType } from "#enums/pokeball"; -import { RewardTier } from "#enums/reward-tier"; +import { RewardPoolType } from "#enums/reward-pool-type"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerType } from "#enums/trainer-type"; import { doShinySparkleAnim } from "#field/anims"; import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import { EnemyPokemon } from "#field/pokemon"; -import { getHeldItemTier } from "#items/held-item-tiers"; +import { getHeldItemTier } from "#items/held-item-default-tiers"; +import type { RewardOption } from "#items/reward"; +import { generatePlayerRewardOptions, generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils"; +import { isTmReward } from "#items/reward-utils"; import { TrainerItemEffect } from "#items/trainer-item"; -import type { ModifierTypeOption } from "#modifiers/modifier-type"; -import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; import { PokemonMove } from "#moves/pokemon-move"; import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { @@ -413,26 +414,26 @@ 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 = getHeldItemTier(heldItemId) ?? RewardTier.GREAT; + let tier = getHeldItemTier(heldItemId) ?? RarityTier.GREAT; // Increment tier by 1 - if (tier < RewardTier.MASTER) { + if (tier < RarityTier.MASTER) { tier++; } - regenerateModifierPoolThresholds(party, ModifierPoolType.PLAYER, 0); - let item: ModifierTypeOption | null = null; - // TMs excluded from possible rewards - while (!item || item.type.id.includes("TM_")) { - item = getPlayerModifierTypeOptions(1, party, [], { - guaranteedModifierTiers: [tier], + generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), party, 0); + let item: RewardOption | null = null; + // TMs excluded from possible allRewards + while (!item || isTmReward(item.type)) { + item = generatePlayerRewardOptions(1, party, [], { + guaranteedRarityTiers: [tier], allowLuckUpgrades: false, })[0]; } encounter.setDialogueToken("itemName", item.type.name); setEncounterRewards({ - guaranteedModifierTypeOptions: [item], + guaranteedRewardOptions: [item], fillRemaining: false, }); @@ -458,7 +459,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index d8dddf5d8b5..ec26298bf93 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -1,10 +1,10 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { initBattleWithEnemyConfig, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; @@ -147,7 +147,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], + guaranteedRewardFuncs: [allRewards.TM_COMMON, allRewards.TM_GREAT, allRewards.MEMORY_MUSHROOM], fillRemaining: true, }); @@ -175,7 +175,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.ULTRA, RewardTier.ULTRA, RewardTier.GREAT, RewardTier.GREAT], + guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT], fillRemaining: true, }); @@ -206,7 +206,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter encounter.expMultiplier = 0.9; setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ROGUE, RewardTier.ULTRA, RewardTier.GREAT], + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.GREAT], fillRemaining: true, }); diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index a205b67dc00..ae526b6b4a3 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -4,7 +4,7 @@ import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; @@ -141,25 +141,25 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) { // Choose between 2 COMMON / 2 GREAT tier items (20%) setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.COMMON, RewardTier.COMMON, RewardTier.GREAT, RewardTier.GREAT], + guaranteedRarityTiers: [RarityTier.COMMON, RarityTier.COMMON, RarityTier.GREAT, RarityTier.GREAT], }); - // Display result message then proceed to rewards + // Display result message then proceed to allRewards queueEncounterMessage(`${namespace}:option.1.normal`); leaveEncounterWithoutBattle(); } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) { // Choose between 3 ULTRA tier items (30%) setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.ULTRA, RewardTier.ULTRA, RewardTier.ULTRA], + guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA], }); - // Display result message then proceed to rewards + // Display result message then proceed to allRewards queueEncounterMessage(`${namespace}:option.1.good`); leaveEncounterWithoutBattle(); } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) { // Choose between 2 ROGUE tier items (10%) setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ROGUE], + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE], }); - // Display result message then proceed to rewards + // Display result message then proceed to allRewards queueEncounterMessage(`${namespace}:option.1.great`); leaveEncounterWithoutBattle(); } else if ( @@ -168,9 +168,9 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde ) { // Choose 1 MASTER tier item (5%) setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.MASTER], + guaranteedRarityTiers: [RarityTier.MASTER], }); - // Display result message then proceed to rewards + // Display result message then proceed to allRewards queueEncounterMessage(`${namespace}:option.1.amazing`); leaveEncounterWithoutBattle(); } else { @@ -208,7 +208,7 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 7da0adcb106..fe8254804ee 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -125,7 +125,7 @@ export const SafariZoneEncounter: MysteryEncounter = MysteryEncounterBuilder.wit ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 7d052eaadd7..4c5a7e7a222 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -227,7 +227,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 04a16c9178e..e3d55b6724b 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { CustomPokemonData } from "#data/pokemon-data"; import { AiType } from "#enums/ai-type"; import { BattlerIndex } from "#enums/battler-index"; @@ -116,7 +116,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil // Pick battle const encounter = globalScene.currentBattle.mysteryEncounter!; setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], + guaranteedRewardFuncs: [allRewards.LEFTOVERS], fillRemaining: true, }); encounter.startOfBattleEffects.push({ @@ -163,7 +163,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil // Steal the Snorlax's Leftovers const instance = globalScene.currentBattle.mysteryEncounter!; setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], + guaranteedRewardFuncs: [allRewards.LEFTOVERS], fillRemaining: false, }); // Snorlax exp to Pokemon that did the stealing diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index b547064fd66..6ee1e198b39 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -1,7 +1,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -13,11 +13,10 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { getBiomeKey } from "#field/arena"; import type { Pokemon } from "#field/pokemon"; import { EnemyPokemon } from "#field/pokemon"; -import { getPartyLuckValue } from "#modifiers/modifier-type"; +import { generateRewardOption } from "#items/reward-utils"; import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { - generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, @@ -34,6 +33,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou import { MoneyRequirement, WaveModulusRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { PokemonData } from "#system/pokemon-data"; import { randSeedInt } from "#utils/common"; +import { getPartyLuckValue } from "#utils/party"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/teleportingHijinks"; @@ -173,10 +173,10 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui ], }; - const magnet = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [PokemonType.STEEL])!; - const metalCoat = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [PokemonType.ELECTRIC])!; + const magnet = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.STEEL])!; + const metalCoat = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.ELECTRIC])!; setEncounterRewards({ - guaranteedModifierTypeOptions: [magnet, metalCoat], + guaranteedRewardOptions: [magnet, metalCoat], fillRemaining: true, }); await transitionMysteryEncounterIntroVisuals(true, true); diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index cf5cf86eed1..4eb63d94af0 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { speciesStarterCosts } from "#balance/starters"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import type { IEggOptions } from "#data/egg"; import { getPokeballTintColor } from "#data/pokeball"; import { BiomeId } from "#enums/biome-id"; @@ -294,7 +294,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs); setEncounterRewards( { - guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], + guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], fillRemaining: true, }, eggOptions, @@ -304,7 +304,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount // Remove all Pokemon from the party except the chosen Pokemon removePokemonFromPartyAndStoreHeldItems(encounter, pokemon1); - // Configure outro dialogue for egg rewards + // Configure outro dialogue for egg allRewards encounter.dialogue.outro = [ { speaker: trainerNameKey, @@ -353,7 +353,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs); setEncounterRewards( { - guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], + guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], fillRemaining: true, }, eggOptions, @@ -363,7 +363,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount // Remove all Pokemon from the party except the chosen Pokemon removePokemonFromPartyAndStoreHeldItems(encounter, pokemon2); - // Configure outro dialogue for egg rewards + // Configure outro dialogue for egg allRewards encounter.dialogue.outro = [ { speaker: trainerNameKey, @@ -412,7 +412,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs); setEncounterRewards( { - guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], + guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], fillRemaining: true, }, eggOptions, @@ -422,7 +422,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount // Remove all Pokemon from the party except the chosen Pokemon removePokemonFromPartyAndStoreHeldItems(encounter, pokemon3); - // Configure outro dialogue for egg rewards + // Configure outro dialogue for egg allRewards encounter.dialogue.outro = [ { speaker: trainerNameKey, @@ -640,7 +640,7 @@ function onGameOver() { const chosenPokemon = encounter.misc.chosenPokemon; chosenPokemon.friendship = 0; - // Clear all rewards that would have been earned + // Clear all allRewards that would have been earned encounter.doEncounterRewards = undefined; // Set flag that encounter was failed 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 91662993a51..539e1a3670b 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -238,7 +238,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index f159873b89a..fd1e6268132 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -1,6 +1,6 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { CustomPokemonData } from "#data/pokemon-data"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -193,7 +193,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder // Pick battle const encounter = globalScene.currentBattle.mysteryEncounter!; setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], + guaranteedRewardFuncs: [allRewards.SOUL_DEW], fillRemaining: true, }); encounter.startOfBattleEffects.push( diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index 2b85cfde356..8c2e422aa35 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -1,7 +1,7 @@ import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { SpeciesFormChangeAbilityTrigger } from "#data/form-change-triggers"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -11,13 +11,13 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Nature } from "#enums/nature"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; +import { generateRewardOption } from "#items/reward-utils"; import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import { - generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, @@ -117,7 +117,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter ], }, async () => { - // Spawn 5 trainer battles back to back with Macho Brace in rewards + // Spawn 5 trainer battles back to back with Macho Brace in allRewards globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = async () => { await endTrainerBattleAndShowDialogue(); }; @@ -140,7 +140,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter // Refuse the challenge, they full heal the party and give the player a Rarer Candy globalScene.phaseManager.unshiftNew("PartyHealPhase", true); setEncounterRewards({ - guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], + guaranteedRewardFuncs: [allRewards.RARER_CANDY], fillRemaining: false, }); leaveEncounterWithoutBattle(); @@ -156,17 +156,17 @@ async function spawnNextTrainerOrEndEncounter() { await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`); // Give 10x Voucher - const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier(); - globalScene.addModifier(newModifier); + const reward = allRewards.VOUCHER_PREMIUM(); + globalScene.applyReward(reward, {}); globalScene.playSound("item_fanfare"); - await showEncounterText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name })); + await showEncounterText(i18next.t("battle:rewardGain", { modifierName: reward.name })); await showEncounterDialogue(`${namespace}:victory_2`, `${namespace}:speaker`); - globalScene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in - const machoBrace = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!; - machoBrace.type.tier = RewardTier.MASTER; + globalScene.ui.clearText(); // Clears "Winstrate" title from screen as allRewards get animated in + const machoBrace = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_MACHO_BRACE)!; + machoBrace.type.tier = RarityTier.MASTER; setEncounterRewards({ - guaranteedModifierTypeOptions: [machoBrace], + guaranteedRewardOptions: [machoBrace], fillRemaining: false, }); encounter.doContinueEncounter = undefined; diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 4a0db1614e0..af3766e08ae 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -346,7 +346,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde ], }, async () => { - // Leave encounter with no rewards or exp + // Leave encounter with no allRewards or exp leaveEncounterWithoutBattle(true); return true; }, diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index e752ba09ba7..c0b87645219 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -8,7 +8,7 @@ import { MoveUseMode } from "#enums/move-use-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerItemId } from "#enums/trainer-item-id"; import { assignItemToFirstFreePokemon } from "#items/item-utility"; @@ -167,7 +167,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde const encounter = globalScene.currentBattle.mysteryEncounter!; setEncounterRewards({ - guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ROGUE, RewardTier.ULTRA, RewardTier.GREAT], + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.GREAT], fillRemaining: true, }); encounter.startOfBattleEffects.push( diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 8f43abe8fa9..9876b47696c 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { allSpecies, modifierTypes } from "#data/data-lists"; +import { allRewards, allSpecies } from "#data/data-lists"; import { getLevelTotalExp } from "#data/exp"; import type { PokemonSpecies } from "#data/pokemon-species"; import { Challenges } from "#enums/challenges"; @@ -11,7 +11,7 @@ import { Nature } from "#enums/nature"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { PlayerGender } from "#enums/player-gender"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import type { PlayerPokemon, Pokemon } from "#field/pokemon"; @@ -218,12 +218,12 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit await doNewTeamPostProcess(transformations); setEncounterRewards({ - guaranteedModifierTypeFuncs: [ - modifierTypes.MEMORY_MUSHROOM, - modifierTypes.ROGUE_BALL, - modifierTypes.MINT, - modifierTypes.MINT, - modifierTypes.MINT, + guaranteedRewardFuncs: [ + allRewards.MEMORY_MUSHROOM, + allRewards.ROGUE_BALL, + allRewards.MINT, + allRewards.MINT, + allRewards.MINT, ], fillRemaining: false, }); @@ -242,7 +242,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit ], }, async () => { - // Battle your "future" team for some item rewards + // Battle your "future" team for some item allRewards const transformations: PokemonTransformation[] = globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations; @@ -293,7 +293,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit }; const onBeforeRewards = () => { - // Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently) + // Before battle allRewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently) // One random pokemon will get its passive unlocked const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); if (passiveDisabledPokemon?.length > 0) { @@ -306,13 +306,13 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit setEncounterRewards( { - guaranteedModifierTiers: [ - RewardTier.ROGUE, - RewardTier.ROGUE, - RewardTier.ULTRA, - RewardTier.ULTRA, - RewardTier.GREAT, - RewardTier.GREAT, + guaranteedRarityTiers: [ + RarityTier.ROGUE, + RarityTier.ROGUE, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.GREAT, + RarityTier.GREAT, ], fillRemaining: false, }, diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index a2ca2b20ce7..a3cd81db201 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -173,11 +173,11 @@ export class MysteryEncounter implements IMysteryEncounter { onVisualsStart?: () => boolean; /** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */ onTurnStart?: () => boolean; - /** Event prior to any rewards logic in {@linkcode MysteryEncounterRewardsPhase} */ + /** Event prior to any allRewards logic in {@linkcode MysteryEncounterRewardsPhase} */ onRewards?: () => Promise; - /** Will provide the player party EXP before rewards are displayed for that wave */ + /** Will provide the player party EXP before allRewards are displayed for that wave */ doEncounterExp?: () => boolean; - /** Will provide the player a rewards shop for that wave */ + /** Will provide the player a allRewards shop for that wave */ doEncounterRewards?: () => boolean; /** Will execute callback during VictoryPhase of a continuousEncounter */ doContinueEncounter?: () => Promise; @@ -237,10 +237,10 @@ export class MysteryEncounter implements IMysteryEncounter { encounterMode: MysteryEncounterMode; /** * Flag for checking if it's the first time a shop is being shown for an encounter. - * Defaults to true so that the first shop does not override the specified rewards. + * Defaults to true so that the first shop does not override the specified allRewards. * Will be set to false after a shop is shown (so can't reroll same rarity items for free) */ - lockEncounterRewardTiers: boolean; + lockEncounterRarityTiers: boolean; /** * Will be set automatically, indicates special moves in startOfBattleEffects are complete (so will not repeat) */ @@ -295,7 +295,7 @@ export class MysteryEncounter implements IMysteryEncounter { // Reset any dirty flags or encounter data this.startOfBattleEffectsComplete = false; - this.lockEncounterRewardTiers = true; + this.lockEncounterRarityTiers = true; this.dialogueTokens = {}; this.enemyPartyConfigs = []; this.startOfBattleEffects = []; @@ -561,7 +561,7 @@ export class MysteryEncounterBuilder implements Partial { continuousEncounter = false; catchAllowed = false; fleeAllowed = true; - lockEncounterRewardTiers = false; + lockEncounterRarityTiers = false; startOfBattleEffectsComplete = false; hasBattleAnimationsWithoutTargets = false; skipEnemyBattleTurns = false; @@ -928,13 +928,13 @@ export class MysteryEncounterBuilder implements Partial { } /** - * Can set custom encounter rewards via this callback function - * If rewards are always deterministic for an encounter, this is a good way to set them + * Can set custom encounter allRewards via this callback function + * If allRewards are always deterministic for an encounter, this is a good way to set them * - * NOTE: If rewards are dependent on options selected, runtime data, etc., + * NOTE: If allRewards are dependent on options selected, runtime data, etc., * It may be better to programmatically set doEncounterRewards elsewhere. - * There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set rewards - * @param doEncounterRewards Synchronous callback function to perform during rewards phase of the encounter + * There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set allRewards + * @param doEncounterRewards Synchronous callback function to perform during allRewards phase of the encounter * @returns */ withRewards(doEncounterRewards: () => boolean): this & Required> { @@ -945,10 +945,10 @@ export class MysteryEncounterBuilder implements Partial { * Can set custom encounter exp via this callback function * If exp always deterministic for an encounter, this is a good way to set them * - * NOTE: If rewards are dependent on options selected, runtime data, etc., + * NOTE: If allRewards are dependent on options selected, runtime data, etc., * It may be better to programmatically set doEncounterExp elsewhere. - * There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set rewards - * @param doEncounterExp Synchronous callback function to perform during rewards phase of the encounter + * There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set allRewards + * @param doEncounterExp Synchronous callback function to perform during allRewards phase of the encounter * @returns */ withExp(doEncounterExp: () => boolean): this & Required> { diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 4fde6531ad3..48968a2ff82 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -5,7 +5,6 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { BiomePoolTier, biomeLinks } from "#balance/biomes"; import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; -import { modifierTypes } from "#data/data-lists"; import type { IEggOptions } from "#data/egg"; import { Egg } from "#data/egg"; import type { Gender } from "#data/gender"; @@ -17,7 +16,6 @@ import type { AiType } from "#enums/ai-type"; import type { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; import { FieldPosition } from "#enums/field-position"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; import type { MoveId } from "#enums/move-id"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import type { Nature } from "#enums/nature"; @@ -31,8 +29,7 @@ import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import { EnemyPokemon } from "#field/pokemon"; import { Trainer } from "#field/trainer"; import type { HeldItemConfiguration } from "#items/held-item-data-types"; -import type { CustomModifierSettings, ModifierType } from "#modifiers/modifier-type"; -import { getPartyLuckValue, ModifierTypeGenerator, ModifierTypeOption } from "#modifiers/modifier-type"; +import type { CustomRewardSettings } from "#items/reward-pool-utils"; import { PokemonMove } from "#moves/pokemon-move"; import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option"; @@ -44,6 +41,7 @@ import type { OptionSelectConfig, OptionSelectItem } from "#ui/abstact-option-se import type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler"; import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common"; +import { getPartyLuckValue } from "#utils/party"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import i18next from "i18next"; @@ -474,45 +472,6 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes } } -/** - * Converts modifier bullshit to an actual item - * @param modifier - * @param pregenArgs Can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. - */ -export function generateModifierType(modifier: () => ModifierType, pregenArgs?: any[]): ModifierType | null { - const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier); - if (!modifierId) { - return null; - } - - let result: ModifierType = modifierTypes[modifierId](); - - // Populates item id and tier (order matters) - result = result - .withIdFromFunc(modifierTypes[modifierId]) - .withTierFromPool(ModifierPoolType.PLAYER, globalScene.getPlayerParty()); - - return result instanceof ModifierTypeGenerator - ? result.generateType(globalScene.getPlayerParty(), pregenArgs) - : result; -} - -/** - * Converts modifier bullshit to an actual item - * @param modifier - * @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. - */ -export function generateModifierTypeOption( - modifier: () => ModifierType, - pregenArgs?: any[], -): ModifierTypeOption | null { - const result = generateModifierType(modifier, pregenArgs); - if (result) { - return new ModifierTypeOption(result, 0); - } - return result; -} - /** * This function is intended for use inside onPreOptionPhase() of an encounter option * @param onPokemonSelected - Any logic that needs to be performed when Pokemon is chosen @@ -727,12 +686,12 @@ export function selectOptionThenPokemon( /** * Will initialize reward phases to follow the mystery encounter * Can have shop displayed or skipped - * @param customShopRewards - adds a shop phase with the specified rewards / reward tiers + * @param customShopRewards - adds a shop phase with the specified allRewards / reward tiers * @param eggRewards * @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before {@linkcode MysteryEncounterRewardsPhase}) */ export function setEncounterRewards( - customShopRewards?: CustomModifierSettings, + customShopRewards?: CustomRewardSettings, eggRewards?: IEggOptions[], preRewardsCallback?: Function, ) { @@ -809,8 +768,8 @@ export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSet /** * Can be used to exit an encounter without any battles or followup - * Will skip any shops and rewards, and queue the next encounter phase as normal - * @param addHealPhase - when true, will add a shop phase to end of encounter with 0 rewards but healing items are available + * Will skip any shops and allRewards, and queue the next encounter phase as normal + * @param addHealPhase - when true, will add a shop phase to end of encounter with 0 allRewards but healing items are available * @param encounterMode - Can set custom encounter mode if necessary (may be required for forcing Pokemon to return before next phase) */ export function leaveEncounterWithoutBattle( diff --git a/src/data/trainers/fixed-battle-configs.ts b/src/data/trainers/fixed-battle-configs.ts index bb6d591654b..903743b38f5 100644 --- a/src/data/trainers/fixed-battle-configs.ts +++ b/src/data/trainers/fixed-battle-configs.ts @@ -4,8 +4,8 @@ import { globalScene } from "#app/global-scene"; import { randSeedInt } from "#app/utils/common"; import { BattleType } from "#enums/battle-type"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; -import { ModifierTier } from "#enums/modifier-tier"; import { PlayerGender } from "#enums/player-gender"; +import { RarityTier } from "#enums/reward-tier"; import { TrainerType } from "#enums/trainer-type"; import { TrainerVariant } from "#enums/trainer-variant"; @@ -45,8 +45,8 @@ export const classicFixedBattles: FixedBattleConfigs = { globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, ), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], + .setCustomRewards({ + guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT], allowLuckUpgrades: false, }), [ClassicFixedBossWaves.EVIL_GRUNT_1]: new FixedBattleConfig() @@ -77,8 +77,8 @@ export const classicFixedBattles: FixedBattleConfigs = { globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, ), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], + .setCustomRewards({ + guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT], allowLuckUpgrades: false, }), [ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig() @@ -150,8 +150,8 @@ export const classicFixedBattles: FixedBattleConfigs = { globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, ), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], + .setCustomRewards({ + guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA], allowLuckUpgrades: false, }), [ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig() @@ -212,14 +212,8 @@ export const classicFixedBattles: FixedBattleConfigs = { TrainerType.PENNY, ]), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ], + .setCustomRewards({ + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA], allowLuckUpgrades: false, }), [ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig() @@ -231,14 +225,8 @@ export const classicFixedBattles: FixedBattleConfigs = { globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, ), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ], + .setCustomRewards({ + guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.ULTRA], allowLuckUpgrades: false, }), [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig() @@ -258,14 +246,14 @@ export const classicFixedBattles: FixedBattleConfigs = { TrainerType.PENNY_2, ]), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ModifierTier.ULTRA, + .setCustomRewards({ + guaranteedRarityTiers: [ + RarityTier.ROGUE, + RarityTier.ROGUE, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.ULTRA, ], allowLuckUpgrades: false, }), @@ -362,14 +350,14 @@ export const classicFixedBattles: FixedBattleConfigs = { globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, ), ) - .setCustomModifierRewards({ - guaranteedModifierTiers: [ - ModifierTier.ROGUE, - ModifierTier.ROGUE, - ModifierTier.ULTRA, - ModifierTier.ULTRA, - ModifierTier.GREAT, - ModifierTier.GREAT, + .setCustomRewards({ + guaranteedRarityTiers: [ + RarityTier.ROGUE, + RarityTier.ROGUE, + RarityTier.ULTRA, + RarityTier.ULTRA, + RarityTier.GREAT, + RarityTier.GREAT, ], allowLuckUpgrades: false, }), diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 3e1ad67918a..2cc5125c626 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -3,7 +3,7 @@ import { globalScene } from "#app/global-scene"; import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions"; import { signatureSpecies } from "#balance/signature-species"; import { tmSpecies } from "#balance/tms"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { doubleBattleDialogue } from "#data/double-battle-dialogue"; import { Gender } from "#data/gender"; import type { PokemonSpecies, PokemonSpeciesFilter } from "#data/pokemon-species"; @@ -31,7 +31,7 @@ import { TrainerPartyTemplate, trainerPartyTemplates, } from "#trainers/trainer-party-template"; -import type { ModifierTypeFunc } from "#types/modifier-types"; +import type { RewardFunc } from "#types/rewards"; import type { GenAIFunc, GenTrainerItemsFunc, @@ -113,9 +113,9 @@ export class TrainerConfig { public femaleEncounterBgm: string; public doubleEncounterBgm: string; public victoryBgm: string; - public genModifiersFunc: GenTrainerItemsFunc; + public genTrainerItemsFunc: GenTrainerItemsFunc; public genAIFuncs: GenAIFunc[] = []; - public modifierRewardFuncs: ModifierTypeFunc[] = []; + public rewardFuncs: RewardFunc[] = []; public partyTemplates: TrainerPartyTemplate[]; public partyTemplateFunc: PartyTemplateFunc; public partyMemberFuncs: PartyMemberFuncs = {}; @@ -465,8 +465,8 @@ export class TrainerConfig { return this; } - setGenModifiersFunc(genModifiersFunc: GenTrainerItemsFunc): TrainerConfig { - this.genModifiersFunc = genModifiersFunc; + setGenTrainerItemsFunc(genTrainerItemsFunc: GenTrainerItemsFunc): TrainerConfig { + this.genTrainerItemsFunc = genTrainerItemsFunc; return this; } @@ -476,7 +476,7 @@ export class TrainerConfig { * @param slot Optional, a specified slot that should be terastallized. Wraps to match party size (-1 will get the last slot and so on). * @returns this */ - setRandomTeraModifiers(count: () => number, slot?: number): TrainerConfig { + setRandomTeraType(count: () => number, slot?: number): TrainerConfig { this.genAIFuncs.push((party: EnemyPokemon[]) => { const shedinjaCanTera = !this.hasSpecialtyType() || this.specialtyType === PokemonType.BUG; // Better to check one time than 6 const partyMemberIndexes = new Array(party.length) @@ -507,23 +507,11 @@ export class TrainerConfig { return this; } - // function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: Type[]): PersistentModifier[] { - // const ret: PersistentModifier[] = []; - // const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i); - // for (let t = 0; t < Math.min(count, party.length); t++) { - // const randomIndex = Utils.randSeedItem(partyMemberIndexes); - // partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); - // ret.push(modifierTypes.TERA_SHARD().generateType([], [ Utils.randSeedItem(types ? types : party[randomIndex].getTypes()) ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct? - // } - // return ret; - // } - - setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig { - this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => { - const modifierTypeFunc = func(); - const modifierType = modifierTypeFunc(); - modifierType.withIdFromFunc(modifierTypeFunc); - return modifierType; + setRewardFuncs(...rewardFuncs: (() => RewardFunc)[]): TrainerConfig { + this.rewardFuncs = rewardFuncs.map(func => () => { + const rewardFunc = func(); + const reward = rewardFunc(); + return reward; }); return this; } @@ -689,7 +677,7 @@ export class TrainerConfig { this.setHasVoucher(true); this.setBattleBgm("battle_unova_gym"); this.setVictoryBgm("victory_gym"); - this.setRandomTeraModifiers( + this.setRandomTeraType( () => (ignoreMinTeraWave || globalScene.currentBattle.waveIndex >= GYM_LEADER_TERA_WAVE ? 1 : 0), teraSlot, ); @@ -750,7 +738,7 @@ export class TrainerConfig { this.setHasVoucher(true); this.setBattleBgm("battle_unova_elite"); this.setVictoryBgm("victory_gym"); - this.setRandomTeraModifiers(() => 1, teraSlot); + this.setRandomTeraType(() => 1, teraSlot); return this; } @@ -927,11 +915,11 @@ export class TrainerConfig { clone = this.battleBgm ? clone.setBattleBgm(this.battleBgm) : clone; clone = this.encounterBgm ? clone.setEncounterBgm(this.encounterBgm) : clone; clone = this.victoryBgm ? clone.setVictoryBgm(this.victoryBgm) : clone; - clone = this.genModifiersFunc ? clone.setGenModifiersFunc(this.genModifiersFunc) : clone; + clone = this.genTrainerItemsFunc ? clone.setGenTrainerItemsFunc(this.genTrainerItemsFunc) : clone; - if (this.modifierRewardFuncs) { + if (this.rewardFuncs) { // Clones array instead of passing ref - clone.modifierRewardFuncs = this.modifierRewardFuncs.slice(0); + clone.rewardFuncs = this.rewardFuncs.slice(0); } if (this.partyTemplates) { @@ -4443,9 +4431,9 @@ export const trainerConfigs: TrainerConfigs = { .setBattleBgm("battle_rival") .setMixedBattleBgm("battle_rival") .setPartyTemplates(trainerPartyTemplates.RIVAL) - .setModifierRewardFuncs( - () => modifierTypes.SUPER_EXP_CHARM, - () => modifierTypes.EXP_SHARE, + .setRewardFuncs( + () => allRewards.SUPER_EXP_CHARM, + () => allRewards.EXP_SHARE, ) .setPartyMemberFunc( 0, @@ -4513,7 +4501,7 @@ export const trainerConfigs: TrainerConfigs = { .setBattleBgm("battle_rival") .setMixedBattleBgm("battle_rival") .setPartyTemplates(trainerPartyTemplates.RIVAL_2) - .setModifierRewardFuncs(() => modifierTypes.EXP_SHARE) + .setRewardFuncs(() => allRewards.EXP_SHARE) .setPartyMemberFunc( 0, getRandomPartyMemberFunc( @@ -4666,7 +4654,7 @@ export const trainerConfigs: TrainerConfigs = { .setBattleBgm("battle_rival_2") .setMixedBattleBgm("battle_rival_2") .setPartyTemplates(trainerPartyTemplates.RIVAL_4) - .setModifierRewardFuncs(() => modifierTypes.TERA_ORB) + .setRewardFuncs(() => allRewards.TERA_ORB) .setPartyMemberFunc( 0, getRandomPartyMemberFunc( diff --git a/src/enums/held-item-effect.ts b/src/enums/held-item-effect.ts new file mode 100644 index 00000000000..209a74b921e --- /dev/null +++ b/src/enums/held-item-effect.ts @@ -0,0 +1,37 @@ +import type { EnumValues } from "#types/enum-types"; + +/** + * Enum representing the various "classes" of item effects that can be applied. + */ +export const HeldItemEffect = { + ATTACK_TYPE_BOOST: 1, + TURN_END_HEAL: 2, + HIT_HEAL: 3, + RESET_NEGATIVE_STAT_STAGE: 4, + EXP_BOOSTER: 5, + // Should we actually distinguish different berry effects? + BERRY: 6, + BASE_STAT_BOOSTER: 7, + INSTANT_REVIVE: 8, + STAT_BOOST: 9, + CRIT_BOOST: 10, + TURN_END_STATUS: 11, + SURVIVE_CHANCE: 12, + BYPASS_SPEED_CHANCE: 13, + FLINCH_CHANCE: 14, + FIELD_EFFECT: 15, + FRIENDSHIP_BOOSTER: 16, + NATURE_WEIGHT_BOOSTER: 17, + ACCURACY_BOOSTER: 18, + MULTI_HIT: 19, + DAMAGE_MONEY_REWARD: 20, + BATON: 21, + TURN_END_ITEM_STEAL: 22, + CONTACT_ITEM_STEAL_CHANCE: 23, + EVO_TRACKER: 40, + BASE_STAT_TOTAL: 50, + BASE_STAT_FLAT: 51, + INCREMENTING_STAT: 52, +} as const; + +export type HeldItemEffect = EnumValues; diff --git a/src/enums/held-item-id.ts b/src/enums/held-item-id.ts index f714f332d2a..c112ab6f37a 100644 --- a/src/enums/held-item-id.ts +++ b/src/enums/held-item-id.ts @@ -1,3 +1,5 @@ +import type { EnumValues } from "#types/enum-types"; + // TODO: make category the lower 2 bytes export const HeldItemId = { NONE: 0x0000, @@ -92,19 +94,24 @@ export const HeldItemId = { GIMMIGHOUL_EVO_TRACKER: 0x0A01, } as const; -export type HeldItemId = (typeof HeldItemId)[keyof typeof HeldItemId]; +export type HeldItemId = EnumValues; + +type HeldItemNameMap = { + [k in HeldItemName as (typeof HeldItemId)[k]]: k +} type HeldItemName = keyof typeof HeldItemId; -type HeldItemValue = typeof HeldItemId[HeldItemName]; // equivalent to `HeldItemId` -// Use a type-safe reducer to force number keys and values -export const HeldItemNames: Record = Object.entries(HeldItemId).reduce( +/** `const object` mapping all held item IDs to their respective names. */ +// TODO: This stores names as UPPER_SNAKE_CASE, but the locales are in PascalCase... +export const HeldItemNames = Object.freeze(Object.entries(HeldItemId).reduce( + // Use a type-safe reducer to force number keys and values (acc, [key, value]) => { - acc[value as HeldItemValue] = key as HeldItemName; + acc[value] = key; return acc; }, - {} as Record -); + {} +)) as HeldItemNameMap; export const HeldItemCategoryId = { NONE: 0x0000, @@ -120,7 +127,7 @@ export const HeldItemCategoryId = { EVO_TRACKER: 0x0A00, } as const; -export type HeldItemCategoryId = (typeof HeldItemCategoryId)[keyof typeof HeldItemCategoryId]; +export type HeldItemCategoryId = EnumValues; const ITEM_CATEGORY_MASK = 0xFF00 diff --git a/src/enums/reward-id.ts b/src/enums/reward-id.ts new file mode 100644 index 00000000000..97a6a1f5747 --- /dev/null +++ b/src/enums/reward-id.ts @@ -0,0 +1,99 @@ +import type { EnumValues } from "#types/enum-types"; + +export const RewardId = { + NONE: 0x0000, + + POKEBALL: 0x2001, + GREAT_BALL: 0x2002, + ULTRA_BALL: 0x2003, + ROGUE_BALL: 0x2004, + MASTER_BALL: 0x2005, + + VOUCHER: 0x2101, + VOUCHER_PLUS: 0x2102, + VOUCHER_PREMIUM: 0x2103, + + NUGGET: 0x2201, + BIG_NUGGET: 0x2202, + RELIC_GOLD: 0x2203, + + RARE_CANDY: 0x2301, + RARER_CANDY: 0x2302, + + EVOLUTION_ITEM: 0x2401, + RARE_EVOLUTION_ITEM: 0x2402, + + POTION: 0x2501, + SUPER_POTION: 0x2502, + HYPER_POTION: 0x2503, + MAX_POTION: 0x2504, + FULL_HEAL: 0x2505, + FULL_RESTORE: 0x2506, + + REVIVE: 0x2601, + MAX_REVIVE: 0x2602, + SACRED_ASH: 0x2603, + + ETHER: 0x2701, + MAX_ETHER: 0x2702, + + ELIXIR: 0x2801, + MAX_ELIXIR: 0x2802, + + PP_UP: 0x2901, + PP_MAX: 0x2902, + + TM_COMMON: 0x2A01, + TM_GREAT: 0x2A02, + TM_ULTRA: 0x2A03, + + MINT: 0x2B01, + TERA_SHARD: 0x2B02, + MEMORY_MUSHROOM: 0x2B03, + DNA_SPLICERS: 0x2B04, + + HELD_ITEM: 0x2C01, + SPECIES_STAT_BOOSTER: 0x2C02, + RARE_SPECIES_STAT_BOOSTER: 0x2C03, + BASE_STAT_BOOSTER: 0x2C04, + ATTACK_TYPE_BOOSTER: 0x2C05, + BERRY: 0x2C06, + + TRAINER_ITEM: 0x2D01, + TEMP_STAT_STAGE_BOOSTER: 0x2D02, + LURE: 0x2D03, + SUPER_LURE: 0x2D04, + MAX_LURE: 0x2D05, + + FORM_CHANGE_ITEM: 0x2E01, + RARE_FORM_CHANGE_ITEM: 0x2E02, +} as const; + +export type RewardId = EnumValues; + +export const RewardCategoryId = { + NONE: 0x0000, + POKEBALL: 0x0100, + VOUCHER: 0x0200, + MONEY: 0x0300, + CANDY: 0x0400, + EVOLUTION_ITEM: 0x0500, + HEALING: 0x0600, + REVIVE: 0x0700, + ETHER: 0x0800, + ELIXIR: 0x0900, + PP_UP: 0x0A00, + TM: 0x0B00, + OTHER: 0x0C00, + HELD_ITEM: 0x0D00, + TRAINER_ITEM: 0x0E00, + FORM_CHANGE_ITEM: 0x0F00, +} as const; + +export type RewardCategoryId = EnumValues; + +const ITEM_CATEGORY_MASK = 0xFF00 + +export function getRewardCategory(itemId: RewardId): RewardCategoryId { + return (itemId & ITEM_CATEGORY_MASK) as RewardCategoryId; +} diff --git a/src/enums/modifier-pool-type.ts b/src/enums/reward-pool-type.ts similarity index 80% rename from src/enums/modifier-pool-type.ts rename to src/enums/reward-pool-type.ts index 6513dc45ac1..fa8c3834009 100644 --- a/src/enums/modifier-pool-type.ts +++ b/src/enums/reward-pool-type.ts @@ -1,4 +1,4 @@ -export enum ModifierPoolType { +export enum RewardPoolType { PLAYER, } diff --git a/src/enums/reward-tier.ts b/src/enums/reward-tier.ts index e7ccc1d9166..51b799a8a5c 100644 --- a/src/enums/reward-tier.ts +++ b/src/enums/reward-tier.ts @@ -1,4 +1,4 @@ -export enum RewardTier { +export enum RarityTier { COMMON, GREAT, ULTRA, diff --git a/src/enums/trainer-item-id.ts b/src/enums/trainer-item-id.ts index 032c8933eb8..9d07e5c3c50 100644 --- a/src/enums/trainer-item-id.ts +++ b/src/enums/trainer-item-id.ts @@ -1,56 +1,56 @@ export const TrainerItemId = { NONE: 0x0000, - MAP: 0x0B01, - IV_SCANNER: 0x0B02, - LOCK_CAPSULE: 0x0B03, - MEGA_BRACELET: 0x0B04, - DYNAMAX_BAND: 0x0B05, - TERA_ORB: 0x0B06, +MAP: 0x1001, +IV_SCANNER: 0x1002, +LOCK_CAPSULE: 0x1003, +MEGA_BRACELET: 0x1004, +DYNAMAX_BAND: 0x1005, +TERA_ORB: 0x1006, - GOLDEN_POKEBALL: 0x0B07, +GOLDEN_POKEBALL: 0x1007, - OVAL_CHARM: 0x0B08, - EXP_SHARE: 0x0B09, - EXP_BALANCE: 0x0B0A, +OVAL_CHARM: 0x1008, +EXP_SHARE: 0x1009, +EXP_BALANCE: 0x100A, - CANDY_JAR: 0x0B0B, - BERRY_POUCH: 0x0B0C, +CANDY_JAR: 0x100B, +BERRY_POUCH: 0x100C, - HEALING_CHARM: 0x0B0D, - EXP_CHARM: 0x0B0E, - SUPER_EXP_CHARM: 0x0B0F, - GOLDEN_EXP_CHARM: 0x0B10, - AMULET_COIN: 0x0B11, +HEALING_CHARM: 0x100D, +EXP_CHARM: 0x100E, +SUPER_EXP_CHARM: 0x100F, +GOLDEN_EXP_CHARM: 0x1010, +AMULET_COIN: 0x1011, - ABILITY_CHARM: 0x0B12, - SHINY_CHARM: 0x0B13, - CATCHING_CHARM: 0x0B14, +ABILITY_CHARM: 0x1012, +SHINY_CHARM: 0x1013, +CATCHING_CHARM: 0x1014, - BLACK_SLUDGE: 0x0B15, - GOLDEN_BUG_NET: 0x0B16, +BLACK_SLUDGE: 0x1015, +GOLDEN_BUG_NET: 0x1016, - LURE: 0x0C01, - SUPER_LURE: 0x0C02, - MAX_LURE: 0x0C03, +LURE: 0x1101, +SUPER_LURE: 0x1102, +MAX_LURE: 0x1103, - X_ATTACK: 0x0D01, - X_DEFENSE: 0x0D02, - X_SP_ATK: 0x0D03, - X_SP_DEF: 0x0D04, - X_SPEED: 0x0D05, - X_ACCURACY: 0x0D06, - DIRE_HIT: 0x0D07, +X_ATTACK: 0x1201, +X_DEFENSE: 0x1202, +X_SP_ATK: 0x1203, +X_SP_DEF: 0x1204, +X_SPEED: 0x1205, +X_ACCURACY: 0x1206, +DIRE_HIT: 0x1207, - ENEMY_DAMAGE_BOOSTER: 0x0E01, - ENEMY_DAMAGE_REDUCTION: 0x0E02, - ENEMY_HEAL: 0x0E03, - ENEMY_ATTACK_POISON_CHANCE: 0x0E04, - ENEMY_ATTACK_PARALYZE_CHANCE: 0x0E05, - ENEMY_ATTACK_BURN_CHANCE: 0x0E06, - ENEMY_STATUS_EFFECT_HEAL_CHANCE: 0x0E07, - ENEMY_ENDURE_CHANCE: 0x0E08, - ENEMY_FUSED_CHANCE: 0x0E09, +ENEMY_DAMAGE_BOOSTER: 0x1301, +ENEMY_DAMAGE_REDUCTION: 0x1302, +ENEMY_HEAL: 0x1303, +ENEMY_ATTACK_POISON_CHANCE: 0x1304, +ENEMY_ATTACK_PARALYZE_CHANCE: 0x1305, +ENEMY_ATTACK_BURN_CHANCE: 0x1306, +ENEMY_STATUS_EFFECT_HEAL_CHANCE: 0x1307, +ENEMY_ENDURE_CHANCE: 0x1308, +ENEMY_FUSED_CHANCE: 0x1309, } as const; export type TrainerItemId = (typeof TrainerItemId)[keyof typeof TrainerItemId]; diff --git a/src/enums/ui-mode.ts b/src/enums/ui-mode.ts index dcf6bd2a238..ffe742b9d8e 100644 --- a/src/enums/ui-mode.ts +++ b/src/enums/ui-mode.ts @@ -5,7 +5,7 @@ export enum UiMode { FIGHT, BALL, TARGET_SELECT, - MODIFIER_SELECT, + REWARD_SELECT, SAVE_SLOT, PARTY, SUMMARY, diff --git a/src/field/arena.ts b/src/field/arena.ts index 37bee016c76..883e7958d42 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -19,6 +19,7 @@ import { ArenaTagSide } from "#enums/arena-tag-side"; import type { ArenaTagType } from "#enums/arena-tag-type"; import type { BattlerIndex } from "#enums/battler-index"; import { BiomeId } from "#enums/biome-id"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { CommonAnim } from "#enums/move-anims-common"; import type { MoveId } from "#enums/move-id"; import type { PokemonType } from "#enums/pokemon-type"; @@ -29,7 +30,6 @@ import { WeatherType } from "#enums/weather-type"; import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import type { Move } from "#moves/move"; import type { AbstractConstructor } from "#types/type-helpers"; import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 61463731089..e9b41cbaa52 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -80,6 +80,7 @@ import { ChallengeType } from "#enums/challenge-type"; import { Challenges } from "#enums/challenges"; import { DexAttr } from "#enums/dex-attr"; import { FieldPosition } from "#enums/field-position"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { HitResult } from "#enums/hit-result"; import { LearnMoveSituation } from "#enums/learn-move-situation"; @@ -92,7 +93,7 @@ import { Nature } from "#enums/nature"; import { PokeballType } from "#enums/pokeball"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesId } from "#enums/species-id"; import { @@ -111,7 +112,6 @@ import { UiMode } from "#enums/ui-mode"; import { WeatherType } from "#enums/weather-type"; import { doShinySparkleAnim } from "#field/anims"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import type { HeldItemConfiguration } from "#items/held-item-data-types"; import { HeldItemManager } from "#items/held-item-manager"; import { assignItemsFromConfiguration } from "#items/held-item-pool"; @@ -2882,13 +2882,13 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536` * @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) - * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride} + * @param applyItemsToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride} * @returns `true` if the Pokemon has been set as a shiny, `false` otherwise */ - public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean { + public trySetShinySeed(thresholdOverride?: number, applyItemsToOverride?: boolean): boolean { if (!this.shiny) { const shinyThreshold = new NumberHolder(thresholdOverride ?? BASE_SHINY_CHANCE); - if (applyModifiersToOverride) { + if (applyItemsToOverride) { if (timedEventManager.isEventActive()) { shinyThreshold.value *= timedEventManager.getShinyMultiplier(); } @@ -2954,15 +2954,15 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * * The base hidden ability odds are {@linkcode BASE_HIDDEN_ABILITY_CHANCE} / `65536` * @param thresholdOverride number that is divided by `2^16` (`65536`) to get the HA chance, overrides {@linkcode haThreshold} if set (bypassing HA rate modifiers such as Ability Charm) - * @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Ability Charm to {@linkcode thresholdOverride} + * @param applyItemsToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Ability Charm to {@linkcode thresholdOverride} * @returns `true` if the Pokemon has been set to have its hidden ability, `false` otherwise */ - public tryRerollHiddenAbilitySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean { + public tryRerollHiddenAbilitySeed(thresholdOverride?: number, applyItemsToOverride?: boolean): boolean { if (!this.species.abilityHidden) { return false; } const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE); - if (applyModifiersToOverride) { + if (applyItemsToOverride) { if (!this.hasTrainer()) { globalScene.applyPlayerItems(TrainerItemEffect.HIDDEN_ABILITY_CHANCE_BOOSTER, { numberHolder: haThreshold }); } @@ -3108,11 +3108,11 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { } } if (compatible && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) { - if (tmPoolTiers[moveId] === RewardTier.COMMON && this.level >= 15) { + if (tmPoolTiers[moveId] === RarityTier.COMMON && this.level >= 15) { movePool.push([moveId, 4]); - } else if (tmPoolTiers[moveId] === RewardTier.GREAT && this.level >= 30) { + } else if (tmPoolTiers[moveId] === RarityTier.GREAT && this.level >= 30) { movePool.push([moveId, 8]); - } else if (tmPoolTiers[moveId] === RewardTier.ULTRA && this.level >= 50) { + } else if (tmPoolTiers[moveId] === RarityTier.ULTRA && this.level >= 50) { movePool.push([moveId, 14]); } } diff --git a/src/field/trainer.ts b/src/field/trainer.ts index c19e96f24dd..dea610b4195 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -634,7 +634,7 @@ export class Trainer extends Phaser.GameObjects.Container { return maxScorePartyMemberIndexes[0]; } - getPartyMemberModifierChanceMultiplier(index: number): number { + getPartyMemberItemChanceMultiplier(index: number): number { switch (this.getPartyTemplate().getStrength(index)) { case PartyMemberStrength.WEAKER: return 0.75; @@ -647,14 +647,14 @@ export class Trainer extends Phaser.GameObjects.Container { case PartyMemberStrength.STRONGER: return 0.375; default: - console.warn("getPartyMemberModifierChanceMultiplier not defined. Using default 0"); + console.warn("getPartyMemberItemChanceMultiplier not defined. Using default 0"); return 0; } } genTrainerItems(party: EnemyPokemon[]): TrainerItemConfiguration { - if (this.config.genModifiersFunc) { - return this.config.genModifiersFunc(party); + if (this.config.genTrainerItemsFunc) { + return this.config.genTrainerItemsFunc(party); } return []; } diff --git a/src/game-mode.ts b/src/game-mode.ts index c5ab120e218..d12e801b196 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -323,7 +323,7 @@ export class GameMode implements GameModeConfig { } } - getEnemyModifierChance(isBoss: boolean): number { + getEnemyItemChance(isBoss: boolean): number { switch (this.modeId) { case GameModes.CLASSIC: case GameModes.CHALLENGE: diff --git a/src/items/all-held-items.ts b/src/items/all-held-items.ts index ea9db959892..fa3c8d8cebe 100644 --- a/src/items/all-held-items.ts +++ b/src/items/all-held-items.ts @@ -24,7 +24,6 @@ import { ExpBoosterHeldItem, type ExpBoostParams } from "#items/exp-booster"; import { FieldEffectHeldItem, type FieldEffectParams } from "#items/field-effect"; import { FlinchChanceHeldItem, type FlinchChanceParams } from "#items/flinch-chance"; import { FriendshipBoosterHeldItem, type FriendshipBoostParams } from "#items/friendship-booster"; -import { HeldItemEffect } from "#items/held-item"; import { HitHealHeldItem, type HitHealParams } from "#items/hit-heal"; import { IncrementingStatHeldItem, type IncrementingStatParams } from "#items/incrementing-stat"; import { InstantReviveHeldItem, type InstantReviveParams } from "#items/instant-revive"; @@ -36,7 +35,8 @@ import { EvolutionStatBoostHeldItem, SpeciesStatBoostHeldItem, type StatBoostPar import { SurviveChanceHeldItem, type SurviveChanceParams } from "#items/survive-chance"; import { TurnEndHealHeldItem, type TurnEndHealParams } from "#items/turn-end-heal"; import { TurnEndStatusHeldItem, type TurnEndStatusParams } from "#items/turn-end-status"; -import { getEnumValues } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; +import { HeldItemEffect } from "./HeldItemEffect"; export function initHeldItems() { for (const berry of getEnumValues(BerryType)) { diff --git a/src/items/all-trainer-items.ts b/src/items/all-trainer-items.ts index 0b4901ca6b9..487bd3224d0 100644 --- a/src/items/all-trainer-items.ts +++ b/src/items/all-trainer-items.ts @@ -43,7 +43,7 @@ export function initTrainerItems() { allTrainerItems[TrainerItemId.CANDY_JAR] = new LevelIncrementBoosterTrainerItem(TrainerItemId.CANDY_JAR, 99); allTrainerItems[TrainerItemId.BERRY_POUCH] = new PreserveBerryTrainerItem(TrainerItemId.BERRY_POUCH, 3); - allTrainerItems[TrainerItemId.HEALING_CHARM] = new HealingBoosterTrainerItem(TrainerItemId.HEALING_CHARM, 1.1, 5); + allTrainerItems[TrainerItemId.HEALING_CHARM] = new HealingBoosterTrainerItem(TrainerItemId.HEALING_CHARM, 0.1, 5); allTrainerItems[TrainerItemId.EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.EXP_CHARM, 25, 99); allTrainerItems[TrainerItemId.SUPER_EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.SUPER_EXP_CHARM, 60, 30); allTrainerItems[TrainerItemId.GOLDEN_EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.GOLDEN_EXP_CHARM, 100, 10); diff --git a/src/items/held-item-data-types.ts b/src/items/held-item-data-types.ts index 58d3f25d20c..af4a3054bac 100644 --- a/src/items/held-item-data-types.ts +++ b/src/items/held-item-data-types.ts @@ -1,13 +1,21 @@ -// TODO: move to `src/@types/` +// TODO: move all types to `src/@types/` and all functions to a utility place import type { FormChangeItem } from "#enums/form-change-item"; import type { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; -import type { RewardTier } from "#enums/reward-tier"; +import type { RarityTier } from "#enums/reward-tier"; import type { Pokemon } from "#field/pokemon"; export type HeldItemData = { stack: number; + /** + * Whether this item is currently disabled. + * @defaultValue `false` + */ disabled?: boolean; + /** + * The item's current cooldown. + * @defaultValue `0` + */ cooldown?: number; }; @@ -67,7 +75,7 @@ export function isHeldItemPool(value: any): value is HeldItemPool { } export type HeldItemTieredPool = { - [key in RewardTier]?: HeldItemPool; + [key in RarityTier]?: HeldItemPool; }; type HeldItemConfigurationEntry = { diff --git a/src/items/held-item-default-tiers.ts b/src/items/held-item-default-tiers.ts new file mode 100644 index 00000000000..8ca4c286343 --- /dev/null +++ b/src/items/held-item-default-tiers.ts @@ -0,0 +1,47 @@ +import { getHeldItemCategory, HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; +import { RarityTier } from "#enums/reward-tier"; + +export const heldItemRarities = { + [HeldItemCategoryId.BERRY]: RarityTier.COMMON, + + [HeldItemCategoryId.BASE_STAT_BOOST]: RarityTier.GREAT, + [HeldItemId.WHITE_HERB]: RarityTier.GREAT, + [HeldItemId.METAL_POWDER]: RarityTier.GREAT, + [HeldItemId.QUICK_POWDER]: RarityTier.GREAT, + [HeldItemId.DEEP_SEA_SCALE]: RarityTier.GREAT, + [HeldItemId.DEEP_SEA_TOOTH]: RarityTier.GREAT, + [HeldItemId.SOOTHE_BELL]: RarityTier.GREAT, + + [HeldItemCategoryId.TYPE_ATTACK_BOOSTER]: RarityTier.ULTRA, + [HeldItemId.REVIVER_SEED]: RarityTier.ULTRA, + [HeldItemId.LIGHT_BALL]: RarityTier.ULTRA, + [HeldItemId.EVIOLITE]: RarityTier.ULTRA, + [HeldItemId.QUICK_CLAW]: RarityTier.ULTRA, + [HeldItemId.MYSTICAL_ROCK]: RarityTier.ULTRA, + [HeldItemId.WIDE_LENS]: RarityTier.ULTRA, + [HeldItemId.GOLDEN_PUNCH]: RarityTier.ULTRA, + [HeldItemId.TOXIC_ORB]: RarityTier.ULTRA, + [HeldItemId.FLAME_ORB]: RarityTier.ULTRA, + [HeldItemId.LUCKY_EGG]: RarityTier.ULTRA, + + [HeldItemId.FOCUS_BAND]: RarityTier.ROGUE, + [HeldItemId.KINGS_ROCK]: RarityTier.ROGUE, + [HeldItemId.LEFTOVERS]: RarityTier.ROGUE, + [HeldItemId.SHELL_BELL]: RarityTier.ROGUE, + [HeldItemId.GRIP_CLAW]: RarityTier.ROGUE, + [HeldItemId.SOUL_DEW]: RarityTier.ROGUE, + [HeldItemId.BATON]: RarityTier.ROGUE, + [HeldItemId.GOLDEN_EGG]: RarityTier.ULTRA, + + [HeldItemId.MINI_BLACK_HOLE]: RarityTier.MASTER, + [HeldItemId.MULTI_LENS]: RarityTier.MASTER, +}; + +export function getHeldItemTier(item: HeldItemId): RarityTier { + let tier = heldItemRarities[item]; + if (!tier) { + const category = getHeldItemCategory(item); + tier = heldItemRarities[category]; + } + return tier ?? RarityTier.LUXURY; +} diff --git a/src/items/held-item-pool.ts b/src/items/held-item-pool.ts index 077c5be730b..55066a86ede 100644 --- a/src/items/held-item-pool.ts +++ b/src/items/held-item-pool.ts @@ -1,9 +1,9 @@ import { allHeldItems } from "#data/data-lists"; import { BerryType } from "#enums/berry-type"; import { HeldItemCategoryId, HeldItemId, HeldItemNames, 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"; +import { HeldItemPoolType } from "#enums/reward-pool-type"; +import { RarityTier } from "#enums/reward-tier"; import { PERMANENT_STATS } from "#enums/stat"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { attackTypeToHeldItem } from "#items/attack-type-booster"; @@ -21,7 +21,8 @@ import { isHeldItemPool, isHeldItemSpecs, } from "#items/held-item-data-types"; -import { coerceArray, getEnumValues, isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common"; +import { coerceArray, isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common"; +import { getEnumValues } from "#utils/enums"; export const wildHeldItemPool: HeldItemTieredPool = {}; @@ -34,7 +35,7 @@ export function assignDailyRunStarterHeldItems(party: PlayerPokemon[]) { for (let m = 0; m < 3; m++) { const tierValue = randSeedInt(64); - const tier = getDailyRewardTier(tierValue); + const tier = getDailyRarityTier(tierValue); const item = getNewHeldItemFromPool( getHeldItemPool(HeldItemPoolType.DAILY_STARTER)[tier] as HeldItemPool, @@ -46,20 +47,20 @@ export function assignDailyRunStarterHeldItems(party: PlayerPokemon[]) { } } -function getDailyRewardTier(tierValue: number): RewardTier { +function getDailyRarityTier(tierValue: number): RarityTier { if (tierValue > 25) { - return RewardTier.COMMON; + return RarityTier.COMMON; } if (tierValue > 12) { - return RewardTier.GREAT; + return RarityTier.GREAT; } if (tierValue > 4) { - return RewardTier.ULTRA; + return RarityTier.ULTRA; } if (tierValue > 0) { - return RewardTier.ROGUE; + return RarityTier.ROGUE; } - return RewardTier.MASTER; + return RarityTier.MASTER; } function getHeldItemPool(poolType: HeldItemPoolType): HeldItemTieredPool { @@ -101,25 +102,25 @@ export function assignEnemyHeldItemsForWave( } } -function getRandomTier(): RewardTier { +function getRandomTier(): RarityTier { const tierValue = randSeedInt(1024); if (tierValue > 255) { - return RewardTier.COMMON; + return RarityTier.COMMON; } if (tierValue > 60) { - return RewardTier.GREAT; + return RarityTier.GREAT; } if (tierValue > 12) { - return RewardTier.ULTRA; + return RarityTier.ULTRA; } if (tierValue) { - return RewardTier.ROGUE; + return RarityTier.ROGUE; } - return RewardTier.MASTER; + return RarityTier.MASTER; } -function determineItemPoolTier(pool: HeldItemTieredPool, upgradeCount?: number): RewardTier { +function determineItemPoolTier(pool: HeldItemTieredPool, upgradeCount?: number): RarityTier { let tier = getRandomTier(); if (!upgradeCount) { diff --git a/src/items/held-item-tiers.ts b/src/items/held-item-tiers.ts deleted file mode 100644 index fb340998198..00000000000 --- a/src/items/held-item-tiers.ts +++ /dev/null @@ -1,47 +0,0 @@ -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; -} diff --git a/src/items/held-item.ts b/src/items/held-item.ts index 9ca3fbd616d..6dd76883e47 100644 --- a/src/items/held-item.ts +++ b/src/items/held-item.ts @@ -4,42 +4,9 @@ import { type HeldItemId, HeldItemNames } from "#enums/held-item-id"; import type { Pokemon } from "#field/pokemon"; import i18next from "i18next"; -// TODO: this should be moved to its own file -export const HeldItemEffect = { - ATTACK_TYPE_BOOST: 1, - TURN_END_HEAL: 2, - HIT_HEAL: 3, - RESET_NEGATIVE_STAT_STAGE: 4, - EXP_BOOSTER: 5, - // Should we actually distinguish different berry effects? - BERRY: 6, - BASE_STAT_BOOSTER: 7, - INSTANT_REVIVE: 8, - STAT_BOOST: 9, - CRIT_BOOST: 10, - TURN_END_STATUS: 11, - SURVIVE_CHANCE: 12, - BYPASS_SPEED_CHANCE: 13, - FLINCH_CHANCE: 14, - FIELD_EFFECT: 15, - FRIENDSHIP_BOOSTER: 16, - NATURE_WEIGHT_BOOSTER: 17, - ACCURACY_BOOSTER: 18, - MULTI_HIT: 19, - DAMAGE_MONEY_REWARD: 20, - BATON: 21, - TURN_END_ITEM_STEAL: 22, - CONTACT_ITEM_STEAL_CHANCE: 23, - EVO_TRACKER: 40, - BASE_STAT_TOTAL: 50, - BASE_STAT_FLAT: 51, - INCREMENTING_STAT: 52, -} as const; - -export type HeldItemEffect = (typeof HeldItemEffect)[keyof typeof HeldItemEffect]; - export class HeldItem { // public pokemonId: number; + // TODO: Should this be readonly? public type: HeldItemId; public readonly maxStackCount: number; public isTransferable = true; diff --git a/src/items/held-items/accuracy-booster.ts b/src/items/held-items/accuracy-booster.ts index 09abb723300..ff57a82b192 100644 --- a/src/items/held-items/accuracy-booster.ts +++ b/src/items/held-items/accuracy-booster.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface AccuracyBoostParams { @@ -24,17 +25,17 @@ export class AccuracyBoosterHeldItem extends HeldItem { } /** - * Checks if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied + * Checks if {@linkcode PokemonMoveAccuracyBoosterHeldItem} should be applied * @param pokemon - The {@linkcode Pokemon} to apply the move accuracy boost to * @param moveAccuracy - {@linkcode NumberHolder} holding the move accuracy boost - * @returns `true` if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied + * @returns `true` if {@linkcode PokemonMoveAccuracyBoosterHeldItem} should be applied */ // override shouldApply(pokemon?: Pokemon, moveAccuracy?: NumberHolder): boolean { // return super.shouldApply(pokemon, moveAccuracy) && !!moveAccuracy; // } /** - * Applies {@linkcode PokemonMoveAccuracyBoosterModifier} + * Applies {@linkcode PokemonMoveAccuracyBoosterHeldItem} */ apply({ pokemon, moveAccuracy }: AccuracyBoostParams): true { const stackCount = pokemon.heldItemManager.getStack(this.type); diff --git a/src/items/held-items/attack-type-booster.ts b/src/items/held-items/attack-type-booster.ts index 7e5f1f10141..f1558d095a8 100644 --- a/src/items/held-items/attack-type-booster.ts +++ b/src/items/held-items/attack-type-booster.ts @@ -1,7 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId, HeldItemNames } from "#enums/held-item-id"; import { PokemonType } from "#enums/pokemon-type"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/base-stat-booster.ts b/src/items/held-items/base-stat-booster.ts index 7525974ca92..03be45ec4b9 100644 --- a/src/items/held-items/base-stat-booster.ts +++ b/src/items/held-items/base-stat-booster.ts @@ -1,7 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { getStatKey, type PermanentStat, Stat } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import i18next from "i18next"; export interface BaseStatBoosterParams { @@ -11,9 +12,9 @@ export interface BaseStatBoosterParams { baseStats: number[]; } -interface PermanentStatToHeldItemMap { - [key: number]: HeldItemId; -} +type PermanentStatToHeldItemMap = { + [key in PermanentStat]: HeldItemId; +}; export const permanentStatToHeldItem: PermanentStatToHeldItemMap = { [Stat.HP]: HeldItemId.HP_UP, diff --git a/src/items/held-items/base-stat-flat.ts b/src/items/held-items/base-stat-flat.ts index ae10f6d7d1d..d0c622af6ba 100644 --- a/src/items/held-items/base-stat-flat.ts +++ b/src/items/held-items/base-stat-flat.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { Stat } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import i18next from "i18next"; export interface BaseStatFlatParams { diff --git a/src/items/held-items/base-stat-total.ts b/src/items/held-items/base-stat-total.ts index 1c1eb154cb3..dce4355a009 100644 --- a/src/items/held-items/base-stat-total.ts +++ b/src/items/held-items/base-stat-total.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import i18next from "i18next"; export interface BaseStatTotalParams { diff --git a/src/items/held-items/baton.ts b/src/items/held-items/baton.ts index e518f8f0c5d..72c89727a73 100644 --- a/src/items/held-items/baton.ts +++ b/src/items/held-items/baton.ts @@ -1,5 +1,6 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface BatonParams { diff --git a/src/items/held-items/berry.ts b/src/items/held-items/berry.ts index 3b0b3bcf2f1..0ce886c41fe 100644 --- a/src/items/held-items/berry.ts +++ b/src/items/held-items/berry.ts @@ -1,18 +1,21 @@ import { globalScene } from "#app/global-scene"; import { getBerryEffectDescription, getBerryEffectFunc, getBerryName, getBerryPredicate } from "#data/berry"; import { BerryType } from "#enums/berry-type"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { BerryUsedEvent } from "#events/battle-scene"; import type { Pokemon } from "#field/pokemon"; -import { ConsumableHeldItem, HeldItemEffect } from "#items/held-item"; +import { ConsumableHeldItem } from "#items/held-item"; import { TrainerItemEffect } from "#items/trainer-item"; +import type { EnumValues } from "#types/enum-types"; import { BooleanHolder } from "#utils/common"; -interface BerryTypeToHeldItemMap { - [key: number]: HeldItemId; -} +type BerryTypeToHeldItemMap = { + [key in EnumValues]: HeldItemId; +}; -export const berryTypeToHeldItem: BerryTypeToHeldItemMap = { +// TODO: Rework this to use a bitwise XOR +export const berryTypeToHeldItem = { [BerryType.SITRUS]: HeldItemId.SITRUS_BERRY, [BerryType.LUM]: HeldItemId.LUM_BERRY, [BerryType.ENIGMA]: HeldItemId.ENIGMA_BERRY, @@ -24,7 +27,7 @@ export const berryTypeToHeldItem: BerryTypeToHeldItemMap = { [BerryType.LANSAT]: HeldItemId.LANSAT_BERRY, [BerryType.STARF]: HeldItemId.STARF_BERRY, [BerryType.LEPPA]: HeldItemId.LEPPA_BERRY, -}; +} satisfies BerryTypeToHeldItemMap; export interface BerryParams { /** The pokemon with the berry */ diff --git a/src/items/held-items/bypass-speed-chance.ts b/src/items/held-items/bypass-speed-chance.ts index ba3800d2635..3d00e644df7 100644 --- a/src/items/held-items/bypass-speed-chance.ts +++ b/src/items/held-items/bypass-speed-chance.ts @@ -1,8 +1,9 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { Command } from "#enums/command"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { BooleanHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/crit-booster.ts b/src/items/held-items/crit-booster.ts index d1ba07c90c9..8a8c153e1a0 100644 --- a/src/items/held-items/crit-booster.ts +++ b/src/items/held-items/crit-booster.ts @@ -1,7 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { SpeciesId } from "#enums/species-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface CritBoostParams { diff --git a/src/items/held-items/damage-money-reward.ts b/src/items/held-items/damage-money-reward.ts index b158640cb7f..1d57e61b697 100644 --- a/src/items/held-items/damage-money-reward.ts +++ b/src/items/held-items/damage-money-reward.ts @@ -1,6 +1,7 @@ import { globalScene } from "#app/global-scene"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import { TrainerItemEffect } from "#items/trainer-item"; import { NumberHolder } from "#utils/common"; diff --git a/src/items/held-items/evo-tracker.ts b/src/items/held-items/evo-tracker.ts index a52ac286f06..29e84958910 100644 --- a/src/items/held-items/evo-tracker.ts +++ b/src/items/held-items/evo-tracker.ts @@ -1,9 +1,10 @@ import { globalScene } from "#app/global-scene"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import type { SpeciesId } from "#enums/species-id"; import { TrainerItemId } from "#enums/trainer-item-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import i18next from "i18next"; export interface EvoTrackerParams { diff --git a/src/items/held-items/exp-booster.ts b/src/items/held-items/exp-booster.ts index d257fc8d774..e4417e8ac95 100644 --- a/src/items/held-items/exp-booster.ts +++ b/src/items/held-items/exp-booster.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/field-effect.ts b/src/items/held-items/field-effect.ts index a076a2fc7dd..25baf9c889a 100644 --- a/src/items/held-items/field-effect.ts +++ b/src/items/held-items/field-effect.ts @@ -1,5 +1,6 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface FieldEffectParams { diff --git a/src/items/held-items/flinch-chance.ts b/src/items/held-items/flinch-chance.ts index 82d716368aa..81320c55ba9 100644 --- a/src/items/held-items/flinch-chance.ts +++ b/src/items/held-items/flinch-chance.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { BooleanHolder } from "#utils/common"; export interface FlinchChanceParams { diff --git a/src/items/held-items/friendship-booster.ts b/src/items/held-items/friendship-booster.ts index 960838f8925..783b77c1a15 100644 --- a/src/items/held-items/friendship-booster.ts +++ b/src/items/held-items/friendship-booster.ts @@ -1,5 +1,6 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/hit-heal.ts b/src/items/held-items/hit-heal.ts index 5d4a05582ec..8e14509b429 100644 --- a/src/items/held-items/hit-heal.ts +++ b/src/items/held-items/hit-heal.ts @@ -1,7 +1,8 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; import { toDmgValue } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/incrementing-stat.ts b/src/items/held-items/incrementing-stat.ts index a462b70326e..3dc62d8ebb3 100644 --- a/src/items/held-items/incrementing-stat.ts +++ b/src/items/held-items/incrementing-stat.ts @@ -1,6 +1,7 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { Stat } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/instant-revive.ts b/src/items/held-items/instant-revive.ts index 10137efe61d..839f660d09a 100644 --- a/src/items/held-items/instant-revive.ts +++ b/src/items/held-items/instant-revive.ts @@ -1,8 +1,9 @@ import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { ConsumableHeldItem, HeldItemEffect } from "#items/held-item"; +import { ConsumableHeldItem } from "#items/held-item"; import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; import { toDmgValue } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/item-steal.ts b/src/items/held-items/item-steal.ts index acba8d6930f..681a1d619b1 100644 --- a/src/items/held-items/item-steal.ts +++ b/src/items/held-items/item-steal.ts @@ -1,9 +1,10 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { allHeldItems } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import { coerceArray, randSeedFloat } from "#utils/common"; import i18next from "i18next"; @@ -47,7 +48,7 @@ export abstract class ItemTransferHeldItem extends HeldItem { } // TODO: Change this logic to use held items - const transferredModifierTypes: HeldItemId[] = []; + const transferredRewards: HeldItemId[] = []; const heldItems = targetPokemon.heldItemManager.getTransferableHeldItems(); for (let i = 0; i < transferredItemCount; i++) { @@ -58,16 +59,16 @@ export abstract class ItemTransferHeldItem extends HeldItem { const randItem = heldItems[randItemIndex]; // TODO: Fix this after updating the various methods in battle-scene.ts if (globalScene.tryTransferHeldItem(randItem, targetPokemon, pokemon, false)) { - transferredModifierTypes.push(randItem); + transferredRewards.push(randItem); heldItems.splice(randItemIndex, 1); } } - for (const mt of transferredModifierTypes) { + for (const mt of transferredRewards) { globalScene.phaseManager.queueMessage(this.getTransferMessage(params, mt)); } - return !!transferredModifierTypes.length; + return !!transferredRewards.length; } abstract getTargets(params: ItemStealParams): Pokemon[]; @@ -80,7 +81,7 @@ export abstract class ItemTransferHeldItem extends HeldItem { /** * Modifier for held items that steal items from the enemy at the end of * each turn. - * @see {@linkcode modifierTypes[MINI_BLACK_HOLE]} + * @see {@linkcode allRewards[MINI_BLACK_HOLE]} */ export class TurnEndItemStealHeldItem extends ItemTransferHeldItem { public effects: HeldItemEffect[] = [HeldItemEffect.TURN_END_ITEM_STEAL]; @@ -121,7 +122,7 @@ export class TurnEndItemStealHeldItem extends ItemTransferHeldItem { /** * Modifier for held items that add a chance to steal items from the target of a * successful attack. - * @see {@linkcode modifierTypes[GRIP_CLAW]} + * @see {@linkcode allRewards[GRIP_CLAW]} * @see {@linkcode HeldItemTransferModifier} */ export class ContactItemStealChanceHeldItem extends ItemTransferHeldItem { diff --git a/src/items/held-items/multi-hit.ts b/src/items/held-items/multi-hit.ts index 66cb7cdfa2b..1c2d34544e6 100644 --- a/src/items/held-items/multi-hit.ts +++ b/src/items/held-items/multi-hit.ts @@ -1,7 +1,8 @@ import { allMoves } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { MoveId } from "#enums/move-id"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import { isNullOrUndefined, type NumberHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/nature-weight-booster.ts b/src/items/held-items/nature-weight-booster.ts index ec6e91eb437..1e810798f73 100644 --- a/src/items/held-items/nature-weight-booster.ts +++ b/src/items/held-items/nature-weight-booster.ts @@ -1,5 +1,6 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface NatureWeightBoostParams { diff --git a/src/items/held-items/reset-negative-stat-stage.ts b/src/items/held-items/reset-negative-stat-stage.ts index 0714270cc51..acb935cd971 100644 --- a/src/items/held-items/reset-negative-stat-stage.ts +++ b/src/items/held-items/reset-negative-stat-stage.ts @@ -1,8 +1,9 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { BATTLE_STATS } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; -import { ConsumableHeldItem, HeldItemEffect } from "#items/held-item"; +import { ConsumableHeldItem } from "#items/held-item"; import i18next from "i18next"; export interface ResetNegativeStatStageParams { diff --git a/src/items/held-items/stat-booster.ts b/src/items/held-items/stat-booster.ts index aa7eaea93ec..bb66e2e6d22 100644 --- a/src/items/held-items/stat-booster.ts +++ b/src/items/held-items/stat-booster.ts @@ -1,9 +1,10 @@ import { pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import type { SpeciesId } from "#enums/species-id"; import type { Stat } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { NumberHolder } from "#utils/common"; export interface StatBoostParams { diff --git a/src/items/held-items/survive-chance.ts b/src/items/held-items/survive-chance.ts index 4120e452d1a..b46297170d1 100644 --- a/src/items/held-items/survive-chance.ts +++ b/src/items/held-items/survive-chance.ts @@ -1,7 +1,8 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import type { BooleanHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/turn-end-heal.ts b/src/items/held-items/turn-end-heal.ts index 598b9bc61fa..f6660fab935 100644 --- a/src/items/held-items/turn-end-heal.ts +++ b/src/items/held-items/turn-end-heal.ts @@ -1,7 +1,8 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; +import { HeldItemEffect } from "#enums/held-item-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; import { toDmgValue } from "#utils/common"; import i18next from "i18next"; diff --git a/src/items/held-items/turn-end-status.ts b/src/items/held-items/turn-end-status.ts index d6cbe240fd1..72ccf440d4c 100644 --- a/src/items/held-items/turn-end-status.ts +++ b/src/items/held-items/turn-end-status.ts @@ -1,7 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import type { HeldItemId } from "#enums/held-item-id"; import type { StatusEffect } from "#enums/status-effect"; import type { Pokemon } from "#field/pokemon"; -import { HeldItem, HeldItemEffect } from "#items/held-item"; +import { HeldItem } from "#items/held-item"; export interface TurnEndStatusParams { /** The pokemon with the item */ diff --git a/src/items/init-held-item-pools.ts b/src/items/init-held-item-pools.ts index 2a335246b73..3957faed1ca 100644 --- a/src/items/init-held-item-pools.ts +++ b/src/items/init-held-item-pools.ts @@ -1,42 +1,42 @@ import { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { dailyStarterHeldItemPool, trainerHeldItemPool, wildHeldItemPool } from "#items/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] = [ + wildHeldItemPool[RarityTier.COMMON] = [{ entry: HeldItemCategoryId.BERRY, weight: 1 }]; + wildHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }]; + wildHeldItemPool[RarityTier.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 }]; + wildHeldItemPool[RarityTier.ROGUE] = [{ entry: HeldItemId.LUCKY_EGG, weight: 4 }]; + wildHeldItemPool[RarityTier.MASTER] = [{ entry: HeldItemId.GOLDEN_EGG, weight: 1 }]; } /** * Initialize the trainer pokemon held item pool */ function initTrainerHeldItemPool() { - trainerHeldItemPool[RewardTier.COMMON] = [ + trainerHeldItemPool[RarityTier.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] = [ + trainerHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }]; + trainerHeldItemPool[RarityTier.ULTRA] = [ { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 10 }, { entry: HeldItemId.WHITE_HERB, weight: 0 }, ]; - trainerHeldItemPool[RewardTier.ROGUE] = [ + trainerHeldItemPool[RarityTier.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] = [ + trainerHeldItemPool[RarityTier.MASTER] = [ { entry: HeldItemId.KINGS_ROCK, weight: 1 }, { entry: HeldItemId.LEFTOVERS, weight: 1 }, { entry: HeldItemId.SHELL_BELL, weight: 1 }, @@ -47,26 +47,26 @@ function initTrainerHeldItemPool() { /** * Initialize the daily starter held item pool */ -function initDailyStarterModifierPool() { - dailyStarterHeldItemPool[RewardTier.COMMON] = [ +function initDailyStarterRewardPool() { + dailyStarterHeldItemPool[RarityTier.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] = [ + dailyStarterHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }]; + dailyStarterHeldItemPool[RarityTier.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] = [ + dailyStarterHeldItemPool[RarityTier.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] = [ + dailyStarterHeldItemPool[RarityTier.MASTER] = [ { entry: HeldItemId.LEFTOVERS, weight: 1 }, { entry: HeldItemId.SHELL_BELL, weight: 1 }, ]; @@ -76,5 +76,5 @@ export function initHeldItemPools() { // Default held item pools for specific scenarios initWildHeldItemPool(); initTrainerHeldItemPool(); - initDailyStarterModifierPool(); + initDailyStarterRewardPool(); } diff --git a/src/modifier/init-modifier-pools.ts b/src/items/init-reward-pools.ts similarity index 67% rename from src/modifier/init-modifier-pools.ts rename to src/items/init-reward-pools.ts index 88ebfec5a4d..ec1f8ee50ff 100644 --- a/src/modifier/init-modifier-pools.ts +++ b/src/items/init-reward-pools.ts @@ -1,37 +1,37 @@ /* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ -import type { initModifierTypes } from "#modifiers/modifier-type"; +import type { initRewards, Reward } from "#items/reward"; /* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */ import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; import { pokemonEvolutions } from "#balance/pokemon-evolutions"; -import { allHeldItems, allTrainerItems, modifierTypes } from "#data/data-lists"; +import { allHeldItems, allRewards, allTrainerItems } from "#data/data-lists"; import { MAX_PER_TYPE_POKEBALLS } from "#data/pokeball"; import { AbilityId } from "#enums/ability-id"; import { HeldItemId } from "#enums/held-item-id"; import { MoveId } from "#enums/move-id"; import { PokeballType } from "#enums/pokeball"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { StatusEffect } from "#enums/status-effect"; import { TrainerItemId } from "#enums/trainer-item-id"; import { Unlockables } from "#enums/unlockables"; import type { Pokemon } from "#field/pokemon"; +import { WeightedReward } from "#items/reward"; +import { rewardPool } from "#items/reward-pools"; import type { TurnEndStatusHeldItem } from "#items/turn-end-status"; -import { modifierPool } from "#modifiers/modifier-pools"; -import { WeightedModifierType } from "#modifiers/modifier-type"; -import type { WeightedModifierTypeWeightFunc } from "#types/modifier-types"; +import type { WeightedRewardWeightFunc } from "#types/rewards"; import { isNullOrUndefined } from "#utils/common"; /** * Initialize the common modifier pool */ -function initCommonModifierPool() { - modifierPool[RewardTier.COMMON] = [ - new WeightedModifierType(modifierTypes.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6), - new WeightedModifierType(modifierTypes.RARE_CANDY, 2), - new WeightedModifierType( - modifierTypes.POTION, +function initCommonRewardPool() { + rewardPool[RarityTier.COMMON] = [ + new WeightedReward(allRewards.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6), + new WeightedReward(allRewards.RARE_CANDY, 2), + new WeightedReward( + allRewards.POTION, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter(p => p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875 && !p.isFainted()).length, @@ -41,8 +41,8 @@ function initCommonModifierPool() { }, 9, ), - new WeightedModifierType( - modifierTypes.SUPER_POTION, + new WeightedReward( + allRewards.SUPER_POTION, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter(p => p.getInverseHp() >= 25 && p.getHpRatio() <= 0.75 && !p.isFainted()).length, @@ -52,8 +52,8 @@ function initCommonModifierPool() { }, 3, ), - new WeightedModifierType( - modifierTypes.ETHER, + new WeightedReward( + allRewards.ETHER, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter( @@ -71,8 +71,8 @@ function initCommonModifierPool() { }, 9, ), - new WeightedModifierType( - modifierTypes.MAX_ETHER, + new WeightedReward( + allRewards.MAX_ETHER, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter( @@ -90,25 +90,22 @@ function initCommonModifierPool() { }, 3, ), - new WeightedModifierType(modifierTypes.LURE, lureWeightFunc(TrainerItemId.LURE, 2)), - new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4), - new WeightedModifierType(modifierTypes.BERRY, 2), - new WeightedModifierType(modifierTypes.TM_COMMON, 2), - ].map(m => { - m.setTier(RewardTier.COMMON); - return m; - }); + new WeightedReward(allRewards.LURE, lureWeightFunc(TrainerItemId.LURE, 2)), + new WeightedReward(allRewards.TEMP_STAT_STAGE_BOOSTER, 4), + new WeightedReward(allRewards.BERRY, 2), + new WeightedReward(allRewards.TM_COMMON, 2), + ]; } /** * Initialize the Great modifier pool */ -function initGreatModifierPool() { - modifierPool[RewardTier.GREAT] = [ - new WeightedModifierType(modifierTypes.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6), - new WeightedModifierType(modifierTypes.PP_UP, 2), - new WeightedModifierType( - modifierTypes.FULL_HEAL, +function initGreatRewardPool() { + rewardPool[RarityTier.GREAT] = [ + new WeightedReward(allRewards.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6), + new WeightedReward(allRewards.PP_UP, 2), + new WeightedReward( + allRewards.FULL_HEAL, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min( party.filter( @@ -126,31 +123,31 @@ function initGreatModifierPool() { }, 18, ), - new WeightedModifierType( - modifierTypes.REVIVE, + new WeightedReward( + allRewards.REVIVE, (party: Pokemon[]) => { const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); return faintedPartyMemberCount * 9; }, 27, ), - new WeightedModifierType( - modifierTypes.MAX_REVIVE, + new WeightedReward( + allRewards.MAX_REVIVE, (party: Pokemon[]) => { const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); return faintedPartyMemberCount * 3; }, 9, ), - new WeightedModifierType( - modifierTypes.SACRED_ASH, + new WeightedReward( + allRewards.SACRED_ASH, (party: Pokemon[]) => { return party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0; }, 1, ), - new WeightedModifierType( - modifierTypes.HYPER_POTION, + new WeightedReward( + allRewards.HYPER_POTION, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.625 && !p.isFainted()).length, @@ -160,8 +157,8 @@ function initGreatModifierPool() { }, 9, ), - new WeightedModifierType( - modifierTypes.MAX_POTION, + new WeightedReward( + allRewards.MAX_POTION, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length, @@ -171,8 +168,8 @@ function initGreatModifierPool() { }, 3, ), - new WeightedModifierType( - modifierTypes.FULL_RESTORE, + new WeightedReward( + allRewards.FULL_RESTORE, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min( party.filter( @@ -195,8 +192,8 @@ function initGreatModifierPool() { }, 3, ), - new WeightedModifierType( - modifierTypes.ELIXIR, + new WeightedReward( + allRewards.ELIXIR, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter( @@ -214,8 +211,8 @@ function initGreatModifierPool() { }, 9, ), - new WeightedModifierType( - modifierTypes.MAX_ELIXIR, + new WeightedReward( + allRewards.MAX_ELIXIR, (party: Pokemon[]) => { const thresholdPartyMemberCount = Math.min( party.filter( @@ -233,26 +230,26 @@ function initGreatModifierPool() { }, 3, ), - new WeightedModifierType(modifierTypes.DIRE_HIT, 4), - new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(TrainerItemId.SUPER_LURE, 4)), - new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)), - new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4), - new WeightedModifierType( - modifierTypes.EVOLUTION_ITEM, + new WeightedReward(allRewards.DIRE_HIT, 4), + new WeightedReward(allRewards.SUPER_LURE, lureWeightFunc(TrainerItemId.SUPER_LURE, 4)), + new WeightedReward(allRewards.NUGGET, skipInLastClassicWaveOrDefault(5)), + new WeightedReward(allRewards.SPECIES_STAT_BOOSTER, 4), + new WeightedReward( + allRewards.EVOLUTION_ITEM, () => { return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8); }, 8, ), - new WeightedModifierType( - modifierTypes.MAP, + new WeightedReward( + allRewards.MAP, () => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0), 2, ), - new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2), - new WeightedModifierType(modifierTypes.TM_GREAT, 3), - new WeightedModifierType( - modifierTypes.MEMORY_MUSHROOM, + new WeightedReward(allRewards.SOOTHE_BELL, 2), + new WeightedReward(allRewards.TM_GREAT, 3), + new WeightedReward( + allRewards.MEMORY_MUSHROOM, (party: Pokemon[]) => { if (!party.find(p => p.getLearnableLevelMoves().length)) { return 0; @@ -264,8 +261,8 @@ function initGreatModifierPool() { }, 4, ), - new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), - new WeightedModifierType(modifierTypes.TERA_SHARD, (party: Pokemon[]) => + new WeightedReward(allRewards.BASE_STAT_BOOSTER, 3), + new WeightedReward(allRewards.TERA_SHARD, (party: Pokemon[]) => party.filter( p => !(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)), @@ -273,8 +270,8 @@ function initGreatModifierPool() { ? 1 : 0, ), - new WeightedModifierType( - modifierTypes.DNA_SPLICERS, + new WeightedReward( + allRewards.DNA_SPLICERS, (party: Pokemon[]) => { if (party.filter(p => !p.fusionSpecies).length > 1) { if (globalScene.gameMode.isSplicedOnly) { @@ -288,39 +285,36 @@ function initGreatModifierPool() { }, 4, ), - new WeightedModifierType( - modifierTypes.VOUCHER, + new WeightedReward( + allRewards.VOUCHER, (_party: Pokemon[], rerollCount: number) => (!globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0), 1, ), - ].map(m => { - m.setTier(RewardTier.GREAT); - return m; - }); + ]; } /** * Initialize the Ultra modifier pool */ -function initUltraModifierPool() { - modifierPool[RewardTier.ULTRA] = [ - new WeightedModifierType(modifierTypes.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15), - new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(TrainerItemId.MAX_LURE, 4)), - new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), - new WeightedModifierType(modifierTypes.PP_MAX, 3), - new WeightedModifierType(modifierTypes.MINT, 4), - new WeightedModifierType( - modifierTypes.RARE_EVOLUTION_ITEM, +function initUltraRewardPool() { + rewardPool[RarityTier.ULTRA] = [ + new WeightedReward(allRewards.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15), + new WeightedReward(allRewards.MAX_LURE, lureWeightFunc(TrainerItemId.MAX_LURE, 4)), + new WeightedReward(allRewards.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), + new WeightedReward(allRewards.PP_MAX, 3), + new WeightedReward(allRewards.MINT, 4), + new WeightedReward( + allRewards.RARE_EVOLUTION_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32), 32, ), - new WeightedModifierType( - modifierTypes.FORM_CHANGE_ITEM, + new WeightedReward( + allRewards.FORM_CHANGE_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, 24, ), - new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), - new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { + new WeightedReward(allRewards.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), + new WeightedReward(allRewards.EVIOLITE, (party: Pokemon[]) => { const { gameMode, gameData } = globalScene; if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { return party.some(p => { @@ -340,9 +334,9 @@ function initUltraModifierPool() { } return 0; }), - new WeightedModifierType(modifierTypes.RARE_SPECIES_STAT_BOOSTER, 12), - new WeightedModifierType( - modifierTypes.LEEK, + new WeightedReward(allRewards.RARE_SPECIES_STAT_BOOSTER, 12), + new WeightedReward( + allRewards.LEEK, (party: Pokemon[]) => { const checkedSpecies = [SpeciesId.FARFETCHD, SpeciesId.GALAR_FARFETCHD, SpeciesId.SIRFETCHD]; // If a party member doesn't already have a Leek and is one of the relevant species, Leek can appear @@ -357,8 +351,8 @@ function initUltraModifierPool() { }, 12, ), - new WeightedModifierType( - modifierTypes.TOXIC_ORB, + new WeightedReward( + allRewards.TOXIC_ORB, (party: Pokemon[]) => { return party.some(p => { const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]); @@ -403,8 +397,8 @@ function initUltraModifierPool() { }, 10, ), - new WeightedModifierType( - modifierTypes.FLAME_ORB, + new WeightedReward( + allRewards.FLAME_ORB, (party: Pokemon[]) => { return party.some(p => { const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]); @@ -449,8 +443,8 @@ function initUltraModifierPool() { }, 10, ), - new WeightedModifierType( - modifierTypes.MYSTICAL_ROCK, + new WeightedReward( + allRewards.MYSTICAL_ROCK, (party: Pokemon[]) => { return party.some(p => { const stack = p.heldItemManager.getStack(HeldItemId.MYSTICAL_ROCK); @@ -496,94 +490,88 @@ function initUltraModifierPool() { }, 10, ), - new WeightedModifierType(modifierTypes.REVIVER_SEED, 4), - new WeightedModifierType(modifierTypes.CANDY_JAR, skipInLastClassicWaveOrDefault(5)), - new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 9), - new WeightedModifierType(modifierTypes.TM_ULTRA, 11), - new WeightedModifierType(modifierTypes.RARER_CANDY, 4), - new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)), - new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), - new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), - new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), - new WeightedModifierType( - modifierTypes.TERA_ORB, + new WeightedReward(allRewards.REVIVER_SEED, 4), + new WeightedReward(allRewards.CANDY_JAR, skipInLastClassicWaveOrDefault(5)), + new WeightedReward(allRewards.ATTACK_TYPE_BOOSTER, 9), + new WeightedReward(allRewards.TM_ULTRA, 11), + new WeightedReward(allRewards.RARER_CANDY, 4), + new WeightedReward(allRewards.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)), + new WeightedReward(allRewards.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), + new WeightedReward(allRewards.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), + new WeightedReward(allRewards.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), + new WeightedReward( + allRewards.TERA_ORB, () => !globalScene.gameMode.isClassic ? Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4) : 0, 4, ), - new WeightedModifierType(modifierTypes.QUICK_CLAW, 3), - new WeightedModifierType(modifierTypes.WIDE_LENS, 7), - ].map(m => { - m.setTier(RewardTier.ULTRA); - return m; - }); + new WeightedReward(allRewards.QUICK_CLAW, 3), + new WeightedReward(allRewards.WIDE_LENS, 7), + ]; } -function initRogueModifierPool() { - modifierPool[RewardTier.ROGUE] = [ - new WeightedModifierType(modifierTypes.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16), - new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), - new WeightedModifierType(modifierTypes.LEFTOVERS, 3), - new WeightedModifierType(modifierTypes.SHELL_BELL, 3), - new WeightedModifierType(modifierTypes.BERRY_POUCH, 4), - new WeightedModifierType(modifierTypes.GRIP_CLAW, 5), - new WeightedModifierType(modifierTypes.SCOPE_LENS, 4), - new WeightedModifierType(modifierTypes.BATON, 2), - new WeightedModifierType(modifierTypes.SOUL_DEW, 7), - new WeightedModifierType(modifierTypes.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4), - new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), - new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), - new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), - new WeightedModifierType(modifierTypes.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)), - new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), - new WeightedModifierType( - modifierTypes.RARE_FORM_CHANGE_ITEM, +function initRogueRewardPool() { + rewardPool[RarityTier.ROGUE] = [ + new WeightedReward(allRewards.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16), + new WeightedReward(allRewards.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), + new WeightedReward(allRewards.LEFTOVERS, 3), + new WeightedReward(allRewards.SHELL_BELL, 3), + new WeightedReward(allRewards.BERRY_POUCH, 4), + new WeightedReward(allRewards.GRIP_CLAW, 5), + new WeightedReward(allRewards.SCOPE_LENS, 4), + new WeightedReward(allRewards.BATON, 2), + new WeightedReward(allRewards.SOUL_DEW, 7), + new WeightedReward(allRewards.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4), + new WeightedReward(allRewards.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), + new WeightedReward(allRewards.FOCUS_BAND, 5), + new WeightedReward(allRewards.KINGS_ROCK, 3), + new WeightedReward(allRewards.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)), + new WeightedReward(allRewards.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), + new WeightedReward( + allRewards.RARE_FORM_CHANGE_ITEM, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, 24, ), - new WeightedModifierType( - modifierTypes.MEGA_BRACELET, + new WeightedReward( + allRewards.MEGA_BRACELET, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, 36, ), - new WeightedModifierType( - modifierTypes.DYNAMAX_BAND, + new WeightedReward( + allRewards.DYNAMAX_BAND, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, 36, ), - new WeightedModifierType( - modifierTypes.VOUCHER_PLUS, + new WeightedReward( + allRewards.VOUCHER_PLUS, (_party: Pokemon[], rerollCount: number) => !globalScene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3, ), - ].map(m => { - m.setTier(RewardTier.ROGUE); - return m; - }); + ]; } /** * Initialize the Master modifier pool */ -function initMasterModifierPool() { - modifierPool[RewardTier.MASTER] = [ - new WeightedModifierType(modifierTypes.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24), - new WeightedModifierType(modifierTypes.SHINY_CHARM, 14), - new WeightedModifierType(modifierTypes.HEALING_CHARM, 18), - new WeightedModifierType(modifierTypes.MULTI_LENS, 18), - new WeightedModifierType( - modifierTypes.VOUCHER_PREMIUM, +function initMasterRewardPool() { + rewardPool[RarityTier.MASTER] = [ + new WeightedReward(allRewards.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24), + new WeightedReward(allRewards.SHINY_CHARM, 14), + new WeightedReward(allRewards.HEALING_CHARM, 18), + new WeightedReward(allRewards.MULTI_LENS, 18), + new WeightedReward( + allRewards.VOUCHER_PREMIUM, (_party: Pokemon[], rerollCount: number) => !globalScene.gameMode.isDaily && !globalScene.gameMode.isEndless && !globalScene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5, ), - new WeightedModifierType( - modifierTypes.DNA_SPLICERS, + new WeightedReward( + allRewards.DNA_SPLICERS, (party: Pokemon[]) => !(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) && !globalScene.gameMode.isSplicedOnly && @@ -592,8 +580,8 @@ function initMasterModifierPool() { : 0, 24, ), - new WeightedModifierType( - modifierTypes.MINI_BLACK_HOLE, + new WeightedReward( + allRewards.MINI_BLACK_HOLE, () => globalScene.gameMode.isDaily || (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE)) @@ -601,33 +589,30 @@ function initMasterModifierPool() { : 0, 1, ), - ].map(m => { - m.setTier(RewardTier.MASTER); - return m; - }); + ]; } /** - * Initialize {@linkcode modifierPool} with the initial set of modifier types. - * {@linkcode initModifierTypes} MUST be called before this function. + * Initialize {@linkcode rewardPool} with the initial set of modifier types. + * {@linkcode initRewards} MUST be called before this function. */ -export function initModifierPools() { +export function initRewardPools() { // The modifier pools the player chooses from during modifier selection - initCommonModifierPool(); - initGreatModifierPool(); - initUltraModifierPool(); - initRogueModifierPool(); - initMasterModifierPool(); + initCommonRewardPool(); + initGreatRewardPool(); + initUltraRewardPool(); + initRogueRewardPool(); + initMasterRewardPool(); } /** - * High order function that returns a WeightedModifierTypeWeightFunc that will only be applied on - * classic and skip an ModifierType if current wave is greater or equal to the one passed down + * High order function that returns a WeightedRewardWeightFunc that will only be applied on + * classic and skip an Reward if current wave is greater or equal to the one passed down * @param wave - Wave where we should stop showing the modifier - * @param defaultWeight - ModifierType default weight - * @returns A WeightedModifierTypeWeightFunc + * @param defaultWeight - Reward default weight + * @returns A WeightedRewardWeightFunc */ -function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedModifierTypeWeightFunc { +function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedRewardWeightFunc { return () => { const gameMode = globalScene.gameMode; const currentWave = globalScene.currentBattle.waveIndex; @@ -636,23 +621,23 @@ function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedMo } /** - * High order function that returns a WeightedModifierTypeWeightFunc that will only be applied on - * classic and it will skip a ModifierType if it is the last wave pull. - * @param defaultWeight ModifierType default weight - * @returns A WeightedModifierTypeWeightFunc + * High order function that returns a WeightedRewardWeightFunc that will only be applied on + * classic and it will skip a Reward if it is the last wave pull. + * @param defaultWeight Reward default weight + * @returns A WeightedRewardWeightFunc */ -function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedModifierTypeWeightFunc { +function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedRewardWeightFunc { return skipInClassicAfterWave(199, defaultWeight); } /** - * High order function that returns a WeightedModifierTypeWeightFunc to ensure Lures don't spawn on Classic 199 + * High order function that returns a WeightedRewardWeightFunc to ensure Lures don't spawn on Classic 199 * or if the lure still has over 60% of its duration left * @param lureId The id of the lure type in question. * @param weight The desired weight for the lure when it does spawn - * @returns A WeightedModifierTypeWeightFunc + * @returns A WeightedRewardWeightFunc */ -function lureWeightFunc(lureId: TrainerItemId, weight: number): WeightedModifierTypeWeightFunc { +function lureWeightFunc(lureId: TrainerItemId, weight: number): WeightedRewardWeightFunc { return () => { const lureCount = globalScene.trainerItems.getStack(lureId); return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) && diff --git a/src/items/init-trainer-item-pools.ts b/src/items/init-trainer-item-pools.ts index 1a3e5ccf27c..3b8de62f0bc 100644 --- a/src/items/init-trainer-item-pools.ts +++ b/src/items/init-trainer-item-pools.ts @@ -1,4 +1,4 @@ -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { TrainerItemId } from "#enums/trainer-item-id"; import { enemyBuffTokenPool } from "#items/trainer-item-pool"; @@ -6,7 +6,7 @@ import { enemyBuffTokenPool } from "#items/trainer-item-pool"; * Initialize the enemy buff modifier pool */ function initEnemyBuffTokenPool() { - enemyBuffTokenPool[RewardTier.COMMON] = [ + enemyBuffTokenPool[RarityTier.COMMON] = [ { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 9 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 9 }, { entry: TrainerItemId.ENEMY_ATTACK_POISON_CHANCE, weight: 3 }, @@ -16,14 +16,14 @@ function initEnemyBuffTokenPool() { { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 4 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 }, ]; - enemyBuffTokenPool[RewardTier.GREAT] = [ + enemyBuffTokenPool[RarityTier.GREAT] = [ { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 5 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 5 }, { entry: TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE, weight: 5 }, { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 5 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 }, ]; - enemyBuffTokenPool[RewardTier.ULTRA] = [ + enemyBuffTokenPool[RarityTier.ULTRA] = [ { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 10 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 10 }, { entry: TrainerItemId.ENEMY_HEAL, weight: 10 }, @@ -31,8 +31,8 @@ function initEnemyBuffTokenPool() { { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 10 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 5 }, ]; - enemyBuffTokenPool[RewardTier.ROGUE] = []; - enemyBuffTokenPool[RewardTier.MASTER] = []; + enemyBuffTokenPool[RarityTier.ROGUE] = []; + enemyBuffTokenPool[RarityTier.MASTER] = []; } export function initTrainerItemPools() { diff --git a/src/items/item-overrides.ts b/src/items/item-overrides.ts new file mode 100644 index 00000000000..8e179d618fa --- /dev/null +++ b/src/items/item-overrides.ts @@ -0,0 +1,50 @@ +import { globalScene } from "#app/global-scene"; +import Overrides from "#app/overrides"; +import type { Pokemon } from "#field/pokemon"; +import type { HeldItemConfiguration } from "#items/held-item-data-types"; +import { assignItemsFromConfiguration } from "#items/held-item-pool"; +import type { TrainerItemConfiguration } from "#items/trainer-item-data-types"; + +/** + * Uses either `MODIFIER_OVERRIDE` in overrides.ts to set {@linkcode PersistentModifier}s for either: + * - The player + * - The enemy + * @param isPlayer {@linkcode boolean} for whether the player (`true`) or enemy (`false`) is being overridden + */ +export function overrideTrainerItems(isPlayer = true): void { + const trainerItemsOverride: TrainerItemConfiguration = isPlayer + ? Overrides.STARTING_TRAINER_ITEMS_OVERRIDE + : Overrides.OPP_TRAINER_ITEMS_OVERRIDE; + if (!trainerItemsOverride || trainerItemsOverride.length === 0 || !globalScene) { + return; + } + + // If it's the opponent, clear all of their current modifiers to avoid stacking + if (!isPlayer) { + globalScene.clearEnemyItems(); + } + + globalScene.assignTrainerItemsFromConfiguration(trainerItemsOverride, isPlayer); +} + +/** + * Uses either `HELD_ITEMS_OVERRIDE` in overrides.ts to set {@linkcode PokemonHeldItemModifier}s for either: + * - The first member of the player's team when starting a new game + * - An enemy {@linkcode Pokemon} being spawned in + * @param pokemon {@linkcode Pokemon} whose held items are being overridden + * @param isPlayer {@linkcode boolean} for whether the {@linkcode pokemon} is the player's (`true`) or an enemy (`false`) + */ +export function overrideHeldItems(pokemon: Pokemon, isPlayer = true): void { + const heldItemsOverride: HeldItemConfiguration = isPlayer + ? Overrides.STARTING_HELD_ITEMS_OVERRIDE + : Overrides.OPP_HELD_ITEMS_OVERRIDE; + if (!heldItemsOverride || heldItemsOverride.length === 0 || !globalScene) { + return; + } + + if (!isPlayer) { + pokemon.heldItemManager.clearItems(); + } + + assignItemsFromConfiguration(heldItemsOverride, pokemon); +} diff --git a/src/items/reward-defaults-tiers.ts b/src/items/reward-defaults-tiers.ts new file mode 100644 index 00000000000..2c720d97405 --- /dev/null +++ b/src/items/reward-defaults-tiers.ts @@ -0,0 +1,72 @@ +import { RewardId } from "#enums/reward-id"; +import { RarityTier } from "#enums/reward-tier"; + +export const rewardRarities = { + [RewardId.POKEBALL]: RarityTier.COMMON, + [RewardId.GREAT_BALL]: RarityTier.GREAT, + [RewardId.ULTRA_BALL]: RarityTier.ULTRA, + [RewardId.ROGUE_BALL]: RarityTier.ROGUE, + [RewardId.MASTER_BALL]: RarityTier.MASTER, + + [RewardId.VOUCHER]: RarityTier.GREAT, + [RewardId.VOUCHER_PLUS]: RarityTier.ROGUE, + [RewardId.VOUCHER_PREMIUM]: RarityTier.MASTER, + + [RewardId.NUGGET]: RarityTier.GREAT, + [RewardId.BIG_NUGGET]: RarityTier.ULTRA, + [RewardId.RELIC_GOLD]: RarityTier.ROGUE, + + [RewardId.RARE_CANDY]: RarityTier.COMMON, + [RewardId.RARER_CANDY]: RarityTier.ULTRA, + + [RewardId.EVOLUTION_ITEM]: RarityTier.GREAT, + [RewardId.RARE_EVOLUTION_ITEM]: RarityTier.ULTRA, + + [RewardId.POTION]: RarityTier.COMMON, + [RewardId.SUPER_POTION]: RarityTier.COMMON, + [RewardId.HYPER_POTION]: RarityTier.GREAT, + [RewardId.MAX_POTION]: RarityTier.GREAT, + [RewardId.FULL_HEAL]: RarityTier.GREAT, + [RewardId.FULL_RESTORE]: RarityTier.GREAT, + + [RewardId.REVIVE]: RarityTier.GREAT, + [RewardId.MAX_REVIVE]: RarityTier.GREAT, + [RewardId.SACRED_ASH]: RarityTier.GREAT, + + [RewardId.ETHER]: RarityTier.COMMON, + [RewardId.MAX_ETHER]: RarityTier.COMMON, + + [RewardId.ELIXIR]: RarityTier.GREAT, + [RewardId.MAX_ELIXIR]: RarityTier.GREAT, + + [RewardId.PP_UP]: RarityTier.GREAT, + [RewardId.PP_MAX]: RarityTier.ULTRA, + + [RewardId.TM_COMMON]: RarityTier.COMMON, + [RewardId.TM_GREAT]: RarityTier.GREAT, + [RewardId.TM_ULTRA]: RarityTier.ULTRA, + + [RewardId.MINT]: RarityTier.ULTRA, + [RewardId.TERA_SHARD]: RarityTier.GREAT, + [RewardId.MEMORY_MUSHROOM]: RarityTier.GREAT, + [RewardId.DNA_SPLICERS]: RarityTier.MASTER, + + [RewardId.SPECIES_STAT_BOOSTER]: RarityTier.GREAT, + [RewardId.RARE_SPECIES_STAT_BOOSTER]: RarityTier.ULTRA, + [RewardId.BASE_STAT_BOOSTER]: RarityTier.GREAT, + [RewardId.ATTACK_TYPE_BOOSTER]: RarityTier.ULTRA, + [RewardId.BERRY]: RarityTier.COMMON, + + [RewardId.TEMP_STAT_STAGE_BOOSTER]: RarityTier.COMMON, + [RewardId.LURE]: RarityTier.COMMON, + [RewardId.SUPER_LURE]: RarityTier.GREAT, + [RewardId.MAX_LURE]: RarityTier.ULTRA, + + [RewardId.FORM_CHANGE_ITEM]: RarityTier.ULTRA, + [RewardId.RARE_FORM_CHANGE_ITEM]: RarityTier.ROGUE, +}; + +export function getRewardTier(reward: RewardId): RarityTier { + const tier = rewardRarities[reward]; + return tier ?? RarityTier.LUXURY; +} diff --git a/src/items/reward-pool-utils.ts b/src/items/reward-pool-utils.ts new file mode 100644 index 00000000000..7a9886a76f2 --- /dev/null +++ b/src/items/reward-pool-utils.ts @@ -0,0 +1,340 @@ +import { globalScene } from "#app/global-scene"; +import Overrides from "#app/overrides"; +import { allRewards } from "#data/data-lists"; +import { RewardPoolType } from "#enums/reward-pool-type"; +import { RarityTier } from "#enums/reward-tier"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import type { RewardFunc, RewardPool, RewardPoolWeights } from "#types/rewards"; +import { isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common"; +import { getPartyLuckValue } from "#utils/party"; +import { type Reward, RewardGenerator, RewardOption, type RewardOverride, TrainerItemReward } from "./reward"; +import { rewardPool, rewardPoolWeights } from "./reward-pools"; +import { getRewardDefaultTier } from "./reward-utils"; + +/* +This file still contains several functions to generate rewards from pools. The hierarchy of these functions is explained here. + +At the top of the food chain is `generatePlayerRewardOptions`, which is responsible for creating item rewards for the player. +It can take a `CustomRewardSettings` to fix any number of rewards or tiers, then fills the remaining spots randomly. +Note that this function generates `RewardOption` instances, not yet `Reward`s. +Currently, there is only one reward pool, but in the future we will want to allow for custom pools. + +The function `getNewRewardOption` is responsible for generating a single RewardOption from a given pool and set of weights. +Note that, in the previous system, this function could in principle generate rewards for enemies, which was used in some +cases to assign modifiers. This usage is now deprecated, as we have separate pools for held items and trainer items for enemies. + +However, `getNewRewardOption` is not called directly by `generatePlayerRewardOptions`. Instead, it is filtered +by `getRewardOptionWithRetry`, which also checks existing rewards to minimize the chance of duplicates. + +Note that the pool contains `WeightedReward` instances, which contain either a `Reward` or a `RewardGenerator`. +Once a pool entry is chosen, a specific `Reward` is generated accordingly and put in the returned `RewardOption`. + +This will allow more customization in creating pools for challenges, MEs etc. +*/ + +export interface CustomRewardSettings { + guaranteedRarityTiers?: RarityTier[]; + guaranteedRewardOptions?: RewardOption[]; + /** If specified, will override the next X items to be auto-generated from specific reward functions (these don't have to be pre-genned). */ + guaranteedRewardFuncs?: RewardFunc[]; + /** + * If set to `true`, will fill the remainder of shop items that were not overridden by the 3 options above, up to the `count` param value. + * @example + * ```ts + * count = 4; + * customRewardSettings = { guaranteedRarityTiers: [RarityTier.GREAT], fillRemaining: true }; + * ``` + * The first item in the shop will be `GREAT` tier, and the remaining `3` items will be generated normally. + * + * If `fillRemaining: false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of the value of `count`). + * @defaultValue `false` + */ + fillRemaining?: boolean; + /** If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all. */ + rerollMultiplier?: number; + /** + * If `false`, will prevent set item tiers from upgrading via luck. + * @defaultValue `true` + */ + allowLuckUpgrades?: boolean; +} + +/** + * Generates weights for a {@linkcode RewardPool}. An array of weights is generated for each rarity tier. Weights can be 0. + * @param pool - The pool for which weights must be generated + * @param party - Party is required for generating the weights + * @param rerollCount - (Optional) Needed for weights of vouchers. + */ +export function generateRewardPoolWeights(pool: RewardPool, party: Pokemon[], rerollCount = 0) { + for (const tier of Object.keys(pool)) { + const poolWeights = pool[tier].map(w => { + if (w.reward instanceof TrainerItemReward) { + const id = w.reward.itemId; + if (globalScene.trainerItems.isMaxStack(id)) { + return 0; + } + } + if (typeof w.weight === "number") { + return w.weight; + } + return w.weight(party, rerollCount); + }); + rewardPoolWeights[tier] = poolWeights; + } +} + +/** + * Generates a random RarityTier to draw rewards from the pool. The probabilities are: + * 1/1024 (Master tier) + * 12/1024 (Rogue tier) + * 48/1024 (Ultra tier) + * 195/1024 (Great tier) + * 768/1024 (Common tier) + * return {@linkcode RarityTier} + */ +function randomBaseTier(): RarityTier { + const tierValue = randSeedInt(1024); + + if (tierValue > 255) { + return RarityTier.COMMON; + } + if (tierValue > 60) { + return RarityTier.GREAT; + } + if (tierValue > 12) { + return RarityTier.ULTRA; + } + if (tierValue) { + return RarityTier.ROGUE; + } + return RarityTier.MASTER; +} + +/** + * Determines the upgrade count for a given rarity tier, based on the party luck. Will not update + * if the pool would have no entries at the new rarity. + * @param pool - RewardPool from which the reward will be generated + * @param baseTier - The initial tier to upgrade + * @param party - Party of the trainer using the item + * return {@linkcode RarityTier} + */ +function getRarityUpgradeCount(pool: RewardPool, baseTier: RarityTier, party: Pokemon[]): RarityTier { + let upgradeCount = 0; + if (baseTier < RarityTier.MASTER) { + const partyLuckValue = getPartyLuckValue(party); + const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4)); + while (pool.hasOwnProperty(baseTier + upgradeCount + 1) && pool[baseTier + upgradeCount + 1].length) { + if (randSeedInt(upgradeOdds) < 4) { + upgradeCount++; + } else { + break; + } + } + } + return upgradeCount; +} + +/** + * Generates reward options for a {@linkcode SelectRewardPhase} + * @param count - Determines the number of items to generate + * @param party - Party is required for generating proper reward pools + * @param rarityTiers - (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule. + * @param customRewardSettings - (Optional) See {@linkcode CustomRewardSettings} + */ +export function generatePlayerRewardOptions( + count: number, + party: PlayerPokemon[], + rarityTiers?: RarityTier[], + customRewardSettings?: CustomRewardSettings, +): RewardOption[] { + const options: RewardOption[] = []; + const retryCount = Math.min(count * 5, 50); + // TODO: Change this to allow for custom reward pools + const pool = getRewardPoolForType(RewardPoolType.PLAYER); + const weights = getRewardWeightsForType(RewardPoolType.PLAYER); + if (!customRewardSettings) { + for (let i = 0; i < count; i++) { + const tier = rarityTiers && rarityTiers.length > i ? rarityTiers[i] : undefined; + options.push(getRewardOptionWithRetry(pool, weights, options, retryCount, party, tier)); + } + } else { + // Guaranteed mod options first + if (customRewardSettings?.guaranteedRewardOptions && customRewardSettings.guaranteedRewardOptions.length > 0) { + options.push(...customRewardSettings.guaranteedRewardOptions!); + } + + // Guaranteed mod functions second + if (customRewardSettings.guaranteedRewardFuncs && customRewardSettings.guaranteedRewardFuncs.length > 0) { + customRewardSettings.guaranteedRewardFuncs!.forEach((mod, _i) => { + const rewardId = Object.keys(allRewards).find(k => allRewards[k] === mod) as string; + const guaranteedMod: Reward = allRewards[rewardId]?.(); + + // Populates item id and tier + const guaranteedModTier = getRewardDefaultTier(guaranteedMod); + + const modType = guaranteedMod instanceof RewardGenerator ? guaranteedMod.generateReward(party) : guaranteedMod; + if (modType) { + const option = new RewardOption(modType, 0, guaranteedModTier); + options.push(option); + } + }); + } + + // Guaranteed tiers third + if (customRewardSettings.guaranteedRarityTiers && customRewardSettings.guaranteedRarityTiers.length > 0) { + const allowLuckUpgrades = customRewardSettings.allowLuckUpgrades ?? true; + for (const tier of customRewardSettings.guaranteedRarityTiers) { + options.push(getRewardOptionWithRetry(pool, weights, options, retryCount, party, tier, allowLuckUpgrades)); + } + } + + // Fill remaining + if (options.length < count && customRewardSettings.fillRemaining) { + while (options.length < count) { + options.push(getRewardOptionWithRetry(pool, weights, options, retryCount, party, undefined)); + } + } + } + + // Applies overrides for testing + overridePlayerRewardOptions(options, party); + + return options; +} + +/** + * Will generate a {@linkcode RewardOption} from the {@linkcode RewardPoolType.PLAYER} pool, attempting to retry duplicated items up to retryCount + * @param pool - {@linkcode RewardPool} to generate items from + * @param weights - {@linkcode RewardPoolWeights} to use when generating items + * @param existingOptions Currently generated options + * @param retryCount How many times to retry before allowing a dupe item + * @param party Current player party, used to calculate items in the pool + * @param tier If specified will generate item of tier + * @param allowLuckUpgrades `true` to allow items to upgrade tiers (the little animation that plays and is affected by luck) + */ +function getRewardOptionWithRetry( + pool: RewardPool, + weights: RewardPoolWeights, + existingOptions: RewardOption[], + retryCount: number, + party: PlayerPokemon[], + tier?: RarityTier, + allowLuckUpgrades?: boolean, +): RewardOption { + allowLuckUpgrades = allowLuckUpgrades ?? true; + let candidate = getNewRewardOption(pool, weights, party, tier, undefined, 0, allowLuckUpgrades); + let r = 0; + while ( + existingOptions.length && + ++r < retryCount && + //TODO: Improve this condition to refine what counts as a dupe + existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length + ) { + console.log("Retry count:", r); + console.log(candidate?.type.group); + console.log(candidate?.type.name); + console.log(existingOptions.filter(o => o.type.name === candidate?.type.name).length); + console.log(existingOptions.filter(o => o.type.group === candidate?.type.group).length); + candidate = getNewRewardOption( + pool, + weights, + party, + candidate?.type.tier ?? tier, + candidate?.upgradeCount, + 0, + allowLuckUpgrades, + ); + } + return candidate!; +} + +/** + * Generates a Reward from the specified pool + * @param pool - {@linkcode RewardPool} to generate items from + * @param weights - {@linkcode RewardPoolWeights} to use when generating items + * @param party - party of the trainer using the item + * @param baseTier - If specified, will override the initial tier of an item (can still upgrade with luck) + * @param upgradeCount - If defined, means that this is a new Reward being generated to override another via luck upgrade. Used for recursive logic + * @param retryCount - Max allowed tries before the next tier down is checked for a valid Reward + * @param allowLuckUpgrades - Default true. If false, will not allow Reward to randomly upgrade to next tier + */ +function getNewRewardOption( + pool: RewardPool, + weights: RewardPoolWeights, + party: PlayerPokemon[], + baseTier?: RarityTier, + upgradeCount?: number, + retryCount = 0, + allowLuckUpgrades = true, +): RewardOption | null { + let tier = 0; + if (isNullOrUndefined(baseTier)) { + baseTier = randomBaseTier(); + } + if (isNullOrUndefined(upgradeCount)) { + upgradeCount = allowLuckUpgrades ? getRarityUpgradeCount(pool, baseTier, party) : 0; + tier = baseTier + upgradeCount; + } else { + tier = baseTier; + } + + const tierWeights = weights[tier]; + const index = pickWeightedIndex(tierWeights); + + if (index === undefined) { + return null; + } + + let reward: Reward | RewardGenerator | null = pool[tier][index].reward; + if (reward instanceof RewardGenerator) { + reward = (reward as RewardGenerator).generateReward(party); + if (reward === null) { + console.log(RarityTier[tier], upgradeCount); + return getNewRewardOption(pool, weights, party, tier, upgradeCount, ++retryCount); + } + } + + console.log(reward); + + return new RewardOption(reward as Reward, upgradeCount!, tier); // TODO: is this bang correct? +} + +/** + * Replaces the {@linkcode Reward} of the entries within {@linkcode options} with any + * {@linkcode RewardOverride} entries listed in {@linkcode Overrides.REWARD_OVERRIDE} + * up to the smallest amount of entries between {@linkcode options} and the override array. + * @param options Array of naturally rolled {@linkcode RewardOption}s + * @param party Array of the player's current party + */ +export function overridePlayerRewardOptions(options: RewardOption[], party: PlayerPokemon[]) { + const minLength = Math.min(options.length, Overrides.REWARD_OVERRIDE.length); + for (let i = 0; i < minLength; i++) { + const override: RewardOverride = Overrides.REWARD_OVERRIDE[i]; + const rewardFunc = allRewards[override.name]; + let reward: Reward | RewardGenerator | null = rewardFunc(); + + if (reward instanceof RewardGenerator) { + const pregenArgs = "type" in override && override.type !== null ? [override.type] : undefined; + reward = reward.generateReward(party, pregenArgs); + } + + if (reward) { + options[i].type = reward; + options[i].tier = getRewardDefaultTier(reward); + } + } +} + +export function getRewardPoolForType(poolType: RewardPoolType): RewardPool { + switch (poolType) { + case RewardPoolType.PLAYER: + return rewardPool; + } +} + +export function getRewardWeightsForType(poolType: RewardPoolType): RewardPoolWeights { + switch (poolType) { + case RewardPoolType.PLAYER: + return rewardPoolWeights; + } +} diff --git a/src/items/reward-pools.ts b/src/items/reward-pools.ts new file mode 100644 index 00000000000..12affd1c5df --- /dev/null +++ b/src/items/reward-pools.ts @@ -0,0 +1,9 @@ +/* + * Contains modifier pools for different contexts in the game. + * Can be safely imported without worrying about circular dependencies. + */ + +import type { RewardPool, RewardPoolWeights } from "#types/rewards"; + +export const rewardPool: RewardPool = {}; +export const rewardPoolWeights: RewardPoolWeights = {}; diff --git a/src/items/reward-utils.ts b/src/items/reward-utils.ts new file mode 100644 index 00000000000..f9872738fe9 --- /dev/null +++ b/src/items/reward-utils.ts @@ -0,0 +1,115 @@ +import { globalScene } from "#app/global-scene"; +import { allRewards } from "#data/data-lists"; +import type { HeldItemId } from "#enums/held-item-id"; +import { getRewardCategory, RewardCategoryId, RewardId } from "#enums/reward-id"; +import type { RarityTier } from "#enums/reward-tier"; +import type { TrainerItemId } from "#enums/trainer-item-id"; +import type { RewardFunc, RewardPoolId } from "#types/rewards"; +import { getHeldItemTier } from "./held-item-default-tiers"; +import { + type HeldItemReward, + type PokemonMoveReward, + type RememberMoveReward, + type Reward, + RewardGenerator, + RewardOption, + type TmReward, + type TrainerItemReward, +} from "./reward"; +import { getRewardTier } from "./reward-defaults-tiers"; +import { getTrainerItemTier } from "./trainer-item-default-tiers"; + +export function isTmReward(reward: Reward): reward is TmReward { + return getRewardCategory(reward.id) === RewardCategoryId.TM; +} + +export function isMoveReward(reward: Reward): reward is PokemonMoveReward { + const categoryId = getRewardCategory(reward.id); + return categoryId === RewardCategoryId.ETHER || categoryId === RewardCategoryId.PP_UP; +} + +export function isRememberMoveReward(reward: Reward): reward is RememberMoveReward { + return reward.id === RewardId.MEMORY_MUSHROOM; +} + +/** + * Generates a Reward from a given function + * @param rewardFunc + * @param pregenArgs Can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. + */ +export function generateReward(rewardFunc: RewardFunc, pregenArgs?: any[]): Reward | null { + const reward = rewardFunc(); + return reward instanceof RewardGenerator ? reward.generateReward(globalScene.getPlayerParty(), pregenArgs) : reward; +} + +/** + * Generates a Reward Option from a given function + * @param rewardFunc + * @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. + */ +export function generateRewardOption(rewardFunc: RewardFunc, pregenArgs?: any[]): RewardOption | null { + const reward = generateReward(rewardFunc, pregenArgs); + if (reward) { + const tier = getRewardDefaultTier(reward); + return new RewardOption(reward, 0, tier); + } + return null; +} + +/** + * Finds the default rarity tier for a given reward. For unique held item or trainer item rewards, + * falls back to the default rarity tier for the item. + * @param reward The {@linkcode Reward} to determine the tier for. + */ +export function getRewardDefaultTier(reward: Reward): RarityTier { + if (reward.id === RewardId.HELD_ITEM) { + return getHeldItemTier((reward as HeldItemReward).itemId); + } + if (reward.id === RewardId.TRAINER_ITEM) { + return getTrainerItemTier((reward as TrainerItemReward).itemId); + } + return getRewardTier(reward.id); +} + +export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: number): RewardOption[] { + if (!(waveIndex % 10)) { + return []; + } + + const options = [ + [ + new RewardOption(allRewards.POTION(), 0, baseCost * 0.2), + new RewardOption(allRewards.ETHER(), 0, baseCost * 0.4), + new RewardOption(allRewards.REVIVE(), 0, baseCost * 2), + ], + [ + new RewardOption(allRewards.SUPER_POTION(), 0, baseCost * 0.45), + new RewardOption(allRewards.FULL_HEAL(), 0, baseCost), + ], + [new RewardOption(allRewards.ELIXIR(), 0, baseCost), new RewardOption(allRewards.MAX_ETHER(), 0, baseCost)], + [ + new RewardOption(allRewards.HYPER_POTION(), 0, baseCost * 0.8), + new RewardOption(allRewards.MAX_REVIVE(), 0, baseCost * 2.75), + new RewardOption(allRewards.MEMORY_MUSHROOM(), 0, baseCost * 4), + ], + [ + new RewardOption(allRewards.MAX_POTION(), 0, baseCost * 1.5), + new RewardOption(allRewards.MAX_ELIXIR(), 0, baseCost * 2.5), + ], + [new RewardOption(allRewards.FULL_RESTORE(), 0, baseCost * 2.25)], + [new RewardOption(allRewards.SACRED_ASH(), 0, baseCost * 10)], + ]; + return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); +} + +export function isRewardId(id: RewardPoolId): id is RewardId { + return id > 0x2000; +} + +export function isTrainerItemId(id: RewardPoolId): id is TrainerItemId { + return id > 0x1000 && id < 0x2000; +} + +export function isHeldItemId(id: RewardPoolId): id is HeldItemId { + return id < 0x1000; +} diff --git a/src/items/reward.ts b/src/items/reward.ts new file mode 100644 index 00000000000..244b80e1314 --- /dev/null +++ b/src/items/reward.ts @@ -0,0 +1,1852 @@ +import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; +import { globalScene } from "#app/global-scene"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { EvolutionItem, FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions"; +import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters"; +import { tmPoolTiers, tmSpecies } from "#balance/tms"; +import { allHeldItems, allMoves, allRewards, allTrainerItems } from "#data/data-lists"; +import { getLevelTotalExp } from "#data/exp"; +import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; +import { getNatureName, getNatureStatMultiplier } from "#data/nature"; +import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#data/pokeball"; +import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-forms"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { BerryType } from "#enums/berry-type"; +import { FormChangeItem } from "#enums/form-change-item"; +import { HeldItemId } from "#enums/held-item-id"; +import { LearnMoveType } from "#enums/learn-move-type"; +import { MoveId } from "#enums/move-id"; +import { Nature } from "#enums/nature"; +import { PokeballType } from "#enums/pokeball"; +import { PokemonType } from "#enums/pokemon-type"; +import { RewardId } from "#enums/reward-id"; +import { RarityTier } from "#enums/reward-tier"; +import { SpeciesFormKey } from "#enums/species-form-key"; +import { SpeciesId } from "#enums/species-id"; +import type { PermanentStat, TempBattleStat } from "#enums/stat"; +import { Stat, TEMP_BATTLE_STATS } from "#enums/stat"; +import { TrainerItemId } from "#enums/trainer-item-id"; +import type { PlayerPokemon, Pokemon } from "#field/pokemon"; +import { attackTypeToHeldItem } from "#items/attack-type-booster"; +import { permanentStatToHeldItem, statBoostItems } from "#items/base-stat-booster"; +import { berryTypeToHeldItem } from "#items/berry"; +import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#items/held-item-pool"; +import { formChangeItemName } from "#items/item-utility"; +import { + SPECIES_STAT_BOOSTER_ITEMS, + type SpeciesStatBoosterItemId, + type SpeciesStatBoostHeldItem, +} from "#items/stat-booster"; +import { TrainerItemEffect, tempStatToTrainerItem } from "#items/trainer-item"; +import type { PokemonMove } from "#moves/pokemon-move"; +import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#system/voucher"; +import type { RewardFunc, WeightedRewardWeightFunc } from "#types/rewards"; +import type { Exact } from "#types/type-helpers"; +import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler"; +import { PartyUiHandler } from "#ui/party-ui-handler"; +import { formatMoney, NumberHolder, padInt, randSeedInt, randSeedItem } from "#utils/common"; +import { getEnumKeys, getEnumValues } from "#utils/enums"; +import i18next from "i18next"; + +/* +The term "Reward" refers to items the player can access in the post-battle screen (although +they may be used in other places of the code as well). + +Examples include (but are not limited to): +- Potions and other healing items +- Held items and trainer items +- Money items such as nugget and ancient relic + +Rewards have a basic structure with a name, description, and icon. These are used to display +the reward in the reward select screen. All rewards have an .apply() method, which applies the +effect, for example: +- Apply healing to a pokemon +- Assign a held item to a pokemon, or a trainer item to the player +- Add money + +Some rewards, once clicked, simply have their effect---these are Rewards that add money, pokéball, +vouchers, or global effect such as Sacred Ash. +Most rewards require extra parameters. They are divided into subclasses depending on the parameters +that they need, in particular: +- PokemonReward requires to pass a Pokemon (to apply healing, assign item...) +- PokemonMoveReward requires to pass a Pokemon and a move (for Elixir, or PP Up) +Plus some edge cases for Memory Mushroom and DNA Splicers. + +The parameters to be passed are generated by the .applyReward() function in SelectRewardPhase. +This function takes care of opening the party screen and letting the player select a party pokemon, +a move, etc. depending on what is required. Once the parameters are generated, instead of calling +.apply() directly, we call the .applyReward() method in BattleScene, which also plays the sound. +[This method could perhaps be removed]. + +Rewards are assigned RewardId, and there are also RewardCategoryId. For example, TM is a RewardCategoryId, +while CommonTM, RareTM etc are RewardIds. There is _not_ a RewardId for _each_ move. Similarly, +some specific categories of held items are assigned their own RewardId, but they all fall under a single +RewardCategoryId. + +rewardInitObj plays a similar role to allHeldItems, except instead of containing all possible reward +instances, it instead contains functions that generate those rewards. Here, the keys used are strings +rather than RewardId, the difference exists because here we want to distinguish unique held items +for example. The entries of rewardInitObj are used in the RewardPool. + +There are some more derived classes, in particular: +RewardGenerator, which creates Reward instances from a certain group (e.g. TMs, nature mints, or berries); +WeightedReward, which is a Reward with an attached weight or weight function to be used in pools; +and RewardOption, which is displayed during the select reward phase at the end of each encounter. +*/ + +export abstract class Reward { + // TODO: If all we care about for categorization is the reward's ID's _category_, why not do it there? + // TODO: Make abstract and readonly + public id: RewardId; + public localeKey: string; + public iconImage: string; + public group: string; // TODO: Make a union type of all groups + public soundName: string; + public tier: RarityTier; + + constructor(localeKey: string | null, iconImage: string | null, group?: string, soundName?: string) { + this.localeKey = localeKey!; // TODO: is this bang correct? + this.iconImage = iconImage!; // TODO: is this bang correct? + this.group = group!; // TODO: is this bang correct? + this.soundName = soundName ?? "se/restore"; + } + + get name(): string { + return i18next.t(`${this.localeKey}.name`); + } + + getDescription(): string { + return i18next.t(`${this.localeKey}.description`); + } + + getIcon(): string { + return this.iconImage; + } + + // TODO: Should this be abstract? + /** + * Check whether this reward should be applied. + */ + // TODO: This is erroring on stuff of typ + shouldApply(_params: Exact[0]>): boolean { + return true; + } + + /** Apply this Reward's effects. */ + // TODO: Remove `boolean` return from all superclasses' type signatures + abstract apply(_params?: unknown): void; +} + +// TODO: Can this return null? +// TODO: Make this generic based on T +type RewardGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => T | null; + +export abstract class RewardGenerator { + private genRewardFunc: RewardGeneratorFunc; + public id: RewardId; + + constructor(genRewardFunc: RewardGeneratorFunc) { + this.genRewardFunc = genRewardFunc; + } + + generateReward(party: Pokemon[], pregenArgs?: any[]) { + const ret = this.genRewardFunc(party, pregenArgs); + if (ret && this.id) { + ret.id = this.id; + } + return ret; + } +} + +export class AddPokeballReward extends Reward { + private pokeballType: PokeballType; + private count: number; + + constructor(iconImage: string, pokeballType: PokeballType, count: number, id: RewardId) { + super("", iconImage, "pb", "se/pb_bounce_1"); + this.pokeballType = pokeballType; + this.count = count; + this.id = id; + } + + get name(): string { + return i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", { + modifierCount: this.count, + pokeballName: getPokeballName(this.pokeballType), + }); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", { + modifierCount: this.count, + pokeballName: getPokeballName(this.pokeballType), + catchRate: + getPokeballCatchMultiplier(this.pokeballType) > -1 + ? `${getPokeballCatchMultiplier(this.pokeballType)}x` + : "100%", + pokeballAmount: `${globalScene.pokeballCounts[this.pokeballType]}`, + }); + } + + /** + * Applies {@linkcode AddPokeballReward} + * @returns always `true` + */ + apply(): boolean { + const pokeballCounts = globalScene.pokeballCounts; + pokeballCounts[this.pokeballType] = Math.min( + pokeballCounts[this.pokeballType] + this.count, + MAX_PER_TYPE_POKEBALLS, + ); + + return true; + } +} + +export class AddVoucherReward extends Reward { + private voucherType: VoucherType; + private count: number; + + constructor(voucherType: VoucherType, count: number, id: RewardId) { + super("", getVoucherTypeIcon(voucherType), "voucher"); + this.count = count; + this.voucherType = voucherType; + this.id = id; + } + + get name(): string { + return i18next.t("modifierType:ModifierType.AddVoucherConsumableType.name", { + modifierCount: this.count, + voucherTypeName: getVoucherTypeName(this.voucherType), + }); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.AddVoucherConsumableType.description", { + modifierCount: this.count, + voucherTypeName: getVoucherTypeName(this.voucherType), + }); + } + + /** + * Applies {@linkcode AddVoucherReward} + * @param battleScene {@linkcode BattleScene} + * @returns always `true` + */ + apply(): boolean { + const voucherCounts = globalScene.gameData.voucherCounts; + voucherCounts[this.voucherType] += this.count; + + return true; + } +} + +export class AddMoneyReward extends Reward { + private moneyMultiplier: number; + private moneyMultiplierDescriptorKey: string; + + constructor( + localeKey: string, + iconImage: string, + moneyMultiplier: number, + moneyMultiplierDescriptorKey: string, + id: RewardId, + ) { + super(localeKey, iconImage, "money", "se/buy"); + + this.moneyMultiplier = moneyMultiplier; + this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey; + this.id = id; + } + + getDescription(): string { + const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); + globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount }); + const formattedMoney = formatMoney(globalScene.moneyFormat, moneyAmount.value); + + return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", { + moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any), + moneyAmount: formattedMoney, + }); + } + + /** + * Applies {@linkcode AddMoneyReward} + * @returns always `true` + */ + apply(): boolean { + const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); + + globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount }); + + globalScene.addMoney(moneyAmount.value); + + for (const p of globalScene.getPlayerParty()) { + if (p.hasSpecies(SpeciesId.GIMMIGHOUL)) { + const factor = Math.min(Math.floor(this.moneyMultiplier), 3); + p.heldItemManager.add(HeldItemId.GIMMIGHOUL_EVO_TRACKER, factor); + } + } + + return true; + } +} + +/** Rewards that are applied to individual Pokemon. */ +export abstract class PokemonReward extends Reward { + public selectFilter: PokemonSelectFilter | undefined; + + constructor( + localeKey: string, + iconImage: string, + selectFilter?: PokemonSelectFilter, + group?: string, + soundName?: string, + ) { + super(localeKey, iconImage, group, soundName); + this.selectFilter = selectFilter; + } + + abstract override apply(_params: PokemonRewardParams): void; +} + +export interface PokemonRewardParams { + pokemon: PlayerPokemon; +} + +export interface PokemonMoveRewardParams { + pokemon: PlayerPokemon; + moveIndex: number; +} + +export interface PokemonMoveRecallRewardParams { + pokemon: PlayerPokemon; + moveIndex: number; + cost?: number; +} + +export interface PokemonFusionRewardParams { + pokemon: PlayerPokemon; + pokemon2: PlayerPokemon; +} + +export class HeldItemReward extends PokemonReward { + public itemId: HeldItemId; + constructor(itemId: HeldItemId, group?: string, soundName?: string) { + super( + "", + "", + (pokemon: PlayerPokemon) => { + const hasItem = pokemon.heldItemManager.hasItem(this.itemId); + const maxStackCount = allHeldItems[this.itemId].getMaxStackCount(); + if (!maxStackCount) { + return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { + pokemonName: getPokemonNameWithAffix(pokemon), + }); + } + if (hasItem && pokemon.heldItemManager.getStack(this.itemId) === maxStackCount) { + return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.tooMany", { + pokemonName: getPokemonNameWithAffix(pokemon), + }); + } + return null; + }, + group, + soundName, + ); + this.itemId = itemId; + this.id = RewardId.HELD_ITEM; + } + + get name(): string { + return allHeldItems[this.itemId].name; + } + + getDescription(): string { + return allHeldItems[this.itemId].description; + } + + getIcon(): string { + return allHeldItems[this.itemId].iconName; + } + + apply({ pokemon }: PokemonRewardParams): boolean { + return pokemon.heldItemManager.add(this.itemId); + } +} + +export class TrainerItemReward extends Reward { + public itemId: TrainerItemId; + constructor(itemId: TrainerItemId, group?: string, soundName?: string) { + super("", "", group, soundName); + this.itemId = itemId; + this.id = RewardId.TRAINER_ITEM; + } + + get name(): string { + return allTrainerItems[this.itemId].name; + } + + getDescription(): string { + return allTrainerItems[this.itemId].description; + } + + getIcon(): string { + return allTrainerItems[this.itemId].iconName; + } + + apply(): boolean { + return globalScene.trainerItems.add(this.itemId); + } +} + +export class LapsingTrainerItemReward extends TrainerItemReward { + constructor(itemId: TrainerItemId, id?: RewardId) { + super(itemId); + this.id = id ?? RewardId.TRAINER_ITEM; + } + + apply(): boolean { + return globalScene.trainerItems.add(this.itemId, allTrainerItems[this.itemId].getMaxStackCount()); + } +} + +export class ChangeTeraTypeReward extends PokemonReward { + private teraType: PokemonType; + + constructor(teraType: PokemonType) { + super( + "", + `${PokemonType[teraType].toLowerCase()}_tera_shard`, + (pokemon: PlayerPokemon) => { + if ( + [pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter( + s => s === SpeciesId.TERAPAGOS || s === SpeciesId.OGERPON || s === SpeciesId.SHEDINJA, + ).length > 0 + ) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "tera_shard", + ); + + this.teraType = teraType; + } + + get name(): string { + return i18next.t("modifierType:ModifierType.ChangeTeraTypeModifierType.name", { + teraType: i18next.t(`pokemonInfo:Type.${PokemonType[this.teraType]}`), + }); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.ChangeTeraTypeModifierType.description", { + teraType: i18next.t(`pokemonInfo:Type.${PokemonType[this.teraType]}`), + }); + } + + getPregenArgs(): any[] { + return [this.teraType]; + } + + /** + * Checks if {@linkcode TerrastalizeConsumable} should be applied + * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item + * @returns `true` if the {@linkcode TerrastalizeConsumable} should be applied + */ + shouldApply({ pokemon }: PokemonRewardParams): boolean { + return ( + pokemon.teraType !== this.teraType && + ![SpeciesId.SHEDINJA, SpeciesId.OGERPON, SpeciesId.TERAPAGOS].some(s => pokemon.hasSpecies(s)) + ); + } + + /** + * Applies {@linkcode TerrastalizeConsumable} + * @param pokemon The {@linkcode PlayerPokemon} that consumes the item + * @returns `true` if hp was restored + */ + apply({ pokemon }: PokemonRewardParams): boolean { + pokemon.teraType = this.teraType; + return true; + } +} + +function restorePokemonHp( + pokemon: Pokemon, + percentToRestore: number, + pointsToRestore = 0, + healStatus = false, + fainted = false, +): boolean { + if (!pokemon.hp === fainted) { + if (fainted || healStatus) { + pokemon.resetStatus(true, true, false, false); + } + // Apply HealingCharm + let multiplier = 1; + if (!fainted) { + const hpRestoreMultiplier = new NumberHolder(1); + this.applyPlayerItems(TrainerItemEffect.HEALING_BOOSTER, { numberHolder: hpRestoreMultiplier }); + multiplier = hpRestoreMultiplier.value; + } + const restorePoints = Math.floor(pointsToRestore * multiplier); + const restorePercent = Math.floor(percentToRestore * 0.01 * multiplier * pokemon.getMaxHp()); + pokemon.heal(Math.max(restorePercent, restorePoints, 1)); + return true; + } + return false; +} + +export class PokemonHpRestoreReward extends PokemonReward { + protected restorePoints: number; + protected restorePercent: number; + protected healStatus: boolean; + + constructor( + localeKey: string, + iconImage: string, + id: RewardId, + restorePoints: number, + restorePercent: number, + healStatus = false, + selectFilter?: PokemonSelectFilter, + group?: string, + ) { + super( + localeKey, + iconImage, + selectFilter || + ((pokemon: PlayerPokemon) => { + if ( + !pokemon.hp || + (pokemon.isFullHp() && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED)))) + ) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }), + group || "potion", + ); + + this.restorePoints = restorePoints; + this.restorePercent = restorePercent; + this.healStatus = healStatus; + this.id = id; + } + + getDescription(): string { + return this.restorePoints + ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", { + restorePoints: this.restorePoints, + restorePercent: this.restorePercent, + }) + : this.healStatus + ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fullyWithStatus") + : i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fully"); + } + + apply({ pokemon }: PokemonRewardParams): boolean { + return restorePokemonHp(pokemon, this.restorePercent, this.restorePoints, this.healStatus, false); + } +} + +export class PokemonReviveReward extends PokemonHpRestoreReward { + constructor(localeKey: string, iconImage: string, id: RewardId, restorePercent: number) { + super( + localeKey, + iconImage, + id, + 0, + restorePercent, + false, + (pokemon: PlayerPokemon) => { + if (!pokemon.isFainted()) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "revive", + ); + + this.selectFilter = (pokemon: PlayerPokemon) => { + if (pokemon.hp) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }; + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { + restorePercent: this.restorePercent, + }); + } + + apply({ pokemon }: PokemonRewardParams): boolean { + return restorePokemonHp(pokemon, this.restorePercent, 0, false, true); + } +} + +class AllPokemonFullReviveReward extends Reward { + constructor(localeKey: string, iconImage: string) { + super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType"); + this.id = RewardId.SACRED_ASH; + } + + apply(): boolean { + for (const pokemon of globalScene.getPlayerParty()) { + restorePokemonHp(pokemon, 100, 0, false, true); + } + + return true; + } +} + +export class PokemonStatusHealReward extends PokemonReward { + constructor(localeKey: string, iconImage: string) { + super(localeKey, iconImage, (pokemon: PlayerPokemon) => { + if (!pokemon.hp || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }); + this.id = RewardId.FULL_HEAL; + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description"); + } + + apply({ pokemon }: PokemonRewardParams): boolean { + pokemon.resetStatus(true, true, false, false); + return true; + } +} + +export abstract class PokemonMoveReward extends PokemonReward { + public moveSelectFilter: PokemonMoveSelectFilter | undefined; + + constructor( + localeKey: string, + iconImage: string, + id: RewardId, + selectFilter?: PokemonSelectFilter, + moveSelectFilter?: PokemonMoveSelectFilter, + group?: string, + ) { + super(localeKey, iconImage, selectFilter, group); + this.moveSelectFilter = moveSelectFilter; + this.id = id; + } + + apply(_params: PokemonMoveRewardParams): boolean { + return false; + } +} + +export class PokemonPpRestoreReward extends PokemonMoveReward { + protected restorePoints: number; + + constructor(localeKey: string, iconImage: string, id: RewardId, restorePoints: number) { + super( + localeKey, + iconImage, + id, + (_pokemon: PlayerPokemon) => { + return null; + }, + (pokemonMove: PokemonMove) => { + if (!pokemonMove.ppUsed) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "ether", + ); + + this.restorePoints = restorePoints; + } + + getDescription(): string { + return this.restorePoints > -1 + ? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { + restorePoints: this.restorePoints, + }) + : i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.extra.fully"); + } + + /** + * Applies {@linkcode PokemonPpRestoreConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should get move pp restored + * @returns always `true` + */ + apply({ pokemon, moveIndex }: PokemonMoveRewardParams): boolean { + const move = pokemon.getMoveset()[moveIndex]; + + if (move) { + move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; + } + + return true; + } +} + +export class PokemonAllMovePpRestoreReward extends PokemonReward { + protected restorePoints: number; + + constructor(localeKey: string, iconImage: string, id: RewardId, restorePoints: number) { + super( + localeKey, + iconImage, + (pokemon: PlayerPokemon) => { + if (!pokemon.getMoveset().filter(m => m.ppUsed).length) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "elixir", + ); + + this.restorePoints = restorePoints; + this.id = id; + } + + getDescription(): string { + return this.restorePoints > -1 + ? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { + restorePoints: this.restorePoints, + }) + : i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.extra.fully"); + } + + /** + * Applies {@linkcode PokemonAllMovePpRestoreConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should get all move pp restored + * @returns always `true` + */ + apply({ pokemon }: PokemonRewardParams): boolean { + for (const move of pokemon.getMoveset()) { + if (move) { + move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; + } + } + + return true; + } +} + +export class PokemonPpUpReward extends PokemonMoveReward { + protected upPoints: number; + + constructor(localeKey: string, iconImage: string, id: RewardId, upPoints: number) { + super( + localeKey, + iconImage, + id, + (_pokemon: PlayerPokemon) => { + return null; + }, + (pokemonMove: PokemonMove) => { + if (pokemonMove.getMove().pp < 5 || pokemonMove.ppUp >= 3 || pokemonMove.maxPpOverride) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "ppUp", + ); + + this.upPoints = upPoints; + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints }); + } + + /** + * Applies {@linkcode PokemonPpUpConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that gets a pp up on move-slot {@linkcode moveIndex} + * @returns + */ + apply({ pokemon, moveIndex }: PokemonMoveRewardParams): boolean { + const move = pokemon.getMoveset()[moveIndex]; + + if (move && !move.maxPpOverride) { + move.ppUp = Math.min(move.ppUp + this.upPoints, 3); + } + + return true; + } +} + +export class PokemonNatureChangeReward extends PokemonReward { + protected nature: Nature; + + constructor(nature: Nature) { + super( + "", + `mint_${ + getEnumKeys(Stat) + .find(s => getNatureStatMultiplier(nature, Stat[s]) > 1) + ?.toLowerCase() || "neutral" + }`, + (pokemon: PlayerPokemon) => { + if (pokemon.getNature() === this.nature) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "mint", + ); + + this.nature = nature; + this.id = RewardId.MINT; + } + + get name(): string { + return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { + natureName: getNatureName(this.nature), + }); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { + natureName: getNatureName(this.nature, true, true, true), + }); + } + + /** + * Applies {@linkcode PokemonNatureChangeConsumable} + * @param playerPokemon {@linkcode PlayerPokemon} to apply the {@linkcode Nature} change to + * @returns + */ + apply({ pokemon }: PokemonRewardParams): boolean { + pokemon.setCustomNature(this.nature); + globalScene.gameData.unlockSpeciesNature(pokemon.species, this.nature); + + return true; + } +} + +export class RememberMoveReward extends PokemonReward { + constructor(localeKey: string, iconImage: string, group?: string) { + super( + localeKey, + iconImage, + (pokemon: PlayerPokemon) => { + if (!pokemon.getLearnableLevelMoves().length) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + group, + ); + this.id = RewardId.MEMORY_MUSHROOM; + } + + /** + * Applies {@linkcode RememberMoveConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should remember the move + * @returns always `true` + */ + apply({ pokemon, moveIndex, cost }: PokemonMoveRecallRewardParams): boolean { + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(pokemon as PlayerPokemon), + pokemon.getLearnableLevelMoves()[moveIndex], + LearnMoveType.MEMORY, + cost, + ); + + return true; + } +} + +class BerryRewardGenerator extends RewardGenerator { + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in BerryType) { + const item = berryTypeToHeldItem[pregenArgs[0] as BerryType]; + return new HeldItemReward(item); + } + const item = getNewBerryHeldItem(); + return new HeldItemReward(item); + }); + this.id = RewardId.BERRY; + } +} + +class MintRewardGenerator extends RewardGenerator { + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) { + return new PokemonNatureChangeReward(pregenArgs[0] as Nature); + } + return new PokemonNatureChangeReward(randSeedItem(getEnumValues(Nature))); + }); + this.id = RewardId.MINT; + } +} + +class TeraTypeRewardGenerator extends RewardGenerator { + constructor() { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { + return new ChangeTeraTypeReward(pregenArgs[0] as PokemonType); + } + if (!globalScene.trainerItems.hasItem(TrainerItemId.TERA_ORB)) { + return null; + } + const teraTypes: PokemonType[] = []; + for (const p of party) { + if ( + !(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)) + ) { + teraTypes.push(p.teraType); + } + } + let excludedType = PokemonType.UNKNOWN; + if (teraTypes.length > 0 && teraTypes.filter(t => t === teraTypes[0]).length === teraTypes.length) { + excludedType = teraTypes[0]; + } + let shardType = randSeedInt(64) ? (randSeedInt(18) as PokemonType) : PokemonType.STELLAR; + while (shardType === excludedType) { + shardType = randSeedInt(64) ? (randSeedInt(18) as PokemonType) : PokemonType.STELLAR; + } + return new ChangeTeraTypeReward(shardType); + }); + this.id = RewardId.TERA_SHARD; + } +} + +export class AttackTypeBoosterReward extends HeldItemReward { + public moveType: PokemonType; + public boostPercent: number; + + constructor(moveType: PokemonType, boostPercent: number) { + const itemId = attackTypeToHeldItem[moveType]; + super(itemId); + this.moveType = moveType; + this.boostPercent = boostPercent; + } + + getPregenArgs(): any[] { + return [this.moveType]; + } +} + +function incrementLevelWithCandy(pokemon: Pokemon): boolean { + const levelCount = new NumberHolder(1); + globalScene.applyPlayerItems(TrainerItemEffect.LEVEL_INCREMENT_BOOSTER, { numberHolder: levelCount }); + + pokemon.level += levelCount.value; + if (pokemon.level <= globalScene.getMaxExpLevel(true)) { + pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate); + pokemon.levelExp = 0; + } + + if (pokemon.isPlayer()) { + pokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY); + + globalScene.phaseManager.unshiftNew( + "LevelUpPhase", + globalScene.getPlayerParty().indexOf(pokemon), + pokemon.level - levelCount.value, + pokemon.level, + ); + } + return true; +} + +export class PokemonLevelIncrementReward extends PokemonReward { + constructor(localeKey: string, iconImage: string) { + super(localeKey, iconImage, (_pokemon: PlayerPokemon) => null); + this.id = RewardId.RARE_CANDY; + } + + getDescription(): string { + let levels = 1; + const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR); + levels += candyJarStack; + return i18next.t("modifierType:ModifierType.PokemonLevelIncrementModifierType.description", { levels }); + } + + /** + * Applies {@linkcode PokemonLevelIncrementConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should get levels incremented + * @param levelCount The amount of levels to increment + * @returns always `true` + */ + apply({ pokemon }: PokemonRewardParams): boolean { + return incrementLevelWithCandy(pokemon); + } +} + +export class AllPokemonLevelIncrementReward extends Reward { + id = RewardId.RARER_CANDY; + + getDescription(): string { + let levels = 1; + const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR); + levels += candyJarStack; + return i18next.t("modifierType:ModifierType.AllPokemonLevelIncrementModifierType.description", { levels }); + } + + apply(): boolean { + for (const pokemon of globalScene.getPlayerParty()) { + incrementLevelWithCandy(pokemon); + } + + return true; + } +} + +export class BaseStatBoosterReward extends HeldItemReward { + private stat: PermanentStat; + private key: string; + + constructor(stat: PermanentStat) { + const key = statBoostItems[stat]; + const itemId = permanentStatToHeldItem[stat]; + super(itemId); + + this.stat = stat; + this.key = key; + } +} + +export class TmReward extends PokemonReward { + public moveId: MoveId; + + constructor(moveId: MoveId) { + super( + "", + `tm_${PokemonType[allMoves[moveId].type].toLowerCase()}`, + (pokemon: PlayerPokemon) => { + if ( + pokemon.compatibleTms.indexOf(moveId) === -1 || + pokemon.getMoveset().filter(m => m.moveId === moveId).length + ) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }, + "tm", + ); + + this.moveId = moveId; + } + + get name(): string { + return i18next.t("modifierType:ModifierType.TmModifierType.name", { + moveId: padInt(Object.keys(tmSpecies).indexOf(this.moveId.toString()) + 1, 3), + moveName: allMoves[this.moveId].name, + }); + } + + getDescription(): string { + return i18next.t( + globalScene.enableMoveInfo + ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" + : "modifierType:ModifierType.TmModifierType.description", + { moveName: allMoves[this.moveId].name }, + ); + } + + /** + * Applies {@linkcode TmConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should learn the TM + * @returns always `true` + */ + apply({ pokemon }: PokemonRewardParams): boolean { + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(pokemon), + this.moveId, + LearnMoveType.TM, + ); + + return true; + } +} + +export class EvolutionItemReward extends PokemonReward { + public evolutionItem: EvolutionItem; + + constructor(evolutionItem: EvolutionItem) { + super("", EvolutionItem[evolutionItem].toLowerCase(), (pokemon: PlayerPokemon) => { + if ( + pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && + pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem)) + .length && + pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX + ) { + return null; + } + if ( + pokemon.isFusion() && + pokemon.fusionSpecies && + pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && + pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.validate(pokemon, true, this.evolutionItem)) + .length && + pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX + ) { + return null; + } + + return PartyUiHandler.NoEffectMessage; + }); + + this.evolutionItem = evolutionItem; + } + + get name(): string { + return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description"); + } + + getPregenArgs(): any[] { + return [this.evolutionItem]; + } + + /** + * Applies {@linkcode EvolutionItemConsumable} + * @param playerPokemon The {@linkcode PlayerPokemon} that should evolve via item + * @returns `true` if the evolution was successful + */ + apply({ pokemon }: PokemonRewardParams): boolean { + let matchingEvolution = pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) + ? pokemonEvolutions[pokemon.species.speciesId].find( + e => e.evoItem === this.evolutionItem && e.validate(pokemon, false, e.item!), + ) + : null; + + if (!matchingEvolution && pokemon.isFusion()) { + matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies!.speciesId].find( + e => e.evoItem === this.evolutionItem && e.validate(pokemon, true, e.item!), + ); + if (matchingEvolution) { + matchingEvolution = new FusionSpeciesFormEvolution(pokemon.species.speciesId, matchingEvolution); + } + } + + if (matchingEvolution) { + globalScene.phaseManager.unshiftNew("EvolutionPhase", pokemon, matchingEvolution, pokemon.level - 1); + return true; + } + + return false; + } +} + +/** + * Class that represents form changing items + */ +export class FormChangeItemReward extends PokemonReward { + public formChangeItem: FormChangeItem; + + constructor(formChangeItem: FormChangeItem) { + super("", FormChangeItem[formChangeItem].toLowerCase(), (pokemon: PlayerPokemon) => { + // Make sure the Pokemon has alternate forms + if ( + pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && + // Get all form changes for this species with an item trigger, including any compound triggers + pokemonFormChanges[pokemon.species.speciesId] + .filter( + fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger) && fc.preFormKey === pokemon.getFormKey(), + ) + // Returns true if any form changes match this item + .flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) + .flatMap(fc => fc.item) + .includes(this.formChangeItem) + ) { + return null; + } + + return PartyUiHandler.NoEffectMessage; + }); + + this.formChangeItem = formChangeItem; + this.id = RewardId.FORM_CHANGE_ITEM; + } + + get name(): string { + return formChangeItemName(this.formChangeItem); + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description"); + } + + apply({ pokemon }: PokemonRewardParams): boolean { + if (pokemon.heldItemManager.hasFormChangeItem(this.formChangeItem)) { + return false; + } + + pokemon.heldItemManager.addFormChangeItem(this.formChangeItem); + pokemon.heldItemManager.toggleActive(this.formChangeItem); + + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger); + + globalScene.updateItems(true); + + return true; + } +} + +export class FusePokemonReward extends PokemonReward { + constructor(localeKey: string, iconImage: string) { + super(localeKey, iconImage, (pokemon: PlayerPokemon) => { + if (pokemon.isFusion()) { + return PartyUiHandler.NoEffectMessage; + } + return null; + }); + this.id = RewardId.DNA_SPLICERS; + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description"); + } + + /** + * Applies {@linkcode FusePokemonConsumable} + * @param playerPokemon {@linkcode PlayerPokemon} that should be fused + * @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon} + * @returns always Promise + */ + apply({ pokemon, pokemon2 }: PokemonFusionRewardParams): boolean { + pokemon.fuse(pokemon2); + return true; + } +} + +class AttackTypeBoosterRewardGenerator extends RewardGenerator { + constructor() { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { + return new AttackTypeBoosterReward(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT); + } + + const item = getNewAttackTypeBoosterHeldItem(party); + + return item ? new HeldItemReward(item) : null; + }); + this.id = RewardId.ATTACK_TYPE_BOOSTER; + } +} + +class BaseStatBoosterRewardGenerator extends RewardGenerator { + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs) { + return new BaseStatBoosterReward(pregenArgs[0]); + } + return new HeldItemReward(getNewVitaminHeldItem()); + }); + this.id = RewardId.BASE_STAT_BOOSTER; + } +} + +class TempStatStageBoosterRewardGenerator extends RewardGenerator { + public static readonly items: Record = { + [Stat.ATK]: "x_attack", + [Stat.DEF]: "x_defense", + [Stat.SPATK]: "x_sp_atk", + [Stat.SPDEF]: "x_sp_def", + [Stat.SPD]: "x_speed", + [Stat.ACC]: "x_accuracy", + }; + + constructor() { + super((_party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && TEMP_BATTLE_STATS.includes(pregenArgs[0])) { + return new LapsingTrainerItemReward(tempStatToTrainerItem[pregenArgs[0]]); + } + const randStat: TempBattleStat = randSeedInt(Stat.ACC, Stat.ATK); + return new LapsingTrainerItemReward(tempStatToTrainerItem[randStat]); + }); + this.id = RewardId.TEMP_STAT_STAGE_BOOSTER; + } +} + +/** + * Consumable type generator for {@linkcode SpeciesStatBoosterReward}, which + * encapsulates the logic for weighting the most useful held item from + * the current list of {@linkcode items}. + * @extends RewardGenerator + */ +class SpeciesStatBoosterRewardGenerator extends RewardGenerator { + /** Object comprised of the currently available species-based stat boosting held items */ + + constructor(rare: boolean) { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in SPECIES_STAT_BOOSTER_ITEMS) { + return new HeldItemReward(pregenArgs[0] as HeldItemId); + } + + // Get a pool of items based on the rarity. + const tierItems = rare + ? [HeldItemId.LIGHT_BALL, HeldItemId.THICK_CLUB, HeldItemId.METAL_POWDER, HeldItemId.QUICK_POWDER] + : [HeldItemId.DEEP_SEA_SCALE, HeldItemId.DEEP_SEA_TOOTH]; + + const weights = new Array(tierItems.length).fill(0); + + for (const p of party) { + const speciesId = p.getSpeciesForm(true).speciesId; + const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null; + // TODO: Use commented boolean when Fling is implemented + const hasFling = false; /* p.getMoveset(true).some(m => m.moveId === MoveId.FLING) */ + + for (const i in tierItems) { + const checkedSpecies = (allHeldItems[tierItems[i]] as SpeciesStatBoostHeldItem).species; + + // If party member already has the item being weighted currently, skip to the next item + const hasItem = p.heldItemManager.hasItem(tierItems[i]); + + if (!hasItem) { + if (checkedSpecies.includes(speciesId) || (!!fusionSpeciesId && checkedSpecies.includes(fusionSpeciesId))) { + // Add weight if party member has a matching species or, if applicable, a matching fusion species + weights[i]++; + } else if (checkedSpecies.includes(SpeciesId.PIKACHU) && hasFling) { + // Add weight to Light Ball if party member has Fling + weights[i]++; + } + } + } + } + + // TODO: Replace this with a helper function + let totalWeight = 0; + for (const weight of weights) { + totalWeight += weight; + } + + if (totalWeight !== 0) { + const randInt = randSeedInt(totalWeight, 1); + let weight = 0; + + for (const i in weights) { + if (weights[i] !== 0) { + const curWeight = weight + weights[i]; + if (randInt <= weight + weights[i]) { + return new HeldItemReward(tierItems[i]); + } + weight = curWeight; + } + } + } + + return null; + }); + this.id = rare ? RewardId.SPECIES_STAT_BOOSTER : RewardId.RARE_SPECIES_STAT_BOOSTER; + } +} + +class TmRewardGenerator extends RewardGenerator { + constructor(tier: RarityTier) { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in MoveId) { + return new TmReward(pregenArgs[0] as MoveId); + } + const partyMemberCompatibleTms = party.map(p => { + const previousLevelMoves = p.getLearnableLevelMoves(); + return (p as PlayerPokemon).compatibleTms.filter( + tm => !p.moveset.find(m => m.moveId === tm) && !previousLevelMoves.find(lm => lm === tm), + ); + }); + const tierUniqueCompatibleTms = partyMemberCompatibleTms + .flat() + .filter(tm => tmPoolTiers[tm] === tier) + .filter(tm => !allMoves[tm].name.endsWith(" (N)")) + .filter((tm, i, array) => array.indexOf(tm) === i); + if (!tierUniqueCompatibleTms.length) { + return null; + } + // TODO: should this use `randSeedItem`? + const randTmIndex = randSeedInt(tierUniqueCompatibleTms.length); + return new TmReward(tierUniqueCompatibleTms[randTmIndex]); + }); + this.id = + tier === RarityTier.COMMON + ? RewardId.TM_COMMON + : tier === RarityTier.GREAT + ? RewardId.TM_GREAT + : RewardId.TM_ULTRA; + } +} + +class EvolutionItemRewardGenerator extends RewardGenerator { + constructor(rare: boolean, id: RewardId) { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in EvolutionItem) { + return new EvolutionItemReward(pregenArgs[0] as EvolutionItem); + } + + const evolutionItemPool = [ + party + .filter( + p => + pokemonEvolutions.hasOwnProperty(p.species.speciesId) && + (!p.pauseEvolutions || + p.species.speciesId === SpeciesId.SLOWPOKE || + p.species.speciesId === SpeciesId.EEVEE || + p.species.speciesId === SpeciesId.KIRLIA || + p.species.speciesId === SpeciesId.SNORUNT), + ) + .flatMap(p => { + const evolutions = pokemonEvolutions[p.species.speciesId]; + return evolutions.filter(e => e.isValidItemEvolution(p)); + }), + party + .filter( + p => + p.isFusion() && + p.fusionSpecies && + pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && + (!p.pauseEvolutions || + p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE || + p.fusionSpecies.speciesId === SpeciesId.EEVEE || + p.fusionSpecies.speciesId === SpeciesId.KIRLIA || + p.fusionSpecies.speciesId === SpeciesId.SNORUNT), + ) + .flatMap(p => { + const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; + return evolutions.filter(e => e.isValidItemEvolution(p, true)); + }), + ] + .flat() + .flatMap(e => e.evoItem) + .filter(i => !!i && i > 50 === rare); + + if (!evolutionItemPool.length) { + return null; + } + + // TODO: should this use `randSeedItem`? + return new EvolutionItemReward(evolutionItemPool[randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? + }); + this.id = id; + } +} + +export class FormChangeItemRewardGenerator extends RewardGenerator { + constructor(isRareFormChangeItem: boolean, id: RewardId) { + super((party: Pokemon[], pregenArgs?: any[]) => { + if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in FormChangeItem) { + return new FormChangeItemReward(pregenArgs[0] as FormChangeItem); + } + + const formChangeItemPool = [ + ...new Set( + party + .filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)) + .flatMap(p => { + const formChanges = pokemonFormChanges[p.species.speciesId]; + let formChangeItemTriggers = formChanges + .filter( + fc => + ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && + fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || + globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) && + ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && + fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || + globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) && + (!fc.conditions.length || + fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)) + .length) && + fc.preFormKey === p.getFormKey(), + ) + .map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) + .filter(t => t?.active && !p.heldItemManager.hasFormChangeItem(t.item)); + + if (p.species.speciesId === SpeciesId.NECROZMA) { + // technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break... + let foundULTRA_Z = false, + foundN_LUNA = false, + foundN_SOLAR = false; + formChangeItemTriggers.forEach((fc, _i) => { + console.log("Checking ", fc.item); + switch (fc.item) { + case FormChangeItem.ULTRANECROZIUM_Z: + foundULTRA_Z = true; + break; + case FormChangeItem.N_LUNARIZER: + foundN_LUNA = true; + break; + case FormChangeItem.N_SOLARIZER: + foundN_SOLAR = true; + break; + } + }); + if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) { + // all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition. + formChangeItemTriggers = formChangeItemTriggers.filter( + fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z, + ); + } else { + console.log("DID NOT FIND "); + } + } + return formChangeItemTriggers; + }), + ), + ] + .flat() + .flatMap(fc => fc.item) + .filter(i => (i && i < 100) === isRareFormChangeItem); + // convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party. + + if (!formChangeItemPool.length) { + return null; + } + + // TODO: should this use `randSeedItem`? + return new FormChangeItemReward(formChangeItemPool[randSeedInt(formChangeItemPool.length)]); + }); + this.id = id; + } +} + +export class WeightedReward { + public reward: Reward | RewardGenerator; + public weight: number | WeightedRewardWeightFunc; + public maxWeight: number | WeightedRewardWeightFunc; + + constructor( + rewardFunc: RewardFunc, + weight: number | WeightedRewardWeightFunc, + maxWeight?: number | WeightedRewardWeightFunc, + ) { + this.reward = rewardFunc(); + this.weight = weight; + this.maxWeight = maxWeight || (!(weight instanceof Function) ? weight : 0); + } +} + +type BaseRewardOverride = { + name: Exclude; + count?: number; +}; + +/** Type for modifiers and held items that are constructed via {@linkcode RewardGenerator}. */ +export type GeneratorRewardOverride = { + count?: number; +} & ( + | { + name: keyof Pick; + type?: SpeciesStatBoosterItemId; + } + | { + name: keyof Pick; + type?: TempBattleStat; + } + | { + name: keyof Pick; + type?: Stat; + } + | { + name: keyof Pick; + type?: Nature; + } + | { + name: keyof Pick; + type?: PokemonType; + } + | { + name: keyof Pick; + type?: BerryType; + } + | { + name: keyof Pick; + type?: EvolutionItem; + } + | { + name: keyof Pick; + type?: FormChangeItem; + } + | { + name: keyof Pick; + type?: MoveId; + } +); + +/** Type used to construct modifiers and held items for overriding purposes. */ +export type RewardOverride = GeneratorRewardOverride | BaseRewardOverride; + +const rewardInitObj = Object.freeze({ + // Pokeball rewards + POKEBALL: () => new AddPokeballReward("pb", PokeballType.POKEBALL, 5, RewardId.POKEBALL), + GREAT_BALL: () => new AddPokeballReward("gb", PokeballType.GREAT_BALL, 5, RewardId.GREAT_BALL), + ULTRA_BALL: () => new AddPokeballReward("ub", PokeballType.ULTRA_BALL, 5, RewardId.ULTRA_BALL), + ROGUE_BALL: () => new AddPokeballReward("rb", PokeballType.ROGUE_BALL, 5, RewardId.ROGUE_BALL), + MASTER_BALL: () => new AddPokeballReward("mb", PokeballType.MASTER_BALL, 1, RewardId.MASTER_BALL), + + // Voucher rewards + VOUCHER: () => new AddVoucherReward(VoucherType.REGULAR, 1, RewardId.VOUCHER), + VOUCHER_PLUS: () => new AddVoucherReward(VoucherType.PLUS, 1, RewardId.VOUCHER_PLUS), + VOUCHER_PREMIUM: () => new AddVoucherReward(VoucherType.PREMIUM, 1, RewardId.VOUCHER_PREMIUM), + + // Money rewards + NUGGET: () => + new AddMoneyReward( + "modifierType:ModifierType.NUGGET", + "nugget", + 1, + "modifierType:ModifierType.MoneyRewardModifierType.extra.small", + RewardId.NUGGET, + ), + BIG_NUGGET: () => + new AddMoneyReward( + "modifierType:ModifierType.BIG_NUGGET", + "big_nugget", + 2.5, + "modifierType:ModifierType.MoneyRewardModifierType.extra.moderate", + RewardId.BIG_NUGGET, + ), + RELIC_GOLD: () => + new AddMoneyReward( + "modifierType:ModifierType.RELIC_GOLD", + "relic_gold", + 10, + "modifierType:ModifierType.MoneyRewardModifierType.extra.large", + RewardId.RELIC_GOLD, + ), + + // Party-wide consumables + RARER_CANDY: () => new AllPokemonLevelIncrementReward("modifierType:ModifierType.RARER_CANDY", "rarer_candy"), + SACRED_ASH: () => new AllPokemonFullReviveReward("modifierType:ModifierType.SACRED_ASH", "sacred_ash"), + + // Pokemon consumables + RARE_CANDY: () => new PokemonLevelIncrementReward("modifierType:ModifierType.RARE_CANDY", "rare_candy"), + + EVOLUTION_ITEM: () => new EvolutionItemRewardGenerator(false, RewardId.EVOLUTION_ITEM), + RARE_EVOLUTION_ITEM: () => new EvolutionItemRewardGenerator(true, RewardId.RARE_EVOLUTION_ITEM), + + POTION: () => new PokemonHpRestoreReward("modifierType:ModifierType.POTION", "potion", RewardId.POTION, 20, 10), + SUPER_POTION: () => + new PokemonHpRestoreReward("modifierType:ModifierType.SUPER_POTION", "super_potion", RewardId.SUPER_POTION, 50, 25), + HYPER_POTION: () => + new PokemonHpRestoreReward( + "modifierType:ModifierType.HYPER_POTION", + "hyper_potion", + RewardId.HYPER_POTION, + 200, + 50, + ), + MAX_POTION: () => + new PokemonHpRestoreReward("modifierType:ModifierType.MAX_POTION", "max_potion", RewardId.MAX_POTION, 0, 100), + FULL_RESTORE: () => + new PokemonHpRestoreReward( + "modifierType:ModifierType.FULL_RESTORE", + "full_restore", + RewardId.FULL_RESTORE, + 0, + 100, + true, + ), + + REVIVE: () => new PokemonReviveReward("modifierType:ModifierType.REVIVE", "revive", RewardId.REVIVE, 50), + MAX_REVIVE: () => + new PokemonReviveReward("modifierType:ModifierType.MAX_REVIVE", "max_revive", RewardId.MAX_REVIVE, 100), + + FULL_HEAL: () => new PokemonStatusHealReward("modifierType:ModifierType.FULL_HEAL", "full_heal"), + + ETHER: () => new PokemonPpRestoreReward("modifierType:ModifierType.ETHER", "ether", RewardId.ETHER, 10), + MAX_ETHER: () => + new PokemonPpRestoreReward("modifierType:ModifierType.MAX_ETHER", "max_ether", RewardId.MAX_ETHER, -1), + + ELIXIR: () => new PokemonAllMovePpRestoreReward("modifierType:ModifierType.ELIXIR", "elixir", RewardId.ELIXIR, 10), + MAX_ELIXIR: () => + new PokemonAllMovePpRestoreReward("modifierType:ModifierType.MAX_ELIXIR", "max_elixir", RewardId.MAX_ELIXIR, -1), + + PP_UP: () => new PokemonPpUpReward("modifierType:ModifierType.PP_UP", "pp_up", RewardId.PP_UP, 1), + PP_MAX: () => new PokemonPpUpReward("modifierType:ModifierType.PP_MAX", "pp_max", RewardId.PP_MAX, 3), + + /*REPEL: () => new DoubleBattleChanceBoosterReward('Repel', 5), + SUPER_REPEL: () => new DoubleBattleChanceBoosterReward('Super Repel', 10), + MAX_REPEL: () => new DoubleBattleChanceBoosterReward('Max Repel', 25),*/ + + MINT: () => new MintRewardGenerator(), + + TERA_SHARD: () => new TeraTypeRewardGenerator(), + + TM_COMMON: () => new TmRewardGenerator(RarityTier.COMMON), + TM_GREAT: () => new TmRewardGenerator(RarityTier.GREAT), + TM_ULTRA: () => new TmRewardGenerator(RarityTier.ULTRA), + + MEMORY_MUSHROOM: () => new RememberMoveReward("modifierType:ModifierType.MEMORY_MUSHROOM", "big_mushroom"), + + DNA_SPLICERS: () => new FusePokemonReward("modifierType:ModifierType.DNA_SPLICERS", "dna_splicers"), + + // Form change items + FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(false, RewardId.FORM_CHANGE_ITEM), + RARE_FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(true, RewardId.RARE_FORM_CHANGE_ITEM), + + // Held items + REVIVER_SEED: () => new HeldItemReward(HeldItemId.REVIVER_SEED), + + WHITE_HERB: () => new HeldItemReward(HeldItemId.WHITE_HERB), + + SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(false), + RARE_SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(true), + + BASE_STAT_BOOSTER: () => new BaseStatBoosterRewardGenerator(), + + ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterRewardGenerator(), + + MYSTICAL_ROCK: () => new HeldItemReward(HeldItemId.MYSTICAL_ROCK), + + BERRY: () => new BerryRewardGenerator(), + + LUCKY_EGG: () => new HeldItemReward(HeldItemId.LUCKY_EGG), + GOLDEN_EGG: () => new HeldItemReward(HeldItemId.GOLDEN_EGG), + + SOOTHE_BELL: () => new HeldItemReward(HeldItemId.SOOTHE_BELL), + + SCOPE_LENS: () => new HeldItemReward(HeldItemId.SCOPE_LENS), + LEEK: () => new HeldItemReward(HeldItemId.LEEK), + + EVIOLITE: () => new HeldItemReward(HeldItemId.EVIOLITE), + + SOUL_DEW: () => new HeldItemReward(HeldItemId.SOUL_DEW), + + GOLDEN_PUNCH: () => new HeldItemReward(HeldItemId.GOLDEN_PUNCH), + + GRIP_CLAW: () => new HeldItemReward(HeldItemId.GRIP_CLAW), + WIDE_LENS: () => new HeldItemReward(HeldItemId.WIDE_LENS), + + MULTI_LENS: () => new HeldItemReward(HeldItemId.MULTI_LENS), + + FOCUS_BAND: () => new HeldItemReward(HeldItemId.FOCUS_BAND), + + QUICK_CLAW: () => new HeldItemReward(HeldItemId.QUICK_CLAW), + + KINGS_ROCK: () => new HeldItemReward(HeldItemId.KINGS_ROCK), + + LEFTOVERS: () => new HeldItemReward(HeldItemId.LEFTOVERS), + + SHELL_BELL: () => new HeldItemReward(HeldItemId.SHELL_BELL), + + TOXIC_ORB: () => new HeldItemReward(HeldItemId.TOXIC_ORB), + + FLAME_ORB: () => new HeldItemReward(HeldItemId.FLAME_ORB), + + BATON: () => new HeldItemReward(HeldItemId.BATON), + + MINI_BLACK_HOLE: () => new HeldItemReward(HeldItemId.MINI_BLACK_HOLE), + + // Trainer items + MEGA_BRACELET: () => new TrainerItemReward(TrainerItemId.MEGA_BRACELET), + DYNAMAX_BAND: () => new TrainerItemReward(TrainerItemId.DYNAMAX_BAND), + TERA_ORB: () => new TrainerItemReward(TrainerItemId.TERA_ORB), + + MAP: () => new TrainerItemReward(TrainerItemId.MAP), + + LURE: () => new LapsingTrainerItemReward(TrainerItemId.LURE, RewardId.LURE), + SUPER_LURE: () => new LapsingTrainerItemReward(TrainerItemId.SUPER_LURE, RewardId.SUPER_LURE), + MAX_LURE: () => new LapsingTrainerItemReward(TrainerItemId.MAX_LURE, RewardId.MAX_LURE), + + TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterRewardGenerator(), + + DIRE_HIT: () => new LapsingTrainerItemReward(TrainerItemId.DIRE_HIT, RewardId.TEMP_STAT_STAGE_BOOSTER), + + EXP_SHARE: () => new TrainerItemReward(TrainerItemId.EXP_SHARE), + EXP_BALANCE: () => new TrainerItemReward(TrainerItemId.EXP_BALANCE), + + OVAL_CHARM: () => new TrainerItemReward(TrainerItemId.OVAL_CHARM), + + EXP_CHARM: () => new TrainerItemReward(TrainerItemId.EXP_CHARM), + SUPER_EXP_CHARM: () => new TrainerItemReward(TrainerItemId.SUPER_EXP_CHARM), + + AMULET_COIN: () => new TrainerItemReward(TrainerItemId.AMULET_COIN), + + LOCK_CAPSULE: () => new TrainerItemReward(TrainerItemId.LOCK_CAPSULE), + + HEALING_CHARM: () => new TrainerItemReward(TrainerItemId.HEALING_CHARM), + CANDY_JAR: () => new TrainerItemReward(TrainerItemId.CANDY_JAR), + + BERRY_POUCH: () => new TrainerItemReward(TrainerItemId.BERRY_POUCH), + + SHINY_CHARM: () => new TrainerItemReward(TrainerItemId.SHINY_CHARM), + ABILITY_CHARM: () => new TrainerItemReward(TrainerItemId.ABILITY_CHARM), + CATCHING_CHARM: () => new TrainerItemReward(TrainerItemId.CATCHING_CHARM), + + IV_SCANNER: () => new TrainerItemReward(TrainerItemId.IV_SCANNER), + + GOLDEN_POKEBALL: () => new TrainerItemReward(TrainerItemId.GOLDEN_POKEBALL), + + // Tokens //TODO: do we even need them here? + ENEMY_DAMAGE_BOOSTER: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_BOOSTER), + ENEMY_DAMAGE_REDUCTION: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_REDUCTION), + //ENEMY_SUPER_EFFECT_BOOSTER: () => new Reward('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_custom_super_effective'), + ENEMY_HEAL: () => new TrainerItemReward(TrainerItemId.ENEMY_HEAL), + ENEMY_ATTACK_POISON_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_POISON_CHANCE), + ENEMY_ATTACK_PARALYZE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_PARALYZE_CHANCE), + ENEMY_ATTACK_BURN_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_BURN_CHANCE), + ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE), + ENEMY_ENDURE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ENDURE_CHANCE), + ENEMY_FUSED_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_FUSED_CHANCE), + + // Items from mystery encounters + MYSTERY_ENCOUNTER_SHUCKLE_JUICE_GOOD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_GOOD), + MYSTERY_ENCOUNTER_SHUCKLE_JUICE_BAD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_BAD), + + MYSTERY_ENCOUNTER_OLD_GATEAU: () => new HeldItemReward(HeldItemId.OLD_GATEAU), + + MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new TrainerItemReward(TrainerItemId.BLACK_SLUDGE), + + MYSTERY_ENCOUNTER_MACHO_BRACE: () => new HeldItemReward(HeldItemId.MACHO_BRACE), + + MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new TrainerItemReward(TrainerItemId.GOLDEN_BUG_NET), +}); + +/** + * The initial set of modifier types, used to generate the modifier pool. + */ +export type Rewards = typeof rewardInitObj; +export type RewardKeys = keyof typeof rewardInitObj; + +export class RewardOption { + public type: Reward; + public upgradeCount: number; + public tier: RarityTier; + public cost: number; + + constructor(type: Reward, upgradeCount: number, tier: RarityTier, cost = 0) { + this.type = type; + this.upgradeCount = upgradeCount; + this.tier = tier; + this.cost = Math.min(Math.round(cost), Number.MAX_SAFE_INTEGER); + } +} + +export function initRewards() { + for (const [key, value] of Object.entries(rewardInitObj)) { + allRewards[key] = value; + } +} + +// TODO: If necessary, add the rest of the modifier types here. +// For now, doing the minimal work until the modifier rework lands. +const RewardConstructorMap = Object.freeze({ + RewardGenerator, +}); + +/** + * Map of of modifier type strings to their constructor type + */ +export type RewardConstructorMap = typeof RewardConstructorMap; + +/** + * Map of modifier type strings to their instance type + */ +export type RewardInstanceMap = { + [K in keyof RewardConstructorMap]: InstanceType; +}; + +export type RewardString = keyof RewardConstructorMap; diff --git a/src/items/trainer-item-data-types.ts b/src/items/trainer-item-data-types.ts index 179c4de3eaa..305b3f1ebb3 100644 --- a/src/items/trainer-item-data-types.ts +++ b/src/items/trainer-item-data-types.ts @@ -1,5 +1,5 @@ // TODO: move to `src/@types/` -import type { RewardTier } from "#enums/reward-tier"; +import type { RarityTier } from "#enums/reward-tier"; import type { TrainerItemId } from "#enums/trainer-item-id"; export type TrainerItemData = { @@ -28,7 +28,7 @@ type TrainerItemPoolEntry = { export type TrainerItemPool = TrainerItemPoolEntry[]; export type TrainerItemTieredPool = { - [key in RewardTier]?: TrainerItemPool; + [key in RarityTier]?: TrainerItemPool; }; export function isTrainerItemPool(value: any): value is TrainerItemPool { diff --git a/src/items/trainer-item-default-tiers.ts b/src/items/trainer-item-default-tiers.ts new file mode 100644 index 00000000000..aab1d0a6f02 --- /dev/null +++ b/src/items/trainer-item-default-tiers.ts @@ -0,0 +1,50 @@ +import { RarityTier } from "#enums/reward-tier"; +import { TrainerItemId } from "#enums/trainer-item-id"; + +export const trainerItemRarities = { + [TrainerItemId.MAP]: RarityTier.COMMON, + [TrainerItemId.IV_SCANNER]: RarityTier.ULTRA, + [TrainerItemId.LOCK_CAPSULE]: RarityTier.ROGUE, + [TrainerItemId.MEGA_BRACELET]: RarityTier.ROGUE, + [TrainerItemId.DYNAMAX_BAND]: RarityTier.ROGUE, + [TrainerItemId.TERA_ORB]: RarityTier.ULTRA, + + [TrainerItemId.GOLDEN_POKEBALL]: RarityTier.LUXURY, + + [TrainerItemId.OVAL_CHARM]: RarityTier.LUXURY, + [TrainerItemId.EXP_SHARE]: RarityTier.ULTRA, + [TrainerItemId.EXP_BALANCE]: RarityTier.LUXURY, + + [TrainerItemId.CANDY_JAR]: RarityTier.ULTRA, + [TrainerItemId.BERRY_POUCH]: RarityTier.ROGUE, + + [TrainerItemId.HEALING_CHARM]: RarityTier.MASTER, + [TrainerItemId.EXP_CHARM]: RarityTier.ULTRA, + [TrainerItemId.SUPER_EXP_CHARM]: RarityTier.ROGUE, + [TrainerItemId.GOLDEN_EXP_CHARM]: RarityTier.LUXURY, + [TrainerItemId.AMULET_COIN]: RarityTier.ULTRA, + + [TrainerItemId.ABILITY_CHARM]: RarityTier.ULTRA, + [TrainerItemId.SHINY_CHARM]: RarityTier.MASTER, + [TrainerItemId.CATCHING_CHARM]: RarityTier.ULTRA, + + [TrainerItemId.BLACK_SLUDGE]: RarityTier.LUXURY, + [TrainerItemId.GOLDEN_BUG_NET]: RarityTier.LUXURY, + + [TrainerItemId.LURE]: RarityTier.COMMON, + [TrainerItemId.SUPER_LURE]: RarityTier.GREAT, + [TrainerItemId.MAX_LURE]: RarityTier.ULTRA, + + [TrainerItemId.X_ATTACK]: RarityTier.COMMON, + [TrainerItemId.X_DEFENSE]: RarityTier.COMMON, + [TrainerItemId.X_SP_ATK]: RarityTier.COMMON, + [TrainerItemId.X_SP_DEF]: RarityTier.COMMON, + [TrainerItemId.X_SPEED]: RarityTier.COMMON, + [TrainerItemId.X_ACCURACY]: RarityTier.COMMON, + [TrainerItemId.DIRE_HIT]: RarityTier.GREAT, +}; + +export function getTrainerItemTier(item: TrainerItemId): RarityTier { + const tier = trainerItemRarities[item]; + return tier ?? RarityTier.LUXURY; +} diff --git a/src/items/trainer-item-pool.ts b/src/items/trainer-item-pool.ts index 80e85224241..e75f1cec8e1 100644 --- a/src/items/trainer-item-pool.ts +++ b/src/items/trainer-item-pool.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { allTrainerItems } from "#data/data-lists"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import type { TrainerItemId } from "#enums/trainer-item-id"; import type { TrainerItemPool, TrainerItemTieredPool } from "#items/trainer-item-data-types"; import type { TrainerItemManager } from "#items/trainer-item-manager"; @@ -29,13 +29,13 @@ export function getNewTrainerItemFromPool(pool: TrainerItemPool, manager: Traine return entry as TrainerItemId; } -export function assignEnemyBuffTokenForWave(tier: RewardTier) { +export function assignEnemyBuffTokenForWave(tier: RarityTier) { let tierStackCount: number; switch (tier) { - case RewardTier.ULTRA: + case RarityTier.ULTRA: tierStackCount = 5; break; - case RewardTier.GREAT: + case RarityTier.GREAT: tierStackCount = 3; break; default: diff --git a/src/items/trainer-item.ts b/src/items/trainer-item.ts index 2c2200b38be..b2a6c15389c 100644 --- a/src/items/trainer-item.ts +++ b/src/items/trainer-item.ts @@ -297,9 +297,9 @@ export class DoubleBattleChanceBoosterTrainerItem extends LapsingTrainerItem { } } -interface TempStatToTrainerItemMap { - [key: number]: TrainerItemId; -} +type TempStatToTrainerItemMap = { + [key in TempBattleStat]: TrainerItemId; +}; export const tempStatToTrainerItem: TempStatToTrainerItemMap = { [Stat.ATK]: TrainerItemId.X_ATTACK, diff --git a/src/loading-scene.ts b/src/loading-scene.ts index fe581f9f5ec..de5e8a6515b 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -15,9 +15,9 @@ import { getBiomeHasProps } from "#field/arena"; import { initHeldItems } from "#items/all-held-items"; import { initTrainerItems } from "#items/all-trainer-items"; import { initHeldItemPools } from "#items/init-held-item-pools"; +import { initRewardPools } from "#items/init-reward-pools"; import { initTrainerItemPools } from "#items/init-trainer-item-pools"; -import { initModifierPools } from "#modifiers/init-modifier-pools"; -import { initModifierTypes } from "#modifiers/modifier-type"; +import { initRewards } from "#items/reward"; import { initMoves } from "#moves/move"; import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters"; import { CacheBustedLoaderPlugin } from "#plugins/cache-busted-loader-plugin"; @@ -370,8 +370,8 @@ export class LoadingScene extends SceneBase { this.loadLoadingScreen(); - initModifierTypes(); - initModifierPools(); + initRewards(); + initRewardPools(); initHeldItemPools(); initTrainerItemPools(); diff --git a/src/modifier/modifier-pools.ts b/src/modifier/modifier-pools.ts deleted file mode 100644 index aa5dff39094..00000000000 --- a/src/modifier/modifier-pools.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Contains modifier pools for different contexts in the game. - * Can be safely imported without worrying about circular dependencies. - */ - -import type { ModifierPool } from "#types/modifier-types"; - -export const modifierPool: ModifierPool = {}; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts deleted file mode 100644 index 9c57d0aa6ce..00000000000 --- a/src/modifier/modifier-type.ts +++ /dev/null @@ -1,2058 +0,0 @@ -import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; -import { timedEventManager } from "#app/global-event-manager"; -import { globalScene } from "#app/global-scene"; -import { getPokemonNameWithAffix } from "#app/messages"; -import Overrides from "#app/overrides"; -import { EvolutionItem, pokemonEvolutions } from "#balance/pokemon-evolutions"; -import { tmPoolTiers, tmSpecies } from "#balance/tms"; -import { allHeldItems, allMoves, allTrainerItems, modifierTypes } from "#data/data-lists"; -import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; -import { getNatureName, getNatureStatMultiplier } from "#data/nature"; -import { getPokeballCatchMultiplier, getPokeballName } from "#data/pokeball"; -import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-forms"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { BerryType } from "#enums/berry-type"; -import { FormChangeItem } from "#enums/form-change-item"; -import { HeldItemId } from "#enums/held-item-id"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { MoveId } from "#enums/move-id"; -import { Nature } from "#enums/nature"; -import { PokeballType } from "#enums/pokeball"; -import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; -import { SpeciesFormKey } from "#enums/species-form-key"; -import { SpeciesId } from "#enums/species-id"; -import type { PermanentStat, TempBattleStat } from "#enums/stat"; -import { Stat, TEMP_BATTLE_STATS } from "#enums/stat"; -import { TrainerItemId } from "#enums/trainer-item-id"; -import type { PlayerPokemon, Pokemon } from "#field/pokemon"; -import { attackTypeToHeldItem } from "#items/attack-type-booster"; -import { permanentStatToHeldItem, statBoostItems } from "#items/base-stat-booster"; -import { berryTypeToHeldItem } from "#items/berry"; -import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#items/held-item-pool"; -import { formChangeItemName } from "#items/item-utility"; -import { - SPECIES_STAT_BOOSTER_ITEMS, - type SpeciesStatBoosterItemId, - type SpeciesStatBoostHeldItem, -} from "#items/stat-booster"; -import { TrainerItemEffect, tempStatToTrainerItem } from "#items/trainer-item"; -import { - AddPokeballModifier, - AddVoucherModifier, - EvolutionItemModifier, - FusePokemonModifier, - type Modifier, - MoneyRewardModifier, - PokemonAllMovePpRestoreModifier, - PokemonHpRestoreModifier, - PokemonLevelIncrementModifier, - PokemonNatureChangeModifier, - PokemonPpRestoreModifier, - PokemonPpUpModifier, - PokemonStatusHealModifier, - RememberMoveModifier, - TerrastalizeModifier, - TmModifier, -} from "#modifiers/modifier"; -import type { PokemonMove } from "#moves/pokemon-move"; -import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#system/voucher"; -import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#types/modifier-types"; -import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler"; -import { PartyUiHandler } from "#ui/party-ui-handler"; -import { getModifierTierTextTint } from "#ui/text"; -import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#utils/common"; -import { getEnumKeys, getEnumValues } from "#utils/enums"; -import { getModifierPoolForType } from "#utils/modifier-utils"; -import i18next from "i18next"; - -type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier | null; - -export class ModifierType { - public id: string; - public localeKey: string; - public iconImage: string; - public group: string; - public soundName: string; - public tier: RewardTier; - protected newModifierFunc: NewModifierFunc | null; - - /** - * Checks if the modifier type is of a specific type - * @param modifierType - The type to check against - * @return Whether the modifier type is of the specified type - */ - public is(modifierType: K): this is ModifierTypeInstanceMap[K] { - const targetType = ModifierTypeConstructorMap[modifierType]; - if (!targetType) { - return false; - } - return this instanceof targetType; - } - - constructor( - localeKey: string | null, - iconImage: string | null, - newModifierFunc: NewModifierFunc | null, - group?: string, - soundName?: string, - ) { - this.localeKey = localeKey!; // TODO: is this bang correct? - this.iconImage = iconImage!; // TODO: is this bang correct? - this.group = group!; // TODO: is this bang correct? - this.soundName = soundName ?? "se/restore"; - this.newModifierFunc = newModifierFunc; - } - - get name(): string { - return i18next.t(`${this.localeKey}.name` as any); - } - - getDescription(): string { - return i18next.t(`${this.localeKey}.description` as any); - } - - getIcon(): string { - return this.iconImage; - } - - setTier(tier: RewardTier): void { - this.tier = tier; - } - - /** - * Populates item id for ModifierType instance - * @param func - */ - withIdFromFunc(func: ModifierTypeFunc): ModifierType { - this.id = Object.keys(modifierTypeInitObj).find(k => modifierTypeInitObj[k] === func)!; // TODO: is this bang correct? - return this; - } - - /** - * Populates item tier for ModifierType instance - * Tier is a necessary field for items that appear in player shop (determines the Pokeball visual they use) - * To find the tier, this function performs a reverse lookup of the item type in modifier pools - * It checks the weight of the item and will use the first tier for which the weight is greater than 0 - * This is to allow items to be in multiple item pools depending on the conditions, for example for events - * If all tiers have a weight of 0 for the item, the first tier where the item was found is used - * @param poolType Default 'ModifierPoolType.PLAYER'. Which pool to lookup item tier from - * @param party optional. Needed to check the weight of modifiers with conditional weight (see {@linkcode WeightedModifierTypeWeightFunc}) - * if not provided or empty, the weight check will be ignored - * @param rerollCount Default `0`. Used to check the weight of modifiers with conditional weight (see {@linkcode WeightedModifierTypeWeightFunc}) - */ - withTierFromPool( - poolType: ModifierPoolType = ModifierPoolType.PLAYER, - party?: PlayerPokemon[], - rerollCount = 0, - ): ModifierType { - let defaultTier: undefined | RewardTier; - for (const tier of Object.values(getModifierPoolForType(poolType))) { - for (const modifier of tier) { - if (this.id === modifier.modifierType.id) { - let weight: number; - if (modifier.weight instanceof Function) { - weight = party ? modifier.weight(party, rerollCount) : 0; - } else { - weight = modifier.weight; - } - if (weight > 0) { - this.tier = modifier.modifierType.tier; - return this; - } - if (isNullOrUndefined(defaultTier)) { - // If weight is 0, keep track of the first tier where the item was found - defaultTier = modifier.modifierType.tier; - } - } - } - } - - // Didn't find a pool with weight > 0, fallback to first tier where the item was found, if any - if (defaultTier) { - this.tier = defaultTier; - } - - return this; - } - - newModifier(...args: any[]): Modifier | null { - // biome-ignore lint/complexity/useOptionalChain: Changing to optional would coerce null return into undefined - return this.newModifierFunc && this.newModifierFunc(this, args); - } -} - -type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType | null; - -export class ModifierTypeGenerator extends ModifierType { - private genTypeFunc: ModifierTypeGeneratorFunc; - - constructor(genTypeFunc: ModifierTypeGeneratorFunc) { - super(null, null, null); - this.genTypeFunc = genTypeFunc; - } - - generateType(party: Pokemon[], pregenArgs?: any[]) { - const ret = this.genTypeFunc(party, pregenArgs); - if (ret) { - ret.id = this.id; - ret.setTier(this.tier); - } - return ret; - } -} - -export interface GeneratedPersistentModifierType { - getPregenArgs(): any[]; -} - -export class AddPokeballModifierType extends ModifierType { - private pokeballType: PokeballType; - private count: number; - - constructor(iconImage: string, pokeballType: PokeballType, count: number) { - super("", iconImage, (_type, _args) => new AddPokeballModifier(this, pokeballType, count), "pb", "se/pb_bounce_1"); - this.pokeballType = pokeballType; - this.count = count; - } - - get name(): string { - return i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", { - modifierCount: this.count, - pokeballName: getPokeballName(this.pokeballType), - }); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", { - modifierCount: this.count, - pokeballName: getPokeballName(this.pokeballType), - catchRate: - getPokeballCatchMultiplier(this.pokeballType) > -1 - ? `${getPokeballCatchMultiplier(this.pokeballType)}x` - : "100%", - pokeballAmount: `${globalScene.pokeballCounts[this.pokeballType]}`, - }); - } -} - -export class AddVoucherModifierType extends ModifierType { - private voucherType: VoucherType; - private count: number; - - constructor(voucherType: VoucherType, count: number) { - super( - "", - getVoucherTypeIcon(voucherType), - (_type, _args) => new AddVoucherModifier(this, voucherType, count), - "voucher", - ); - this.count = count; - this.voucherType = voucherType; - } - - get name(): string { - return i18next.t("modifierType:ModifierType.AddVoucherModifierType.name", { - modifierCount: this.count, - voucherTypeName: getVoucherTypeName(this.voucherType), - }); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.AddVoucherModifierType.description", { - modifierCount: this.count, - voucherTypeName: getVoucherTypeName(this.voucherType), - }); - } -} - -export class PokemonModifierType extends ModifierType { - public selectFilter: PokemonSelectFilter | undefined; - - constructor( - localeKey: string, - iconImage: string, - newModifierFunc: NewModifierFunc, - selectFilter?: PokemonSelectFilter, - group?: string, - soundName?: string, - ) { - super(localeKey, iconImage, newModifierFunc, group, soundName); - - this.selectFilter = selectFilter; - } -} - -export class HeldItemReward extends PokemonModifierType { - public itemId: HeldItemId; - constructor(itemId: HeldItemId, group?: string, soundName?: string) { - super( - "", - "", - () => null, - (pokemon: PlayerPokemon) => { - const hasItem = pokemon.heldItemManager.hasItem(this.itemId); - const maxStackCount = allHeldItems[this.itemId].getMaxStackCount(); - if (!maxStackCount) { - return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); - } - if (hasItem && pokemon.heldItemManager.getStack(this.itemId) === maxStackCount) { - return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.tooMany", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); - } - return null; - }, - group, - soundName, - ); - this.itemId = itemId; - } - - get name(): string { - return allHeldItems[this.itemId].name; - } - - getDescription(): string { - return allHeldItems[this.itemId].description; - } - - getIcon(): string { - return allHeldItems[this.itemId].iconName; - } - - apply(pokemon: Pokemon) { - pokemon.heldItemManager.add(this.itemId); - } -} - -export class TrainerItemReward extends ModifierType { - public itemId: TrainerItemId; - constructor(itemId: TrainerItemId, group?: string, soundName?: string) { - super("", "", () => null, group, soundName); - this.itemId = itemId; - } - - get name(): string { - return allTrainerItems[this.itemId].name; - } - - getDescription(): string { - return allTrainerItems[this.itemId].description; - } - - getIcon(): string { - return allTrainerItems[this.itemId].iconName; - } - - apply() { - globalScene.trainerItems.add(this.itemId); - } -} - -export class LapsingTrainerItemReward extends TrainerItemReward { - apply() { - globalScene.trainerItems.add(this.itemId, allTrainerItems[this.itemId].getMaxStackCount()); - console.log("WE GOT HERE WE ADDED IT"); - } -} - -export class TerastallizeModifierType extends PokemonModifierType { - private teraType: PokemonType; - - constructor(teraType: PokemonType) { - super( - "", - `${PokemonType[teraType].toLowerCase()}_tera_shard`, - (type, args) => new TerrastalizeModifier(type as TerastallizeModifierType, (args[0] as Pokemon).id, teraType), - (pokemon: PlayerPokemon) => { - if ( - [pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter( - s => s === SpeciesId.TERAPAGOS || s === SpeciesId.OGERPON || s === SpeciesId.SHEDINJA, - ).length > 0 - ) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "tera_shard", - ); - - this.teraType = teraType; - } - - get name(): string { - return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { - teraType: i18next.t(`pokemonInfo:Type.${PokemonType[this.teraType]}`), - }); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { - teraType: i18next.t(`pokemonInfo:Type.${PokemonType[this.teraType]}`), - }); - } - - getPregenArgs(): any[] { - return [this.teraType]; - } -} - -export class PokemonHpRestoreModifierType extends PokemonModifierType { - protected restorePoints: number; - protected restorePercent: number; - protected healStatus: boolean; - - constructor( - localeKey: string, - iconImage: string, - restorePoints: number, - restorePercent: number, - healStatus = false, - newModifierFunc?: NewModifierFunc, - selectFilter?: PokemonSelectFilter, - group?: string, - ) { - super( - localeKey, - iconImage, - newModifierFunc || - ((_type, args) => - new PokemonHpRestoreModifier( - this, - (args[0] as PlayerPokemon).id, - this.restorePoints, - this.restorePercent, - this.healStatus, - false, - )), - selectFilter || - ((pokemon: PlayerPokemon) => { - if ( - !pokemon.hp || - (pokemon.isFullHp() && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED)))) - ) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }), - group || "potion", - ); - - this.restorePoints = restorePoints; - this.restorePercent = restorePercent; - this.healStatus = healStatus; - } - - getDescription(): string { - return this.restorePoints - ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", { - restorePoints: this.restorePoints, - restorePercent: this.restorePercent, - }) - : this.healStatus - ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fullyWithStatus") - : i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fully"); - } -} - -export class PokemonReviveModifierType extends PokemonHpRestoreModifierType { - constructor(localeKey: string, iconImage: string, restorePercent: number) { - super( - localeKey, - iconImage, - 0, - restorePercent, - false, - (_type, args) => - new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, 0, this.restorePercent, false, true), - (pokemon: PlayerPokemon) => { - if (!pokemon.isFainted()) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "revive", - ); - - this.selectFilter = (pokemon: PlayerPokemon) => { - if (pokemon.hp) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }; - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { - restorePercent: this.restorePercent, - }); - } -} - -export class PokemonStatusHealModifierType extends PokemonModifierType { - constructor(localeKey: string, iconImage: string) { - super( - localeKey, - iconImage, - (_type, args) => new PokemonStatusHealModifier(this, (args[0] as PlayerPokemon).id), - (pokemon: PlayerPokemon) => { - if (!pokemon.hp || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - ); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description"); - } -} - -export abstract class PokemonMoveModifierType extends PokemonModifierType { - public moveSelectFilter: PokemonMoveSelectFilter | undefined; - - constructor( - localeKey: string, - iconImage: string, - newModifierFunc: NewModifierFunc, - selectFilter?: PokemonSelectFilter, - moveSelectFilter?: PokemonMoveSelectFilter, - group?: string, - ) { - super(localeKey, iconImage, newModifierFunc, selectFilter, group); - - this.moveSelectFilter = moveSelectFilter; - } -} - -export class PokemonPpRestoreModifierType extends PokemonMoveModifierType { - protected restorePoints: number; - - constructor(localeKey: string, iconImage: string, restorePoints: number) { - super( - localeKey, - iconImage, - (_type, args) => - new PokemonPpRestoreModifier(this, (args[0] as PlayerPokemon).id, args[1] as number, this.restorePoints), - (_pokemon: PlayerPokemon) => { - return null; - }, - (pokemonMove: PokemonMove) => { - if (!pokemonMove.ppUsed) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "ether", - ); - - this.restorePoints = restorePoints; - } - - getDescription(): string { - return this.restorePoints > -1 - ? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { - restorePoints: this.restorePoints, - }) - : i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.extra.fully"); - } -} - -export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType { - protected restorePoints: number; - - constructor(localeKey: string, iconImage: string, restorePoints: number) { - super( - localeKey, - iconImage, - (_type, args) => new PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints), - (pokemon: PlayerPokemon) => { - if (!pokemon.getMoveset().filter(m => m.ppUsed).length) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "elixir", - ); - - this.restorePoints = restorePoints; - } - - getDescription(): string { - return this.restorePoints > -1 - ? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { - restorePoints: this.restorePoints, - }) - : i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.extra.fully"); - } -} - -export class PokemonPpUpModifierType extends PokemonMoveModifierType { - protected upPoints: number; - - constructor(localeKey: string, iconImage: string, upPoints: number) { - super( - localeKey, - iconImage, - (_type, args) => new PokemonPpUpModifier(this, (args[0] as PlayerPokemon).id, args[1] as number, this.upPoints), - (_pokemon: PlayerPokemon) => { - return null; - }, - (pokemonMove: PokemonMove) => { - if (pokemonMove.getMove().pp < 5 || pokemonMove.ppUp >= 3 || pokemonMove.maxPpOverride) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "ppUp", - ); - - this.upPoints = upPoints; - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints }); - } -} - -export class PokemonNatureChangeModifierType extends PokemonModifierType { - protected nature: Nature; - - constructor(nature: Nature) { - super( - "", - `mint_${ - getEnumKeys(Stat) - .find(s => getNatureStatMultiplier(nature, Stat[s]) > 1) - ?.toLowerCase() || "neutral" - }`, - (_type, args) => new PokemonNatureChangeModifier(this, (args[0] as PlayerPokemon).id, this.nature), - (pokemon: PlayerPokemon) => { - if (pokemon.getNature() === this.nature) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "mint", - ); - - this.nature = nature; - } - - get name(): string { - return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { - natureName: getNatureName(this.nature), - }); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { - natureName: getNatureName(this.nature, true, true, true), - }); - } -} - -export class RememberMoveModifierType extends PokemonModifierType { - constructor(localeKey: string, iconImage: string, group?: string) { - super( - localeKey, - iconImage, - (type, args) => new RememberMoveModifier(type, (args[0] as PlayerPokemon).id, args[1] as number), - (pokemon: PlayerPokemon) => { - if (!pokemon.getLearnableLevelMoves().length) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - group, - ); - } -} - -class BerryRewardGenerator extends ModifierTypeGenerator { - constructor() { - super((_party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in BerryType) { - const item = berryTypeToHeldItem[pregenArgs[0] as BerryType]; - return new HeldItemReward(item); - } - const item = getNewBerryHeldItem(); - return new HeldItemReward(item); - }); - } -} - -export class AttackTypeBoosterReward extends HeldItemReward implements GeneratedPersistentModifierType { - public moveType: PokemonType; - public boostPercent: number; - - constructor(moveType: PokemonType, boostPercent: number) { - const itemId = attackTypeToHeldItem[moveType]; - super(itemId); - this.moveType = moveType; - this.boostPercent = boostPercent; - } - - getPregenArgs(): any[] { - return [this.moveType]; - } -} - -export class PokemonLevelIncrementModifierType extends PokemonModifierType { - constructor(localeKey: string, iconImage: string) { - super( - localeKey, - iconImage, - (_type, args) => new PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), - (_pokemon: PlayerPokemon) => null, - ); - } - - getDescription(): string { - let levels = 1; - const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR); - levels += candyJarStack; - return i18next.t("modifierType:ModifierType.PokemonLevelIncrementModifierType.description", { levels }); - } -} - -export class AllPokemonLevelIncrementModifierType extends ModifierType { - constructor(localeKey: string, iconImage: string) { - super(localeKey, iconImage, (_type, _args) => new PokemonLevelIncrementModifier(this, -1)); - } - - getDescription(): string { - let levels = 1; - const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR); - levels += candyJarStack; - return i18next.t("modifierType:ModifierType.AllPokemonLevelIncrementModifierType.description", { levels }); - } -} - -export class BaseStatBoosterReward extends HeldItemReward { - private stat: PermanentStat; - private key: string; - - constructor(stat: PermanentStat) { - const key = statBoostItems[stat]; - const itemId = permanentStatToHeldItem[stat]; - super(itemId); - - this.stat = stat; - this.key = key; - } -} - -/** - * Shuckle Juice item - */ -export class BaseStatTotalHeldItemReward extends HeldItemReward { - private readonly statModifier: number; - - constructor(itemId: HeldItemId, statModifier: number) { - super(itemId); - this.statModifier = statModifier; - } - - apply(pokemon: Pokemon) { - super.apply(pokemon); - pokemon.heldItemManager[this.itemId].data.statModifier = this.statModifier; - } -} - -class AllPokemonFullHpRestoreModifierType extends ModifierType { - private descriptionKey: string; - - constructor(localeKey: string, iconImage: string, descriptionKey?: string, newModifierFunc?: NewModifierFunc) { - super( - localeKey, - iconImage, - newModifierFunc || ((_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false)), - ); - - this.descriptionKey = descriptionKey!; // TODO: is this bang correct? - } - - getDescription(): string { - return i18next.t( - `${this.descriptionKey || "modifierType:ModifierType.AllPokemonFullHpRestoreModifierType"}.description` as any, - ); - } -} - -class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierType { - constructor(localeKey: string, iconImage: string) { - super( - localeKey, - iconImage, - "modifierType:ModifierType.AllPokemonFullReviveModifierType", - (_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false, true), - ); - } -} - -export class MoneyRewardModifierType extends ModifierType { - private moneyMultiplier: number; - private moneyMultiplierDescriptorKey: string; - - constructor(localeKey: string, iconImage: string, moneyMultiplier: number, moneyMultiplierDescriptorKey: string) { - super(localeKey, iconImage, (_type, _args) => new MoneyRewardModifier(this, moneyMultiplier), "money", "se/buy"); - - this.moneyMultiplier = moneyMultiplier; - this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey; - } - - getDescription(): string { - const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); - globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount }); - const formattedMoney = formatMoney(globalScene.moneyFormat, moneyAmount.value); - - return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", { - moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any), - moneyAmount: formattedMoney, - }); - } -} - -export class TmModifierType extends PokemonModifierType { - public moveId: MoveId; - - constructor(moveId: MoveId) { - super( - "", - `tm_${PokemonType[allMoves[moveId].type].toLowerCase()}`, - (_type, args) => new TmModifier(this, (args[0] as PlayerPokemon).id), - (pokemon: PlayerPokemon) => { - if ( - pokemon.compatibleTms.indexOf(moveId) === -1 || - pokemon.getMoveset().filter(m => m.moveId === moveId).length - ) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - "tm", - ); - - this.moveId = moveId; - } - - get name(): string { - return i18next.t("modifierType:ModifierType.TmModifierType.name", { - moveId: padInt(Object.keys(tmSpecies).indexOf(this.moveId.toString()) + 1, 3), - moveName: allMoves[this.moveId].name, - }); - } - - getDescription(): string { - return i18next.t( - globalScene.enableMoveInfo - ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" - : "modifierType:ModifierType.TmModifierType.description", - { moveName: allMoves[this.moveId].name }, - ); - } -} - -export class EvolutionItemModifierType extends PokemonModifierType implements GeneratedPersistentModifierType { - public evolutionItem: EvolutionItem; - - constructor(evolutionItem: EvolutionItem) { - super( - "", - EvolutionItem[evolutionItem].toLowerCase(), - (_type, args) => new EvolutionItemModifier(this, (args[0] as PlayerPokemon).id), - (pokemon: PlayerPokemon) => { - if ( - pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && - pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem)) - .length && - pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX - ) { - return null; - } - if ( - pokemon.isFusion() && - pokemon.fusionSpecies && - pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && - pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.validate(pokemon, true, this.evolutionItem)) - .length && - pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX - ) { - return null; - } - - return PartyUiHandler.NoEffectMessage; - }, - ); - - this.evolutionItem = evolutionItem; - } - - get name(): string { - return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description"); - } - - getPregenArgs(): any[] { - return [this.evolutionItem]; - } -} - -/** - * Class that represents form changing items - */ -export class FormChangeItemReward extends PokemonModifierType { - public formChangeItem: FormChangeItem; - - constructor(formChangeItem: FormChangeItem) { - super( - "", - FormChangeItem[formChangeItem].toLowerCase(), - () => null, - (pokemon: PlayerPokemon) => { - // Make sure the Pokemon has alternate forms - if ( - pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && - // Get all form changes for this species with an item trigger, including any compound triggers - pokemonFormChanges[pokemon.species.speciesId] - .filter( - fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger) && fc.preFormKey === pokemon.getFormKey(), - ) - // Returns true if any form changes match this item - .flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) - .flatMap(fc => fc.item) - .includes(this.formChangeItem) - ) { - return null; - } - - return PartyUiHandler.NoEffectMessage; - }, - ); - - this.formChangeItem = formChangeItem; - } - - get name(): string { - return formChangeItemName(this.formChangeItem); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description"); - } - - apply(pokemon: Pokemon) { - if (pokemon.heldItemManager.hasFormChangeItem(this.formChangeItem)) { - return; - } - - pokemon.heldItemManager.addFormChangeItem(this.formChangeItem); - pokemon.heldItemManager.toggleActive(this.formChangeItem); - - globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger); - - globalScene.updateItems(true); - } -} - -export class FusePokemonModifierType extends PokemonModifierType { - constructor(localeKey: string, iconImage: string) { - super( - localeKey, - iconImage, - (_type, args) => new FusePokemonModifier(this, (args[0] as PlayerPokemon).id, (args[1] as PlayerPokemon).id), - (pokemon: PlayerPokemon) => { - if (pokemon.isFusion()) { - return PartyUiHandler.NoEffectMessage; - } - return null; - }, - ); - } - - getDescription(): string { - return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description"); - } -} - -class AttackTypeBoosterRewardGenerator extends ModifierTypeGenerator { - constructor() { - super((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { - return new AttackTypeBoosterReward(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT); - } - - const item = getNewAttackTypeBoosterHeldItem(party); - - return item ? new HeldItemReward(item) : null; - }); - } -} - -class BaseStatBoosterRewardGenerator extends ModifierTypeGenerator { - constructor() { - super((_party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs) { - return new BaseStatBoosterReward(pregenArgs[0]); - } - return new HeldItemReward(getNewVitaminHeldItem()); - }); - } -} - -class TempStatStageBoosterRewardGenerator extends ModifierTypeGenerator { - public static readonly items: Record = { - [Stat.ATK]: "x_attack", - [Stat.DEF]: "x_defense", - [Stat.SPATK]: "x_sp_atk", - [Stat.SPDEF]: "x_sp_def", - [Stat.SPD]: "x_speed", - [Stat.ACC]: "x_accuracy", - }; - - constructor() { - super((_party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && TEMP_BATTLE_STATS.includes(pregenArgs[0])) { - return new LapsingTrainerItemReward(tempStatToTrainerItem[pregenArgs[0]]); - } - const randStat: TempBattleStat = randSeedInt(Stat.ACC, Stat.ATK); - return new LapsingTrainerItemReward(tempStatToTrainerItem[randStat]); - }); - } -} - -/** - * Modifier type generator for {@linkcode SpeciesStatBoosterModifierType}, which - * encapsulates the logic for weighting the most useful held item from - * the current list of {@linkcode items}. - * @extends ModifierTypeGenerator - */ -class SpeciesStatBoosterRewardGenerator extends ModifierTypeGenerator { - /** Object comprised of the currently available species-based stat boosting held items */ - - constructor(rare: boolean) { - super((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in SPECIES_STAT_BOOSTER_ITEMS) { - return new HeldItemReward(pregenArgs[0] as HeldItemId); - } - - // Get a pool of items based on the rarity. - const tierItems = rare - ? [HeldItemId.LIGHT_BALL, HeldItemId.THICK_CLUB, HeldItemId.METAL_POWDER, HeldItemId.QUICK_POWDER] - : [HeldItemId.DEEP_SEA_SCALE, HeldItemId.DEEP_SEA_TOOTH]; - - const weights = new Array(tierItems.length).fill(0); - - for (const p of party) { - const speciesId = p.getSpeciesForm(true).speciesId; - const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null; - // TODO: Use commented boolean when Fling is implemented - const hasFling = false; /* p.getMoveset(true).some(m => m.moveId === MoveId.FLING) */ - - for (const i in tierItems) { - const checkedSpecies = (allHeldItems[tierItems[i]] as SpeciesStatBoostHeldItem).species; - - // If party member already has the item being weighted currently, skip to the next item - const hasItem = p.heldItemManager.hasItem(tierItems[i]); - - if (!hasItem) { - if (checkedSpecies.includes(speciesId) || (!!fusionSpeciesId && checkedSpecies.includes(fusionSpeciesId))) { - // Add weight if party member has a matching species or, if applicable, a matching fusion species - weights[i]++; - } else if (checkedSpecies.includes(SpeciesId.PIKACHU) && hasFling) { - // Add weight to Light Ball if party member has Fling - weights[i]++; - } - } - } - } - - // TODO: Replace this with a helper function - let totalWeight = 0; - for (const weight of weights) { - totalWeight += weight; - } - - if (totalWeight !== 0) { - const randInt = randSeedInt(totalWeight, 1); - let weight = 0; - - for (const i in weights) { - if (weights[i] !== 0) { - const curWeight = weight + weights[i]; - if (randInt <= weight + weights[i]) { - return new HeldItemReward(tierItems[i]); - } - weight = curWeight; - } - } - } - - return null; - }); - } -} - -class TmModifierTypeGenerator extends ModifierTypeGenerator { - constructor(tier: RewardTier) { - super((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in MoveId) { - return new TmModifierType(pregenArgs[0] as MoveId); - } - const partyMemberCompatibleTms = party.map(p => { - const previousLevelMoves = p.getLearnableLevelMoves(); - return (p as PlayerPokemon).compatibleTms.filter( - tm => !p.moveset.find(m => m.moveId === tm) && !previousLevelMoves.find(lm => lm === tm), - ); - }); - const tierUniqueCompatibleTms = partyMemberCompatibleTms - .flat() - .filter(tm => tmPoolTiers[tm] === tier) - .filter(tm => !allMoves[tm].name.endsWith(" (N)")) - .filter((tm, i, array) => array.indexOf(tm) === i); - if (!tierUniqueCompatibleTms.length) { - return null; - } - // TODO: should this use `randSeedItem`? - const randTmIndex = randSeedInt(tierUniqueCompatibleTms.length); - return new TmModifierType(tierUniqueCompatibleTms[randTmIndex]); - }); - } -} - -class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { - constructor(rare: boolean) { - super((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in EvolutionItem) { - return new EvolutionItemModifierType(pregenArgs[0] as EvolutionItem); - } - - const evolutionItemPool = [ - party - .filter( - p => - pokemonEvolutions.hasOwnProperty(p.species.speciesId) && - (!p.pauseEvolutions || - p.species.speciesId === SpeciesId.SLOWPOKE || - p.species.speciesId === SpeciesId.EEVEE || - p.species.speciesId === SpeciesId.KIRLIA || - p.species.speciesId === SpeciesId.SNORUNT), - ) - .flatMap(p => { - const evolutions = pokemonEvolutions[p.species.speciesId]; - return evolutions.filter(e => e.isValidItemEvolution(p)); - }), - party - .filter( - p => - p.isFusion() && - p.fusionSpecies && - pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && - (!p.pauseEvolutions || - p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE || - p.fusionSpecies.speciesId === SpeciesId.EEVEE || - p.fusionSpecies.speciesId === SpeciesId.KIRLIA || - p.fusionSpecies.speciesId === SpeciesId.SNORUNT), - ) - .flatMap(p => { - const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; - return evolutions.filter(e => e.isValidItemEvolution(p, true)); - }), - ] - .flat() - .flatMap(e => e.evoItem) - .filter(i => !!i && i > 50 === rare); - - if (!evolutionItemPool.length) { - return null; - } - - // TODO: should this use `randSeedItem`? - return new EvolutionItemModifierType(evolutionItemPool[randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? - }); - } -} - -export class FormChangeItemRewardGenerator extends ModifierTypeGenerator { - constructor(isRareFormChangeItem: boolean) { - super((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in FormChangeItem) { - return new FormChangeItemReward(pregenArgs[0] as FormChangeItem); - } - - const formChangeItemPool = [ - ...new Set( - party - .filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId)) - .flatMap(p => { - const formChanges = pokemonFormChanges[p.species.speciesId]; - let formChangeItemTriggers = formChanges - .filter( - fc => - ((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 && - fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) || - globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) && - ((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 && - fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) || - globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) && - (!fc.conditions.length || - fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p)) - .length) && - fc.preFormKey === p.getFormKey(), - ) - .map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) - .filter(t => t?.active && !p.heldItemManager.hasFormChangeItem(t.item)); - - if (p.species.speciesId === SpeciesId.NECROZMA) { - // technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break... - let foundULTRA_Z = false, - foundN_LUNA = false, - foundN_SOLAR = false; - formChangeItemTriggers.forEach((fc, _i) => { - console.log("Checking ", fc.item); - switch (fc.item) { - case FormChangeItem.ULTRANECROZIUM_Z: - foundULTRA_Z = true; - break; - case FormChangeItem.N_LUNARIZER: - foundN_LUNA = true; - break; - case FormChangeItem.N_SOLARIZER: - foundN_SOLAR = true; - break; - } - }); - if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) { - // all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition. - formChangeItemTriggers = formChangeItemTriggers.filter( - fc => fc.item !== FormChangeItem.ULTRANECROZIUM_Z, - ); - } else { - console.log("DID NOT FIND "); - } - } - return formChangeItemTriggers; - }), - ), - ] - .flat() - .flatMap(fc => fc.item) - .filter(i => (i && i < 100) === isRareFormChangeItem); - // convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party. - - if (!formChangeItemPool.length) { - return null; - } - - // TODO: should this use `randSeedItem`? - return new FormChangeItemReward(formChangeItemPool[randSeedInt(formChangeItemPool.length)]); - }); - } -} - -export class WeightedModifierType { - public modifierType: ModifierType; - public weight: number | WeightedModifierTypeWeightFunc; - public maxWeight: number | WeightedModifierTypeWeightFunc; - - constructor( - modifierTypeFunc: ModifierTypeFunc, - weight: number | WeightedModifierTypeWeightFunc, - maxWeight?: number | WeightedModifierTypeWeightFunc, - ) { - this.modifierType = modifierTypeFunc(); - this.modifierType.id = Object.keys(modifierTypeInitObj).find(k => modifierTypeInitObj[k] === modifierTypeFunc)!; // TODO: is this bang correct? - this.weight = weight; - this.maxWeight = maxWeight || (!(weight instanceof Function) ? weight : 0); - } - - setTier(tier: RewardTier) { - this.modifierType.setTier(tier); - } -} - -type BaseModifierOverride = { - name: Exclude; - count?: number; -}; - -/** Type for modifiers and held items that are constructed via {@linkcode ModifierTypeGenerator}. */ -export type GeneratorModifierOverride = { - count?: number; -} & ( - | { - name: keyof Pick; - type?: SpeciesStatBoosterItemId; - } - | { - name: keyof Pick; - type?: TempBattleStat; - } - | { - name: keyof Pick; - type?: Stat; - } - | { - name: keyof Pick; - type?: Nature; - } - | { - name: keyof Pick; - type?: PokemonType; - } - | { - name: keyof Pick; - type?: BerryType; - } - | { - name: keyof Pick; - type?: EvolutionItem; - } - | { - name: keyof Pick; - type?: FormChangeItem; - } - | { - name: keyof Pick; - type?: MoveId; - } -); - -/** Type used to construct modifiers and held items for overriding purposes. */ -export type ModifierOverride = GeneratorModifierOverride | BaseModifierOverride; - -export type ModifierTypeKeys = keyof typeof modifierTypeInitObj; - -const modifierTypeInitObj = Object.freeze({ - POKEBALL: () => new AddPokeballModifierType("pb", PokeballType.POKEBALL, 5), - GREAT_BALL: () => new AddPokeballModifierType("gb", PokeballType.GREAT_BALL, 5), - ULTRA_BALL: () => new AddPokeballModifierType("ub", PokeballType.ULTRA_BALL, 5), - ROGUE_BALL: () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5), - MASTER_BALL: () => new AddPokeballModifierType("mb", PokeballType.MASTER_BALL, 1), - - RARE_CANDY: () => new PokemonLevelIncrementModifierType("modifierType:ModifierType.RARE_CANDY", "rare_candy"), - RARER_CANDY: () => new AllPokemonLevelIncrementModifierType("modifierType:ModifierType.RARER_CANDY", "rarer_candy"), - - EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(false), - RARE_EVOLUTION_ITEM: () => new EvolutionItemModifierTypeGenerator(true), - - FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(false), - RARE_FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(true), - - EVOLUTION_TRACKER_GIMMIGHOUL: () => new HeldItemReward(HeldItemId.GIMMIGHOUL_EVO_TRACKER), - - MEGA_BRACELET: () => new TrainerItemReward(TrainerItemId.MEGA_BRACELET), - DYNAMAX_BAND: () => new TrainerItemReward(TrainerItemId.DYNAMAX_BAND), - TERA_ORB: () => new TrainerItemReward(TrainerItemId.TERA_ORB), - - MAP: () => new TrainerItemReward(TrainerItemId.MAP), - - POTION: () => new PokemonHpRestoreModifierType("modifierType:ModifierType.POTION", "potion", 20, 10), - SUPER_POTION: () => - new PokemonHpRestoreModifierType("modifierType:ModifierType.SUPER_POTION", "super_potion", 50, 25), - HYPER_POTION: () => - new PokemonHpRestoreModifierType("modifierType:ModifierType.HYPER_POTION", "hyper_potion", 200, 50), - MAX_POTION: () => new PokemonHpRestoreModifierType("modifierType:ModifierType.MAX_POTION", "max_potion", 0, 100), - FULL_RESTORE: () => - new PokemonHpRestoreModifierType("modifierType:ModifierType.FULL_RESTORE", "full_restore", 0, 100, true), - - REVIVE: () => new PokemonReviveModifierType("modifierType:ModifierType.REVIVE", "revive", 50), - MAX_REVIVE: () => new PokemonReviveModifierType("modifierType:ModifierType.MAX_REVIVE", "max_revive", 100), - - FULL_HEAL: () => new PokemonStatusHealModifierType("modifierType:ModifierType.FULL_HEAL", "full_heal"), - - SACRED_ASH: () => new AllPokemonFullReviveModifierType("modifierType:ModifierType.SACRED_ASH", "sacred_ash"), - - REVIVER_SEED: () => new HeldItemReward(HeldItemId.REVIVER_SEED), - - WHITE_HERB: () => new HeldItemReward(HeldItemId.WHITE_HERB), - - ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.ETHER", "ether", 10), - MAX_ETHER: () => new PokemonPpRestoreModifierType("modifierType:ModifierType.MAX_ETHER", "max_ether", -1), - - ELIXIR: () => new PokemonAllMovePpRestoreModifierType("modifierType:ModifierType.ELIXIR", "elixir", 10), - MAX_ELIXIR: () => new PokemonAllMovePpRestoreModifierType("modifierType:ModifierType.MAX_ELIXIR", "max_elixir", -1), - - PP_UP: () => new PokemonPpUpModifierType("modifierType:ModifierType.PP_UP", "pp_up", 1), - PP_MAX: () => new PokemonPpUpModifierType("modifierType:ModifierType.PP_MAX", "pp_max", 3), - - /*REPEL: () => new DoubleBattleChanceBoosterModifierType('Repel', 5), - SUPER_REPEL: () => new DoubleBattleChanceBoosterModifierType('Super Repel', 10), - MAX_REPEL: () => new DoubleBattleChanceBoosterModifierType('Max Repel', 25),*/ - - LURE: () => new LapsingTrainerItemReward(TrainerItemId.LURE), - SUPER_LURE: () => new LapsingTrainerItemReward(TrainerItemId.SUPER_LURE), - MAX_LURE: () => new LapsingTrainerItemReward(TrainerItemId.MAX_LURE), - - SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(false), - RARE_SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(true), - - TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterRewardGenerator(), - - DIRE_HIT: () => new LapsingTrainerItemReward(TrainerItemId.DIRE_HIT), - - BASE_STAT_BOOSTER: () => new BaseStatBoosterRewardGenerator(), - - ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterRewardGenerator(), - - MINT: () => - new ModifierTypeGenerator((_party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) { - return new PokemonNatureChangeModifierType(pregenArgs[0] as Nature); - } - return new PokemonNatureChangeModifierType(randSeedItem(getEnumValues(Nature))); - }), - - MYSTICAL_ROCK: () => new HeldItemReward(HeldItemId.MYSTICAL_ROCK), - - TERA_SHARD: () => - new ModifierTypeGenerator((party: Pokemon[], pregenArgs?: any[]) => { - if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { - return new TerastallizeModifierType(pregenArgs[0] as PokemonType); - } - if (!globalScene.trainerItems.hasItem(TrainerItemId.TERA_ORB)) { - return null; - } - const teraTypes: PokemonType[] = []; - for (const p of party) { - if ( - !(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)) - ) { - teraTypes.push(p.teraType); - } - } - let excludedType = PokemonType.UNKNOWN; - if (teraTypes.length > 0 && teraTypes.filter(t => t === teraTypes[0]).length === teraTypes.length) { - excludedType = teraTypes[0]; - } - let shardType = randSeedInt(64) ? (randSeedInt(18) as PokemonType) : PokemonType.STELLAR; - while (shardType === excludedType) { - shardType = randSeedInt(64) ? (randSeedInt(18) as PokemonType) : PokemonType.STELLAR; - } - return new TerastallizeModifierType(shardType); - }), - - BERRY: () => new BerryRewardGenerator(), - - TM_COMMON: () => new TmModifierTypeGenerator(RewardTier.COMMON), - TM_GREAT: () => new TmModifierTypeGenerator(RewardTier.GREAT), - TM_ULTRA: () => new TmModifierTypeGenerator(RewardTier.ULTRA), - - MEMORY_MUSHROOM: () => new RememberMoveModifierType("modifierType:ModifierType.MEMORY_MUSHROOM", "big_mushroom"), - - EXP_SHARE: () => new TrainerItemReward(TrainerItemId.EXP_SHARE), - EXP_BALANCE: () => new TrainerItemReward(TrainerItemId.EXP_BALANCE), - - OVAL_CHARM: () => new TrainerItemReward(TrainerItemId.OVAL_CHARM), - - EXP_CHARM: () => new TrainerItemReward(TrainerItemId.EXP_CHARM), - SUPER_EXP_CHARM: () => new TrainerItemReward(TrainerItemId.SUPER_EXP_CHARM), - - LUCKY_EGG: () => new HeldItemReward(HeldItemId.LUCKY_EGG), - GOLDEN_EGG: () => new HeldItemReward(HeldItemId.GOLDEN_EGG), - - SOOTHE_BELL: () => new HeldItemReward(HeldItemId.SOOTHE_BELL), - - SCOPE_LENS: () => new HeldItemReward(HeldItemId.SCOPE_LENS), - LEEK: () => new HeldItemReward(HeldItemId.LEEK), - - EVIOLITE: () => new HeldItemReward(HeldItemId.EVIOLITE), - - SOUL_DEW: () => new HeldItemReward(HeldItemId.SOUL_DEW), - - NUGGET: () => - new MoneyRewardModifierType( - "modifierType:ModifierType.NUGGET", - "nugget", - 1, - "modifierType:ModifierType.MoneyRewardModifierType.extra.small", - ), - BIG_NUGGET: () => - new MoneyRewardModifierType( - "modifierType:ModifierType.BIG_NUGGET", - "big_nugget", - 2.5, - "modifierType:ModifierType.MoneyRewardModifierType.extra.moderate", - ), - RELIC_GOLD: () => - new MoneyRewardModifierType( - "modifierType:ModifierType.RELIC_GOLD", - "relic_gold", - 10, - "modifierType:ModifierType.MoneyRewardModifierType.extra.large", - ), - - AMULET_COIN: () => new TrainerItemReward(TrainerItemId.AMULET_COIN), - GOLDEN_PUNCH: () => new HeldItemReward(HeldItemId.GOLDEN_PUNCH), - - LOCK_CAPSULE: () => new TrainerItemReward(TrainerItemId.LOCK_CAPSULE), - - GRIP_CLAW: () => new HeldItemReward(HeldItemId.GRIP_CLAW), - WIDE_LENS: () => new HeldItemReward(HeldItemId.WIDE_LENS), - - MULTI_LENS: () => new HeldItemReward(HeldItemId.MULTI_LENS), - - HEALING_CHARM: () => new TrainerItemReward(TrainerItemId.HEALING_CHARM), - CANDY_JAR: () => new TrainerItemReward(TrainerItemId.CANDY_JAR), - - BERRY_POUCH: () => new TrainerItemReward(TrainerItemId.BERRY_POUCH), - - FOCUS_BAND: () => new HeldItemReward(HeldItemId.FOCUS_BAND), - - QUICK_CLAW: () => new HeldItemReward(HeldItemId.QUICK_CLAW), - - KINGS_ROCK: () => new HeldItemReward(HeldItemId.KINGS_ROCK), - - LEFTOVERS: () => new HeldItemReward(HeldItemId.LEFTOVERS), - - SHELL_BELL: () => new HeldItemReward(HeldItemId.SHELL_BELL), - - TOXIC_ORB: () => new HeldItemReward(HeldItemId.TOXIC_ORB), - - FLAME_ORB: () => new HeldItemReward(HeldItemId.FLAME_ORB), - - BATON: () => new HeldItemReward(HeldItemId.BATON), - - SHINY_CHARM: () => new TrainerItemReward(TrainerItemId.SHINY_CHARM), - ABILITY_CHARM: () => new TrainerItemReward(TrainerItemId.ABILITY_CHARM), - CATCHING_CHARM: () => new TrainerItemReward(TrainerItemId.CATCHING_CHARM), - - IV_SCANNER: () => new TrainerItemReward(TrainerItemId.IV_SCANNER), - - DNA_SPLICERS: () => new FusePokemonModifierType("modifierType:ModifierType.DNA_SPLICERS", "dna_splicers"), - - MINI_BLACK_HOLE: () => new HeldItemReward(HeldItemId.MINI_BLACK_HOLE), - - VOUCHER: () => new AddVoucherModifierType(VoucherType.REGULAR, 1), - VOUCHER_PLUS: () => new AddVoucherModifierType(VoucherType.PLUS, 1), - VOUCHER_PREMIUM: () => new AddVoucherModifierType(VoucherType.PREMIUM, 1), - - GOLDEN_POKEBALL: () => new TrainerItemReward(TrainerItemId.GOLDEN_POKEBALL), - - ENEMY_DAMAGE_BOOSTER: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_BOOSTER), - ENEMY_DAMAGE_REDUCTION: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_REDUCTION), - //ENEMY_SUPER_EFFECT_BOOSTER: () => new ModifierType('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_custom_super_effective'), - ENEMY_HEAL: () => new TrainerItemReward(TrainerItemId.ENEMY_HEAL), - ENEMY_ATTACK_POISON_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_POISON_CHANCE), - ENEMY_ATTACK_PARALYZE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_PARALYZE_CHANCE), - ENEMY_ATTACK_BURN_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_BURN_CHANCE), - ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE), - ENEMY_ENDURE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ENDURE_CHANCE), - ENEMY_FUSED_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_FUSED_CHANCE), - - MYSTERY_ENCOUNTER_SHUCKLE_JUICE_GOOD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_GOOD), - MYSTERY_ENCOUNTER_SHUCKLE_JUICE_BAD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_BAD), - - MYSTERY_ENCOUNTER_OLD_GATEAU: () => new HeldItemReward(HeldItemId.OLD_GATEAU), - - MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new TrainerItemReward(TrainerItemId.BLACK_SLUDGE), - - MYSTERY_ENCOUNTER_MACHO_BRACE: () => new HeldItemReward(HeldItemId.MACHO_BRACE), - - MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new TrainerItemReward(TrainerItemId.GOLDEN_BUG_NET), -}); - -/** - * The initial set of modifier types, used to generate the modifier pool. - */ -export type ModifierTypes = typeof modifierTypeInitObj; - -export interface ModifierPool { - [tier: string]: WeightedModifierType[]; -} - -let modifierPoolThresholds = {}; -let ignoredPoolIndexes = {}; - -/** - * Allows a unit test to check if an item exists in the Modifier Pool. Checks the pool directly, rather than attempting to reroll for the item. - */ -export const itemPoolChecks: Map = new Map(); - -export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount = 0) { - const pool = getModifierPoolForType(poolType); - itemPoolChecks.forEach((_v, k) => { - itemPoolChecks.set(k, false); - }); - - const ignoredIndexes = {}; - const thresholds = Object.fromEntries( - new Map( - Object.keys(pool).map(t => { - ignoredIndexes[t] = []; - const thresholds = new Map(); - const tierModifierIds: string[] = []; - let i = 0; - pool[t].reduce((total: number, modifierType: WeightedModifierType) => { - const weightedModifierType = modifierType as WeightedModifierType; - const itemModifierType = - weightedModifierType.modifierType instanceof ModifierTypeGenerator - ? weightedModifierType.modifierType.generateType(party) - : weightedModifierType.modifierType; - const trainerItemfullStack = - itemModifierType instanceof TrainerItemReward - ? globalScene.trainerItems.isMaxStack(itemModifierType.itemId) - : false; - const weight = - !trainerItemfullStack || - itemModifierType instanceof HeldItemReward || - itemModifierType instanceof FormChangeItemReward - ? weightedModifierType.weight instanceof Function - ? // biome-ignore lint/complexity/noBannedTypes: TODO: refactor to not use Function type - (weightedModifierType.weight as Function)(party, rerollCount) - : (weightedModifierType.weight as number) - : 0; - if (weightedModifierType.maxWeight) { - const modifierId = weightedModifierType.modifierType.id; - tierModifierIds.push(modifierId); - } - if (weight) { - total += weight; - } else { - ignoredIndexes[t].push(i++); - return total; - } - if (itemPoolChecks.has(modifierType.modifierType.id as ModifierTypeKeys)) { - itemPoolChecks.set(modifierType.modifierType.id as ModifierTypeKeys, true); - } - thresholds.set(total, i++); - return total; - }, 0); - return [t, Object.fromEntries(thresholds)]; - }), - ), - ); - switch (poolType) { - case ModifierPoolType.PLAYER: - modifierPoolThresholds = thresholds; - ignoredPoolIndexes = ignoredIndexes; - break; - } -} - -export interface CustomModifierSettings { - guaranteedModifierTiers?: RewardTier[]; - guaranteedModifierTypeOptions?: ModifierTypeOption[]; - /** If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned). */ - guaranteedModifierTypeFuncs?: ModifierTypeFunc[]; - /** - * If set to `true`, will fill the remainder of shop items that were not overridden by the 3 options above, up to the `count` param value. - * @example - * ```ts - * count = 4; - * customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true }; - * ``` - * The first item in the shop will be `GREAT` tier, and the remaining `3` items will be generated normally. - * - * If `fillRemaining: false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of the value of `count`). - * @defaultValue `false` - */ - fillRemaining?: boolean; - /** If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all. */ - rerollMultiplier?: number; - /** - * If `false`, will prevent set item tiers from upgrading via luck. - * @defaultValue `true` - */ - allowLuckUpgrades?: boolean; -} - -export function getModifierTypeFuncById(id: string): ModifierTypeFunc { - return modifierTypeInitObj[id]; -} - -/** - * Generates modifier options for a {@linkcode SelectRewardPhase} - * @param count - Determines the number of items to generate - * @param party - Party is required for generating proper modifier pools - * @param modifierTiers - (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule. - * @param customModifierSettings - See {@linkcode CustomModifierSettings} - */ -export function getPlayerModifierTypeOptions( - count: number, - party: PlayerPokemon[], - modifierTiers?: RewardTier[], - customModifierSettings?: CustomModifierSettings, -): ModifierTypeOption[] { - const options: ModifierTypeOption[] = []; - const retryCount = Math.min(count * 5, 50); - if (!customModifierSettings) { - for (let i = 0; i < count; i++) { - const tier = modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined; - options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier)); - } - } else { - // Guaranteed mod options first - if ( - customModifierSettings?.guaranteedModifierTypeOptions && - customModifierSettings.guaranteedModifierTypeOptions.length > 0 - ) { - options.push(...customModifierSettings.guaranteedModifierTypeOptions!); - } - - // Guaranteed mod functions second - if ( - customModifierSettings.guaranteedModifierTypeFuncs && - customModifierSettings.guaranteedModifierTypeFuncs.length > 0 - ) { - customModifierSettings.guaranteedModifierTypeFuncs!.forEach((mod, _i) => { - const modifierId = Object.keys(modifierTypeInitObj).find(k => modifierTypeInitObj[k] === mod) as string; - let guaranteedMod: ModifierType = modifierTypeInitObj[modifierId]?.(); - - // Populates item id and tier - guaranteedMod = guaranteedMod - .withIdFromFunc(modifierTypeInitObj[modifierId]) - .withTierFromPool(ModifierPoolType.PLAYER, party); - - const modType = - guaranteedMod instanceof ModifierTypeGenerator ? guaranteedMod.generateType(party) : guaranteedMod; - if (modType) { - const option = new ModifierTypeOption(modType, 0); - options.push(option); - } - }); - } - - // Guaranteed tiers third - if (customModifierSettings.guaranteedModifierTiers && customModifierSettings.guaranteedModifierTiers.length > 0) { - const allowLuckUpgrades = customModifierSettings.allowLuckUpgrades ?? true; - for (const tier of customModifierSettings.guaranteedModifierTiers) { - options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier, allowLuckUpgrades)); - } - } - - // Fill remaining - if (options.length < count && customModifierSettings.fillRemaining) { - while (options.length < count) { - options.push(getModifierTypeOptionWithRetry(options, retryCount, party, undefined)); - } - } - } - - overridePlayerModifierTypeOptions(options, party); - - return options; -} - -/** - * Will generate a {@linkcode ModifierType} from the {@linkcode ModifierPoolType.PLAYER} pool, attempting to retry duplicated items up to retryCount - * @param existingOptions Currently generated options - * @param retryCount How many times to retry before allowing a dupe item - * @param party Current player party, used to calculate items in the pool - * @param tier If specified will generate item of tier - * @param allowLuckUpgrades `true` to allow items to upgrade tiers (the little animation that plays and is affected by luck) - */ -function getModifierTypeOptionWithRetry( - existingOptions: ModifierTypeOption[], - retryCount: number, - party: PlayerPokemon[], - tier?: RewardTier, - allowLuckUpgrades?: boolean, -): ModifierTypeOption { - allowLuckUpgrades = allowLuckUpgrades ?? true; - let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades); - let r = 0; - while ( - existingOptions.length && - ++r < retryCount && - existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length - ) { - console.log("Retry count:", r); - console.log(candidate?.type.group); - console.log(candidate?.type.name); - console.log(existingOptions.filter(o => o.type.name === candidate?.type.name).length); - console.log(existingOptions.filter(o => o.type.group === candidate?.type.group).length); - candidate = getNewModifierTypeOption( - party, - ModifierPoolType.PLAYER, - candidate?.type.tier ?? tier, - candidate?.upgradeCount, - 0, - allowLuckUpgrades, - ); - } - return candidate!; -} - -/** - * Replaces the {@linkcode ModifierType} of the entries within {@linkcode options} with any - * {@linkcode ModifierOverride} entries listed in {@linkcode Overrides.ITEM_REWARD_OVERRIDE} - * up to the smallest amount of entries between {@linkcode options} and the override array. - * @param options Array of naturally rolled {@linkcode ModifierTypeOption}s - * @param party Array of the player's current party - */ -export function overridePlayerModifierTypeOptions(options: ModifierTypeOption[], party: PlayerPokemon[]) { - const minLength = Math.min(options.length, Overrides.ITEM_REWARD_OVERRIDE.length); - for (let i = 0; i < minLength; i++) { - const override: ModifierOverride = Overrides.ITEM_REWARD_OVERRIDE[i]; - const modifierFunc = modifierTypeInitObj[override.name]; - let modifierType: ModifierType | null = modifierFunc(); - - if (modifierType instanceof ModifierTypeGenerator) { - const pregenArgs = "type" in override && override.type !== null ? [override.type] : undefined; - modifierType = modifierType.generateType(party, pregenArgs); - } - - if (modifierType) { - options[i].type = modifierType.withIdFromFunc(modifierFunc).withTierFromPool(ModifierPoolType.PLAYER, party); - } - } -} - -export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseCost: number): ModifierTypeOption[] { - if (!(waveIndex % 10)) { - return []; - } - - const options = [ - [ - new ModifierTypeOption(modifierTypeInitObj.POTION(), 0, baseCost * 0.2), - new ModifierTypeOption(modifierTypeInitObj.ETHER(), 0, baseCost * 0.4), - new ModifierTypeOption(modifierTypeInitObj.REVIVE(), 0, baseCost * 2), - ], - [ - new ModifierTypeOption(modifierTypeInitObj.SUPER_POTION(), 0, baseCost * 0.45), - new ModifierTypeOption(modifierTypeInitObj.FULL_HEAL(), 0, baseCost), - ], - [ - new ModifierTypeOption(modifierTypeInitObj.ELIXIR(), 0, baseCost), - new ModifierTypeOption(modifierTypeInitObj.MAX_ETHER(), 0, baseCost), - ], - [ - new ModifierTypeOption(modifierTypeInitObj.HYPER_POTION(), 0, baseCost * 0.8), - new ModifierTypeOption(modifierTypeInitObj.MAX_REVIVE(), 0, baseCost * 2.75), - new ModifierTypeOption(modifierTypeInitObj.MEMORY_MUSHROOM(), 0, baseCost * 4), - ], - [ - new ModifierTypeOption(modifierTypeInitObj.MAX_POTION(), 0, baseCost * 1.5), - new ModifierTypeOption(modifierTypeInitObj.MAX_ELIXIR(), 0, baseCost * 2.5), - ], - [new ModifierTypeOption(modifierTypeInitObj.FULL_RESTORE(), 0, baseCost * 2.25)], - [new ModifierTypeOption(modifierTypeInitObj.SACRED_ASH(), 0, baseCost * 10)], - ]; - return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); -} - -/** - * Generates a ModifierType from the specified pool - * @param party party of the trainer using the item - * @param poolType PLAYER/WILD/TRAINER - * @param tier If specified, will override the initial tier of an item (can still upgrade with luck) - * @param upgradeCount If defined, means that this is a new ModifierType being generated to override another via luck upgrade. Used for recursive logic - * @param retryCount Max allowed tries before the next tier down is checked for a valid ModifierType - * @param allowLuckUpgrades Default true. If false, will not allow ModifierType to randomly upgrade to next tier - */ -function getNewModifierTypeOption( - party: Pokemon[], - poolType: ModifierPoolType, - baseTier?: RewardTier, - upgradeCount?: number, - retryCount = 0, - allowLuckUpgrades = true, -): ModifierTypeOption | null { - const player = !poolType; - const pool = getModifierPoolForType(poolType); - const thresholds = getPoolThresholds(poolType); - - let tier = 0; - if (isNullOrUndefined(baseTier)) { - baseTier = randomBaseTier(); - } - if (isNullOrUndefined(upgradeCount)) { - upgradeCount = allowLuckUpgrades ? getUpgradeCount(party, player, baseTier) : 0; - tier = baseTier + upgradeCount; - } else { - tier = baseTier; - } - - 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 (index === undefined) { - return null; - } - - if (player) { - console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]); - } - let modifierType: ModifierType | null = pool[tier][index].modifierType; - if (modifierType instanceof ModifierTypeGenerator) { - modifierType = (modifierType as ModifierTypeGenerator).generateType(party); - if (modifierType === null) { - if (player) { - console.log(RewardTier[tier], upgradeCount); - } - return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount); - } - } - - console.log(modifierType, !player ? "(enemy)" : ""); - - return new ModifierTypeOption(modifierType as ModifierType, upgradeCount!); // TODO: is this bang correct? -} - -function getPoolThresholds(poolType: ModifierPoolType) { - let thresholds: object; - switch (poolType) { - case ModifierPoolType.PLAYER: - thresholds = modifierPoolThresholds; - break; - } - return thresholds; -} - -function randomBaseTier(): RewardTier { - const tierValue = randSeedInt(1024); - - if (tierValue > 255) { - return RewardTier.COMMON; - } - if (tierValue > 60) { - return RewardTier.GREAT; - } - if (tierValue > 12) { - return RewardTier.ULTRA; - } - if (tierValue) { - return RewardTier.ROGUE; - } - return RewardTier.MASTER; -} - -function getUpgradeCount( - party: Pokemon[], - player: boolean, - baseTier: RewardTier, - allowLuckUpgrades = true, -): RewardTier { - const pool = getModifierPoolForType(ModifierPoolType.PLAYER); - let upgradeCount = 0; - if (player) { - if (baseTier < RewardTier.MASTER && allowLuckUpgrades) { - const partyLuckValue = getPartyLuckValue(party); - const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4)); - while (pool.hasOwnProperty(baseTier + upgradeCount + 1) && pool[baseTier + upgradeCount + 1].length) { - if (randSeedInt(upgradeOdds) < 4) { - upgradeCount++; - } else { - break; - } - } - } - } - return upgradeCount; -} - -export function getDefaultModifierTypeForTier(tier: RewardTier): ModifierType { - const modifierPool = getModifierPoolForType(ModifierPoolType.PLAYER); - let modifierType: ModifierType | WeightedModifierType = modifierPool[tier || RewardTier.COMMON][0]; - if (modifierType instanceof WeightedModifierType) { - modifierType = (modifierType as WeightedModifierType).modifierType; - } - return modifierType; -} - -export class ModifierTypeOption { - public type: ModifierType; - public upgradeCount: number; - public cost: number; - - constructor(type: ModifierType, upgradeCount: number, cost = 0) { - this.type = type; - this.upgradeCount = upgradeCount; - this.cost = Math.min(Math.round(cost), Number.MAX_SAFE_INTEGER); - } -} - -/** - * Calculates the team's luck value. - * @param party The player's party. - * @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode. - */ -export function getPartyLuckValue(party: Pokemon[]): number { - if (globalScene.gameMode.isDaily) { - const DailyLuck = new NumberHolder(0); - globalScene.executeWithSeedOffset( - () => { - DailyLuck.value = randSeedInt(15); // Random number between 0 and 14 - }, - 0, - globalScene.seed, - ); - return DailyLuck.value; - } - const eventSpecies = timedEventManager.getEventLuckBoostedSpecies(); - const luck = Phaser.Math.Clamp( - party - .map(p => (p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 1 : 0) : 0)) - .reduce((total: number, value: number) => (total += value), 0), - 0, - 14, - ); - return Math.min(timedEventManager.getEventLuckBoost() + (luck ?? 0), 14); -} - -export function getLuckString(luckValue: number): string { - return ["D", "C", "C+", "B-", "B", "B+", "A-", "A", "A+", "A++", "S", "S+", "SS", "SS+", "SSS"][luckValue]; -} - -export function getLuckTextTint(luckValue: number): number { - let modifierTier: RewardTier; - if (luckValue > 11) { - modifierTier = RewardTier.LUXURY; - } else if (luckValue > 9) { - modifierTier = RewardTier.MASTER; - } else if (luckValue > 5) { - modifierTier = RewardTier.ROGUE; - } else if (luckValue > 2) { - modifierTier = RewardTier.ULTRA; - } else if (luckValue) { - modifierTier = RewardTier.GREAT; - } else { - modifierTier = RewardTier.COMMON; - } - return getModifierTierTextTint(modifierTier); -} - -export function initModifierTypes() { - for (const [key, value] of Object.entries(modifierTypeInitObj)) { - modifierTypes[key] = value; - } -} - -// TODO: If necessary, add the rest of the modifier types here. -// For now, doing the minimal work until the modifier rework lands. -const ModifierTypeConstructorMap = Object.freeze({ - ModifierTypeGenerator, -}); - -/** - * Map of of modifier type strings to their constructor type - */ -export type ModifierTypeConstructorMap = typeof ModifierTypeConstructorMap; - -/** - * Map of modifier type strings to their instance type - */ -export type ModifierTypeInstanceMap = { - [K in keyof ModifierTypeConstructorMap]: InstanceType; -}; - -export type ModifierTypeString = keyof ModifierTypeConstructorMap; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts deleted file mode 100644 index 15c42fa15e8..00000000000 --- a/src/modifier/modifier.ts +++ /dev/null @@ -1,620 +0,0 @@ -import { globalScene } from "#app/global-scene"; -import Overrides from "#app/overrides"; -import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions"; -import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters"; -import { getLevelTotalExp } from "#data/exp"; -import { MAX_PER_TYPE_POKEBALLS } from "#data/pokeball"; -import { HeldItemId } from "#enums/held-item-id"; -import { LearnMoveType } from "#enums/learn-move-type"; -import type { Nature } from "#enums/nature"; -import type { PokeballType } from "#enums/pokeball"; -import type { PokemonType } from "#enums/pokemon-type"; -import { SpeciesId } from "#enums/species-id"; -import type { PlayerPokemon, Pokemon } from "#field/pokemon"; -import type { HeldItemConfiguration } from "#items/held-item-data-types"; -import { assignItemsFromConfiguration } from "#items/held-item-pool"; -import { TrainerItemEffect } from "#items/trainer-item"; -import type { TrainerItemConfiguration } from "#items/trainer-item-data-types"; -import type { - EvolutionItemModifierType, - ModifierType, - TerastallizeModifierType, - TmModifierType, -} from "#modifiers/modifier-type"; -import type { VoucherType } from "#system/voucher"; -import type { ModifierInstanceMap, ModifierString } from "#types/modifier-types"; -import { isNullOrUndefined, NumberHolder } from "#utils/common"; - -export type ModifierPredicate = (modifier: Modifier) => boolean; - -export abstract class Modifier { - public type: ModifierType; - - constructor(type: ModifierType) { - this.type = type; - } - - /** - * Return whether this modifier is of the given class - * - * @remarks - * Used to avoid requiring the caller to have imported the specific modifier class, avoiding circular dependencies. - * - * @param modifier - The modifier to check against - * @returns Whether the modiifer is an instance of the given type - */ - public is(modifier: T): this is ModifierInstanceMap[T] { - const targetModifier = ModifierClassMap[modifier]; - if (!targetModifier) { - return false; - } - return this instanceof targetModifier; - } - - match(_modifier: Modifier): boolean { - return false; - } - - /** - * Checks if {@linkcode Modifier} should be applied - * @param _args parameters passed to {@linkcode Modifier.apply} - * @returns always `true` by default - */ - shouldApply(..._args: Parameters): boolean { - return true; - } - - /** - * Handles applying of {@linkcode Modifier} - * @param args collection of all passed parameters - */ - abstract apply(...args: unknown[]): boolean; -} - -export abstract class ConsumableModifier extends Modifier { - add(_modifiers: Modifier[]): boolean { - return true; - } -} - -export class AddPokeballModifier extends ConsumableModifier { - private pokeballType: PokeballType; - private count: number; - - constructor(type: ModifierType, pokeballType: PokeballType, count: number) { - super(type); - - this.pokeballType = pokeballType; - this.count = count; - } - - /** - * Applies {@linkcode AddPokeballModifier} - * @param battleScene {@linkcode BattleScene} - * @returns always `true` - */ - override apply(): boolean { - const pokeballCounts = globalScene.pokeballCounts; - pokeballCounts[this.pokeballType] = Math.min( - pokeballCounts[this.pokeballType] + this.count, - MAX_PER_TYPE_POKEBALLS, - ); - - return true; - } -} - -export class AddVoucherModifier extends ConsumableModifier { - private voucherType: VoucherType; - private count: number; - - constructor(type: ModifierType, voucherType: VoucherType, count: number) { - super(type); - - this.voucherType = voucherType; - this.count = count; - } - - /** - * Applies {@linkcode AddVoucherModifier} - * @param battleScene {@linkcode BattleScene} - * @returns always `true` - */ - override apply(): boolean { - const voucherCounts = globalScene.gameData.voucherCounts; - voucherCounts[this.voucherType] += this.count; - - return true; - } -} - -export abstract class ConsumablePokemonModifier extends ConsumableModifier { - public pokemonId: number; - - constructor(type: ModifierType, pokemonId: number) { - super(type); - - this.pokemonId = pokemonId; - } - - /** - * Checks if {@linkcode ConsumablePokemonModifier} should be applied - * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item - * @param _args N/A - * @returns `true` if {@linkcode ConsumablePokemonModifier} should be applied - */ - override shouldApply(playerPokemon?: PlayerPokemon, ..._args: unknown[]): boolean { - return !!playerPokemon && (this.pokemonId === -1 || playerPokemon.id === this.pokemonId); - } - - /** - * Applies {@linkcode ConsumablePokemonModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item - * @param args Additional arguments passed to {@linkcode ConsumablePokemonModifier.apply} - */ - abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean; - - getPokemon() { - return globalScene.getPlayerParty().find(p => p.id === this.pokemonId); - } -} - -export class TerrastalizeModifier extends ConsumablePokemonModifier { - public override type: TerastallizeModifierType; - public teraType: PokemonType; - - constructor(type: TerastallizeModifierType, pokemonId: number, teraType: PokemonType) { - super(type, pokemonId); - - this.teraType = teraType; - } - - /** - * Checks if {@linkcode TerrastalizeModifier} should be applied - * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item - * @returns `true` if the {@linkcode TerrastalizeModifier} should be applied - */ - override shouldApply(playerPokemon?: PlayerPokemon): boolean { - return ( - super.shouldApply(playerPokemon) && - [playerPokemon?.species.speciesId, playerPokemon?.fusionSpecies?.speciesId].filter( - s => s === SpeciesId.TERAPAGOS || s === SpeciesId.OGERPON || s === SpeciesId.SHEDINJA, - ).length === 0 - ); - } - - /** - * Applies {@linkcode TerrastalizeModifier} - * @param pokemon The {@linkcode PlayerPokemon} that consumes the item - * @returns `true` if hp was restored - */ - override apply(pokemon: Pokemon): boolean { - pokemon.teraType = this.teraType; - return true; - } -} - -export class PokemonHpRestoreModifier extends ConsumablePokemonModifier { - private restorePoints: number; - private restorePercent: number; - private healStatus: boolean; - public fainted: boolean; - - constructor( - type: ModifierType, - pokemonId: number, - restorePoints: number, - restorePercent: number, - healStatus: boolean, - fainted?: boolean, - ) { - super(type, pokemonId); - - this.restorePoints = restorePoints; - this.restorePercent = restorePercent; - this.healStatus = healStatus; - this.fainted = !!fainted; - } - - /** - * Checks if {@linkcode PokemonHpRestoreModifier} should be applied - * @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item - * @param multiplier The multiplier of the hp restore - * @returns `true` if the {@linkcode PokemonHpRestoreModifier} should be applied - */ - override shouldApply(playerPokemon?: PlayerPokemon, multiplier?: number): boolean { - return ( - super.shouldApply(playerPokemon) && - (this.fainted || (!isNullOrUndefined(multiplier) && typeof multiplier === "number")) - ); - } - - /** - * Applies {@linkcode PokemonHpRestoreModifier} - * @param pokemon The {@linkcode PlayerPokemon} that consumes the item - * @param multiplier The multiplier of the hp restore - * @returns `true` if hp was restored - */ - override apply(pokemon: Pokemon, multiplier: number): boolean { - if (!pokemon.hp === this.fainted) { - let restorePoints = this.restorePoints; - if (!this.fainted) { - restorePoints = Math.floor(restorePoints * multiplier); - } - if (this.fainted || this.healStatus) { - pokemon.resetStatus(true, true, false, false); - } - pokemon.hp = Math.min( - pokemon.hp + - Math.max(Math.ceil(Math.max(Math.floor(this.restorePercent * 0.01 * pokemon.getMaxHp()), restorePoints)), 1), - pokemon.getMaxHp(), - ); - return true; - } - return false; - } -} - -export class PokemonStatusHealModifier extends ConsumablePokemonModifier { - /** - * Applies {@linkcode PokemonStatusHealModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that gets healed from the status - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon): boolean { - playerPokemon.resetStatus(true, true, false, false); - return true; - } -} - -export abstract class ConsumablePokemonMoveModifier extends ConsumablePokemonModifier { - public moveIndex: number; - - constructor(type: ModifierType, pokemonId: number, moveIndex: number) { - super(type, pokemonId); - - this.moveIndex = moveIndex; - } -} - -export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier { - private restorePoints: number; - - constructor(type: ModifierType, pokemonId: number, moveIndex: number, restorePoints: number) { - super(type, pokemonId, moveIndex); - - this.restorePoints = restorePoints; - } - - /** - * Applies {@linkcode PokemonPpRestoreModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should get move pp restored - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon): boolean { - const move = playerPokemon.getMoveset()[this.moveIndex]; - - if (move) { - move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; - } - - return true; - } -} - -export class PokemonAllMovePpRestoreModifier extends ConsumablePokemonModifier { - private restorePoints: number; - - constructor(type: ModifierType, pokemonId: number, restorePoints: number) { - super(type, pokemonId); - - this.restorePoints = restorePoints; - } - - /** - * Applies {@linkcode PokemonAllMovePpRestoreModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should get all move pp restored - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon): boolean { - for (const move of playerPokemon.getMoveset()) { - if (move) { - move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; - } - } - - return true; - } -} - -export class PokemonPpUpModifier extends ConsumablePokemonMoveModifier { - private upPoints: number; - - constructor(type: ModifierType, pokemonId: number, moveIndex: number, upPoints: number) { - super(type, pokemonId, moveIndex); - - this.upPoints = upPoints; - } - - /** - * Applies {@linkcode PokemonPpUpModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that gets a pp up on move-slot {@linkcode moveIndex} - * @returns - */ - override apply(playerPokemon: PlayerPokemon): boolean { - const move = playerPokemon.getMoveset()[this.moveIndex]; - - if (move && !move.maxPpOverride) { - move.ppUp = Math.min(move.ppUp + this.upPoints, 3); - } - - return true; - } -} - -export class PokemonNatureChangeModifier extends ConsumablePokemonModifier { - public nature: Nature; - - constructor(type: ModifierType, pokemonId: number, nature: Nature) { - super(type, pokemonId); - - this.nature = nature; - } - - /** - * Applies {@linkcode PokemonNatureChangeModifier} - * @param playerPokemon {@linkcode PlayerPokemon} to apply the {@linkcode Nature} change to - * @returns - */ - override apply(playerPokemon: PlayerPokemon): boolean { - playerPokemon.setCustomNature(this.nature); - globalScene.gameData.unlockSpeciesNature(playerPokemon.species, this.nature); - - return true; - } -} - -export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier { - /** - * Applies {@linkcode PokemonLevelIncrementModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should get levels incremented - * @param levelCount The amount of levels to increment - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon, levelCount: NumberHolder = new NumberHolder(1)): boolean { - globalScene.applyPlayerItems(TrainerItemEffect.LEVEL_INCREMENT_BOOSTER, { numberHolder: levelCount }); - - playerPokemon.level += levelCount.value; - if (playerPokemon.level <= globalScene.getMaxExpLevel(true)) { - playerPokemon.exp = getLevelTotalExp(playerPokemon.level, playerPokemon.species.growthRate); - playerPokemon.levelExp = 0; - } - - playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY); - - globalScene.phaseManager.unshiftNew( - "LevelUpPhase", - globalScene.getPlayerParty().indexOf(playerPokemon), - playerPokemon.level - levelCount.value, - playerPokemon.level, - ); - - return true; - } -} - -export class TmModifier extends ConsumablePokemonModifier { - public override type: TmModifierType; - - /** - * Applies {@linkcode TmModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should learn the TM - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon): boolean { - globalScene.phaseManager.unshiftNew( - "LearnMovePhase", - globalScene.getPlayerParty().indexOf(playerPokemon), - this.type.moveId, - LearnMoveType.TM, - ); - - return true; - } -} - -export class RememberMoveModifier extends ConsumablePokemonModifier { - public levelMoveIndex: number; - - constructor(type: ModifierType, pokemonId: number, levelMoveIndex: number) { - super(type, pokemonId); - - this.levelMoveIndex = levelMoveIndex; - } - - /** - * Applies {@linkcode RememberMoveModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should remember the move - * @returns always `true` - */ - override apply(playerPokemon: PlayerPokemon, cost?: number): boolean { - globalScene.phaseManager.unshiftNew( - "LearnMovePhase", - globalScene.getPlayerParty().indexOf(playerPokemon), - playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], - LearnMoveType.MEMORY, - cost, - ); - - return true; - } -} - -export class EvolutionItemModifier extends ConsumablePokemonModifier { - public override type: EvolutionItemModifierType; - /** - * Applies {@linkcode EvolutionItemModifier} - * @param playerPokemon The {@linkcode PlayerPokemon} that should evolve via item - * @returns `true` if the evolution was successful - */ - override apply(playerPokemon: PlayerPokemon): boolean { - let matchingEvolution = pokemonEvolutions.hasOwnProperty(playerPokemon.species.speciesId) - ? pokemonEvolutions[playerPokemon.species.speciesId].find( - e => e.evoItem === this.type.evolutionItem && e.validate(playerPokemon, false, e.item!), - ) - : null; - - if (!matchingEvolution && playerPokemon.isFusion()) { - matchingEvolution = pokemonEvolutions[playerPokemon.fusionSpecies!.speciesId].find( - e => e.evoItem === this.type.evolutionItem && e.validate(playerPokemon, true, e.item!), - ); - if (matchingEvolution) { - matchingEvolution = new FusionSpeciesFormEvolution(playerPokemon.species.speciesId, matchingEvolution); - } - } - - if (matchingEvolution) { - globalScene.phaseManager.unshiftNew("EvolutionPhase", playerPokemon, matchingEvolution, playerPokemon.level - 1); - return true; - } - - return false; - } -} - -export class FusePokemonModifier extends ConsumablePokemonModifier { - public fusePokemonId: number; - - constructor(type: ModifierType, pokemonId: number, fusePokemonId: number) { - super(type, pokemonId); - - this.fusePokemonId = fusePokemonId; - } - - /** - * Checks if {@linkcode FusePokemonModifier} should be applied - * @param playerPokemon {@linkcode PlayerPokemon} that should be fused - * @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon} - * @returns `true` if {@linkcode FusePokemonModifier} should be applied - */ - override shouldApply(playerPokemon?: PlayerPokemon, playerPokemon2?: PlayerPokemon): boolean { - return ( - super.shouldApply(playerPokemon, playerPokemon2) && !!playerPokemon2 && this.fusePokemonId === playerPokemon2.id - ); - } - - /** - * Applies {@linkcode FusePokemonModifier} - * @param playerPokemon {@linkcode PlayerPokemon} that should be fused - * @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon} - * @returns always Promise - */ - override apply(playerPokemon: PlayerPokemon, playerPokemon2: PlayerPokemon): boolean { - playerPokemon.fuse(playerPokemon2); - return true; - } -} - -export class MoneyRewardModifier extends ConsumableModifier { - private moneyMultiplier: number; - - constructor(type: ModifierType, moneyMultiplier: number) { - super(type); - - this.moneyMultiplier = moneyMultiplier; - } - - /** - * Applies {@linkcode MoneyRewardModifier} - * @returns always `true` - */ - override apply(): boolean { - const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); - - globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount }); - - globalScene.addMoney(moneyAmount.value); - - for (const p of globalScene.getPlayerParty()) { - if (p.species?.speciesId === SpeciesId.GIMMIGHOUL || p.fusionSpecies?.speciesId === SpeciesId.GIMMIGHOUL) { - const factor = Math.min(Math.floor(this.moneyMultiplier), 3); - p.heldItemManager.add(HeldItemId.GIMMIGHOUL_EVO_TRACKER, factor); - } - } - - return true; - } -} - -/** - * Uses either `MODIFIER_OVERRIDE` in overrides.ts to set {@linkcode PersistentModifier}s for either: - * - The player - * - The enemy - * @param isPlayer {@linkcode boolean} for whether the player (`true`) or enemy (`false`) is being overridden - */ -export function overrideTrainerItems(isPlayer = true): void { - const trainerItemsOverride: TrainerItemConfiguration = isPlayer - ? Overrides.STARTING_TRAINER_ITEMS_OVERRIDE - : Overrides.OPP_TRAINER_ITEMS_OVERRIDE; - if (!trainerItemsOverride || trainerItemsOverride.length === 0 || !globalScene) { - return; - } - - // If it's the opponent, clear all of their current modifiers to avoid stacking - if (!isPlayer) { - globalScene.clearEnemyItems(); - } - - globalScene.assignTrainerItemsFromConfiguration(trainerItemsOverride, isPlayer); -} - -/** - * Uses either `HELD_ITEMS_OVERRIDE` in overrides.ts to set {@linkcode PokemonHeldItemModifier}s for either: - * - The first member of the player's team when starting a new game - * - An enemy {@linkcode Pokemon} being spawned in - * @param pokemon {@linkcode Pokemon} whose held items are being overridden - * @param isPlayer {@linkcode boolean} for whether the {@linkcode pokemon} is the player's (`true`) or an enemy (`false`) - */ -export function overrideHeldItems(pokemon: Pokemon, isPlayer = true): void { - const heldItemsOverride: HeldItemConfiguration = isPlayer - ? Overrides.STARTING_HELD_ITEMS_OVERRIDE - : Overrides.OPP_HELD_ITEMS_OVERRIDE; - if (!heldItemsOverride || heldItemsOverride.length === 0 || !globalScene) { - return; - } - - if (!isPlayer) { - pokemon.heldItemManager.clearItems(); - } - - assignItemsFromConfiguration(heldItemsOverride, pokemon); -} - -/** - * Private map from modifier strings to their constructors. - * - * @remarks - * Used for {@linkcode Modifier.is} to check if a modifier is of a certain type without - * requiring modifier types to be imported in every file. - */ -const ModifierClassMap = Object.freeze({ - ConsumableModifier, - AddPokeballModifier, - AddVoucherModifier, - ConsumablePokemonModifier, - TerrastalizeModifier, - PokemonHpRestoreModifier, - PokemonStatusHealModifier, - ConsumablePokemonMoveModifier, - PokemonPpRestoreModifier, - PokemonAllMovePpRestoreModifier, - PokemonPpUpModifier, - PokemonNatureChangeModifier, - PokemonLevelIncrementModifier, - TmModifier, - RememberMoveModifier, - EvolutionItemModifier, - FusePokemonModifier, -}); - -export type ModifierConstructorMap = typeof ModifierClassMap; diff --git a/src/overrides.ts b/src/overrides.ts index 0bf44d59329..3a96a1c7bea 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -22,8 +22,8 @@ import { Unlockables } from "#enums/unlockables"; import { VariantTier } from "#enums/variant-tier"; import { WeatherType } from "#enums/weather-type"; import { HeldItemConfiguration } from "#items/held-item-data-types"; +import { type RewardOverride } from "#items/reward"; import { TrainerItemConfiguration } from "#items/trainer-item-data-types"; -import { type ModifierOverride } from "#modifiers/modifier-type"; import { Variant } from "#sprites/variant"; /** @@ -248,16 +248,16 @@ class DefaultOverrides { // ------------------------- /** * Overrides labeled `MODIFIER` deal with any modifier so long as it doesn't require a party - * member to hold it (typically this is, extends, or generates a {@linkcode ModifierType}), + * member to hold it (typically this is, extends, or generates a {@linkcode Reward}), * like `EXP_SHARE`, `CANDY_JAR`, etc. * - * Overrides labeled `HELD_ITEM` specifically pertain to any entry in {@linkcode modifierTypes} that - * extends, or generates a {@linkcode PokemonHeldItemModifierType}, like `SOUL_DEW`, `TOXIC_ORB`, etc. + * Overrides labeled `HELD_ITEM` specifically pertain to any entry in {@linkcode allRewards} that + * extends, or generates a {@linkcode PokemonHeldItemReward}, like `SOUL_DEW`, `TOXIC_ORB`, etc. * * Note that, if count is not provided, it will default to 1. * * Additionally, note that some held items and modifiers are grouped together via - * a {@linkcode ModifierTypeGenerator} and require pre-generation arguments to get + * a {@linkcode RewardGenerator} and require pre-generation arguments to get * a specific item from that group. If a type is not set, the generator will either * use the party to weight item choice or randomly pick an item. * @@ -273,25 +273,25 @@ class DefaultOverrides { * STARTING_HELD_ITEM_OVERRIDE = [{name: "BERRY"}] * ``` */ - /** Override array of {@linkcode ModifierOverride}s used to provide held items to first party member when starting a new game. */ + /** Override array of {@linkcode RewardOverride}s used to provide held items to first party member when starting a new game. */ readonly STARTING_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = []; - /** Override array of {@linkcode ModifierOverride}s used to provide held items to enemies on spawn. */ + /** Override array of {@linkcode RewardOverride}s used to provide held items to enemies on spawn. */ readonly OPP_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = []; - /** Override array of {@linkcode ModifierOverride}s used to provide held items to first party member when starting a new game. */ + /** Override array of {@linkcode RewardOverride}s used to provide held items to first party member when starting a new game. */ readonly STARTING_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = []; - /** Override array of {@linkcode ModifierOverride}s used to provide held items to enemies on spawn. */ + /** Override array of {@linkcode RewardOverride}s used to provide held items to enemies on spawn. */ readonly OPP_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = []; /** - * Override array of {@linkcode ModifierOverride}s used to replace the generated item rolls after a wave. + * Override array of {@linkcode RewardOverride}s used to replace the generated reward rolls after a wave. * * If less entries are listed than rolled, only those entries will be used to replace the corresponding items while the rest randomly generated. * If more entries are listed than rolled, only the first X entries will be used, where X is the number of items rolled. * * Note that, for all items in the array, `count` is not used. */ - readonly ITEM_REWARD_OVERRIDE: ModifierOverride[] = []; + readonly REWARD_OVERRIDE: RewardOverride[] = []; /** * If `true`, disable all non-scripted opponent trainer encounters. diff --git a/src/phase-manager.ts b/src/phase-manager.ts index 5fb56aff54e..db488e1583f 100644 --- a/src/phase-manager.ts +++ b/src/phase-manager.ts @@ -4,7 +4,7 @@ import { type PhasePriorityQueue, PostSummonPhasePriorityQueue } from "#data/pha import type { DynamicPhaseType } from "#enums/dynamic-phase-type"; import type { Pokemon } from "#field/pokemon"; import { ActivatePriorityQueuePhase } from "#phases/activate-priority-queue-phase"; -import { AddEnemyBuffModifierPhase } from "#phases/add-enemy-buff-modifier-phase"; +import { AddEnemyTokenPhase } from "#phases/add-enemy-token-phase"; import { AttemptCapturePhase } from "#phases/attempt-capture-phase"; import { AttemptRunPhase } from "#phases/attempt-run-phase"; import { BattleEndPhase } from "#phases/battle-end-phase"; @@ -116,7 +116,7 @@ import { type Constructor, coerceArray } from "#utils/common"; */ const PHASES = Object.freeze({ ActivatePriorityQueuePhase, - AddEnemyBuffModifierPhase, + AddEnemyTokenPhase, AttemptCapturePhase, AttemptRunPhase, BattleEndPhase, diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-token-phase.ts similarity index 61% rename from src/phases/add-enemy-buff-modifier-phase.ts rename to src/phases/add-enemy-token-phase.ts index 5d3d529420b..af617b49bb4 100644 --- a/src/phases/add-enemy-buff-modifier-phase.ts +++ b/src/phases/add-enemy-token-phase.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { assignEnemyBuffTokenForWave } from "#items/trainer-item-pool"; -export class AddEnemyBuffModifierPhase extends Phase { - public readonly phaseName = "AddEnemyBuffModifierPhase"; +export class AddEnemyTokenPhase extends Phase { + public readonly phaseName = "AddEnemyTokenPhase"; start() { super.start(); const waveIndex = globalScene.currentBattle.waveIndex; - const tier = !(waveIndex % 1000) ? RewardTier.ULTRA : !(waveIndex % 250) ? RewardTier.GREAT : RewardTier.COMMON; + const tier = !(waveIndex % 1000) ? RarityTier.ULTRA : !(waveIndex % 250) ? RarityTier.GREAT : RarityTier.COMMON; const count = Math.ceil(waveIndex / 250); for (let i = 0; i < count; i++) { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 0649d6d7208..fd8783a9e28 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -2,12 +2,12 @@ import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { allHeldItems } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemCategoryId, isItemInCategory } from "#enums/held-item-id"; import { CommonAnim } from "#enums/move-anims-common"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; import type { BerryHeldItem } from "#items/berry"; -import { HeldItemEffect } from "#items/held-item"; import { FieldPhase } from "#phases/field-phase"; import { BooleanHolder } from "#utils/common"; import i18next from "i18next"; diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 94923ae8c1f..30dca435637 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -228,7 +228,7 @@ export class EggHatchPhase extends Phase { if (globalScene.phaseManager.findPhase(p => p.is("EggHatchPhase"))) { this.eggHatchHandler.clear(); } else { - globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); + globalScene.time.delayedCall(250, () => globalScene.setItemsVisible(true)); } super.end(); } diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts index c236c5c3abc..94df2d1143c 100644 --- a/src/phases/egg-summary-phase.ts +++ b/src/phases/egg-summary-phase.ts @@ -39,7 +39,7 @@ export class EggSummaryPhase extends Phase { } end() { - globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); + globalScene.time.delayedCall(250, () => globalScene.setItemsVisible(true)); globalScene.ui.setModeForceTransition(UiMode.MESSAGE).then(() => { super.end(); }); diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 9596ed9a6b0..81a9ef649ab 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -21,7 +21,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { UiMode } from "#enums/ui-mode"; import { EncounterPhaseEvent } from "#events/battle-scene"; import type { Pokemon } from "#field/pokemon"; -import { overrideHeldItems, overrideTrainerItems } from "#modifiers/modifier"; +import { overrideHeldItems, overrideTrainerItems } from "#items/item-overrides"; import { getEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { doTrainerExclamation } from "#mystery-encounters/encounter-phase-utils"; import { getGoldenBugNetSpecies } from "#mystery-encounters/encounter-pokemon-utils"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 1cea69a6baf..504d2bc2b0d 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -10,12 +10,12 @@ import { BattleType } from "#enums/battle-type"; import type { BattlerIndex } from "#enums/battler-index"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HitResult } from "#enums/hit-result"; import { StatusEffect } from "#enums/status-effect"; import { SwitchType } from "#enums/switch-type"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { PokemonMove } from "#moves/pokemon-move"; import { PokemonPhase } from "#phases/pokemon-phase"; import { isNullOrUndefined } from "#utils/common"; diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index dafb2f5693e..debb9aacb86 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -2,7 +2,7 @@ import { pokerogueApi } from "#api/pokerogue-api"; import { clientSessionId } from "#app/account"; import { globalScene } from "#app/global-scene"; import { pokemonEvolutions } from "#balance/pokemon-evolutions"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { getCharVariantFromDialogue } from "#data/dialogue"; import type { PokemonSpecies } from "#data/pokemon-species"; import { BattleType } from "#enums/battle-type"; @@ -151,10 +151,10 @@ export class GameOverPhase extends BattlePhase { this.handleUnlocks(); for (const species of this.firstRibbons) { - globalScene.phaseManager.unshiftNew("RibbonRewardPhase", modifierTypes.VOUCHER_PLUS, species); + globalScene.phaseManager.unshiftNew("RibbonRewardPhase", allRewards.VOUCHER_PLUS, species); } if (!firstClear) { - globalScene.phaseManager.unshiftNew("GameOverRewardPhase", modifierTypes.VOUCHER_PREMIUM); + globalScene.phaseManager.unshiftNew("GameOverRewardPhase", allRewards.VOUCHER_PREMIUM); } } this.getRunHistoryEntry().then(runHistoryEntry => { diff --git a/src/phases/game-over-reward-phase.ts b/src/phases/game-over-reward-phase.ts index 0fe2eae5a61..f8a15e05185 100644 --- a/src/phases/game-over-reward-phase.ts +++ b/src/phases/game-over-reward-phase.ts @@ -7,7 +7,7 @@ export class GameOverRewardPhase extends RewardPhase { public readonly phaseName = "GameOverRewardPhase"; doReward(): Promise { return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); + const newModifier = this.reward.newModifier(); globalScene.addModifier(newModifier); // Sound loaded into game as is globalScene.playSound("level_up_fanfare"); diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index e47a6e0e14d..804b99122b9 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -11,6 +11,7 @@ import { ArenaTagSide } from "#enums/arena-tag-side"; import { BattlerIndex } from "#enums/battler-index"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; import { BattlerTagType } from "#enums/battler-tag-type"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HitCheckResult } from "#enums/hit-check-result"; import { HitResult } from "#enums/hit-result"; import { MoveCategory } from "#enums/move-category"; @@ -23,7 +24,6 @@ import { isReflected, isVirtual, MoveUseMode } from "#enums/move-use-mode"; import { PokemonType } from "#enums/pokemon-type"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { TrainerItemEffect } from "#items/trainer-item"; import { applyFilteredMoveAttrs, applyMoveAttrs } from "#moves/apply-attrs"; import type { Move, MoveAttr } from "#moves/move"; diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 9de008f0af8..4f9c51974a1 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -532,13 +532,13 @@ export class MysteryEncounterRewardsPhase extends Phase { } else { this.doEncounterRewardsAndContinue(); } - // Do not use ME's seedOffset for rewards, these should always be consistent with waveIndex (once per wave) + // Do not use ME's seedOffset for allRewards, these should always be consistent with waveIndex (once per wave) }, globalScene.currentBattle.waveIndex * 1000); } } /** - * Queues encounter EXP and rewards phases, {@linkcode PostMysteryEncounterPhase}, and ends phase + * Queues encounter EXP and allRewards phases, {@linkcode PostMysteryEncounterPhase}, and ends phase */ doEncounterRewardsAndContinue() { const encounter = globalScene.currentBattle.mysteryEncounter!; diff --git a/src/phases/reward-phase.ts b/src/phases/reward-phase.ts index b3c243c3779..033a022958f 100644 --- a/src/phases/reward-phase.ts +++ b/src/phases/reward-phase.ts @@ -1,20 +1,19 @@ import { globalScene } from "#app/global-scene"; -import { type ModifierType, TrainerItemReward } from "#modifiers/modifier-type"; +import type { Reward } from "#items/reward"; import { BattlePhase } from "#phases/battle-phase"; -import type { ModifierTypeFunc } from "#types/modifier-types"; -import { getModifierType } from "#utils/modifier-utils"; +import type { RewardFunc } from "#types/rewards"; import i18next from "i18next"; export class RewardPhase extends BattlePhase { // RibbonRewardPhase extends RewardPhase and to make typescript happy // we need to use a union type here public readonly phaseName: "RewardPhase" | "RibbonRewardPhase" | "GameOverRewardPhase" = "RewardPhase"; - protected modifierType: ModifierType; + protected reward: Reward; - constructor(modifierTypeFunc: ModifierTypeFunc) { + constructor(rewardFunc: RewardFunc) { super(); - this.modifierType = getModifierType(modifierTypeFunc); + this.reward = rewardFunc(); } start() { @@ -25,16 +24,11 @@ export class RewardPhase extends BattlePhase { doReward(): Promise { return new Promise(resolve => { - if (this.modifierType instanceof TrainerItemReward) { - this.modifierType.apply(); - } else { - const newModifier = this.modifierType.newModifier(); - globalScene.addModifier(newModifier); - } + globalScene.applyReward(this.reward); globalScene.playSound("item_fanfare"); globalScene.ui.showText( i18next.t("battle:rewardGain", { - modifierName: this.modifierType.name, + modifierName: this.reward.name, }), null, () => resolve(), diff --git a/src/phases/ribbon-reward-phase.ts b/src/phases/ribbon-reward-phase.ts index 9c1737db2fc..42e5bd826a3 100644 --- a/src/phases/ribbon-reward-phase.ts +++ b/src/phases/ribbon-reward-phase.ts @@ -2,22 +2,22 @@ import { globalScene } from "#app/global-scene"; import type { PokemonSpecies } from "#data/pokemon-species"; import { UiMode } from "#enums/ui-mode"; import { RewardPhase } from "#phases/reward-phase"; -import type { ModifierTypeFunc } from "#types/modifier-types"; +import type { RewardFunc } from "#types/rewards"; import i18next from "i18next"; export class RibbonRewardPhase extends RewardPhase { public readonly phaseName = "RibbonRewardPhase"; private species: PokemonSpecies; - constructor(modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) { - super(modifierTypeFunc); + constructor(rewardFunc: RewardFunc, species: PokemonSpecies) { + super(rewardFunc); this.species = species; } doReward(): Promise { return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); + const newModifier = this.reward.newModifier(); globalScene.addModifier(newModifier); globalScene.playSound("level_up_fanfare"); globalScene.ui.setMode(UiMode.MESSAGE); diff --git a/src/phases/select-reward-phase.ts b/src/phases/select-reward-phase.ts index 5cbad1134eb..4c5e080733c 100644 --- a/src/phases/select-reward-phase.ts +++ b/src/phases/select-reward-phase.ts @@ -1,54 +1,53 @@ import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import type { RewardTier } from "#enums/reward-tier"; +import type { MoveId } from "#enums/move-id"; +import { RewardPoolType } from "#enums/reward-pool-type"; +import type { RarityTier } from "#enums/reward-tier"; import { UiMode } from "#enums/ui-mode"; -import { TrainerItemEffect } from "#items/trainer-item"; -import type { Modifier } from "#modifiers/modifier"; -import type { CustomModifierSettings, ModifierType, ModifierTypeOption } from "#modifiers/modifier-type"; +import type { + PokemonMoveRecallRewardParams, + PokemonMoveRewardParams, + PokemonRewardParams, + Reward, + RewardOption, +} from "#items/reward"; +import { FusePokemonReward, type PokemonMoveReward, PokemonReward, RememberMoveReward, TmReward } from "#items/reward"; import { - FormChangeItemReward, - FusePokemonModifierType, - getPlayerModifierTypeOptions, - getPlayerShopModifierTypeOptionsForWave, - HeldItemReward, - PokemonModifierType, - PokemonMoveModifierType, - PokemonPpRestoreModifierType, - PokemonPpUpModifierType, - RememberMoveModifierType, - regenerateModifierPoolThresholds, - TmModifierType, - TrainerItemReward, -} from "#modifiers/modifier-type"; + type CustomRewardSettings, + generatePlayerRewardOptions, + generateRewardPoolWeights, + getRewardPoolForType, +} from "#items/reward-pool-utils"; +import { getPlayerShopRewardOptionsForWave, isMoveReward, isRememberMoveReward, isTmReward } from "#items/reward-utils"; +import { TrainerItemEffect } from "#items/trainer-item"; import { BattlePhase } from "#phases/battle-phase"; -import { PartyOption, PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; +import { PartyOption, PartyUiHandler, PartyUiMode, type PokemonMoveSelectFilter } from "#ui/party-ui-handler"; import { type RewardSelectUiHandler, SHOP_OPTIONS_ROW_LIMIT } from "#ui/reward-select-ui-handler"; import { isNullOrUndefined, NumberHolder } from "#utils/common"; import i18next from "i18next"; -export type ModifierSelectCallback = (rowCursor: number, cursor: number) => boolean; +export type RewardSelectCallback = (rowCursor: number, cursor: number) => boolean; export class SelectRewardPhase extends BattlePhase { public readonly phaseName = "SelectRewardPhase"; private rerollCount: number; - private modifierTiers?: RewardTier[]; - private customModifierSettings?: CustomModifierSettings; + private rarityTiers?: RarityTier[]; + private customRewardSettings?: CustomRewardSettings; private isCopy: boolean; - private typeOptions: ModifierTypeOption[]; + private typeOptions: RewardOption[]; constructor( rerollCount = 0, - modifierTiers?: RewardTier[], - customModifierSettings?: CustomModifierSettings, + rarityTiers?: RarityTier[], + customRewardSettings?: CustomRewardSettings, isCopy = false, ) { super(); this.rerollCount = rerollCount; - this.modifierTiers = modifierTiers; - this.customModifierSettings = customModifierSettings; + this.rarityTiers = rarityTiers; + this.customRewardSettings = customRewardSettings; this.isCopy = isCopy; } @@ -67,14 +66,15 @@ export class SelectRewardPhase extends BattlePhase { const party = globalScene.getPlayerParty(); if (!this.isCopy) { - regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount); + generateRewardPoolWeights(getRewardPoolForType(this.getPoolType()), party, this.rerollCount); } - const modifierCount = this.getModifierCount(); + const rewardCount = this.getRewardCount(); - this.typeOptions = this.getModifierTypeOptions(modifierCount); + this.typeOptions = this.getRewardOptions(rewardCount); - const modifierSelectCallback = (rowCursor: number, cursor: number) => { + const rewardSelectCallback = (rowCursor: number, cursor: number) => { if (rowCursor < 0 || cursor < 0) { + // Attempt to skip the item pickup globalScene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { globalScene.ui.setOverlayMode( UiMode.CONFIRM, @@ -83,7 +83,7 @@ export class SelectRewardPhase extends BattlePhase { globalScene.ui.setMode(UiMode.MESSAGE); super.end(); }, - () => this.resetModifierSelect(modifierSelectCallback), + () => this.resetRewardSelect(rewardSelectCallback), ); }); return false; @@ -94,13 +94,13 @@ export class SelectRewardPhase extends BattlePhase { case 0: switch (cursor) { case 0: - return this.rerollModifiers(); + return this.rerollRewards(); case 1: - return this.openModifierTransferScreen(modifierSelectCallback); - // Check the party, pass a callback to restore the modifier select screen. + return this.openItemTransferScreen(rewardSelectCallback); + // Check the party, pass a callback to restore the reward select screen. case 2: globalScene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.CHECK, -1, () => { - this.resetModifierSelect(modifierSelectCallback); + this.resetRewardSelect(rewardSelectCallback); }); return true; case 3: @@ -108,38 +108,34 @@ export class SelectRewardPhase extends BattlePhase { default: return false; } - // Pick an option from the rewards + // Pick an option from the allRewards case 1: - return this.selectRewardModifierOption(cursor, modifierSelectCallback); + return this.selectRewardOption(cursor, rewardSelectCallback); // Pick an option from the shop default: { - return this.selectShopModifierOption(rowCursor, cursor, modifierSelectCallback); + return this.selectShopOption(rowCursor, cursor, rewardSelectCallback); } } }; - this.resetModifierSelect(modifierSelectCallback); + this.resetRewardSelect(rewardSelectCallback); } - // Pick a modifier from among the rewards and apply it - private selectRewardModifierOption(cursor: number, modifierSelectCallback: ModifierSelectCallback): boolean { + // Pick a reward from among the allRewards and apply it + private selectRewardOption(cursor: number, rewardSelectCallback: RewardSelectCallback): boolean { if (this.typeOptions.length === 0) { globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.MESSAGE); super.end(); return true; } - const modifierType = this.typeOptions[cursor].type; - return this.applyChosenModifier(modifierType, -1, modifierSelectCallback); + const reward = this.typeOptions[cursor].type; + return this.applyReward(reward, -1, rewardSelectCallback); } - // Pick a modifier from the shop and apply it - private selectShopModifierOption( - rowCursor: number, - cursor: number, - modifierSelectCallback: ModifierSelectCallback, - ): boolean { - const shopOptions = getPlayerShopModifierTypeOptionsForWave( + // Pick a reward from the shop and apply it + private selectShopOption(rowCursor: number, cursor: number, rewardSelectCallback: RewardSelectCallback): boolean { + const shopOptions = getPlayerShopRewardOptionsForWave( globalScene.currentBattle.waveIndex, globalScene.getWaveMoneyAmount(1), ); @@ -147,7 +143,7 @@ export class SelectRewardPhase extends BattlePhase { shopOptions[ rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT ]; - const modifierType = shopOption.type; + const reward = shopOption.type; // Apply Black Sludge to healing item cost const healingItemCost = new NumberHolder(shopOption.cost); globalScene.applyPlayerItems(TrainerItemEffect.HEAL_SHOP_COST, { numberHolder: healingItemCost }); @@ -158,39 +154,30 @@ export class SelectRewardPhase extends BattlePhase { return false; } - return this.applyChosenModifier(modifierType, cost, modifierSelectCallback); + return this.applyReward(reward, cost, rewardSelectCallback); } - // Apply a chosen modifier: do an effect or open the party menu - private applyChosenModifier( - modifierType: ModifierType, - cost: number, - modifierSelectCallback: ModifierSelectCallback, - ): boolean { - if (modifierType instanceof PokemonModifierType) { - if (modifierType instanceof HeldItemReward || modifierType instanceof FormChangeItemReward) { - this.openGiveHeldItemMenu(modifierType, modifierSelectCallback); - } else if (modifierType instanceof FusePokemonModifierType) { - this.openFusionMenu(modifierType, cost, modifierSelectCallback); + // Apply a chosen reward: do an effect or open the party menu + private applyReward(reward: Reward, cost: number, rewardSelectCallback: RewardSelectCallback): boolean { + if (reward instanceof PokemonReward) { + if (reward instanceof FusePokemonReward) { + this.openFusionMenu(reward, cost, rewardSelectCallback); } else { - this.openModifierMenu(modifierType, cost, modifierSelectCallback); + this.openPokemonRewardMenu(reward, cost, rewardSelectCallback); } - } else if (modifierType instanceof TrainerItemReward) { - console.log("WE GOT HERE"); - modifierType.apply(); + } else { + globalScene.applyReward(reward, {}, true); globalScene.updateItems(true); globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.MESSAGE); super.end(); - } else { - this.applyModifier(modifierType.newModifier()!); } return cost === -1; } - // Reroll rewards - private rerollModifiers() { - const rerollCost = this.getRerollCost(globalScene.lockModifierTiers); + // Reroll allRewards + private rerollRewards() { + const rerollCost = this.getRerollCost(globalScene.lockRarityTiers); if (rerollCost < 0 || globalScene.money < rerollCost) { globalScene.ui.playError(); return false; @@ -199,7 +186,7 @@ export class SelectRewardPhase extends BattlePhase { globalScene.phaseManager.unshiftNew( "SelectRewardPhase", this.rerollCount + 1, - this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as RewardTier[], + this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as RarityTier[], ); globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); @@ -212,12 +199,12 @@ export class SelectRewardPhase extends BattlePhase { return true; } - // Transfer modifiers among party pokemon - private openModifierTransferScreen(modifierSelectCallback: ModifierSelectCallback) { + // Transfer rewards among party pokemon + private openItemTransferScreen(rewardSelectCallback: RewardSelectCallback) { const party = globalScene.getPlayerParty(); globalScene.ui.setModeWithoutClear( UiMode.PARTY, - PartyUiMode.MODIFIER_TRANSFER, + PartyUiMode.ITEM_TRANSFER, -1, (fromSlotIndex: number, itemIndex: number, itemQuantity: number, toSlotIndex: number) => { if ( @@ -239,7 +226,7 @@ export class SelectRewardPhase extends BattlePhase { false, ); } else { - this.resetModifierSelect(modifierSelectCallback); + this.resetRewardSelect(rewardSelectCallback); } }, PartyUiHandler.FilterItemMaxStacks, @@ -249,36 +236,115 @@ export class SelectRewardPhase extends BattlePhase { // Toggle reroll lock private toggleRerollLock() { - const rerollCost = this.getRerollCost(globalScene.lockModifierTiers); + const rerollCost = this.getRerollCost(globalScene.lockRarityTiers); if (rerollCost < 0) { // Reroll lock button is also disabled when reroll is disabled globalScene.ui.playError(); return false; } - globalScene.lockModifierTiers = !globalScene.lockModifierTiers; + globalScene.lockRarityTiers = !globalScene.lockRarityTiers; const uiHandler = globalScene.ui.getHandler() as RewardSelectUiHandler; - uiHandler.setRerollCost(this.getRerollCost(globalScene.lockModifierTiers)); + uiHandler.setRerollCost(this.getRerollCost(globalScene.lockRarityTiers)); uiHandler.updateLockRaritiesText(); uiHandler.updateRerollCostText(); return false; } + // Opens the party menu specifically for fusions + private openFusionMenu(reward: FusePokemonReward, _cost: number, rewardSelectCallback: RewardSelectCallback): void { + const party = globalScene.getPlayerParty(); + globalScene.ui.setModeWithoutClear( + UiMode.PARTY, + PartyUiMode.SPLICE, + -1, + (fromSlotIndex: number, spliceSlotIndex: number) => { + if ( + spliceSlotIndex !== undefined && + fromSlotIndex < 6 && + spliceSlotIndex < 6 && + fromSlotIndex !== spliceSlotIndex + ) { + globalScene.ui.setMode(UiMode.REWARD_SELECT, this.isPlayer()).then(() => { + reward.apply({ pokemon: party[fromSlotIndex], pokemon2: party[spliceSlotIndex] }); + }); + } else { + this.resetRewardSelect(rewardSelectCallback); + } + }, + reward.selectFilter, + ); + } + + // Opens the party menu to apply one of various Pokemon rewards. We pass the reward's filter to decide which Pokemon can be selected. + // For MoveReward (e.g. PP UP or Ether) we also pass a filter to decide which moves can be selected. + private openPokemonRewardMenu(reward: PokemonReward, cost: number, rewardSelectCallback: RewardSelectCallback): void { + const party = globalScene.getPlayerParty(); + + let partyUiMode = PartyUiMode.REWARD; + let moveSelectFilter: PokemonMoveSelectFilter | undefined; + let tmMoveId: MoveId | undefined; + let isMove = false; + let getParams = (slotIndex: number, _option: PartyOption) => { + return { pokemon: party[slotIndex] } as PokemonRewardParams; + }; + + if (isMoveReward(reward)) { + partyUiMode = PartyUiMode.MOVE_REWARD; + moveSelectFilter = (reward as PokemonMoveReward).moveSelectFilter; + isMove = true; + getParams = (slotIndex: number, option: PartyOption) => { + return { pokemon: party[slotIndex], moveIndex: option - PartyOption.MOVE_1 } as PokemonMoveRewardParams; + }; + } + if (isRememberMoveReward(reward)) { + partyUiMode = PartyUiMode.REMEMBER_MOVE_REWARD; + getParams = (slotIndex: number, option: PartyOption) => { + return { pokemon: party[slotIndex], moveIndex: option, cost: cost } as PokemonMoveRecallRewardParams; + }; + } + if (isTmReward(reward)) { + partyUiMode = PartyUiMode.TM_REWARD; + tmMoveId = reward.moveId; + } + + globalScene.ui.setModeWithoutClear( + UiMode.PARTY, + partyUiMode, + -1, + (slotIndex: number, option: PartyOption) => { + if (slotIndex < 6) { + globalScene.ui.setMode(UiMode.REWARD_SELECT, this.isPlayer()).then(() => { + const params = getParams(slotIndex, option); + const result = globalScene.applyReward(reward, params, true); + this.postApplyPokemonReward(reward, result, cost); + }); + } else { + this.resetRewardSelect(rewardSelectCallback); + } + }, + reward.selectFilter, + moveSelectFilter, + tmMoveId, + isMove, + ); + } + + // TODO: Rework this to work properly with rewards /** - * Apply the effects of the chosen modifier - * @param modifier - The modifier to apply - * @param cost - The cost of the modifier if it was purchased, or -1 if selected as the modifier reward - * @param playSound - Whether the 'obtain modifier' sound should be played when adding the modifier. + * Apply the effects of the chosen reward + * @param reward - The reward to apply + * @param cost - The cost of the reward if it was purchased, or -1 if selected as the reward reward + * @param playSound - Whether the 'obtain reward' sound should be played when adding the reward. */ - private applyModifier(modifier: Modifier, cost = -1, playSound = false): void { - const result = globalScene.addModifier(modifier, playSound, undefined, cost); + private postApplyPokemonReward(reward: Reward, result = false, cost = -1): void { // Queue a copy of this phase when applying a TM or Memory Mushroom. // If the player selects either of these, then escapes out of consuming them, // they are returned to a shop in the same state. - if (modifier.type instanceof RememberMoveModifierType || modifier.type instanceof TmModifierType) { + if (reward instanceof RememberMoveReward || reward instanceof TmReward) { globalScene.phaseManager.unshiftPhase(this.copy()); } - if (cost !== -1 && !(modifier.type instanceof RememberMoveModifierType)) { + if (cost !== -1 && !(reward instanceof RememberMoveReward)) { if (result) { if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { globalScene.money -= cost; @@ -297,138 +363,37 @@ export class SelectRewardPhase extends BattlePhase { } } - // Opens the party menu specifically for fusions - private openFusionMenu( - modifierType: PokemonModifierType, - cost: number, - modifierSelectCallback: ModifierSelectCallback, - ): void { - const party = globalScene.getPlayerParty(); - globalScene.ui.setModeWithoutClear( - UiMode.PARTY, - PartyUiMode.SPLICE, - -1, - (fromSlotIndex: number, spliceSlotIndex: number) => { - if ( - spliceSlotIndex !== undefined && - fromSlotIndex < 6 && - spliceSlotIndex < 6 && - fromSlotIndex !== spliceSlotIndex - ) { - globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? - this.applyModifier(modifier, cost, true); - }); - } else { - this.resetModifierSelect(modifierSelectCallback); - } - }, - modifierType.selectFilter, - ); - } - - // Opens the party menu to apply one of various modifiers - private openModifierMenu( - modifierType: PokemonModifierType, - cost: number, - modifierSelectCallback: ModifierSelectCallback, - ): void { - const party = globalScene.getPlayerParty(); - const pokemonModifierType = modifierType as PokemonModifierType; - const isMoveModifier = modifierType instanceof PokemonMoveModifierType; - const isTmModifier = modifierType instanceof TmModifierType; - const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType; - const isPpRestoreModifier = - modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType; - const partyUiMode = isMoveModifier - ? PartyUiMode.MOVE_MODIFIER - : isTmModifier - ? PartyUiMode.TM_MODIFIER - : isRememberMoveModifier - ? PartyUiMode.REMEMBER_MOVE_MODIFIER - : PartyUiMode.MODIFIER; - const tmMoveId = isTmModifier ? (modifierType as TmModifierType).moveId : undefined; - globalScene.ui.setModeWithoutClear( - UiMode.PARTY, - partyUiMode, - -1, - (slotIndex: number, option: PartyOption) => { - if (slotIndex < 6) { - globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = !isMoveModifier - ? !isRememberMoveModifier - ? modifierType.newModifier(party[slotIndex]) - : modifierType.newModifier(party[slotIndex], option as number) - : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); - this.applyModifier(modifier!, cost, true); // TODO: is the bang correct? - }); - } else { - this.resetModifierSelect(modifierSelectCallback); - } - }, - pokemonModifierType.selectFilter, - modifierType instanceof PokemonMoveModifierType - ? (modifierType as PokemonMoveModifierType).moveSelectFilter - : undefined, - tmMoveId, - isPpRestoreModifier, - ); - } - - private openGiveHeldItemMenu(reward, modifierSelectCallback) { - const party = globalScene.getPlayerParty(); - const partyUiMode = PartyUiMode.MODIFIER; - globalScene.ui.setModeWithoutClear( - UiMode.PARTY, - partyUiMode, - -1, - (slotIndex: number, _option: PartyOption) => { - if (slotIndex < 6) { - globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { - reward.apply(party[slotIndex]); - globalScene.ui.clearText(); - globalScene.ui.setMode(UiMode.MESSAGE); - super.end(); - }); - } else { - this.resetModifierSelect(modifierSelectCallback); - } - }, - reward.selectFilter, - ); - } - // Function that determines how many reward slots are available - private getModifierCount(): number { - const modifierCountHolder = new NumberHolder(3); - globalScene.applyPlayerItems(TrainerItemEffect.EXTRA_REWARD, { numberHolder: modifierCountHolder }); + private getRewardCount(): number { + const rewardCountHolder = new NumberHolder(3); + globalScene.applyPlayerItems(TrainerItemEffect.EXTRA_REWARD, { numberHolder: rewardCountHolder }); - // If custom modifiers are specified, overrides default item count - if (this.customModifierSettings) { + // If custom rewards are specified, overrides default item count + if (this.customRewardSettings) { const newItemCount = - (this.customModifierSettings.guaranteedModifierTiers?.length ?? 0) + - (this.customModifierSettings.guaranteedModifierTypeOptions?.length ?? 0) + - (this.customModifierSettings.guaranteedModifierTypeFuncs?.length ?? 0); - if (this.customModifierSettings.fillRemaining) { - const originalCount = modifierCountHolder.value; - modifierCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount; + (this.customRewardSettings.guaranteedRarityTiers?.length ?? 0) + + (this.customRewardSettings.guaranteedRewardOptions?.length ?? 0) + + (this.customRewardSettings.guaranteedRewardFuncs?.length ?? 0); + if (this.customRewardSettings.fillRemaining) { + const originalCount = rewardCountHolder.value; + rewardCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount; } else { - modifierCountHolder.value = newItemCount; + rewardCountHolder.value = newItemCount; } } - return modifierCountHolder.value; + return rewardCountHolder.value; } // Function that resets the reward selection screen, // e.g. after pressing cancel in the party ui or while learning a move - private resetModifierSelect(modifierSelectCallback: ModifierSelectCallback) { + private resetRewardSelect(rewardSelectCallback: RewardSelectCallback) { globalScene.ui.setMode( - UiMode.MODIFIER_SELECT, + UiMode.REWARD_SELECT, this.isPlayer(), this.typeOptions, - modifierSelectCallback, - this.getRerollCost(globalScene.lockModifierTiers), + rewardSelectCallback, + this.getRerollCost(globalScene.lockRarityTiers), ); } @@ -455,14 +420,14 @@ export class SelectRewardPhase extends BattlePhase { } let multiplier = 1; - if (!isNullOrUndefined(this.customModifierSettings?.rerollMultiplier)) { - if (this.customModifierSettings.rerollMultiplier < 0) { + if (!isNullOrUndefined(this.customRewardSettings?.rerollMultiplier)) { + if (this.customRewardSettings.rerollMultiplier < 0) { // Completely overrides reroll cost to -1 and early exits return -1; } // Otherwise, continue with custom multiplier - multiplier = this.customModifierSettings.rerollMultiplier; + multiplier = this.customRewardSettings.rerollMultiplier; } const baseMultiplier = Math.min( @@ -476,16 +441,16 @@ export class SelectRewardPhase extends BattlePhase { return modifiedRerollCost.value; } - getPoolType(): ModifierPoolType { - return ModifierPoolType.PLAYER; + getPoolType(): RewardPoolType { + return RewardPoolType.PLAYER; } - getModifierTypeOptions(modifierCount: number): ModifierTypeOption[] { - return getPlayerModifierTypeOptions( - modifierCount, + getRewardOptions(rewardCount: number): RewardOption[] { + return generatePlayerRewardOptions( + rewardCount, globalScene.getPlayerParty(), - globalScene.lockModifierTiers ? this.modifierTiers : undefined, - this.customModifierSettings, + globalScene.lockRarityTiers ? this.rarityTiers : undefined, + this.customRewardSettings, ); } @@ -493,17 +458,13 @@ export class SelectRewardPhase extends BattlePhase { return globalScene.phaseManager.create( "SelectRewardPhase", this.rerollCount, - this.modifierTiers, + this.rarityTiers, { - guaranteedModifierTypeOptions: this.typeOptions, - rerollMultiplier: this.customModifierSettings?.rerollMultiplier, + guaranteedRewardOptions: this.typeOptions, + rerollMultiplier: this.customRewardSettings?.rerollMultiplier, allowLuckUpgrades: false, }, true, ); } - - addModifier(modifier: Modifier): boolean { - return globalScene.addModifier(modifier, false, true); - } } diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 731504d9eb9..ff97a9e33c0 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -7,7 +7,7 @@ import { Gender } from "#data/gender"; import { ChallengeType } from "#enums/challenge-type"; import type { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; -import { overrideHeldItems, overrideTrainerItems } from "#modifiers/modifier"; +import { overrideHeldItems, overrideTrainerItems } from "#items/item-overrides"; import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler"; import type { Starter } from "#ui/starter-select-ui-handler"; import { isNullOrUndefined } from "#utils/common"; diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index c47314e268e..8156149628a 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -8,10 +8,10 @@ import { OctolockTag } from "#data/battler-tags"; import { ArenaTagSide } from "#enums/arena-tag-side"; import { ArenaTagType } from "#enums/arena-tag-type"; import type { BattlerIndex } from "#enums/battler-index"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { type BattleStat, getStatKey, getStatStageChangeDescriptionKey, Stat } from "#enums/stat"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { PokemonPhase } from "#phases/pokemon-phase"; import type { ConditionalUserFieldProtectStatAbAttrParams, PreStatStageChangeAbAttrParams } from "#types/ability-types"; import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#utils/common"; diff --git a/src/phases/trainer-item-reward-phase.ts b/src/phases/trainer-item-reward-phase.ts index f950985cc99..033a022958f 100644 --- a/src/phases/trainer-item-reward-phase.ts +++ b/src/phases/trainer-item-reward-phase.ts @@ -1,20 +1,19 @@ import { globalScene } from "#app/global-scene"; -import type { ModifierType } from "#modifiers/modifier-type"; +import type { Reward } from "#items/reward"; import { BattlePhase } from "#phases/battle-phase"; -import type { ModifierTypeFunc } from "#types/modifier-types"; -import { getModifierType } from "#utils/modifier-utils"; +import type { RewardFunc } from "#types/rewards"; import i18next from "i18next"; export class RewardPhase extends BattlePhase { // RibbonRewardPhase extends RewardPhase and to make typescript happy // we need to use a union type here public readonly phaseName: "RewardPhase" | "RibbonRewardPhase" | "GameOverRewardPhase" = "RewardPhase"; - protected modifierType: ModifierType; + protected reward: Reward; - constructor(modifierTypeFunc: ModifierTypeFunc) { + constructor(rewardFunc: RewardFunc) { super(); - this.modifierType = getModifierType(modifierTypeFunc); + this.reward = rewardFunc(); } start() { @@ -25,12 +24,11 @@ export class RewardPhase extends BattlePhase { doReward(): Promise { return new Promise(resolve => { - const newModifier = this.modifierType.newModifier(); - globalScene.addModifier(newModifier); + globalScene.applyReward(this.reward); globalScene.playSound("item_fanfare"); globalScene.ui.showText( i18next.t("battle:rewardGain", { - modifierName: newModifier?.type.name, + modifierName: this.reward.name, }), null, () => resolve(), diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 3aff1b4fb4e..df9026fc536 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -1,6 +1,6 @@ import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { getCharVariantFromDialogue } from "#data/dialogue"; import { BiomeId } from "#enums/biome-id"; import { TrainerSlot } from "#enums/trainer-slot"; @@ -20,9 +20,9 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.phaseManager.unshiftNew("MoneyRewardPhase", globalScene.currentBattle.trainer?.config.moneyMultiplier!); // TODO: is this bang correct? - const modifierRewardFuncs = globalScene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? - for (const modifierRewardFunc of modifierRewardFuncs) { - globalScene.phaseManager.unshiftNew("RewardPhase", modifierRewardFunc); + const rewardFuncs = globalScene.currentBattle.trainer?.config.rewardFuncs!; // TODO: is this bang correct? + for (const rewardFunc of rewardFuncs) { + globalScene.phaseManager.unshiftNew("RewardPhase", rewardFunc); } const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? @@ -35,17 +35,14 @@ export class TrainerVictoryPhase extends BattlePhase { if (timedEventManager.getUpgradeUnlockedVouchers()) { globalScene.phaseManager.unshiftNew( "RewardPhase", - [ - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PREMIUM, - ][vouchers[TrainerType[trainerType]].voucherType], + [allRewards.VOUCHER_PLUS, allRewards.VOUCHER_PLUS, allRewards.VOUCHER_PLUS, allRewards.VOUCHER_PREMIUM][ + vouchers[TrainerType[trainerType]].voucherType + ], ); } else { globalScene.phaseManager.unshiftNew( "RewardPhase", - [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][ + [allRewards.VOUCHER, allRewards.VOUCHER, allRewards.VOUCHER_PLUS, allRewards.VOUCHER_PREMIUM][ vouchers[TrainerType[trainerType]].voucherType ], ); diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index af34011780b..a9394791a1e 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -3,11 +3,11 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { TerrainType } from "#data/terrain"; import { BattlerTagLapseType } from "#enums/battler-tag-lapse-type"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { WeatherType } from "#enums/weather-type"; import { TurnEndEvent } from "#events/battle-scene"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { TrainerItemEffect } from "#items/trainer-item"; import { FieldPhase } from "#phases/field-phase"; import i18next from "i18next"; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 044987808a5..bf674baa46d 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -4,11 +4,11 @@ import { TrickRoomTag } from "#data/arena-tag"; import { allMoves } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; import { Command } from "#enums/command"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { Stat } from "#enums/stat"; import { SwitchType } from "#enums/switch-type"; import type { Pokemon } from "#field/pokemon"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { PokemonMove } from "#moves/pokemon-move"; import { FieldPhase } from "#phases/field-phase"; import { BooleanHolder, randSeedShuffle } from "#utils/common"; diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index e951adfd6dd..8fdf8d52bc3 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,10 +1,10 @@ import { timedEventManager } from "#app/global-event-manager"; import { globalScene } from "#app/global-scene"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { BattleType } from "#enums/battle-type"; import type { BattlerIndex } from "#enums/battler-index"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; -import type { CustomModifierSettings } from "#modifiers/modifier-type"; +import type { CustomRewardSettings } from "#items/reward-pool-utils"; import { handleMysteryEncounterVictory } from "#mystery-encounters/encounter-phase-utils"; import { PokemonPhase } from "#phases/pokemon-phase"; @@ -55,11 +55,11 @@ export class VictoryPhase extends PokemonPhase { // Get event modifiers for this wave timedEventManager .getFixedBattleEventRewards(globalScene.currentBattle.waveIndex) - .map(r => globalScene.phaseManager.pushNew("RewardPhase", modifierTypes[r])); + .map(r => globalScene.phaseManager.pushNew("RewardPhase", allRewards[r])); break; case ClassicFixedBossWaves.EVIL_BOSS_2: - // Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop - globalScene.phaseManager.pushNew("RewardPhase", modifierTypes.LOCK_CAPSULE); + // Should get Lock Capsule on 165 before shop phase so it can be used in the allRewards shop + globalScene.phaseManager.pushNew("RewardPhase", allRewards.LOCK_CAPSULE); break; } } @@ -68,20 +68,20 @@ export class VictoryPhase extends PokemonPhase { "SelectRewardPhase", undefined, undefined, - this.getFixedBattleCustomModifiers(), + this.getFixedBattleCustomRewards(), ); } else if (globalScene.gameMode.isDaily) { - globalScene.phaseManager.pushNew("RewardPhase", modifierTypes.EXP_CHARM); + globalScene.phaseManager.pushNew("RewardPhase", allRewards.EXP_CHARM); if ( globalScene.currentBattle.waveIndex > 10 && !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex) ) { - globalScene.phaseManager.pushNew("RewardPhase", modifierTypes.GOLDEN_POKEBALL); + globalScene.phaseManager.pushNew("RewardPhase", allRewards.GOLDEN_POKEBALL); } } else { const superExpWave = !globalScene.gameMode.isEndless ? (globalScene.offsetGym ? 0 : 20) : 10; if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex === 10) { - globalScene.phaseManager.pushNew("RewardPhase", modifierTypes.EXP_SHARE); + globalScene.phaseManager.pushNew("RewardPhase", allRewards.EXP_SHARE); } if ( globalScene.currentBattle.waveIndex <= 750 && @@ -90,19 +90,19 @@ export class VictoryPhase extends PokemonPhase { globalScene.phaseManager.pushNew( "RewardPhase", globalScene.currentBattle.waveIndex % 30 !== superExpWave || globalScene.currentBattle.waveIndex > 250 - ? modifierTypes.EXP_CHARM - : modifierTypes.SUPER_EXP_CHARM, + ? allRewards.EXP_CHARM + : allRewards.SUPER_EXP_CHARM, ); } if (globalScene.currentBattle.waveIndex <= 150 && !(globalScene.currentBattle.waveIndex % 50)) { - globalScene.phaseManager.pushNew("RewardPhase", modifierTypes.GOLDEN_POKEBALL); + globalScene.phaseManager.pushNew("RewardPhase", allRewards.GOLDEN_POKEBALL); } if (globalScene.gameMode.isEndless && !(globalScene.currentBattle.waveIndex % 50)) { globalScene.phaseManager.pushNew( "RewardPhase", - !(globalScene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS, + !(globalScene.currentBattle.waveIndex % 250) ? allRewards.VOUCHER_PREMIUM : allRewards.VOUCHER_PLUS, ); - globalScene.phaseManager.pushNew("AddEnemyBuffModifierPhase"); + globalScene.phaseManager.pushNew("AddEnemyTokenPhase"); } } @@ -123,14 +123,14 @@ export class VictoryPhase extends PokemonPhase { } /** - * If this wave is a fixed battle with special custom modifier rewards, + * If this wave is a fixed battle with special custom modifier allRewards, * will pass those settings to the upcoming {@linkcode SelectRewardPhase}`. */ - getFixedBattleCustomModifiers(): CustomModifierSettings | undefined { + getFixedBattleCustomRewards(): CustomRewardSettings | undefined { const gameMode = globalScene.gameMode; const waveIndex = globalScene.currentBattle.waveIndex; if (gameMode.isFixedBattle(waveIndex)) { - return gameMode.getFixedBattle(waveIndex).customModifierRewardSettings; + return gameMode.getFixedBattle(waveIndex).customRewardSettings; } return undefined; diff --git a/src/system/achv.ts b/src/system/achv.ts index bb981d39062..9158d827dc6 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -16,7 +16,6 @@ import type { Pokemon } from "#field/pokemon"; import type { ConditionFn } from "#types/common"; import { NumberHolder } from "#utils/common"; import i18next from "i18next"; -import type { Modifier } from "typescript"; export enum AchvTier { COMMON, @@ -177,19 +176,6 @@ export class LevelAchv extends Achv { } } -export class ModifierAchv extends Achv { - constructor( - localizationKey: string, - name: string, - description: string, - iconImage: string, - score: number, - modifierFunc: (modifier: Modifier) => boolean, - ) { - super(localizationKey, name, description, iconImage, score, (args: any[]) => modifierFunc(args[0] as Modifier)); - } -} - export class HeldItemAchv extends Achv { constructor( localizationKey: string, diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index 19d10baedfd..106e7855fc5 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -66,7 +66,7 @@ const TOUCH_CONTROLS_OPTIONS: SettingOption[] = [ const SHOP_CURSOR_TARGET_OPTIONS: SettingOption[] = [ { value: "Rewards", - label: i18next.t("settings:rewards"), + label: i18next.t("settings:allRewards"), }, { value: "Shop", diff --git a/src/system/version-migration/versions/v1_0_4.ts b/src/system/version-migration/versions/v1_0_4.ts index 2c50e05d40f..643737d7645 100644 --- a/src/system/version-migration/versions/v1_0_4.ts +++ b/src/system/version-migration/versions/v1_0_4.ts @@ -137,7 +137,7 @@ const migrateModifiers: SessionSaveMigrator = { m.className = "ResetNegativeStatStageModifier"; } else if (m.className === "TempBattleStatBoosterModifier") { const maxBattles = 5; - // Dire Hit no longer a part of the TempBattleStatBoosterModifierTypeGenerator + // Dire Hit no longer a part of the TempBattleStatBoosterRewardGenerator if (m.typeId !== "DIRE_HIT") { m.className = "TempStatStageBoosterModifier"; m.typeId = "TEMP_STAT_STAGE_BOOSTER"; diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 6f9705e4563..4c476292fa6 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -65,7 +65,7 @@ interface TimedEvent extends EventBanner { mysteryEncounterTierChanges?: EventMysteryEncounterTier[]; luckBoostedSpecies?: SpeciesId[]; boostFusions?: boolean; //MODIFIER REWORK PLEASE - classicWaveRewards?: EventWaveReward[]; // Rival battle rewards + classicWaveRewards?: EventWaveReward[]; // Rival battle allRewards trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny music?: EventMusicReplacement[]; dailyRunChallenges?: EventChallenge[]; @@ -466,7 +466,7 @@ export class TimedEventManager { /** * For events where Delibirdy gives extra items - * @returns list of ids of {@linkcode ModifierType}s that Delibirdy hands out as a bonus + * @returns list of ids of {@linkcode Reward}s that Delibirdy hands out as a bonus */ getDelibirdyBuff(): TrainerItemId[] { const ret: TrainerItemId[] = []; @@ -567,7 +567,7 @@ export class TimedEventManager { /** * Gets all the modifier types associated with a certain wave during an event * @see EventWaveReward - * @param wave the wave to check for associated rewards + * @param wave the wave to check for associated allRewards * @returns array of strings of the event modifier reward types */ getFixedBattleEventRewards(wave: number): string[] { diff --git a/src/tutorial.ts b/src/tutorial.ts index 018d0927da0..93e11335c65 100644 --- a/src/tutorial.ts +++ b/src/tutorial.ts @@ -98,7 +98,7 @@ const tutorialHandlers = { null, () => globalScene.ui.showText("", null, () => - globalScene.ui.setModeWithoutClear(UiMode.MODIFIER_SELECT).then(() => resolve()), + globalScene.ui.setModeWithoutClear(UiMode.REWARD_SELECT).then(() => resolve()), ), null, true, diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index 71c5ac1049e..cf340cf3ea7 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -185,7 +185,7 @@ export class UiInputs { } case UiMode.TITLE: case UiMode.COMMAND: - case UiMode.MODIFIER_SELECT: + case UiMode.REWARD_SELECT: case UiMode.MYSTERY_ENCOUNTER: globalScene.ui.setOverlayMode(UiMode.MENU); break; diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 5b2c9d40cfa..dda259e899a 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -37,7 +37,7 @@ export class EggHatchSceneHandler extends UiHandler { this.getUi().showText("", 0); - globalScene.setModifiersVisible(false); + globalScene.setItemsVisible(false); return true; } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 6e1ca89c9c7..8e975ffe7e6 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -61,27 +61,27 @@ export enum PartyUiMode { * Indicates that the party UI is open to select a mon to apply a modifier to. * This type of selection can be cancelled. */ - MODIFIER, + REWARD, /** * Indicates that the party UI is open to select a mon to apply a move * modifier to (such as an Ether or PP Up). This type of selection can be cancelled. */ - MOVE_MODIFIER, + MOVE_REWARD, /** * Indicates that the party UI is open to select a mon to teach a TM. This * type of selection can be cancelled. */ - TM_MODIFIER, + TM_REWARD, /** * Indicates that the party UI is open to select a mon to remember a move. * This type of selection can be cancelled. */ - REMEMBER_MOVE_MODIFIER, + REMEMBER_MOVE_REWARD, /** * Indicates that the party UI is open to transfer items between mons. This * type of selection can be cancelled. */ - MODIFIER_TRANSFER, + ITEM_TRANSFER, /** * Indicates that the party UI is open because of a DNA Splicer. This * type of selection can be cancelled. @@ -131,15 +131,15 @@ export enum PartyOption { } export type PartySelectCallback = (cursor: number, option: PartyOption) => void; -export type PartyModifierTransferSelectCallback = ( +export type PartyItemTransferSelectCallback = ( fromCursor: number, index: number, itemQuantity?: number, toCursor?: number, ) => void; -export type PartyModifierSpliceSelectCallback = (fromCursor: number, toCursor?: number) => void; +export type PartyRewardSpliceSelectCallback = (fromCursor: number, toCursor?: number) => void; export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string | null; -export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, item: HeldItemId) => string | null; +export type PokemonItemTransferSelectFilter = (pokemon: PlayerPokemon, item: HeldItemId) => string | null; export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string | null; export class PartyUiHandler extends MessageUiHandler { @@ -176,8 +176,8 @@ export class PartyUiHandler extends MessageUiHandler { private transferAll: boolean; private lastCursor = 0; - private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback | null; - private selectFilter: PokemonSelectFilter | PokemonModifierTransferSelectFilter; + private selectCallback: PartySelectCallback | PartyItemTransferSelectCallback | null; + private selectFilter: PokemonSelectFilter | PokemonItemTransferSelectFilter; private moveSelectFilter: PokemonMoveSelectFilter; private tmMoveId: MoveId; private showMovePp: boolean; @@ -503,7 +503,7 @@ export class PartyUiHandler extends MessageUiHandler { [this.transferCursor].heldItemManager.getTransferableHeldItems() .forEach((_, i, array) => { const invertedIndex = array.length - 1 - i; - (this.selectCallback as PartyModifierTransferSelectCallback)( + (this.selectCallback as PartyItemTransferSelectCallback)( this.transferCursor, invertedIndex, this.transferQuantitiesMax[invertedIndex], @@ -511,7 +511,7 @@ export class PartyUiHandler extends MessageUiHandler { ); }); } else { - (this.selectCallback as PartyModifierTransferSelectCallback)( + (this.selectCallback as PartyItemTransferSelectCallback)( this.transferCursor, this.transferOptionCursor, this.transferQuantities[this.transferOptionCursor], @@ -526,7 +526,7 @@ export class PartyUiHandler extends MessageUiHandler { } // TODO: This will be largely changed with the modifier rework - private processModifierTransferModeInput(pokemon: PlayerPokemon) { + private processItemTransferModeInput(pokemon: PlayerPokemon) { const ui = this.getUi(); const option = this.options[this.optionsCursor]; @@ -581,12 +581,12 @@ export class PartyUiHandler extends MessageUiHandler { } // TODO: Might need to check here for when this.transferMode is active. - private processModifierTransferModeLeftRightInput(button: Button) { + private processItemTransferModeLeftRightInput(button: Button) { let success = false; const option = this.options[this.optionsCursor]; if (button === Button.LEFT) { /** Decrease quantity for the current item and update UI */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { this.transferQuantities[option] = this.transferQuantities[option] === 1 ? this.transferQuantitiesMax[option] @@ -600,7 +600,7 @@ export class PartyUiHandler extends MessageUiHandler { if (button === Button.RIGHT) { /** Increase quantity for the current item and update UI */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { this.transferQuantities[option] = this.transferQuantities[option] === this.transferQuantitiesMax[option] ? 1 @@ -615,11 +615,11 @@ export class PartyUiHandler extends MessageUiHandler { } // TODO: Might need to check here for when this.transferMode is active. - private processModifierTransferModeUpDownInput(button: Button.UP | Button.DOWN) { + private processItemTransferModeUpDownInput(button: Button.UP | Button.DOWN) { let success = false; const option = this.options[this.optionsCursor]; - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { if (option !== PartyOption.ALL) { this.transferQuantities[option] = this.transferQuantitiesMax[option]; } @@ -681,11 +681,11 @@ export class PartyUiHandler extends MessageUiHandler { if (filterResult === null && (option === PartyOption.SEND_OUT || option === PartyOption.PASS_BATON)) { filterResult = this.FilterChallengeLegal(pokemon); } - if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { + if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_REWARD) { filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]); } } else { - filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)( + filterResult = (this.selectFilter as PokemonItemTransferSelectFilter)( pokemon, globalScene.getPlayerParty()[this.transferCursor].heldItemManager.getTransferableHeldItems()[ this.transferOptionCursor @@ -706,12 +706,12 @@ export class PartyUiHandler extends MessageUiHandler { // TODO: Careful about using success for the return values here. Find a better way // PartyOption.ALL, and options specific to the mode (held items) - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - return this.processModifierTransferModeInput(pokemon); + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { + return this.processItemTransferModeInput(pokemon); } // options specific to the mode (moves) - if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_REWARD) { return this.processRememberMoveModeInput(pokemon); } @@ -757,13 +757,13 @@ export class PartyUiHandler extends MessageUiHandler { // PartyUiMode.RELEASE (RELEASE) // PartyUiMode.FAINT_SWITCH (SEND_OUT or PASS_BATON (?)) // PartyUiMode.REVIVAL_BLESSING (REVIVE) - // PartyUiMode.MODIFIER_TRANSFER (held items, and ALL) + // PartyUiMode.ITEM_TRANSFER (held items, and ALL) // PartyUiMode.CHECK --- no specific option, only relevant on cancel? // PartyUiMode.SPLICE (SPLICE) - // PartyUiMode.MOVE_MODIFIER (MOVE_1, MOVE_2, MOVE_3, MOVE_4) - // PartyUiMode.TM_MODIFIER (TEACH) - // PartyUiMode.REMEMBER_MOVE_MODIFIER (no specific option, callback is invoked when selecting a move) - // PartyUiMode.MODIFIER (APPLY option) + // PartyUiMode.MOVE_REWARD (MOVE_1, MOVE_2, MOVE_3, MOVE_4) + // PartyUiMode.TM_REWARD (TEACH) + // PartyUiMode.REMEMBER_MOVE_REWARD (no specific option, callback is invoked when selecting a move) + // PartyUiMode.REWARD (APPLY option) // PartyUiMode.POST_BATTLE_SWITCH (SEND_OUT) // These are the options that need a callback @@ -773,7 +773,7 @@ export class PartyUiHandler extends MessageUiHandler { if (this.partyUiMode === PartyUiMode.SPLICE) { if (option === PartyOption.SPLICE) { - (this.selectCallback as PartyModifierSpliceSelectCallback)(this.transferCursor, this.cursor); + (this.selectCallback as PartyRewardSpliceSelectCallback)(this.transferCursor, this.cursor); this.clearTransfer(); } else if (option === PartyOption.APPLY) { this.startTransfer(); @@ -837,11 +837,11 @@ export class PartyUiHandler extends MessageUiHandler { } if (button === Button.UP || button === Button.DOWN) { - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - return this.processModifierTransferModeUpDownInput(button); + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { + return this.processItemTransferModeUpDownInput(button); } - if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_REWARD) { return this.processRememberMoveModeUpDownInput(button); } @@ -849,8 +849,8 @@ export class PartyUiHandler extends MessageUiHandler { } if (button === Button.LEFT || button === Button.RIGHT) { - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { - return this.processModifierTransferModeLeftRightInput(button); + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { + return this.processItemTransferModeLeftRightInput(button); } } @@ -907,7 +907,7 @@ export class PartyUiHandler extends MessageUiHandler { private processPartyActionInput(): boolean { const ui = this.getUi(); if (this.cursor < 6) { - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && !this.transferMode) { + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER && !this.transferMode) { /** Initialize item quantities for the selected Pokemon */ const pokemon = globalScene.getPlayerParty()[this.cursor]; const items = pokemon.heldItemManager.getTransferableHeldItems(); @@ -931,7 +931,7 @@ export class PartyUiHandler extends MessageUiHandler { private processPartyCancelInput(): boolean { const ui = this.getUi(); if ( - (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) && + (this.partyUiMode === PartyUiMode.ITEM_TRANSFER || this.partyUiMode === PartyUiMode.SPLICE) && this.transferMode ) { this.clearTransfer(); @@ -1111,10 +1111,10 @@ export class PartyUiHandler extends MessageUiHandler { let optionsMessage = i18next.t("partyUiHandler:doWhatWithThisPokemon"); switch (this.partyUiMode) { - case PartyUiMode.MOVE_MODIFIER: + case PartyUiMode.MOVE_REWARD: optionsMessage = i18next.t("partyUiHandler:selectAMove"); break; - case PartyUiMode.MODIFIER_TRANSFER: + case PartyUiMode.ITEM_TRANSFER: if (!this.transferMode) { optionsMessage = i18next.t("partyUiHandler:changeQuantity"); } @@ -1131,7 +1131,7 @@ export class PartyUiHandler extends MessageUiHandler { this.updateOptions(); /** When an item is being selected for transfer, the message box is taller as the message occupies two lines */ - if (this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER) { + if (this.partyUiMode === PartyUiMode.ITEM_TRANSFER) { this.partyMessageBox.setSize(262 - Math.max(this.optionsBg.displayWidth - 56, 0), 42); } else { this.partyMessageBox.setSize(262 - Math.max(this.optionsBg.displayWidth - 56, 0), 30); @@ -1140,7 +1140,7 @@ export class PartyUiHandler extends MessageUiHandler { this.setCursor(0); } - private allowBatonModifierSwitch(): boolean { + private allowBatonSwitch(): boolean { return !!( this.partyUiMode !== PartyUiMode.FAINT_SWITCH && globalScene.getPlayerField()[this.fieldIndex].heldItemManager.hasItem(HeldItemId.BATON) @@ -1158,7 +1158,7 @@ export class PartyUiHandler extends MessageUiHandler { ); } - private updateOptionsWithRememberMoveModifierMode(pokemon): void { + private updateOptionsWithRememberMoveRewardMode(pokemon): void { const learnableMoves = pokemon.getLearnableLevelMoves(); for (let m = 0; m < learnableMoves.length; m++) { this.options.push(m); @@ -1169,14 +1169,14 @@ export class PartyUiHandler extends MessageUiHandler { } } - private updateOptionsWithMoveModifierMode(pokemon): void { + private updateOptionsWithMoveRewardMode(pokemon): void { // MOVE_1, MOVE_2, MOVE_3, MOVE_4 for (let m = 0; m < pokemon.moveset.length; m++) { this.options.push(PartyOption.MOVE_1 + m); } } - private updateOptionsWithModifierTransferMode(pokemon): void { + private updateOptionsWithItemTransferMode(pokemon): void { const items = pokemon.getHeldItems(); for (let im = 0; im < items.length; im++) { this.options.push(im); @@ -1233,15 +1233,15 @@ export class PartyUiHandler extends MessageUiHandler { } switch (this.partyUiMode) { - case PartyUiMode.MOVE_MODIFIER: - this.updateOptionsWithMoveModifierMode(pokemon); + case PartyUiMode.MOVE_REWARD: + this.updateOptionsWithMoveRewardMode(pokemon); break; - case PartyUiMode.REMEMBER_MOVE_MODIFIER: - this.updateOptionsWithRememberMoveModifierMode(pokemon); + case PartyUiMode.REMEMBER_MOVE_REWARD: + this.updateOptionsWithRememberMoveRewardMode(pokemon); break; - case PartyUiMode.MODIFIER_TRANSFER: + case PartyUiMode.ITEM_TRANSFER: if (!this.transferMode) { - this.updateOptionsWithModifierTransferMode(pokemon); + this.updateOptionsWithItemTransferMode(pokemon); } else { this.options.push(PartyOption.TRANSFER); this.addCommonOptions(pokemon); @@ -1254,21 +1254,19 @@ export class PartyUiHandler extends MessageUiHandler { case PartyUiMode.FAINT_SWITCH: case PartyUiMode.POST_BATTLE_SWITCH: if (this.cursor >= globalScene.currentBattle.getBattlerCount()) { - const allowBatonModifierSwitch = this.allowBatonModifierSwitch(); + const allowBatonSwitch = this.allowBatonSwitch(); const isBatonPassMove = this.isBatonPassMove(); - if (allowBatonModifierSwitch && !isBatonPassMove) { + if (allowBatonSwitch && !isBatonPassMove) { // the BATON modifier gives an extra switch option for // pokemon-command switches, allowing buffs to be optionally passed this.options.push(PartyOption.PASS_BATON); } - // isBatonPassMove and allowBatonModifierSwitch shouldn't ever be true + // isBatonPassMove and allowBatonItemSwitch shouldn't ever be true // at the same time, because they both explicitly check for a mutually // exclusive partyUiMode. But better safe than sorry. - this.options.push( - isBatonPassMove && !allowBatonModifierSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT, - ); + this.options.push(isBatonPassMove && !allowBatonSwitch ? PartyOption.PASS_BATON : PartyOption.SEND_OUT); } this.addCommonOptions(pokemon); if (this.partyUiMode === PartyUiMode.SWITCH) { @@ -1282,11 +1280,11 @@ export class PartyUiHandler extends MessageUiHandler { this.options.push(PartyOption.REVIVE); this.addCommonOptions(pokemon); break; - case PartyUiMode.MODIFIER: + case PartyUiMode.REWARD: this.options.push(PartyOption.APPLY); this.addCommonOptions(pokemon); break; - case PartyUiMode.TM_MODIFIER: + case PartyUiMode.TM_REWARD: this.options.push(PartyOption.TEACH); this.addCommonOptions(pokemon); break; @@ -1351,8 +1349,8 @@ export class PartyUiHandler extends MessageUiHandler { } else if (option === PartyOption.SCROLL_DOWN) { optionName = "↓"; } else if ( - (this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER && - (this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER || this.transferMode)) || + (this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_REWARD && + (this.partyUiMode !== PartyUiMode.ITEM_TRANSFER || this.transferMode)) || option === PartyOption.CANCEL ) { switch (option) { @@ -1387,7 +1385,7 @@ export class PartyUiHandler extends MessageUiHandler { break; } } - } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER) { + } else if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_REWARD) { const learnableLevelMoves = pokemon.getLearnableLevelMoves(); const move = learnableLevelMoves[option]; optionName = allMoves[move].name; @@ -1415,7 +1413,7 @@ export class PartyUiHandler extends MessageUiHandler { const items = pokemon.getHeldItems(); const item = items[option]; if ( - this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER && + this.partyUiMode === PartyUiMode.ITEM_TRANSFER && this.transferQuantitiesMax[option] > 1 && !this.transferMode && item !== undefined && @@ -1816,7 +1814,7 @@ class PartySlot extends Phaser.GameObjects.Container { slotInfoContainer.add([this.slotHpBar, this.slotHpOverlay, this.slotHpText, this.slotDescriptionLabel]); - if (partyUiMode !== PartyUiMode.TM_MODIFIER) { + if (partyUiMode !== PartyUiMode.TM_REWARD) { this.slotDescriptionLabel.setVisible(false); this.slotHpBar.setVisible(true); this.slotHpOverlay.setVisible(true); diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index c95f412c834..707d476da5f 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -434,7 +434,7 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container { this.setVisible(true); this.shown = true; - globalScene.hideEnemyModifierBar(); + globalScene.hideEnemyItemBar(); }); } @@ -488,7 +488,7 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container { hide(speedMultiplier = 1): Promise { return new Promise(resolve => { if (!this.shown) { - globalScene.showEnemyModifierBar(); + globalScene.showEnemyItemBar(); return resolve(); } @@ -509,7 +509,7 @@ export class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonShinyIcon.off("pointerover"); this.pokemonShinyIcon.off("pointerout"); globalScene.ui.hideTooltip(); - globalScene.showEnemyModifierBar(); + globalScene.showEnemyItemBar(); resolve(); }, }); diff --git a/src/ui/reward-select-ui-handler.ts b/src/ui/reward-select-ui-handler.ts index 256e581e867..2e4776adc0b 100644 --- a/src/ui/reward-select-ui-handler.ts +++ b/src/ui/reward-select-ui-handler.ts @@ -8,12 +8,12 @@ import type { PokeballType } from "#enums/pokeball"; import { ShopCursorTarget } from "#enums/shop-cursor-target"; import { TrainerItemId } from "#enums/trainer-item-id"; import { UiMode } from "#enums/ui-mode"; +import type { RewardOption } from "#items/reward"; +import { getPlayerShopRewardOptionsForWave, isTmReward } from "#items/reward-utils"; import { TrainerItemEffect } from "#items/trainer-item"; -import type { ModifierTypeOption } from "#modifiers/modifier-type"; -import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "#modifiers/modifier-type"; import { AwaitableUiHandler } from "#ui/awaitable-ui-handler"; import { MoveInfoOverlay } from "#ui/move-info-overlay"; -import { addTextObject, getModifierTierTextTint, getTextColor, getTextStyleOptions, TextStyle } from "#ui/text"; +import { addTextObject, getRarityTierTextTint, getTextColor, getTextStyleOptions, TextStyle } from "#ui/text"; import { formatMoney, NumberHolder } from "#utils/common"; import i18next from "i18next"; import Phaser from "phaser"; @@ -69,8 +69,8 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { if (context) { context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily; - this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width; - this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width; + this.transferButtonWidth = context.measureText(i18next.t("rewardSelectUiHandler:transfer")).width; + this.checkButtonWidth = context.measureText(i18next.t("rewardSelectUiHandler:checkTeam")).width; } this.transferButtonContainer = globalScene.add.container( @@ -81,7 +81,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { this.transferButtonContainer.setVisible(false); ui.add(this.transferButtonContainer); - const transferButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:transfer"), TextStyle.PARTY); + const transferButtonText = addTextObject(-4, -2, i18next.t("rewardSelectUiHandler:transfer"), TextStyle.PARTY); transferButtonText.setName("text-transfer-btn"); transferButtonText.setOrigin(1, 0); this.transferButtonContainer.add(transferButtonText); @@ -94,7 +94,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { this.checkButtonContainer.setVisible(false); ui.add(this.checkButtonContainer); - const checkButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:checkTeam"), TextStyle.PARTY); + const checkButtonText = addTextObject(-4, -2, i18next.t("rewardSelectUiHandler:checkTeam"), TextStyle.PARTY); checkButtonText.setName("text-use-btn"); checkButtonText.setOrigin(1, 0); this.checkButtonContainer.add(checkButtonText); @@ -104,7 +104,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { this.rerollButtonContainer.setVisible(false); ui.add(this.rerollButtonContainer); - const rerollButtonText = addTextObject(-4, -2, i18next.t("modifierSelectUiHandler:reroll"), TextStyle.PARTY); + const rerollButtonText = addTextObject(-4, -2, i18next.t("rewardSelectUiHandler:reroll"), TextStyle.PARTY); rerollButtonText.setName("text-reroll-btn"); rerollButtonText.setOrigin(0, 0); this.rerollButtonContainer.add(rerollButtonText); @@ -119,12 +119,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { this.lockRarityButtonContainer.setVisible(false); ui.add(this.lockRarityButtonContainer); - this.lockRarityButtonText = addTextObject( - -4, - -2, - i18next.t("modifierSelectUiHandler:lockRarities"), - TextStyle.PARTY, - ); + this.lockRarityButtonText = addTextObject(-4, -2, i18next.t("rewardSelectUiHandler:lockRarities"), TextStyle.PARTY); this.lockRarityButtonText.setOrigin(0, 0); this.lockRarityButtonContainer.add(this.lockRarityButtonText); @@ -139,7 +134,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { const continueButtonText = addTextObject( -24, 5, - i18next.t("modifierSelectUiHandler:continueNextWaveButton"), + i18next.t("rewardSelectUiHandler:continueNextWaveButton"), TextStyle.MESSAGE, ); continueButtonText.setName("text-continue-btn"); @@ -211,12 +206,12 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { this.updateRerollCostText(); - const typeOptions = args[1] as ModifierTypeOption[]; + const typeOptions = args[1] as RewardOption[]; const removeHealShop = globalScene.gameMode.hasNoShop; const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1)); globalScene.applyPlayerItems(TrainerItemEffect.HEAL_SHOP_COST, { numberHolder: baseShopCost }); const shopTypeOptions = !removeHealShop - ? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value) + ? getPlayerShopRewardOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value) : []; const optionsYOffset = shopTypeOptions.length > SHOP_OPTIONS_ROW_LIMIT ? -SINGLE_SHOP_ROW_YOFFSET : -DOUBLE_SHOP_ROW_YOFFSET; @@ -543,7 +538,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { -globalScene.game.canvas.height / 12 - (this.shopOptionsRows.length > 1 ? SINGLE_SHOP_ROW_YOFFSET - 2 : DOUBLE_SHOP_ROW_YOFFSET - 2), ); - ui.showText(i18next.t("modifierSelectUiHandler:continueNextWaveDescription")); + ui.showText(i18next.t("rewardSelectUiHandler:continueNextWaveDescription")); return ret; } @@ -565,33 +560,33 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { ); } - const type = options[this.cursor].modifierTypeOption.type; - type && ui.showText(type.getDescription()); - if (type instanceof TmModifierType) { + const reward = options[this.cursor].rewardOption.type; + reward && ui.showText(reward.getDescription()); + if (isTmReward(reward)) { // prepare the move overlay to be shown with the toggle - this.moveInfoOverlay.show(allMoves[type.moveId]); + this.moveInfoOverlay.show(allMoves[reward.moveId]); } } else if (cursor === 0) { this.cursorObj.setPosition( 6, this.lockRarityButtonContainer.visible ? OPTION_BUTTON_YPOSITION - 8 : OPTION_BUTTON_YPOSITION + 4, ); - ui.showText(i18next.t("modifierSelectUiHandler:rerollDesc")); + ui.showText(i18next.t("rewardSelectUiHandler:rerollDesc")); } else if (cursor === 1) { this.cursorObj.setPosition( (globalScene.game.canvas.width - this.transferButtonWidth - this.checkButtonWidth) / 6 - 30, OPTION_BUTTON_YPOSITION + 4, ); - ui.showText(i18next.t("modifierSelectUiHandler:transferDesc")); + ui.showText(i18next.t("rewardSelectUiHandler:transferDesc")); } else if (cursor === 2) { this.cursorObj.setPosition( (globalScene.game.canvas.width - this.checkButtonWidth) / 6 - 10, OPTION_BUTTON_YPOSITION + 4, ); - ui.showText(i18next.t("modifierSelectUiHandler:checkTeamDesc")); + ui.showText(i18next.t("rewardSelectUiHandler:checkTeamDesc")); } else { this.cursorObj.setPosition(6, OPTION_BUTTON_YPOSITION + 4); - ui.showText(i18next.t("modifierSelectUiHandler:lockRaritiesDesc")); + ui.showText(i18next.t("rewardSelectUiHandler:lockRaritiesDesc")); } return ret; @@ -671,13 +666,13 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { const formattedMoney = formatMoney(globalScene.moneyFormat, this.rerollCost); - this.rerollCostText.setText(i18next.t("modifierSelectUiHandler:rerollCost", { formattedMoney })); + this.rerollCostText.setText(i18next.t("rewardSelectUiHandler:rerollCost", { formattedMoney })); this.rerollCostText.setColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED)); this.rerollCostText.setShadowColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED, true)); } updateLockRaritiesText(): void { - const textStyle = globalScene.lockModifierTiers ? TextStyle.SUMMARY_BLUE : TextStyle.PARTY; + const textStyle = globalScene.lockRarityTiers ? TextStyle.SUMMARY_BLUE : TextStyle.PARTY; this.lockRarityButtonText.setColor(this.getTextColor(textStyle)); this.lockRarityButtonText.setShadowColor(this.getTextColor(textStyle, true)); } @@ -753,7 +748,7 @@ export class RewardSelectUiHandler extends AwaitableUiHandler { } class ModifierOption extends Phaser.GameObjects.Container { - public modifierTypeOption: ModifierTypeOption; + public rewardOption: RewardOption; private pb: Phaser.GameObjects.Sprite; private pbTint: Phaser.GameObjects.Sprite; private itemContainer: Phaser.GameObjects.Container; @@ -762,18 +757,18 @@ class ModifierOption extends Phaser.GameObjects.Container { private itemText: Phaser.GameObjects.Text; private itemCostText: Phaser.GameObjects.Text; - constructor(x: number, y: number, modifierTypeOption: ModifierTypeOption) { + constructor(x: number, y: number, rewardOption: RewardOption) { super(globalScene, x, y); - this.modifierTypeOption = modifierTypeOption; + this.rewardOption = rewardOption; this.setup(); } setup() { - if (!this.modifierTypeOption.cost) { + if (!this.rewardOption.cost) { const getPb = (): Phaser.GameObjects.Sprite => { - const pb = globalScene.add.sprite(0, -182, "pb", this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount)); + const pb = globalScene.add.sprite(0, -182, "pb", this.getPbAtlasKey(-this.rewardOption.upgradeCount)); pb.setScale(2); return pb; }; @@ -792,28 +787,28 @@ class ModifierOption extends Phaser.GameObjects.Container { this.add(this.itemContainer); const getItem = () => { - const item = globalScene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.getIcon()); + const item = globalScene.add.sprite(0, 0, "items", this.rewardOption.type?.getIcon()); return item; }; this.item = getItem(); this.itemContainer.add(this.item); - if (!this.modifierTypeOption.cost) { + if (!this.rewardOption.cost) { this.itemTint = getItem(); this.itemTint.setTintFill(Phaser.Display.Color.GetColor(255, 192, 255)); this.itemContainer.add(this.itemTint); } - this.itemText = addTextObject(0, 35, this.modifierTypeOption.type?.name!, TextStyle.PARTY, { align: "center" }); // TODO: is this bang correct? + this.itemText = addTextObject(0, 35, this.rewardOption.type?.name!, TextStyle.PARTY, { align: "center" }); // TODO: is this bang correct? this.itemText.setOrigin(0.5, 0); this.itemText.setAlpha(0); this.itemText.setTint( - this.modifierTypeOption.type?.tier ? getModifierTierTextTint(this.modifierTypeOption.type?.tier) : undefined, + this.rewardOption.type?.tier ? getRarityTierTextTint(this.rewardOption.type?.tier) : undefined, ); this.add(this.itemText); - if (this.modifierTypeOption.cost) { + if (this.rewardOption.cost) { this.itemCostText = addTextObject(0, 45, "", TextStyle.MONEY, { align: "center", }); @@ -827,7 +822,7 @@ class ModifierOption extends Phaser.GameObjects.Container { } show(remainingDuration: number, upgradeCountOffset: number) { - if (!this.modifierTypeOption.cost) { + if (!this.rewardOption.cost) { globalScene.tweens.add({ targets: this.pb, y: 0, @@ -863,11 +858,11 @@ class ModifierOption extends Phaser.GameObjects.Container { // TODO: Figure out proper delay between chains and then convert this into a single tween chain // rather than starting multiple tween chains. - for (let u = 0; u < this.modifierTypeOption.upgradeCount; u++) { + for (let u = 0; u < this.rewardOption.upgradeCount; u++) { globalScene.tweens.chain({ tweens: [ { - delay: remainingDuration - 2000 * (this.modifierTypeOption.upgradeCount - (u + 1 + upgradeCountOffset)), + delay: remainingDuration - 2000 * (this.rewardOption.upgradeCount - (u + 1 + upgradeCountOffset)), onStart: () => { globalScene.playSound("se/upgrade", { rate: 1 + 0.25 * u, @@ -879,7 +874,7 @@ class ModifierOption extends Phaser.GameObjects.Container { duration: 1000, ease: "Sine.easeIn", onComplete: () => { - this.pb.setTexture("pb", this.getPbAtlasKey(-this.modifierTypeOption.upgradeCount + (u + 1))); + this.pb.setTexture("pb", this.getPbAtlasKey(-this.rewardOption.upgradeCount + (u + 1))); }, }, { @@ -901,7 +896,7 @@ class ModifierOption extends Phaser.GameObjects.Container { return; } - if (!this.modifierTypeOption.cost) { + if (!this.rewardOption.cost) { this.pb.setTexture("pb", `${this.getPbAtlasKey(0)}_open`); globalScene.playSound("se/pb_rel"); @@ -922,7 +917,7 @@ class ModifierOption extends Phaser.GameObjects.Container { scale: 2, alpha: 1, }); - if (!this.modifierTypeOption.cost) { + if (!this.rewardOption.cost) { globalScene.tweens.add({ targets: this.itemTint, alpha: 0, @@ -951,16 +946,16 @@ class ModifierOption extends Phaser.GameObjects.Container { } getPbAtlasKey(tierOffset = 0) { - return getPokeballAtlasKey((this.modifierTypeOption.type?.tier! + tierOffset) as number as PokeballType); // TODO: is this bang correct? + return getPokeballAtlasKey((this.rewardOption.type?.tier! + tierOffset) as number as PokeballType); // TODO: is this bang correct? } updateCostText(): void { - const cost = Overrides.WAIVE_ROLL_FEE_OVERRIDE ? 0 : this.modifierTypeOption.cost; + const cost = Overrides.WAIVE_ROLL_FEE_OVERRIDE ? 0 : this.rewardOption.cost; const textStyle = cost <= globalScene.money ? TextStyle.MONEY : TextStyle.PARTY_RED; const formattedMoney = formatMoney(globalScene.moneyFormat, cost); - this.itemCostText.setText(i18next.t("modifierSelectUiHandler:itemCost", { formattedMoney })); + this.itemCostText.setText(i18next.t("rewardSelectUiHandler:itemCost", { formattedMoney })); this.itemCostText.setColor(getTextColor(textStyle, false, globalScene.uiTheme)); this.itemCostText.setShadowColor(getTextColor(textStyle, true, globalScene.uiTheme)); } diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 96b1500f7dd..8bd0b276b6f 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -16,7 +16,6 @@ import type { SpeciesId } from "#enums/species-id"; import { TrainerVariant } from "#enums/trainer-variant"; import { UiMode } from "#enums/ui-mode"; import { heldItemSortFunc } from "#items/item-utility"; -import { getLuckString, getLuckTextTint } from "#modifiers/modifier-type"; import { getVariantTint } from "#sprites/variant"; import type { SessionSaveData } from "#system/game-data"; import type { PokemonData } from "#system/pokemon-data"; @@ -25,6 +24,7 @@ import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ui import { UiHandler } from "#ui/ui-handler"; import { addWindow } from "#ui/ui-theme"; import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common"; +import { getLuckString, getLuckTextTint } from "#utils/party"; import i18next from "i18next"; import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle"; diff --git a/src/ui/text.ts b/src/ui/text.ts index 3c56d5a046b..73ddc235593 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { EggTier } from "#enums/egg-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { UiTheme } from "#enums/ui-theme"; import i18next from "#plugins/i18n"; import type Phaser from "phaser"; @@ -709,19 +709,19 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui } } -export function getModifierTierTextTint(tier: RewardTier): number { +export function getRarityTierTextTint(tier: RarityTier): number { switch (tier) { - case RewardTier.COMMON: + case RarityTier.COMMON: return 0xf8f8f8; - case RewardTier.GREAT: + case RarityTier.GREAT: return 0x4998f8; - case RewardTier.ULTRA: + case RarityTier.ULTRA: return 0xf8d038; - case RewardTier.ROGUE: + case RarityTier.ROGUE: return 0xdb4343; - case RewardTier.MASTER: + case RarityTier.MASTER: return 0xe331c5; - case RewardTier.LUXURY: + case RarityTier.LUXURY: return 0xe74c18; } } @@ -729,12 +729,12 @@ export function getModifierTierTextTint(tier: RewardTier): number { export function getEggTierTextTint(tier: EggTier): number { switch (tier) { case EggTier.COMMON: - return getModifierTierTextTint(RewardTier.COMMON); + return getRarityTierTextTint(RarityTier.COMMON); case EggTier.RARE: - return getModifierTierTextTint(RewardTier.GREAT); + return getRarityTierTextTint(RarityTier.GREAT); case EggTier.EPIC: - return getModifierTierTextTint(RewardTier.ULTRA); + return getRarityTierTextTint(RarityTier.ULTRA); case EggTier.LEGENDARY: - return getModifierTierTextTint(RewardTier.MASTER); + return getRarityTierTextTint(RarityTier.MASTER); } } diff --git a/src/utils/modifier-utils.ts b/src/utils/modifier-utils.ts deleted file mode 100644 index 2cc9c041457..00000000000 --- a/src/utils/modifier-utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { modifierTypes } from "#data/data-lists"; -import { ModifierPoolType } from "#enums/modifier-pool-type"; -import { modifierPool } from "#modifiers/modifier-pools"; -import type { ModifierType } from "#modifiers/modifier-type"; -import type { ModifierPool, ModifierTypeFunc } from "#types/modifier-types"; - -export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool { - switch (poolType) { - case ModifierPoolType.PLAYER: - return modifierPool; - } -} - -// TODO: document this -export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType { - const modifierType = modifierTypeFunc(); - if (!modifierType.id) { - modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc)!; // TODO: is this bang correct? - } - return modifierType; -} diff --git a/src/utils/party.ts b/src/utils/party.ts new file mode 100644 index 00000000000..f1ed8579f29 --- /dev/null +++ b/src/utils/party.ts @@ -0,0 +1,56 @@ +import { timedEventManager } from "#app/global-event-manager"; +import { globalScene } from "#app/global-scene"; +import { RarityTier } from "#enums/reward-tier"; +import type { Pokemon } from "#field/pokemon"; +import { getRarityTierTextTint } from "#ui/text"; +import { NumberHolder, randSeedInt } from "./common"; + +/** + * Calculates the team's luck value. + * @param party The player's party. + * @returns A number between 0 and 14 based on the party's total luck value, or a random number between 0 and 14 if the player is in Daily Run mode. + */ +export function getPartyLuckValue(party: Pokemon[]): number { + if (globalScene.gameMode.isDaily) { + const DailyLuck = new NumberHolder(0); + globalScene.executeWithSeedOffset( + () => { + DailyLuck.value = randSeedInt(15); // Random number between 0 and 14 + }, + 0, + globalScene.seed, + ); + return DailyLuck.value; + } + const eventSpecies = timedEventManager.getEventLuckBoostedSpecies(); + const luck = Phaser.Math.Clamp( + party + .map(p => (p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 1 : 0) : 0)) + .reduce((total: number, value: number) => (total += value), 0), + 0, + 14, + ); + return Math.min(timedEventManager.getEventLuckBoost() + (luck ?? 0), 14); +} + +export function getLuckString(luckValue: number): string { + return ["D", "C", "C+", "B-", "B", "B+", "A-", "A", "A+", "A++", "S", "S+", "SS", "SS+", "SSS"][luckValue]; +} + +export function getLuckTextTint(luckValue: number): number { + let rarityTier: RarityTier; + if (luckValue > 11) { + rarityTier = RarityTier.LUXURY; + } else if (luckValue > 9) { + rarityTier = RarityTier.MASTER; + } else if (luckValue > 5) { + rarityTier = RarityTier.ROGUE; + } else if (luckValue > 2) { + rarityTier = RarityTier.ULTRA; + } else if (luckValue) { + rarityTier = RarityTier.GREAT; + } else { + rarityTier = RarityTier.COMMON; + } + return getRarityTierTextTint(rarityTier); +} diff --git a/test/achievements/achievement.test.ts b/test/achievements/achievement.test.ts index 3b59aceac97..7afadc5d2ad 100644 --- a/test/achievements/achievement.test.ts +++ b/test/achievements/achievement.test.ts @@ -11,7 +11,6 @@ import { HealAchv, HeldItemAchv, LevelAchv, - ModifierAchv, MoneyAchv, RibbonAchv, } from "#system/achv"; @@ -260,7 +259,7 @@ describe("achvs", () => { expect(achvs.TERASTALLIZE).toBeInstanceOf(Achv); expect(achvs.STELLAR_TERASTALLIZE).toBeInstanceOf(Achv); expect(achvs.SPLICE).toBeInstanceOf(Achv); - expect(achvs.MINI_BLACK_HOLE).toBeInstanceOf(ModifierAchv); + expect(achvs.MINI_BLACK_HOLE).toBeInstanceOf(HeldItemAchv); expect(achvs.CATCH_MYTHICAL).toBeInstanceOf(Achv); expect(achvs.CATCH_SUB_LEGENDARY).toBeInstanceOf(Achv); expect(achvs.CATCH_LEGENDARY).toBeInstanceOf(Achv); diff --git a/test/daily-mode.test.ts b/test/daily-mode.test.ts index 6973982c69d..425ebae2879 100644 --- a/test/daily-mode.test.ts +++ b/test/daily-mode.test.ts @@ -74,7 +74,7 @@ describe("Shop modifications", async () => { game.move.select(MoveId.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); - game.onNextPrompt("SelectRewardPhase", UiMode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectRewardPhase", UiMode.REWARD_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(RewardSelectUiHandler); game.modifiers.testCheck("EVIOLITE", false).testCheck("MINI_BLACK_HOLE", false); }); @@ -85,7 +85,7 @@ describe("Shop modifications", async () => { game.move.select(MoveId.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); - game.onNextPrompt("SelectRewardPhase", UiMode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectRewardPhase", UiMode.REWARD_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(RewardSelectUiHandler); game.modifiers.testCheck("EVIOLITE", true).testCheck("MINI_BLACK_HOLE", true); }); diff --git a/test/items/dire-hit.test.ts b/test/items/dire-hit.test.ts index d00faabe773..de20e378b4e 100644 --- a/test/items/dire-hit.test.ts +++ b/test/items/dire-hit.test.ts @@ -69,7 +69,7 @@ describe("Items - Dire Hit", () => { // Forced DIRE_HIT to spawn in the first slot with override game.onNextPrompt( "SelectRewardPhase", - UiMode.MODIFIER_SELECT, + UiMode.REWARD_SELECT, () => { const handler = game.scene.ui.getHandler() as RewardSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/double-battle-chance-booster.test.ts b/test/items/double-battle-chance-booster.test.ts index a7192b89ee4..9483095104d 100644 --- a/test/items/double-battle-chance-booster.test.ts +++ b/test/items/double-battle-chance-booster.test.ts @@ -75,7 +75,7 @@ describe("Items - Double Battle Chance Boosters", () => { // Forced LURE to spawn in the first slot with override game.onNextPrompt( "SelectRewardPhase", - UiMode.MODIFIER_SELECT, + UiMode.REWARD_SELECT, () => { const handler = game.scene.ui.getHandler() as RewardSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/eviolite.test.ts b/test/items/eviolite.test.ts index d56f2a99910..19c50ba90b4 100644 --- a/test/items/eviolite.test.ts +++ b/test/items/eviolite.test.ts @@ -1,8 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder, randItem } from "#utils/common"; import Phaser from "phaser"; diff --git a/test/items/exp-booster.test.ts b/test/items/exp-booster.test.ts index 1206cadc89a..87d22350c00 100644 --- a/test/items/exp-booster.test.ts +++ b/test/items/exp-booster.test.ts @@ -1,7 +1,7 @@ import { AbilityId } from "#enums/ability-id"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder } from "#utils/common"; import Phaser from "phaser"; diff --git a/test/items/light-ball.test.ts b/test/items/light-ball.test.ts index 0f631002dce..74f88f695c1 100644 --- a/test/items/light-ball.test.ts +++ b/test/items/light-ball.test.ts @@ -1,8 +1,8 @@ +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import i18next from "#plugins/i18n"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder } from "#utils/common"; diff --git a/test/items/lock-capsule.test.ts b/test/items/lock-capsule.test.ts index 64216c6045a..435b6814bc4 100644 --- a/test/items/lock-capsule.test.ts +++ b/test/items/lock-capsule.test.ts @@ -1,6 +1,6 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { TrainerItemId } from "#enums/trainer-item-id"; import { UiMode } from "#enums/ui-mode"; import { SelectRewardPhase } from "#phases/select-reward-phase"; @@ -37,14 +37,14 @@ describe("Items - Lock Capsule", () => { await game.classicMode.startBattle(); game.scene.phaseManager.overridePhase( new SelectRewardPhase(0, undefined, { - guaranteedModifierTiers: [RewardTier.COMMON, RewardTier.COMMON, RewardTier.COMMON], + guaranteedRarityTiers: [RarityTier.COMMON, RarityTier.COMMON, RarityTier.COMMON], fillRemaining: false, }), ); - game.onNextPrompt("SelectRewardPhase", UiMode.MODIFIER_SELECT, () => { - const selectModifierPhase = game.scene.phaseManager.getCurrentPhase() as SelectRewardPhase; - const rerollCost = selectModifierPhase.getRerollCost(true); + game.onNextPrompt("SelectRewardPhase", UiMode.REWARD_SELECT, () => { + const selectRewardPhase = game.scene.phaseManager.getCurrentPhase() as SelectRewardPhase; + const rerollCost = selectRewardPhase.getRerollCost(true); expect(rerollCost).toBe(150); }); diff --git a/test/items/metal-powder.test.ts b/test/items/metal-powder.test.ts index b846a085075..9cb069c73ce 100644 --- a/test/items/metal-powder.test.ts +++ b/test/items/metal-powder.test.ts @@ -1,9 +1,8 @@ -import { modifierTypes } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import i18next from "#plugins/i18n"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder } from "#utils/common"; @@ -97,10 +96,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.METAL_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.DEF, statValue: defValue }); expect(defValue.value / defStat).toBe(2); @@ -130,10 +126,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.METAL_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.DEF, statValue: defValue }); expect(defValue.value / defStat).toBe(2); @@ -163,10 +156,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.METAL_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.DEF, statValue: defValue }); expect(defValue.value / defStat).toBe(2); @@ -186,10 +176,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.METAL_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.DEF, statValue: defValue }); expect(defValue.value / defStat).toBe(1); diff --git a/test/items/quick-powder.test.ts b/test/items/quick-powder.test.ts index ddb77587a66..fdab41514b6 100644 --- a/test/items/quick-powder.test.ts +++ b/test/items/quick-powder.test.ts @@ -1,9 +1,8 @@ -import { modifierTypes } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import i18next from "#plugins/i18n"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder } from "#utils/common"; @@ -97,10 +96,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.QUICK_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.SPD, statValue: spdValue }); expect(spdValue.value / spdStat).toBe(2); @@ -130,10 +126,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.QUICK_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.SPD, statValue: spdValue }); expect(spdValue.value / spdStat).toBe(2); @@ -163,10 +156,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.QUICK_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.SPD, statValue: spdValue }); expect(spdValue.value / spdStat).toBe(2); @@ -186,10 +176,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.QUICK_POWDER); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.SPD, statValue: spdValue }); expect(spdValue.value / spdStat).toBe(1); diff --git a/test/items/temp-stat-stage-booster.test.ts b/test/items/temp-stat-stage-booster.test.ts index d1cedd92cd2..de51c1612b6 100644 --- a/test/items/temp-stat-stage-booster.test.ts +++ b/test/items/temp-stat-stage-booster.test.ts @@ -131,7 +131,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { // Forced X_ATTACK to spawn in the first slot with override game.onNextPrompt( "SelectRewardPhase", - UiMode.MODIFIER_SELECT, + UiMode.REWARD_SELECT, () => { const handler = game.scene.ui.getHandler() as RewardSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/thick-club.test.ts b/test/items/thick-club.test.ts index e998466126e..1e7236d616d 100644 --- a/test/items/thick-club.test.ts +++ b/test/items/thick-club.test.ts @@ -1,9 +1,8 @@ -import { modifierTypes } from "#data/data-lists"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { SpeciesId } from "#enums/species-id"; import { Stat } from "#enums/stat"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import i18next from "#plugins/i18n"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder, randInt } from "#utils/common"; @@ -97,10 +96,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(2); @@ -120,10 +116,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(2); @@ -143,10 +136,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(2); @@ -180,10 +170,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(2); @@ -217,10 +204,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(2); @@ -240,10 +224,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - await game.scene.addModifier( - modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), - true, - ); + partyMember.heldItemManager.add(HeldItemId.THICK_CLUB); applyHeldItems(HeldItemEffect.STAT_BOOST, { pokemon: partyMember, stat: Stat.ATK, statValue: atkValue }); expect(atkValue.value / atkStat).toBe(1); diff --git a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index 2d000c77a6c..a12508a04a0 100644 --- a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -149,13 +149,13 @@ describe("Berries Abound - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - for (const option of modifierSelectHandler.options) { - expect(option.modifierTypeOption.type.id).toContain("BERRY"); + expect(rewardSelectHandler.options.length).toEqual(5); + for (const option of rewardSelectHandler.options) { + expect(option.rewardOption.type.id).toContain("BERRY"); } }); }); @@ -234,13 +234,13 @@ describe("Berries Abound - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - for (const option of modifierSelectHandler.options) { - expect(option.modifierTypeOption.type.id).toContain("BERRY"); + expect(rewardSelectHandler.options.length).toEqual(5); + for (const option of rewardSelectHandler.options) { + expect(option.rewardOption.type.id).toContain("BERRY"); } expect(EncounterDialogueUtils.showEncounterText).toHaveBeenCalledWith(`${namespace}:option.2.selected`); diff --git a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts index e3926e1f2ef..2a653f903b3 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -412,23 +412,23 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); }); - it("should proceed to rewards screen with 0-1 Bug Types reward options", async () => { + it("should proceed to allRewards screen with 0-1 Bug Types reward options", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty); await runMysteryEncounterToEnd(game, 2); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(2); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("SUPER_LURE"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toBe("GREAT_BALL"); + expect(rewardSelectHandler.options.length).toEqual(2); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("SUPER_LURE"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toBe("GREAT_BALL"); }); - it("should proceed to rewards screen with 2-3 Bug Types reward options", async () => { + it("should proceed to allRewards screen with 2-3 Bug Types reward options", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [ SpeciesId.BUTTERFREE, SpeciesId.BEEDRILL, @@ -438,17 +438,17 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("QUICK_CLAW"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toBe("MAX_LURE"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toBe("ULTRA_BALL"); + expect(rewardSelectHandler.options.length).toEqual(3); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("QUICK_CLAW"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toBe("MAX_LURE"); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toBe("ULTRA_BALL"); }); - it("should proceed to rewards screen with 4-5 Bug Types reward options", async () => { + it("should proceed to allRewards screen with 4-5 Bug Types reward options", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [ SpeciesId.BUTTERFREE, SpeciesId.BEEDRILL, @@ -460,17 +460,17 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("GRIP_CLAW"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toBe("MAX_LURE"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toBe("ROGUE_BALL"); + expect(rewardSelectHandler.options.length).toEqual(3); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("GRIP_CLAW"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toBe("MAX_LURE"); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toBe("ROGUE_BALL"); }); - it("should proceed to rewards screen with 6 Bug Types reward options (including form change item)", async () => { + it("should proceed to allRewards screen with 6 Bug Types reward options (including form change item)", async () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [ SpeciesId.BUTTERFREE, SpeciesId.BEEDRILL, @@ -484,15 +484,15 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("MASTER_BALL"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toBe("MEGA_BRACELET"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toBe("DYNAMAX_BAND"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toBe("FORM_CHANGE_ITEM"); + expect(rewardSelectHandler.options.length).toEqual(4); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("MASTER_BALL"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toBe("MEGA_BRACELET"); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toBe("DYNAMAX_BAND"); + expect(rewardSelectHandler.options[3].rewardOption.type.id).toBe("FORM_CHANGE_ITEM"); }); it("should leave encounter without battle", async () => { @@ -548,7 +548,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); }); - it("should remove the gifted item and proceed to rewards screen", async () => { + it("should remove the gifted item and proceed to allRewards screen", async () => { game.override.startingHeldItems([{ entry: HeldItemId.GRIP_CLAW, count: 1 }]); await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.BUTTERFREE]); @@ -559,13 +559,13 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(2); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("MYSTERY_ENCOUNTER_GOLDEN_BUG_NET"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toBe("REVIVER_SEED"); + expect(rewardSelectHandler.options.length).toEqual(2); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("MYSTERY_ENCOUNTER_GOLDEN_BUG_NET"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toBe("REVIVER_SEED"); const gripClawCountAfter = scene.getPlayerParty()[0].heldItemManager.getStack(HeldItemId.GRIP_CLAW); expect(gripClawCountBefore - 1).toBe(gripClawCountAfter); diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 3233582f892..72c69ac36e3 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -9,11 +9,11 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokemonType } from "#enums/pokemon-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerType } from "#enums/trainer-type"; import { UiMode } from "#enums/ui-mode"; -import { getHeldItemTier } from "#items/held-item-tiers"; +import { getHeldItemTier } from "#items/held-item-default-tiers"; import { PokemonMove } from "#moves/pokemon-move"; import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-encounter"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; @@ -277,10 +277,10 @@ describe("Clowning Around - Mystery Encounter", () => { const leadItemsAfter = scene.getPlayerParty()[0].getHeldItems(); const ultraCountAfter = leadItemsAfter - .filter(m => getHeldItemTier(m) === RewardTier.ULTRA) + .filter(m => getHeldItemTier(m) === RarityTier.ULTRA) .reduce((a, b) => a + scene.getPlayerParty()[0].heldItemManager.getStack(b), 0); const rogueCountAfter = leadItemsAfter - .filter(m => getHeldItemTier(m) === RewardTier.ROGUE) + .filter(m => getHeldItemTier(m) === RarityTier.ROGUE) .reduce((a, b) => a + scene.getPlayerParty()[0].heldItemManager.getStack(b), 0); expect(ultraCountAfter).toBe(13); expect(rogueCountAfter).toBe(7); diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index f8247e378b4..afbe2e30e59 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -118,7 +118,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(movePhases.filter(p => (p as MovePhase).move.moveId === MoveId.REVELATION_DANCE).length).toBe(1); // Revelation Dance used before battle }); - it("should have a Baton in the rewards after battle", async () => { + it("should have a Baton in the allRewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); // Make party lead's level arbitrarily high to not get KOed by move const partyLead = scene.getPlayerParty()[0]; @@ -130,12 +130,12 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); // Should fill remaining - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toContain("BATON"); + expect(rewardSelectHandler.options.length).toEqual(3); // Should fill remaining + expect(rewardSelectHandler.options[0].rewardOption.type.id).toContain("BATON"); }); }); diff --git a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index 6fb96ca6a39..11b1cadd7e9 100644 --- a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -96,13 +96,13 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - for (const option of modifierSelectHandler.options) { - expect(option.modifierTypeOption.type.id).toContain("TM_"); + expect(rewardSelectHandler.options.length).toEqual(5); + for (const option of rewardSelectHandler.options) { + expect(option.rewardOption.type.id).toContain("TM_"); } }); @@ -133,15 +133,14 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - for (const option of modifierSelectHandler.options) { + expect(rewardSelectHandler.options.length).toEqual(3); + for (const option of rewardSelectHandler.options) { expect( - option.modifierTypeOption.type.id.includes("PP_UP") || - option.modifierTypeOption.type.id.includes("BASE_STAT_BOOSTER"), + option.rewardOption.type.id.includes("PP_UP") || option.rewardOption.type.id.includes("BASE_STAT_BOOSTER"), ).toBeTruthy(); } }); @@ -173,15 +172,15 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - for (const option of modifierSelectHandler.options) { + expect(rewardSelectHandler.options.length).toEqual(5); + for (const option of rewardSelectHandler.options) { expect( - option.modifierTypeOption.type.id.includes("DIRE_HIT") || - option.modifierTypeOption.type.id.includes("TEMP_STAT_STAGE_BOOSTER"), + option.rewardOption.type.id.includes("DIRE_HIT") || + option.rewardOption.type.id.includes("TEMP_STAT_STAGE_BOOSTER"), ).toBeTruthy(); } }); @@ -213,13 +212,13 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); - for (const option of modifierSelectHandler.options) { - expect(option.modifierTypeOption.type.id).toContain("BALL"); + expect(rewardSelectHandler.options.length).toEqual(4); + for (const option of rewardSelectHandler.options) { + expect(option.rewardOption.type.id).toContain("BALL"); } }); diff --git a/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/test/mystery-encounter/encounters/field-trip-encounter.test.ts index 46a0791784b..bde7ec88e5e 100644 --- a/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -87,36 +87,36 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 2 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(0); + expect(rewardSelectHandler.options.length).toEqual(0); }); - it("Should give proper rewards on correct Physical move option", async () => { + it("Should give proper allRewards on correct Physical move option", async () => { await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options.length).toEqual(5); + expect(rewardSelectHandler.options[0].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_attack"), ); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[1].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_defense"), ); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[2].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_speed"), ); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[3].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.DIRE_HIT.name"), ); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[4].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.RARER_CANDY.name"), ); }); @@ -148,36 +148,36 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(0); + expect(rewardSelectHandler.options.length).toEqual(0); }); - it("Should give proper rewards on correct Special move option", async () => { + it("Should give proper allRewards on correct Special move option", async () => { await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty); await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 2 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options.length).toEqual(5); + expect(rewardSelectHandler.options[0].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_sp_atk"), ); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[1].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_sp_def"), ); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[2].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_speed"), ); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[3].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.DIRE_HIT.name"), ); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[4].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.RARER_CANDY.name"), ); }); @@ -209,31 +209,31 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(0); + expect(rewardSelectHandler.options.length).toEqual(0); }); - it("Should give proper rewards on correct Special move option", async () => { + it("Should give proper allRewards on correct Special move option", async () => { vi.spyOn(i18next, "t"); await game.runToMysteryEncounter(MysteryEncounterType.FIELD_TRIP, defaultParty); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 3 }); await game.phaseInterceptor.to(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options.length).toEqual(5); + expect(rewardSelectHandler.options[0].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_accuracy"), ); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[1].rewardOption.type.name).toBe( i18next.t("modifierType:TempStatStageBoosterItem.x_speed"), ); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[2].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", { modifierCount: 5, pokeballName: i18next.t("pokeball:greatBall"), @@ -243,10 +243,10 @@ describe("Field Trip - Mystery Encounter", () => { "modifierType:ModifierType.AddPokeballModifierType.name", expect.objectContaining({ modifierCount: 5 }), ); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[3].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.IV_SCANNER.name"), ); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe( + expect(rewardSelectHandler.options[4].rewardOption.type.name).toBe( i18next.t("modifierType:ModifierType.RARER_CANDY.name"), ); }); diff --git a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts index 34b88b3bf67..19d13e69a0d 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -125,13 +125,13 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.phaseInterceptor.to(SelectRewardPhase, false); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(item.type.name).toBe(modifierSelectHandler.options[0].modifierTypeOption.type.name); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(item.type.name).toBe(rewardSelectHandler.options[0].rewardOption.type.name); }); }); @@ -185,13 +185,13 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.phaseInterceptor.to(SelectRewardPhase, false); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(item.type.name).toBe(modifierSelectHandler.options[0].modifierTypeOption.type.name); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(item.type.name).toBe(rewardSelectHandler.options[0].rewardOption.type.name); expect(leaveEncounterWithoutBattleSpy).toBeCalled(); }); diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index 412686cd658..2cae72c3c8d 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -168,7 +168,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); }); - it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => { + it("should have no items in allRewards if Wubboffet doesn't take enough damage", async () => { scene.money = 20000; await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); @@ -187,14 +187,14 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(0); + expect(rewardSelectHandler.options.length).toEqual(0); }); - it("should have Wide Lens item in rewards if Wubboffet is at 15-33% HP remaining", async () => { + it("should have Wide Lens item in allRewards if Wubboffet is at 15-33% HP remaining", async () => { scene.money = 20000; game.override.moveset([MoveId.SPLASH]); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); @@ -216,15 +216,15 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("WIDE_LENS"); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual("WIDE_LENS"); }); - it("should have Scope Lens item in rewards if Wubboffet is at 3-15% HP remaining", async () => { + it("should have Scope Lens item in allRewards if Wubboffet is at 3-15% HP remaining", async () => { scene.money = 20000; game.override.moveset([MoveId.SPLASH]); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); @@ -246,15 +246,15 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("SCOPE_LENS"); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual("SCOPE_LENS"); }); - it("should have Multi Lens item in rewards if Wubboffet is at <3% HP remaining", async () => { + it("should have Multi Lens item in allRewards if Wubboffet is at <3% HP remaining", async () => { scene.money = 20000; game.override.moveset([MoveId.SPLASH]); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); @@ -276,12 +276,12 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MULTI_LENS"); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual("MULTI_LENS"); }); }); diff --git a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts index 66f05e7b8ed..a58bb187ad2 100644 --- a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts +++ b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts @@ -4,7 +4,7 @@ import { HeldItemId } from "#enums/held-item-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; @@ -212,7 +212,7 @@ describe("Global Trade System - Mystery Encounter", () => { }); }); - it("should decrease item stacks of chosen item and have a tiered up item in rewards", async () => { + it("should decrease item stacks of chosen item and have a tiered up item in allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty); // Set 2 Soul Dew on party lead @@ -223,12 +223,12 @@ describe("Global Trade System - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.tier).toBe(RewardTier.MASTER); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.tier).toBe(RarityTier.MASTER); const soulDewAfter = scene.getPlayerParty()[0].heldItemManager.getStack(HeldItemId.SOUL_DEW); expect(soulDewAfter).toBe(1); }); diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index 092dd6092be..4df9d75e0d6 100644 --- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -5,7 +5,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-challengers-encounter"; @@ -157,7 +157,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); - it("should have normal trainer rewards after battle", async () => { + it("should have normal trainer allRewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -165,14 +165,14 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toContain("TM_COMMON"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toContain("TM_GREAT"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toContain("MEMORY_MUSHROOM"); + expect(rewardSelectHandler.options.length).toEqual(3); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toContain("TM_COMMON"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toContain("TM_GREAT"); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toContain("MEMORY_MUSHROOM"); }); }); @@ -201,7 +201,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); - it("should have hard trainer rewards after battle", async () => { + it("should have hard trainer allRewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -209,27 +209,27 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); + expect(rewardSelectHandler.options.length).toEqual(4); expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.ULTRA); + rewardSelectHandler.options[0].rewardOption.type.tier - + rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toBe(RarityTier.ULTRA); expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.ULTRA); + rewardSelectHandler.options[1].rewardOption.type.tier - + rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toBe(RarityTier.ULTRA); expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.GREAT); + rewardSelectHandler.options[2].rewardOption.type.tier - + rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toBe(RarityTier.GREAT); expect( - modifierSelectHandler.options[3].modifierTypeOption.type.tier - - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.GREAT); + rewardSelectHandler.options[3].rewardOption.type.tier - + rewardSelectHandler.options[3].rewardOption.upgradeCount, + ).toBe(RarityTier.GREAT); }); }); @@ -258,7 +258,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); - it("should have brutal trainer rewards after battle", async () => { + it("should have brutal trainer allRewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -266,27 +266,27 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); + expect(rewardSelectHandler.options.length).toEqual(4); expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.ROGUE); + rewardSelectHandler.options[0].rewardOption.type.tier - + rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toBe(RarityTier.ROGUE); expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.ROGUE); + rewardSelectHandler.options[1].rewardOption.type.tier - + rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toBe(RarityTier.ROGUE); expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.ULTRA); + rewardSelectHandler.options[2].rewardOption.type.tier - + rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toBe(RarityTier.ULTRA); expect( - modifierSelectHandler.options[3].modifierTypeOption.type.tier - - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount, - ).toBe(RewardTier.GREAT); + rewardSelectHandler.options[3].rewardOption.type.tier - + rewardSelectHandler.options[3].rewardOption.upgradeCount, + ).toBe(RarityTier.GREAT); }); }); }); diff --git a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 56526791102..3e9c8e2e5ad 100644 --- a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -295,7 +295,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(enemyField[0].isBoss()).toBe(true); }); - it("should have Magnet and Metal Coat in rewards after battle", async () => { + it("should have Magnet and Metal Coat in allRewards after battle", async () => { await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -303,18 +303,18 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; expect( - modifierSelectHandler.options.some( - opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.metal_coat"), + rewardSelectHandler.options.some( + opt => opt.rewardOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.metal_coat"), ), ).toBe(true); expect( - modifierSelectHandler.options.some( - opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.magnet"), + rewardSelectHandler.options.some( + opt => opt.rewardOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.magnet"), ), ).toBe(true); }); diff --git a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 328f3463ff0..872b2f9c076 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -4,6 +4,7 @@ import { CustomPokemonData } from "#data/pokemon-data"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BiomeId } from "#enums/biome-id"; +import { HeldItemEffect } from "#enums/held-item-effect"; import { HeldItemId } from "#enums/held-item-id"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -13,7 +14,6 @@ import { Nature } from "#enums/nature"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; import { applyHeldItems } from "#items/all-held-items"; -import { HeldItemEffect } from "#items/held-item"; import { PokemonMove } from "#moves/pokemon-move"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; @@ -218,7 +218,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(movePhases.filter(p => (p as MovePhase).move.moveId === MoveId.STEALTH_ROCK).length).toBe(1); }); - it("should have Soul Dew in rewards", async () => { + it("should have Soul Dew in allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -226,12 +226,12 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("SOUL_DEW"); + expect(rewardSelectHandler.options.length).toEqual(3); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual("SOUL_DEW"); }); }); }); diff --git a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts index 1847ba15fcf..8be8061fdfa 100644 --- a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -293,18 +293,18 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(0); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); - // Should have Macho Brace in the rewards + // Should have Macho Brace in the allRewards await skipBattleToNextBattle(game, true); await game.phaseInterceptor.to(SelectRewardPhase, false); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("MYSTERY_ENCOUNTER_MACHO_BRACE"); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("MYSTERY_ENCOUNTER_MACHO_BRACE"); }); }); @@ -335,18 +335,18 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(partyHealPhases.length).toBe(1); }); - it("should have a Rarer Candy in the rewards", async () => { + it("should have a Rarer Candy in the allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await runMysteryEncounterToEnd(game, 2); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(1); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toBe("RARER_CANDY"); + expect(rewardSelectHandler.options.length).toEqual(1); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toBe("RARER_CANDY"); }); }); }); diff --git a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts index 5b10ecf4f0c..1e4d1c3deba 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -6,7 +6,7 @@ import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { TrainerItemId } from "#enums/trainer-item-id"; import { UiMode } from "#enums/ui-mode"; @@ -200,7 +200,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(movePhases.filter(p => (p as MovePhase).move.moveId === MoveId.STOCKPILE).length).toBe(1); }); - it("should have 2 Rogue, 1 Ultra, 1 Great in rewards", async () => { + it("should have 2 Rogue, 1 Ultra, 1 Great in allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -208,27 +208,27 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); + expect(rewardSelectHandler.options.length).toEqual(4); expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ROGUE); + rewardSelectHandler.options[0].rewardOption.type.tier - + rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toEqual(RarityTier.ROGUE); expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ROGUE); + rewardSelectHandler.options[1].rewardOption.type.tier - + rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toEqual(RarityTier.ROGUE); expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ULTRA); + rewardSelectHandler.options[2].rewardOption.type.tier - + rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toEqual(RarityTier.ULTRA); expect( - modifierSelectHandler.options[3].modifierTypeOption.type.tier - - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.GREAT); + rewardSelectHandler.options[3].rewardOption.type.tier - + rewardSelectHandler.options[3].rewardOption.upgradeCount, + ).toEqual(RarityTier.GREAT); }); }); }); diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index fd7c093a8ef..c32779e26aa 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -3,7 +3,7 @@ import { BiomeId } from "#enums/biome-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -import { RewardTier } from "#enums/reward-tier"; +import { RarityTier } from "#enums/reward-tier"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; @@ -136,23 +136,23 @@ describe("Weird Dream - Mystery Encounter", () => { expect(plus40To50.length).toBe(1); }); - it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in rewards", async () => { + it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectRewardPhase, false); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("ROGUE_BALL"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("MINT"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("MINT"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("MINT"); + expect(rewardSelectHandler.options.length).toEqual(5); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual("MEMORY_MUSHROOM"); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toEqual("ROGUE_BALL"); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toEqual("MINT"); + expect(rewardSelectHandler.options[3].rewardOption.type.id).toEqual("MINT"); + expect(rewardSelectHandler.options[3].rewardOption.type.id).toEqual("MINT"); }); it("should leave encounter without battle", async () => { @@ -191,7 +191,7 @@ describe("Weird Dream - Mystery Encounter", () => { expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length); }); - it("should have 2 Rogue/2 Ultra/2 Great items in rewards", async () => { + it("should have 2 Rogue/2 Ultra/2 Great items in allRewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); @@ -199,35 +199,35 @@ describe("Weird Dream - Mystery Encounter", () => { expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectRewardPhase.name); await game.phaseInterceptor.run(SelectRewardPhase); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( h => h instanceof RewardSelectUiHandler, ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(6); + expect(rewardSelectHandler.options.length).toEqual(6); expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ROGUE); + rewardSelectHandler.options[0].rewardOption.type.tier - + rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toEqual(RarityTier.ROGUE); expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ROGUE); + rewardSelectHandler.options[1].rewardOption.type.tier - + rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toEqual(RarityTier.ROGUE); expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ULTRA); + rewardSelectHandler.options[2].rewardOption.type.tier - + rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toEqual(RarityTier.ULTRA); expect( - modifierSelectHandler.options[3].modifierTypeOption.type.tier - - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ULTRA); + rewardSelectHandler.options[3].rewardOption.type.tier - + rewardSelectHandler.options[3].rewardOption.upgradeCount, + ).toEqual(RarityTier.ULTRA); expect( - modifierSelectHandler.options[4].modifierTypeOption.type.tier - - modifierSelectHandler.options[4].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.GREAT); + rewardSelectHandler.options[4].rewardOption.type.tier - + rewardSelectHandler.options[4].rewardOption.upgradeCount, + ).toEqual(RarityTier.GREAT); expect( - modifierSelectHandler.options[5].modifierTypeOption.type.tier - - modifierSelectHandler.options[5].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.GREAT); + rewardSelectHandler.options[5].rewardOption.type.tier - + rewardSelectHandler.options[5].rewardOption.upgradeCount, + ).toEqual(RarityTier.GREAT); }); }); diff --git a/test/phases/form-change-phase.test.ts b/test/phases/form-change-phase.test.ts index 3caf824b252..df0bd74f8ef 100644 --- a/test/phases/form-change-phase.test.ts +++ b/test/phases/form-change-phase.test.ts @@ -1,60 +1,59 @@ -import { modifierTypes } from "#data/data-lists"; -import { AbilityId } from "#enums/ability-id"; -import { MoveId } from "#enums/move-id"; -import { PokemonType } from "#enums/pokemon-type"; -import { SpeciesId } from "#enums/species-id"; -import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; -import { GameManager } from "#test/test-utils/game-manager"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import type { RewardKeys } from "#items/reward"; +import { itemPoolChecks } from "#items/reward"; +import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"; +import { expect } from "vitest"; -describe("Form Change Phase", () => { - let phaserGame: Phaser.Game; - let game: GameManager; +export class ModifierHelper extends GameManagerHelper { + /** + * Adds a Modifier to the list of modifiers to check for. + * + * Note that all modifiers are updated during the start of `SelectRewardPhase`. + * @param modifier The Modifier to add. + * @returns `this` + */ + addCheck(modifier: RewardKeys): this { + itemPoolChecks.set(modifier, undefined); + return this; + } - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); + /** + * `get`s a value from the `itemPoolChecks` map. + * + * If the item is in the Modifier Pool, and the player can get it, will return `true`. + * + * If the item is *not* in the Modifier Pool, will return `false`. + * + * If a `SelectRewardPhase` has not occurred, and we do not know if the item is in the Modifier Pool or not, will return `undefined`. + * @param modifier + * @returns + */ + getCheck(modifier: RewardKeys): boolean | undefined { + return itemPoolChecks.get(modifier); + } - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); + /** + * `expect`s a Modifier `toBeTruthy` (in the Modifier Pool) or `Falsy` (unobtainable on this floor). Use during a test. + * + * Note that if a `SelectRewardPhase` has not been run yet, these values will be `undefined`, and the check will fail. + * @param modifier The modifier to check. + * @param expectToBePreset Whether the Modifier should be in the Modifier Pool. Set to `false` to expect it to be absent instead. + * @returns `this` + */ + testCheck(modifier: RewardKeys, expectToBePreset: boolean): this { + if (expectToBePreset) { + expect(itemPoolChecks.get(modifier)).toBeTruthy(); + } + expect(itemPoolChecks.get(modifier)).toBeFalsy(); + return this; + } - beforeEach(() => { - game = new GameManager(phaserGame); - game.override - .moveset([MoveId.SPLASH]) - .ability(AbilityId.BALL_FETCH) - .battleStyle("single") - .criticalHits(false) - .enemySpecies(SpeciesId.MAGIKARP) - .enemyAbility(AbilityId.BALL_FETCH) - .enemyMoveset(MoveId.SPLASH); - }); + /** Removes all modifier checks. @returns `this` */ + clearChecks() { + itemPoolChecks.clear(); + return this; + } - it("Zacian should successfully change into Crowned form", async () => { - await game.classicMode.startBattle([SpeciesId.ZACIAN]); - - // Before the form change: Should be Hero form - const zacian = game.scene.getPlayerParty()[0]; - expect(zacian.getFormKey()).toBe("hero-of-many-battles"); - expect(zacian.getTypes()).toStrictEqual([PokemonType.FAIRY]); - expect(zacian.calculateBaseStats()).toStrictEqual([92, 120, 115, 80, 115, 138]); - - // Give Zacian a Rusted Sword - const rustedSwordType = generateModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM)!; - const rustedSword = rustedSwordType.newModifier(zacian); - await game.scene.addModifier(rustedSword); - - game.move.select(MoveId.SPLASH); - await game.toNextTurn(); - - // After the form change: Should be Crowned form - expect(game.phaseInterceptor.log.includes("FormChangePhase")).toBe(true); - expect(zacian.getFormKey()).toBe("crowned"); - expect(zacian.getTypes()).toStrictEqual([PokemonType.FAIRY, PokemonType.STEEL]); - expect(zacian.calculateBaseStats()).toStrictEqual([92, 150, 115, 80, 115, 148]); - }); -}); + private log(...params: any[]) { + console.log("Modifiers:", ...params); + } +} \ No newline at end of file diff --git a/test/phases/game-over-phase.test.ts b/test/phases/game-over-phase.test.ts index 81c00294d90..2baf1939b29 100644 --- a/test/phases/game-over-phase.test.ts +++ b/test/phases/game-over-phase.test.ts @@ -36,7 +36,7 @@ describe("Game Over Phase", () => { .startingLevel(10000); }); - it("winning a run should give rewards", async () => { + it("winning a run should give allRewards", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); vi.spyOn(game.scene, "validateAchv"); @@ -59,7 +59,7 @@ describe("Game Over Phase", () => { expect(game.scene.gameData.achvUnlocks[achvs.CLASSIC_VICTORY.id]).toBeTruthy(); }); - it("losing a run should not give rewards", async () => { + it("losing a run should not give allRewards", async () => { await game.classicMode.startBattle([SpeciesId.BULBASAUR]); vi.spyOn(game.scene, "validateAchv"); diff --git a/test/phases/select-modifier-phase.test.ts b/test/phases/select-modifier-phase.test.ts deleted file mode 100644 index 3f3ff81472f..00000000000 --- a/test/phases/select-modifier-phase.test.ts +++ /dev/null @@ -1,279 +0,0 @@ -import type { BattleScene } from "#app/battle-scene"; -import { modifierTypes } from "#data/data-lists"; -import { AbilityId } from "#enums/ability-id"; -import { Button } from "#enums/buttons"; -import { MoveId } from "#enums/move-id"; -import { RewardTier } from "#enums/reward-tier"; -import { SpeciesId } from "#enums/species-id"; -import { TrainerItemId } from "#enums/trainer-item-id"; -import { UiMode } from "#enums/ui-mode"; -import { PlayerPokemon } from "#field/pokemon"; -import type { CustomModifierSettings } from "#modifiers/modifier-type"; -import { ModifierTypeOption } from "#modifiers/modifier-type"; -import { SelectRewardPhase } from "#phases/select-reward-phase"; -import { GameManager } from "#test/test-utils/game-manager"; -import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils"; -import { RewardSelectUiHandler } from "#ui/reward-select-ui-handler"; -import { shiftCharCodes } from "#utils/common"; -import { getPokemonSpecies } from "#utils/pokemon-utils"; -import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; - -describe("SelectRewardPhase", () => { - let phaserGame: Phaser.Game; - let game: GameManager; - let scene: BattleScene; - - beforeAll(() => { - phaserGame = new Phaser.Game({ - type: Phaser.HEADLESS, - }); - }); - - beforeEach(() => { - game = new GameManager(phaserGame); - scene = game.scene; - - game.override - .moveset([MoveId.FISSURE, MoveId.SPLASH]) - .ability(AbilityId.NO_GUARD) - .startingLevel(200) - .enemySpecies(SpeciesId.MAGIKARP); - }); - - afterEach(() => { - game.phaseInterceptor.restoreOg(); - }); - - it("should start a select modifier phase", async () => { - initSceneWithoutEncounterPhase(scene, [SpeciesId.ABRA, SpeciesId.VOLCARONA]); - const selectModifierPhase = new SelectRewardPhase(); - scene.phaseManager.unshiftPhase(selectModifierPhase); - await game.phaseInterceptor.to(SelectRewardPhase); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - }); - - it("should generate random modifiers", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - game.move.select(MoveId.FISSURE); - await game.phaseInterceptor.to("SelectRewardPhase"); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - }); - - it("should modify reroll cost", async () => { - initSceneWithoutEncounterPhase(scene, [SpeciesId.ABRA, SpeciesId.VOLCARONA]); - const options = [ - new ModifierTypeOption(modifierTypes.POTION(), 0, 100), - new ModifierTypeOption(modifierTypes.ETHER(), 0, 400), - new ModifierTypeOption(modifierTypes.REVIVE(), 0, 1000), - ]; - - const selectModifierPhase1 = new SelectRewardPhase(0, undefined, { - guaranteedModifierTypeOptions: options, - }); - const selectModifierPhase2 = new SelectRewardPhase(0, undefined, { - guaranteedModifierTypeOptions: options, - rerollMultiplier: 2, - }); - - const cost1 = selectModifierPhase1.getRerollCost(false); - const cost2 = selectModifierPhase2.getRerollCost(false); - expect(cost2).toEqual(cost1 * 2); - }); - - it.todo("should generate random modifiers from reroll", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - scene.shopCursorTarget = 0; - - game.move.select(MoveId.FISSURE); - await game.phaseInterceptor.to("SelectRewardPhase"); - - // TODO: nagivate the ui to reroll somehow - //const smphase = scene.phaseManager.getCurrentPhase() as SelectRewardPhase; - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - - modifierSelectHandler.processInput(Button.ACTION); - - expect(scene.money).toBe(1000000 - 250); - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - expect(modifierSelectHandler.options.length).toEqual(3); - }); - - it.todo("should generate random modifiers of same tier for reroll with reroll lock", async () => { - game.override.startingTrainerItems([{ entry: TrainerItemId.LOCK_CAPSULE }]); - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - // Just use fully random seed for this test - vi.spyOn(scene, "resetSeed").mockImplementation(() => { - scene.waveSeed = shiftCharCodes(scene.seed, 5); - Phaser.Math.RND.sow([scene.waveSeed]); - console.log("Wave Seed:", scene.waveSeed, 5); - scene.rngCounter = 0; - }); - - game.move.select(MoveId.FISSURE); - await game.phaseInterceptor.to("SelectRewardPhase"); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - const firstRollTiers: RewardTier[] = modifierSelectHandler.options.map(o => o.modifierTypeOption.type.tier); - - // TODO: nagivate ui to reroll with lock capsule enabled - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - expect(modifierSelectHandler.options.length).toEqual(3); - // Reroll with lock can still upgrade - expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toEqual(firstRollTiers[0]); - expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toEqual(firstRollTiers[1]); - expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toEqual(firstRollTiers[2]); - }); - - it("should generate custom modifiers", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - const customModifiers: CustomModifierSettings = { - guaranteedModifierTypeFuncs: [ - modifierTypes.MEMORY_MUSHROOM, - modifierTypes.TM_ULTRA, - modifierTypes.LEFTOVERS, - modifierTypes.AMULET_COIN, - modifierTypes.GOLDEN_PUNCH, - ], - }; - const selectModifierPhase = new SelectRewardPhase(0, undefined, customModifiers); - scene.phaseManager.unshiftPhase(selectModifierPhase); - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to("SelectRewardPhase"); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("TM_ULTRA"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.id).toEqual("LEFTOVERS"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.id).toEqual("AMULET_COIN"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.id).toEqual("GOLDEN_PUNCH"); - }); - - it("should generate custom modifier tiers that can upgrade from luck", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - const customModifiers: CustomModifierSettings = { - guaranteedModifierTiers: [ - RewardTier.COMMON, - RewardTier.GREAT, - RewardTier.ULTRA, - RewardTier.ROGUE, - RewardTier.MASTER, - ], - }; - const pokemon = new PlayerPokemon(getPokemonSpecies(SpeciesId.BULBASAUR), 10, undefined, 0, undefined, true, 2); - - // Fill party with max shinies - while (scene.getPlayerParty().length > 0) { - scene.getPlayerParty().pop(); - } - scene.getPlayerParty().push(pokemon, pokemon, pokemon, pokemon, pokemon, pokemon); - - const selectModifierPhase = new SelectRewardPhase(0, undefined, customModifiers); - scene.phaseManager.unshiftPhase(selectModifierPhase); - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.to("SelectRewardPhase"); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(5); - expect( - modifierSelectHandler.options[0].modifierTypeOption.type.tier - - modifierSelectHandler.options[0].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.COMMON); - expect( - modifierSelectHandler.options[1].modifierTypeOption.type.tier - - modifierSelectHandler.options[1].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.GREAT); - expect( - modifierSelectHandler.options[2].modifierTypeOption.type.tier - - modifierSelectHandler.options[2].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ULTRA); - expect( - modifierSelectHandler.options[3].modifierTypeOption.type.tier - - modifierSelectHandler.options[3].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.ROGUE); - expect( - modifierSelectHandler.options[4].modifierTypeOption.type.tier - - modifierSelectHandler.options[4].modifierTypeOption.upgradeCount, - ).toEqual(RewardTier.MASTER); - }); - - it("should generate custom modifiers and modifier tiers together", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - const customModifiers: CustomModifierSettings = { - guaranteedModifierTypeFuncs: [modifierTypes.MEMORY_MUSHROOM, modifierTypes.TM_COMMON], - guaranteedModifierTiers: [RewardTier.MASTER, RewardTier.MASTER], - }; - const selectModifierPhase = new SelectRewardPhase(0, undefined, customModifiers); - scene.phaseManager.unshiftPhase(selectModifierPhase); - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.run(SelectRewardPhase); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(4); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.id).toEqual("TM_COMMON"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.tier).toEqual(RewardTier.MASTER); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.tier).toEqual(RewardTier.MASTER); - }); - - it("should fill remaining modifiers if fillRemaining is true with custom modifiers", async () => { - await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); - scene.money = 1000000; - const customModifiers: CustomModifierSettings = { - guaranteedModifierTypeFuncs: [modifierTypes.MEMORY_MUSHROOM], - guaranteedModifierTiers: [RewardTier.MASTER], - fillRemaining: true, - }; - const selectModifierPhase = new SelectRewardPhase(0, undefined, customModifiers); - scene.phaseManager.unshiftPhase(selectModifierPhase); - game.move.select(MoveId.SPLASH); - await game.phaseInterceptor.run(SelectRewardPhase); - - expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); - const modifierSelectHandler = scene.ui.handlers.find( - h => h instanceof RewardSelectUiHandler, - ) as RewardSelectUiHandler; - expect(modifierSelectHandler.options.length).toEqual(3); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.id).toEqual("MEMORY_MUSHROOM"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.tier).toEqual(RewardTier.MASTER); - }); -}); diff --git a/test/phases/select-reward-phase.test.ts b/test/phases/select-reward-phase.test.ts new file mode 100644 index 00000000000..ed664cf1aff --- /dev/null +++ b/test/phases/select-reward-phase.test.ts @@ -0,0 +1,281 @@ +import type { BattleScene } from "#app/battle-scene"; +import { allRewards } from "#data/data-lists"; +import { AbilityId } from "#enums/ability-id"; +import { Button } from "#enums/buttons"; +import { HeldItemId } from "#enums/held-item-id"; +import { MoveId } from "#enums/move-id"; +import { RewardId } from "#enums/reward-id"; +import { RarityTier } from "#enums/reward-tier"; +import { SpeciesId } from "#enums/species-id"; +import { TrainerItemId } from "#enums/trainer-item-id"; +import { UiMode } from "#enums/ui-mode"; +import { PlayerPokemon } from "#field/pokemon"; +import type { HeldItemReward, TrainerItemReward } from "#items/reward"; +import { RewardOption } from "#items/reward"; +import type { CustomRewardSettings } from "#items/reward-pool-utils"; +import { SelectRewardPhase } from "#phases/select-reward-phase"; +import { GameManager } from "#test/test-utils/game-manager"; +import { initSceneWithoutEncounterPhase } from "#test/test-utils/game-manager-utils"; +import { RewardSelectUiHandler } from "#ui/reward-select-ui-handler"; +import { shiftCharCodes } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("SelectRewardPhase", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let scene: BattleScene; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + scene = game.scene; + + game.override + .moveset([MoveId.FISSURE, MoveId.SPLASH]) + .ability(AbilityId.NO_GUARD) + .startingLevel(200) + .enemySpecies(SpeciesId.MAGIKARP); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + it("should start a select modifier phase", async () => { + initSceneWithoutEncounterPhase(scene, [SpeciesId.ABRA, SpeciesId.VOLCARONA]); + const selectRewardPhase = new SelectRewardPhase(); + scene.phaseManager.unshiftPhase(selectRewardPhase); + await game.phaseInterceptor.to(SelectRewardPhase); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + }); + + it("should generate random modifiers", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + game.move.select(MoveId.FISSURE); + await game.phaseInterceptor.to("SelectRewardPhase"); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(3); + }); + + it("should modify reroll cost", async () => { + initSceneWithoutEncounterPhase(scene, [SpeciesId.ABRA, SpeciesId.VOLCARONA]); + const options = [ + new RewardOption(allRewards.POTION(), 0, RarityTier.COMMON, 100), + new RewardOption(allRewards.ETHER(), 0, RarityTier.COMMON, 400), + new RewardOption(allRewards.REVIVE(), 0, RarityTier.COMMON, 1000), + ]; + + const selectRewardPhase1 = new SelectRewardPhase(0, undefined, { + guaranteedRewardOptions: options, + }); + const selectRewardPhase2 = new SelectRewardPhase(0, undefined, { + guaranteedRewardOptions: options, + rerollMultiplier: 2, + }); + + const cost1 = selectRewardPhase1.getRerollCost(false); + const cost2 = selectRewardPhase2.getRerollCost(false); + expect(cost2).toEqual(cost1 * 2); + }); + + it.todo("should generate random modifiers from reroll", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + scene.shopCursorTarget = 0; + + game.move.select(MoveId.FISSURE); + await game.phaseInterceptor.to("SelectRewardPhase"); + + // TODO: nagivate the ui to reroll somehow + //const smphase = scene.phaseManager.getCurrentPhase() as SelectRewardPhase; + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(3); + + rewardSelectHandler.processInput(Button.ACTION); + + expect(scene.money).toBe(1000000 - 250); + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + expect(rewardSelectHandler.options.length).toEqual(3); + }); + + it.todo("should generate random modifiers of same tier for reroll with reroll lock", async () => { + game.override.startingTrainerItems([{ entry: TrainerItemId.LOCK_CAPSULE }]); + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + // Just use fully random seed for this test + vi.spyOn(scene, "resetSeed").mockImplementation(() => { + scene.waveSeed = shiftCharCodes(scene.seed, 5); + Phaser.Math.RND.sow([scene.waveSeed]); + console.log("Wave Seed:", scene.waveSeed, 5); + scene.rngCounter = 0; + }); + + game.move.select(MoveId.FISSURE); + await game.phaseInterceptor.to("SelectRewardPhase"); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(3); + const firstRollTiers: RarityTier[] = rewardSelectHandler.options.map(o => o.rewardOption.tier); + + // TODO: nagivate ui to reroll with lock capsule enabled + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + expect(rewardSelectHandler.options.length).toEqual(3); + // Reroll with lock can still upgrade + expect( + rewardSelectHandler.options[0].rewardOption.tier - rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toEqual(firstRollTiers[0]); + expect( + rewardSelectHandler.options[1].rewardOption.tier - rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toEqual(firstRollTiers[1]); + expect( + rewardSelectHandler.options[2].rewardOption.tier - rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toEqual(firstRollTiers[2]); + }); + + it("should generate custom modifiers", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + const customRewards: CustomRewardSettings = { + guaranteedRewardFuncs: [ + allRewards.MEMORY_MUSHROOM, + allRewards.TM_ULTRA, + allRewards.LEFTOVERS, + allRewards.AMULET_COIN, + allRewards.GOLDEN_PUNCH, + ], + }; + const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards); + scene.phaseManager.unshiftPhase(selectRewardPhase); + game.move.select(MoveId.SPLASH); + await game.phaseInterceptor.to("SelectRewardPhase"); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(5); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual(RewardId.MEMORY_MUSHROOM); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toEqual(RewardId.TM_ULTRA); + expect(rewardSelectHandler.options[2].rewardOption.type.id).toEqual(RewardId.HELD_ITEM); + expect((rewardSelectHandler.options[2].rewardOption.type as HeldItemReward).itemId).toEqual(HeldItemId.LEFTOVERS); + expect(rewardSelectHandler.options[3].rewardOption.type.id).toEqual(RewardId.TRAINER_ITEM); + expect((rewardSelectHandler.options[3].rewardOption.type as TrainerItemReward).itemId).toEqual( + TrainerItemId.AMULET_COIN, + ); + expect(rewardSelectHandler.options[4].rewardOption.type.id).toEqual(RewardId.HELD_ITEM); + expect((rewardSelectHandler.options[4].rewardOption.type as HeldItemReward).itemId).toEqual( + HeldItemId.GOLDEN_PUNCH, + ); + }); + + it("should generate custom modifier tiers that can upgrade from luck", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + const customRewards: CustomRewardSettings = { + guaranteedRarityTiers: [ + RarityTier.COMMON, + RarityTier.GREAT, + RarityTier.ULTRA, + RarityTier.ROGUE, + RarityTier.MASTER, + ], + }; + const pokemon = new PlayerPokemon(getPokemonSpecies(SpeciesId.BULBASAUR), 10, undefined, 0, undefined, true, 2); + + // Fill party with max shinies + while (scene.getPlayerParty().length > 0) { + scene.getPlayerParty().pop(); + } + scene.getPlayerParty().push(pokemon, pokemon, pokemon, pokemon, pokemon, pokemon); + + const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards); + scene.phaseManager.unshiftPhase(selectRewardPhase); + game.move.select(MoveId.SPLASH); + await game.phaseInterceptor.to("SelectRewardPhase"); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(5); + expect( + rewardSelectHandler.options[0].rewardOption.tier - rewardSelectHandler.options[0].rewardOption.upgradeCount, + ).toEqual(RarityTier.COMMON); + expect( + rewardSelectHandler.options[1].rewardOption.tier - rewardSelectHandler.options[1].rewardOption.upgradeCount, + ).toEqual(RarityTier.GREAT); + expect( + rewardSelectHandler.options[2].rewardOption.tier - rewardSelectHandler.options[2].rewardOption.upgradeCount, + ).toEqual(RarityTier.ULTRA); + expect( + rewardSelectHandler.options[3].rewardOption.tier - rewardSelectHandler.options[3].rewardOption.upgradeCount, + ).toEqual(RarityTier.ROGUE); + expect( + rewardSelectHandler.options[4].rewardOption.tier - rewardSelectHandler.options[4].rewardOption.upgradeCount, + ).toEqual(RarityTier.MASTER); + }); + + it("should generate custom modifiers and modifier tiers together", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + const customRewards: CustomRewardSettings = { + guaranteedRewardFuncs: [allRewards.MEMORY_MUSHROOM, allRewards.TM_COMMON], + guaranteedRarityTiers: [RarityTier.MASTER, RarityTier.MASTER], + }; + const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards); + scene.phaseManager.unshiftPhase(selectRewardPhase); + game.move.select(MoveId.SPLASH); + await game.phaseInterceptor.run(SelectRewardPhase); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(4); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual(RewardId.MEMORY_MUSHROOM); + expect(rewardSelectHandler.options[1].rewardOption.type.id).toEqual(RewardId.TM_COMMON); + expect(rewardSelectHandler.options[2].rewardOption.tier).toEqual(RarityTier.MASTER); + expect(rewardSelectHandler.options[3].rewardOption.tier).toEqual(RarityTier.MASTER); + }); + + it("should fill remaining modifiers if fillRemaining is true with custom modifiers", async () => { + await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); + scene.money = 1000000; + const customRewards: CustomRewardSettings = { + guaranteedRewardFuncs: [allRewards.MEMORY_MUSHROOM], + guaranteedRarityTiers: [RarityTier.MASTER], + fillRemaining: true, + }; + const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards); + scene.phaseManager.unshiftPhase(selectRewardPhase); + game.move.select(MoveId.SPLASH); + await game.phaseInterceptor.run(SelectRewardPhase); + + expect(scene.ui.getMode()).to.equal(UiMode.REWARD_SELECT); + const rewardSelectHandler = scene.ui.handlers.find( + h => h instanceof RewardSelectUiHandler, + ) as RewardSelectUiHandler; + expect(rewardSelectHandler.options.length).toEqual(3); + expect(rewardSelectHandler.options[0].rewardOption.type.id).toEqual(RewardId.MEMORY_MUSHROOM); + expect(rewardSelectHandler.options[1].rewardOption.tier).toEqual(RarityTier.MASTER); + }); +}); diff --git a/test/test-utils/game-manager.ts b/test/test-utils/game-manager.ts index 40ced8495d2..01e11d1f69f 100644 --- a/test/test-utils/game-manager.ts +++ b/test/test-utils/game-manager.ts @@ -3,7 +3,7 @@ import { BattleScene } from "#app/battle-scene"; import { getGameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene"; import overrides from "#app/overrides"; -import { modifierTypes } from "#data/data-lists"; +import { allRewards } from "#data/data-lists"; import { BattlerIndex } from "#enums/battler-index"; import { Button } from "#enums/buttons"; import { ExpGainsSpeed } from "#enums/exp-gains-speed"; @@ -16,7 +16,6 @@ import type { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; import type { EnemyPokemon, PlayerPokemon } from "#field/pokemon"; import { Trainer } from "#field/trainer"; -import { ModifierTypeOption } from "#modifiers/modifier-type"; import { CheckSwitchPhase } from "#phases/check-switch-phase"; import { CommandPhase } from "#phases/command-phase"; import { EncounterPhase } from "#phases/encounter-phase"; @@ -315,7 +314,7 @@ export class GameManager { doSelectModifier() { this.onNextPrompt( "SelectRewardPhase", - UiMode.MODIFIER_SELECT, + UiMode.REWARD_SELECT, () => { const handler = this.scene.ui.getHandler() as RewardSelectUiHandler; handler.processInput(Button.CANCEL); @@ -481,9 +480,8 @@ export class GameManager { */ doRevivePokemon(pokemonIndex: number) { const party = this.scene.getPlayerParty(); - const candidate = new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0); - const modifier = candidate.type!.newModifier(party[pokemonIndex]); - this.scene.addModifier(modifier, false); + const reward = allRewards.MAX_REVIVE(); + reward.apply({ pokemon: party[pokemonIndex] }); } /** diff --git a/test/test-utils/helpers/modifiers-helper.ts b/test/test-utils/helpers/modifiers-helper.ts index 14ae6bcb61c..df0bd74f8ef 100644 --- a/test/test-utils/helpers/modifiers-helper.ts +++ b/test/test-utils/helpers/modifiers-helper.ts @@ -1,5 +1,5 @@ -import type { ModifierTypeKeys } from "#modifiers/modifier-type"; -import { itemPoolChecks } from "#modifiers/modifier-type"; +import type { RewardKeys } from "#items/reward"; +import { itemPoolChecks } from "#items/reward"; import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"; import { expect } from "vitest"; @@ -11,7 +11,7 @@ export class ModifierHelper extends GameManagerHelper { * @param modifier The Modifier to add. * @returns `this` */ - addCheck(modifier: ModifierTypeKeys): this { + addCheck(modifier: RewardKeys): this { itemPoolChecks.set(modifier, undefined); return this; } @@ -27,7 +27,7 @@ export class ModifierHelper extends GameManagerHelper { * @param modifier * @returns */ - getCheck(modifier: ModifierTypeKeys): boolean | undefined { + getCheck(modifier: RewardKeys): boolean | undefined { return itemPoolChecks.get(modifier); } @@ -39,7 +39,7 @@ export class ModifierHelper extends GameManagerHelper { * @param expectToBePreset Whether the Modifier should be in the Modifier Pool. Set to `false` to expect it to be absent instead. * @returns `this` */ - testCheck(modifier: ModifierTypeKeys, expectToBePreset: boolean): this { + testCheck(modifier: RewardKeys, expectToBePreset: boolean): this { if (expectToBePreset) { expect(itemPoolChecks.get(modifier)).toBeTruthy(); } @@ -56,4 +56,4 @@ export class ModifierHelper extends GameManagerHelper { private log(...params: any[]) { console.log("Modifiers:", ...params); } -} +} \ No newline at end of file diff --git a/test/test-utils/helpers/overrides-helper.ts b/test/test-utils/helpers/overrides-helper.ts index f3aa771d033..0d2d39a7b49 100644 --- a/test/test-utils/helpers/overrides-helper.ts +++ b/test/test-utils/helpers/overrides-helper.ts @@ -18,8 +18,8 @@ import { StatusEffect } from "#enums/status-effect"; import type { Unlockables } from "#enums/unlockables"; import { WeatherType } from "#enums/weather-type"; import type { HeldItemConfiguration } from "#items/held-item-data-types"; +import type { RewardOverride } from "#items/reward"; import type { TrainerItemConfiguration } from "#items/trainer-item-data-types"; -import type { ModifierOverride } from "#modifiers/modifier-type"; import type { Variant } from "#sprites/variant"; import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"; import { coerceArray, shiftCharCodes } from "#utils/common"; @@ -557,9 +557,9 @@ export class OverridesHelper extends GameManagerHelper { * @param items - The items to be rolled * @returns `this` */ - public itemRewards(items: ModifierOverride[]): this { - vi.spyOn(Overrides, "ITEM_REWARD_OVERRIDE", "get").mockReturnValue(items); - this.log("Item rewards set to:", items); + public itemRewards(items: RewardOverride[]): this { + vi.spyOn(Overrides, "REWARD_OVERRIDE", "get").mockReturnValue(items); + this.log("Item allRewards set to:", items); return this; } diff --git a/test/test-utils/test-file-initialization.ts b/test/test-utils/test-file-initialization.ts index 7979cc7243b..3dc107c653a 100644 --- a/test/test-utils/test-file-initialization.ts +++ b/test/test-utils/test-file-initialization.ts @@ -9,9 +9,9 @@ import { initSpecies } from "#data/pokemon-species"; import { initHeldItems } from "#items/all-held-items"; import { initTrainerItems } from "#items/all-trainer-items"; import { initHeldItemPools } from "#items/init-held-item-pools"; +import { initRewardPools } from "#items/init-reward-pools"; import { initTrainerItemPools } from "#items/init-trainer-item-pools"; -import { initModifierPools } from "#modifiers/init-modifier-pools"; -import { initModifierTypes } from "#modifiers/modifier-type"; +import { initRewards } from "#items/reward"; import { initMoves } from "#moves/move"; import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters"; import { initI18n } from "#plugins/i18n"; @@ -98,8 +98,8 @@ export function initTestFile() { initHeldItemPools(); initTrainerItems(); initTrainerItemPools(); - initModifierTypes(); - initModifierPools(); + initRewards(); + initRewardPools(); initVouchers(); initAchievements(); initStatsKeys(); diff --git a/test/ui/transfer-item.test.ts b/test/ui/transfer-item.test.ts index 368895ccf0b..5b67c38a609 100644 --- a/test/ui/transfer-item.test.ts +++ b/test/ui/transfer-item.test.ts @@ -43,14 +43,14 @@ describe("UI - Transfer Items", () => { game.move.select(MoveId.DRAGON_CLAW); - game.onNextPrompt("SelectRewardPhase", UiMode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectRewardPhase", UiMode.REWARD_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(RewardSelectUiHandler); const handler = game.scene.ui.getHandler() as RewardSelectUiHandler; handler.setCursor(1); handler.processInput(Button.ACTION); - void game.scene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.MODIFIER_TRANSFER); + void game.scene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.ITEM_TRANSFER); }); await game.phaseInterceptor.to("BattleEndPhase"); diff --git a/tsconfig.json b/tsconfig.json index c179ab22c8b..6e714ceaa79 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,6 @@ "#field/*": ["./field/*.ts"], "#inputs/*": ["./configs/inputs/*.ts"], "#items/*": ["./items/held-items/*.ts", "./items/*.ts"], - "#modifiers/*": ["./modifier/*.ts"], "#moves/*": ["./data/moves/*.ts"], "#mystery-encounters/*": [ "./data/mystery-encounters/utils/*.ts",