diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 64409c3c989..cf1e4061987 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -9,14 +9,14 @@ import { Nature } from "#enums/nature"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { TimeOfDay } from "#enums/time-of-day"; -import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier, TempExtraModifierModifier } from "#app/modifier/modifier"; import { SpeciesFormKey } from "#enums/species-form-key"; +import { TimeOfDay } from "#enums/time-of-day"; +import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifier, SpeciesStatBoosterModifier, TempExtraModifierModifier } from "#app/modifier/modifier"; +import type { SpeciesStatBoosterModifierType } from "#app/modifier/modifier-type"; import { speciesStarterCosts } from "./starters"; import i18next from "i18next"; import { initI18n } from "#app/plugins/i18n"; - export enum SpeciesWildEvolutionDelay { NONE, SHORT, @@ -1793,8 +1793,9 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.CLAMPERL]: [ - new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new GenderEvolutionCondition(Gender.MALE /* Deep Sea Tooth */), SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(Species.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new GenderEvolutionCondition(Gender.FEMALE /* Deep Sea Scale */), SpeciesWildEvolutionDelay.VERY_LONG) + // TODO: Change the SpeciesEvolutionConditions here to use a bespoke HeldItemEvolutionCondition after the modifier rework + new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m instanceof SpeciesStatBoosterModifier && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_TOOTH")), SpeciesWildEvolutionDelay.VERY_LONG), + new SpeciesEvolution(Species.GOREBYSS, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.getHeldItems().some(m => m instanceof SpeciesStatBoosterModifier && (m.type as SpeciesStatBoosterModifierType).key === "DEEP_SEA_SCALE")), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.BOLDORE]: [ new SpeciesEvolution(Species.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 4c61123eb2d..912e12f19dc 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -873,7 +873,7 @@ export class SpeciesStatBoosterModifierType extends PokemonHeldItemModifierType implements GeneratedPersistentModifierType { - private key: SpeciesStatBoosterItem; + public key: SpeciesStatBoosterItem; constructor(key: SpeciesStatBoosterItem) { const item = SpeciesStatBoosterModifierTypeGenerator.items[key]; @@ -1440,34 +1440,59 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { stats: [Stat.ATK, Stat.SPATK], multiplier: 2, species: [Species.PIKACHU], + rare: true, }, THICK_CLUB: { stats: [Stat.ATK], multiplier: 2, species: [Species.CUBONE, Species.MAROWAK, Species.ALOLA_MAROWAK], + rare: true, }, METAL_POWDER: { stats: [Stat.DEF], multiplier: 2, species: [Species.DITTO], + rare: true, }, QUICK_POWDER: { stats: [Stat.SPD], multiplier: 2, species: [Species.DITTO], + rare: true, + }, + DEEP_SEA_SCALE: { + stats: [Stat.SPDEF], + multiplier: 2, + species: [Species.CLAMPERL], + rare: false, + }, + DEEP_SEA_TOOTH: { + stats: [Stat.SPATK], + multiplier: 2, + species: [Species.CLAMPERL], + rare: false, }, }; - constructor() { + constructor(rare: boolean) { super((party: Pokemon[], pregenArgs?: any[]) => { const items = SpeciesStatBoosterModifierTypeGenerator.items; if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in items) { return new SpeciesStatBoosterModifierType(pregenArgs[0] as SpeciesStatBoosterItem); } - const values = Object.values(items); - const keys = Object.keys(items); - const weights = keys.map(() => 0); + // Get a pool of items based on the rarity. + const keys: (keyof SpeciesStatBoosterItem)[] = []; + const values: (typeof items)[keyof typeof items][] = []; + const weights: number[] = []; + for (const [key, val] of Object.entries(SpeciesStatBoosterModifierTypeGenerator.items)) { + if (val.rare !== rare) { + continue; + } + values.push(val); + keys.push(key as keyof SpeciesStatBoosterItem); + weights.push(0); + } for (const p of party) { const speciesId = p.getSpeciesForm(true).speciesId; @@ -1846,7 +1871,7 @@ export type GeneratorModifierOverride = { count?: number; } & ( | { - name: keyof Pick; + name: keyof Pick; type?: SpeciesStatBoosterItem; } | { @@ -1874,7 +1899,7 @@ export type GeneratorModifierOverride = { type?: EvolutionItem; } | { - name: keyof Pick; + name: keyof Pick; type?: FormChangeItem; } | { @@ -1977,7 +2002,8 @@ export const modifierTypes = { SUPER_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.SUPER_LURE", "super_lure", 15), MAX_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.MAX_LURE", "max_lure", 30), - SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(), + SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(false), + RARE_SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(true), TEMP_STAT_STAGE_BOOSTER: () => new TempStatStageBoosterModifierTypeGenerator(), @@ -2617,6 +2643,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.DIRE_HIT, 4), new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(15, 4)), new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)), + new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 4), new WeightedModifierType( modifierTypes.EVOLUTION_ITEM, () => { @@ -2713,7 +2740,7 @@ const modifierPool: ModifierPool = { } return 0; }), - new WeightedModifierType(modifierTypes.SPECIES_STAT_BOOSTER, 12), + new WeightedModifierType(modifierTypes.RARE_SPECIES_STAT_BOOSTER, 12), new WeightedModifierType( modifierTypes.LEEK, (party: Pokemon[]) => { diff --git a/test/items/light_ball.test.ts b/test/items/light_ball.test.ts index 91195d0b1e5..cdcffe810fa 100644 --- a/test/items/light_ball.test.ts +++ b/test/items/light_ball.test.ts @@ -29,7 +29,7 @@ describe("Items - Light Ball", () => { }); it("LIGHT_BALL activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "LIGHT_BALL" }]); + game.override.startingHeldItems([{ name: "RARE_SPECIES_STAT_BOOSTER", type: "LIGHT_BALL" }]); const consoleSpy = vi.spyOn(console, "log"); await game.classicMode.startBattle([Species.PIKACHU]); @@ -100,7 +100,7 @@ describe("Items - Light Ball", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -139,7 +139,7 @@ describe("Items - Light Ball", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -178,7 +178,7 @@ describe("Items - Light Ball", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -207,7 +207,7 @@ describe("Items - Light Ball", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); diff --git a/test/items/metal_powder.test.ts b/test/items/metal_powder.test.ts index 6be7655ec70..e924d75fce9 100644 --- a/test/items/metal_powder.test.ts +++ b/test/items/metal_powder.test.ts @@ -29,7 +29,7 @@ describe("Items - Metal Powder", () => { }); it("METAL_POWDER activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "METAL_POWDER" }]); + game.override.startingHeldItems([{ name: "RARE_SPECIES_STAT_BOOSTER", type: "METAL_POWDER" }]); const consoleSpy = vi.spyOn(console, "log"); await game.classicMode.startBattle([Species.DITTO]); @@ -96,7 +96,7 @@ describe("Items - Metal Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); @@ -129,7 +129,7 @@ describe("Items - Metal Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); @@ -162,7 +162,7 @@ describe("Items - Metal Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); @@ -185,7 +185,7 @@ describe("Items - Metal Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); diff --git a/test/items/quick_powder.test.ts b/test/items/quick_powder.test.ts index d77f981f04d..fb08d6bc71e 100644 --- a/test/items/quick_powder.test.ts +++ b/test/items/quick_powder.test.ts @@ -29,7 +29,7 @@ describe("Items - Quick Powder", () => { }); it("QUICK_POWDER activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "QUICK_POWDER" }]); + game.override.startingHeldItems([{ name: "RARE_SPECIES_STAT_BOOSTER", type: "QUICK_POWDER" }]); const consoleSpy = vi.spyOn(console, "log"); await game.classicMode.startBattle([Species.DITTO]); @@ -96,7 +96,7 @@ describe("Items - Quick Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); @@ -129,7 +129,7 @@ describe("Items - Quick Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); @@ -162,7 +162,7 @@ describe("Items - Quick Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); @@ -185,7 +185,7 @@ describe("Items - Quick Powder", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); diff --git a/test/items/thick_club.test.ts b/test/items/thick_club.test.ts index 2a63a60a0e6..350735a363c 100644 --- a/test/items/thick_club.test.ts +++ b/test/items/thick_club.test.ts @@ -29,7 +29,7 @@ describe("Items - Thick Club", () => { }); it("THICK_CLUB activates in battle correctly", async () => { - game.override.startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "THICK_CLUB" }]); + game.override.startingHeldItems([{ name: "RARE_SPECIES_STAT_BOOSTER", type: "THICK_CLUB" }]); const consoleSpy = vi.spyOn(console, "log"); await game.classicMode.startBattle([Species.CUBONE]); @@ -96,7 +96,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -119,7 +119,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -142,7 +142,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -179,7 +179,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -216,7 +216,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); @@ -239,7 +239,7 @@ describe("Items - Thick Club", () => { // Giving Eviolite to party member and testing if it applies await game.scene.addModifier( - modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), + modifierTypes.RARE_SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true, ); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue);