[Modifier Refactor] Modernize reward pool (#6208)

* Added missing `export`s to classes

* Created `allRewards` object

* RewardPool now uses ids; function to generate appropriate reward from id (including held item or trainer item)

* Added generateRewardOptionFromId function

* Using `RewardSpecs` for predetermined reward generation

* Removed RewardOverride

* Removed rewardInitObj

* Removed WeightedReward

* Proper initialization of allRewards
This commit is contained in:
Wlowscha 2025-08-04 09:04:48 +02:00 committed by GitHub
parent ad26adf426
commit dac9e202a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 640 additions and 779 deletions

View File

@ -9,9 +9,18 @@ export type WeightedRewardWeightFunc = (party: Pokemon[], rerollCount?: number)
export type RewardPoolId = RewardId | HeldItemId | TrainerItemId; export type RewardPoolId = RewardId | HeldItemId | TrainerItemId;
export type RewardGeneratorSpecs = {
id: RewardId;
args: RewardGeneratorArgs;
};
// TODO: fix this with correctly typed args for different RewardIds
export type RewardSpecs = RewardPoolId | RewardGeneratorSpecs;
export type RewardPoolEntry = { export type RewardPoolEntry = {
id: RewardPoolId; id: RewardPoolId;
weight: number | WeightedRewardWeightFunc; weight: number | WeightedRewardWeightFunc;
maxWeight?: number;
}; };
export type RewardPool = { export type RewardPool = {

View File

@ -1,11 +1,12 @@
import type { Ability } from "#abilities/ability"; import type { Ability } from "#abilities/ability";
import type { PokemonSpecies } from "#data/pokemon-species"; 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 { RewardId } from "#enums/reward-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 { Move } from "#moves/move"; import type { Move } from "#moves/move";
import type { RewardFunc } from "#types/rewards";
export const allAbilities: Ability[] = []; export const allAbilities: Ability[] = [];
export const allMoves: Move[] = []; export const allMoves: Move[] = [];
@ -13,6 +14,4 @@ export const allSpecies: PokemonSpecies[] = [];
export const allHeldItems: Record<HeldItemId, HeldItem> = {}; export const allHeldItems: Record<HeldItemId, HeldItem> = {};
export const allTrainerItems: Record<TrainerItemId, TrainerItem> = {}; export const allTrainerItems: Record<TrainerItemId, TrainerItem> = {};
export const allRewards: Record<RewardId, RewardFunc> = {};
// TODO: Figure out what this is used for and provide an appropriate tsdoc comment
export const allRewards = {} as Rewards;

View File

@ -1,11 +1,11 @@
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 { 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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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";
@ -164,7 +164,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.
encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`));
setEncounterRewards( setEncounterRewards(
{ {
guaranteedRewardFuncs: [allRewards.SACRED_ASH], guaranteedRewardSpecs: [RewardId.SACRED_ASH],
guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ULTRA], guaranteedRarityTiers: [RarityTier.ROGUE, RarityTier.ULTRA],
fillRemaining: true, fillRemaining: true,
}, },

View File

@ -1,19 +1,19 @@
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 { 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 { 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 { RewardId } from "#enums/reward-id";
import { RewardPoolType } from "#enums/reward-pool-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 { RewardOption } from "#items/reward"; import type { RewardOption } from "#items/reward";
import { generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils"; import { generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils";
import { generateRewardOption } from "#items/reward-utils"; import { generateRewardOptionFromId } 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 {
@ -162,7 +162,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
const shopOptions: RewardOption[] = []; 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 = generateRewardOption(allRewards.BERRY); const mod = generateRewardOptionFromId(RewardId.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }
@ -189,7 +189,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder.
const shopOptions: RewardOption[] = []; 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 = generateRewardOption(allRewards.BERRY); const mod = generateRewardOptionFromId(RewardId.BERRY);
if (mod) { if (mod) {
shopOptions.push(mod); shopOptions.push(mod);
} }

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, allRewards } from "#data/data-lists"; import { allHeldItems, allMoves } 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,6 +8,7 @@ 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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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";
@ -15,7 +16,7 @@ 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 { RewardOption } from "#items/reward"; import type { RewardOption } from "#items/reward";
import { generateRewardOption } from "#items/reward-utils"; import { generateRewardOptionFromId } 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";
@ -285,7 +286,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
moveTutorOptions, moveTutorOptions,
}; };
// Assigns callback that teaches move before continuing to allRewards // Assigns callback that teaches move before continuing to RewardId
encounter.onRewards = doBugTypeMoveTutor; encounter.onRewards = doBugTypeMoveTutor;
setEncounterRewards({ fillRemaining: true }); setEncounterRewards({ fillRemaining: true });
@ -305,7 +306,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 allRewards depending on the number of bug types they have // Player gets different RewardId 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 +315,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
if (numBugTypes < 2) { if (numBugTypes < 2) {
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.SUPER_LURE, allRewards.GREAT_BALL], guaranteedRewardSpecs: [RewardId.SUPER_LURE, RewardId.GREAT_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -325,7 +326,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
]; ];
} else if (numBugTypes < 4) { } else if (numBugTypes < 4) {
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.QUICK_CLAW, allRewards.MAX_LURE, allRewards.ULTRA_BALL], guaranteedRewardSpecs: [HeldItemId.QUICK_CLAW, RewardId.MAX_LURE, RewardId.ULTRA_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -336,7 +337,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
]; ];
} else if (numBugTypes < 6) { } else if (numBugTypes < 6) {
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.GRIP_CLAW, allRewards.MAX_LURE, allRewards.ROGUE_BALL], guaranteedRewardSpecs: [HeldItemId.GRIP_CLAW, RewardId.MAX_LURE, RewardId.ROGUE_BALL],
fillRemaining: false, fillRemaining: false,
}); });
encounter.selectedOption!.dialogue!.selected = [ encounter.selectedOption!.dialogue!.selected = [
@ -348,28 +349,28 @@ 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 rewardOptions: RewardOption[] = [generateRewardOption(allRewards.MASTER_BALL)!]; const rewardOptions: RewardOption[] = [generateRewardOptionFromId(RewardId.MASTER_BALL)!];
const specialOptions: RewardOption[] = []; const specialOptions: RewardOption[] = [];
if (!globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) { if (!globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) {
rewardOptions.push(generateRewardOption(allRewards.MEGA_BRACELET)!); rewardOptions.push(generateRewardOptionFromId(TrainerItemId.MEGA_BRACELET)!);
} }
if (!globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) { if (!globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) {
rewardOptions.push(generateRewardOption(allRewards.DYNAMAX_BAND)!); rewardOptions.push(generateRewardOptionFromId(TrainerItemId.DYNAMAX_BAND)!);
} }
const nonRareEvolutionReward = generateRewardOption(allRewards.EVOLUTION_ITEM); const nonRareEvolutionReward = generateRewardOptionFromId(RewardId.EVOLUTION_ITEM);
if (nonRareEvolutionReward) { if (nonRareEvolutionReward) {
specialOptions.push(nonRareEvolutionReward); specialOptions.push(nonRareEvolutionReward);
} }
const rareEvolutionReward = generateRewardOption(allRewards.RARE_EVOLUTION_ITEM); const rareEvolutionReward = generateRewardOptionFromId(RewardId.RARE_EVOLUTION_ITEM);
if (rareEvolutionReward) { if (rareEvolutionReward) {
specialOptions.push(rareEvolutionReward); specialOptions.push(rareEvolutionReward);
} }
const formChangeReward = generateRewardOption(allRewards.FORM_CHANGE_ITEM); const formChangeReward = generateRewardOptionFromId(RewardId.FORM_CHANGE_ITEM);
if (formChangeReward) { if (formChangeReward) {
specialOptions.push(formChangeReward); specialOptions.push(formChangeReward);
} }
const rareFormChangeReward = generateRewardOption(allRewards.RARE_FORM_CHANGE_ITEM); const rareFormChangeReward = generateRewardOptionFromId(RewardId.RARE_FORM_CHANGE_ITEM);
if (rareFormChangeReward) { if (rareFormChangeReward) {
specialOptions.push(rareFormChangeReward); specialOptions.push(rareFormChangeReward);
} }
@ -465,12 +466,12 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = MysteryEncounterBuilde
chosenPokemon.loseHeldItem(lostItem, false); chosenPokemon.loseHeldItem(lostItem, false);
globalScene.updateItems(true); globalScene.updateItems(true);
const bugNet = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_GOLDEN_BUG_NET)!; const bugNet = generateRewardOptionFromId(TrainerItemId.GOLDEN_BUG_NET)!;
bugNet.type.tier = RarityTier.ROGUE; bugNet.type.tier = RarityTier.ROGUE;
setEncounterRewards({ setEncounterRewards({
guaranteedRewardOptions: [bugNet], guaranteedRewardOptions: [bugNet],
guaranteedRewardFuncs: [allRewards.REVIVER_SEED], guaranteedRewardSpecs: [HeldItemId.REVIVER_SEED],
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(true); leaveEncounterWithoutBattle(true);
@ -744,7 +745,7 @@ function doBugTypeMoveTutor(): Promise<void> {
); );
} }
// Complete battle and go to allRewards // Complete battle and go to RewardId
resolve(); resolve();
}); });
} }

View File

@ -1,11 +1,11 @@
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 { 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";
import { EncounterAnim } from "#enums/encounter-anims"; import { EncounterAnim } from "#enums/encounter-anims";
import { HeldItemId } from "#enums/held-item-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { MoveUseMode } from "#enums/move-use-mode"; import { MoveUseMode } from "#enums/move-use-mode";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -219,7 +219,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
await hideOricorioPokemon(); await hideOricorioPokemon();
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.BATON], guaranteedRewardSpecs: [HeldItemId.BATON],
fillRemaining: true, fillRemaining: true,
}); });
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);

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 { 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 { RewardId } from "#enums/reward-id";
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 { RewardFunc } from "#types/rewards"; import type { RewardSpecs } 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 rewards: RewardFunc[] = []; const rewards: RewardSpecs[] = [];
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) {
rewards.push(allRewards.TM_COMMON); rewards.push(RewardId.TM_COMMON);
} else if (roll < 4) { } else if (roll < 4) {
rewards.push(allRewards.TM_GREAT); rewards.push(RewardId.TM_GREAT);
} else { } else {
rewards.push(allRewards.TM_ULTRA); rewards.push(RewardId.TM_ULTRA);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: rewards, guaranteedRewardSpecs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -88,21 +88,21 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose Vitamins // Choose Vitamins
const rewards: RewardFunc[] = []; const rewards: RewardSpecs[] = [];
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) {
rewards.push(allRewards.PP_UP); rewards.push(RewardId.PP_UP);
} else { } else {
rewards.push(allRewards.BASE_STAT_BOOSTER); rewards.push(RewardId.BASE_STAT_BOOSTER);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: rewards, guaranteedRewardSpecs: 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 rewards: RewardFunc[] = []; const rewards: RewardSpecs[] = [];
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) {
rewards.push(allRewards.DIRE_HIT); rewards.push(RewardId.DIRE_HIT);
} else { } else {
rewards.push(allRewards.TEMP_STAT_STAGE_BOOSTER); rewards.push(RewardId.TEMP_STAT_STAGE_BOOSTER);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: rewards, guaranteedRewardSpecs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -142,25 +142,25 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBu
}, },
async () => { async () => {
// Choose Pokeballs // Choose Pokeballs
const rewards: RewardFunc[] = []; const rewards: RewardSpecs[] = [];
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) {
rewards.push(allRewards.POKEBALL); rewards.push(RewardId.POKEBALL);
} else if (roll < 40) { } else if (roll < 40) {
rewards.push(allRewards.GREAT_BALL); rewards.push(RewardId.GREAT_BALL);
} else if (roll < 60) { } else if (roll < 60) {
rewards.push(allRewards.ULTRA_BALL); rewards.push(RewardId.ULTRA_BALL);
} else { } else {
rewards.push(allRewards.ROGUE_BALL); rewards.push(RewardId.ROGUE_BALL);
} }
i++; i++;
} }
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: rewards, guaranteedRewardSpecs: rewards,
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();

View File

@ -1,13 +1,11 @@
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 { 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 {
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
@ -96,11 +94,11 @@ 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 = [
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.ATK])!,
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateRewardOption(allRewards.DIRE_HIT)!, generateRewardOptionFromId(RewardId.DIRE_HIT)!,
generateRewardOption(allRewards.RARER_CANDY)!, generateRewardOptionFromId(RewardId.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({
@ -144,11 +142,11 @@ 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 = [
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.SPATK])!,
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateRewardOption(allRewards.DIRE_HIT)!, generateRewardOptionFromId(RewardId.DIRE_HIT)!,
generateRewardOption(allRewards.RARER_CANDY)!, generateRewardOptionFromId(RewardId.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({
@ -192,11 +190,11 @@ 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 = [
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.ACC])!,
generateRewardOption(allRewards.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!, generateRewardOptionFromId(RewardId.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateRewardOption(allRewards.GREAT_BALL)!, generateRewardOptionFromId(RewardId.GREAT_BALL)!,
generateRewardOption(allRewards.IV_SCANNER)!, generateRewardOptionFromId(RewardId.IV_SCANNER)!,
generateRewardOption(allRewards.RARER_CANDY)!, generateRewardOptionFromId(RewardId.RARER_CANDY)!,
]; ];
setEncounterRewards({ setEncounterRewards({

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 { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
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";
import { HeldItemId } 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";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
@ -160,7 +160,7 @@ export const FunAndGamesEncounter: MysteryEncounter = MysteryEncounterBuilder.wi
], ],
}, },
async () => { async () => {
// Leave encounter with no allRewards or exp // Leave encounter with no RewardId 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({
guaranteedRewardFuncs: [allRewards.MULTI_LENS], guaranteedRewardSpecs: [HeldItemId.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({
guaranteedRewardFuncs: [allRewards.SCOPE_LENS], guaranteedRewardSpecs: [HeldItemId.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({
guaranteedRewardFuncs: [allRewards.WIDE_LENS], guaranteedRewardSpecs: [HeldItemId.WIDE_LENS],
fillRemaining: false, fillRemaining: false,
}); });
resultMessageKey = `${namespace}:good_result`; resultMessageKey = `${namespace}:good_result`;

View File

@ -1,9 +1,9 @@
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 { 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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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";
@ -147,7 +147,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0]; const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.TM_COMMON, allRewards.TM_GREAT, allRewards.MEMORY_MUSHROOM], guaranteedRewardSpecs: [RewardId.TM_COMMON, RewardId.TM_GREAT, RewardId.MEMORY_MUSHROOM],
fillRemaining: true, fillRemaining: true,
}); });

View File

@ -1,5 +1,4 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
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 +115,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
// Pick battle // Pick battle
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.LEFTOVERS], guaranteedRewardSpecs: [HeldItemId.LEFTOVERS],
fillRemaining: true, fillRemaining: true,
}); });
encounter.startOfBattleEffects.push({ encounter.startOfBattleEffects.push({
@ -163,7 +162,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({
guaranteedRewardFuncs: [allRewards.LEFTOVERS], guaranteedRewardSpecs: [HeldItemId.LEFTOVERS],
fillRemaining: false, fillRemaining: false,
}); });
// Snorlax exp to Pokemon that did the stealing // Snorlax exp to Pokemon that did the stealing

View File

@ -1,9 +1,9 @@
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 { 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 { HeldItemId } 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";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
@ -13,7 +13,6 @@ 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 { 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 {
@ -173,10 +172,8 @@ export const TeleportingHijinksEncounter: MysteryEncounter = MysteryEncounterBui
], ],
}; };
const magnet = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.STEEL])!;
const metalCoat = generateRewardOption(allRewards.ATTACK_TYPE_BOOSTER, [PokemonType.ELECTRIC])!;
setEncounterRewards({ setEncounterRewards({
guaranteedRewardOptions: [magnet, metalCoat], guaranteedRewardSpecs: [HeldItemId.MAGNET, HeldItemId.METAL_COAT],
fillRemaining: true, fillRemaining: true,
}); });
await transitionMysteryEncounterIntroVisuals(true, true); await transitionMysteryEncounterIntroVisuals(true, true);

View File

@ -1,11 +1,11 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { speciesStarterCosts } from "#balance/starters"; import { speciesStarterCosts } from "#balance/starters";
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";
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 { 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";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
@ -294,7 +294,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs); const eggOptions = getEggOptions(pokemon1CommonEggs, pokemon1RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], guaranteedRewardSpecs: [HeldItemId.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,
@ -353,7 +353,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs); const eggOptions = getEggOptions(pokemon2CommonEggs, pokemon2RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], guaranteedRewardSpecs: [HeldItemId.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,
@ -412,7 +412,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount
const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs); const eggOptions = getEggOptions(pokemon3CommonEggs, pokemon3RareEggs);
setEncounterRewards( setEncounterRewards(
{ {
guaranteedRewardFuncs: [allRewards.SOOTHE_BELL], guaranteedRewardSpecs: [HeldItemId.SOOTHE_BELL],
fillRemaining: true, fillRemaining: true,
}, },
eggOptions, eggOptions,

View File

@ -1,6 +1,5 @@
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 { 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 +192,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
// Pick battle // Pick battle
const encounter = globalScene.currentBattle.mysteryEncounter!; const encounter = globalScene.currentBattle.mysteryEncounter!;
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [allRewards.SOUL_DEW], guaranteedRewardSpecs: [HeldItemId.SOUL_DEW],
fillRemaining: true, fillRemaining: true,
}); });
encounter.startOfBattleEffects.push( encounter.startOfBattleEffects.push(

View File

@ -11,10 +11,12 @@ 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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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 type { Reward } from "#items/reward";
import { generateRewardOptionFromId } 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 {
@ -140,7 +142,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({
guaranteedRewardFuncs: [allRewards.RARER_CANDY], guaranteedRewardSpecs: [RewardId.RARER_CANDY],
fillRemaining: false, fillRemaining: false,
}); });
leaveEncounterWithoutBattle(); leaveEncounterWithoutBattle();
@ -156,14 +158,14 @@ async function spawnNextTrainerOrEndEncounter() {
await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`); await showEncounterDialogue(`${namespace}:victory`, `${namespace}:speaker`);
// Give 10x Voucher // Give 10x Voucher
const reward = allRewards.VOUCHER_PREMIUM(); const reward = allRewards[RewardId.VOUCHER_PREMIUM]();
globalScene.applyReward(reward, {}); globalScene.applyReward(reward as Reward, {});
globalScene.playSound("item_fanfare"); globalScene.playSound("item_fanfare");
await showEncounterText(i18next.t("battle:rewardGain", { modifierName: reward.name })); await showEncounterText(i18next.t("battle:rewardGain", { modifierName: (reward as 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 allRewards get animated in globalScene.ui.clearText(); // Clears "Winstrate" title from screen as allRewards get animated in
const machoBrace = generateRewardOption(allRewards.MYSTERY_ENCOUNTER_MACHO_BRACE)!; const machoBrace = generateRewardOptionFromId(HeldItemId.MACHO_BRACE)!;
machoBrace.type.tier = RarityTier.MASTER; machoBrace.type.tier = RarityTier.MASTER;
setEncounterRewards({ setEncounterRewards({
guaranteedRewardOptions: [machoBrace], guaranteedRewardOptions: [machoBrace],

View File

@ -1,5 +1,5 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { allRewards, allSpecies } from "#data/data-lists"; import { 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,6 +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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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";
@ -218,12 +219,12 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
await doNewTeamPostProcess(transformations); await doNewTeamPostProcess(transformations);
setEncounterRewards({ setEncounterRewards({
guaranteedRewardFuncs: [ guaranteedRewardSpecs: [
allRewards.MEMORY_MUSHROOM, RewardId.MEMORY_MUSHROOM,
allRewards.ROGUE_BALL, RewardId.ROGUE_BALL,
allRewards.MINT, RewardId.MINT,
allRewards.MINT, RewardId.MINT,
allRewards.MINT, RewardId.MINT,
], ],
fillRemaining: false, fillRemaining: false,
}); });
@ -242,7 +243,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
], ],
}, },
async () => { async () => {
// Battle your "future" team for some item allRewards // Battle your "future" team for some item RewardId
const transformations: PokemonTransformation[] = const transformations: PokemonTransformation[] =
globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations; globalScene.currentBattle.mysteryEncounter!.misc.teamTransformations;
@ -293,7 +294,7 @@ export const WeirdDreamEncounter: MysteryEncounter = MysteryEncounterBuilder.wit
}; };
const onBeforeRewards = () => { const onBeforeRewards = () => {
// Before battle allRewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently) // Before battle RewardId, 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) {

View File

@ -61,9 +61,10 @@ export const RewardId = {
TRAINER_ITEM: 0x2D01, TRAINER_ITEM: 0x2D01,
TEMP_STAT_STAGE_BOOSTER: 0x2D02, TEMP_STAT_STAGE_BOOSTER: 0x2D02,
LURE: 0x2D03, DIRE_HIT: 0x2D03,
SUPER_LURE: 0x2D04, LURE: 0x2D04,
MAX_LURE: 0x2D05, SUPER_LURE: 0x2D05,
MAX_LURE: 0x2D06,
FORM_CHANGE_ITEM: 0x2E01, FORM_CHANGE_ITEM: 0x2E01,
RARE_FORM_CHANGE_ITEM: 0x2E02, RARE_FORM_CHANGE_ITEM: 0x2E02,

View File

@ -7,11 +7,11 @@ import { initChallenges } from "#data/challenge";
import { initTrainerTypeDialogue } from "#data/dialogue"; import { initTrainerTypeDialogue } from "#data/dialogue";
import { initPokemonForms } from "#data/pokemon-forms"; import { initPokemonForms } from "#data/pokemon-forms";
import { initHeldItems } from "#items/all-held-items"; import { initHeldItems } from "#items/all-held-items";
import { initRewards } from "#items/all-rewards";
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 { initRewardPools } from "#items/init-reward-pools";
import { initTrainerItemPools } from "#items/init-trainer-item-pools"; import { initTrainerItemPools } from "#items/init-trainer-item-pools";
import { initRewards } from "#items/reward";
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 { initAchievements } from "#system/achv"; import { initAchievements } from "#system/achv";

View File

@ -1,5 +1,6 @@
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 { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id"; import { HeldItemId } from "#enums/held-item-id";
import type { PokemonType } from "#enums/pokemon-type"; import type { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
@ -36,7 +37,6 @@ import { SurviveChanceHeldItem, type SurviveChanceParams } from "#items/survive-
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/enums"; 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)) {

182
src/items/all-rewards.ts Normal file
View File

@ -0,0 +1,182 @@
import { allRewards } from "#data/data-lists";
import { PokeballType } from "#enums/pokeball";
import { RewardId } from "#enums/reward-id";
import { RarityTier } from "#enums/reward-tier";
import { TrainerItemId } from "#enums/trainer-item-id";
import { VoucherType } from "#system/voucher";
import {
AddMoneyReward,
AddPokeballReward,
AddVoucherReward,
AllPokemonFullReviveReward,
AllPokemonLevelIncrementReward,
AttackTypeBoosterRewardGenerator,
BaseStatBoosterRewardGenerator,
BerryRewardGenerator,
EvolutionItemRewardGenerator,
FormChangeItemRewardGenerator,
FusePokemonReward,
LapsingTrainerItemReward,
MintRewardGenerator,
PokemonAllMovePpRestoreReward,
PokemonHpRestoreReward,
PokemonLevelIncrementReward,
PokemonPpRestoreReward,
PokemonPpUpReward,
PokemonReviveReward,
PokemonStatusHealReward,
RememberMoveReward,
SpeciesStatBoosterRewardGenerator,
TempStatStageBoosterRewardGenerator,
TeraTypeRewardGenerator,
TmRewardGenerator,
} from "./reward";
export function initRewards() {
// Pokeball rewards
allRewards[RewardId.POKEBALL] = () => new AddPokeballReward("pb", PokeballType.POKEBALL, 5, RewardId.POKEBALL);
allRewards[RewardId.GREAT_BALL] = () => new AddPokeballReward("gb", PokeballType.GREAT_BALL, 5, RewardId.GREAT_BALL);
allRewards[RewardId.ULTRA_BALL] = () => new AddPokeballReward("ub", PokeballType.ULTRA_BALL, 5, RewardId.ULTRA_BALL);
allRewards[RewardId.ROGUE_BALL] = () => new AddPokeballReward("rb", PokeballType.ROGUE_BALL, 5, RewardId.ROGUE_BALL);
allRewards[RewardId.MASTER_BALL] = () =>
new AddPokeballReward("mb", PokeballType.MASTER_BALL, 1, RewardId.MASTER_BALL);
// Voucher rewards
allRewards[RewardId.VOUCHER] = () => new AddVoucherReward(VoucherType.REGULAR, 1, RewardId.VOUCHER);
allRewards[RewardId.VOUCHER_PLUS] = () => new AddVoucherReward(VoucherType.PLUS, 1, RewardId.VOUCHER_PLUS);
allRewards[RewardId.VOUCHER_PREMIUM] = () => new AddVoucherReward(VoucherType.PREMIUM, 1, RewardId.VOUCHER_PREMIUM);
// Money rewards
allRewards[RewardId.NUGGET] = () =>
new AddMoneyReward(
"modifierType:ModifierType.NUGGET",
"nugget",
1,
"modifierType:ModifierType.MoneyRewardModifierType.extra.small",
RewardId.NUGGET,
);
allRewards[RewardId.BIG_NUGGET] = () =>
new AddMoneyReward(
"modifierType:ModifierType.BIG_NUGGET",
"big_nugget",
2.5,
"modifierType:ModifierType.MoneyRewardModifierType.extra.moderate",
RewardId.BIG_NUGGET,
);
allRewards[RewardId.RELIC_GOLD] = () =>
new AddMoneyReward(
"modifierType:ModifierType.RELIC_GOLD",
"relic_gold",
10,
"modifierType:ModifierType.MoneyRewardModifierType.extra.large",
RewardId.RELIC_GOLD,
);
// Party-wide consumables
allRewards[RewardId.RARER_CANDY] = () =>
new AllPokemonLevelIncrementReward("modifierType:ModifierType.RARER_CANDY", "rarer_candy");
allRewards[RewardId.SACRED_ASH] = () =>
new AllPokemonFullReviveReward("modifierType:ModifierType.SACRED_ASH", "sacred_ash");
// Pokemon consumables
allRewards[RewardId.RARE_CANDY] = () =>
new PokemonLevelIncrementReward("modifierType:ModifierType.RARE_CANDY", "rare_candy");
allRewards[RewardId.EVOLUTION_ITEM] = () => new EvolutionItemRewardGenerator(false, RewardId.EVOLUTION_ITEM);
allRewards[RewardId.RARE_EVOLUTION_ITEM] = () => new EvolutionItemRewardGenerator(true, RewardId.RARE_EVOLUTION_ITEM);
allRewards[RewardId.POTION] = () =>
new PokemonHpRestoreReward("modifierType:ModifierType.POTION", "potion", RewardId.POTION, 20, 10);
allRewards[RewardId.SUPER_POTION] = () =>
new PokemonHpRestoreReward("modifierType:ModifierType.SUPER_POTION", "super_potion", RewardId.SUPER_POTION, 50, 25);
allRewards[RewardId.HYPER_POTION] = () =>
new PokemonHpRestoreReward(
"modifierType:ModifierType.HYPER_POTION",
"hyper_potion",
RewardId.HYPER_POTION,
200,
50,
);
allRewards[RewardId.MAX_POTION] = () =>
new PokemonHpRestoreReward("modifierType:ModifierType.MAX_POTION", "max_potion", RewardId.MAX_POTION, 0, 100);
allRewards[RewardId.FULL_RESTORE] = () =>
new PokemonHpRestoreReward(
"modifierType:ModifierType.FULL_RESTORE",
"full_restore",
RewardId.FULL_RESTORE,
0,
100,
true,
);
allRewards[RewardId.REVIVE] = () =>
new PokemonReviveReward("modifierType:ModifierType.REVIVE", "revive", RewardId.REVIVE, 50);
allRewards[RewardId.MAX_REVIVE] = () =>
new PokemonReviveReward("modifierType:ModifierType.MAX_REVIVE", "max_revive", RewardId.MAX_REVIVE, 100);
allRewards[RewardId.FULL_HEAL] = () =>
new PokemonStatusHealReward("modifierType:ModifierType.FULL_HEAL", "full_heal");
allRewards[RewardId.ETHER] = () =>
new PokemonPpRestoreReward("modifierType:ModifierType.ETHER", "ether", RewardId.ETHER, 10);
allRewards[RewardId.MAX_ETHER] = () =>
new PokemonPpRestoreReward("modifierType:ModifierType.MAX_ETHER", "max_ether", RewardId.MAX_ETHER, -1);
allRewards[RewardId.ELIXIR] = () =>
new PokemonAllMovePpRestoreReward("modifierType:ModifierType.ELIXIR", "elixir", RewardId.ELIXIR, 10);
allRewards[RewardId.MAX_ELIXIR] = () =>
new PokemonAllMovePpRestoreReward("modifierType:ModifierType.MAX_ELIXIR", "max_elixir", RewardId.MAX_ELIXIR, -1);
allRewards[RewardId.PP_UP] = () =>
new PokemonPpUpReward("modifierType:ModifierType.PP_UP", "pp_up", RewardId.PP_UP, 1);
allRewards[RewardId.PP_MAX] = () =>
new PokemonPpUpReward("modifierType:ModifierType.PP_MAX", "pp_max", RewardId.PP_MAX, 3);
/*REPEL] = () => new DoubleBattleChanceBoosterReward('Repel', 5),
SUPER_REPEL] = () => new DoubleBattleChanceBoosterReward('Super Repel', 10),
MAX_REPEL] = () => new DoubleBattleChanceBoosterReward('Max Repel', 25),*/
allRewards[RewardId.MINT] = () => new MintRewardGenerator();
allRewards[RewardId.TERA_SHARD] = () => new TeraTypeRewardGenerator();
allRewards[RewardId.TM_COMMON] = () => new TmRewardGenerator(RarityTier.COMMON);
allRewards[RewardId.TM_GREAT] = () => new TmRewardGenerator(RarityTier.GREAT);
allRewards[RewardId.TM_ULTRA] = () => new TmRewardGenerator(RarityTier.ULTRA);
allRewards[RewardId.MEMORY_MUSHROOM] = () =>
new RememberMoveReward("modifierType:ModifierType.MEMORY_MUSHROOM", "big_mushroom");
allRewards[RewardId.DNA_SPLICERS] = () =>
new FusePokemonReward("modifierType:ModifierType.DNA_SPLICERS", "dna_splicers");
// Form change items
allRewards[RewardId.FORM_CHANGE_ITEM] = () => new FormChangeItemRewardGenerator(false, RewardId.FORM_CHANGE_ITEM);
allRewards[RewardId.RARE_FORM_CHANGE_ITEM] = () =>
new FormChangeItemRewardGenerator(true, RewardId.RARE_FORM_CHANGE_ITEM);
// Held items
allRewards[RewardId.SPECIES_STAT_BOOSTER] = () => new SpeciesStatBoosterRewardGenerator(false);
allRewards[RewardId.RARE_SPECIES_STAT_BOOSTER] = () => new SpeciesStatBoosterRewardGenerator(true);
allRewards[RewardId.BASE_STAT_BOOSTER] = () => new BaseStatBoosterRewardGenerator();
allRewards[RewardId.ATTACK_TYPE_BOOSTER] = () => new AttackTypeBoosterRewardGenerator();
allRewards[RewardId.BERRY] = () => new BerryRewardGenerator();
// MINI_BLACK_HOLE] = () => new HeldItemReward(HeldItemId.MINI_BLACK_HOLE),
// Trainer items
allRewards[RewardId.LURE] = () => new LapsingTrainerItemReward(TrainerItemId.LURE, RewardId.LURE);
allRewards[RewardId.SUPER_LURE] = () => new LapsingTrainerItemReward(TrainerItemId.SUPER_LURE, RewardId.SUPER_LURE);
allRewards[RewardId.MAX_LURE] = () => new LapsingTrainerItemReward(TrainerItemId.MAX_LURE, RewardId.MAX_LURE);
allRewards[RewardId.TEMP_STAT_STAGE_BOOSTER] = () => new TempStatStageBoosterRewardGenerator();
allRewards[RewardId.DIRE_HIT] = () =>
new LapsingTrainerItemReward(TrainerItemId.DIRE_HIT, RewardId.TEMP_STAT_STAGE_BOOSTER);
// GOLDEN_POKEBALL] = () => new TrainerItemReward(TrainerItemId.GOLDEN_POKEBALL),
}

View File

@ -1,23 +1,23 @@
/* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */ /* biome-ignore-start lint/correctness/noUnusedImports: tsdoc imports */
import type { initRewards, Reward } from "#items/reward"; import { 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, allRewards, allTrainerItems } from "#data/data-lists"; import { allHeldItems, 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 { RewardId } from "#enums/reward-id";
import { RarityTier } 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 { rewardPool } from "#items/reward-pools";
import type { TurnEndStatusHeldItem } from "#items/turn-end-status"; import type { TurnEndStatusHeldItem } from "#items/turn-end-status";
import type { WeightedRewardWeightFunc } from "#types/rewards"; import type { WeightedRewardWeightFunc } from "#types/rewards";
@ -28,33 +28,33 @@ import { isNullOrUndefined } from "#utils/common";
*/ */
function initCommonRewardPool() { function initCommonRewardPool() {
rewardPool[RarityTier.COMMON] = [ rewardPool[RarityTier.COMMON] = [
new WeightedReward(allRewards.POKEBALL, () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), 6), { id: RewardId.POKEBALL, weight: () => (hasMaximumBalls(PokeballType.POKEBALL) ? 0 : 6), maxWeight: 6 },
new WeightedReward(allRewards.RARE_CANDY, 2), { id: RewardId.RARE_CANDY, weight: 2 },
new WeightedReward( {
allRewards.POTION, id: RewardId.POTION,
(party: Pokemon[]) => { weight: (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,
3, 3,
); );
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}, },
9, maxWeight: 9,
), },
new WeightedReward( {
allRewards.SUPER_POTION, id: RewardId.SUPER_POTION,
(party: Pokemon[]) => { weight: (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,
3, 3,
); );
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}, },
3, maxWeight: 3,
), },
new WeightedReward( {
allRewards.ETHER, id: RewardId.ETHER,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -69,11 +69,11 @@ function initCommonRewardPool() {
); );
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}, },
9, maxWeight: 9,
), },
new WeightedReward( {
allRewards.MAX_ETHER, id: RewardId.MAX_ETHER,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -88,12 +88,12 @@ function initCommonRewardPool() {
); );
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}, },
3, maxWeight: 3,
), },
new WeightedReward(allRewards.LURE, lureWeightFunc(TrainerItemId.LURE, 2)), { id: RewardId.LURE, weight: lureWeightFunc(TrainerItemId.LURE, 2) },
new WeightedReward(allRewards.TEMP_STAT_STAGE_BOOSTER, 4), { id: RewardId.TEMP_STAT_STAGE_BOOSTER, weight: 4 },
new WeightedReward(allRewards.BERRY, 2), { id: RewardId.BERRY, weight: 2 },
new WeightedReward(allRewards.TM_COMMON, 2), { id: RewardId.TM_COMMON, weight: 2 },
]; ];
} }
@ -102,11 +102,11 @@ function initCommonRewardPool() {
*/ */
function initGreatRewardPool() { function initGreatRewardPool() {
rewardPool[RarityTier.GREAT] = [ rewardPool[RarityTier.GREAT] = [
new WeightedReward(allRewards.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6), { id: RewardId.GREAT_BALL, weight: () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), maxWeight: 6 },
new WeightedReward(allRewards.PP_UP, 2), { id: RewardId.PP_UP, weight: 2 },
new WeightedReward( {
allRewards.FULL_HEAL, id: RewardId.FULL_HEAL,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min( const statusEffectPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -121,56 +121,56 @@ function initGreatRewardPool() {
); );
return statusEffectPartyMemberCount * 6; return statusEffectPartyMemberCount * 6;
}, },
18, maxWeight: 18,
), },
new WeightedReward( {
allRewards.REVIVE, id: RewardId.REVIVE,
(party: Pokemon[]) => { weight: (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, maxWeight: 27,
), },
new WeightedReward( {
allRewards.MAX_REVIVE, id: RewardId.MAX_REVIVE,
(party: Pokemon[]) => { weight: (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, maxWeight: 9,
), },
new WeightedReward( {
allRewards.SACRED_ASH, id: RewardId.SACRED_ASH,
(party: Pokemon[]) => { weight: (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, maxWeight: 1,
), },
new WeightedReward( {
allRewards.HYPER_POTION, id: RewardId.HYPER_POTION,
(party: Pokemon[]) => { weight: (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,
3, 3,
); );
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}, },
9, maxWeight: 9,
), },
new WeightedReward( {
allRewards.MAX_POTION, id: RewardId.MAX_POTION,
(party: Pokemon[]) => { weight: (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,
3, 3,
); );
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}, },
3, maxWeight: 3,
), },
new WeightedReward( {
allRewards.FULL_RESTORE, id: RewardId.FULL_RESTORE,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const statusEffectPartyMemberCount = Math.min( const statusEffectPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -190,11 +190,11 @@ function initGreatRewardPool() {
); );
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}, },
3, maxWeight: 3,
), },
new WeightedReward( {
allRewards.ELIXIR, id: RewardId.ELIXIR,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -209,11 +209,11 @@ function initGreatRewardPool() {
); );
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}, },
9, maxWeight: 9,
), },
new WeightedReward( {
allRewards.MAX_ELIXIR, id: RewardId.MAX_ELIXIR,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min( const thresholdPartyMemberCount = Math.min(
party.filter( party.filter(
p => p =>
@ -228,29 +228,29 @@ function initGreatRewardPool() {
); );
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}, },
3, maxWeight: 3,
), },
new WeightedReward(allRewards.DIRE_HIT, 4), { id: RewardId.DIRE_HIT, weight: 4 },
new WeightedReward(allRewards.SUPER_LURE, lureWeightFunc(TrainerItemId.SUPER_LURE, 4)), { id: RewardId.SUPER_LURE, weight: lureWeightFunc(TrainerItemId.SUPER_LURE, 4) },
new WeightedReward(allRewards.NUGGET, skipInLastClassicWaveOrDefault(5)), { id: RewardId.NUGGET, weight: skipInLastClassicWaveOrDefault(5) },
new WeightedReward(allRewards.SPECIES_STAT_BOOSTER, 4), { id: RewardId.SPECIES_STAT_BOOSTER, weight: 4 },
new WeightedReward( {
allRewards.EVOLUTION_ITEM, id: RewardId.EVOLUTION_ITEM,
() => { weight: () => {
return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8); return Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15), 8);
}, },
8, maxWeight: 8,
), },
new WeightedReward( {
allRewards.MAP, id: TrainerItemId.MAP,
() => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0), weight: () => (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex < 180 ? 2 : 0),
2, maxWeight: 2,
), },
new WeightedReward(allRewards.SOOTHE_BELL, 2), { id: HeldItemId.SOOTHE_BELL, weight: 2 },
new WeightedReward(allRewards.TM_GREAT, 3), { id: RewardId.TM_GREAT, weight: 3 },
new WeightedReward( {
allRewards.MEMORY_MUSHROOM, id: RewardId.MEMORY_MUSHROOM,
(party: Pokemon[]) => { weight: (party: Pokemon[]) => {
if (!party.find(p => p.getLearnableLevelMoves().length)) { if (!party.find(p => p.getLearnableLevelMoves().length)) {
return 0; return 0;
} }
@ -259,20 +259,22 @@ function initGreatRewardPool() {
.reduce((highestLevel: number, level: number) => Math.max(highestLevel, level), 1); .reduce((highestLevel: number, level: number) => Math.max(highestLevel, level), 1);
return Math.min(Math.ceil(highestPartyLevel / 20), 4); return Math.min(Math.ceil(highestPartyLevel / 20), 4);
}, },
4, maxWeight: 4,
), },
new WeightedReward(allRewards.BASE_STAT_BOOSTER, 3), { id: RewardId.BASE_STAT_BOOSTER, weight: 3 },
new WeightedReward(allRewards.TERA_SHARD, (party: Pokemon[]) => {
party.filter( id: RewardId.TERA_SHARD,
p => weight: (party: Pokemon[]) =>
!(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)), party.filter(
).length > 0 p =>
? 1 !(p.hasSpecies(SpeciesId.TERAPAGOS) || p.hasSpecies(SpeciesId.OGERPON) || p.hasSpecies(SpeciesId.SHEDINJA)),
: 0, ).length > 0
), ? 1
new WeightedReward( : 0,
allRewards.DNA_SPLICERS, },
(party: Pokemon[]) => { {
id: RewardId.DNA_SPLICERS,
weight: (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) {
return 4; return 4;
@ -283,13 +285,14 @@ function initGreatRewardPool() {
} }
return 0; return 0;
}, },
4, maxWeight: 4,
), },
new WeightedReward( {
allRewards.VOUCHER, id: RewardId.VOUCHER,
(_party: Pokemon[], rerollCount: number) => (!globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0), weight: (_party: Pokemon[], rerollCount: number) =>
1, !globalScene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0,
), maxWeight: 1,
},
]; ];
} }
@ -298,46 +301,49 @@ function initGreatRewardPool() {
*/ */
function initUltraRewardPool() { function initUltraRewardPool() {
rewardPool[RarityTier.ULTRA] = [ rewardPool[RarityTier.ULTRA] = [
new WeightedReward(allRewards.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15), { id: RewardId.ULTRA_BALL, weight: () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), maxWeight: 15 },
new WeightedReward(allRewards.MAX_LURE, lureWeightFunc(TrainerItemId.MAX_LURE, 4)), { id: RewardId.MAX_LURE, weight: lureWeightFunc(TrainerItemId.MAX_LURE, 4) },
new WeightedReward(allRewards.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), { id: RewardId.BIG_NUGGET, weight: skipInLastClassicWaveOrDefault(12) },
new WeightedReward(allRewards.PP_MAX, 3), { id: RewardId.PP_MAX, weight: 3 },
new WeightedReward(allRewards.MINT, 4), { id: RewardId.MINT, weight: 4 },
new WeightedReward( {
allRewards.RARE_EVOLUTION_ITEM, id: RewardId.RARE_EVOLUTION_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32), weight: () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 15) * 4, 32),
32, maxWeight: 32,
), },
new WeightedReward( {
allRewards.FORM_CHANGE_ITEM, id: RewardId.FORM_CHANGE_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, weight: () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
24, maxWeight: 24,
), },
new WeightedReward(allRewards.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), { id: TrainerItemId.AMULET_COIN, weight: skipInLastClassicWaveOrDefault(3) },
new WeightedReward(allRewards.EVIOLITE, (party: Pokemon[]) => { {
const { gameMode, gameData } = globalScene; id: HeldItemId.EVIOLITE,
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { weight: (party: Pokemon[]) => {
return party.some(p => { const { gameMode, gameData } = globalScene;
// Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
if ( return party.some(p => {
!p.isMax() && // Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd
(p.getSpeciesForm(true).speciesId in pokemonEvolutions || if (
(p.isFusion() && p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions)) !p.isMax() &&
) { (p.getSpeciesForm(true).speciesId in pokemonEvolutions ||
// Check if Pokemon is already holding an Eviolite (p.isFusion() && p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))
return !p.heldItemManager.hasItem(HeldItemId.EVIOLITE); ) {
} // Check if Pokemon is already holding an Eviolite
return false; return !p.heldItemManager.hasItem(HeldItemId.EVIOLITE);
}) }
? 10 return false;
: 0; })
} ? 10
return 0; : 0;
}), }
new WeightedReward(allRewards.RARE_SPECIES_STAT_BOOSTER, 12), return 0;
new WeightedReward( },
allRewards.LEEK, },
(party: Pokemon[]) => { { id: RewardId.RARE_SPECIES_STAT_BOOSTER, weight: 12 },
{
id: HeldItemId.LEEK,
weight: (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
return party.some( return party.some(
@ -349,11 +355,11 @@ function initUltraRewardPool() {
? 12 ? 12
: 0; : 0;
}, },
12, maxWeight: 12,
), },
new WeightedReward( {
allRewards.TOXIC_ORB, id: HeldItemId.TOXIC_ORB,
(party: Pokemon[]) => { weight: (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]);
@ -395,11 +401,11 @@ function initUltraRewardPool() {
? 10 ? 10
: 0; : 0;
}, },
10, maxWeight: 10,
), },
new WeightedReward( {
allRewards.FLAME_ORB, id: HeldItemId.FLAME_ORB,
(party: Pokemon[]) => { weight: (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]);
@ -441,11 +447,11 @@ function initUltraRewardPool() {
? 10 ? 10
: 0; : 0;
}, },
10, maxWeight: 10,
), },
new WeightedReward( {
allRewards.MYSTICAL_ROCK, id: HeldItemId.MYSTICAL_ROCK,
(party: Pokemon[]) => { weight: (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);
const isHoldingMax = stack === allHeldItems[HeldItemId.MYSTICAL_ROCK].maxStackCount; const isHoldingMax = stack === allHeldItems[HeldItemId.MYSTICAL_ROCK].maxStackCount;
@ -488,68 +494,68 @@ function initUltraRewardPool() {
? 10 ? 10
: 0; : 0;
}, },
10, maxWeight: 10,
), },
new WeightedReward(allRewards.REVIVER_SEED, 4), { id: HeldItemId.REVIVER_SEED, weight: 4 },
new WeightedReward(allRewards.CANDY_JAR, skipInLastClassicWaveOrDefault(5)), { id: TrainerItemId.CANDY_JAR, weight: skipInLastClassicWaveOrDefault(5) },
new WeightedReward(allRewards.ATTACK_TYPE_BOOSTER, 9), { id: RewardId.ATTACK_TYPE_BOOSTER, weight: 9 },
new WeightedReward(allRewards.TM_ULTRA, 11), { id: RewardId.TM_ULTRA, weight: 11 },
new WeightedReward(allRewards.RARER_CANDY, 4), { id: RewardId.RARER_CANDY, weight: 4 },
new WeightedReward(allRewards.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)), { id: HeldItemId.GOLDEN_PUNCH, weight: skipInLastClassicWaveOrDefault(2) },
new WeightedReward(allRewards.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), { id: TrainerItemId.IV_SCANNER, weight: skipInLastClassicWaveOrDefault(4) },
new WeightedReward(allRewards.EXP_CHARM, skipInLastClassicWaveOrDefault(8)), { id: TrainerItemId.EXP_CHARM, weight: skipInLastClassicWaveOrDefault(8) },
new WeightedReward(allRewards.EXP_SHARE, skipInLastClassicWaveOrDefault(10)), { id: TrainerItemId.EXP_SHARE, weight: skipInLastClassicWaveOrDefault(10) },
new WeightedReward( {
allRewards.TERA_ORB, id: TrainerItemId.TERA_ORB,
() => weight: () =>
!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, maxWeight: 4,
), },
new WeightedReward(allRewards.QUICK_CLAW, 3), { id: HeldItemId.QUICK_CLAW, weight: 3 },
new WeightedReward(allRewards.WIDE_LENS, 7), { id: HeldItemId.WIDE_LENS, weight: 7 },
]; ];
} }
function initRogueRewardPool() { function initRogueRewardPool() {
rewardPool[RarityTier.ROGUE] = [ rewardPool[RarityTier.ROGUE] = [
new WeightedReward(allRewards.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16), { id: RewardId.ROGUE_BALL, weight: () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), maxWeight: 16 },
new WeightedReward(allRewards.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), { id: RewardId.RELIC_GOLD, weight: skipInLastClassicWaveOrDefault(2) },
new WeightedReward(allRewards.LEFTOVERS, 3), { id: HeldItemId.LEFTOVERS, weight: 3 },
new WeightedReward(allRewards.SHELL_BELL, 3), { id: HeldItemId.SHELL_BELL, weight: 3 },
new WeightedReward(allRewards.BERRY_POUCH, 4), { id: TrainerItemId.BERRY_POUCH, weight: 4 },
new WeightedReward(allRewards.GRIP_CLAW, 5), { id: HeldItemId.GRIP_CLAW, weight: 5 },
new WeightedReward(allRewards.SCOPE_LENS, 4), { id: HeldItemId.SCOPE_LENS, weight: 4 },
new WeightedReward(allRewards.BATON, 2), { id: HeldItemId.BATON, weight: 2 },
new WeightedReward(allRewards.SOUL_DEW, 7), { id: HeldItemId.SOUL_DEW, weight: 7 },
new WeightedReward(allRewards.CATCHING_CHARM, () => (!globalScene.gameMode.isClassic ? 4 : 0), 4), { id: TrainerItemId.CATCHING_CHARM, weight: () => (!globalScene.gameMode.isClassic ? 4 : 0), maxWeight: 4 },
new WeightedReward(allRewards.ABILITY_CHARM, skipInClassicAfterWave(189, 6)), { id: TrainerItemId.ABILITY_CHARM, weight: skipInClassicAfterWave(189, 6) },
new WeightedReward(allRewards.FOCUS_BAND, 5), { id: HeldItemId.FOCUS_BAND, weight: 5 },
new WeightedReward(allRewards.KINGS_ROCK, 3), { id: HeldItemId.KINGS_ROCK, weight: 3 },
new WeightedReward(allRewards.LOCK_CAPSULE, () => (globalScene.gameMode.isClassic ? 0 : 3)), { id: TrainerItemId.LOCK_CAPSULE, weight: () => (globalScene.gameMode.isClassic ? 0 : 3) },
new WeightedReward(allRewards.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)), { id: TrainerItemId.SUPER_EXP_CHARM, weight: skipInLastClassicWaveOrDefault(8) },
new WeightedReward( {
allRewards.RARE_FORM_CHANGE_ITEM, id: RewardId.RARE_FORM_CHANGE_ITEM,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6, weight: () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 6,
24, maxWeight: 24,
), },
new WeightedReward( {
allRewards.MEGA_BRACELET, id: TrainerItemId.MEGA_BRACELET,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, weight: () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
36, maxWeight: 36,
), },
new WeightedReward( {
allRewards.DYNAMAX_BAND, id: TrainerItemId.DYNAMAX_BAND,
() => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9, weight: () => Math.min(Math.ceil(globalScene.currentBattle.waveIndex / 50), 4) * 9,
36, maxWeight: 36,
), },
new WeightedReward( {
allRewards.VOUCHER_PLUS, id: RewardId.VOUCHER_PLUS,
(_party: Pokemon[], rerollCount: number) => weight: (_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, maxWeight: 3,
), },
]; ];
} }
@ -558,37 +564,37 @@ function initRogueRewardPool() {
*/ */
function initMasterRewardPool() { function initMasterRewardPool() {
rewardPool[RarityTier.MASTER] = [ rewardPool[RarityTier.MASTER] = [
new WeightedReward(allRewards.MASTER_BALL, () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), 24), { id: RewardId.MASTER_BALL, weight: () => (hasMaximumBalls(PokeballType.MASTER_BALL) ? 0 : 24), maxWeight: 24 },
new WeightedReward(allRewards.SHINY_CHARM, 14), { id: TrainerItemId.SHINY_CHARM, weight: 14 },
new WeightedReward(allRewards.HEALING_CHARM, 18), { id: TrainerItemId.HEALING_CHARM, weight: 18 },
new WeightedReward(allRewards.MULTI_LENS, 18), { id: HeldItemId.MULTI_LENS, weight: 18 },
new WeightedReward( {
allRewards.VOUCHER_PREMIUM, id: RewardId.VOUCHER_PREMIUM,
(_party: Pokemon[], rerollCount: number) => weight: (_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, maxWeight: 5,
), },
new WeightedReward( {
allRewards.DNA_SPLICERS, id: RewardId.DNA_SPLICERS,
(party: Pokemon[]) => weight: (party: Pokemon[]) =>
!(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) && !(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) &&
!globalScene.gameMode.isSplicedOnly && !globalScene.gameMode.isSplicedOnly &&
party.filter(p => !p.fusionSpecies).length > 1 party.filter(p => !p.fusionSpecies).length > 1
? 24 ? 24
: 0, : 0,
24, maxWeight: 24,
), },
new WeightedReward( {
allRewards.MINI_BLACK_HOLE, id: HeldItemId.MINI_BLACK_HOLE,
() => weight: () =>
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))
? 1 ? 1
: 0, : 0,
1, maxWeight: 1,
), },
]; ];
} }

