Replace remaining Modifiers with Rewards (#6091)

* Changing remaining Modifiers to Consumables, and renaming ModifierType to Reward

* Renamed modifier files and moved them into items folder

* Using rewards in most places

* Removed consumables in favor of using rewards directly

* Renamed RewardTier to RarityTier

* Reward ids, function to match rewards

* Getting reward tiers from player pool still

* Messing around with parameters of Reward.apply()

* Always requiring player pokemon in rewards

* Fixing some functions in select-reward-phase and battle-scene

* Fixed various post-merge issues

* Fixed most localization strings (accidentally broken by replacing modifierType with reward)

* Fixed tests for select reward phase

* Using Pokemon.hasSpecies()

* Zero weight for trainer items rewards which are already max stack

* Cleaning up SelectRewardPhase, held item rewards behave the same as any PokemonReward

* Cleaned up some functions

* Introduced RewardCategoryId, distributed RewardIds

* Utility `is` functions for rewards

* Minor fixes

* Moved `HeldItemEffect` to its own file

* rmade some todo comments

* Adding a big comment

* Added tsdocs and removed `RewardClass`

* undid breaking changes

* added TODO

* Moved matchingRewards function to reward-utils.ts

* Added RewardGenerator classes for mints and tera shards

* Introducing default rarity tiers for trainer items and rewards

* RewardFunc now can return RewardGenerator

* Moved pool reward functions to their own file, plus other utility files

* Fixed WeightedModifier to work with the new RewardFunc

* Fixed wrong type import

* Shifting trainer item and reward ids to avoid overlaps

* Added some types

* Updated comment in reward.ts

* Added strong typing ot item maps

* added type safety to held item name map

---------

Co-authored-by: Bertie690 <taylormw163@gmail.com>
Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
This commit is contained in:
Wlowscha 2025-07-28 02:09:21 +02:00 committed by GitHub
parent d3f2659cdf
commit 466c4aede2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
175 changed files with 4842 additions and 5112 deletions

View File

@ -183,7 +183,7 @@ input:-internal-autofill-selected {
/* Show #apadStats only in battle and shop */ /* Show #apadStats only in battle and shop */
#touchControls:not([data-ui-mode="COMMAND"]):not([data-ui-mode="FIGHT"]):not( #touchControls:not([data-ui-mode="COMMAND"]):not([data-ui-mode="FIGHT"]):not(
[data-ui-mode="BALL"] [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 { #apadStats {
display: none; display: none;
} }

View File

@ -24,15 +24,15 @@ export interface AbilityTranslationEntries {
[key: string]: AbilityTranslationEntry; [key: string]: AbilityTranslationEntry;
} }
export interface ModifierTypeTranslationEntry { export interface RewardTranslationEntry {
name?: string; name?: string;
description?: string; description?: string;
extra?: SimpleTranslationEntries; extra?: SimpleTranslationEntries;
} }
export interface ModifierTypeTranslationEntries { export interface RewardTranslationEntries {
ModifierType: { [key: string]: ModifierTypeTranslationEntry }; Reward: { [key: string]: RewardTranslationEntry };
SpeciesBoosterItem: { [key: string]: ModifierTypeTranslationEntry }; SpeciesBoosterItem: { [key: string]: RewardTranslationEntry };
AttackTypeBoosterItem: SimpleTranslationEntries; AttackTypeBoosterItem: SimpleTranslationEntries;
TempStatStageBoosterItem: SimpleTranslationEntries; TempStatStageBoosterItem: SimpleTranslationEntries;
BaseStatBoosterItem: SimpleTranslationEntries; BaseStatBoosterItem: SimpleTranslationEntries;

View File

@ -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<ModifierConstructorMap[K]>;
};
/**
* 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[];
};

23
src/@types/rewards.ts Normal file
View File

@ -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[];
}

View File

@ -53,8 +53,8 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { ExpNotification } from "#enums/exp-notification"; import { ExpNotification } from "#enums/exp-notification";
import { FormChangeItem } from "#enums/form-change-item"; import { FormChangeItem } from "#enums/form-change-item";
import { GameModes } from "#enums/game-modes"; import { GameModes } from "#enums/game-modes";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import { HeldItemPoolType, ModifierPoolType } from "#enums/modifier-pool-type";
import { MoneyFormat } from "#enums/money-format"; import { MoneyFormat } from "#enums/money-format";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
@ -65,6 +65,7 @@ import { PlayerGender } from "#enums/player-gender";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import type { PokemonAnimType } from "#enums/pokemon-anim-type"; import type { PokemonAnimType } from "#enums/pokemon-anim-type";
import { PokemonType } from "#enums/pokemon-type"; import { PokemonType } from "#enums/pokemon-type";
import { HeldItemPoolType, RewardPoolType } from "#enums/reward-pool-type";
import { ShopCursorTarget } from "#enums/shop-cursor-target"; import { ShopCursorTarget } from "#enums/shop-cursor-target";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
@ -82,9 +83,10 @@ import { PokemonSpriteSparkleHandler } from "#field/pokemon-sprite-sparkle-handl
import { Trainer } from "#field/trainer"; import { Trainer } from "#field/trainer";
import { applyHeldItems } from "#items/all-held-items"; import { applyHeldItems } from "#items/all-held-items";
import { type ApplyTrainerItemsParams, applyTrainerItems } from "#items/apply-trainer-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 type { HeldItemConfiguration } from "#items/held-item-data-types";
import { assignEnemyHeldItemsForWave, assignItemsFromConfiguration } from "#items/held-item-pool"; 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 { type EnemyAttackStatusEffectChanceTrainerItem, TrainerItemEffect } from "#items/trainer-item";
import { import {
isTrainerItemPool, isTrainerItemPool,
@ -94,15 +96,6 @@ import {
} from "#items/trainer-item-data-types"; } from "#items/trainer-item-data-types";
import { TrainerItemManager } from "#items/trainer-item-manager"; import { TrainerItemManager } from "#items/trainer-item-manager";
import { getNewTrainerItemFromPool } from "#items/trainer-item-pool"; 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 { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters"; 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 type { Variant } from "#sprites/variant";
import { clearVariantData, variantData } from "#sprites/variant"; import { clearVariantData, variantData } from "#sprites/variant";
import type { Achv } from "#system/achv"; 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 { GameData } from "#system/game-data";
import { initGameSpeed } from "#system/game-speed"; import { initGameSpeed } from "#system/game-speed";
import type { PokemonData } from "#system/pokemon-data"; import type { PokemonData } from "#system/pokemon-data";
@ -148,7 +141,7 @@ import {
} from "#utils/common"; } from "#utils/common";
import { deepMergeSpriteData } from "#utils/data"; import { deepMergeSpriteData } from "#utils/data";
import { getEnumValues } from "#utils/enums"; 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 { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next"; import i18next from "i18next";
import Phaser from "phaser"; import Phaser from "phaser";
@ -267,7 +260,7 @@ export class BattleScene extends SceneBase {
public arena: Arena; public arena: Arena;
public gameMode: GameMode; public gameMode: GameMode;
public score: number; public score: number;
public lockModifierTiers: boolean; public lockRarityTiers: boolean;
public trainer: Phaser.GameObjects.Sprite; public trainer: Phaser.GameObjects.Sprite;
public lastEnemyTrainer: Trainer | null; public lastEnemyTrainer: Trainer | null;
public currentBattle: Battle; public currentBattle: Battle;
@ -480,12 +473,12 @@ export class BattleScene extends SceneBase {
this.enemyTrainerItems = new TrainerItemManager(); this.enemyTrainerItems = new TrainerItemManager();
this.itemBar = new ItemBar(); this.itemBar = new ItemBar();
this.itemBar.setName("modifier-bar"); this.itemBar.setName("item-bar");
this.add.existing(this.itemBar); this.add.existing(this.itemBar);
uiContainer.add(this.itemBar); uiContainer.add(this.itemBar);
this.enemyItemBar = new ItemBar(true); this.enemyItemBar = new ItemBar(true);
this.enemyItemBar.setName("enemy-modifier-bar"); this.enemyItemBar.setName("enemy-item-bar");
this.add.existing(this.enemyItemBar); this.add.existing(this.enemyItemBar);
uiContainer.add(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` * @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 { getItemBar(isEnemy = false): ItemBar {
return isEnemy ? this.enemyItemBar : this.itemBar; return isEnemy ? this.enemyItemBar : this.itemBar;
@ -1167,7 +1160,7 @@ export class BattleScene extends SceneBase {
this.score = 0; this.score = 0;
this.money = 0; this.money = 0;
this.lockModifierTiers = false; this.lockRarityTiers = false;
this.pokeballCounts = Object.fromEntries( this.pokeballCounts = Object.fromEntries(
getEnumValues(PokeballType) getEnumValues(PokeballType)
@ -1243,12 +1236,12 @@ export class BattleScene extends SceneBase {
...allSpecies, ...allSpecies,
...allMoves, ...allMoves,
...allAbilities, ...allAbilities,
...getEnumValues(ModifierPoolType) ...getEnumValues(RewardPoolType)
.map(mpt => getModifierPoolForType(mpt)) .map(mpt => getRewardPoolForType(mpt))
.flatMap(mp => .flatMap(mp =>
Object.values(mp) Object.values(mp)
.flat() .flat()
.map(mt => mt.modifierType) .map(mt => mt.reward)
.filter(mt => "localize" in mt) .filter(mt => "localize" in mt)
.map(lpb => lpb as unknown as Localizable), .map(lpb => lpb as unknown as Localizable),
), ),
@ -1987,11 +1980,11 @@ export class BattleScene extends SceneBase {
}); });
} }
showEnemyModifierBar(): void { showEnemyItemBar(): void {
this.enemyItemBar.setVisible(true); this.enemyItemBar.setVisible(true);
} }
hideEnemyModifierBar(): void { hideEnemyItemBar(): void {
this.enemyItemBar.setVisible(false); this.enemyItemBar.setVisible(false);
} }
@ -2085,11 +2078,11 @@ export class BattleScene extends SceneBase {
} }
updateUIPositions(): void { updateUIPositions(): void {
const enemyModifierCount = this.enemyItemBar.totalVisibleLength; const enemyItemCount = this.enemyItemBar.totalVisibleLength;
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y; const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY( this.biomeWaveText.setY(
-(this.game.canvas.height / 6) + -(this.game.canvas.height / 6) +
(enemyModifierCount ? (enemyModifierCount <= 12 ? 15 : 24) : 0) + (enemyItemCount ? (enemyItemCount <= 12 ? 15 : 24) : 0) +
biomeWaveTextHeight / 2, biomeWaveTextHeight / 2,
); );
this.moneyText.setY(this.biomeWaveText.y + 10); this.moneyText.setY(this.biomeWaveText.y + 10);
@ -2641,56 +2634,19 @@ export class BattleScene extends SceneBase {
applyTrainerItems(effect, this.trainerItems, params); applyTrainerItems(effect, this.trainerItems, params);
} }
addModifier(modifier: Modifier | null, playSound?: boolean, instant?: boolean, cost?: number): boolean { applyReward<T extends Reward>(reward: T, params: Parameters<T["apply"]>[0], playSound?: boolean): 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 const soundName = reward.soundName;
// 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 (playSound && !this.sound.get(soundName)) {
if (!modifier || !modifier.type) { this.playSound(soundName);
}
if (!reward.shouldApply(params)) {
return false; 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) { reward.apply(params);
for (const p in this.party) { return true;
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;
} }
addHeldItem(heldItemId: HeldItemId, pokemon: Pokemon, amount = 1, playSound?: boolean, ignoreUpdate?: boolean) { addHeldItem(heldItemId: HeldItemId, pokemon: Pokemon, amount = 1, playSound?: boolean, ignoreUpdate?: boolean) {
@ -2864,7 +2820,7 @@ export class BattleScene extends SceneBase {
} }
let count = 0; let count = 0;
for (let c = 0; c < chances; c++) { for (let c = 0; c < chances; c++) {
if (!randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) { if (!randSeedInt(this.gameMode.getEnemyItemChance(isBoss))) {
count++; 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 { clearEnemyItems(): void {
this.enemyTrainerItems.clearItems(); this.enemyTrainerItems.clearItems();
@ -2911,7 +2867,7 @@ export class BattleScene extends SceneBase {
this.updateUIPositions(); this.updateUIPositions();
} }
setModifiersVisible(visible: boolean) { setItemsVisible(visible: boolean) {
[this.itemBar, this.enemyItemBar].map(m => m.setVisible(visible)); [this.itemBar, this.enemyItemBar].map(m => m.setVisible(visible));
} }

View File

@ -10,15 +10,14 @@ import type { MoveId } from "#enums/move-id";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type";
import type { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { RewardTier } from "#enums/reward-tier";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant"; import { TrainerVariant } from "#enums/trainer-variant";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer"; import { Trainer } from "#field/trainer";
import type { CustomRewardSettings } from "#items/reward-pool-utils";
import { TrainerItemEffect } from "#items/trainer-item"; import { TrainerItemEffect } from "#items/trainer-item";
import type { CustomModifierSettings } from "#modifiers/modifier-type";
import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import i18next from "#plugins/i18n"; import i18next from "#plugins/i18n";
import { MusicPreference } from "#system/settings"; import { MusicPreference } from "#system/settings";
@ -481,7 +480,7 @@ export class FixedBattleConfig {
public getTrainer: GetTrainerFunc; public getTrainer: GetTrainerFunc;
public getEnemyParty: GetEnemyPartyFunc; public getEnemyParty: GetEnemyPartyFunc;
public seedOffsetWaveIndex: number; public seedOffsetWaveIndex: number;
public customModifierRewardSettings?: CustomModifierSettings; public customRewardSettings?: CustomRewardSettings;
setBattleType(battleType: BattleType): FixedBattleConfig { setBattleType(battleType: BattleType): FixedBattleConfig {
this.battleType = battleType; this.battleType = battleType;
@ -508,8 +507,8 @@ export class FixedBattleConfig {
return this; return this;
} }
setCustomModifierRewards(customModifierRewardSettings: CustomModifierSettings) { setCustomRewards(customRewardSettings: CustomRewardSettings) {
this.customModifierRewardSettings = customModifierRewardSettings; this.customRewardSettings = customRewardSettings;
return this; return this;
} }
} }

View File

@ -1,4 +1,4 @@
import { RewardTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
@ -68591,324 +68591,324 @@ function transposeTmSpecies(): SpeciesTmMoves {
export const speciesTmMoves: SpeciesTmMoves = transposeTmSpecies(); export const speciesTmMoves: SpeciesTmMoves = transposeTmSpecies();
interface TmPoolTiers { interface TmPoolTiers {
[key: number]: RewardTier [key: number]: RarityTier
} }
export const tmPoolTiers: TmPoolTiers = { export const tmPoolTiers: TmPoolTiers = {
[MoveId.MEGA_PUNCH]: RewardTier.GREAT, [MoveId.MEGA_PUNCH]: RarityTier.GREAT,
[MoveId.PAY_DAY]: RewardTier.ULTRA, [MoveId.PAY_DAY]: RarityTier.ULTRA,
[MoveId.FIRE_PUNCH]: RewardTier.GREAT, [MoveId.FIRE_PUNCH]: RarityTier.GREAT,
[MoveId.ICE_PUNCH]: RewardTier.GREAT, [MoveId.ICE_PUNCH]: RarityTier.GREAT,
[MoveId.THUNDER_PUNCH]: RewardTier.GREAT, [MoveId.THUNDER_PUNCH]: RarityTier.GREAT,
[MoveId.SWORDS_DANCE]: RewardTier.COMMON, [MoveId.SWORDS_DANCE]: RarityTier.COMMON,
[MoveId.CUT]: RewardTier.COMMON, [MoveId.CUT]: RarityTier.COMMON,
[MoveId.FLY]: RewardTier.COMMON, [MoveId.FLY]: RarityTier.COMMON,
[MoveId.MEGA_KICK]: RewardTier.GREAT, [MoveId.MEGA_KICK]: RarityTier.GREAT,
[MoveId.BODY_SLAM]: RewardTier.GREAT, [MoveId.BODY_SLAM]: RarityTier.GREAT,
[MoveId.TAKE_DOWN]: RewardTier.GREAT, [MoveId.TAKE_DOWN]: RarityTier.GREAT,
[MoveId.DOUBLE_EDGE]: RewardTier.ULTRA, [MoveId.DOUBLE_EDGE]: RarityTier.ULTRA,
[MoveId.PIN_MISSILE]: RewardTier.COMMON, [MoveId.PIN_MISSILE]: RarityTier.COMMON,
[MoveId.ROAR]: RewardTier.COMMON, [MoveId.ROAR]: RarityTier.COMMON,
[MoveId.FLAMETHROWER]: RewardTier.ULTRA, [MoveId.FLAMETHROWER]: RarityTier.ULTRA,
[MoveId.HYDRO_PUMP]: RewardTier.ULTRA, [MoveId.HYDRO_PUMP]: RarityTier.ULTRA,
[MoveId.SURF]: RewardTier.ULTRA, [MoveId.SURF]: RarityTier.ULTRA,
[MoveId.ICE_BEAM]: RewardTier.ULTRA, [MoveId.ICE_BEAM]: RarityTier.ULTRA,
[MoveId.BLIZZARD]: RewardTier.ULTRA, [MoveId.BLIZZARD]: RarityTier.ULTRA,
[MoveId.PSYBEAM]: RewardTier.GREAT, [MoveId.PSYBEAM]: RarityTier.GREAT,
[MoveId.HYPER_BEAM]: RewardTier.ULTRA, [MoveId.HYPER_BEAM]: RarityTier.ULTRA,
[MoveId.LOW_KICK]: RewardTier.COMMON, [MoveId.LOW_KICK]: RarityTier.COMMON,
[MoveId.COUNTER]: RewardTier.COMMON, [MoveId.COUNTER]: RarityTier.COMMON,
[MoveId.STRENGTH]: RewardTier.GREAT, [MoveId.STRENGTH]: RarityTier.GREAT,
[MoveId.SOLAR_BEAM]: RewardTier.ULTRA, [MoveId.SOLAR_BEAM]: RarityTier.ULTRA,
[MoveId.FIRE_SPIN]: RewardTier.COMMON, [MoveId.FIRE_SPIN]: RarityTier.COMMON,
[MoveId.THUNDERBOLT]: RewardTier.ULTRA, [MoveId.THUNDERBOLT]: RarityTier.ULTRA,
[MoveId.THUNDER_WAVE]: RewardTier.COMMON, [MoveId.THUNDER_WAVE]: RarityTier.COMMON,
[MoveId.THUNDER]: RewardTier.ULTRA, [MoveId.THUNDER]: RarityTier.ULTRA,
[MoveId.EARTHQUAKE]: RewardTier.ULTRA, [MoveId.EARTHQUAKE]: RarityTier.ULTRA,
[MoveId.DIG]: RewardTier.GREAT, [MoveId.DIG]: RarityTier.GREAT,
[MoveId.TOXIC]: RewardTier.GREAT, [MoveId.TOXIC]: RarityTier.GREAT,
[MoveId.PSYCHIC]: RewardTier.ULTRA, [MoveId.PSYCHIC]: RarityTier.ULTRA,
[MoveId.AGILITY]: RewardTier.COMMON, [MoveId.AGILITY]: RarityTier.COMMON,
[MoveId.NIGHT_SHADE]: RewardTier.COMMON, [MoveId.NIGHT_SHADE]: RarityTier.COMMON,
[MoveId.SCREECH]: RewardTier.COMMON, [MoveId.SCREECH]: RarityTier.COMMON,
[MoveId.DOUBLE_TEAM]: RewardTier.COMMON, [MoveId.DOUBLE_TEAM]: RarityTier.COMMON,
[MoveId.CONFUSE_RAY]: RewardTier.COMMON, [MoveId.CONFUSE_RAY]: RarityTier.COMMON,
[MoveId.LIGHT_SCREEN]: RewardTier.COMMON, [MoveId.LIGHT_SCREEN]: RarityTier.COMMON,
[MoveId.HAZE]: RewardTier.COMMON, [MoveId.HAZE]: RarityTier.COMMON,
[MoveId.REFLECT]: RewardTier.COMMON, [MoveId.REFLECT]: RarityTier.COMMON,
[MoveId.FOCUS_ENERGY]: RewardTier.COMMON, [MoveId.FOCUS_ENERGY]: RarityTier.COMMON,
[MoveId.METRONOME]: RewardTier.COMMON, [MoveId.METRONOME]: RarityTier.COMMON,
[MoveId.SELF_DESTRUCT]: RewardTier.GREAT, [MoveId.SELF_DESTRUCT]: RarityTier.GREAT,
[MoveId.FIRE_BLAST]: RewardTier.ULTRA, [MoveId.FIRE_BLAST]: RarityTier.ULTRA,
[MoveId.WATERFALL]: RewardTier.GREAT, [MoveId.WATERFALL]: RarityTier.GREAT,
[MoveId.SWIFT]: RewardTier.COMMON, [MoveId.SWIFT]: RarityTier.COMMON,
[MoveId.AMNESIA]: RewardTier.COMMON, [MoveId.AMNESIA]: RarityTier.COMMON,
[MoveId.DREAM_EATER]: RewardTier.GREAT, [MoveId.DREAM_EATER]: RarityTier.GREAT,
[MoveId.LEECH_LIFE]: RewardTier.ULTRA, [MoveId.LEECH_LIFE]: RarityTier.ULTRA,
[MoveId.FLASH]: RewardTier.COMMON, [MoveId.FLASH]: RarityTier.COMMON,
[MoveId.EXPLOSION]: RewardTier.GREAT, [MoveId.EXPLOSION]: RarityTier.GREAT,
[MoveId.REST]: RewardTier.COMMON, [MoveId.REST]: RarityTier.COMMON,
[MoveId.ROCK_SLIDE]: RewardTier.GREAT, [MoveId.ROCK_SLIDE]: RarityTier.GREAT,
[MoveId.TRI_ATTACK]: RewardTier.ULTRA, [MoveId.TRI_ATTACK]: RarityTier.ULTRA,
[MoveId.SUPER_FANG]: RewardTier.COMMON, [MoveId.SUPER_FANG]: RarityTier.COMMON,
[MoveId.SUBSTITUTE]: RewardTier.COMMON, [MoveId.SUBSTITUTE]: RarityTier.COMMON,
[MoveId.THIEF]: RewardTier.GREAT, [MoveId.THIEF]: RarityTier.GREAT,
[MoveId.SNORE]: RewardTier.COMMON, [MoveId.SNORE]: RarityTier.COMMON,
[MoveId.CURSE]: RewardTier.COMMON, [MoveId.CURSE]: RarityTier.COMMON,
[MoveId.REVERSAL]: RewardTier.COMMON, [MoveId.REVERSAL]: RarityTier.COMMON,
[MoveId.SPITE]: RewardTier.COMMON, [MoveId.SPITE]: RarityTier.COMMON,
[MoveId.PROTECT]: RewardTier.COMMON, [MoveId.PROTECT]: RarityTier.COMMON,
[MoveId.SCARY_FACE]: RewardTier.COMMON, [MoveId.SCARY_FACE]: RarityTier.COMMON,
[MoveId.SLUDGE_BOMB]: RewardTier.GREAT, [MoveId.SLUDGE_BOMB]: RarityTier.GREAT,
[MoveId.MUD_SLAP]: RewardTier.COMMON, [MoveId.MUD_SLAP]: RarityTier.COMMON,
[MoveId.SPIKES]: RewardTier.COMMON, [MoveId.SPIKES]: RarityTier.COMMON,
[MoveId.ICY_WIND]: RewardTier.GREAT, [MoveId.ICY_WIND]: RarityTier.GREAT,
[MoveId.OUTRAGE]: RewardTier.ULTRA, [MoveId.OUTRAGE]: RarityTier.ULTRA,
[MoveId.SANDSTORM]: RewardTier.COMMON, [MoveId.SANDSTORM]: RarityTier.COMMON,
[MoveId.GIGA_DRAIN]: RewardTier.ULTRA, [MoveId.GIGA_DRAIN]: RarityTier.ULTRA,
[MoveId.ENDURE]: RewardTier.COMMON, [MoveId.ENDURE]: RarityTier.COMMON,
[MoveId.CHARM]: RewardTier.COMMON, [MoveId.CHARM]: RarityTier.COMMON,
[MoveId.FALSE_SWIPE]: RewardTier.COMMON, [MoveId.FALSE_SWIPE]: RarityTier.COMMON,
[MoveId.SWAGGER]: RewardTier.COMMON, [MoveId.SWAGGER]: RarityTier.COMMON,
[MoveId.STEEL_WING]: RewardTier.GREAT, [MoveId.STEEL_WING]: RarityTier.GREAT,
[MoveId.ATTRACT]: RewardTier.COMMON, [MoveId.ATTRACT]: RarityTier.COMMON,
[MoveId.SLEEP_TALK]: RewardTier.COMMON, [MoveId.SLEEP_TALK]: RarityTier.COMMON,
[MoveId.HEAL_BELL]: RewardTier.COMMON, [MoveId.HEAL_BELL]: RarityTier.COMMON,
[MoveId.RETURN]: RewardTier.ULTRA, [MoveId.RETURN]: RarityTier.ULTRA,
[MoveId.FRUSTRATION]: RewardTier.COMMON, [MoveId.FRUSTRATION]: RarityTier.COMMON,
[MoveId.SAFEGUARD]: RewardTier.COMMON, [MoveId.SAFEGUARD]: RarityTier.COMMON,
[MoveId.PAIN_SPLIT]: RewardTier.COMMON, [MoveId.PAIN_SPLIT]: RarityTier.COMMON,
[MoveId.MEGAHORN]: RewardTier.ULTRA, [MoveId.MEGAHORN]: RarityTier.ULTRA,
[MoveId.BATON_PASS]: RewardTier.COMMON, [MoveId.BATON_PASS]: RarityTier.COMMON,
[MoveId.ENCORE]: RewardTier.COMMON, [MoveId.ENCORE]: RarityTier.COMMON,
[MoveId.IRON_TAIL]: RewardTier.GREAT, [MoveId.IRON_TAIL]: RarityTier.GREAT,
[MoveId.METAL_CLAW]: RewardTier.COMMON, [MoveId.METAL_CLAW]: RarityTier.COMMON,
[MoveId.SYNTHESIS]: RewardTier.GREAT, [MoveId.SYNTHESIS]: RarityTier.GREAT,
[MoveId.HIDDEN_POWER]: RewardTier.GREAT, [MoveId.HIDDEN_POWER]: RarityTier.GREAT,
[MoveId.RAIN_DANCE]: RewardTier.COMMON, [MoveId.RAIN_DANCE]: RarityTier.COMMON,
[MoveId.SUNNY_DAY]: RewardTier.COMMON, [MoveId.SUNNY_DAY]: RarityTier.COMMON,
[MoveId.CRUNCH]: RewardTier.GREAT, [MoveId.CRUNCH]: RarityTier.GREAT,
[MoveId.PSYCH_UP]: RewardTier.COMMON, [MoveId.PSYCH_UP]: RarityTier.COMMON,
[MoveId.SHADOW_BALL]: RewardTier.ULTRA, [MoveId.SHADOW_BALL]: RarityTier.ULTRA,
[MoveId.FUTURE_SIGHT]: RewardTier.GREAT, [MoveId.FUTURE_SIGHT]: RarityTier.GREAT,
[MoveId.ROCK_SMASH]: RewardTier.COMMON, [MoveId.ROCK_SMASH]: RarityTier.COMMON,
[MoveId.WHIRLPOOL]: RewardTier.COMMON, [MoveId.WHIRLPOOL]: RarityTier.COMMON,
[MoveId.BEAT_UP]: RewardTier.COMMON, [MoveId.BEAT_UP]: RarityTier.COMMON,
[MoveId.UPROAR]: RewardTier.GREAT, [MoveId.UPROAR]: RarityTier.GREAT,
[MoveId.HEAT_WAVE]: RewardTier.ULTRA, [MoveId.HEAT_WAVE]: RarityTier.ULTRA,
[MoveId.HAIL]: RewardTier.COMMON, [MoveId.HAIL]: RarityTier.COMMON,
[MoveId.TORMENT]: RewardTier.COMMON, [MoveId.TORMENT]: RarityTier.COMMON,
[MoveId.WILL_O_WISP]: RewardTier.COMMON, [MoveId.WILL_O_WISP]: RarityTier.COMMON,
[MoveId.FACADE]: RewardTier.GREAT, [MoveId.FACADE]: RarityTier.GREAT,
[MoveId.FOCUS_PUNCH]: RewardTier.COMMON, [MoveId.FOCUS_PUNCH]: RarityTier.COMMON,
[MoveId.NATURE_POWER]: RewardTier.COMMON, [MoveId.NATURE_POWER]: RarityTier.COMMON,
[MoveId.CHARGE]: RewardTier.COMMON, [MoveId.CHARGE]: RarityTier.COMMON,
[MoveId.TAUNT]: RewardTier.COMMON, [MoveId.TAUNT]: RarityTier.COMMON,
[MoveId.HELPING_HAND]: RewardTier.COMMON, [MoveId.HELPING_HAND]: RarityTier.COMMON,
[MoveId.TRICK]: RewardTier.COMMON, [MoveId.TRICK]: RarityTier.COMMON,
[MoveId.SUPERPOWER]: RewardTier.ULTRA, [MoveId.SUPERPOWER]: RarityTier.ULTRA,
[MoveId.RECYCLE]: RewardTier.COMMON, [MoveId.RECYCLE]: RarityTier.COMMON,
[MoveId.REVENGE]: RewardTier.GREAT, [MoveId.REVENGE]: RarityTier.GREAT,
[MoveId.BRICK_BREAK]: RewardTier.GREAT, [MoveId.BRICK_BREAK]: RarityTier.GREAT,
[MoveId.KNOCK_OFF]: RewardTier.GREAT, [MoveId.KNOCK_OFF]: RarityTier.GREAT,
[MoveId.ENDEAVOR]: RewardTier.COMMON, [MoveId.ENDEAVOR]: RarityTier.COMMON,
[MoveId.SKILL_SWAP]: RewardTier.COMMON, [MoveId.SKILL_SWAP]: RarityTier.COMMON,
[MoveId.IMPRISON]: RewardTier.COMMON, [MoveId.IMPRISON]: RarityTier.COMMON,
[MoveId.SECRET_POWER]: RewardTier.COMMON, [MoveId.SECRET_POWER]: RarityTier.COMMON,
[MoveId.DIVE]: RewardTier.GREAT, [MoveId.DIVE]: RarityTier.GREAT,
[MoveId.FEATHER_DANCE]: RewardTier.COMMON, [MoveId.FEATHER_DANCE]: RarityTier.COMMON,
[MoveId.BLAZE_KICK]: RewardTier.GREAT, [MoveId.BLAZE_KICK]: RarityTier.GREAT,
[MoveId.HYPER_VOICE]: RewardTier.ULTRA, [MoveId.HYPER_VOICE]: RarityTier.ULTRA,
[MoveId.BLAST_BURN]: RewardTier.ULTRA, [MoveId.BLAST_BURN]: RarityTier.ULTRA,
[MoveId.HYDRO_CANNON]: RewardTier.ULTRA, [MoveId.HYDRO_CANNON]: RarityTier.ULTRA,
[MoveId.WEATHER_BALL]: RewardTier.COMMON, [MoveId.WEATHER_BALL]: RarityTier.COMMON,
[MoveId.FAKE_TEARS]: RewardTier.COMMON, [MoveId.FAKE_TEARS]: RarityTier.COMMON,
[MoveId.AIR_CUTTER]: RewardTier.GREAT, [MoveId.AIR_CUTTER]: RarityTier.GREAT,
[MoveId.OVERHEAT]: RewardTier.ULTRA, [MoveId.OVERHEAT]: RarityTier.ULTRA,
[MoveId.ROCK_TOMB]: RewardTier.GREAT, [MoveId.ROCK_TOMB]: RarityTier.GREAT,
[MoveId.METAL_SOUND]: RewardTier.COMMON, [MoveId.METAL_SOUND]: RarityTier.COMMON,
[MoveId.COSMIC_POWER]: RewardTier.COMMON, [MoveId.COSMIC_POWER]: RarityTier.COMMON,
[MoveId.SIGNAL_BEAM]: RewardTier.GREAT, [MoveId.SIGNAL_BEAM]: RarityTier.GREAT,
[MoveId.SAND_TOMB]: RewardTier.COMMON, [MoveId.SAND_TOMB]: RarityTier.COMMON,
[MoveId.MUDDY_WATER]: RewardTier.GREAT, [MoveId.MUDDY_WATER]: RarityTier.GREAT,
[MoveId.BULLET_SEED]: RewardTier.GREAT, [MoveId.BULLET_SEED]: RarityTier.GREAT,
[MoveId.AERIAL_ACE]: RewardTier.GREAT, [MoveId.AERIAL_ACE]: RarityTier.GREAT,
[MoveId.ICICLE_SPEAR]: RewardTier.GREAT, [MoveId.ICICLE_SPEAR]: RarityTier.GREAT,
[MoveId.IRON_DEFENSE]: RewardTier.GREAT, [MoveId.IRON_DEFENSE]: RarityTier.GREAT,
[MoveId.DRAGON_CLAW]: RewardTier.ULTRA, [MoveId.DRAGON_CLAW]: RarityTier.ULTRA,
[MoveId.FRENZY_PLANT]: RewardTier.ULTRA, [MoveId.FRENZY_PLANT]: RarityTier.ULTRA,
[MoveId.BULK_UP]: RewardTier.COMMON, [MoveId.BULK_UP]: RarityTier.COMMON,
[MoveId.BOUNCE]: RewardTier.GREAT, [MoveId.BOUNCE]: RarityTier.GREAT,
[MoveId.MUD_SHOT]: RewardTier.GREAT, [MoveId.MUD_SHOT]: RarityTier.GREAT,
[MoveId.POISON_TAIL]: RewardTier.GREAT, [MoveId.POISON_TAIL]: RarityTier.GREAT,
[MoveId.COVET]: RewardTier.GREAT, [MoveId.COVET]: RarityTier.GREAT,
[MoveId.MAGICAL_LEAF]: RewardTier.GREAT, [MoveId.MAGICAL_LEAF]: RarityTier.GREAT,
[MoveId.CALM_MIND]: RewardTier.GREAT, [MoveId.CALM_MIND]: RarityTier.GREAT,
[MoveId.LEAF_BLADE]: RewardTier.ULTRA, [MoveId.LEAF_BLADE]: RarityTier.ULTRA,
[MoveId.DRAGON_DANCE]: RewardTier.GREAT, [MoveId.DRAGON_DANCE]: RarityTier.GREAT,
[MoveId.ROCK_BLAST]: RewardTier.GREAT, [MoveId.ROCK_BLAST]: RarityTier.GREAT,
[MoveId.WATER_PULSE]: RewardTier.GREAT, [MoveId.WATER_PULSE]: RarityTier.GREAT,
[MoveId.ROOST]: RewardTier.GREAT, [MoveId.ROOST]: RarityTier.GREAT,
[MoveId.GRAVITY]: RewardTier.COMMON, [MoveId.GRAVITY]: RarityTier.COMMON,
[MoveId.GYRO_BALL]: RewardTier.COMMON, [MoveId.GYRO_BALL]: RarityTier.COMMON,
[MoveId.BRINE]: RewardTier.GREAT, [MoveId.BRINE]: RarityTier.GREAT,
[MoveId.PLUCK]: RewardTier.GREAT, [MoveId.PLUCK]: RarityTier.GREAT,
[MoveId.TAILWIND]: RewardTier.GREAT, [MoveId.TAILWIND]: RarityTier.GREAT,
[MoveId.U_TURN]: RewardTier.GREAT, [MoveId.U_TURN]: RarityTier.GREAT,
[MoveId.CLOSE_COMBAT]: RewardTier.ULTRA, [MoveId.CLOSE_COMBAT]: RarityTier.ULTRA,
[MoveId.PAYBACK]: RewardTier.COMMON, [MoveId.PAYBACK]: RarityTier.COMMON,
[MoveId.ASSURANCE]: RewardTier.COMMON, [MoveId.ASSURANCE]: RarityTier.COMMON,
[MoveId.EMBARGO]: RewardTier.COMMON, [MoveId.EMBARGO]: RarityTier.COMMON,
[MoveId.FLING]: RewardTier.COMMON, [MoveId.FLING]: RarityTier.COMMON,
[MoveId.GASTRO_ACID]: RewardTier.GREAT, [MoveId.GASTRO_ACID]: RarityTier.GREAT,
[MoveId.POWER_SWAP]: RewardTier.COMMON, [MoveId.POWER_SWAP]: RarityTier.COMMON,
[MoveId.GUARD_SWAP]: RewardTier.COMMON, [MoveId.GUARD_SWAP]: RarityTier.COMMON,
[MoveId.WORRY_SEED]: RewardTier.GREAT, [MoveId.WORRY_SEED]: RarityTier.GREAT,
[MoveId.TOXIC_SPIKES]: RewardTier.GREAT, [MoveId.TOXIC_SPIKES]: RarityTier.GREAT,
[MoveId.FLARE_BLITZ]: RewardTier.ULTRA, [MoveId.FLARE_BLITZ]: RarityTier.ULTRA,
[MoveId.AURA_SPHERE]: RewardTier.GREAT, [MoveId.AURA_SPHERE]: RarityTier.GREAT,
[MoveId.ROCK_POLISH]: RewardTier.COMMON, [MoveId.ROCK_POLISH]: RarityTier.COMMON,
[MoveId.POISON_JAB]: RewardTier.GREAT, [MoveId.POISON_JAB]: RarityTier.GREAT,
[MoveId.DARK_PULSE]: RewardTier.GREAT, [MoveId.DARK_PULSE]: RarityTier.GREAT,
[MoveId.AQUA_TAIL]: RewardTier.GREAT, [MoveId.AQUA_TAIL]: RarityTier.GREAT,
[MoveId.SEED_BOMB]: RewardTier.GREAT, [MoveId.SEED_BOMB]: RarityTier.GREAT,
[MoveId.AIR_SLASH]: RewardTier.GREAT, [MoveId.AIR_SLASH]: RarityTier.GREAT,
[MoveId.X_SCISSOR]: RewardTier.GREAT, [MoveId.X_SCISSOR]: RarityTier.GREAT,
[MoveId.BUG_BUZZ]: RewardTier.GREAT, [MoveId.BUG_BUZZ]: RarityTier.GREAT,
[MoveId.DRAGON_PULSE]: RewardTier.GREAT, [MoveId.DRAGON_PULSE]: RarityTier.GREAT,
[MoveId.POWER_GEM]: RewardTier.GREAT, [MoveId.POWER_GEM]: RarityTier.GREAT,
[MoveId.DRAIN_PUNCH]: RewardTier.GREAT, [MoveId.DRAIN_PUNCH]: RarityTier.GREAT,
[MoveId.VACUUM_WAVE]: RewardTier.COMMON, [MoveId.VACUUM_WAVE]: RarityTier.COMMON,
[MoveId.FOCUS_BLAST]: RewardTier.GREAT, [MoveId.FOCUS_BLAST]: RarityTier.GREAT,
[MoveId.ENERGY_BALL]: RewardTier.GREAT, [MoveId.ENERGY_BALL]: RarityTier.GREAT,
[MoveId.BRAVE_BIRD]: RewardTier.ULTRA, [MoveId.BRAVE_BIRD]: RarityTier.ULTRA,
[MoveId.EARTH_POWER]: RewardTier.ULTRA, [MoveId.EARTH_POWER]: RarityTier.ULTRA,
[MoveId.GIGA_IMPACT]: RewardTier.GREAT, [MoveId.GIGA_IMPACT]: RarityTier.GREAT,
[MoveId.NASTY_PLOT]: RewardTier.COMMON, [MoveId.NASTY_PLOT]: RarityTier.COMMON,
[MoveId.AVALANCHE]: RewardTier.GREAT, [MoveId.AVALANCHE]: RarityTier.GREAT,
[MoveId.SHADOW_CLAW]: RewardTier.GREAT, [MoveId.SHADOW_CLAW]: RarityTier.GREAT,
[MoveId.THUNDER_FANG]: RewardTier.GREAT, [MoveId.THUNDER_FANG]: RarityTier.GREAT,
[MoveId.ICE_FANG]: RewardTier.GREAT, [MoveId.ICE_FANG]: RarityTier.GREAT,
[MoveId.FIRE_FANG]: RewardTier.GREAT, [MoveId.FIRE_FANG]: RarityTier.GREAT,
[MoveId.PSYCHO_CUT]: RewardTier.GREAT, [MoveId.PSYCHO_CUT]: RarityTier.GREAT,
[MoveId.ZEN_HEADBUTT]: RewardTier.GREAT, [MoveId.ZEN_HEADBUTT]: RarityTier.GREAT,
[MoveId.FLASH_CANNON]: RewardTier.GREAT, [MoveId.FLASH_CANNON]: RarityTier.GREAT,
[MoveId.ROCK_CLIMB]: RewardTier.GREAT, [MoveId.ROCK_CLIMB]: RarityTier.GREAT,
[MoveId.DEFOG]: RewardTier.COMMON, [MoveId.DEFOG]: RarityTier.COMMON,
[MoveId.TRICK_ROOM]: RewardTier.COMMON, [MoveId.TRICK_ROOM]: RarityTier.COMMON,
[MoveId.DRACO_METEOR]: RewardTier.ULTRA, [MoveId.DRACO_METEOR]: RarityTier.ULTRA,
[MoveId.LEAF_STORM]: RewardTier.ULTRA, [MoveId.LEAF_STORM]: RarityTier.ULTRA,
[MoveId.POWER_WHIP]: RewardTier.ULTRA, [MoveId.POWER_WHIP]: RarityTier.ULTRA,
[MoveId.CROSS_POISON]: RewardTier.GREAT, [MoveId.CROSS_POISON]: RarityTier.GREAT,
[MoveId.GUNK_SHOT]: RewardTier.ULTRA, [MoveId.GUNK_SHOT]: RarityTier.ULTRA,
[MoveId.IRON_HEAD]: RewardTier.GREAT, [MoveId.IRON_HEAD]: RarityTier.GREAT,
[MoveId.STONE_EDGE]: RewardTier.ULTRA, [MoveId.STONE_EDGE]: RarityTier.ULTRA,
[MoveId.STEALTH_ROCK]: RewardTier.COMMON, [MoveId.STEALTH_ROCK]: RarityTier.COMMON,
[MoveId.GRASS_KNOT]: RewardTier.ULTRA, [MoveId.GRASS_KNOT]: RarityTier.ULTRA,
[MoveId.BUG_BITE]: RewardTier.GREAT, [MoveId.BUG_BITE]: RarityTier.GREAT,
[MoveId.CHARGE_BEAM]: RewardTier.GREAT, [MoveId.CHARGE_BEAM]: RarityTier.GREAT,
[MoveId.HONE_CLAWS]: RewardTier.COMMON, [MoveId.HONE_CLAWS]: RarityTier.COMMON,
[MoveId.WONDER_ROOM]: RewardTier.COMMON, [MoveId.WONDER_ROOM]: RarityTier.COMMON,
[MoveId.PSYSHOCK]: RewardTier.GREAT, [MoveId.PSYSHOCK]: RarityTier.GREAT,
[MoveId.VENOSHOCK]: RewardTier.GREAT, [MoveId.VENOSHOCK]: RarityTier.GREAT,
[MoveId.MAGIC_ROOM]: RewardTier.COMMON, [MoveId.MAGIC_ROOM]: RarityTier.COMMON,
[MoveId.SMACK_DOWN]: RewardTier.COMMON, [MoveId.SMACK_DOWN]: RarityTier.COMMON,
[MoveId.SLUDGE_WAVE]: RewardTier.GREAT, [MoveId.SLUDGE_WAVE]: RarityTier.GREAT,
[MoveId.HEAVY_SLAM]: RewardTier.GREAT, [MoveId.HEAVY_SLAM]: RarityTier.GREAT,
[MoveId.ELECTRO_BALL]: RewardTier.GREAT, [MoveId.ELECTRO_BALL]: RarityTier.GREAT,
[MoveId.FLAME_CHARGE]: RewardTier.GREAT, [MoveId.FLAME_CHARGE]: RarityTier.GREAT,
[MoveId.LOW_SWEEP]: RewardTier.GREAT, [MoveId.LOW_SWEEP]: RarityTier.GREAT,
[MoveId.ACID_SPRAY]: RewardTier.COMMON, [MoveId.ACID_SPRAY]: RarityTier.COMMON,
[MoveId.FOUL_PLAY]: RewardTier.ULTRA, [MoveId.FOUL_PLAY]: RarityTier.ULTRA,
[MoveId.ROUND]: RewardTier.COMMON, [MoveId.ROUND]: RarityTier.COMMON,
[MoveId.ECHOED_VOICE]: RewardTier.COMMON, [MoveId.ECHOED_VOICE]: RarityTier.COMMON,
[MoveId.STORED_POWER]: RewardTier.COMMON, [MoveId.STORED_POWER]: RarityTier.COMMON,
[MoveId.ALLY_SWITCH]: RewardTier.COMMON, [MoveId.ALLY_SWITCH]: RarityTier.COMMON,
[MoveId.SCALD]: RewardTier.GREAT, [MoveId.SCALD]: RarityTier.GREAT,
[MoveId.HEX]: RewardTier.GREAT, [MoveId.HEX]: RarityTier.GREAT,
[MoveId.SKY_DROP]: RewardTier.GREAT, [MoveId.SKY_DROP]: RarityTier.GREAT,
[MoveId.INCINERATE]: RewardTier.GREAT, [MoveId.INCINERATE]: RarityTier.GREAT,
[MoveId.QUASH]: RewardTier.COMMON, [MoveId.QUASH]: RarityTier.COMMON,
[MoveId.ACROBATICS]: RewardTier.GREAT, [MoveId.ACROBATICS]: RarityTier.GREAT,
[MoveId.RETALIATE]: RewardTier.GREAT, [MoveId.RETALIATE]: RarityTier.GREAT,
[MoveId.WATER_PLEDGE]: RewardTier.GREAT, [MoveId.WATER_PLEDGE]: RarityTier.GREAT,
[MoveId.FIRE_PLEDGE]: RewardTier.GREAT, [MoveId.FIRE_PLEDGE]: RarityTier.GREAT,
[MoveId.GRASS_PLEDGE]: RewardTier.GREAT, [MoveId.GRASS_PLEDGE]: RarityTier.GREAT,
[MoveId.VOLT_SWITCH]: RewardTier.GREAT, [MoveId.VOLT_SWITCH]: RarityTier.GREAT,
[MoveId.STRUGGLE_BUG]: RewardTier.COMMON, [MoveId.STRUGGLE_BUG]: RarityTier.COMMON,
[MoveId.BULLDOZE]: RewardTier.GREAT, [MoveId.BULLDOZE]: RarityTier.GREAT,
[MoveId.FROST_BREATH]: RewardTier.GREAT, [MoveId.FROST_BREATH]: RarityTier.GREAT,
[MoveId.DRAGON_TAIL]: RewardTier.GREAT, [MoveId.DRAGON_TAIL]: RarityTier.GREAT,
[MoveId.WORK_UP]: RewardTier.COMMON, [MoveId.WORK_UP]: RarityTier.COMMON,
[MoveId.ELECTROWEB]: RewardTier.GREAT, [MoveId.ELECTROWEB]: RarityTier.GREAT,
[MoveId.WILD_CHARGE]: RewardTier.GREAT, [MoveId.WILD_CHARGE]: RarityTier.GREAT,
[MoveId.DRILL_RUN]: RewardTier.GREAT, [MoveId.DRILL_RUN]: RarityTier.GREAT,
[MoveId.RAZOR_SHELL]: RewardTier.GREAT, [MoveId.RAZOR_SHELL]: RarityTier.GREAT,
[MoveId.HEAT_CRASH]: RewardTier.GREAT, [MoveId.HEAT_CRASH]: RarityTier.GREAT,
[MoveId.TAIL_SLAP]: RewardTier.GREAT, [MoveId.TAIL_SLAP]: RarityTier.GREAT,
[MoveId.HURRICANE]: RewardTier.ULTRA, [MoveId.HURRICANE]: RarityTier.ULTRA,
[MoveId.SNARL]: RewardTier.COMMON, [MoveId.SNARL]: RarityTier.COMMON,
[MoveId.PHANTOM_FORCE]: RewardTier.ULTRA, [MoveId.PHANTOM_FORCE]: RarityTier.ULTRA,
[MoveId.PETAL_BLIZZARD]: RewardTier.GREAT, [MoveId.PETAL_BLIZZARD]: RarityTier.GREAT,
[MoveId.DISARMING_VOICE]: RewardTier.GREAT, [MoveId.DISARMING_VOICE]: RarityTier.GREAT,
[MoveId.DRAINING_KISS]: RewardTier.GREAT, [MoveId.DRAINING_KISS]: RarityTier.GREAT,
[MoveId.GRASSY_TERRAIN]: RewardTier.COMMON, [MoveId.GRASSY_TERRAIN]: RarityTier.COMMON,
[MoveId.MISTY_TERRAIN]: RewardTier.COMMON, [MoveId.MISTY_TERRAIN]: RarityTier.COMMON,
[MoveId.PLAY_ROUGH]: RewardTier.GREAT, [MoveId.PLAY_ROUGH]: RarityTier.GREAT,
[MoveId.CONFIDE]: RewardTier.COMMON, [MoveId.CONFIDE]: RarityTier.COMMON,
[MoveId.MYSTICAL_FIRE]: RewardTier.GREAT, [MoveId.MYSTICAL_FIRE]: RarityTier.GREAT,
[MoveId.EERIE_IMPULSE]: RewardTier.COMMON, [MoveId.EERIE_IMPULSE]: RarityTier.COMMON,
[MoveId.VENOM_DRENCH]: RewardTier.COMMON, [MoveId.VENOM_DRENCH]: RarityTier.COMMON,
[MoveId.ELECTRIC_TERRAIN]: RewardTier.COMMON, [MoveId.ELECTRIC_TERRAIN]: RarityTier.COMMON,
[MoveId.DAZZLING_GLEAM]: RewardTier.ULTRA, [MoveId.DAZZLING_GLEAM]: RarityTier.ULTRA,
[MoveId.INFESTATION]: RewardTier.COMMON, [MoveId.INFESTATION]: RarityTier.COMMON,
[MoveId.POWER_UP_PUNCH]: RewardTier.GREAT, [MoveId.POWER_UP_PUNCH]: RarityTier.GREAT,
[MoveId.DARKEST_LARIAT]: RewardTier.GREAT, [MoveId.DARKEST_LARIAT]: RarityTier.GREAT,
[MoveId.HIGH_HORSEPOWER]: RewardTier.ULTRA, [MoveId.HIGH_HORSEPOWER]: RarityTier.ULTRA,
[MoveId.SOLAR_BLADE]: RewardTier.GREAT, [MoveId.SOLAR_BLADE]: RarityTier.GREAT,
[MoveId.THROAT_CHOP]: RewardTier.GREAT, [MoveId.THROAT_CHOP]: RarityTier.GREAT,
[MoveId.POLLEN_PUFF]: RewardTier.GREAT, [MoveId.POLLEN_PUFF]: RarityTier.GREAT,
[MoveId.PSYCHIC_TERRAIN]: RewardTier.COMMON, [MoveId.PSYCHIC_TERRAIN]: RarityTier.COMMON,
[MoveId.LUNGE]: RewardTier.GREAT, [MoveId.LUNGE]: RarityTier.GREAT,
[MoveId.SPEED_SWAP]: RewardTier.COMMON, [MoveId.SPEED_SWAP]: RarityTier.COMMON,
[MoveId.SMART_STRIKE]: RewardTier.GREAT, [MoveId.SMART_STRIKE]: RarityTier.GREAT,
[MoveId.BRUTAL_SWING]: RewardTier.GREAT, [MoveId.BRUTAL_SWING]: RarityTier.GREAT,
[MoveId.AURORA_VEIL]: RewardTier.COMMON, [MoveId.AURORA_VEIL]: RarityTier.COMMON,
[MoveId.PSYCHIC_FANGS]: RewardTier.GREAT, [MoveId.PSYCHIC_FANGS]: RarityTier.GREAT,
[MoveId.STOMPING_TANTRUM]: RewardTier.GREAT, [MoveId.STOMPING_TANTRUM]: RarityTier.GREAT,
[MoveId.LIQUIDATION]: RewardTier.ULTRA, [MoveId.LIQUIDATION]: RarityTier.ULTRA,
[MoveId.BODY_PRESS]: RewardTier.ULTRA, [MoveId.BODY_PRESS]: RarityTier.ULTRA,
[MoveId.BREAKING_SWIPE]: RewardTier.GREAT, [MoveId.BREAKING_SWIPE]: RarityTier.GREAT,
[MoveId.STEEL_BEAM]: RewardTier.ULTRA, [MoveId.STEEL_BEAM]: RarityTier.ULTRA,
[MoveId.EXPANDING_FORCE]: RewardTier.GREAT, [MoveId.EXPANDING_FORCE]: RarityTier.GREAT,
[MoveId.STEEL_ROLLER]: RewardTier.COMMON, [MoveId.STEEL_ROLLER]: RarityTier.COMMON,
[MoveId.SCALE_SHOT]: RewardTier.ULTRA, [MoveId.SCALE_SHOT]: RarityTier.ULTRA,
[MoveId.METEOR_BEAM]: RewardTier.GREAT, [MoveId.METEOR_BEAM]: RarityTier.GREAT,
[MoveId.MISTY_EXPLOSION]: RewardTier.COMMON, [MoveId.MISTY_EXPLOSION]: RarityTier.COMMON,
[MoveId.GRASSY_GLIDE]: RewardTier.COMMON, [MoveId.GRASSY_GLIDE]: RarityTier.COMMON,
[MoveId.RISING_VOLTAGE]: RewardTier.COMMON, [MoveId.RISING_VOLTAGE]: RarityTier.COMMON,
[MoveId.TERRAIN_PULSE]: RewardTier.COMMON, [MoveId.TERRAIN_PULSE]: RarityTier.COMMON,
[MoveId.SKITTER_SMACK]: RewardTier.GREAT, [MoveId.SKITTER_SMACK]: RarityTier.GREAT,
[MoveId.BURNING_JEALOUSY]: RewardTier.GREAT, [MoveId.BURNING_JEALOUSY]: RarityTier.GREAT,
[MoveId.LASH_OUT]: RewardTier.GREAT, [MoveId.LASH_OUT]: RarityTier.GREAT,
[MoveId.POLTERGEIST]: RewardTier.ULTRA, [MoveId.POLTERGEIST]: RarityTier.ULTRA,
[MoveId.CORROSIVE_GAS]: RewardTier.COMMON, [MoveId.CORROSIVE_GAS]: RarityTier.COMMON,
[MoveId.COACHING]: RewardTier.COMMON, [MoveId.COACHING]: RarityTier.COMMON,
[MoveId.FLIP_TURN]: RewardTier.COMMON, [MoveId.FLIP_TURN]: RarityTier.COMMON,
[MoveId.TRIPLE_AXEL]: RewardTier.COMMON, [MoveId.TRIPLE_AXEL]: RarityTier.COMMON,
[MoveId.DUAL_WINGBEAT]: RewardTier.COMMON, [MoveId.DUAL_WINGBEAT]: RarityTier.COMMON,
[MoveId.SCORCHING_SANDS]: RewardTier.GREAT, [MoveId.SCORCHING_SANDS]: RarityTier.GREAT,
[MoveId.TERA_BLAST]: RewardTier.GREAT, [MoveId.TERA_BLAST]: RarityTier.GREAT,
[MoveId.ICE_SPINNER]: RewardTier.GREAT, [MoveId.ICE_SPINNER]: RarityTier.GREAT,
[MoveId.SNOWSCAPE]: RewardTier.COMMON, [MoveId.SNOWSCAPE]: RarityTier.COMMON,
[MoveId.POUNCE]: RewardTier.COMMON, [MoveId.POUNCE]: RarityTier.COMMON,
[MoveId.TRAILBLAZE]: RewardTier.COMMON, [MoveId.TRAILBLAZE]: RarityTier.COMMON,
[MoveId.CHILLING_WATER]: RewardTier.COMMON, [MoveId.CHILLING_WATER]: RarityTier.COMMON,
[MoveId.HARD_PRESS]: RewardTier.GREAT, [MoveId.HARD_PRESS]: RarityTier.GREAT,
[MoveId.DRAGON_CHEER]: RewardTier.COMMON, [MoveId.DRAGON_CHEER]: RarityTier.COMMON,
[MoveId.ALLURING_VOICE]: RewardTier.GREAT, [MoveId.ALLURING_VOICE]: RarityTier.GREAT,
[MoveId.TEMPER_FLARE]: RewardTier.GREAT, [MoveId.TEMPER_FLARE]: RarityTier.GREAT,
[MoveId.SUPERCELL_SLAM]: RewardTier.GREAT, [MoveId.SUPERCELL_SLAM]: RarityTier.GREAT,
[MoveId.PSYCHIC_NOISE]: RewardTier.GREAT, [MoveId.PSYCHIC_NOISE]: RarityTier.GREAT,
[MoveId.UPPER_HAND]: RewardTier.COMMON, [MoveId.UPPER_HAND]: RarityTier.COMMON,
}; };

View File

@ -16,7 +16,7 @@ import type { MoveId } from "#enums/move-id";
import type { MoveSourceType } from "#enums/move-source-type"; import type { MoveSourceType } from "#enums/move-source-type";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { PokemonType } from "#enums/pokemon-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 { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant"; import { TrainerVariant } from "#enums/trainer-variant";
@ -457,13 +457,13 @@ export class SingleGenerationChallenge extends Challenge {
.setBattleType(BattleType.TRAINER) .setBattleType(BattleType.TRAINER)
.setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true)) .setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true))
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
], ],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}); });
@ -474,14 +474,14 @@ export class SingleGenerationChallenge extends Challenge {
.setBattleType(BattleType.TRAINER) .setBattleType(BattleType.TRAINER)
.setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1) .setSeedOffsetWave(ClassicFixedBossWaves.EVIL_GRUNT_1)
.setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true)) .setGetTrainerFunc(getRandomTrainerFunc(trainerTypes, true))
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
], ],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}); });

View File

@ -3,8 +3,8 @@ import type { PokemonSpecies } from "#data/pokemon-species";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { TrainerItemId } from "#enums/trainer-item-id"; import type { TrainerItemId } from "#enums/trainer-item-id";
import type { HeldItem } from "#items/held-item"; import type { HeldItem } from "#items/held-item";
import type { Rewards } from "#items/reward";
import type { TrainerItem } from "#items/trainer-item"; import type { TrainerItem } from "#items/trainer-item";
import type { ModifierTypes } from "#modifiers/modifier-type";
import type { Move } from "#moves/move"; import type { Move } from "#moves/move";
export const allAbilities: Ability[] = []; export const allAbilities: Ability[] = [];
@ -15,4 +15,4 @@ export const allHeldItems: Record<HeldItemId, HeldItem> = {};
export const allTrainerItems: Record<TrainerItemId, TrainerItem> = {}; export const allTrainerItems: Record<TrainerItemId, TrainerItem> = {};
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment // TODO: Figure out what this is used for and provide an appropriate tsdoc comment
export const modifierTypes = {} as ModifierTypes; export const allRewards = {} as Rewards;

View File

@ -69,7 +69,7 @@ import { MoveUsedEvent } from "#events/battle-scene";
import type { EnemyPokemon, Pokemon } from "#field/pokemon"; import type { EnemyPokemon, Pokemon } from "#field/pokemon";
import { applyHeldItems } from "#items/all-held-items"; import { applyHeldItems } from "#items/all-held-items";
import { BerryHeldItem, berryTypeToHeldItem } from "#items/berry"; 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 { TrainerItemEffect } from "#items/trainer-item";
import { applyMoveAttrs } from "#moves/apply-attrs"; import { applyMoveAttrs } from "#moves/apply-attrs";
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves"; 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")) { if (!this.hasAttr("TypelessAttr")) {
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power); globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);
applyHeldItems(HeldItemEffect.ATTACK_TYPE_BOOST, { applyHeldItems(HeldItemEffect.ATTACK_TYPE_BOOST, {
pokemon: source, pokemon: source,
moveType: typeChangeHolder.value, moveType: typeChangeHolder.value,
movePower: power, movePower: power,
}); });
@ -2636,14 +2636,14 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
} }
const stolenItem = heldItems[user.randBattleSeedInt(heldItems.length)]; const stolenItem = heldItems[user.randBattleSeedInt(heldItems.length)];
if (!globalScene.tryTransferHeldItem(stolenItem, target, user, false)) { if (!globalScene.tryTransferHeldItem(stolenItem, target, user, false)) {
return false; return false;
} }
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem",
{ pokemonName: getPokemonNameWithAffix(user), { pokemonName: getPokemonNameWithAffix(user),
targetName: getPokemonNameWithAffix(target), targetName: getPokemonNameWithAffix(target),
itemName: allHeldItems[stolenItem].name itemName: allHeldItems[stolenItem].name
} }
)); ));
@ -2719,16 +2719,16 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
globalScene.updateItems(target.isPlayer()); globalScene.updateItems(target.isPlayer());
if (this.berriesOnly) { 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 })); { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name }));
} else { } 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 })); { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name }));
} }
return true; return true;
} }
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const heldItems = target.getHeldItems(); const heldItems = target.getHeldItems();
return heldItems.length ? 5 : 0; return heldItems.length ? 5 : 0;
@ -2775,7 +2775,7 @@ export class EatBerryAttr extends MoveEffectAttr {
// check for berry pouch preservation // check for berry pouch preservation
globalScene.applyPlayerItems(TrainerItemEffect.PRESERVE_BERRY, {pokemon: pokemon, doPreserve: preserve}); globalScene.applyPlayerItems(TrainerItemEffect.PRESERVE_BERRY, {pokemon: pokemon, doPreserve: preserve});
if (!preserve.value) { if (!preserve.value) {
this.reduceBerryModifier(pokemon); this.reduceBerryItem(pokemon);
} }
// Don't update harvest for berries preserved via Berry pouch (no item dupes lol) // 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)); return target.getHeldItems().filter(m => isItemInCategory(m, HeldItemCategoryId.BERRY));
} }
reduceBerryModifier(target: Pokemon) { reduceBerryItem(target: Pokemon) {
if (this.chosenBerry) { if (this.chosenBerry) {
target.loseHeldItem(this.chosenBerry); target.loseHeldItem(this.chosenBerry);
} }
@ -2850,7 +2850,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
applyAbAttrs("PostItemLostAbAttr", {pokemon: target}); applyAbAttrs("PostItemLostAbAttr", {pokemon: target});
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: allHeldItems[this.chosenBerry].name }); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: allHeldItems[this.chosenBerry].name });
globalScene.phaseManager.queueMessage(message); globalScene.phaseManager.queueMessage(message);
this.reduceBerryModifier(target); this.reduceBerryItem(target);
this.eatBerry(user, target); this.eatBerry(user, target);
return true; return true;

View File

@ -1,12 +1,12 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 type { IEggOptions } from "#data/egg";
import { EggSourceType } from "#enums/egg-source-types"; import { EggSourceType } from "#enums/egg-source-types";
import { EggTier } from "#enums/egg-type"; import { EggTier } from "#enums/egg-type";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
@ -150,7 +150,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
}, },
async () => { async () => {
const encounter = globalScene.currentBattle.mysteryEncounter!; 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]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
await transitionMysteryEncounterIntroVisuals(); await transitionMysteryEncounterIntroVisuals();
@ -164,8 +164,8 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedRewardFuncs: [allRewards.SACRED_ASH],
guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ULTRA], guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ULTRA],
fillRemaining: true, fillRemaining: true,
}, },
[eggOptions], [eggOptions],

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { speciesStarterCosts } from "#balance/starters"; 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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
@ -136,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
}) })
.withOptionPhase(async () => { .withOptionPhase(async () => {
// Give the player a Shiny Charm // Give the player a Shiny Charm
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.SHINY_CHARM); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.SHINY_CHARM);
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
}) })
.build(), .build(),
@ -184,7 +184,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,22 +1,22 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; 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 { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { RewardPoolType } from "#enums/reward-pool-type";
import { PERMANENT_STATS, Stat } from "#enums/stat"; import { PERMANENT_STATS, Stat } from "#enums/stat";
import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { berryTypeToHeldItem } from "#items/berry"; import { berryTypeToHeldItem } from "#items/berry";
import type { ModifierTypeOption } from "#modifiers/modifier-type"; import type { RewardOption } from "#items/reward";
import { regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; import { generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils";
import { generateRewardOption } from "#items/reward-utils";
import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { import {
generateModifierTypeOption,
getRandomEncounterSpecies, getRandomEncounterSpecies,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
@ -88,7 +88,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
: globalScene.currentBattle.waveIndex > 40 : globalScene.currentBattle.waveIndex > 40
? 4 ? 4
: 2; : 2;
regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0); generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), globalScene.getPlayerParty(), 0);
encounter.misc = { numBerries }; encounter.misc = { numBerries };
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon); 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++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(modifierTypes.BERRY); const mod = generateRewardOption(allRewards.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
} }
setEncounterRewards( setEncounterRewards({ guaranteedRewardOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
{ guaranteedModifierTypeOptions: shopOptions, fillRemaining: false },
undefined,
doBerryRewards,
);
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); 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 speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
const numBerries: number = encounter.misc.numBerries; const numBerries: number = encounter.misc.numBerries;
const shopOptions: ModifierTypeOption[] = []; const shopOptions: RewardOption[] = [];
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
// Generate shop berries // Generate shop berries
const mod = generateModifierTypeOption(modifierTypes.BERRY); const mod = generateRewardOption(allRewards.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
@ -246,7 +242,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
}; };
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeOptions: shopOptions, guaranteedRewardOptions: shopOptions,
fillRemaining: false, fillRemaining: false,
}, },
undefined, undefined,
@ -279,7 +275,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp); setEncounterExp(fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeOptions: shopOptions, guaranteedRewardOptions: shopOptions,
fillRemaining: false, fillRemaining: false,
}, },
undefined, undefined,
@ -301,7 +297,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,6 +1,6 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 { HeldItemId } from "#enums/held-item-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; 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 { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { PokemonType } from "#enums/pokemon-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 { SpeciesId } from "#enums/species-id";
import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerItemId } from "#enums/trainer-item-id";
import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerSlot } from "#enums/trainer-slot";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import type { PlayerPokemon, Pokemon } from "#field/pokemon"; 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 { PokemonMove } from "#moves/pokemon-move";
import { getEncounterText, showEncounterDialogue } from "#mystery-encounters/encounter-dialogue-utils"; import { getEncounterText, showEncounterDialogue } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { import {
generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
selectOptionThenPokemon, selectOptionThenPokemon,
@ -285,7 +285,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
moveTutorOptions, moveTutorOptions,
}; };
// Assigns callback that teaches move before continuing to rewards // Assigns callback that teaches move before continuing to allRewards
encounter.onRewards = doBugTypeMoveTutor; encounter.onRewards = doBugTypeMoveTutor;
setEncounterRewards({ fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
@ -305,7 +305,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
// Player shows off their bug types // Player shows off their bug types
const encounter = globalScene.currentBattle.mysteryEncounter!; 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 numBugTypes = globalScene.getPlayerParty().filter(p => p.isOfType(PokemonType.BUG, true)).length;
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, {
count: numBugTypes, count: numBugTypes,
@ -314,7 +314,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
if (numBugTypes < 2) { if (numBugTypes < 2) {
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.SUPER_LURE, modifierTypes.GREAT_BALL], guaranteedRewardFuncs: [allRewards.SUPER_LURE, allRewards.GREAT_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -325,7 +325,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
]; ];
} else if (numBugTypes < 4) { } else if (numBugTypes < 4) {
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.QUICK_CLAW, modifierTypes.MAX_LURE, modifierTypes.ULTRA_BALL], guaranteedRewardFuncs: [allRewards.QUICK_CLAW, allRewards.MAX_LURE, allRewards.ULTRA_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -336,7 +336,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
]; ];
} else if (numBugTypes < 6) { } else if (numBugTypes < 6) {
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.GRIP_CLAW, modifierTypes.MAX_LURE, modifierTypes.ROGUE_BALL], guaranteedRewardFuncs: [allRewards.GRIP_CLAW, allRewards.MAX_LURE, allRewards.ROGUE_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -348,38 +348,38 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
} else { } else {
// If the player has any evolution/form change items that are valid for their party, // 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 // spawn one of those items in addition to Dynamax Band, Mega Band, and Master Ball
const modifierOptions: ModifierTypeOption[] = [generateModifierTypeOption(modifierTypes.MASTER_BALL)!]; const rewardOptions: RewardOption[] = [generateRewardOption(allRewards.MASTER_BALL)!];
const specialOptions: ModifierTypeOption[] = []; const specialOptions: RewardOption[] = [];
if (!globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) { 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)) { 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); const nonRareEvolutionReward = generateRewardOption(allRewards.EVOLUTION_ITEM);
if (nonRareEvolutionModifier) { if (nonRareEvolutionReward) {
specialOptions.push(nonRareEvolutionModifier); specialOptions.push(nonRareEvolutionReward);
} }
const rareEvolutionModifier = generateModifierTypeOption(modifierTypes.RARE_EVOLUTION_ITEM); const rareEvolutionReward = generateRewardOption(allRewards.RARE_EVOLUTION_ITEM);
if (rareEvolutionModifier) { if (rareEvolutionReward) {
specialOptions.push(rareEvolutionModifier); specialOptions.push(rareEvolutionReward);
} }
const formChangeModifier = generateModifierTypeOption(modifierTypes.FORM_CHANGE_ITEM); const formChangeReward = generateRewardOption(allRewards.FORM_CHANGE_ITEM);
if (formChangeModifier) { if (formChangeReward) {
specialOptions.push(formChangeModifier); specialOptions.push(formChangeReward);
} }
const rareFormChangeModifier = generateModifierTypeOption(modifierTypes.RARE_FORM_CHANGE_ITEM); const rareFormChangeReward = generateRewardOption(allRewards.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeModifier) { if (rareFormChangeReward) {
specialOptions.push(rareFormChangeModifier); specialOptions.push(rareFormChangeReward);
} }
if (specialOptions.length > 0) { if (specialOptions.length > 0) {
// TODO: should this use `randSeedItem`? // TODO: should this use `randSeedItem`?
modifierOptions.push(specialOptions[randSeedInt(specialOptions.length)]); rewardOptions.push(specialOptions[randSeedInt(specialOptions.length)]);
} }
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: modifierOptions, guaranteedRewardOptions: rewardOptions,
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -465,12 +465,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
chosenPokemon.loseHeldItem(lostItem, false); chosenPokemon.loseHeldItem(lostItem, false);
globalScene.updateItems(true); globalScene.updateItems(true);
const bugNet = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; const bugNet = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!;
bugNet.type.tier = RewardTier.ROGUE; bugNet.type.tier = RarityTier.ROGUE;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: [bugNet], guaranteedRewardOptions: [bugNet],
guaranteedModifierTypeFuncs: [modifierTypes.REVIVER_SEED], guaranteedRewardFuncs: [allRewards.REVIVER_SEED],
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
@ -744,7 +744,7 @@ function doBugTypeMoveTutor(): Promise<void> {
); );
} }
// Complete battle and go to rewards // Complete battle and go to allRewards
resolve(); resolve();
}); });
} }

View File

@ -16,13 +16,13 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { PokemonType } from "#enums/pokemon-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 { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon"; import type { PlayerPokemon } from "#field/pokemon";
import { getHeldItemTier } from "#items/held-item-default-tiers";
import { assignItemsFromConfiguration } from "#items/held-item-pool"; import { assignItemsFromConfiguration } from "#items/held-item-pool";
import { getHeldItemTier } from "#items/held-item-tiers";
import { PokemonMove } from "#moves/pokemon-move"; import { PokemonMove } from "#moves/pokemon-move";
import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
@ -329,11 +329,11 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
let numRogue = 0; let numRogue = 0;
for (const m of items) { for (const m of items) {
const tier = getHeldItemTier(m) ?? RewardTier.ULTRA; const tier = getHeldItemTier(m) ?? RarityTier.ULTRA;
const stack = mostHeldItemsPokemon.heldItemManager.getStack(m); const stack = mostHeldItemsPokemon.heldItemManager.getStack(m);
if (tier === RewardTier.ROGUE) { if (tier === RarityTier.ROGUE) {
numRogue += stack; numRogue += stack;
} else if (tier === RewardTier.ULTRA) { } else if (tier === RarityTier.ULTRA) {
numUltra += stack; numUltra += stack;
} }
mostHeldItemsPokemon.heldItemManager.remove(m, stack); mostHeldItemsPokemon.heldItemManager.remove(m, stack);

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { EncounterBattleAnim } from "#data/battle-anims"; 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 { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
@ -219,7 +219,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
await hideOricorioPokemon(); await hideOricorioPokemon();
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.BATON], guaranteedRewardFuncs: [allRewards.BATON],
fillRemaining: true, fillRemaining: true,
}); });
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);

View File

@ -1,6 +1,6 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#data/data-lists"; import { allRewards } from "#data/data-lists";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -160,7 +160,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE
.withOptionPhase(async () => { .withOptionPhase(async () => {
// Give the player 5 Rogue Balls // Give the player 5 Rogue Balls
const encounter = globalScene.currentBattle.mysteryEncounter!; 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 // 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 // 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 () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { timedEventManager } from "#app/global-event-manager"; import { timedEventManager } from "#app/global-event-manager";
import { globalScene } from "#app/global-scene"; 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 { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -34,7 +34,7 @@ const namespace = "mysteryEncounters/delibirdy";
/** Berries only */ /** Berries only */
const OPTION_2_ALLOWED_HELD_ITEMS = [HeldItemCategoryId.BERRY, HeldItemId.REVIVER_SEED]; 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 OPTION_3_DISALLOWED_HELD_ITEMS = [HeldItemCategoryId.BERRY, HeldItemId.REVIVER_SEED];
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
@ -61,10 +61,10 @@ const doEventReward = () => {
return !fullStack; return !fullStack;
}); });
if (candidates.length > 0) { if (candidates.length > 0) {
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes[randSeedItem(candidates)]); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards[randSeedItem(candidates)]);
} else { } else {
// At max stacks, give a Voucher instead // 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 // At max stacks, give the first party pokemon a Shell Bell instead
backupOption(); backupOption();
} else { } else {
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.AMULET_COIN); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.AMULET_COIN);
doEventReward(); doEventReward();
} }
@ -238,7 +238,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
backupOption(); backupOption();
} else { } else {
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.CANDY_JAR); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.CANDY_JAR);
doEventReward(); doEventReward();
} }
} else { } else {
@ -249,7 +249,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
backupOption(); backupOption();
} else { } else {
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.BERRY_POUCH); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.BERRY_POUCH);
doEventReward(); doEventReward();
} }
} }
@ -321,7 +321,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with
// At max stacks, give the first party pokemon a Shell Bell instead // At max stacks, give the first party pokemon a Shell Bell instead
backupOption(); backupOption();
} else { } else {
globalScene.phaseManager.unshiftNew("RewardPhase", modifierTypes.HEALING_CHARM); globalScene.phaseManager.unshiftNew("RewardPhase", allRewards.HEALING_CHARM);
doEventReward(); doEventReward();
} }

