From 8f0eee9c4c70f3b1ef561febffa15bb19b562530 Mon Sep 17 00:00:00 2001 From: damocleas Date: Fri, 2 May 2025 01:11:02 -0400 Subject: [PATCH] [Balance] [Mystery Encounter] Many Minor Mystery Encounter Adjustments (#5726) * Update slumbering-snorlax-encounter.ts * Add Slumbering Snorlax to Tall Grass, remove Absolute Avarice from Plains * Update absolute-avarice-encounter.ts * Update absolute-avarice-encounter.ts * Update slumbering-snorlax-encounter.ts * Update slumbering-snorlax-encounter.ts * Update the-expert-pokemon-breeder-encounter.ts * Update slumbering-snorlax-encounter.ts nature * Update bug-type-superfan-encounter.ts move reward * Update bug-type-superfan-encounter.ts moves again * fix encounter waves * Update absolute-avarice-encounter.test.ts * add Nature import * Update bug-type-superfan-encounter.test.ts * greedent moves * test moves * Updated mysterious-chest-encounter.ts trap/reward chance * swapped Macho Brace stats, +2 / 10% for HP stats and +1 / 5% for all else * Update bug-type-superfan-encounter.ts moves * Update the-expert-pokemon-breeder-encounter.ts tera * Update bug-type-superfan-encounter.test.ts fix test --- .../encounters/absolute-avarice-encounter.ts | 11 ++++--- .../encounters/bug-type-superfan-encounter.ts | 28 +++++++++++------ .../encounters/mysterious-chest-encounter.ts | 4 +-- .../slumbering-snorlax-encounter.ts | 30 +++++++++++-------- .../the-expert-pokemon-breeder-encounter.ts | 7 ++--- .../mystery-encounters/mystery-encounters.ts | 4 +-- src/modifier/modifier.ts | 14 ++++----- .../absolute-avarice-encounter.test.ts | 8 +++-- .../bug-type-superfan-encounter.test.ts | 28 +++++++++++------ 9 files changed, 80 insertions(+), 54 deletions(-) diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index e0486c83e77..acfc8cb16a1 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -37,7 +37,6 @@ import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; import type { BerryType } from "#enums/berry-type"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import i18next from "i18next"; /** the i18n namespace for this encounter */ @@ -52,8 +51,8 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde MysteryEncounterType.ABSOLUTE_AVARICE, ) .withEncounterTier(MysteryEncounterTier.GREAT) - .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) - .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn + .withSceneWaveRangeRequirement(20, 180) + .withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 6)) // Must have at least 6 berries to spawn .withFleeAllowed(false) .withIntroSpriteConfigs([ { @@ -220,9 +219,9 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde // Do NOT remove the real berries yet or else it will be persisted in the session data - // SpDef buff below wave 50, +1 to all stats otherwise + // +1 SpDef below wave 50, SpDef and Speed otherwise const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = - globalScene.currentBattle.waveIndex < 50 ? [Stat.SPDEF] : [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD]; + globalScene.currentBattle.waveIndex < 50 ? [Stat.SPDEF] : [Stat.SPDEF, Stat.SPD]; // Calculate boss mon const config: EnemyPartyConfig = { @@ -233,7 +232,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde isBoss: true, bossSegments: 3, shiny: false, // Shiny lock because of consistency issues between the different options - moveSet: [Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH], + moveSet: [Moves.THRASH, Moves.CRUNCH, Moves.BODY_PRESS, Moves.SLACK_OFF], modifierConfigs: bossModifierConfigs, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 001faf3a67f..87b223d5245 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -146,24 +146,34 @@ const POOL_4_POKEMON = [Species.GENESECT, Species.SLITHER_WING, Species.BUZZWOLE const PHYSICAL_TUTOR_MOVES = [ Moves.MEGAHORN, - Moves.X_SCISSOR, Moves.ATTACK_ORDER, - Moves.PIN_MISSILE, + Moves.BUG_BITE, Moves.FIRST_IMPRESSION, + Moves.LUNGE ]; -const SPECIAL_TUTOR_MOVES = [Moves.SILVER_WIND, Moves.BUG_BUZZ, Moves.SIGNAL_BEAM, Moves.POLLEN_PUFF]; +const SPECIAL_TUTOR_MOVES = [ + Moves.SILVER_WIND, + Moves.SIGNAL_BEAM, + Moves.BUG_BUZZ, + Moves.POLLEN_PUFF, + Moves.STRUGGLE_BUG +]; -const STATUS_TUTOR_MOVES = [Moves.STRING_SHOT, Moves.STICKY_WEB, Moves.SILK_TRAP, Moves.RAGE_POWDER, Moves.HEAL_ORDER]; +const STATUS_TUTOR_MOVES = [ + Moves.STRING_SHOT, + Moves.DEFEND_ORDER, + Moves.RAGE_POWDER, + Moves.STICKY_WEB, + Moves.SILK_TRAP +]; const MISC_TUTOR_MOVES = [ - Moves.BUG_BITE, Moves.LEECH_LIFE, - Moves.DEFEND_ORDER, - Moves.QUIVER_DANCE, - Moves.TAIL_GLOW, - Moves.INFESTATION, Moves.U_TURN, + Moves.HEAL_ORDER, + Moves.QUIVER_DANCE, + Moves.INFESTATION, ]; /** diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index e9976ba04aa..e6c11378163 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -29,8 +29,8 @@ import { Species } from "#enums/species"; const namespace = "mysteryEncounters/mysteriousChest"; const RAND_LENGTH = 100; -const TRAP_PERCENT = 35; -const COMMON_REWARDS_PERCENT = 20; +const TRAP_PERCENT = 30; +const COMMON_REWARDS_PERCENT = 25; const ULTRA_REWARDS_PERCENT = 30; const ROGUE_REWARDS_PERCENT = 10; const MASTER_REWARDS_PERCENT = 5; diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 41c20f35ba1..2654f6b18d8 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -19,6 +19,7 @@ import { setEncounterRewards, } from "../utils/encounter-phase-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; +import { Nature } from "#enums/nature"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; import { AiType, PokemonMove } from "#app/field/pokemon"; @@ -26,9 +27,10 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { BerryType } from "#enums/berry-type"; +import { Stat } from "#enums/stat"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; +import { randSeedInt } from "#app/utils/common"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounters/slumberingSnorlax"; @@ -42,7 +44,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil MysteryEncounterType.SLUMBERING_SNORLAX, ) .withEncounterTier(MysteryEncounterTier.GREAT) - .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) + .withSceneWaveRangeRequirement(15, 150) .withCatchAllowed(true) .withHideWildIntroMessage(true) .withFleeAllowed(false) @@ -72,16 +74,26 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil species: bossSpecies, isBoss: true, shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked - status: [StatusEffect.SLEEP, 5], // Extra turns on timer for Snorlax's start of fight moves - moveSet: [Moves.REST, Moves.SLEEP_TALK, Moves.CRUNCH, Moves.GIGA_IMPACT], + status: [StatusEffect.SLEEP, 6], // Extra turns on timer for Snorlax's start of fight moves + nature: Nature.DOCILE, + moveSet: [Moves.BODY_SLAM, Moves.CRUNCH, Moves.SLEEP_TALK, Moves.REST], modifierConfigs: [ { modifier: generateModifierType(modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType, - stackCount: 2, }, { modifier: generateModifierType(modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType, - stackCount: 2, + }, + { + modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER, [Stat.HP]) as PokemonHeldItemModifierType, + }, + { + modifier: generateModifierType(modifierTypes.SOOTHE_BELL) as PokemonHeldItemModifierType, + stackCount: randSeedInt(2, 0), + }, + { + modifier: generateModifierType(modifierTypes.LUCKY_EGG) as PokemonHeldItemModifierType, + stackCount: randSeedInt(2, 0), }, ], customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }), @@ -128,12 +140,6 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil move: new PokemonMove(Moves.SNORE), ignorePp: true, }, - { - sourceBattlerIndex: BattlerIndex.ENEMY, - targets: [BattlerIndex.PLAYER], - move: new PokemonMove(Moves.SNORE), - ignorePp: true, - }, ); await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]); }, diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 076171b3e5e..15063bc2763 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -11,7 +11,6 @@ import { randSeedShuffle } from "#app/utils/common"; import type MysteryEncounter from "../mystery-encounter"; import { MysteryEncounterBuilder } from "../mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { Biome } from "#enums/biome"; import { TrainerType } from "#enums/trainer-type"; import i18next from "i18next"; @@ -123,7 +122,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = MysteryEncount MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, ) .withEncounterTier(MysteryEncounterTier.ULTRA) - .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) + .withSceneWaveRangeRequirement(25, 180) .withScenePartySizeRequirement(4, 6, true) // Must have at least 4 legal pokemon in party .withIntroSpriteConfigs([]) // These are set in onInit() .withIntroDialogue([ @@ -483,9 +482,9 @@ function getPartyConfig(): EnemyPartyConfig { abilityIndex: 1, // Magic Guard shiny: false, nature: Nature.ADAMANT, - moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH], + moveSet: [Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH, Moves.METEOR_MASH], ivs: [31, 31, 31, 31, 31, 31], - tera: PokemonType.STEEL, + tera: PokemonType.FAIRY, }, ], }; diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 5dd952b2bce..1a36dc27df2 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -226,9 +226,9 @@ const anyBiomeEncounters: MysteryEncounterType[] = [ */ export const mysteryEncountersByBiome = new Map([ [Biome.TOWN, []], - [Biome.PLAINS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE]], + [Biome.PLAINS, [MysteryEncounterType.SLUMBERING_SNORLAX]], [Biome.GRASS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE]], - [Biome.TALL_GRASS, [MysteryEncounterType.ABSOLUTE_AVARICE]], + [Biome.TALL_GRASS, [MysteryEncounterType.SLUMBERING_SNORLAX, MysteryEncounterType.ABSOLUTE_AVARICE]], [Biome.METROPOLIS, []], [Biome.FOREST, [MysteryEncounterType.SAFARI_ZONE, MysteryEncounterType.ABSOLUTE_AVARICE]], [Biome.SEA, [MysteryEncounterType.LOST_AT_SEA]], diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 2823e74fffe..9df7aa7813f 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1114,20 +1114,20 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { * @returns always `true` */ override apply(_pokemon: Pokemon, stat: Stat, statHolder: NumberHolder): boolean { - // Modifies the passed in stat number holder by +1 per stack for HP, +2 per stack for other stats - // If the Macho Brace is at max stacks (50), adds additional 5% to total HP and 10% to other stats + // Modifies the passed in stat number holder by +2 per stack for HP, +1 per stack for other stats + // If the Macho Brace is at max stacks (50), adds additional 10% to total HP and 5% to other stats const isHp = stat === Stat.HP; if (isHp) { - statHolder.value += this.stackCount; - if (this.stackCount === this.getMaxHeldItemCount()) { - statHolder.value = Math.floor(statHolder.value * 1.05); - } - } else { statHolder.value += 2 * this.stackCount; if (this.stackCount === this.getMaxHeldItemCount()) { statHolder.value = Math.floor(statHolder.value * 1.1); } + } else { + statHolder.value += this.stackCount; + if (this.stackCount === this.getMaxHeldItemCount()) { + statHolder.value = Math.floor(statHolder.value * 1.05); + } } return true; diff --git a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index e00ce03333c..36a284880c1 100644 --- a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -23,7 +23,7 @@ import i18next from "i18next"; const namespace = "mysteryEncounters/absoluteAvarice"; const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; -const defaultBiome = Biome.PLAINS; +const defaultBiome = Biome.TALL_GRASS; const defaultWave = 45; describe("Absolute Avarice - Mystery Encounter", () => { @@ -45,7 +45,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( new Map([ - [Biome.PLAINS, [MysteryEncounterType.ABSOLUTE_AVARICE]], + [Biome.TALL_GRASS, [MysteryEncounterType.ABSOLUTE_AVARICE]], [Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]], ]), ); @@ -91,6 +91,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { game.override.startingHeldItems([ { name: "BERRY", count: 2, type: BerryType.SITRUS }, { name: "BERRY", count: 3, type: BerryType.GANLON }, + { name: "BERRY", count: 2, type: BerryType.APICOT }, ]); await game.runToMysteryEncounter(); @@ -102,6 +103,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { game.override.startingHeldItems([ { name: "BERRY", count: 2, type: BerryType.SITRUS }, { name: "BERRY", count: 3, type: BerryType.GANLON }, + { name: "BERRY", count: 2, type: BerryType.APICOT }, ]); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); @@ -138,7 +140,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { expect(enemyField[0].species.speciesId).toBe(Species.GREEDENT); const moveset = enemyField[0].moveset.map(m => m.moveId); expect(moveset?.length).toBe(4); - expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH]); + expect(moveset).toEqual([Moves.THRASH, Moves.CRUNCH, Moves.BODY_PRESS, Moves.SLACK_OFF]); const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]); expect(movePhases.length).toBe(1); diff --git a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts index fc208ed7180..455a5d28194 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -118,24 +118,34 @@ const POOL_4_POKEMON = [Species.GENESECT, Species.SLITHER_WING, Species.BUZZWOLE const PHYSICAL_TUTOR_MOVES = [ Moves.MEGAHORN, - Moves.X_SCISSOR, Moves.ATTACK_ORDER, - Moves.PIN_MISSILE, + Moves.BUG_BITE, Moves.FIRST_IMPRESSION, + Moves.LUNGE ]; -const SPECIAL_TUTOR_MOVES = [Moves.SILVER_WIND, Moves.BUG_BUZZ, Moves.SIGNAL_BEAM, Moves.POLLEN_PUFF]; +const SPECIAL_TUTOR_MOVES = [ + Moves.SILVER_WIND, + Moves.SIGNAL_BEAM, + Moves.BUG_BUZZ, + Moves.POLLEN_PUFF, + Moves.STRUGGLE_BUG +]; -const STATUS_TUTOR_MOVES = [Moves.STRING_SHOT, Moves.STICKY_WEB, Moves.SILK_TRAP, Moves.RAGE_POWDER, Moves.HEAL_ORDER]; +const STATUS_TUTOR_MOVES = [ + Moves.STRING_SHOT, + Moves.DEFEND_ORDER, + Moves.RAGE_POWDER, + Moves.STICKY_WEB, + Moves.SILK_TRAP +]; const MISC_TUTOR_MOVES = [ - Moves.BUG_BITE, Moves.LEECH_LIFE, - Moves.DEFEND_ORDER, - Moves.QUIVER_DANCE, - Moves.TAIL_GLOW, - Moves.INFESTATION, Moves.U_TURN, + Moves.HEAL_ORDER, + Moves.QUIVER_DANCE, + Moves.INFESTATION, ]; describe("Bug-Type Superfan - Mystery Encounter", () => {