From 8d29c1fb31693c8dd830e6c60c262b5ef47c8446 Mon Sep 17 00:00:00 2001 From: AJ Fontaine Date: Mon, 24 Mar 2025 14:21:59 -0400 Subject: [PATCH] Challenge types for shop removals and reward table changes --- src/data/challenge.ts | 64 ++++++++++++++++++ src/modifier/modifier-type.ts | 122 ++++++++++++++++------------------ 2 files changed, 121 insertions(+), 65 deletions(-) diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 985d9a2891e..7e307ba96c9 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -21,6 +21,7 @@ import { TypeColor, TypeShadow } from "#enums/color"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; import { ModifierTier } from "#app/modifier/modifier-tier"; +import type { ModifierTypeKeys, RewardTableModification } from "#app/modifier/modifier-type"; /** A constant for the default max cost of the starting party before a run */ const DEFAULT_PARTY_MAX_COST = 10; @@ -105,6 +106,14 @@ export enum ChallengeType { * Negates PP Usage */ NO_PP_USE, + /** + * Modifies reward table + */ + REWARD_TABLE_MODIFY, + /** + * Removes items from the shop + */ + SHOP_REMOVAL, } /** @@ -457,6 +466,14 @@ export abstract class Challenge { applyNoPPUsage(_valid: Utils.BooleanHolder) { return false; } + + applyRewardTableModify(_modifications: RewardTableModification[]) { + return false; + } + + applyShopRemovals(_removals: ModifierTypeKeys[]) { + return false; + } } type ChallengeCondition = (data: GameData) => boolean; @@ -910,6 +927,14 @@ export class FreshStartChallenge extends Challenge { return true; } + override applyRewardTableModify(modifications: RewardTableModification[]): boolean { + modifications.push( + { type: "EVIOLITE", tier: ModifierTier.ULTRA, maxWeight: 0 }, // No Eviolite + { type: "MINI_BLACK_HOLE", tier: ModifierTier.MASTER, maxWeight: 0 }, // No MBH + ); + return true; + } + override getDifficulty(): number { return 0; } @@ -1014,6 +1039,27 @@ export class MetronomeChallenge extends Challenge { return true; } + override applyRewardTableModify(modifications: RewardTableModification[]): boolean { + modifications.push( + { type: "TM_COMMON", tier: ModifierTier.COMMON, maxWeight: 0 }, // Remove TMs + { type: "ETHER", tier: ModifierTier.COMMON, maxWeight: 0 }, // Remove PP Restores + { type: "MAX_ETHER", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove PP Restores + { type: "ELIXIR", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove PP Restores + { type: "MAX_ELIXIR", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove PP Restores + { type: "PP_UP", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove PP Upgrades + { type: "MEMORY_MUSHROOM", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove Mushrooms + { type: "TM_GREAT", tier: ModifierTier.GREAT, maxWeight: 0 }, // Remove TMs + { type: "TM_ULTRA", tier: ModifierTier.ULTRA, maxWeight: 0 }, // Remove TMs + { type: "PP_MAX", tier: ModifierTier.ULTRA, maxWeight: 0 }, // Remove PP Upgrades + ); + return true; + } + + override applyShopRemovals(removals: ModifierTypeKeys[]): boolean { + removals.push("ETHER", "MAX_ETHER", "ELIXIR", "MAX_ELIXIR", "MEMORY_MUSHROOM"); + return true; + } + static loadChallenge(source: MetronomeChallenge | any): MetronomeChallenge { const newChallenge = new MetronomeChallenge(); newChallenge.value = source.value; @@ -1298,6 +1344,18 @@ export function applyChallenges( valid: Utils.BooleanHolder, ): boolean; +export function applyChallenges( + gameMode: GameMode, + challengeType: ChallengeType.REWARD_TABLE_MODIFY, + modifications: RewardTableModification[], +): boolean; + +export function applyChallenges( + gameMode: GameMode, + challengeType: ChallengeType.SHOP_REMOVAL, + removals: ModifierTypeKeys[], +): boolean; + export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean { let ret = false; gameMode.challenges.forEach(c => { @@ -1354,6 +1412,12 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType case ChallengeType.NO_PP_USE: ret ||= c.applyNoPPUsage(args[0]); break; + case ChallengeType.REWARD_TABLE_MODIFY: + ret ||= c.applyRewardTableModify(args[0]); + break; + case ChallengeType.SHOP_REMOVAL: + ret ||= c.applyShopRemovals(args[0]); + break; } } }); diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 6e914565a3f..d172f27b7a9 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -2468,30 +2468,14 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.LURE, lureWeightFunc(10, 2)), new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4), new WeightedModifierType(modifierTypes.BERRY, 2), - new WeightedModifierType( - modifierTypes.TM_COMMON, - () => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_MOVE_LEARNING, noMoveLearning); - return noMoveLearning.value ? 0 : 2; - }, - 2, - ), + new WeightedModifierType(modifierTypes.TM_COMMON, 2), ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), [ModifierTier.GREAT]: [ new WeightedModifierType(modifierTypes.GREAT_BALL, () => (hasMaximumBalls(PokeballType.GREAT_BALL) ? 0 : 6), 6), - new WeightedModifierType( - modifierTypes.PP_UP, - () => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_PP_USE, noMoveLearning); - return noMoveLearning.value ? 0 : 2; - }, - 2, - ), + new WeightedModifierType(modifierTypes.PP_UP, 2), new WeightedModifierType( modifierTypes.FULL_HEAL, (party: Pokemon[]) => { @@ -2587,11 +2571,6 @@ const modifierPool: ModifierPool = { new WeightedModifierType( modifierTypes.ELIXIR, (party: Pokemon[]) => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_PP_USE, noMoveLearning); - if (noMoveLearning.value) { - return 0; - } const thresholdPartyMemberCount = Math.min( party.filter( p => @@ -2611,11 +2590,6 @@ const modifierPool: ModifierPool = { new WeightedModifierType( modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_PP_USE, noMoveLearning); - if (noMoveLearning.value) { - return 0; - } const thresholdPartyMemberCount = Math.min( party.filter( p => @@ -2648,21 +2622,11 @@ const modifierPool: ModifierPool = { 2, ), new WeightedModifierType(modifierTypes.SOOTHE_BELL, 2), - new WeightedModifierType( - modifierTypes.TM_GREAT, - () => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_MOVE_LEARNING, noMoveLearning); - return noMoveLearning.value ? 0 : 3; - }, - 3, - ), + new WeightedModifierType(modifierTypes.TM_GREAT, 3), new WeightedModifierType( modifierTypes.MEMORY_MUSHROOM, (party: Pokemon[]) => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_MOVE_LEARNING, noMoveLearning); // Yeah this is kind of dumb - if (noMoveLearning.value || !party.find(p => p.getLearnableLevelMoves().length)) { + if (!party.find(p => p.getLearnableLevelMoves().length)) { return 0; } const highestPartyLevel = party @@ -2708,15 +2672,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.ULTRA_BALL, () => (hasMaximumBalls(PokeballType.ULTRA_BALL) ? 0 : 15), 15), new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(30, 4)), new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)), - new WeightedModifierType( - modifierTypes.PP_MAX, - () => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_PP_USE, noMoveLearning); // Yeah this is kind of dumb - return noMoveLearning.value ? 0 : 3; - }, - 3, - ), + new WeightedModifierType(modifierTypes.PP_MAX, 3), new WeightedModifierType(modifierTypes.MINT, 4), new WeightedModifierType( modifierTypes.RARE_EVOLUTION_ITEM, @@ -2731,7 +2687,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)), new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => { const { gameMode, gameData } = globalScene; - if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) { + if (gameMode.isDaily || gameData.isUnlocked(Unlockables.EVIOLITE)) { return party.some(p => { // Check if Pokemon's species (or fusion species, if applicable) can evolve or if they're G-Max'd if ( @@ -2861,15 +2817,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.REVIVER_SEED, 4), new WeightedModifierType(modifierTypes.CANDY_JAR, skipInLastClassicWaveOrDefault(5)), new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 9), - new WeightedModifierType( - modifierTypes.TM_ULTRA, - () => { - const noMoveLearning = new BooleanHolder(false); - applyChallenges(globalScene.gameMode, ChallengeType.NO_MOVE_LEARNING, noMoveLearning); // Yeah this is kind of dumb - return noMoveLearning.value ? 0 : 11; - }, - 11, - ), + new WeightedModifierType(modifierTypes.TM_ULTRA, 11), new WeightedModifierType(modifierTypes.RARER_CANDY, 4), new WeightedModifierType(modifierTypes.GOLDEN_PUNCH, skipInLastClassicWaveOrDefault(2)), new WeightedModifierType(modifierTypes.IV_SCANNER, skipInLastClassicWaveOrDefault(4)), @@ -2955,11 +2903,7 @@ const modifierPool: ModifierPool = { ), new WeightedModifierType( modifierTypes.MINI_BLACK_HOLE, - () => - globalScene.gameMode.isDaily || - (!globalScene.gameMode.isFreshStartChallenge() && globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE)) - ? 1 - : 0, + () => (globalScene.gameMode.isDaily || globalScene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE) ? 1 : 0), 1, ), ].map(m => { @@ -3170,8 +3114,51 @@ const tierWeights = [768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024]; */ export const itemPoolChecks: Map = new Map(); +export interface RewardTableModification { + type: ModifierTypeKeys; + tier: ModifierTier; + maxWeight: number; +} + export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount = 0) { + const modifications: RewardTableModification[] = []; + applyChallenges(globalScene.gameMode, ChallengeType.REWARD_TABLE_MODIFY, modifications); const pool = getModifierPoolForType(poolType); + modifications.map(mod => { + let t = mod.tier; + let dindex = pool[mod.tier].findIndex(wm => wm.modifierType.id === mod.type); + if (mod.maxWeight === 0) { + // Remove the modifier from the specified tier + pool[t].splice(dindex, 1); + } else if (dindex < 0) { + // Add the modifier to specified tier + for (t = ModifierTier.COMMON; t <= ModifierTier.MASTER && dindex < 0; t++) { + if (t === mod.tier) { + // We know it's not in that tier + continue; + } + dindex = pool[t].findIndex(wm => wm.modifierType.id === mod.type); + } + if (dindex >= 0) { + // Move the existing WMT to the specified tier with same func and specified max weight + const wmt = pool[t].splice(dindex, 1)[0]; + wmt.maxWeight = mod.maxWeight; + wmt.setTier(mod.tier); + pool[mod.tier].push(wmt); + } else { + // Item isn't anywhere on the table, make a new WMT and push it + const newWMT = new WeightedModifierType(getModifierTypeFuncById(mod.type), mod.maxWeight); + newWMT.setTier(mod.tier); + pool[mod.tier].push(newWMT); + } + } else { + pool[t].map(wmt => { + if (wmt.modifierType.id === mod.type) { + wmt.maxWeight = mod.maxWeight; + } + }); + } + }); itemPoolChecks.forEach((_v, k) => { itemPoolChecks.set(k, false); }); @@ -3459,7 +3446,12 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseC [new ModifierTypeOption(modifierTypes.FULL_RESTORE(), 0, baseCost * 2.25)], [new ModifierTypeOption(modifierTypes.SACRED_ASH(), 0, baseCost * 10)], ]; - return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat(); + const removeShop: ModifierTypeKeys[] = []; + applyChallenges(globalScene.gameMode, ChallengeType.SHOP_REMOVAL, removeShop); + return options + .slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)) + .flat() + .filter(s => !removeShop.includes(s.type.id as ModifierTypeKeys)); } export function getEnemyBuffModifierForWave(