View File

@ -1,12 +1,12 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { leaveEncounterWithoutBattle, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; import { leaveEncounterWithoutBattle, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils";
import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } 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"; import { randSeedInt } from "#utils/common";
/** i18n namespace for encounter */ /** i18n namespace for encounter */
@ -59,23 +59,23 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose TMs // Choose TMs
const modifiers: ModifierTypeFunc[] = []; const rewards: RewardFunc[] = [];
let i = 0; let i = 0;
while (i < 5) { while (i < 5) {
// 2/2/1 weight on TM rarity // 2/2/1 weight on TM rarity
const roll = randSeedInt(5); const roll = randSeedInt(5);
if (roll < 2) { if (roll < 2) {
modifiers.push(modifierTypes.TM_COMMON); rewards.push(allRewards.TM_COMMON);
} else if (roll < 4) { } else if (roll < 4) {
modifiers.push(modifierTypes.TM_GREAT); rewards.push(allRewards.TM_GREAT);
} else { } else {
modifiers.push(modifierTypes.TM_ULTRA); rewards.push(allRewards.TM_ULTRA);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: modifiers, guaranteedRewardFuncs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -88,21 +88,21 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose Vitamins // Choose Vitamins
const modifiers: ModifierTypeFunc[] = []; const rewards: RewardFunc[] = [];
let i = 0; let i = 0;
while (i < 3) { while (i < 3) {
// 2/1 weight on base stat booster vs PP Up // 2/1 weight on base stat booster vs PP Up
const roll = randSeedInt(3); const roll = randSeedInt(3);
if (roll === 0) { if (roll === 0) {
modifiers.push(modifierTypes.PP_UP); rewards.push(allRewards.PP_UP);
} else { } else {
modifiers.push(modifierTypes.BASE_STAT_BOOSTER); rewards.push(allRewards.BASE_STAT_BOOSTER);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: modifiers, guaranteedRewardFuncs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -115,21 +115,21 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose X Items // Choose X Items
const modifiers: ModifierTypeFunc[] = []; const rewards: RewardFunc[] = [];
let i = 0; let i = 0;
while (i < 5) { while (i < 5) {
// 4/1 weight on base stat booster vs Dire Hit // 4/1 weight on base stat booster vs Dire Hit
const roll = randSeedInt(5); const roll = randSeedInt(5);
if (roll === 0) { if (roll === 0) {
modifiers.push(modifierTypes.DIRE_HIT); rewards.push(allRewards.DIRE_HIT);
} else { } else {
modifiers.push(modifierTypes.TEMP_STAT_STAGE_BOOSTER); rewards.push(allRewards.TEMP_STAT_STAGE_BOOSTER);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: modifiers, guaranteedRewardFuncs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -142,25 +142,25 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose Pokeballs // Choose Pokeballs
const modifiers: ModifierTypeFunc[] = []; const rewards: RewardFunc[] = [];
let i = 0; let i = 0;
while (i < 4) { while (i < 4) {
// 10/30/20/5 weight on pokeballs // 10/30/20/5 weight on pokeballs
const roll = randSeedInt(65); const roll = randSeedInt(65);
if (roll < 10) { if (roll < 10) {
modifiers.push(modifierTypes.POKEBALL); rewards.push(allRewards.POKEBALL);
} else if (roll < 40) { } else if (roll < 40) {
modifiers.push(modifierTypes.GREAT_BALL); rewards.push(allRewards.GREAT_BALL);
} else if (roll < 60) { } else if (roll < 60) {
modifiers.push(modifierTypes.ULTRA_BALL); rewards.push(allRewards.ULTRA_BALL);
} else { } else {
modifiers.push(modifierTypes.ROGUE_BALL); rewards.push(allRewards.ROGUE_BALL);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: modifiers, guaranteedRewardFuncs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();

View File

@ -1,15 +1,15 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 { MoveCategory } from "#enums/move-category";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import type { PlayerPokemon } from "#field/pokemon"; import type { PlayerPokemon } from "#field/pokemon";
import { generateRewardOption } from "#items/reward-utils";
import type { PokemonMove } from "#moves/pokemon-move"; import type { PokemonMove } from "#moves/pokemon-move";
import { import {
generateModifierTypeOption,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
selectPokemonForOption, selectPokemonForOption,
setEncounterExp, setEncounterExp,
@ -96,15 +96,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.DIRE_HIT)!, generateRewardOption(allRewards.DIRE_HIT)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!, generateRewardOption(allRewards.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: modifiers, guaranteedRewardOptions: modifiers,
fillRemaining: false, fillRemaining: false,
}); });
} }
@ -144,15 +144,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.DIRE_HIT)!, generateRewardOption(allRewards.DIRE_HIT)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!, generateRewardOption(allRewards.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: modifiers, guaranteedRewardOptions: modifiers,
fillRemaining: false, fillRemaining: false,
}); });
} }
@ -192,15 +192,15 @@ export const FieldTripEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
if (encounter.misc.correctMove) { if (encounter.misc.correctMove) {
const modifiers = [ const modifiers = [
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!,
generateModifierTypeOption(modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(modifierTypes.GREAT_BALL)!, generateRewardOption(allRewards.GREAT_BALL)!,
generateModifierTypeOption(modifierTypes.IV_SCANNER)!, generateRewardOption(allRewards.IV_SCANNER)!,
generateModifierTypeOption(modifierTypes.RARER_CANDY)!, generateRewardOption(allRewards.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: modifiers, guaranteedRewardOptions: modifiers,
fillRemaining: false, fillRemaining: false,
}); });
} }

View File

@ -250,7 +250,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
} }
} }
// No rewards // No allRewards
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
}, },
) )