View File

@ -1,15 +1,14 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { allRewards } from "#data/data-lists";
import { RewardPoolType } from "#enums/reward-pool-type"; import { RewardPoolType } from "#enums/reward-pool-type";
import { RarityTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
import type { PlayerPokemon, Pokemon } from "#field/pokemon"; import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import type { RewardFunc, RewardPool, RewardPoolWeights } from "#types/rewards"; import type { RewardPool, RewardPoolWeights, RewardSpecs } from "#types/rewards";
import { isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common"; import { isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common";
import { getPartyLuckValue } from "#utils/party"; import { getPartyLuckValue } from "#utils/party";
import { type Reward, RewardGenerator, RewardOption, type RewardOverride, TrainerItemReward } from "./reward"; import type { RewardOption } from "./reward";
import { rewardPool, rewardPoolWeights } from "./reward-pools"; import { rewardPool, rewardPoolWeights } from "./reward-pools";
import { getRewardDefaultTier } from "./reward-utils"; import { generateRewardOptionFromId, generateRewardOptionFromSpecs, isTrainerItemId } from "./reward-utils";
/* /*
This file still contains several functions to generate rewards from pools. The hierarchy of these functions is explained here. This file still contains several functions to generate rewards from pools. The hierarchy of these functions is explained here.
@ -26,9 +25,6 @@ cases to assign modifiers. This usage is now deprecated, as we have separate poo
However, `getNewRewardOption` is not called directly by `generatePlayerRewardOptions`. Instead, it is filtered 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. 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. This will allow more customization in creating pools for challenges, MEs etc.
*/ */
@ -36,7 +32,7 @@ export interface CustomRewardSettings {
guaranteedRarityTiers?: RarityTier[]; guaranteedRarityTiers?: RarityTier[];
guaranteedRewardOptions?: RewardOption[]; 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). */ /** 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[]; guaranteedRewardSpecs?: RewardSpecs[];
/** /**
* 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. * 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 * @example
@ -68,9 +64,8 @@ export interface CustomRewardSettings {
export function generateRewardPoolWeights(pool: RewardPool, party: Pokemon[], rerollCount = 0) { export function generateRewardPoolWeights(pool: RewardPool, party: Pokemon[], rerollCount = 0) {
for (const tier of Object.keys(pool)) { for (const tier of Object.keys(pool)) {
const poolWeights = pool[tier].map(w => { const poolWeights = pool[tier].map(w => {
if (w.reward instanceof TrainerItemReward) { if (isTrainerItemId(w.id)) {
const id = w.reward.itemId; if (globalScene.trainerItems.isMaxStack(w.id)) {
if (globalScene.trainerItems.isMaxStack(id)) {
return 0; return 0;
} }
} }
@ -158,26 +153,18 @@ export function generatePlayerRewardOptions(
options.push(getRewardOptionWithRetry(pool, weights, options, retryCount, party, tier)); options.push(getRewardOptionWithRetry(pool, weights, options, retryCount, party, tier));
} }
} else { } else {
// Guaranteed mod options first // Guaranteed reward options first
if (customRewardSettings?.guaranteedRewardOptions && customRewardSettings.guaranteedRewardOptions.length > 0) { if (customRewardSettings?.guaranteedRewardOptions && customRewardSettings.guaranteedRewardOptions.length > 0) {
options.push(...customRewardSettings.guaranteedRewardOptions!); options.push(...customRewardSettings.guaranteedRewardOptions!);
} }
// Guaranteed mod functions second if (customRewardSettings?.guaranteedRewardSpecs && customRewardSettings.guaranteedRewardSpecs.length > 0) {
if (customRewardSettings.guaranteedRewardFuncs && customRewardSettings.guaranteedRewardFuncs.length > 0) { for (const specs of customRewardSettings.guaranteedRewardSpecs) {
customRewardSettings.guaranteedRewardFuncs!.forEach((mod, _i) => { const rewardOption = generateRewardOptionFromSpecs(specs);
const rewardId = Object.keys(allRewards).find(k => allRewards[k] === mod) as string; if (rewardOption) {
const guaranteedMod: Reward = allRewards[rewardId]?.(); options.push(rewardOption);
// 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 // Guaranteed tiers third
@ -285,42 +272,30 @@ function getNewRewardOption(
return null; return null;
} }
let reward: Reward | RewardGenerator | null = pool[tier][index].reward; const rewardOption = generateRewardOptionFromId(pool[tier][index].id, 0, tier, upgradeCount);
if (reward instanceof RewardGenerator) { if (rewardOption === null) {
reward = (reward as RewardGenerator).generateReward(party); console.log(RarityTier[tier], upgradeCount);
if (reward === null) { return getNewRewardOption(pool, weights, party, tier, upgradeCount, ++retryCount);
console.log(RarityTier[tier], upgradeCount);
return getNewRewardOption(pool, weights, party, tier, upgradeCount, ++retryCount);
}
} }
console.log(reward); console.log(rewardOption);
return new RewardOption(reward as Reward, upgradeCount!, tier); // TODO: is this bang correct? return rewardOption;
} }
/** /**
* Replaces the {@linkcode Reward} of the entries within {@linkcode options} with any * 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. * up to the smallest amount of entries between {@linkcode options} and the override array.
* @param options Array of naturally rolled {@linkcode RewardOption}s * @param options Array of naturally rolled {@linkcode RewardOption}s
* @param party Array of the player's current party * @param party Array of the player's current party
*/ */
export function overridePlayerRewardOptions(options: RewardOption[], party: PlayerPokemon[]) { export function overridePlayerRewardOptions(options: RewardOption[]) {
const minLength = Math.min(options.length, Overrides.REWARD_OVERRIDE.length); const minLength = Math.min(options.length, Overrides.REWARD_OVERRIDE.length);
for (let i = 0; i < minLength; i++) { for (let i = 0; i < minLength; i++) {
const override: RewardOverride = Overrides.REWARD_OVERRIDE[i]; const specs: RewardSpecs = Overrides.REWARD_OVERRIDE[i];
const rewardFunc = allRewards[override.name]; const rewardOption = generateRewardOptionFromSpecs(specs);
let reward: Reward | RewardGenerator | null = rewardFunc(); if (rewardOption) {
options[i] = rewardOption;
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);
} }
} }
} }

View File

@ -4,20 +4,20 @@ import type { HeldItemId } from "#enums/held-item-id";
import { getRewardCategory, RewardCategoryId, RewardId } from "#enums/reward-id"; import { getRewardCategory, RewardCategoryId, RewardId } from "#enums/reward-id";
import type { RarityTier } 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";
import type { RewardFunc, RewardPoolId } from "#types/rewards"; import type { RewardFunc, RewardPoolId, RewardSpecs } from "#types/rewards";
import { getHeldItemTier } from "./held-item-default-tiers"; import { heldItemRarities } from "./held-item-default-tiers";
import { import {
type HeldItemReward, HeldItemReward,
type PokemonMoveReward, type PokemonMoveReward,
type RememberMoveReward, type RememberMoveReward,
type Reward, type Reward,
RewardGenerator, RewardGenerator,
RewardOption, RewardOption,
type TmReward, type TmReward,
type TrainerItemReward, TrainerItemReward,
} from "./reward"; } from "./reward";
import { getRewardTier } from "./reward-defaults-tiers"; import { rewardRarities } from "./reward-defaults-tiers";
import { getTrainerItemTier } from "./trainer-item-default-tiers"; import { trainerItemRarities } from "./trainer-item-default-tiers";
export function isTmReward(reward: Reward): reward is TmReward { export function isTmReward(reward: Reward): reward is TmReward {
return getRewardCategory(reward.id) === RewardCategoryId.TM; return getRewardCategory(reward.id) === RewardCategoryId.TM;
@ -42,33 +42,43 @@ export function generateReward(rewardFunc: RewardFunc, pregenArgs?: any[]): Rewa
return reward instanceof RewardGenerator ? reward.generateReward(globalScene.getPlayerParty(), pregenArgs) : reward; return reward instanceof RewardGenerator ? reward.generateReward(globalScene.getPlayerParty(), pregenArgs) : reward;
} }
/** export function generateRewardOptionFromId(
* Generates a Reward Option from a given function id: RewardPoolId,
* @param rewardFunc cost = 0,
* @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc. tierOverride?: RarityTier,
*/ upgradeCount = 0,
export function generateRewardOption(rewardFunc: RewardFunc, pregenArgs?: any[]): RewardOption | null { pregenArgs?: any[],
): RewardOption | null {
if (isHeldItemId(id)) {
const reward = new HeldItemReward(id);
const tier = tierOverride ?? heldItemRarities[id];
return new RewardOption(reward, upgradeCount, tier, cost);
}
if (isTrainerItemId(id)) {
const reward = new TrainerItemReward(id);
const tier = tierOverride ?? trainerItemRarities[id];
return new RewardOption(reward, upgradeCount, tier, cost);
}
const rewardFunc = allRewards[id];
const reward = generateReward(rewardFunc, pregenArgs); const reward = generateReward(rewardFunc, pregenArgs);
if (reward) { if (reward) {
const tier = getRewardDefaultTier(reward); const tier = tierOverride ?? rewardRarities[id];
return new RewardOption(reward, 0, tier); return new RewardOption(reward, upgradeCount, tier, cost);
} }
return null; return null;
} }
/** export function generateRewardOptionFromSpecs(
* Finds the default rarity tier for a given reward. For unique held item or trainer item rewards, specs: RewardSpecs,
* falls back to the default rarity tier for the item. cost = 0,
* @param reward The {@linkcode Reward} to determine the tier for. overrideTier?: RarityTier,
*/ ): RewardOption | null {
export function getRewardDefaultTier(reward: Reward): RarityTier { if (typeof specs === "number") {
if (reward.id === RewardId.HELD_ITEM) { return generateRewardOptionFromId(specs, cost, overrideTier);
return getHeldItemTier((reward as HeldItemReward).itemId);
} }
if (reward.id === RewardId.TRAINER_ITEM) { return generateRewardOptionFromId(specs.id, cost, overrideTier, 0, specs.args);
return getTrainerItemTier((reward as TrainerItemReward).itemId);
}
return getRewardTier(reward.id);
} }
export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: number): RewardOption[] { export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: number): RewardOption[] {
@ -78,26 +88,26 @@ export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: n
const options = [ const options = [
[ [
new RewardOption(allRewards.POTION(), 0, baseCost * 0.2), generateRewardOptionFromId(RewardId.POTION, baseCost * 0.2),
new RewardOption(allRewards.ETHER(), 0, baseCost * 0.4), generateRewardOptionFromId(RewardId.ETHER, baseCost * 0.4),
new RewardOption(allRewards.REVIVE(), 0, baseCost * 2), generateRewardOptionFromId(RewardId.REVIVE, baseCost * 2),
], ],
[ [
new RewardOption(allRewards.SUPER_POTION(), 0, baseCost * 0.45), generateRewardOptionFromId(RewardId.SUPER_POTION, baseCost * 0.45),
new RewardOption(allRewards.FULL_HEAL(), 0, baseCost), generateRewardOptionFromId(RewardId.FULL_HEAL, baseCost),
], ],
[new RewardOption(allRewards.ELIXIR(), 0, baseCost), new RewardOption(allRewards.MAX_ETHER(), 0, baseCost)], [generateRewardOptionFromId(RewardId.ELIXIR, baseCost), generateRewardOptionFromId(RewardId.MAX_ETHER, baseCost)],
[ [
new RewardOption(allRewards.HYPER_POTION(), 0, baseCost * 0.8), generateRewardOptionFromId(RewardId.HYPER_POTION, baseCost * 0.8),
new RewardOption(allRewards.MAX_REVIVE(), 0, baseCost * 2.75), generateRewardOptionFromId(RewardId.MAX_REVIVE, baseCost * 2.75),
new RewardOption(allRewards.MEMORY_MUSHROOM(), 0, baseCost * 4), generateRewardOptionFromId(RewardId.MEMORY_MUSHROOM, baseCost * 4),
], ],
[ [
new RewardOption(allRewards.MAX_POTION(), 0, baseCost * 1.5), generateRewardOptionFromId(RewardId.MAX_POTION, baseCost * 1.5),
new RewardOption(allRewards.MAX_ELIXIR(), 0, baseCost * 2.5), generateRewardOptionFromId(RewardId.MAX_ELIXIR, baseCost * 2.5),
], ],
[new RewardOption(allRewards.FULL_RESTORE(), 0, baseCost * 2.25)], [generateRewardOptionFromId(RewardId.FULL_RESTORE, baseCost * 2.25)],
[new RewardOption(allRewards.SACRED_ASH(), 0, baseCost * 10)], [generateRewardOptionFromId(RewardId.SACRED_ASH, baseCost * 10)],
]; ];
return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat();
} }

