From aadb57ab75ab5af4355c50f7e5132bebd016a1e6 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:03:49 -0400 Subject: [PATCH] [Balance] [Mystery] Salesman ME offers mons from event encounter pool (#5674) * Initial event commit * Salesman odds * Clean up imports * globalScene shiny rate getter, fix reroll, remove placeholder event * Rerolling shiny also tries rerolling for better variant * Shiny reroll affects 'trainer' mons too --------- Co-authored-by: damocleas Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- .../the-pokemon-salesman-encounter.ts | 49 ++++++++++++++++--- .../utils/encounter-phase-utils.ts | 4 +- src/field/pokemon.ts | 32 ++++++------ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 4e8e1c2524e..cfff59b45f5 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -3,7 +3,7 @@ import { transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; +import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedItem } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; @@ -28,7 +28,8 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { Abilities } from "#enums/abilities"; -import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; +import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#app/data/balance/special-species-groups"; +import { timedEventManager } from "#app/global-event-manager"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/thePokemonSalesman"; @@ -38,6 +39,9 @@ const MAX_POKEMON_PRICE_MULTIPLIER = 4; /** Odds of shiny magikarp will be 1/value */ const SHINY_MAGIKARP_WEIGHT = 100; +/** Odds of event sale will be value/100 */ +const EVENT_THRESHOLD = 50; + /** * Pokemon Salesman encounter. * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3799 | GitHub Issue #3799} @@ -82,15 +86,46 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui tries++; } + const r = randSeedInt(SHINY_MAGIKARP_WEIGHT); + + const validEventEncounters = timedEventManager + .getEventEncounters() + .filter( + s => + !getPokemonSpecies(s.species).legendary && + !getPokemonSpecies(s.species).subLegendary && + !getPokemonSpecies(s.species).mythical && + !NON_LEGEND_PARADOX_POKEMON.includes(s.species) && + !NON_LEGEND_ULTRA_BEASTS.includes(s.species), + ); + let pokemon: PlayerPokemon; + /** + * Mon is determined as follows: + * If you roll the 1% for Shiny Magikarp, you get Magikarp with a random variant + * If an event with more than 1 valid event encounter species is active, you have 20% chance to get one of those + * If the rolled species has no HA, and there are valid event encounters, you will get one of those + * If the rolled species has no HA and there are no valid event encounters, you will get Shiny Magikarp + * Mons rolled from the event encounter pool get 2 extra shiny rolls + */ if ( - randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || - isNullOrUndefined(species.abilityHidden) || - species.abilityHidden === Abilities.NONE + r === 0 || + ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && + (validEventEncounters.length === 0)) ) { - // If no HA mon found or you roll 1%, give shiny Magikarp with random variant + // If you roll 1%, give shiny Magikarp with random variant species = getPokemonSpecies(Species.MAGIKARP); - pokemon = new PlayerPokemon(species, 5, 2, species.formIndex, undefined, true); + pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true); + } else if ( + (validEventEncounters.length > 0 && (r <= EVENT_THRESHOLD || + (isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE))) + ) { + // If you roll 20%, give event encounter with 2 extra shiny rolls and its HA, if it has one + const enc = randSeedItem(validEventEncounters); + species = getPokemonSpecies(enc.species); + pokemon = new PlayerPokemon(species, 5, species.abilityHidden === Abilities.NONE ? undefined : 2, enc.formIndex); + pokemon.trySetShinySeed(); + pokemon.trySetShinySeed(); } else { pokemon = new PlayerPokemon(species, 5, 2, species.formIndex); } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index d77b70caa31..65051b937f8 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1075,8 +1075,8 @@ export function getRandomEncounterSpecies(level: number, isBoss = false, rerollH ret.formIndex = formIndex; } - //Reroll shiny for event encounters - if (isEventEncounter && !ret.shiny) { + //Reroll shiny or variant for event encounters + if (isEventEncounter) { ret.trySetShinySeed(); } //Reroll hidden ability diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 6356f723a79..86d74ea5555 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3170,7 +3170,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Function that tries to set a Pokemon shiny based on seed. * For manual use only, usually to roll a Pokemon's shiny chance a second time. - * If it rolls shiny, also sets a random variant and give the Pokemon the associated luck. + * If it rolls shiny, or if it's already shiny, also sets a random variant and give the Pokemon the associated luck. * * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536` * @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) @@ -3181,29 +3181,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { thresholdOverride?: number, applyModifiersToOverride?: boolean, ): boolean { - const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); - if (thresholdOverride === undefined || applyModifiersToOverride) { - if (thresholdOverride !== undefined && applyModifiersToOverride) { - shinyThreshold.value = thresholdOverride; - } - if (timedEventManager.isEventActive()) { - shinyThreshold.value *= timedEventManager.getShinyMultiplier(); - } - if (!this.hasTrainer()) { + if (!this.shiny) { + const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); + if (thresholdOverride === undefined || applyModifiersToOverride) { + if (thresholdOverride !== undefined && applyModifiersToOverride) { + shinyThreshold.value = thresholdOverride; + } + if (timedEventManager.isEventActive()) { + shinyThreshold.value *= timedEventManager.getShinyMultiplier(); + } globalScene.applyModifiers( ShinyRateBoosterModifier, true, shinyThreshold, ); } - } else { - shinyThreshold.value = thresholdOverride; + else { + shinyThreshold.value = thresholdOverride; + } + + this.shiny = randSeedInt(65536) < shinyThreshold.value; } - this.shiny = randSeedInt(65536) < shinyThreshold.value; - if (this.shiny) { - this.variant = this.generateShinyVariant(); + this.variant = this.variant ?? 0; + this.variant = Math.max(this.generateShinyVariant(), this.variant) as Variant; // Don't set a variant lower than the current one this.luck = this.variant + 1 + (this.fusionShiny ? this.fusionVariant + 1 : 0); this.initShinySparkle();