Pools can now take a HaldItemConfigurationId without weights; set up table of item tiers; used custom pools in Clowning Around ME

This commit is contained in:
Wlowscha 2025-06-19 19:55:17 +02:00
parent 46204067f5
commit f1c6cf4419
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
6 changed files with 147 additions and 145 deletions

View File

@ -1,6 +1,5 @@
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import {
generateModifierType,
initBattleWithEnemyConfig,
leaveEncounterWithoutBattle,
loadCustomMovesForEncounter,
@ -12,8 +11,6 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config";
import { TrainerPartyCompoundTemplate } from "#app/data/trainers/TrainerPartyTemplate";
import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate";
import { RewardTier } from "#enums/reward-tier";
import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PartyMemberStrength } from "#enums/party-member-strength";
import { globalScene } from "#app/global-scene";
@ -24,10 +21,7 @@ import { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type";
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
import { AbilityId } from "#enums/ability-id";
import {
applyAbilityOverrideToPokemon,
applyModifierTypeToPlayerPokemon,
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { applyAbilityOverrideToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { PokemonType } from "#enums/pokemon-type";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
@ -38,8 +32,6 @@ import i18next from "i18next";
import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
import type { PlayerPokemon } from "#app/field/pokemon";
import { PokemonMove } from "#app/data/moves/pokemon-move";
import { BerryModifier } from "#app/modifier/modifier";
import { BerryType } from "#enums/berry-type";
import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id";
import { EncounterBattleAnim } from "#app/data/battle-anims";
@ -49,7 +41,10 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { EncounterAnim } from "#enums/encounter-anims";
import { Challenges } from "#enums/challenges";
import { MoveUseMode } from "#enums/move-use-mode";
import { allAbilities, modifierTypes } from "#app/data/data-lists";
import { allAbilities } from "#app/data/data-lists";
import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id";
import { getHeldItemTier } from "#app/items/held-item-tiers";
import { assignItemsFromConfiguration } from "#app/items/held-item-pool";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/clowningAround";
@ -283,16 +278,16 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
const party = globalScene.getPlayerParty();
let mostHeldItemsPokemon = party[0];
let count = mostHeldItemsPokemon
.getHeldItems()
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
.reduce((v, m) => v + m.stackCount, 0);
let count = mostHeldItemsPokemon.heldItemManager
.getTransferableHeldItems()
.filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY))
.reduce((v, m) => v + mostHeldItemsPokemon.heldItemManager.getStack(m), 0);
for (const pokemon of party) {
const nextCount = pokemon
.getHeldItems()
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
.reduce((v, m) => v + m.stackCount, 0);
const nextCount = pokemon.heldItemManager
.getTransferableHeldItems()
.filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY))
.reduce((v, m) => v + pokemon.heldItemManager.getStack(m), 0);
if (nextCount > count) {
mostHeldItemsPokemon = pokemon;
count = nextCount;
@ -301,16 +296,31 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender());
const items = mostHeldItemsPokemon.getHeldItems();
const items = mostHeldItemsPokemon.heldItemManager
.getTransferableHeldItems()
.filter(m => !isItemInCategory(m, HeldItemCategoryId.BERRY));
// Shuffles Berries (if they have any)
const oldBerries = mostHeldItemsPokemon.heldItemManager
.getHeldItems()
.filter(m => isItemInCategory(m, HeldItemCategoryId.BERRY));
let numBerries = 0;
for (const m of items.filter(m => m instanceof BerryModifier)) {
numBerries += m.stackCount;
globalScene.removeModifier(m);
for (const berry of oldBerries) {
const stack = mostHeldItemsPokemon.heldItemManager.getStack(berry);
numBerries += stack;
mostHeldItemsPokemon.heldItemManager.remove(berry, stack);
}
generateItemsOfTier(mostHeldItemsPokemon, numBerries, "Berries");
assignItemsFromConfiguration(
[
{
entry: HeldItemCategoryId.BERRY,
count: numBerries,
},
],
mostHeldItemsPokemon,
);
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
// For the purpose of this ME, Soothe Bells and Lucky Eggs are counted as Ultra tier
@ -318,20 +328,36 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
let numUltra = 0;
let numRogue = 0;
for (const m of items.filter(m => m.isTransferable && !(m instanceof BerryModifier))) {
const type = m.type.withTierFromPool(ModifierPoolType.PLAYER, party);
const tier = type.tier ?? RewardTier.ULTRA;
if (type.id === "GOLDEN_EGG" || tier === RewardTier.ROGUE) {
numRogue += m.stackCount;
globalScene.removeModifier(m);
} else if (type.id === "LUCKY_EGG" || type.id === "SOOTHE_BELL" || tier === RewardTier.ULTRA) {
numUltra += m.stackCount;
globalScene.removeModifier(m);
for (const m of items) {
const tier = getHeldItemTier(m) ?? RewardTier.ULTRA;
const stack = mostHeldItemsPokemon.heldItemManager.getStack(m);
if (tier === RewardTier.ROGUE) {
numRogue += stack;
} else if (tier === RewardTier.ULTRA) {
numUltra += stack;
}
mostHeldItemsPokemon.heldItemManager.remove(m, stack);
}
generateItemsOfTier(mostHeldItemsPokemon, numUltra, RewardTier.ULTRA);
generateItemsOfTier(mostHeldItemsPokemon, numRogue, RewardTier.ROGUE);
assignItemsFromConfiguration(
[
{
entry: ultraPool,
count: numUltra,
},
],
mostHeldItemsPokemon,
);
assignItemsFromConfiguration(
[
{
entry: roguePool,
count: numRogue,
},
],
mostHeldItemsPokemon,
);
})
.withOptionPhase(async () => {
leaveEncounterWithoutBattle(true);
@ -487,68 +513,21 @@ function onYesAbilitySwap(resolve) {
selectPokemonForOption(onPokemonSelected, onPokemonNotSelected);
}
function generateItemsOfTier(pokemon: PlayerPokemon, numItems: number, tier: RewardTier | "Berries") {
// These pools have to be defined at runtime so that modifierTypes exist
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
// This is to prevent "over-generating" a random item of a certain type during item swaps
const ultraPool = [
[modifierTypes.REVIVER_SEED, 1],
[modifierTypes.GOLDEN_PUNCH, 5],
[modifierTypes.ATTACK_TYPE_BOOSTER, 99],
[modifierTypes.QUICK_CLAW, 3],
[modifierTypes.WIDE_LENS, 3],
];
const ultraPool = [
{ entry: HeldItemCategoryId.TYPE_ATTACK_BOOSTER, weight: 1 },
{ entry: HeldItemId.REVIVER_SEED, weight: 1 },
{ entry: HeldItemId.GOLDEN_PUNCH, weight: 1 },
{ entry: HeldItemId.QUICK_CLAW, weight: 1 },
{ entry: HeldItemId.WIDE_LENS, weight: 1 },
];
const roguePool = [
[modifierTypes.LEFTOVERS, 4],
[modifierTypes.SHELL_BELL, 4],
[modifierTypes.SOUL_DEW, 10],
[modifierTypes.SCOPE_LENS, 1],
[modifierTypes.BATON, 1],
[modifierTypes.FOCUS_BAND, 5],
[modifierTypes.KINGS_ROCK, 3],
[modifierTypes.GRIP_CLAW, 5],
];
const berryPool = [
[BerryType.APICOT, 3],
[BerryType.ENIGMA, 2],
[BerryType.GANLON, 3],
[BerryType.LANSAT, 3],
[BerryType.LEPPA, 2],
[BerryType.LIECHI, 3],
[BerryType.LUM, 2],
[BerryType.PETAYA, 3],
[BerryType.SALAC, 2],
[BerryType.SITRUS, 2],
[BerryType.STARF, 3],
];
let pool: any[];
if (tier === "Berries") {
pool = berryPool;
} else {
pool = tier === RewardTier.ULTRA ? ultraPool : roguePool;
}
for (let i = 0; i < numItems; i++) {
if (pool.length === 0) {
// Stop generating new items if somehow runs out of items to spawn
return;
}
const randIndex = randSeedInt(pool.length);
const newItemType = pool[randIndex];
let newMod: PokemonHeldItemModifierType;
if (tier === "Berries") {
newMod = generateModifierType(modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType;
} else {
newMod = generateModifierType(newItemType[0]) as PokemonHeldItemModifierType;
}
applyModifierTypeToPlayerPokemon(pokemon, newMod);
// Decrement max stacks and remove from pool if at max
newItemType[1]--;
if (newItemType[1] <= 0) {
pool.splice(randIndex, 1);
}
}
}
const roguePool = [
{ entry: HeldItemId.LEFTOVERS, weight: 1 },
{ entry: HeldItemId.SHELL_BELL, weight: 1 },
{ entry: HeldItemId.SOUL_DEW, weight: 1 },
{ entry: HeldItemId.SCOPE_LENS, weight: 1 },
{ entry: HeldItemId.BATON, weight: 1 },
{ entry: HeldItemId.FOCUS_BAND, weight: 1 },
{ entry: HeldItemId.KINGS_ROCK, weight: 1 },
{ entry: HeldItemId.GRIP_CLAW, weight: 1 },
];

View File

@ -46,9 +46,10 @@ import type { PokeballType } from "#enums/pokeball";
import { doShinySparkleAnim } from "#app/field/anims";
import { TrainerType } from "#enums/trainer-type";
import { timedEventManager } from "#app/global-event-manager";
import { HeldItemCategoryId, HeldItemId, isItemInCategory } from "#enums/held-item-id";
import { HeldItemCategoryId, type HeldItemId, isItemInCategory } from "#enums/held-item-id";
import { allHeldItems } from "#app/items/all-held-items";
import { RewardTier } from "#enums/reward-tier";
import { getHeldItemTier } from "#app/items/held-item-tiers";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/globalTradeSystem";
@ -419,7 +420,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
const chosenPokemon: PlayerPokemon = encounter.misc.chosenPokemon;
// Check tier of the traded item, the received item will be one tier up
let tier = globalTradeItemTiers[heldItemId] ?? RewardTier.GREAT;
let tier = getHeldItemTier(heldItemId) ?? RewardTier.GREAT;
// Increment tier by 1
if (tier < RewardTier.MASTER) {
@ -975,38 +976,3 @@ function generateRandomTraderName() {
const trainerNames = trainerNameString.split(" & ");
return trainerNames[randInt(trainerNames.length)];
}
const globalTradeItemTiers = {
[HeldItemCategoryId.BERRY]: RewardTier.COMMON,
[HeldItemCategoryId.BASE_STAT_BOOST]: RewardTier.GREAT,
[HeldItemId.WHITE_HERB]: RewardTier.GREAT,
[HeldItemId.METAL_POWDER]: RewardTier.GREAT,
[HeldItemId.QUICK_POWDER]: RewardTier.GREAT,
[HeldItemId.DEEP_SEA_SCALE]: RewardTier.GREAT,
[HeldItemId.DEEP_SEA_TOOTH]: RewardTier.GREAT,
[HeldItemCategoryId.TYPE_ATTACK_BOOSTER]: RewardTier.ULTRA,
[HeldItemId.REVIVER_SEED]: RewardTier.ULTRA,
[HeldItemId.LIGHT_BALL]: RewardTier.ULTRA,
[HeldItemId.EVIOLITE]: RewardTier.ULTRA,
[HeldItemId.QUICK_CLAW]: RewardTier.ULTRA,
[HeldItemId.MYSTICAL_ROCK]: RewardTier.ULTRA,
[HeldItemId.WIDE_LENS]: RewardTier.ULTRA,
[HeldItemId.GOLDEN_PUNCH]: RewardTier.ULTRA,
[HeldItemId.TOXIC_ORB]: RewardTier.ULTRA,
[HeldItemId.FLAME_ORB]: RewardTier.ULTRA,
[HeldItemId.LUCKY_EGG]: RewardTier.ULTRA,
[HeldItemId.FOCUS_BAND]: RewardTier.ROGUE,
[HeldItemId.KINGS_ROCK]: RewardTier.ROGUE,
[HeldItemId.LEFTOVERS]: RewardTier.ROGUE,
[HeldItemId.SHELL_BELL]: RewardTier.ROGUE,
[HeldItemId.GRIP_CLAW]: RewardTier.ROGUE,
[HeldItemId.SOUL_DEW]: RewardTier.ROGUE,
[HeldItemId.BATON]: RewardTier.ROGUE,
[HeldItemId.GOLDEN_EGG]: RewardTier.ULTRA,
[HeldItemId.MINI_BLACK_HOLE]: RewardTier.MASTER,
[HeldItemId.MULTI_LENS]: RewardTier.MASTER,
};

View File

@ -123,7 +123,7 @@ export type HeldItemCategoryId = (typeof HeldItemCategoryId)[keyof typeof HeldIt
const ITEM_CATEGORY_MASK = 0xFF00
function getHeldItemCategory(itemId: HeldItemId): HeldItemCategoryId {
export function getHeldItemCategory(itemId: HeldItemId): HeldItemCategoryId {
return itemId & ITEM_CATEGORY_MASK;
}

View File

@ -50,7 +50,7 @@ export function isHeldItemCategoryEntry(entry: any): entry is HeldItemCategoryEn
}
type HeldItemPoolEntry = {
entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs;
entry: HeldItemId | HeldItemCategoryId | HeldItemCategoryEntry | HeldItemSpecs;
weight: number | HeldItemWeightFunc;
};
@ -65,7 +65,7 @@ export type HeldItemTieredPool = {
};
type HeldItemConfigurationEntry = {
entry: HeldItemId | HeldItemCategoryEntry | HeldItemSpecs | HeldItemPool;
entry: HeldItemId | HeldItemCategoryId | HeldItemCategoryEntry | HeldItemSpecs | HeldItemPool;
count?: number | (() => number);
};

View File

@ -2,7 +2,7 @@ import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
import type Pokemon from "#app/field/pokemon";
import { coerceArray, getEnumValues, randSeedFloat, randSeedInt } from "#app/utils/common";
import { BerryType } from "#enums/berry-type";
import { HeldItemCategoryId, HeldItemId } from "#enums/held-item-id";
import { HeldItemCategoryId, HeldItemId, isCategoryId } from "#enums/held-item-id";
import { HeldItemPoolType } from "#enums/modifier-pool-type";
import type { PokemonType } from "#enums/pokemon-type";
import { RewardTier } from "#enums/reward-tier";
@ -263,6 +263,9 @@ function getNewHeldItemFromPool(pool: HeldItemPool, pokemon: Pokemon, party?: Po
const entry = pool[pickWeightedIndex(weights)].entry;
if (typeof entry === "number") {
if (isCategoryId(entry)) {
return getNewHeldItemFromCategory(entry, party ?? pokemon, {}, pokemon) as HeldItemId;
}
return entry as HeldItemId;
}
@ -273,12 +276,24 @@ function getNewHeldItemFromPool(pool: HeldItemPool, pokemon: Pokemon, party?: Po
return entry as HeldItemSpecs;
}
function assignItemsFromCategory(id: HeldItemCategoryId, pokemon: Pokemon, count: number) {
for (let i = 1; i <= count; i++) {
const newItem = getNewHeldItemFromCategory(id, pokemon, {}, pokemon);
if (newItem) {
pokemon.heldItemManager.add(newItem);
}
}
}
export function assignItemsFromConfiguration(config: HeldItemConfiguration, pokemon: Pokemon) {
config.forEach(item => {
const { entry, count } = item;
const actualCount = typeof count === "function" ? count() : (count ?? 1);
if (typeof entry === "number") {
if (isCategoryId(entry)) {
assignItemsFromCategory(entry, pokemon, actualCount);
}
pokemon.heldItemManager.add(entry, actualCount);
}
@ -287,12 +302,7 @@ export function assignItemsFromConfiguration(config: HeldItemConfiguration, poke
}
if (isHeldItemCategoryEntry(entry)) {
for (let i = 1; i <= actualCount; i++) {
const newItem = getNewHeldItemFromCategory(entry.id, pokemon, entry?.customWeights, pokemon);
if (newItem) {
pokemon.heldItemManager.add(newItem);
}
}
assignItemsFromCategory(entry.id, pokemon, actualCount);
}
if (isHeldItemPool(entry)) {

View File

@ -0,0 +1,47 @@
import { getHeldItemCategory, HeldItemCategoryId, HeldItemId } from "#enums/held-item-id";
import { RewardTier } from "#enums/reward-tier";
export const heldItemTiers = {
[HeldItemCategoryId.BERRY]: RewardTier.COMMON,
[HeldItemCategoryId.BASE_STAT_BOOST]: RewardTier.GREAT,
[HeldItemId.WHITE_HERB]: RewardTier.GREAT,
[HeldItemId.METAL_POWDER]: RewardTier.GREAT,
[HeldItemId.QUICK_POWDER]: RewardTier.GREAT,
[HeldItemId.DEEP_SEA_SCALE]: RewardTier.GREAT,
[HeldItemId.DEEP_SEA_TOOTH]: RewardTier.GREAT,
[HeldItemId.SOOTHE_BELL]: RewardTier.GREAT,
[HeldItemCategoryId.TYPE_ATTACK_BOOSTER]: RewardTier.ULTRA,
[HeldItemId.REVIVER_SEED]: RewardTier.ULTRA,
[HeldItemId.LIGHT_BALL]: RewardTier.ULTRA,
[HeldItemId.EVIOLITE]: RewardTier.ULTRA,
[HeldItemId.QUICK_CLAW]: RewardTier.ULTRA,
[HeldItemId.MYSTICAL_ROCK]: RewardTier.ULTRA,
[HeldItemId.WIDE_LENS]: RewardTier.ULTRA,
[HeldItemId.GOLDEN_PUNCH]: RewardTier.ULTRA,
[HeldItemId.TOXIC_ORB]: RewardTier.ULTRA,
[HeldItemId.FLAME_ORB]: RewardTier.ULTRA,
[HeldItemId.LUCKY_EGG]: RewardTier.ULTRA,
[HeldItemId.FOCUS_BAND]: RewardTier.ROGUE,
[HeldItemId.KINGS_ROCK]: RewardTier.ROGUE,
[HeldItemId.LEFTOVERS]: RewardTier.ROGUE,
[HeldItemId.SHELL_BELL]: RewardTier.ROGUE,
[HeldItemId.GRIP_CLAW]: RewardTier.ROGUE,
[HeldItemId.SOUL_DEW]: RewardTier.ROGUE,
[HeldItemId.BATON]: RewardTier.ROGUE,
[HeldItemId.GOLDEN_EGG]: RewardTier.ULTRA,
[HeldItemId.MINI_BLACK_HOLE]: RewardTier.MASTER,
[HeldItemId.MULTI_LENS]: RewardTier.MASTER,
};
export function getHeldItemTier(item: HeldItemId): RewardTier | undefined {
let tier = heldItemTiers[item];
if (!tier) {
const category = getHeldItemCategory(item);
tier = heldItemTiers[category];
}
return tier;
}