Using RewardSpecs for predetermined reward generation

This commit is contained in:
Wlowscha 2025-08-04 01:35:04 +02:00
parent 49092aad55
commit 9a1f773257
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
20 changed files with 160 additions and 162 deletions

View File

@ -9,6 +9,14 @@ 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;

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

@ -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.
@ -36,7 +35,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 +67,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 +156,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,18 +275,15 @@ 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);
if (reward === null) {
console.log(RarityTier[tier], upgradeCount); console.log(RarityTier[tier], upgradeCount);
return getNewRewardOption(pool, weights, party, tier, upgradeCount, ++retryCount); 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;
} }
/** /**
@ -306,21 +293,13 @@ function getNewRewardOption(
* @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

@ -2,8 +2,9 @@ import { globalScene } from "#app/global-scene";
import { allRewards } from "#data/data-lists"; import { allRewards } from "#data/data-lists";
import type { HeldItemId } from "#enums/held-item-id"; 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 { 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 { heldItemRarities } from "./held-item-default-tiers"; import { heldItemRarities } from "./held-item-default-tiers";
import { import {
HeldItemReward, HeldItemReward,
@ -41,33 +42,45 @@ 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 id 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 generateRewardOptionFromId(id: RewardPoolId, cost = 0, pregenArgs?: any[]): RewardOption | null { pregenArgs?: any[],
): RewardOption | null {
if (isHeldItemId(id)) { if (isHeldItemId(id)) {
const reward = new HeldItemReward(id); const reward = new HeldItemReward(id);
const tier = heldItemRarities[id]; const tier = tierOverride ?? heldItemRarities[id];
return new RewardOption(reward, 0, tier, cost); return new RewardOption(reward, upgradeCount, tier, cost);
} }
if (isTrainerItemId(id)) { if (isTrainerItemId(id)) {
const reward = new TrainerItemReward(id); const reward = new TrainerItemReward(id);
const tier = trainerItemRarities[id]; const tier = tierOverride ?? trainerItemRarities[id];
return new RewardOption(reward, 0, tier, cost); return new RewardOption(reward, upgradeCount, tier, cost);
} }
const rewardFunc = allRewards[id]; const rewardFunc = allRewards[id];
const reward = generateReward(rewardFunc, pregenArgs); const reward = generateReward(rewardFunc, pregenArgs);
if (reward) { if (reward) {
const tier = rewardRarities[id]; const tier = tierOverride ?? rewardRarities[id];
return new RewardOption(reward, 0, tier, cost); return new RewardOption(reward, upgradeCount, tier, cost);
} }
return null; return null;
} }
export function generateRewardOptionFromSpecs(
specs: RewardSpecs,
cost = 0,
overrideTier?: RarityTier,
): RewardOption | null {
if (typeof specs === "number") {
return generateRewardOptionFromId(specs, cost, overrideTier);
}
return generateRewardOptionFromId(specs.id, cost, overrideTier, 0, specs.args);
}
export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: number): RewardOption[] { export function getPlayerShopRewardOptionsForWave(waveIndex: number, baseCost: number): RewardOption[] {
if (!(waveIndex % 10)) { if (!(waveIndex % 10)) {
return []; return [];

View File

@ -25,6 +25,7 @@ import { HeldItemConfiguration } from "#items/held-item-data-types";
import { type RewardOverride } from "#items/reward"; 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
@ -291,7 +292,7 @@ class DefaultOverrides {
* *
* 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,
}; };