View File

@ -1,14 +1,16 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 { Pokemon } from "#field/pokemon";
import type { ModifierTypeOption } from "#modifiers/modifier-type"; import type { RewardOption, TrainerItemReward } from "#items/reward";
import { getPlayerModifierTypeOptions, regenerateModifierPoolThresholds } from "#modifiers/modifier-type"; import { generatePlayerRewardOptions, generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils";
import { isTmReward } from "#items/reward-utils";
import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils"; import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { import {
@ -89,18 +91,18 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
// Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER // Waves 10-40 GREAT, 60-120 ULTRA, 120-160 ROGUE, 160-180 MASTER
const tier = const tier =
globalScene.currentBattle.waveIndex > 160 globalScene.currentBattle.waveIndex > 160
? RewardTier.MASTER ? RarityTier.MASTER
: globalScene.currentBattle.waveIndex > 120 : globalScene.currentBattle.waveIndex > 120
? RewardTier.ROGUE ? RarityTier.ROGUE
: globalScene.currentBattle.waveIndex > 40 : globalScene.currentBattle.waveIndex > 40
? RewardTier.ULTRA ? RarityTier.ULTRA
: RewardTier.GREAT; : RarityTier.GREAT;
regenerateModifierPoolThresholds(globalScene.getPlayerParty(), ModifierPoolType.PLAYER, 0); generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), globalScene.getPlayerParty(), 0);
let item: ModifierTypeOption | null = null; let item: RewardOption | null = null;
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward // TMs and Candy Jar excluded from possible allRewards as they're too swingy in value for a singular item reward
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") { while (!item || isTmReward(item.type) || (item.type as TrainerItemReward).itemId === TrainerItemId.CANDY_JAR) {
item = getPlayerModifierTypeOptions(1, globalScene.getPlayerParty(), [], { item = generatePlayerRewardOptions(1, globalScene.getPlayerParty(), [], {
guaranteedModifierTiers: [tier], guaranteedRarityTiers: [tier],
allowLuckUpgrades: false, allowLuckUpgrades: false,
})[0]; })[0];
} }
@ -151,9 +153,9 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
async () => { async () => {
// Pick battle // Pick battle
// Pokemon will randomly boost 1 stat by 2 stages // 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({ setEncounterRewards({
guaranteedModifierTypeOptions: [item], guaranteedRewardOptions: [item],
fillRemaining: false, fillRemaining: false,
}); });
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
@ -175,9 +177,9 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
.withOptionPhase(async () => { .withOptionPhase(async () => {
// Pick steal // Pick steal
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
const item = globalScene.currentBattle.mysteryEncounter!.misc as ModifierTypeOption; const item = globalScene.currentBattle.mysteryEncounter!.misc as RewardOption;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: [item], guaranteedRewardOptions: [item],
fillRemaining: false, fillRemaining: false,
}); });
@ -199,7 +201,7 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder.
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; 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 { SpeciesFormChangeActiveTrigger } from "#data/form-change-triggers";
import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball"; import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball";
import { FieldPosition } from "#enums/field-position"; import { FieldPosition } from "#enums/field-position";
@ -160,7 +160,7 @@ export const FunAndGamesEncounter: MysteryEncounter = MysteryEncounterBuilder.wi
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
await transitionMysteryEncounterIntroVisuals(true, true); await transitionMysteryEncounterIntroVisuals(true, true);
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
@ -281,21 +281,21 @@ function handleNextTurn() {
if (healthRatio < 0.03) { if (healthRatio < 0.03) {
// Grand prize // Grand prize
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.MULTI_LENS], guaranteedRewardFuncs: [allRewards.MULTI_LENS],
fillRemaining: false, fillRemaining: false,
}); });
resultMessageKey = `${namespace}:best_result`; resultMessageKey = `${namespace}:best_result`;
} else if (healthRatio < 0.15) { } else if (healthRatio < 0.15) {
// 2nd prize // 2nd prize
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.SCOPE_LENS], guaranteedRewardFuncs: [allRewards.SCOPE_LENS],
fillRemaining: false, fillRemaining: false,
}); });
resultMessageKey = `${namespace}:great_result`; resultMessageKey = `${namespace}:great_result`;
} else if (healthRatio < 0.33) { } else if (healthRatio < 0.33) {
// 3rd prize // 3rd prize
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.WIDE_LENS], guaranteedRewardFuncs: [allRewards.WIDE_LENS],
fillRemaining: false, fillRemaining: false,
}); });
resultMessageKey = `${namespace}:good_result`; resultMessageKey = `${namespace}:good_result`;