View File

@ -4,7 +4,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { EvolutionItem, FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions"; import { EvolutionItem, FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions";
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters"; import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters";
import { tmPoolTiers, tmSpecies } from "#balance/tms"; import { tmPoolTiers, tmSpecies } from "#balance/tms";
import { allHeldItems, allMoves, allRewards, allTrainerItems } from "#data/data-lists"; import { allHeldItems, allMoves, allTrainerItems } from "#data/data-lists";
import { getLevelTotalExp } from "#data/exp"; import { getLevelTotalExp } from "#data/exp";
import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers"; import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers";
import { getNatureName, getNatureStatMultiplier } from "#data/nature"; import { getNatureName, getNatureStatMultiplier } from "#data/nature";
@ -17,7 +17,7 @@ import { HeldItemId } from "#enums/held-item-id";
import { LearnMoveType } from "#enums/learn-move-type"; import { LearnMoveType } from "#enums/learn-move-type";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { PokeballType } from "#enums/pokeball"; import type { PokeballType } from "#enums/pokeball";
import { PokemonType } from "#enums/pokemon-type"; import { PokemonType } from "#enums/pokemon-type";
import { RewardId } from "#enums/reward-id"; import { RewardId } from "#enums/reward-id";
import { RarityTier } from "#enums/reward-tier"; import { RarityTier } from "#enums/reward-tier";
@ -32,15 +32,10 @@ import { permanentStatToHeldItem, statBoostItems } from "#items/base-stat-booste
import { berryTypeToHeldItem } from "#items/berry"; import { berryTypeToHeldItem } from "#items/berry";
import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#items/held-item-pool"; import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#items/held-item-pool";
import { formChangeItemName } from "#items/item-utility"; import { formChangeItemName } from "#items/item-utility";
import { import { SPECIES_STAT_BOOSTER_ITEMS, type SpeciesStatBoostHeldItem } from "#items/stat-booster";
SPECIES_STAT_BOOSTER_ITEMS,
type SpeciesStatBoosterItemId,
type SpeciesStatBoostHeldItem,
} from "#items/stat-booster";
import { TrainerItemEffect, tempStatToTrainerItem } from "#items/trainer-item"; import { TrainerItemEffect, tempStatToTrainerItem } from "#items/trainer-item";
import type { PokemonMove } from "#moves/pokemon-move"; import type { PokemonMove } from "#moves/pokemon-move";
import { getVoucherTypeIcon, getVoucherTypeName, VoucherType } from "#system/voucher"; import { getVoucherTypeIcon, getVoucherTypeName, type VoucherType } from "#system/voucher";
import type { RewardFunc, WeightedRewardWeightFunc } from "#types/rewards";
import type { Exact } from "#types/type-helpers"; import type { Exact } from "#types/type-helpers";
import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler"; import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler";
import { PartyUiHandler } from "#ui/party-ui-handler"; import { PartyUiHandler } from "#ui/party-ui-handler";
@ -90,7 +85,6 @@ for example. The entries of rewardInitObj are used in the RewardPool.
There are some more derived classes, in particular: There are some more derived classes, in particular:
RewardGenerator, which creates Reward instances from a certain group (e.g. TMs, nature mints, or berries); RewardGenerator, which creates Reward instances from a certain group (e.g. TMs, nature mints, or berries);
WeightedReward, which is a Reward with an attached weight or weight function to be used in pools;
and RewardOption, which is displayed during the select reward phase at the end of each encounter. and RewardOption, which is displayed during the select reward phase at the end of each encounter.
*/ */
@ -589,7 +583,7 @@ export class PokemonReviveReward extends PokemonHpRestoreReward {
} }
} }
class AllPokemonFullReviveReward extends Reward { export class AllPokemonFullReviveReward extends Reward {
constructor(localeKey: string, iconImage: string) { constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType"); super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType");
this.id = RewardId.SACRED_ASH; this.id = RewardId.SACRED_ASH;
@ -863,7 +857,7 @@ export class RememberMoveReward extends PokemonReward {
} }
} }
class BerryRewardGenerator extends RewardGenerator { export class BerryRewardGenerator extends RewardGenerator {
constructor() { constructor() {
super((_party: Pokemon[], pregenArgs?: any[]) => { super((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in BerryType) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in BerryType) {
@ -877,7 +871,7 @@ class BerryRewardGenerator extends RewardGenerator {
} }
} }
class MintRewardGenerator extends RewardGenerator { export class MintRewardGenerator extends RewardGenerator {
constructor() { constructor() {
super((_party: Pokemon[], pregenArgs?: any[]) => { super((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in Nature) {
@ -889,7 +883,7 @@ class MintRewardGenerator extends RewardGenerator {
} }
} }
class TeraTypeRewardGenerator extends RewardGenerator { export class TeraTypeRewardGenerator extends RewardGenerator {
constructor() { constructor() {
super((party: Pokemon[], pregenArgs?: any[]) => { super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) {
@ -1225,7 +1219,7 @@ export class FusePokemonReward extends PokemonReward {
} }
} }
class AttackTypeBoosterRewardGenerator extends RewardGenerator { export class AttackTypeBoosterRewardGenerator extends RewardGenerator {
constructor() { constructor() {
super((party: Pokemon[], pregenArgs?: any[]) => { super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) {
@ -1240,7 +1234,7 @@ class AttackTypeBoosterRewardGenerator extends RewardGenerator {
} }
} }
class BaseStatBoosterRewardGenerator extends RewardGenerator { export class BaseStatBoosterRewardGenerator extends RewardGenerator {
constructor() { constructor() {
super((_party: Pokemon[], pregenArgs?: any[]) => { super((_party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs) { if (pregenArgs) {
@ -1252,7 +1246,7 @@ class BaseStatBoosterRewardGenerator extends RewardGenerator {
} }
} }
class TempStatStageBoosterRewardGenerator extends RewardGenerator { export class TempStatStageBoosterRewardGenerator extends RewardGenerator {
public static readonly items: Record<TempBattleStat, string> = { public static readonly items: Record<TempBattleStat, string> = {
[Stat.ATK]: "x_attack", [Stat.ATK]: "x_attack",
[Stat.DEF]: "x_defense", [Stat.DEF]: "x_defense",
@ -1280,7 +1274,7 @@ class TempStatStageBoosterRewardGenerator extends RewardGenerator {
* the current list of {@linkcode items}. * the current list of {@linkcode items}.
* @extends RewardGenerator * @extends RewardGenerator
*/ */
class SpeciesStatBoosterRewardGenerator extends RewardGenerator { export class SpeciesStatBoosterRewardGenerator extends RewardGenerator {
/** Object comprised of the currently available species-based stat boosting held items */ /** Object comprised of the currently available species-based stat boosting held items */
constructor(rare: boolean) { constructor(rare: boolean) {
@ -1347,7 +1341,7 @@ class SpeciesStatBoosterRewardGenerator extends RewardGenerator {
} }
} }
class TmRewardGenerator extends RewardGenerator { export class TmRewardGenerator extends RewardGenerator {
constructor(tier: RarityTier) { constructor(tier: RarityTier) {
super((party: Pokemon[], pregenArgs?: any[]) => { super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in MoveId) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in MoveId) {
@ -1380,7 +1374,7 @@ class TmRewardGenerator extends RewardGenerator {
} }
} }
class EvolutionItemRewardGenerator extends RewardGenerator { export class EvolutionItemRewardGenerator extends RewardGenerator {
constructor(rare: boolean, id: RewardId) { constructor(rare: boolean, id: RewardId) {
super((party: Pokemon[], pregenArgs?: any[]) => { super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in EvolutionItem) { if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in EvolutionItem) {
@ -1512,305 +1506,6 @@ export class FormChangeItemRewardGenerator extends RewardGenerator {
} }
} }
export class WeightedReward {
public reward: Reward | RewardGenerator;
public weight: number | WeightedRewardWeightFunc;
public maxWeight: number | WeightedRewardWeightFunc;
constructor(
rewardFunc: RewardFunc,
weight: number | WeightedRewardWeightFunc,
maxWeight?: number | WeightedRewardWeightFunc,
) {
this.reward = rewardFunc();
this.weight = weight;
this.maxWeight = maxWeight || (!(weight instanceof Function) ? weight : 0);
}
}
type BaseRewardOverride = {
name: Exclude<RewardKeys, GeneratorRewardOverride["name"]>;
count?: number;
};
/** Type for modifiers and held items that are constructed via {@linkcode RewardGenerator}. */
export type GeneratorRewardOverride = {
count?: number;
} & (
| {
name: keyof Pick<typeof rewardInitObj, "SPECIES_STAT_BOOSTER" | "RARE_SPECIES_STAT_BOOSTER">;
type?: SpeciesStatBoosterItemId;
}
| {
name: keyof Pick<typeof rewardInitObj, "TEMP_STAT_STAGE_BOOSTER">;
type?: TempBattleStat;
}
| {
name: keyof Pick<typeof rewardInitObj, "BASE_STAT_BOOSTER">;
type?: Stat;
}
| {
name: keyof Pick<typeof rewardInitObj, "MINT">;
type?: Nature;
}
| {
name: keyof Pick<typeof rewardInitObj, "ATTACK_TYPE_BOOSTER" | "TERA_SHARD">;
type?: PokemonType;
}
| {
name: keyof Pick<typeof rewardInitObj, "BERRY">;
type?: BerryType;
}
| {
name: keyof Pick<typeof rewardInitObj, "EVOLUTION_ITEM" | "RARE_EVOLUTION_ITEM">;
type?: EvolutionItem;
}
| {
name: keyof Pick<typeof rewardInitObj, "FORM_CHANGE_ITEM" | "RARE_FORM_CHANGE_ITEM">;
type?: FormChangeItem;
}
| {
name: keyof Pick<typeof rewardInitObj, "TM_COMMON" | "TM_GREAT" | "TM_ULTRA">;
type?: MoveId;
}
);
/** Type used to construct modifiers and held items for overriding purposes. */
export type RewardOverride = GeneratorRewardOverride | BaseRewardOverride;
const rewardInitObj = Object.freeze({
// Pokeball rewards
POKEBALL: () => new AddPokeballReward("pb", PokeballType.POKEBALL, 5, RewardId.POKEBALL),
GREAT_BALL: () => new AddPokeballReward("gb", PokeballType.GREAT_BALL, 5, RewardId.GREAT_BALL),
ULTRA_BALL: () => new AddPokeballReward("ub", PokeballType.ULTRA_BALL, 5, RewardId.ULTRA_BALL),
ROGUE_BALL: () => new AddPokeballReward("rb", PokeballType.ROGUE_BALL, 5, RewardId.ROGUE_BALL),
MASTER_BALL: () => new AddPokeballReward("mb", PokeballType.MASTER_BALL, 1, RewardId.MASTER_BALL),
// Voucher rewards
VOUCHER: () => new AddVoucherReward(VoucherType.REGULAR, 1, RewardId.VOUCHER),
VOUCHER_PLUS: () => new AddVoucherReward(VoucherType.PLUS, 1, RewardId.VOUCHER_PLUS),
VOUCHER_PREMIUM: () => new AddVoucherReward(VoucherType.PREMIUM, 1, RewardId.VOUCHER_PREMIUM),
// Money rewards
NUGGET: () =>
new AddMoneyReward(
"modifierType:ModifierType.NUGGET",
"nugget",
1,
"modifierType:ModifierType.MoneyRewardModifierType.extra.small",
RewardId.NUGGET,
),
BIG_NUGGET: () =>
new AddMoneyReward(
"modifierType:ModifierType.BIG_NUGGET",
"big_nugget",
2.5,
"modifierType:ModifierType.MoneyRewardModifierType.extra.moderate",
RewardId.BIG_NUGGET,
),
RELIC_GOLD: () =>
new AddMoneyReward(
"modifierType:ModifierType.RELIC_GOLD",
"relic_gold",
10,
"modifierType:ModifierType.MoneyRewardModifierType.extra.large",
RewardId.RELIC_GOLD,
),
// Party-wide consumables
RARER_CANDY: () => new AllPokemonLevelIncrementReward("modifierType:ModifierType.RARER_CANDY", "rarer_candy"),
SACRED_ASH: () => new AllPokemonFullReviveReward("modifierType:ModifierType.SACRED_ASH", "sacred_ash"),
// Pokemon consumables
RARE_CANDY: () => new PokemonLevelIncrementReward("modifierType:ModifierType.RARE_CANDY", "rare_candy"),
EVOLUTION_ITEM: () => new EvolutionItemRewardGenerator(false, RewardId.EVOLUTION_ITEM),
RARE_EVOLUTION_ITEM: () => new EvolutionItemRewardGenerator(true, RewardId.RARE_EVOLUTION_ITEM),
POTION: () => new PokemonHpRestoreReward("modifierType:ModifierType.POTION", "potion", RewardId.POTION, 20, 10),
SUPER_POTION: () =>
new PokemonHpRestoreReward("modifierType:ModifierType.SUPER_POTION", "super_potion", RewardId.SUPER_POTION, 50, 25),
HYPER_POTION: () =>
new PokemonHpRestoreReward(
"modifierType:ModifierType.HYPER_POTION",
"hyper_potion",
RewardId.HYPER_POTION,
200,
50,
),
MAX_POTION: () =>
new PokemonHpRestoreReward("modifierType:ModifierType.MAX_POTION", "max_potion", RewardId.MAX_POTION, 0, 100),
FULL_RESTORE: () =>
new PokemonHpRestoreReward(
"modifierType:ModifierType.FULL_RESTORE",
"full_restore",
RewardId.FULL_RESTORE,
0,
100,
true,
),
REVIVE: () => new PokemonReviveReward("modifierType:ModifierType.REVIVE", "revive", RewardId.REVIVE, 50),
MAX_REVIVE: () =>
new PokemonReviveReward("modifierType:ModifierType.MAX_REVIVE", "max_revive", RewardId.MAX_REVIVE, 100),
FULL_HEAL: () => new PokemonStatusHealReward("modifierType:ModifierType.FULL_HEAL", "full_heal"),
ETHER: () => new PokemonPpRestoreReward("modifierType:ModifierType.ETHER", "ether", RewardId.ETHER, 10),
MAX_ETHER: () =>
new PokemonPpRestoreReward("modifierType:ModifierType.MAX_ETHER", "max_ether", RewardId.MAX_ETHER, -1),
ELIXIR: () => new PokemonAllMovePpRestoreReward("modifierType:ModifierType.ELIXIR", "elixir", RewardId.ELIXIR, 10),
MAX_ELIXIR: () =>
new PokemonAllMovePpRestoreReward("modifierType:ModifierType.MAX_ELIXIR", "max_elixir", RewardId.MAX_ELIXIR, -1),
PP_UP: () => new PokemonPpUpReward("modifierType:ModifierType.PP_UP", "pp_up", RewardId.PP_UP, 1),
PP_MAX: () => new PokemonPpUpReward("modifierType:ModifierType.PP_MAX", "pp_max", RewardId.PP_MAX, 3),
/*REPEL: () => new DoubleBattleChanceBoosterReward('Repel', 5),
SUPER_REPEL: () => new DoubleBattleChanceBoosterReward('Super Repel', 10),
MAX_REPEL: () => new DoubleBattleChanceBoosterReward('Max Repel', 25),*/
MINT: () => new MintRewardGenerator(),
TERA_SHARD: () => new TeraTypeRewardGenerator(),
TM_COMMON: () => new TmRewardGenerator(RarityTier.COMMON),
TM_GREAT: () => new TmRewardGenerator(RarityTier.GREAT),
TM_ULTRA: () => new TmRewardGenerator(RarityTier.ULTRA),
MEMORY_MUSHROOM: () => new RememberMoveReward("modifierType:ModifierType.MEMORY_MUSHROOM", "big_mushroom"),
DNA_SPLICERS: () => new FusePokemonReward("modifierType:ModifierType.DNA_SPLICERS", "dna_splicers"),
// Form change items
FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(false, RewardId.FORM_CHANGE_ITEM),
RARE_FORM_CHANGE_ITEM: () => new FormChangeItemRewardGenerator(true, RewardId.RARE_FORM_CHANGE_ITEM),
// Held items
REVIVER_SEED: () => new HeldItemReward(HeldItemId.REVIVER_SEED),
WHITE_HERB: () => new HeldItemReward(HeldItemId.WHITE_HERB),
SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(false),
RARE_SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterRewardGenerator(true),
BASE_STAT_BOOSTER: () => new BaseStatBoosterRewardGenerator(),
ATTACK_TYPE_BOOSTER: () => new AttackTypeBoosterRewardGenerator(),
MYSTICAL_ROCK: () => new HeldItemReward(HeldItemId.MYSTICAL_ROCK),
BERRY: () => new BerryRewardGenerator(),
LUCKY_EGG: () => new HeldItemReward(HeldItemId.LUCKY_EGG),
GOLDEN_EGG: () => new HeldItemReward(HeldItemId.GOLDEN_EGG),
SOOTHE_BELL: () => new HeldItemReward(HeldItemId.SOOTHE_BELL),
SCOPE_LENS: () => new HeldItemReward(HeldItemId.SCOPE_LENS),
LEEK: () => new HeldItemReward(HeldItemId.LEEK),
EVIOLITE: () => new HeldItemReward(HeldItemId.EVIOLITE),
SOUL_DEW: () => new HeldItemReward(HeldItemId.SOUL_DEW),
GOLDEN_PUNCH: () => new HeldItemReward(HeldItemId.GOLDEN_PUNCH),
GRIP_CLAW: () => new HeldItemReward(HeldItemId.GRIP_CLAW),
WIDE_LENS: () => new HeldItemReward(HeldItemId.WIDE_LENS),
MULTI_LENS: () => new HeldItemReward(HeldItemId.MULTI_LENS),
FOCUS_BAND: () => new HeldItemReward(HeldItemId.FOCUS_BAND),
QUICK_CLAW: () => new HeldItemReward(HeldItemId.QUICK_CLAW),
KINGS_ROCK: () => new HeldItemReward(HeldItemId.KINGS_ROCK),
LEFTOVERS: () => new HeldItemReward(HeldItemId.LEFTOVERS),
SHELL_BELL: () => new HeldItemReward(HeldItemId.SHELL_BELL),
TOXIC_ORB: () => new HeldItemReward(HeldItemId.TOXIC_ORB),
FLAME_ORB: () => new HeldItemReward(HeldItemId.FLAME_ORB),
BATON: () => new HeldItemReward(HeldItemId.BATON),
MINI_BLACK_HOLE: () => new HeldItemReward(HeldItemId.MINI_BLACK_HOLE),
// Trainer items
MEGA_BRACELET: () => new TrainerItemReward(TrainerItemId.MEGA_BRACELET),
DYNAMAX_BAND: () => new TrainerItemReward(TrainerItemId.DYNAMAX_BAND),
TERA_ORB: () => new TrainerItemReward(TrainerItemId.TERA_ORB),
MAP: () => new TrainerItemReward(TrainerItemId.MAP),
LURE: () => new LapsingTrainerItemReward(TrainerItemId.LURE, RewardId.LURE),
SUPER_LURE: () => new LapsingTrainerItemReward(TrainerItemId.SUPER_LURE, RewardId.SUPER_LURE),
MAX_LURE: () => new LapsingTrainerItemReward(TrainerItemId.MAX_LURE, RewardId.MAX_LURE),
TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterRewardGenerator(),
DIRE_HIT: () => new LapsingTrainerItemReward(TrainerItemId.DIRE_HIT, RewardId.TEMP_STAT_STAGE_BOOSTER),
EXP_SHARE: () => new TrainerItemReward(TrainerItemId.EXP_SHARE),
EXP_BALANCE: () => new TrainerItemReward(TrainerItemId.EXP_BALANCE),
OVAL_CHARM: () => new TrainerItemReward(TrainerItemId.OVAL_CHARM),
EXP_CHARM: () => new TrainerItemReward(TrainerItemId.EXP_CHARM),
SUPER_EXP_CHARM: () => new TrainerItemReward(TrainerItemId.SUPER_EXP_CHARM),
AMULET_COIN: () => new TrainerItemReward(TrainerItemId.AMULET_COIN),
LOCK_CAPSULE: () => new TrainerItemReward(TrainerItemId.LOCK_CAPSULE),
HEALING_CHARM: () => new TrainerItemReward(TrainerItemId.HEALING_CHARM),
CANDY_JAR: () => new TrainerItemReward(TrainerItemId.CANDY_JAR),
BERRY_POUCH: () => new TrainerItemReward(TrainerItemId.BERRY_POUCH),
SHINY_CHARM: () => new TrainerItemReward(TrainerItemId.SHINY_CHARM),
ABILITY_CHARM: () => new TrainerItemReward(TrainerItemId.ABILITY_CHARM),
CATCHING_CHARM: () => new TrainerItemReward(TrainerItemId.CATCHING_CHARM),
IV_SCANNER: () => new TrainerItemReward(TrainerItemId.IV_SCANNER),
GOLDEN_POKEBALL: () => new TrainerItemReward(TrainerItemId.GOLDEN_POKEBALL),
// Tokens //TODO: do we even need them here?
ENEMY_DAMAGE_BOOSTER: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_BOOSTER),
ENEMY_DAMAGE_REDUCTION: () => new TrainerItemReward(TrainerItemId.ENEMY_DAMAGE_REDUCTION),
//ENEMY_SUPER_EFFECT_BOOSTER: () => new Reward('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_custom_super_effective'),
ENEMY_HEAL: () => new TrainerItemReward(TrainerItemId.ENEMY_HEAL),
ENEMY_ATTACK_POISON_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_POISON_CHANCE),
ENEMY_ATTACK_PARALYZE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_PARALYZE_CHANCE),
ENEMY_ATTACK_BURN_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ATTACK_BURN_CHANCE),
ENEMY_STATUS_EFFECT_HEAL_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_STATUS_EFFECT_HEAL_CHANCE),
ENEMY_ENDURE_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_ENDURE_CHANCE),
ENEMY_FUSED_CHANCE: () => new TrainerItemReward(TrainerItemId.ENEMY_FUSED_CHANCE),
// Items from mystery encounters
MYSTERY_ENCOUNTER_SHUCKLE_JUICE_GOOD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_GOOD),
MYSTERY_ENCOUNTER_SHUCKLE_JUICE_BAD: () => new HeldItemReward(HeldItemId.SHUCKLE_JUICE_BAD),
MYSTERY_ENCOUNTER_OLD_GATEAU: () => new HeldItemReward(HeldItemId.OLD_GATEAU),
MYSTERY_ENCOUNTER_BLACK_SLUDGE: () => new TrainerItemReward(TrainerItemId.BLACK_SLUDGE),
MYSTERY_ENCOUNTER_MACHO_BRACE: () => new HeldItemReward(HeldItemId.MACHO_BRACE),
MYSTERY_ENCOUNTER_GOLDEN_BUG_NET: () => new TrainerItemReward(TrainerItemId.GOLDEN_BUG_NET),
});
/**
* The initial set of modifier types, used to generate the modifier pool.
*/
export type Rewards = typeof rewardInitObj;
export type RewardKeys = keyof typeof rewardInitObj;
export class RewardOption { export class RewardOption {
public type: Reward; public type: Reward;
public upgradeCount: number; public upgradeCount: number;
@ -1825,12 +1520,6 @@ export class RewardOption {
} }
} }
export function initRewards() {
for (const [key, value] of Object.entries(rewardInitObj)) {
allRewards[key] = value;
}
}
// TODO: If necessary, add the rest of the modifier types here. // TODO: If necessary, add the rest of the modifier types here.
// For now, doing the minimal work until the modifier rework lands. // For now, doing the minimal work until the modifier rework lands.
const RewardConstructorMap = Object.freeze({ const RewardConstructorMap = Object.freeze({

View File

@ -22,9 +22,9 @@ import { Unlockables } from "#enums/unlockables";
import { VariantTier } from "#enums/variant-tier"; import { VariantTier } from "#enums/variant-tier";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { HeldItemConfiguration } from "#items/held-item-data-types"; import { HeldItemConfiguration } from "#items/held-item-data-types";
import { type RewardOverride } from "#items/reward";
import { TrainerItemConfiguration } from "#items/trainer-item-data-types"; import { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import { Variant } from "#sprites/variant"; import { Variant } from "#sprites/variant";
import { RewardSpecs } from "#types/rewards";
/** /**
* This comment block exists to prevent IDEs from automatically removing unused imports * This comment block exists to prevent IDEs from automatically removing unused imports
@ -273,25 +273,18 @@ class DefaultOverrides {
* STARTING_HELD_ITEM_OVERRIDE = [{name: "BERRY"}] * STARTING_HELD_ITEM_OVERRIDE = [{name: "BERRY"}]
* ``` * ```
*/ */
/** Override array of {@linkcode RewardOverride}s used to provide held items to first party member when starting a new game. */
readonly STARTING_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = []; readonly STARTING_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = [];
/** Override array of {@linkcode RewardOverride}s used to provide held items to enemies on spawn. */
readonly OPP_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = []; readonly OPP_TRAINER_ITEMS_OVERRIDE: TrainerItemConfiguration = [];
/** Override array of {@linkcode RewardOverride}s used to provide held items to first party member when starting a new game. */
readonly STARTING_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = []; readonly STARTING_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = [];
/** Override array of {@linkcode RewardOverride}s used to provide held items to enemies on spawn. */
readonly OPP_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = []; readonly OPP_HELD_ITEMS_OVERRIDE: HeldItemConfiguration = [];
/** /**
* Override array of {@linkcode RewardOverride}s used to replace the generated reward rolls after a wave.
*
* If less entries are listed than rolled, only those entries will be used to replace the corresponding items while the rest randomly generated. * If less entries are listed than rolled, only those entries will be used to replace the corresponding items while the rest randomly generated.
* If more entries are listed than rolled, only the first X entries will be used, where X is the number of items rolled. * If more entries are listed than rolled, only the first X entries will be used, where X is the number of items rolled.
* *
* Note that, for all items in the array, `count` is not used. * Note that, for all items in the array, `count` is not used.
*/ */
readonly REWARD_OVERRIDE: RewardOverride[] = []; readonly REWARD_OVERRIDE: RewardSpecs[] = [];
/** /**
* If `true`, disable all non-scripted opponent trainer encounters. * If `true`, disable all non-scripted opponent trainer encounters.

View File

@ -373,7 +373,7 @@ export class SelectRewardPhase extends BattlePhase {
const newItemCount = const newItemCount =
(this.customRewardSettings.guaranteedRarityTiers?.length ?? 0) + (this.customRewardSettings.guaranteedRarityTiers?.length ?? 0) +
(this.customRewardSettings.guaranteedRewardOptions?.length ?? 0) + (this.customRewardSettings.guaranteedRewardOptions?.length ?? 0) +
(this.customRewardSettings.guaranteedRewardFuncs?.length ?? 0); (this.customRewardSettings.guaranteedRewardSpecs?.length ?? 0);
if (this.customRewardSettings.fillRemaining) { if (this.customRewardSettings.fillRemaining) {
const originalCount = rewardCountHolder.value; const originalCount = rewardCountHolder.value;
rewardCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount; rewardCountHolder.value = originalCount > newItemCount ? originalCount : newItemCount;

View File

@ -155,7 +155,7 @@ describe("SelectRewardPhase", () => {
await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]);
scene.money = 1000000; scene.money = 1000000;
const customRewards: CustomRewardSettings = { const customRewards: CustomRewardSettings = {
guaranteedRewardFuncs: [ guaranteedRewardSpecs: [
allRewards.MEMORY_MUSHROOM, allRewards.MEMORY_MUSHROOM,
allRewards.TM_ULTRA, allRewards.TM_ULTRA,
allRewards.LEFTOVERS, allRewards.LEFTOVERS,
@ -238,7 +238,7 @@ describe("SelectRewardPhase", () => {
await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]);
scene.money = 1000000; scene.money = 1000000;
const customRewards: CustomRewardSettings = { const customRewards: CustomRewardSettings = {
guaranteedRewardFuncs: [allRewards.MEMORY_MUSHROOM, allRewards.TM_COMMON], guaranteedRewardSpecs: [allRewards.MEMORY_MUSHROOM, allRewards.TM_COMMON],
guaranteedRarityTiers: [RarityTier.MASTER, RarityTier.MASTER], guaranteedRarityTiers: [RarityTier.MASTER, RarityTier.MASTER],
}; };
const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards); const selectRewardPhase = new SelectRewardPhase(0, undefined, customRewards);
@ -261,7 +261,7 @@ describe("SelectRewardPhase", () => {
await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]); await game.classicMode.startBattle([SpeciesId.ABRA, SpeciesId.VOLCARONA]);
scene.money = 1000000; scene.money = 1000000;
const customRewards: CustomRewardSettings = { const customRewards: CustomRewardSettings = {
guaranteedRewardFuncs: [allRewards.MEMORY_MUSHROOM], guaranteedRewardSpecs: [allRewards.MEMORY_MUSHROOM],
guaranteedRarityTiers: [RarityTier.MASTER], guaranteedRarityTiers: [RarityTier.MASTER],
fillRemaining: true, fillRemaining: true,
}; };

View File

@ -18,10 +18,10 @@ import { StatusEffect } from "#enums/status-effect";
import type { Unlockables } from "#enums/unlockables"; import type { Unlockables } from "#enums/unlockables";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import type { HeldItemConfiguration } from "#items/held-item-data-types"; import type { HeldItemConfiguration } from "#items/held-item-data-types";
import type { RewardOverride } from "#items/reward";
import type { TrainerItemConfiguration } from "#items/trainer-item-data-types"; import type { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import type { Variant } from "#sprites/variant"; import type { Variant } from "#sprites/variant";
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper"; import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
import type { RewardSpecs } from "#types/rewards";
import { coerceArray, shiftCharCodes } from "#utils/common"; import { coerceArray, shiftCharCodes } from "#utils/common";
import { vi } from "vitest"; import { vi } from "vitest";
@ -557,7 +557,7 @@ export class OverridesHelper extends GameManagerHelper {
* @param items - The items to be rolled * @param items - The items to be rolled
* @returns `this` * @returns `this`
*/ */
public itemRewards(items: RewardOverride[]): this { public rewards(items: RewardSpecs[]): this {
vi.spyOn(Overrides, "REWARD_OVERRIDE", "get").mockReturnValue(items); vi.spyOn(Overrides, "REWARD_OVERRIDE", "get").mockReturnValue(items);
this.log("Item allRewards set to:", items); this.log("Item allRewards set to:", items);
return this; return this;