View File

@ -8,22 +8,23 @@ import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball";
import type { PokemonSpecies } from "#data/pokemon-species"; import type { PokemonSpecies } from "#data/pokemon-species";
import { getTypeRgb } from "#data/type"; import { getTypeRgb } from "#data/type";
import { HeldItemCategoryId, type HeldItemId, isItemInCategory } from "#enums/held-item-id"; 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 { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import type { PokeballType } from "#enums/pokeball"; 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 { SpeciesId } from "#enums/species-id";
import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerSlot } from "#enums/trainer-slot";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { doShinySparkleAnim } from "#field/anims"; import { doShinySparkleAnim } from "#field/anims";
import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { EnemyPokemon } 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 { 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 { PokemonMove } from "#moves/pokemon-move";
import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { getEncounterText, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import { import {
@ -413,26 +414,26 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon; const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check tier of the traded item, the received item will be one tier up // 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 // Increment tier by 1
if (tier < RewardTier.MASTER) { if (tier < RarityTier.MASTER) {
tier++; tier++;
} }
regenerateModifierPoolThresholds(party, ModifierPoolType.PLAYER, 0); generateRewardPoolWeights(getRewardPoolForType(RewardPoolType.PLAYER), party, 0);
let item: ModifierTypeOption | null = null; let item: RewardOption | null = null;
// TMs excluded from possible rewards // TMs excluded from possible allRewards
while (!item || item.type.id.includes("TM_")) { while (!item || isTmReward(item.type)) {
item = getPlayerModifierTypeOptions(1, party, [], { item = generatePlayerRewardOptions(1, party, [], {
guaranteedModifierTiers: [tier], guaranteedRarityTiers: [tier],
allowLuckUpgrades: false, allowLuckUpgrades: false,
})[0]; })[0];
} }
encounter.setDialogueToken("itemName", item.type.name); encounter.setDialogueToken("itemName", item.type.name);
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: [item], guaranteedRewardOptions: [item],
fillRemaining: false, fillRemaining: false,
}); });
@ -458,7 +459,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,10 +1,10 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength"; 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 type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { initBattleWithEnemyConfig, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils"; import { initBattleWithEnemyConfig, setEncounterRewards } from "#mystery-encounters/encounter-phase-utils";
import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
@ -147,7 +147,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], guaranteedRewardFuncs: [allRewards.TM_COMMON, allRewards.TM_GREAT, allRewards.MEMORY_MUSHROOM],
fillRemaining: true, fillRemaining: true,
}); });
@ -175,7 +175,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTiers: [RewardTier.ULTRA, RewardTier.ULTRA, RewardTier.GREAT, RewardTier.GREAT], guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT],
fillRemaining: true, fillRemaining: true,
}); });
@ -206,7 +206,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
encounter.expMultiplier = 0.9; encounter.expMultiplier = 0.9;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ROGUE, RewardTier.ULTRA, RewardTier.GREAT], guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.GREAT],
fillRemaining: true, fillRemaining: true,
}); });

View File

@ -4,7 +4,7 @@ import { MoveId } from "#enums/move-id";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 { SpeciesId } from "#enums/species-id";
import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-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) { if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT) {
// Choose between 2 COMMON / 2 GREAT tier items (20%) // Choose between 2 COMMON / 2 GREAT tier items (20%)
setEncounterRewards({ 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`); queueEncounterMessage(`${namespace}:option.1.normal`);
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT) {
// Choose between 3 ULTRA tier items (30%) // Choose between 3 ULTRA tier items (30%)
setEncounterRewards({ 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`); queueEncounterMessage(`${namespace}:option.1.good`);
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
} else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) { } else if (roll >= RAND_LENGTH - COMMON_REWARDS_PERCENT - ULTRA_REWARDS_PERCENT - ROGUE_REWARDS_PERCENT) {
// Choose between 2 ROGUE tier items (10%) // Choose between 2 ROGUE tier items (10%)
setEncounterRewards({ 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`); queueEncounterMessage(`${namespace}:option.1.great`);
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
} else if ( } else if (
@ -168,9 +168,9 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
) { ) {
// Choose 1 MASTER tier item (5%) // Choose 1 MASTER tier item (5%)
setEncounterRewards({ 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`); queueEncounterMessage(`${namespace}:option.1.amazing`);
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
} else { } else {
@ -208,7 +208,7 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -125,7 +125,7 @@ export const SafariZoneEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -227,7 +227,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBui
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene"; 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 { CustomPokemonData } from "#data/pokemon-data";
import { AiType } from "#enums/ai-type"; import { AiType } from "#enums/ai-type";
import { BattlerIndex } from "#enums/battler-index"; import { BattlerIndex } from "#enums/battler-index";
@ -116,7 +116,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
// Pick battle // Pick battle
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], guaranteedRewardFuncs: [allRewards.LEFTOVERS],
fillRemaining: true, fillRemaining: true,
}); });
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
@ -163,7 +163,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
// Steal the Snorlax's Leftovers // Steal the Snorlax's Leftovers
const instance = globalScene.currentBattle.mysteryEncounter!; const instance = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], guaranteedRewardFuncs: [allRewards.LEFTOVERS],
fillRemaining: false, fillRemaining: false,
}); });
// Snorlax exp to Pokemon that did the stealing // Snorlax exp to Pokemon that did the stealing

View File

@ -1,7 +1,7 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; 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 { BattlerTagType } from "#enums/battler-tag-type";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -13,11 +13,10 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { getBiomeKey } from "#field/arena"; import { getBiomeKey } from "#field/arena";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { EnemyPokemon } 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 { queueEncounterMessage, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { import {
generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
setEncounterExp, setEncounterExp,
setEncounterRewards, setEncounterRewards,
@ -34,6 +33,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
import { MoneyRequirement, WaveModulusRequirement } from "#mystery-encounters/mystery-encounter-requirements"; import { MoneyRequirement, WaveModulusRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import { PokemonData } from "#system/pokemon-data"; import { PokemonData } from "#system/pokemon-data";
import { randSeedInt } from "#utils/common"; import { randSeedInt } from "#utils/common";
import { getPartyLuckValue } from "#utils/party";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounters/teleportingHijinks"; const namespace = "mysteryEncounters/teleportingHijinks";
@ -173,10 +173,10 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui
], ],
}; };
const magnet = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [PokemonType.STEEL])!; const magnet = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.STEEL])!;
const metalCoat = generateModifierTypeOption(modifierTypes.ATTACK_TYPE_BOOSTER, [PokemonType.ELECTRIC])!; const metalCoat = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.ELECTRIC])!;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: [magnet, metalCoat], guaranteedRewardOptions: [magnet, metalCoat],
fillRemaining: true, fillRemaining: true,
}); });
await transitionMysteryEncounterIntroVisuals(true, true); await transitionMysteryEncounterIntroVisuals(true, true);

View File

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { speciesStarterCosts } from "#balance/starters"; import { speciesStarterCosts } from "#balance/starters";
import { modifierTypes } from "#data/data-lists"; import { allRewards } from "#data/data-lists";
import type { IEggOptions } from "#data/egg"; import type { IEggOptions } from "#data/egg";
import { getPokeballTintColor } from "#data/pokeball"; import { getPokeballTintColor } from "#data/pokeball";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
@ -294,7 +294,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs); const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], guaranteedRewardFuncs: [allRewards.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,
@ -304,7 +304,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon1); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon1);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg allRewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
@ -353,7 +353,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs); const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], guaranteedRewardFuncs: [allRewards.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,
@ -363,7 +363,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon2); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon2);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg allRewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
@ -412,7 +412,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs); const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTypeFuncs: [modifierTypes.SOOTHE_BELL], guaranteedRewardFuncs: [allRewards.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,
@ -422,7 +422,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
// Remove all Pokemon from the party except the chosen Pokemon // Remove all Pokemon from the party except the chosen Pokemon
removePokemonFromPartyAndStoreHeldItems(encounter, pokemon3); removePokemonFromPartyAndStoreHeldItems(encounter, pokemon3);
// Configure outro dialogue for egg rewards // Configure outro dialogue for egg allRewards
encounter.dialogue.outro = [ encounter.dialogue.outro = [
{ {
speaker: trainerNameKey, speaker: trainerNameKey,
@ -640,7 +640,7 @@ function onGameOver() {
const chosenPokemon = encounter.misc.chosenPokemon; const chosenPokemon = encounter.misc.chosenPokemon;
chosenPokemon.friendship = 0; chosenPokemon.friendship = 0;
// Clear all rewards that would have been earned // Clear all allRewards that would have been earned
encounter.doEncounterRewards = undefined; encounter.doEncounterRewards = undefined;
// Set flag that encounter was failed // Set flag that encounter was failed

View File

@ -238,7 +238,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -1,6 +1,6 @@
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 { CustomPokemonData } from "#data/pokemon-data";
import { BattlerIndex } from "#enums/battler-index"; import { BattlerIndex } from "#enums/battler-index";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
@ -193,7 +193,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
// Pick battle // Pick battle
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], guaranteedRewardFuncs: [allRewards.SOUL_DEW],
fillRemaining: true, fillRemaining: true,
}); });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(

View File

@ -1,7 +1,7 @@
import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { applyAbAttrs } from "#abilities/apply-ab-attrs";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene"; 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 { SpeciesFormChangeAbilityTrigger } from "#data/form-change-triggers";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { BattlerTagType } from "#enums/battler-tag-type"; 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 { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { RewardTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { generateRewardOption } from "#items/reward-utils";
import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils"; import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import { import {
generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
setEncounterRewards, setEncounterRewards,
@ -117,7 +117,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter
], ],
}, },
async () => { 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 () => { globalScene.currentBattle.mysteryEncounter!.doContinueEncounter = async () => {
await endTrainerBattleAndShowDialogue(); 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 // Refuse the challenge, they full heal the party and give the player a Rarer Candy
globalScene.phaseManager.unshiftNew("PartyHealPhase", true); globalScene.phaseManager.unshiftNew("PartyHealPhase", true);
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], guaranteedRewardFuncs: [allRewards.RARER_CANDY],
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -156,17 +156,17 @@ async function spawnNextTrainerOrEndEncounter() {
await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`);
// Give 10x Voucher // Give 10x Voucher
const newModifier = modifierTypes.VOUCHER_PREMIUM().newModifier(); const reward = allRewards.VOUCHER_PREMIUM();
globalScene.addModifier(newModifier); globalScene.applyReward(reward, {});
globalScene.playSound("item_fanfare"); 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`); await showEncounterDialogue(`${namespace}:victory_2`, `${namespace}:speaker`);
globalScene.ui.clearText(); // Clears "Winstrate" title from screen as rewards get animated in globalScene.ui.clearText(); // Clears "Winstrate" title from screen as allRewards get animated in
const machoBrace = generateModifierTypeOption(modifierTypes.MYSTERY_ENCOUNTER_MACHO_BRACE)!; const machoBrace = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_MACHO_BRACE)!;
machoBrace.type.tier = RewardTier.MASTER; machoBrace.type.tier = RarityTier.MASTER;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeOptions: [machoBrace], guaranteedRewardOptions: [machoBrace],
fillRemaining: false, fillRemaining: false,
}); });
encounter.doContinueEncounter = undefined; encounter.doContinueEncounter = undefined;

View File

@ -346,7 +346,7 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
], ],
}, },
async () => { async () => {
// Leave encounter with no rewards or exp // Leave encounter with no allRewards or exp
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
return true; return true;
}, },

View File

@ -8,7 +8,7 @@ import { MoveUseMode } from "#enums/move-use-mode";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 { SpeciesId } from "#enums/species-id";
import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerItemId } from "#enums/trainer-item-id";
import { assignItemToFirstFreePokemon } from "#items/item-utility"; import { assignItemToFirstFreePokemon } from "#items/item-utility";
@ -167,7 +167,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTiers: [RewardTier.ROGUE, RewardTier.ROGUE, RewardTier.ULTRA, RewardTier.GREAT], guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.GREAT],
fillRemaining: true, fillRemaining: true,
}); });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(

View File

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene"; 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 { getLevelTotalExp } from "#data/exp";
import type { PokemonSpecies } from "#data/pokemon-species"; import type { PokemonSpecies } from "#data/pokemon-species";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
@ -11,7 +11,7 @@ import { Nature } from "#enums/nature";
import { PartyMemberStrength } from "#enums/party-member-strength"; import { PartyMemberStrength } from "#enums/party-member-strength";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { PokemonType } from "#enums/pokemon-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 { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import type { PlayerPokemon, Pokemon } from "#field/pokemon";
@ -218,12 +218,12 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
await doNewTeamPostProcess(transformations); await doNewTeamPostProcess(transformations);
setEncounterRewards({ setEncounterRewards({
guaranteedModifierTypeFuncs: [ guaranteedRewardFuncs: [
modifierTypes.MEMORY_MUSHROOM, allRewards.MEMORY_MUSHROOM,
modifierTypes.ROGUE_BALL, allRewards.ROGUE_BALL,
modifierTypes.MINT, allRewards.MINT,
modifierTypes.MINT, allRewards.MINT,
modifierTypes.MINT, allRewards.MINT,
], ],
fillRemaining: false, fillRemaining: false,
}); });
@ -242,7 +242,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
], ],
}, },
async () => { async () => {
// Battle your "future" team for some item rewards // Battle your "future" team for some item allRewards
const transformations: PokemonTransformation[] = const transformations: PokemonTransformation[] =
globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations; globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
@ -293,7 +293,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
}; };
const onBeforeRewards = () => { 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 // One random pokemon will get its passive unlocked
const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive); const passiveDisabledPokemon = globalScene.getPlayerParty().filter(p => !p.passive);
if (passiveDisabledPokemon?.length > 0) { if (passiveDisabledPokemon?.length > 0) {
@ -306,13 +306,13 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
setEncounterRewards( setEncounterRewards(
{ {
guaranteedModifierTiers: [ guaranteedRarityTiers: [
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ROGUE, RarityTier.ROGUE,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.ULTRA, RarityTier.ULTRA,
RewardTier.GREAT, RarityTier.GREAT,
RewardTier.GREAT, RarityTier.GREAT,
], ],
fillRemaining: false, fillRemaining: false,
}, },

View File

@ -173,11 +173,11 @@ export class MysteryEncounter implements IMysteryEncounter {
onVisualsStart?: () => boolean; onVisualsStart?: () => boolean;
/** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */ /** Event triggered prior to {@linkcode CommandPhase}, during {@linkcode TurnInitPhase} */
onTurnStart?: () => boolean; onTurnStart?: () => boolean;
/** Event prior to any rewards logic in {@linkcode MysteryEncounterRewardsPhase} */ /** Event prior to any allRewards logic in {@linkcode MysteryEncounterRewardsPhase} */
onRewards?: () => Promise<void>; onRewards?: () => Promise<void>;
/** 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; doEncounterExp?: () => boolean;
/** Will provide the player a rewards shop for that wave */ /** Will provide the player a allRewards shop for that wave */
doEncounterRewards?: () => boolean; doEncounterRewards?: () => boolean;
/** Will execute callback during VictoryPhase of a continuousEncounter */ /** Will execute callback during VictoryPhase of a continuousEncounter */
doContinueEncounter?: () => Promise<void>; doContinueEncounter?: () => Promise<void>;
@ -237,10 +237,10 @@ export class MysteryEncounter implements IMysteryEncounter {
encounterMode: MysteryEncounterMode; encounterMode: MysteryEncounterMode;
/** /**
* Flag for checking if it's the first time a shop is being shown for an encounter. * 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) * 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) * 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 // Reset any dirty flags or encounter data
this.startOfBattleEffectsComplete = false; this.startOfBattleEffectsComplete = false;
this.lockEncounterRewardTiers = true; this.lockEncounterRarityTiers = true;
this.dialogueTokens = {}; this.dialogueTokens = {};
this.enemyPartyConfigs = []; this.enemyPartyConfigs = [];
this.startOfBattleEffects = []; this.startOfBattleEffects = [];
@ -561,7 +561,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
continuousEncounter = false; continuousEncounter = false;
catchAllowed = false; catchAllowed = false;
fleeAllowed = true; fleeAllowed = true;
lockEncounterRewardTiers = false; lockEncounterRarityTiers = false;
startOfBattleEffectsComplete = false; startOfBattleEffectsComplete = false;
hasBattleAnimationsWithoutTargets = false; hasBattleAnimationsWithoutTargets = false;
skipEnemyBattleTurns = false; skipEnemyBattleTurns = false;
@ -928,13 +928,13 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
} }
/** /**
* Can set custom encounter rewards via this callback function * Can set custom encounter allRewards via this callback function
* If rewards are always deterministic for an encounter, this is a good way to set them * 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. * 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 * 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 rewards phase of the encounter * @param doEncounterRewards Synchronous callback function to perform during allRewards phase of the encounter
* @returns * @returns
*/ */
withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> { withRewards(doEncounterRewards: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
@ -945,10 +945,10 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* Can set custom encounter exp via this callback function * Can set custom encounter exp via this callback function
* If exp always deterministic for an encounter, this is a good way to set them * 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. * 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 * 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 rewards phase of the encounter * @param doEncounterExp Synchronous callback function to perform during allRewards phase of the encounter
* @returns * @returns
*/ */
withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> { withExp(doEncounterExp: () => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {

View File

@ -5,7 +5,6 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { BiomePoolTier, biomeLinks } from "#balance/biomes"; import { BiomePoolTier, biomeLinks } from "#balance/biomes";
import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims"; import { initMoveAnim, loadMoveAnimAssets } from "#data/battle-anims";
import { modifierTypes } from "#data/data-lists";
import type { IEggOptions } from "#data/egg"; import type { IEggOptions } from "#data/egg";
import { Egg } from "#data/egg"; import { Egg } from "#data/egg";
import type { Gender } from "#data/gender"; 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 type { BattlerTagType } from "#enums/battler-tag-type";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
import { FieldPosition } from "#enums/field-position"; import { FieldPosition } from "#enums/field-position";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import type { MoveId } from "#enums/move-id"; import type { MoveId } from "#enums/move-id";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import type { Nature } from "#enums/nature"; import type { Nature } from "#enums/nature";
@ -31,8 +29,7 @@ import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { EnemyPokemon } from "#field/pokemon"; import { EnemyPokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer"; import { Trainer } from "#field/trainer";
import type { HeldItemConfiguration } from "#items/held-item-data-types"; import type { HeldItemConfiguration } from "#items/held-item-data-types";
import type { CustomModifierSettings, ModifierType } from "#modifiers/modifier-type"; import type { CustomRewardSettings } from "#items/reward-pool-utils";
import { getPartyLuckValue, ModifierTypeGenerator, ModifierTypeOption } from "#modifiers/modifier-type";
import { PokemonMove } from "#moves/pokemon-move"; import { PokemonMove } from "#moves/pokemon-move";
import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils"; import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { MysteryEncounterOption } from "#mystery-encounters/mystery-encounter-option"; 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 type { PartyOption, PokemonSelectFilter } from "#ui/party-ui-handler";
import { PartyUiMode } from "#ui/party-ui-handler"; import { PartyUiMode } from "#ui/party-ui-handler";
import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common"; import { coerceArray, isNullOrUndefined, randomString, randSeedInt, randSeedItem } from "#utils/common";
import { getPartyLuckValue } from "#utils/party";
import { getPokemonSpecies } from "#utils/pokemon-utils"; import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next"; 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 * 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 * @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 * Will initialize reward phases to follow the mystery encounter
* Can have shop displayed or skipped * 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 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}) * @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( export function setEncounterRewards(
customShopRewards?: CustomModifierSettings, customShopRewards?: CustomRewardSettings,
eggRewards?: IEggOptions[], eggRewards?: IEggOptions[],
preRewardsCallback?: Function, preRewardsCallback?: Function,
) { ) {
@ -809,8 +768,8 @@ export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSet
/** /**
* Can be used to exit an encounter without any battles or followup * 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 * 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 rewards but healing items are available * @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) * @param encounterMode - Can set custom encounter mode if necessary (may be required for forcing Pokemon to return before next phase)
*/ */
export function leaveEncounterWithoutBattle( export function leaveEncounterWithoutBattle(

View File

@ -4,8 +4,8 @@ import { globalScene } from "#app/global-scene";
import { randSeedInt } from "#app/utils/common"; import { randSeedInt } from "#app/utils/common";
import { BattleType } from "#enums/battle-type"; import { BattleType } from "#enums/battle-type";
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
import { ModifierTier } from "#enums/modifier-tier";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { RarityTier } from "#enums/reward-tier";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant"; import { TrainerVariant } from "#enums/trainer-variant";
@ -45,8 +45,8 @@ export const classicFixedBattles: FixedBattleConfigs = {
globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
), ),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
[ClassicFixedBossWaves.EVIL_GRUNT_1]: new FixedBattleConfig() [ClassicFixedBossWaves.EVIL_GRUNT_1]: new FixedBattleConfig()
@ -77,8 +77,8 @@ export const classicFixedBattles: FixedBattleConfigs = {
globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
), ),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.GREAT, RarityTier.GREAT],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
[ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig() [ClassicFixedBossWaves.EVIL_GRUNT_2]: new FixedBattleConfig()
@ -150,8 +150,8 @@ export const classicFixedBattles: FixedBattleConfigs = {
globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
), ),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], guaranteedRarityTiers: [RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
[ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig() [ClassicFixedBossWaves.EVIL_GRUNT_4]: new FixedBattleConfig()
@ -212,14 +212,8 @@ export const classicFixedBattles: FixedBattleConfigs = {
TrainerType.PENNY, TrainerType.PENNY,
]), ]),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.ULTRA, RarityTier.ULTRA],
ModifierTier.ROGUE,
ModifierTier.ROGUE,
ModifierTier.ULTRA,
ModifierTier.ULTRA,
ModifierTier.ULTRA,
],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
[ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig() [ClassicFixedBossWaves.RIVAL_5]: new FixedBattleConfig()
@ -231,14 +225,8 @@ export const classicFixedBattles: FixedBattleConfigs = {
globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
), ),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ROGUE, RarityTier.ULTRA, RarityTier.ULTRA],
ModifierTier.ROGUE,
ModifierTier.ROGUE,
ModifierTier.ROGUE,
ModifierTier.ULTRA,
ModifierTier.ULTRA,
],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig() [ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig()
@ -258,14 +246,14 @@ export const classicFixedBattles: FixedBattleConfigs = {
TrainerType.PENNY_2, TrainerType.PENNY_2,
]), ]),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [
ModifierTier.ROGUE, RarityTier.ROGUE,
ModifierTier.ROGUE, RarityTier.ROGUE,
ModifierTier.ULTRA, RarityTier.ULTRA,
ModifierTier.ULTRA, RarityTier.ULTRA,
ModifierTier.ULTRA, RarityTier.ULTRA,
ModifierTier.ULTRA, RarityTier.ULTRA,
], ],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),
@ -362,14 +350,14 @@ export const classicFixedBattles: FixedBattleConfigs = {
globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, globalScene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT,
), ),
) )
.setCustomModifierRewards({ .setCustomRewards({
guaranteedModifierTiers: [ guaranteedRarityTiers: [
ModifierTier.ROGUE, RarityTier.ROGUE,
ModifierTier.ROGUE, RarityTier.ROGUE,
ModifierTier.ULTRA, RarityTier.ULTRA,
ModifierTier.ULTRA, RarityTier.ULTRA,
ModifierTier.GREAT, RarityTier.GREAT,
ModifierTier.GREAT, RarityTier.GREAT,
], ],
allowLuckUpgrades: false, allowLuckUpgrades: false,
}), }),

View File

@ -3,7 +3,7 @@ import { globalScene } from "#app/global-scene";
import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions"; import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions";
import { signatureSpecies } from "#balance/signature-species"; import { signatureSpecies } from "#balance/signature-species";
import { tmSpecies } from "#balance/tms"; 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 { doubleBattleDialogue } from "#data/double-battle-dialogue";
import { Gender } from "#data/gender"; import { Gender } from "#data/gender";
import type { PokemonSpecies, PokemonSpeciesFilter } from "#data/pokemon-species"; import type { PokemonSpecies, PokemonSpeciesFilter } from "#data/pokemon-species";
@ -31,7 +31,7 @@ import {
TrainerPartyTemplate, TrainerPartyTemplate,
trainerPartyTemplates, trainerPartyTemplates,
} from "#trainers/trainer-party-template"; } from "#trainers/trainer-party-template";
import type { ModifierTypeFunc } from "#types/modifier-types"; import type { RewardFunc } from "#types/rewards";
import type { import type {
GenAIFunc, GenAIFunc,
GenTrainerItemsFunc, GenTrainerItemsFunc,
@ -113,9 +113,9 @@ export class TrainerConfig {
public femaleEncounterBgm: string; public femaleEncounterBgm: string;
public doubleEncounterBgm: string; public doubleEncounterBgm: string;
public victoryBgm: string; public victoryBgm: string;
public genModifiersFunc: GenTrainerItemsFunc; public genTrainerItemsFunc: GenTrainerItemsFunc;
public genAIFuncs: GenAIFunc[] = []; public genAIFuncs: GenAIFunc[] = [];
public modifierRewardFuncs: ModifierTypeFunc[] = []; public rewardFuncs: RewardFunc[] = [];
public partyTemplates: TrainerPartyTemplate[]; public partyTemplates: TrainerPartyTemplate[];
public partyTemplateFunc: PartyTemplateFunc; public partyTemplateFunc: PartyTemplateFunc;
public partyMemberFuncs: PartyMemberFuncs = {}; public partyMemberFuncs: PartyMemberFuncs = {};
@ -465,8 +465,8 @@ export class TrainerConfig {
return this; return this;
} }
setGenModifiersFunc(genModifiersFunc: GenTrainerItemsFunc): TrainerConfig { setGenTrainerItemsFunc(genTrainerItemsFunc: GenTrainerItemsFunc): TrainerConfig {
this.genModifiersFunc = genModifiersFunc; this.genTrainerItemsFunc = genTrainerItemsFunc;
return this; 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). * @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 * @returns this
*/ */
setRandomTeraModifiers(count: () => number, slot?: number): TrainerConfig { setRandomTeraType(count: () => number, slot?: number): TrainerConfig {
this.genAIFuncs.push((party: EnemyPokemon[]) => { this.genAIFuncs.push((party: EnemyPokemon[]) => {
const shedinjaCanTera = !this.hasSpecialtyType() || this.specialtyType === PokemonType.BUG; // Better to check one time than 6 const shedinjaCanTera = !this.hasSpecialtyType() || this.specialtyType === PokemonType.BUG; // Better to check one time than 6
const partyMemberIndexes = new Array(party.length) const partyMemberIndexes = new Array(party.length)
@ -507,23 +507,11 @@ export class TrainerConfig {
return this; return this;
} }
// function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: Type[]): PersistentModifier[] { setRewardFuncs(...rewardFuncs: (() => RewardFunc)[]): TrainerConfig {
// const ret: PersistentModifier[] = []; this.rewardFuncs = rewardFuncs.map(func => () => {
// const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i); const rewardFunc = func();
// for (let t = 0; t < Math.min(count, party.length); t++) { const reward = rewardFunc();
// const randomIndex = Utils.randSeedItem(partyMemberIndexes); return reward;
// 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;
}); });
return this; return this;
} }
@ -689,7 +677,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_gym"); this.setBattleBgm("battle_unova_gym");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setRandomTeraModifiers( this.setRandomTeraType(
() => (ignoreMinTeraWave || globalScene.currentBattle.waveIndex >= GYM_LEADER_TERA_WAVE ? 1 : 0), () => (ignoreMinTeraWave || globalScene.currentBattle.waveIndex >= GYM_LEADER_TERA_WAVE ? 1 : 0),
teraSlot, teraSlot,
); );
@ -750,7 +738,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_elite"); this.setBattleBgm("battle_unova_elite");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setRandomTeraModifiers(() => 1, teraSlot); this.setRandomTeraType(() => 1, teraSlot);
return this; return this;
} }
@ -927,11 +915,11 @@ export class TrainerConfig {
clone = this.battleBgm ? clone.setBattleBgm(this.battleBgm) : clone; clone = this.battleBgm ? clone.setBattleBgm(this.battleBgm) : clone;
clone = this.encounterBgm ? clone.setEncounterBgm(this.encounterBgm) : clone; clone = this.encounterBgm ? clone.setEncounterBgm(this.encounterBgm) : clone;
clone = this.victoryBgm ? clone.setVictoryBgm(this.victoryBgm) : 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 // Clones array instead of passing ref
clone.modifierRewardFuncs = this.modifierRewardFuncs.slice(0); clone.rewardFuncs = this.rewardFuncs.slice(0);
} }
if (this.partyTemplates) { if (this.partyTemplates) {
@ -4443,9 +4431,9 @@ export const trainerConfigs: TrainerConfigs = {
.setBattleBgm("battle_rival") .setBattleBgm("battle_rival")
.setMixedBattleBgm("battle_rival") .setMixedBattleBgm("battle_rival")
.setPartyTemplates(trainerPartyTemplates.RIVAL) .setPartyTemplates(trainerPartyTemplates.RIVAL)
.setModifierRewardFuncs( .setRewardFuncs(
() => modifierTypes.SUPER_EXP_CHARM, () => allRewards.SUPER_EXP_CHARM,
() => modifierTypes.EXP_SHARE, () => allRewards.EXP_SHARE,
) )
.setPartyMemberFunc( .setPartyMemberFunc(
0, 0,
@ -4513,7 +4501,7 @@ export const trainerConfigs: TrainerConfigs = {
.setBattleBgm("battle_rival") .setBattleBgm("battle_rival")
.setMixedBattleBgm("battle_rival") .setMixedBattleBgm("battle_rival")
.setPartyTemplates(trainerPartyTemplates.RIVAL_2) .setPartyTemplates(trainerPartyTemplates.RIVAL_2)
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE) .setRewardFuncs(() => allRewards.EXP_SHARE)
.setPartyMemberFunc( .setPartyMemberFunc(
0, 0,
getRandomPartyMemberFunc( getRandomPartyMemberFunc(
@ -4666,7 +4654,7 @@ export const trainerConfigs: TrainerConfigs = {
.setBattleBgm("battle_rival_2") .setBattleBgm("battle_rival_2")
.setMixedBattleBgm("battle_rival_2") .setMixedBattleBgm("battle_rival_2")
.setPartyTemplates(trainerPartyTemplates.RIVAL_4) .setPartyTemplates(trainerPartyTemplates.RIVAL_4)
.setModifierRewardFuncs(() => modifierTypes.TERA_ORB) .setRewardFuncs(() => allRewards.TERA_ORB)
.setPartyMemberFunc( .setPartyMemberFunc(
0, 0,
getRandomPartyMemberFunc( getRandomPartyMemberFunc(

View File

@ -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<typeof HeldItemEffect>;

View File

@ -1,3 +1,5 @@
import type { EnumValues } from "#types/enum-types";
// TODO: make category the lower 2 bytes // TODO: make category the lower 2 bytes
export const HeldItemId = { export const HeldItemId = {
NONE: 0x0000, NONE: 0x0000,
@ -92,19 +94,24 @@ export const HeldItemId = {
GIMMIGHOUL_EVO_TRACKER: 0x0A01, GIMMIGHOUL_EVO_TRACKER: 0x0A01,
} as const; } as const;
export type HeldItemId = (typeof HeldItemId)[keyof typeof HeldItemId]; export type HeldItemId = EnumValues<typeof HeldItemId>;
type HeldItemNameMap = {
[k in HeldItemName as (typeof HeldItemId)[k]]: k
}
type HeldItemName = keyof typeof HeldItemId; type HeldItemName = keyof typeof HeldItemId;
type HeldItemValue = typeof HeldItemId[HeldItemName]; // equivalent to `HeldItemId`
// Use a type-safe reducer to force number keys and values /** `const object` mapping all held item IDs to their respective names. */
export const HeldItemNames: Record<HeldItemValue, HeldItemName> = Object.entries(HeldItemId).reduce( // 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, [key, value]) => {
acc[value as HeldItemValue] = key as HeldItemName; acc[value] = key;
return acc; return acc;
}, },
{} as Record<HeldItemValue, HeldItemName> {}
); )) as HeldItemNameMap;
export const HeldItemCategoryId = { export const HeldItemCategoryId = {
NONE: 0x0000, NONE: 0x0000,
@ -120,7 +127,7 @@ export const HeldItemCategoryId = {
EVO_TRACKER: 0x0A00, EVO_TRACKER: 0x0A00,
} as const; } as const;
export type HeldItemCategoryId = (typeof HeldItemCategoryId)[keyof typeof HeldItemCategoryId]; export type HeldItemCategoryId = EnumValues<typeof HeldItemCategoryId>;
const ITEM_CATEGORY_MASK = 0xFF00 const ITEM_CATEGORY_MASK = 0xFF00

99
src/enums/reward-id.ts Normal file
View File

@ -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<typeof RewardId>;
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<typeof RewardCategoryId>;
const ITEM_CATEGORY_MASK = 0xFF00
export function getRewardCategory(itemId: RewardId): RewardCategoryId {
return (itemId & ITEM_CATEGORY_MASK) as RewardCategoryId;
}

View File

@ -1,4 +1,4 @@
export enum ModifierPoolType { export enum RewardPoolType {
PLAYER, PLAYER,
} }

View File

@ -1,4 +1,4 @@
export enum RewardTier { export enum RarityTier {
COMMON, COMMON,
GREAT, GREAT,
ULTRA, ULTRA,

View File

@ -1,56 +1,56 @@
export const TrainerItemId = { export const TrainerItemId = {
NONE: 0x0000, NONE: 0x0000,
MAP: 0x0B01, MAP: 0x1001,
IV_SCANNER: 0x0B02, IV_SCANNER: 0x1002,
LOCK_CAPSULE: 0x0B03, LOCK_CAPSULE: 0x1003,
MEGA_BRACELET: 0x0B04, MEGA_BRACELET: 0x1004,
DYNAMAX_BAND: 0x0B05, DYNAMAX_BAND: 0x1005,
TERA_ORB: 0x0B06, TERA_ORB: 0x1006,
GOLDEN_POKEBALL: 0x0B07, GOLDEN_POKEBALL: 0x1007,
OVAL_CHARM: 0x0B08, OVAL_CHARM: 0x1008,
EXP_SHARE: 0x0B09, EXP_SHARE: 0x1009,
EXP_BALANCE: 0x0B0A, EXP_BALANCE: 0x100A,
CANDY_JAR: 0x0B0B, CANDY_JAR: 0x100B,
BERRY_POUCH: 0x0B0C, BERRY_POUCH: 0x100C,
HEALING_CHARM: 0x0B0D, HEALING_CHARM: 0x100D,
EXP_CHARM: 0x0B0E, EXP_CHARM: 0x100E,
SUPER_EXP_CHARM: 0x0B0F, SUPER_EXP_CHARM: 0x100F,
GOLDEN_EXP_CHARM: 0x0B10, GOLDEN_EXP_CHARM: 0x1010,
AMULET_COIN: 0x0B11, AMULET_COIN: 0x1011,
ABILITY_CHARM: 0x0B12, ABILITY_CHARM: 0x1012,
SHINY_CHARM: 0x0B13, SHINY_CHARM: 0x1013,
CATCHING_CHARM: 0x0B14, CATCHING_CHARM: 0x1014,
BLACK_SLUDGE: 0x0B15, BLACK_SLUDGE: 0x1015,
GOLDEN_BUG_NET: 0x0B16, GOLDEN_BUG_NET: 0x1016,
LURE: 0x0C01, LURE: 0x1101,
SUPER_LURE: 0x0C02, SUPER_LURE: 0x1102,
MAX_LURE: 0x0C03, MAX_LURE: 0x1103,
X_ATTACK: 0x0D01, X_ATTACK: 0x1201,
X_DEFENSE: 0x0D02, X_DEFENSE: 0x1202,
X_SP_ATK: 0x0D03, X_SP_ATK: 0x1203,
X_SP_DEF: 0x0D04, X_SP_DEF: 0x1204,
X_SPEED: 0x0D05, X_SPEED: 0x1205,
X_ACCURACY: 0x0D06, X_ACCURACY: 0x1206,
DIRE_HIT: 0x0D07, DIRE_HIT: 0x1207,
ENEMY_DAMAGE_BOOSTER: 0x0E01, ENEMY_DAMAGE_BOOSTER: 0x1301,
ENEMY_DAMAGE_REDUCTION: 0x0E02, ENEMY_DAMAGE_REDUCTION: 0x1302,
ENEMY_HEAL: 0x0E03, ENEMY_HEAL: 0x1303,
ENEMY_ATTACK_POISON_CHANCE: 0x0E04, ENEMY_ATTACK_POISON_CHANCE: 0x1304,
ENEMY_ATTACK_PARALYZE_CHANCE: 0x0E05, ENEMY_ATTACK_PARALYZE_CHANCE: 0x1305,
ENEMY_ATTACK_BURN_CHANCE: 0x0E06, ENEMY_ATTACK_BURN_CHANCE: 0x1306,
ENEMY_STATUS_EFFECT_HEAL_CHANCE: 0x0E07, ENEMY_STATUS_EFFECT_HEAL_CHANCE: 0x1307,
ENEMY_ENDURE_CHANCE: 0x0E08, ENEMY_ENDURE_CHANCE: 0x1308,
ENEMY_FUSED_CHANCE: 0x0E09, ENEMY_FUSED_CHANCE: 0x1309,
} as const; } as const;
export type TrainerItemId = (typeof TrainerItemId)[keyof typeof TrainerItemId]; export type TrainerItemId = (typeof TrainerItemId)[keyof typeof TrainerItemId];

View File

@ -5,7 +5,7 @@ export enum UiMode {
FIGHT, FIGHT,
BALL, BALL,
TARGET_SELECT, TARGET_SELECT,
MODIFIER_SELECT, REWARD_SELECT,
SAVE_SLOT, SAVE_SLOT,
PARTY, PARTY,
SUMMARY, SUMMARY,

View File

@ -19,6 +19,7 @@ import { ArenaTagSide } from "#enums/arena-tag-side";
import type { ArenaTagType } from "#enums/arena-tag-type"; import type { ArenaTagType } from "#enums/arena-tag-type";
import type { BattlerIndex } from "#enums/battler-index"; import type { BattlerIndex } from "#enums/battler-index";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
import { HeldItemEffect } from "#enums/held-item-effect";
import { CommonAnim } from "#enums/move-anims-common"; import { CommonAnim } from "#enums/move-anims-common";
import type { MoveId } from "#enums/move-id"; import type { MoveId } from "#enums/move-id";
import type { PokemonType } from "#enums/pokemon-type"; 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 { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { applyHeldItems } from "#items/all-held-items"; import { applyHeldItems } from "#items/all-held-items";
import { HeldItemEffect } from "#items/held-item";
import type { Move } from "#moves/move"; import type { Move } from "#moves/move";
import type { AbstractConstructor } from "#types/type-helpers"; import type { AbstractConstructor } from "#types/type-helpers";
import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common"; import { type Constructor, isNullOrUndefined, NumberHolder, randSeedInt } from "#utils/common";

View File

@ -80,6 +80,7 @@ import { ChallengeType } from "#enums/challenge-type";
import { Challenges } from "#enums/challenges"; import { Challenges } from "#enums/challenges";
import { DexAttr } from "#enums/dex-attr"; import { DexAttr } from "#enums/dex-attr";
import { FieldPosition } from "#enums/field-position"; import { FieldPosition } from "#enums/field-position";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import { HitResult } from "#enums/hit-result"; import { HitResult } from "#enums/hit-result";
import { LearnMoveSituation } from "#enums/learn-move-situation"; import { LearnMoveSituation } from "#enums/learn-move-situation";
@ -92,7 +93,7 @@ import { Nature } from "#enums/nature";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PokemonAnimType } from "#enums/pokemon-anim-type";
import { PokemonType } from "#enums/pokemon-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 { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { import {
@ -111,7 +112,6 @@ import { UiMode } from "#enums/ui-mode";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { doShinySparkleAnim } from "#field/anims"; import { doShinySparkleAnim } from "#field/anims";
import { applyHeldItems } from "#items/all-held-items"; import { applyHeldItems } from "#items/all-held-items";
import { HeldItemEffect } from "#items/held-item";
import type { HeldItemConfiguration } from "#items/held-item-data-types"; import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { HeldItemManager } from "#items/held-item-manager"; import { HeldItemManager } from "#items/held-item-manager";
import { assignItemsFromConfiguration } from "#items/held-item-pool"; 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` * 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 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 * @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) { if (!this.shiny) {
const shinyThreshold = new NumberHolder(thresholdOverride ?? BASE_SHINY_CHANCE); const shinyThreshold = new NumberHolder(thresholdOverride ?? BASE_SHINY_CHANCE);
if (applyModifiersToOverride) { if (applyItemsToOverride) {
if (timedEventManager.isEventActive()) { if (timedEventManager.isEventActive()) {
shinyThreshold.value *= timedEventManager.getShinyMultiplier(); 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` * 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 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 * @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) { if (!this.species.abilityHidden) {
return false; return false;
} }
const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE); const haThreshold = new NumberHolder(thresholdOverride ?? BASE_HIDDEN_ABILITY_CHANCE);
if (applyModifiersToOverride) { if (applyItemsToOverride) {
if (!this.hasTrainer()) { if (!this.hasTrainer()) {
globalScene.applyPlayerItems(TrainerItemEffect.HIDDEN_ABILITY_CHANCE_BOOSTER, { numberHolder: haThreshold }); 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 (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]); 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]); 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]); movePool.push([moveId, 14]);
} }
} }

View File

@ -634,7 +634,7 @@ export class Trainer extends Phaser.GameObjects.Container {
return maxScorePartyMemberIndexes[0]; return maxScorePartyMemberIndexes[0];
} }
getPartyMemberModifierChanceMultiplier(index: number): number { getPartyMemberItemChanceMultiplier(index: number): number {
switch (this.getPartyTemplate().getStrength(index)) { switch (this.getPartyTemplate().getStrength(index)) {
case PartyMemberStrength.WEAKER: case PartyMemberStrength.WEAKER:
return 0.75; return 0.75;
@ -647,14 +647,14 @@ export class Trainer extends Phaser.GameObjects.Container {
case PartyMemberStrength.STRONGER: case PartyMemberStrength.STRONGER:
return 0.375; return 0.375;
default: default:
console.warn("getPartyMemberModifierChanceMultiplier not defined. Using default 0"); console.warn("getPartyMemberItemChanceMultiplier not defined. Using default 0");
return 0; return 0;
} }
} }
genTrainerItems(party: EnemyPokemon[]): TrainerItemConfiguration { genTrainerItems(party: EnemyPokemon[]): TrainerItemConfiguration {
if (this.config.genModifiersFunc) { if (this.config.genTrainerItemsFunc) {
return this.config.genModifiersFunc(party); return this.config.genTrainerItemsFunc(party);
} }
return []; return [];
} }

View File

@ -323,7 +323,7 @@ export class GameMode implements GameModeConfig {
} }
} }
getEnemyModifierChance(isBoss: boolean): number { getEnemyItemChance(isBoss: boolean): number {
switch (this.modeId) { switch (this.modeId) {
case GameModes.CLASSIC: case GameModes.CLASSIC:
case GameModes.CHALLENGE: case GameModes.CHALLENGE:

View File

@ -24,7 +24,6 @@ import { ExpBoosterHeldItem, type ExpBoostParams } from "#items/exp-booster";
import { FieldEffectHeldItem, type FieldEffectParams } from "#items/field-effect"; import { FieldEffectHeldItem, type FieldEffectParams } from "#items/field-effect";
import { FlinchChanceHeldItem, type FlinchChanceParams } from "#items/flinch-chance"; import { FlinchChanceHeldItem, type FlinchChanceParams } from "#items/flinch-chance";
import { FriendshipBoosterHeldItem, type FriendshipBoostParams } from "#items/friendship-booster"; import { FriendshipBoosterHeldItem, type FriendshipBoostParams } from "#items/friendship-booster";
import { HeldItemEffect } from "#items/held-item";
import { HitHealHeldItem, type HitHealParams } from "#items/hit-heal"; import { HitHealHeldItem, type HitHealParams } from "#items/hit-heal";
import { IncrementingStatHeldItem, type IncrementingStatParams } from "#items/incrementing-stat"; import { IncrementingStatHeldItem, type IncrementingStatParams } from "#items/incrementing-stat";
import { InstantReviveHeldItem, type InstantReviveParams } from "#items/instant-revive"; 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 { SurviveChanceHeldItem, type SurviveChanceParams } from "#items/survive-chance";
import { TurnEndHealHeldItem, type TurnEndHealParams } from "#items/turn-end-heal"; import { TurnEndHealHeldItem, type TurnEndHealParams } from "#items/turn-end-heal";
import { TurnEndStatusHeldItem, type TurnEndStatusParams } from "#items/turn-end-status"; 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() { export function initHeldItems() {
for (const berry of getEnumValues(BerryType)) { for (const berry of getEnumValues(BerryType)) {

View File

@ -43,7 +43,7 @@ export function initTrainerItems() {
allTrainerItems[TrainerItemId.CANDY_JAR] = new LevelIncrementBoosterTrainerItem(TrainerItemId.CANDY_JAR, 99); allTrainerItems[TrainerItemId.CANDY_JAR] = new LevelIncrementBoosterTrainerItem(TrainerItemId.CANDY_JAR, 99);
allTrainerItems[TrainerItemId.BERRY_POUCH] = new PreserveBerryTrainerItem(TrainerItemId.BERRY_POUCH, 3); 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.EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.EXP_CHARM, 25, 99);
allTrainerItems[TrainerItemId.SUPER_EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.SUPER_EXP_CHARM, 60, 30); 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); allTrainerItems[TrainerItemId.GOLDEN_EXP_CHARM] = new ExpBoosterTrainerItem(TrainerItemId.GOLDEN_EXP_CHARM, 100, 10);

View File

@ -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 { FormChangeItem } from "#enums/form-change-item";
import type { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; 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"; import type { Pokemon } from "#field/pokemon";
export type HeldItemData = { export type HeldItemData = {
stack: number; stack: number;
/**
* Whether this item is currently disabled.
* @defaultValue `false`
*/
disabled?: boolean; disabled?: boolean;
/**
* The item's current cooldown.
* @defaultValue `0`
*/
cooldown?: number; cooldown?: number;
}; };
@ -67,7 +75,7 @@ export function isHeldItemPool(value: any): value is HeldItemPool {
} }
export type HeldItemTieredPool = { export type HeldItemTieredPool = {
[key in RewardTier]?: HeldItemPool; [key in RarityTier]?: HeldItemPool;
}; };
type HeldItemConfigurationEntry = { type HeldItemConfigurationEntry = {

View File

@ -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;
}

View File

@ -1,9 +1,9 @@
import { allHeldItems } from "#data/data-lists"; import { allHeldItems } from "#data/data-lists";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
import { HeldItemCategoryId, HeldItemId, HeldItemNames, isCategoryId } from "#enums/held-item-id"; 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 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 { PERMANENT_STATS } from "#enums/stat";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon"; import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { attackTypeToHeldItem } from "#items/attack-type-booster"; import { attackTypeToHeldItem } from "#items/attack-type-booster";
@ -21,7 +21,8 @@ import {
isHeldItemPool, isHeldItemPool,
isHeldItemSpecs, isHeldItemSpecs,
} from "#items/held-item-data-types"; } 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 = {}; export const wildHeldItemPool: HeldItemTieredPool = {};
@ -34,7 +35,7 @@ export function assignDailyRunStarterHeldItems(party: PlayerPokemon[]) {
for (let m = 0; m < 3; m++) { for (let m = 0; m < 3; m++) {
const tierValue = randSeedInt(64); const tierValue = randSeedInt(64);
const tier = getDailyRewardTier(tierValue); const tier = getDailyRarityTier(tierValue);
const item = getNewHeldItemFromPool( const item = getNewHeldItemFromPool(
getHeldItemPool(HeldItemPoolType.DAILY_STARTER)[tier] as HeldItemPool, 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) { if (tierValue > 25) {
return RewardTier.COMMON; return RarityTier.COMMON;
} }
if (tierValue > 12) { if (tierValue > 12) {
return RewardTier.GREAT; return RarityTier.GREAT;
} }
if (tierValue > 4) { if (tierValue > 4) {
return RewardTier.ULTRA; return RarityTier.ULTRA;
} }
if (tierValue > 0) { if (tierValue > 0) {
return RewardTier.ROGUE; return RarityTier.ROGUE;
} }
return RewardTier.MASTER; return RarityTier.MASTER;
} }
function getHeldItemPool(poolType: HeldItemPoolType): HeldItemTieredPool { function getHeldItemPool(poolType: HeldItemPoolType): HeldItemTieredPool {
@ -101,25 +102,25 @@ export function assignEnemyHeldItemsForWave(
} }
} }
function getRandomTier(): RewardTier { function getRandomTier(): RarityTier {
const tierValue = randSeedInt(1024); const tierValue = randSeedInt(1024);
if (tierValue > 255) { if (tierValue > 255) {
return RewardTier.COMMON; return RarityTier.COMMON;
} }
if (tierValue > 60) { if (tierValue > 60) {
return RewardTier.GREAT; return RarityTier.GREAT;
} }
if (tierValue > 12) { if (tierValue > 12) {
return RewardTier.ULTRA; return RarityTier.ULTRA;
} }
if (tierValue) { 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(); let tier = getRandomTier();
if (!upgradeCount) { if (!upgradeCount) {

View File

@ -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;
}

View File

@ -4,42 +4,9 @@ import { type HeldItemId, HeldItemNames } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import i18next from "i18next"; 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 { export class HeldItem {
// public pokemonId: number; // public pokemonId: number;
// TODO: Should this be readonly?
public type: HeldItemId; public type: HeldItemId;
public readonly maxStackCount: number; public readonly maxStackCount: number;
public isTransferable = true; public isTransferable = true;

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface AccuracyBoostParams { 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 pokemon - The {@linkcode Pokemon} to apply the move accuracy boost to
* @param moveAccuracy - {@linkcode NumberHolder} holding the move accuracy boost * @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 { // override shouldApply(pokemon?: Pokemon, moveAccuracy?: NumberHolder): boolean {
// return super.shouldApply(pokemon, moveAccuracy) && !!moveAccuracy; // return super.shouldApply(pokemon, moveAccuracy) && !!moveAccuracy;
// } // }
/** /**
* Applies {@linkcode PokemonMoveAccuracyBoosterModifier} * Applies {@linkcode PokemonMoveAccuracyBoosterHeldItem}
*/ */
apply({ pokemon, moveAccuracy }: AccuracyBoostParams): true { apply({ pokemon, moveAccuracy }: AccuracyBoostParams): true {
const stackCount = pokemon.heldItemManager.getStack(this.type); const stackCount = pokemon.heldItemManager.getStack(this.type);

View File

@ -1,7 +1,8 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId, HeldItemNames } from "#enums/held-item-id"; import { HeldItemId, HeldItemNames } from "#enums/held-item-id";
import { PokemonType } from "#enums/pokemon-type"; import { PokemonType } from "#enums/pokemon-type";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,7 +1,8 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import { getStatKey, type PermanentStat, Stat } from "#enums/stat"; import { getStatKey, type PermanentStat, Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { HeldItem, HeldItemEffect } from "#items/held-item"; import { HeldItem } from "#items/held-item";
import i18next from "i18next"; import i18next from "i18next";
export interface BaseStatBoosterParams { export interface BaseStatBoosterParams {
@ -11,9 +12,9 @@ export interface BaseStatBoosterParams {
baseStats: number[]; baseStats: number[];
} }
interface PermanentStatToHeldItemMap { type PermanentStatToHeldItemMap = {
[key: number]: HeldItemId; [key in PermanentStat]: HeldItemId;
} };
export const permanentStatToHeldItem: PermanentStatToHeldItemMap = { export const permanentStatToHeldItem: PermanentStatToHeldItemMap = {
[Stat.HP]: HeldItemId.HP_UP, [Stat.HP]: HeldItemId.HP_UP,

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { HeldItem, HeldItemEffect } from "#items/held-item"; import { HeldItem } from "#items/held-item";
import i18next from "i18next"; import i18next from "i18next";
export interface BaseStatFlatParams { export interface BaseStatFlatParams {

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { HeldItem, HeldItemEffect } from "#items/held-item"; import { HeldItem } from "#items/held-item";
import i18next from "i18next"; import i18next from "i18next";
export interface BaseStatTotalParams { export interface BaseStatTotalParams {

View File

@ -1,5 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface BatonParams { export interface BatonParams {

View File

@ -1,18 +1,21 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getBerryEffectDescription, getBerryEffectFunc, getBerryName, getBerryPredicate } from "#data/berry"; import { getBerryEffectDescription, getBerryEffectFunc, getBerryName, getBerryPredicate } from "#data/berry";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import { BerryUsedEvent } from "#events/battle-scene"; import { BerryUsedEvent } from "#events/battle-scene";
import type { Pokemon } from "#field/pokemon"; 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 { TrainerItemEffect } from "#items/trainer-item";
import type { EnumValues } from "#types/enum-types";
import { BooleanHolder } from "#utils/common"; import { BooleanHolder } from "#utils/common";
interface BerryTypeToHeldItemMap { type BerryTypeToHeldItemMap = {
[key: number]: HeldItemId; [key in EnumValues<typeof BerryType>]: HeldItemId;
} };
export const berryTypeToHeldItem: BerryTypeToHeldItemMap = { // TODO: Rework this to use a bitwise XOR
export const berryTypeToHeldItem = {
[BerryType.SITRUS]: HeldItemId.SITRUS_BERRY, [BerryType.SITRUS]: HeldItemId.SITRUS_BERRY,
[BerryType.LUM]: HeldItemId.LUM_BERRY, [BerryType.LUM]: HeldItemId.LUM_BERRY,
[BerryType.ENIGMA]: HeldItemId.ENIGMA_BERRY, [BerryType.ENIGMA]: HeldItemId.ENIGMA_BERRY,
@ -24,7 +27,7 @@ export const berryTypeToHeldItem: BerryTypeToHeldItemMap = {
[BerryType.LANSAT]: HeldItemId.LANSAT_BERRY, [BerryType.LANSAT]: HeldItemId.LANSAT_BERRY,
[BerryType.STARF]: HeldItemId.STARF_BERRY, [BerryType.STARF]: HeldItemId.STARF_BERRY,
[BerryType.LEPPA]: HeldItemId.LEPPA_BERRY, [BerryType.LEPPA]: HeldItemId.LEPPA_BERRY,
}; } satisfies BerryTypeToHeldItemMap;
export interface BerryParams { export interface BerryParams {
/** The pokemon with the berry */ /** The pokemon with the berry */

View File

@ -1,8 +1,9 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { Command } from "#enums/command"; import { Command } from "#enums/command";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { BooleanHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,7 +1,8 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { SpeciesId } from "#enums/species-id"; import type { SpeciesId } from "#enums/species-id";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface CritBoostParams { export interface CritBoostParams {

View File

@ -1,6 +1,7 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 { TrainerItemEffect } from "#items/trainer-item";
import { NumberHolder } from "#utils/common"; import { NumberHolder } from "#utils/common";

View File

@ -1,9 +1,10 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import type { SpeciesId } from "#enums/species-id"; import type { SpeciesId } from "#enums/species-id";
import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerItemId } from "#enums/trainer-item-id";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { HeldItem, HeldItemEffect } from "#items/held-item"; import { HeldItem } from "#items/held-item";
import i18next from "i18next"; import i18next from "i18next";
export interface EvoTrackerParams { export interface EvoTrackerParams {

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,5 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface FieldEffectParams { export interface FieldEffectParams {

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon"; 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 type { BooleanHolder } from "#utils/common";
export interface FlinchChanceParams { export interface FlinchChanceParams {

View File

@ -1,5 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,7 +1,8 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { toDmgValue } from "#utils/common"; import { toDmgValue } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,6 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,8 +1,9 @@
import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { applyAbAttrs } from "#abilities/apply-ab-attrs";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { toDmgValue } from "#utils/common"; import { toDmgValue } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,9 +1,10 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { allHeldItems } from "#data/data-lists"; import { allHeldItems } from "#data/data-lists";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import { Pokemon } from "#field/pokemon"; 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 { coerceArray, randSeedFloat } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";
@ -47,7 +48,7 @@ export abstract class ItemTransferHeldItem extends HeldItem {
} }
// TODO: Change this logic to use held items // TODO: Change this logic to use held items
const transferredModifierTypes: HeldItemId[] = []; const transferredRewards: HeldItemId[] = [];
const heldItems = targetPokemon.heldItemManager.getTransferableHeldItems(); const heldItems = targetPokemon.heldItemManager.getTransferableHeldItems();
for (let i = 0; i < transferredItemCount; i++) { for (let i = 0; i < transferredItemCount; i++) {
@ -58,16 +59,16 @@ export abstract class ItemTransferHeldItem extends HeldItem {
const randItem = heldItems[randItemIndex]; const randItem = heldItems[randItemIndex];
// TODO: Fix this after updating the various methods in battle-scene.ts // TODO: Fix this after updating the various methods in battle-scene.ts
if (globalScene.tryTransferHeldItem(randItem, targetPokemon, pokemon, false)) { if (globalScene.tryTransferHeldItem(randItem, targetPokemon, pokemon, false)) {
transferredModifierTypes.push(randItem); transferredRewards.push(randItem);
heldItems.splice(randItemIndex, 1); heldItems.splice(randItemIndex, 1);
} }
} }
for (const mt of transferredModifierTypes) { for (const mt of transferredRewards) {
globalScene.phaseManager.queueMessage(this.getTransferMessage(params, mt)); globalScene.phaseManager.queueMessage(this.getTransferMessage(params, mt));
} }
return !!transferredModifierTypes.length; return !!transferredRewards.length;
} }
abstract getTargets(params: ItemStealParams): Pokemon[]; 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 * Modifier for held items that steal items from the enemy at the end of
* each turn. * each turn.
* @see {@linkcode modifierTypes[MINI_BLACK_HOLE]} * @see {@linkcode allRewards[MINI_BLACK_HOLE]}
*/ */
export class TurnEndItemStealHeldItem extends ItemTransferHeldItem { export class TurnEndItemStealHeldItem extends ItemTransferHeldItem {
public effects: HeldItemEffect[] = [HeldItemEffect.TURN_END_ITEM_STEAL]; 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 * Modifier for held items that add a chance to steal items from the target of a
* successful attack. * successful attack.
* @see {@linkcode modifierTypes[GRIP_CLAW]} * @see {@linkcode allRewards[GRIP_CLAW]}
* @see {@linkcode HeldItemTransferModifier} * @see {@linkcode HeldItemTransferModifier}
*/ */
export class ContactItemStealChanceHeldItem extends ItemTransferHeldItem { export class ContactItemStealChanceHeldItem extends ItemTransferHeldItem {

View File

@ -1,7 +1,8 @@
import { allMoves } from "#data/data-lists"; import { allMoves } from "#data/data-lists";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { MoveId } from "#enums/move-id"; import type { MoveId } from "#enums/move-id";
import type { Pokemon } from "#field/pokemon"; 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 { isNullOrUndefined, type NumberHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,5 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface NatureWeightBoostParams { export interface NatureWeightBoostParams {

View File

@ -1,8 +1,9 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { BATTLE_STATS } from "#enums/stat"; import { BATTLE_STATS } from "#enums/stat";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { ConsumableHeldItem, HeldItemEffect } from "#items/held-item"; import { ConsumableHeldItem } from "#items/held-item";
import i18next from "i18next"; import i18next from "i18next";
export interface ResetNegativeStatStageParams { export interface ResetNegativeStatStageParams {

View File

@ -1,9 +1,10 @@
import { pokemonEvolutions } from "#balance/pokemon-evolutions"; import { pokemonEvolutions } from "#balance/pokemon-evolutions";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import type { SpeciesId } from "#enums/species-id"; import type { SpeciesId } from "#enums/species-id";
import type { Stat } from "#enums/stat"; import type { Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon"; 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 type { NumberHolder } from "#utils/common";
export interface StatBoostParams { export interface StatBoostParams {

View File

@ -1,7 +1,8 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 type { BooleanHolder } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,7 +1,8 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon"; 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 { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import { toDmgValue } from "#utils/common"; import { toDmgValue } from "#utils/common";
import i18next from "i18next"; import i18next from "i18next";

View File

@ -1,7 +1,8 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id"; import type { HeldItemId } from "#enums/held-item-id";
import type { StatusEffect } from "#enums/status-effect"; import type { StatusEffect } from "#enums/status-effect";
import type { Pokemon } from "#field/pokemon"; import type { Pokemon } from "#field/pokemon";
import { HeldItem, HeldItemEffect } from "#items/held-item"; import { HeldItem } from "#items/held-item";
export interface TurnEndStatusParams { export interface TurnEndStatusParams {
/** The pokemon with the item */ /** The pokemon with the item */

View File

@ -1,42 +1,42 @@
import { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id"; 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"; import { dailyStarterHeldItemPool, trainerHeldItemPool, wildHeldItemPool } from "#items/held-item-pool";
/** /**
* Initialize the wild held item pool * Initialize the wild held item pool
*/ */
function initWildHeldItemPool() { function initWildHeldItemPool() {
wildHeldItemPool[RewardTier.COMMON] = [{ entry: HeldItemCategoryId.BERRY, weight: 1 }]; wildHeldItemPool[RarityTier.COMMON] = [{ entry: HeldItemCategoryId.BERRY, weight: 1 }];
wildHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }]; wildHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }];
wildHeldItemPool[RewardTier.ULTRA] = [ wildHeldItemPool[RarityTier.ULTRA] = [
{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }, { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 },
{ entry: HeldItemId.WHITE_HERB, weight: 0 }, { entry: HeldItemId.WHITE_HERB, weight: 0 },
]; ];
wildHeldItemPool[RewardTier.ROGUE] = [{ entry: HeldItemId.LUCKY_EGG, weight: 4 }]; wildHeldItemPool[RarityTier.ROGUE] = [{ entry: HeldItemId.LUCKY_EGG, weight: 4 }];
wildHeldItemPool[RewardTier.MASTER] = [{ entry: HeldItemId.GOLDEN_EGG, weight: 1 }]; wildHeldItemPool[RarityTier.MASTER] = [{ entry: HeldItemId.GOLDEN_EGG, weight: 1 }];
} }
/** /**
* Initialize the trainer pokemon held item pool * Initialize the trainer pokemon held item pool
*/ */
function initTrainerHeldItemPool() { function initTrainerHeldItemPool() {
trainerHeldItemPool[RewardTier.COMMON] = [ trainerHeldItemPool[RarityTier.COMMON] = [
{ entry: HeldItemCategoryId.BERRY, weight: 8 }, { entry: HeldItemCategoryId.BERRY, weight: 8 },
{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }, { entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 },
]; ];
trainerHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }]; trainerHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 3 }];
trainerHeldItemPool[RewardTier.ULTRA] = [ trainerHeldItemPool[RarityTier.ULTRA] = [
{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 10 }, { entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 10 },
{ entry: HeldItemId.WHITE_HERB, weight: 0 }, { entry: HeldItemId.WHITE_HERB, weight: 0 },
]; ];
trainerHeldItemPool[RewardTier.ROGUE] = [ trainerHeldItemPool[RarityTier.ROGUE] = [
{ entry: HeldItemId.FOCUS_BAND, weight: 2 }, { entry: HeldItemId.FOCUS_BAND, weight: 2 },
{ entry: HeldItemId.LUCKY_EGG, weight: 4 }, { entry: HeldItemId.LUCKY_EGG, weight: 4 },
{ entry: HeldItemId.QUICK_CLAW, weight: 1 }, { entry: HeldItemId.QUICK_CLAW, weight: 1 },
{ entry: HeldItemId.GRIP_CLAW, weight: 1 }, { entry: HeldItemId.GRIP_CLAW, weight: 1 },
{ entry: HeldItemId.WIDE_LENS, weight: 1 }, { entry: HeldItemId.WIDE_LENS, weight: 1 },
]; ];
trainerHeldItemPool[RewardTier.MASTER] = [ trainerHeldItemPool[RarityTier.MASTER] = [
{ entry: HeldItemId.KINGS_ROCK, weight: 1 }, { entry: HeldItemId.KINGS_ROCK, weight: 1 },
{ entry: HeldItemId.LEFTOVERS, weight: 1 }, { entry: HeldItemId.LEFTOVERS, weight: 1 },
{ entry: HeldItemId.SHELL_BELL, weight: 1 }, { entry: HeldItemId.SHELL_BELL, weight: 1 },
@ -47,26 +47,26 @@ function initTrainerHeldItemPool() {
/** /**
* Initialize the daily starter held item pool * Initialize the daily starter held item pool
*/ */
function initDailyStarterModifierPool() { function initDailyStarterRewardPool() {
dailyStarterHeldItemPool[RewardTier.COMMON] = [ dailyStarterHeldItemPool[RarityTier.COMMON] = [
{ entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 }, { entry: HeldItemCategoryId.BASE_STAT_BOOST, weight: 1 },
{ entry: HeldItemCategoryId.BERRY, weight: 3 }, { entry: HeldItemCategoryId.BERRY, weight: 3 },
]; ];
dailyStarterHeldItemPool[RewardTier.GREAT] = [{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }]; dailyStarterHeldItemPool[RarityTier.GREAT] = [{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 5 }];
dailyStarterHeldItemPool[RewardTier.ULTRA] = [ dailyStarterHeldItemPool[RarityTier.ULTRA] = [
{ entry: HeldItemId.REVIVER_SEED, weight: 4 }, { entry: HeldItemId.REVIVER_SEED, weight: 4 },
{ entry: HeldItemId.SOOTHE_BELL, weight: 1 }, { entry: HeldItemId.SOOTHE_BELL, weight: 1 },
{ entry: HeldItemId.SOUL_DEW, weight: 1 }, { entry: HeldItemId.SOUL_DEW, weight: 1 },
{ entry: HeldItemId.GOLDEN_PUNCH, weight: 1 }, { entry: HeldItemId.GOLDEN_PUNCH, weight: 1 },
]; ];
dailyStarterHeldItemPool[RewardTier.ROGUE] = [ dailyStarterHeldItemPool[RarityTier.ROGUE] = [
{ entry: HeldItemId.GRIP_CLAW, weight: 5 }, { entry: HeldItemId.GRIP_CLAW, weight: 5 },
{ entry: HeldItemId.BATON, weight: 2 }, { entry: HeldItemId.BATON, weight: 2 },
{ entry: HeldItemId.FOCUS_BAND, weight: 5 }, { entry: HeldItemId.FOCUS_BAND, weight: 5 },
{ entry: HeldItemId.QUICK_CLAW, weight: 3 }, { entry: HeldItemId.QUICK_CLAW, weight: 3 },
{ entry: HeldItemId.KINGS_ROCK, weight: 3 }, { entry: HeldItemId.KINGS_ROCK, weight: 3 },
]; ];
dailyStarterHeldItemPool[RewardTier.MASTER] = [ dailyStarterHeldItemPool[RarityTier.MASTER] = [
{ entry: HeldItemId.LEFTOVERS, weight: 1 }, { entry: HeldItemId.LEFTOVERS, weight: 1 },
{ entry: HeldItemId.SHELL_BELL, weight: 1 }, { entry: HeldItemId.SHELL_BELL, weight: 1 },
]; ];
@ -76,5 +76,5 @@ export function initHeldItemPools() {
// Default held item pools for specific scenarios // Default held item pools for specific scenarios
initWildHeldItemPool(); initWildHeldItemPool();
initTrainerHeldItemPool(); initTrainerHeldItemPool();
initDailyStarterModifierPool(); initDailyStarterRewardPool();
} }

View File

@ -1,37 +1,37 @@
/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ /* 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 */ /* biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */
import { timedEventManager } from "#app/global-event-manager"; import { timedEventManager } from "#app/global-event-manager";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { pokemonEvolutions } from "#balance/pokemon-evolutions"; 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 { MAX_PER_TYPE_POKEBALLS } from "#data/pokeball";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { PokeballType } from "#enums/pokeball"; import { PokeballType } from "#enums/pokeball";
import { RewardTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerItemId } from "#enums/trainer-item-id";
import { Unlockables } from "#enums/unlockables"; import { Unlockables } from "#enums/unlockables";
import type { Pokemon } from "#field/pokemon"; 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 type { TurnEndStatusHeldItem } from "#items/turn-end-status";
import { modifierPool } from "#modifiers/modifier-pools"; import type { WeightedRewardWeightFunc } from "#types/rewards";
import { WeightedModifierType } from "#modifiers/modifier-type";
import type { WeightedModifierTypeWeightFunc } from "#types/modifier-types";
import { isNullOrUndefined } from "#utils/common"; import { isNullOrUndefined } from "#utils/common";
/** /**
* Initialize the common modifier pool * Initialize the common modifier pool
*/ */
function initCommonModifierPool() { function initCommonRewardPool() {
modifierPool[RewardTier.COMMON] = [ rewardPool[RarityTier.COMMON] = [
new WeightedModifierType(modifierTypes.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6), new WeightedReward(allRewards.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6),
new WeightedModifierType(modifierTypes.RARE_CANDY, 2), new WeightedReward(allRewards.RARE_CANDY, 2),
new WeightedModifierType( new WeightedReward(
modifierTypes.POTION, allRewards.POTION,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter(p => p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875 && !p.isFainted()).length, party.filter(p => p.getInverseHp() >= 10 && p.getHpRatio() <= 0.875 && !p.isFainted()).length,
@ -41,8 +41,8 @@ function initCommonModifierPool() {
}, },
9, 9,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.SUPER_POTION, allRewards.SUPER_POTION,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter(p => p.getInverseHp() >= 25 && p.getHpRatio() <= 0.75 && !p.isFainted()).length, party.filter(p => p.getInverseHp() >= 25 && p.getHpRatio() <= 0.75 && !p.isFainted()).length,
@ -52,8 +52,8 @@ function initCommonModifierPool() {
}, },
3, 3,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.ETHER, allRewards.ETHER,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
@ -71,8 +71,8 @@ function initCommonModifierPool() {
}, },
9, 9,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MAX_ETHER, allRewards.MAX_ETHER,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
@ -90,25 +90,22 @@ function initCommonModifierPool() {
}, },
3, 3,
), ),
new WeightedModifierType(modifierTypes.LURE, lureWeightFunc(TrainerItemId.LURE, 2)), new WeightedReward(allRewards.LURE, lureWeightFunc(TrainerItemId.LURE, 2)),
new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4), new WeightedReward(allRewards.TEMP_STAT_STAGE_BOOSTER, 4),
new WeightedModifierType(modifierTypes.BERRY, 2), new WeightedReward(allRewards.BERRY, 2),
new WeightedModifierType(modifierTypes.TM_COMMON, 2), new WeightedReward(allRewards.TM_COMMON, 2),
].map(m => { ];
m.setTier(RewardTier.COMMON);
return m;
});
} }
/** /**
* Initialize the Great modifier pool * Initialize the Great modifier pool
*/ */
function initGreatModifierPool() { function initGreatRewardPool() {
modifierPool[RewardTier.GREAT] = [ rewardPool[RarityTier.GREAT] = [
new WeightedModifierType(modifierTypes.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6), new WeightedReward(allRewards.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6),
new WeightedModifierType(modifierTypes.PP_UP, 2), new WeightedReward(allRewards.PP_UP, 2),
new WeightedModifierType( new WeightedReward(
modifierTypes.FULL_HEAL, allRewards.FULL_HEAL,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min( const statusEffectPartyMemberCount = Math.min(
party.filter( party.filter(
@ -126,31 +123,31 @@ function initGreatModifierPool() {
}, },
18, 18,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.REVIVE, allRewards.REVIVE,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3);
return faintedPartyMemberCount * 9; return faintedPartyMemberCount * 9;
}, },
27, 27,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MAX_REVIVE, allRewards.MAX_REVIVE,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3); const faintedPartyMemberCount = Math.min(party.filter(p => p.isFainted()).length, 3);
return faintedPartyMemberCount * 3; return faintedPartyMemberCount * 3;
}, },
9, 9,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.SACRED_ASH, allRewards.SACRED_ASH,
(party: Pokemon[]) => { (party: Pokemon[]) => {
return party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0; return party.filter(p => p.isFainted()).length >= Math.ceil(party.length / 2) ? 1 : 0;
}, },
1, 1,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.HYPER_POTION, allRewards.HYPER_POTION,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.625 && !p.isFainted()).length, party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.625 && !p.isFainted()).length,
@ -160,8 +157,8 @@ function initGreatModifierPool() {
}, },
9, 9,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MAX_POTION, allRewards.MAX_POTION,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length, party.filter(p => p.getInverseHp() >= 100 && p.getHpRatio() <= 0.5 && !p.isFainted()).length,
@ -171,8 +168,8 @@ function initGreatModifierPool() {
}, },
3, 3,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.FULL_RESTORE, allRewards.FULL_RESTORE,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min( const statusEffectPartyMemberCount = Math.min(
party.filter( party.filter(
@ -195,8 +192,8 @@ function initGreatModifierPool() {
}, },
3, 3,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.ELIXIR, allRewards.ELIXIR,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
@ -214,8 +211,8 @@ function initGreatModifierPool() {
}, },
9, 9,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MAX_ELIXIR, allRewards.MAX_ELIXIR,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
@ -233,26 +230,26 @@ function initGreatModifierPool() {
}, },
3, 3,
), ),
new WeightedModifierType(modifierTypes.DIRE_HIT, 4), new WeightedReward(allRewards.DIRE_HIT, 4),
new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(TrainerItemId.SUPER_LURE, 4)), new WeightedReward(allRewards.SUPER_LURE, lureWeightFunc(TrainerItemId.SUPER_LURE, 4)),
new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)), new WeightedReward(allRewards.NUGGET, skipInLastClassicWaveOrDefault(5)),
new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4), new WeightedReward(allRewards.SPECIES_STAT_BOOSTER, 4),
new WeightedModifierType( new WeightedReward(
modifierTypes.EVOLUTION_ITEM, allRewards.EVOLUTION_ITEM,
() => { () => {
return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8); return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8);
}, },
8, 8,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MAP, allRewards.MAP,
() => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0), () => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0),
2, 2,
), ),
new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2), new WeightedReward(allRewards.SOOTHE_BELL, 2),
new WeightedModifierType(modifierTypes.TM_GREAT, 3), new WeightedReward(allRewards.TM_GREAT, 3),
new WeightedModifierType( new WeightedReward(
modifierTypes.MEMORY_MUSHROOM, allRewards.MEMORY_MUSHROOM,
(party: Pokemon[]) => { (party: Pokemon[]) => {
if (!party.find(p => p.getLearnableLevelMoves().length)) { if (!party.find(p => p.getLearnableLevelMoves().length)) {
return 0; return 0;
@ -264,8 +261,8 @@ function initGreatModifierPool() {
}, },
4, 4,
), ),
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3), new WeightedReward(allRewards.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.TERA_SHARD, (party: Pokemon[]) => new WeightedReward(allRewards.TERA_SHARD, (party: Pokemon[]) =>
party.filter( party.filter(
p => p =>
!(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)), !(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)),
@ -273,8 +270,8 @@ function initGreatModifierPool() {
? 1 ? 1
: 0, : 0,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.DNA_SPLICERS, allRewards.DNA_SPLICERS,
(party: Pokemon[]) => { (party: Pokemon[]) => {
if (party.filter(p => !p.fusionSpecies).length > 1) { if (party.filter(p => !p.fusionSpecies).length > 1) {
if (globalScene.gameMode.isSplicedOnly) { if (globalScene.gameMode.isSplicedOnly) {
@ -288,39 +285,36 @@ function initGreatModifierPool() {
}, },
4, 4,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.VOUCHER, allRewards.VOUCHER,
(_party: Pokemon[], rerollCount: number) => (!globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0), (_party: Pokemon[], rerollCount: number) => (!globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0),
1, 1,
), ),
].map(m => { ];
m.setTier(RewardTier.GREAT);
return m;
});
} }
/** /**
* Initialize the Ultra modifier pool * Initialize the Ultra modifier pool
*/ */
function initUltraModifierPool() { function initUltraRewardPool() {
modifierPool[RewardTier.ULTRA] = [ rewardPool[RarityTier.ULTRA] = [
new WeightedModifierType(modifierTypes.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15), new WeightedReward(allRewards.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15),
new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(TrainerItemId.MAX_LURE, 4)), new WeightedReward(allRewards.MAX_LURE, lureWeightFunc(TrainerItemId.MAX_LURE, 4)),
new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), new WeightedReward(allRewards.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)),
new WeightedModifierType(modifierTypes.PP_MAX, 3), new WeightedReward(allRewards.PP_MAX, 3),
new WeightedModifierType(modifierTypes.MINT, 4), new WeightedReward(allRewards.MINT, 4),
new WeightedModifierType( new WeightedReward(
modifierTypes.RARE_EVOLUTION_ITEM, allRewards.RARE_EVOLUTION_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32), () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32),
32, 32,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.FORM_CHANGE_ITEM, allRewards.FORM_CHANGE_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
24, 24,
), ),
new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedReward(allRewards.AMULET_COIN, skipInLastClassicWaveOrDefault(3)),
new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { new WeightedReward(allRewards.EVIOLITE, (party: Pokemon[]) => {
const { gameMode, gameData } = globalScene; const { gameMode, gameData } = globalScene;
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
return party.some(p => { return party.some(p => {
@ -340,9 +334,9 @@ function initUltraModifierPool() {
} }
return 0; return 0;
}), }),
new WeightedModifierType(modifierTypes.RARE_SPECIES_STAT_BOOSTER, 12), new WeightedReward(allRewards.RARE_SPECIES_STAT_BOOSTER, 12),
new WeightedModifierType( new WeightedReward(
modifierTypes.LEEK, allRewards.LEEK,
(party: Pokemon[]) => { (party: Pokemon[]) => {
const checkedSpecies = [SpeciesId.FARFETCHD, SpeciesId.GALAR_FARFETCHD, SpeciesId.SIRFETCHD]; 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 // 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, 12,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.TOXIC_ORB, allRewards.TOXIC_ORB,
(party: Pokemon[]) => { (party: Pokemon[]) => {
return party.some(p => { return party.some(p => {
const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]); const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]);
@ -403,8 +397,8 @@ function initUltraModifierPool() {
}, },
10, 10,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.FLAME_ORB, allRewards.FLAME_ORB,
(party: Pokemon[]) => { (party: Pokemon[]) => {
return party.some(p => { return party.some(p => {
const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]); const isHoldingOrb = p.getHeldItems().some(i => i in [HeldItemId.FLAME_ORB, HeldItemId.TOXIC_ORB]);
@ -449,8 +443,8 @@ function initUltraModifierPool() {
}, },
10, 10,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MYSTICAL_ROCK, allRewards.MYSTICAL_ROCK,
(party: Pokemon[]) => { (party: Pokemon[]) => {
return party.some(p => { return party.some(p => {
const stack = p.heldItemManager.getStack(HeldItemId.MYSTICAL_ROCK); const stack = p.heldItemManager.getStack(HeldItemId.MYSTICAL_ROCK);
@ -496,94 +490,88 @@ function initUltraModifierPool() {
}, },
10, 10,
), ),
new WeightedModifierType(modifierTypes.REVIVER_SEED, 4), new WeightedReward(allRewards.REVIVER_SEED, 4),
new WeightedModifierType(modifierTypes.CANDY_JAR, skipInLastClassicWaveOrDefault(5)), new WeightedReward(allRewards.CANDY_JAR, skipInLastClassicWaveOrDefault(5)),
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 9), new WeightedReward(allRewards.ATTACK_TYPE_BOOSTER, 9),
new WeightedModifierType(modifierTypes.TM_ULTRA, 11), new WeightedReward(allRewards.TM_ULTRA, 11),
new WeightedModifierType(modifierTypes.RARER_CANDY, 4), new WeightedReward(allRewards.RARER_CANDY, 4),
new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)), new WeightedReward(allRewards.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)),
new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), new WeightedReward(allRewards.IV_SCANNER, skipInLastClassicWaveOrDefault(4)),
new WeightedModifierType(modifierTypes.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedReward(allRewards.EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType(modifierTypes.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), new WeightedReward(allRewards.EXP_SHARE, skipInLastClassicWaveOrDefault(10)),
new WeightedModifierType( new WeightedReward(
modifierTypes.TERA_ORB, allRewards.TERA_ORB,
() => () =>
!globalScene.gameMode.isClassic !globalScene.gameMode.isClassic
? Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4) ? Math.min(Math.max(Math.floor(globalScene.currentBattle.waveIndex / 50) * 2, 1), 4)
: 0, : 0,
4, 4,
), ),
new WeightedModifierType(modifierTypes.QUICK_CLAW, 3), new WeightedReward(allRewards.QUICK_CLAW, 3),
new WeightedModifierType(modifierTypes.WIDE_LENS, 7), new WeightedReward(allRewards.WIDE_LENS, 7),
].map(m => { ];
m.setTier(RewardTier.ULTRA);
return m;
});
} }
function initRogueModifierPool() { function initRogueRewardPool() {
modifierPool[RewardTier.ROGUE] = [ rewardPool[RarityTier.ROGUE] = [
new WeightedModifierType(modifierTypes.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16), new WeightedReward(allRewards.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16),
new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), new WeightedReward(allRewards.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)),
new WeightedModifierType(modifierTypes.LEFTOVERS, 3), new WeightedReward(allRewards.LEFTOVERS, 3),
new WeightedModifierType(modifierTypes.SHELL_BELL, 3), new WeightedReward(allRewards.SHELL_BELL, 3),
new WeightedModifierType(modifierTypes.BERRY_POUCH, 4), new WeightedReward(allRewards.BERRY_POUCH, 4),
new WeightedModifierType(modifierTypes.GRIP_CLAW, 5), new WeightedReward(allRewards.GRIP_CLAW, 5),
new WeightedModifierType(modifierTypes.SCOPE_LENS, 4), new WeightedReward(allRewards.SCOPE_LENS, 4),
new WeightedModifierType(modifierTypes.BATON, 2), new WeightedReward(allRewards.BATON, 2),
new WeightedModifierType(modifierTypes.SOUL_DEW, 7), new WeightedReward(allRewards.SOUL_DEW, 7),
new WeightedModifierType(modifierTypes.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4), new WeightedReward(allRewards.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4),
new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), new WeightedReward(allRewards.ABILITY_CHARM, skipInClassicAfterWave(189, 6)),
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5), new WeightedReward(allRewards.FOCUS_BAND, 5),
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3), new WeightedReward(allRewards.KINGS_ROCK, 3),
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)), new WeightedReward(allRewards.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)),
new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), new WeightedReward(allRewards.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
new WeightedModifierType( new WeightedReward(
modifierTypes.RARE_FORM_CHANGE_ITEM, allRewards.RARE_FORM_CHANGE_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
24, 24,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MEGA_BRACELET, allRewards.MEGA_BRACELET,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
36, 36,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.DYNAMAX_BAND, allRewards.DYNAMAX_BAND,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
36, 36,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.VOUCHER_PLUS, allRewards.VOUCHER_PLUS,
(_party: Pokemon[], rerollCount: number) => (_party: Pokemon[], rerollCount: number) =>
!globalScene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, !globalScene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0,
3, 3,
), ),
].map(m => { ];
m.setTier(RewardTier.ROGUE);
return m;
});
} }
/** /**
* Initialize the Master modifier pool * Initialize the Master modifier pool
*/ */
function initMasterModifierPool() { function initMasterRewardPool() {
modifierPool[RewardTier.MASTER] = [ rewardPool[RarityTier.MASTER] = [
new WeightedModifierType(modifierTypes.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24), new WeightedReward(allRewards.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24),
new WeightedModifierType(modifierTypes.SHINY_CHARM, 14), new WeightedReward(allRewards.SHINY_CHARM, 14),
new WeightedModifierType(modifierTypes.HEALING_CHARM, 18), new WeightedReward(allRewards.HEALING_CHARM, 18),
new WeightedModifierType(modifierTypes.MULTI_LENS, 18), new WeightedReward(allRewards.MULTI_LENS, 18),
new WeightedModifierType( new WeightedReward(
modifierTypes.VOUCHER_PREMIUM, allRewards.VOUCHER_PREMIUM,
(_party: Pokemon[], rerollCount: number) => (_party: Pokemon[], rerollCount: number) =>
!globalScene.gameMode.isDaily && !globalScene.gameMode.isEndless && !globalScene.gameMode.isSplicedOnly !globalScene.gameMode.isDaily && !globalScene.gameMode.isEndless && !globalScene.gameMode.isSplicedOnly
? Math.max(5 - rerollCount * 2, 0) ? Math.max(5 - rerollCount * 2, 0)
: 0, : 0,
5, 5,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.DNA_SPLICERS, allRewards.DNA_SPLICERS,
(party: Pokemon[]) => (party: Pokemon[]) =>
!(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) && !(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) &&
!globalScene.gameMode.isSplicedOnly && !globalScene.gameMode.isSplicedOnly &&
@ -592,8 +580,8 @@ function initMasterModifierPool() {
: 0, : 0,
24, 24,
), ),
new WeightedModifierType( new WeightedReward(
modifierTypes.MINI_BLACK_HOLE, allRewards.MINI_BLACK_HOLE,
() => () =>
globalScene.gameMode.isDaily || globalScene.gameMode.isDaily ||
(!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE)) (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))
@ -601,33 +589,30 @@ function initMasterModifierPool() {
: 0, : 0,
1, 1,
), ),
].map(m => { ];
m.setTier(RewardTier.MASTER);
return m;
});
} }
/** /**
* Initialize {@linkcode modifierPool} with the initial set of modifier types. * Initialize {@linkcode rewardPool} with the initial set of modifier types.
* {@linkcode initModifierTypes} MUST be called before this function. * {@linkcode initRewards} MUST be called before this function.
*/ */
export function initModifierPools() { export function initRewardPools() {
// The modifier pools the player chooses from during modifier selection // The modifier pools the player chooses from during modifier selection
initCommonModifierPool(); initCommonRewardPool();
initGreatModifierPool(); initGreatRewardPool();
initUltraModifierPool(); initUltraRewardPool();
initRogueModifierPool(); initRogueRewardPool();
initMasterModifierPool(); initMasterRewardPool();
} }
/** /**
* High order function that returns a WeightedModifierTypeWeightFunc that will only be applied on * High order function that returns a WeightedRewardWeightFunc that will only be applied on
* classic and skip an ModifierType if current wave is greater or equal to the one passed down * 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 wave - Wave where we should stop showing the modifier
* @param defaultWeight - ModifierType default weight * @param defaultWeight - Reward default weight
* @returns A WeightedModifierTypeWeightFunc * @returns A WeightedRewardWeightFunc
*/ */
function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedModifierTypeWeightFunc { function skipInClassicAfterWave(wave: number, defaultWeight: number): WeightedRewardWeightFunc {
return () => { return () => {
const gameMode = globalScene.gameMode; const gameMode = globalScene.gameMode;
const currentWave = globalScene.currentBattle.waveIndex; 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 * High order function that returns a WeightedRewardWeightFunc that will only be applied on
* classic and it will skip a ModifierType if it is the last wave pull. * classic and it will skip a Reward if it is the last wave pull.
* @param defaultWeight ModifierType default weight * @param defaultWeight Reward default weight
* @returns A WeightedModifierTypeWeightFunc * @returns A WeightedRewardWeightFunc
*/ */
function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedModifierTypeWeightFunc { function skipInLastClassicWaveOrDefault(defaultWeight: number): WeightedRewardWeightFunc {
return skipInClassicAfterWave(199, defaultWeight); 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 * or if the lure still has over 60% of its duration left
* @param lureId The id of the lure type in question. * @param lureId The id of the lure type in question.
* @param weight The desired weight for the lure when it does spawn * @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 () => { return () => {
const lureCount = globalScene.trainerItems.getStack(lureId); const lureCount = globalScene.trainerItems.getStack(lureId);
return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) && return !(globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex === 199) &&

View File

@ -1,4 +1,4 @@
import { RewardTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
import { TrainerItemId } from "#enums/trainer-item-id"; import { TrainerItemId } from "#enums/trainer-item-id";
import { enemyBuffTokenPool } from "#items/trainer-item-pool"; import { enemyBuffTokenPool } from "#items/trainer-item-pool";
@ -6,7 +6,7 @@ import { enemyBuffTokenPool } from "#items/trainer-item-pool";
* Initialize the enemy buff modifier pool * Initialize the enemy buff modifier pool
*/ */
function initEnemyBuffTokenPool() { function initEnemyBuffTokenPool() {
enemyBuffTokenPool[RewardTier.COMMON] = [ enemyBuffTokenPool[RarityTier.COMMON] = [
{ entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 9 }, { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 9 },
{ entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 9 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 9 },
{ entry: TrainerItemId.ENEMY_ATTACK_POISON_CHANCE, weight: 3 }, { entry: TrainerItemId.ENEMY_ATTACK_POISON_CHANCE, weight: 3 },
@ -16,14 +16,14 @@ function initEnemyBuffTokenPool() {
{ entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 4 }, { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 4 },
{ entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 },
]; ];
enemyBuffTokenPool[RewardTier.GREAT] = [ enemyBuffTokenPool[RarityTier.GREAT] = [
{ entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 5 }, { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 5 },
{ entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 5 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 5 },
{ entry: TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE, weight: 5 }, { entry: TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE, weight: 5 },
{ entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 5 }, { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 5 },
{ entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 1 },
]; ];
enemyBuffTokenPool[RewardTier.ULTRA] = [ enemyBuffTokenPool[RarityTier.ULTRA] = [
{ entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 10 }, { entry: TrainerItemId.ENEMY_DAMAGE_BOOSTER, weight: 10 },
{ entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 10 }, { entry: TrainerItemId.ENEMY_DAMAGE_REDUCTION, weight: 10 },
{ entry: TrainerItemId.ENEMY_HEAL, weight: 10 }, { entry: TrainerItemId.ENEMY_HEAL, weight: 10 },
@ -31,8 +31,8 @@ function initEnemyBuffTokenPool() {
{ entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 10 }, { entry: TrainerItemId.ENEMY_ENDURE_CHANCE, weight: 10 },
{ entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 5 }, { entry: TrainerItemId.ENEMY_FUSED_CHANCE, weight: 5 },
]; ];
enemyBuffTokenPool[RewardTier.ROGUE] = []; enemyBuffTokenPool[RarityTier.ROGUE] = [];
enemyBuffTokenPool[RewardTier.MASTER] = []; enemyBuffTokenPool[RarityTier.MASTER] = [];
} }
export function initTrainerItemPools() { export function initTrainerItemPools() {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 = {};

115
src/items/reward-utils.ts Normal file
View File

@ -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;
}

1852
src/items/reward.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// TODO: move to `src/@types/` // 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"; import type { TrainerItemId } from "#enums/trainer-item-id";
export type TrainerItemData = { export type TrainerItemData = {
@ -28,7 +28,7 @@ type TrainerItemPoolEntry = {
export type TrainerItemPool = TrainerItemPoolEntry[]; export type TrainerItemPool = TrainerItemPoolEntry[];
export type TrainerItemTieredPool = { export type TrainerItemTieredPool = {
[key in RewardTier]?: TrainerItemPool; [key in RarityTier]?: TrainerItemPool;
}; };
export function isTrainerItemPool(value: any): value is TrainerItemPool { export function isTrainerItemPool(value: any): value is TrainerItemPool {

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { allTrainerItems } from "#data/data-lists"; 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 { TrainerItemId } from "#enums/trainer-item-id";
import type { TrainerItemPool, TrainerItemTieredPool } from "#items/trainer-item-data-types"; import type { TrainerItemPool, TrainerItemTieredPool } from "#items/trainer-item-data-types";
import type { TrainerItemManager } from "#items/trainer-item-manager"; import type { TrainerItemManager } from "#items/trainer-item-manager";
@ -29,13 +29,13 @@ export function getNewTrainerItemFromPool(pool: TrainerItemPool, manager: Traine
return entry as TrainerItemId; return entry as TrainerItemId;
} }
export function assignEnemyBuffTokenForWave(tier: RewardTier) { export function assignEnemyBuffTokenForWave(tier: RarityTier) {
let tierStackCount: number; let tierStackCount: number;
switch (tier) { switch (tier) {
case RewardTier.ULTRA: case RarityTier.ULTRA:
tierStackCount = 5; tierStackCount = 5;
break; break;
case RewardTier.GREAT: case RarityTier.GREAT:
tierStackCount = 3; tierStackCount = 3;
break; break;
default: default:

View File

@ -297,9 +297,9 @@ export class DoubleBattleChanceBoosterTrainerItem extends LapsingTrainerItem {
} }
} }
interface TempStatToTrainerItemMap { type TempStatToTrainerItemMap = {
[key: number]: TrainerItemId; [key in TempBattleStat]: TrainerItemId;
} };
export const tempStatToTrainerItem: TempStatToTrainerItemMap = { export const tempStatToTrainerItem: TempStatToTrainerItemMap = {
[Stat.ATK]: TrainerItemId.X_ATTACK, [Stat.ATK]: TrainerItemId.X_ATTACK,

View File

@ -15,9 +15,9 @@ import { getBiomeHasProps } from "#field/arena";
import { initHeldItems } from "#items/all-held-items"; import { initHeldItems } from "#items/all-held-items";
import { initTrainerItems } from "#items/all-trainer-items"; import { initTrainerItems } from "#items/all-trainer-items";
import { initHeldItemPools } from "#items/init-held-item-pools"; import { initHeldItemPools } from "#items/init-held-item-pools";
import { initRewardPools } from "#items/init-reward-pools";
import { initTrainerItemPools } from "#items/init-trainer-item-pools"; import { initTrainerItemPools } from "#items/init-trainer-item-pools";
import { initModifierPools } from "#modifiers/init-modifier-pools"; import { initRewards } from "#items/reward";
import { initModifierTypes } from "#modifiers/modifier-type";
import { initMoves } from "#moves/move"; import { initMoves } from "#moves/move";
import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters"; import { initMysteryEncounters } from "#mystery-encounters/mystery-encounters";
import { CacheBustedLoaderPlugin } from "#plugins/cache-busted-loader-plugin"; import { CacheBustedLoaderPlugin } from "#plugins/cache-busted-loader-plugin";
@ -370,8 +370,8 @@ export class LoadingScene extends SceneBase {
this.loadLoadingScreen(); this.loadLoadingScreen();
initModifierTypes(); initRewards();
initModifierPools(); initRewardPools();
initHeldItemPools(); initHeldItemPools();
initTrainerItemPools(); initTrainerItemPools();

View File

@ -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 = {};

Some files were not shown because too many files have changed in this diff Show More