diff --git a/src/@types/species-gen-types.ts b/src/@types/species-gen-types.ts new file mode 100644 index 00000000000..1561a427d3f --- /dev/null +++ b/src/@types/species-gen-types.ts @@ -0,0 +1,25 @@ +// biome-ignore lint/correctness/noUnusedImports: Used in TSDoc comment +import type { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; +import type { SpeciesId } from "#enums/species-id"; + +/** + * A tuple representing level thresholds for evolution based on encounter type. + * - `strong`: The evo level for, say, a Gym Leader or Evil Admin, etc. + * - `normal`: The evo level for a regular Trainer with average strength + * - `wild`: The evo level for wild encounters + * + * @see {@linkcode EvoLevelThresholdKind} + */ +export type EvoLevelThreshold = [strong: number, normal: number, wild: number]; + +/** + * Pokemon Evolution tuple type consisting of: + * - `species`: The species of the Pokemon. + * - `level`: The level at which the Pokemon evolves. + */ +export type EvolutionLevel = [species: SpeciesId, level: number]; + +/** + * {@inheritdoc EvolutionLevel} + */ +export type EvolutionLevelWithThreshold = [species: SpeciesId, level: number, evoLevelThreshold?: EvoLevelThreshold]; diff --git a/src/ai/ai-species-gen.ts b/src/ai/ai-species-gen.ts new file mode 100644 index 00000000000..c6a68ae172d --- /dev/null +++ b/src/ai/ai-species-gen.ts @@ -0,0 +1,207 @@ +import { globalScene } from "#app/global-scene"; +import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions"; +import { allSpecies } from "#data/data-lists"; +import type { PokemonSpecies } from "#data/pokemon-species"; +import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; +import { PartyMemberStrength } from "#enums/party-member-strength"; +import type { SpeciesId } from "#enums/species-id"; +import { randSeedInt, randSeedItem } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; + +/** + * Controls the maximum level difference that a Pokémon spawned with + * {@linkcode EvoLevelThresholdKind.NORMAL} is allowd to remain unevolved. + */ +const NORMAL_TRAINER_LEVEL_DIFF_PERCENT = 0.1; +/** + * Controls the maximum level difference that a Pokémon spawned with + * {@linkcode EvoLevelThresholdKind.WILD} is allowd to remain unevolved. + */ +const WILD_LEVEL_DIFF_PERCENT = 0.2; +/** + * Controls the maximum level difference that a Pokémon spawned with + * {@linkcode EvoLevelThresholdKind.} is allowd to remain unevolved. + */ +const STRONG_LEVEL_DIFF_PERCENT = 0; + +/** + * Get the evolution threshold for the given evolution, or `0` for ineligible + * @param ev - The evolution to consider + * @param level - The level of the Pokémon + * @param evolutionPool - The pool of evolutions to add to + * @returns The level threshold required for the evolution, or `0` if not eligible + */ +function calcEvoChance(ev: SpeciesFormEvolution, level: number, encounterKind: EvoLevelThresholdKind): number { + /** The level requirement based on the trainer type */ + const levelThreshold = Math.max(ev.level, ev.evoLevelThreshold?.[encounterKind] ?? 0); + // Disallow evolution if the level is below its required threshold. + if (level < ev.level || level < levelThreshold) { + console.info( + "%cDisallowing evolution of %s to %s at level %d (needs %d)", + "color: blue", + allSpecies[ev.speciesId]?.name, + ); + return 0; + } + return levelThreshold; +} + +/** + * If the Pokémon is below the required level threshold for its species, return the + * first pre-evolution that meets the level requirement. + * @param species - The species to consider + * @param level - The level the Pokémon will be + * @param encounterKind - The kind of evolution threshold to use + * @returns The speciesId of the forced prevolution, or `null` if none is required. + * + * @remarks + * Forced prevolutions do *not* apply the randomness factor. That is, if the + * Pokémon evolves multiple times and its level is currently near (but above) + * the level requirement for its stage 2, it will always return the stage 2 + * evolution. For example, if the provided species is Venusaur, but the spawn + * level is level 17 (bulbasaur evolves at 16), then it will *always* return + * Ivysaur with *no* chance for Bulbasaur. + * + * @privateRemarks + * The above limitation can be overcome, though requires more complex logic. + * + */ +function getRequiredPrevo( + species: PokemonSpecies, + level: number, + encounterKind: EvoLevelThresholdKind, +): SpeciesId | null { + // Get the prevolution levels for this species. + const prevolutionLevels = species.getPrevolutionLevels(true); + + // Return the base species if it's below the prevolution level threshold + // Go in reverse order to go from earlier in evolution line to later + // NOTE: This will *not* apply the randomness factor. + for (let pl = prevolutionLevels.length - 1; pl >= 0; pl--) { + const [prevoSpecies, levelReq, evoThreshold] = prevolutionLevels[pl]; + const threshold = evoThreshold?.[encounterKind] ?? levelReq; + const req = levelReq === 1 ? threshold : Math.min(levelReq, threshold); + if (level < req) { + console.info( + "%cForcing prevo %s for %s at level %d (needs %d)", + "color: orange", + prevoSpecies, + species.speciesId, + level, + req, + ); + return prevoSpecies; + } + } + + return null; +} + +/** + * Determine the species of an enemy Pokémon, based on various factors. + * + * @param species - The species to consider + * @param level - The level the Pokémon will be + * @param allowEvolving - Whether to allow evolution; default `false` + * @param forTrainer - Whether the Pokémon is for a trainer; default `false` + * @param strength - The strength of the party member; default {@linkcode PartyMemberStrength.WEAKER | Weaker} + * @param encounterKind - The kind of evolution threshold to use; default {@linkcode EvoLevelThresholdKind.NORMAL | Normal} for trainers, {@linkcode EvoLevelThresholdKind.WILD | Wild} otherwise + * @param tryForcePrevo - Whether to skip checking for prevolutions. Should only be `false` when invoked recursively; default `true` + * + * @remarks + * Passing a species with split evolutions will randomly choose one of its + * evolutions based on its level. Passing an evolved species _may_ allow a + * pre-evolution to be chosen, based on its level, though if the pre-evolution + * has split evolutions, it will always choose from the species line that has + * the passed species + * + * @see {@link calcEvoChance} + */ +export function determineEnemySpecies( + species: PokemonSpecies, + level: number, + allowEvolving = false, + forTrainer = false, + strength: PartyMemberStrength = PartyMemberStrength.WEAKER, + encounterKind: EvoLevelThresholdKind = forTrainer ? EvoLevelThresholdKind.NORMAL : EvoLevelThresholdKind.WILD, + tryForcePrevo = true, +): SpeciesId { + console.info( + "%c Determining species for %s at level %d with encounter kind %s", + "color: blue", + species.name, + level, + encounterKind, + ); + const requiredPrevo = + tryForcePrevo + && pokemonPrevolutions.hasOwnProperty(species.speciesId) + && getRequiredPrevo(species, level, encounterKind); + if (requiredPrevo) { + return requiredPrevo; + } + const evolutions = pokemonEvolutions[species.speciesId] ?? []; + if ( + // If evolutions shouldn't happen, add more cases here :) + !allowEvolving + || evolutions.length <= 0 + || (globalScene.currentBattle?.waveIndex === 20 + && globalScene.gameMode.isClassic + && globalScene.currentBattle.trainer) + ) { + return species.speciesId; + } + + const evoPool: [number, SpeciesId][] = []; + + for (const e of evolutions) { + const threshold = calcEvoChance(e, level, encounterKind); + if (threshold > 0) { + evoPool.push([threshold, e.speciesId]); + } + } + if (evoPool.length === 0) { + console.log("%c No evolutions available, returning base species", "color: blue"); + return species.speciesId; + } + const [choice, evoSpecies] = randSeedItem(evoPool); + // Add a linearly scaling random factor to the level requirement + // The higher the level, the more likely it is to evolve. + // If the mon is N levels above the requirement, it has a (N - Multiplier*Requirement) / (Multiplier * Requirement) chance to evolve. + // As an example, if the pokemon evolves at level 50, and the mon is currently level 54, and the multiplier is 0.2, + // Then it is guaranteed to evolve by level 60, and has a 10% chance to be evolved + let multiplier = 1; + switch (encounterKind) { + case EvoLevelThresholdKind.STRONG: + multiplier = STRONG_LEVEL_DIFF_PERCENT; + break; + case EvoLevelThresholdKind.NORMAL: + multiplier = NORMAL_TRAINER_LEVEL_DIFF_PERCENT; + break; + case EvoLevelThresholdKind.WILD: + multiplier = WILD_LEVEL_DIFF_PERCENT; + break; + } + + console.info( + "%c Returning a random integer between %d and %d", + "color: blue", + choice, + Math.round(choice * multiplier), + ); + const randomLevel = randSeedInt(choice, Math.round(choice * multiplier)); + console.info("%c Random level is %d", "color: blue", randomLevel); + if (randomLevel <= level) { + return determineEnemySpecies( + getPokemonSpecies(evoSpecies), + level, + true, + forTrainer, + strength, + encounterKind, + false, + ); + } + return species.speciesId; +} diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index 9af2dbe221c..f4efedb0fb2 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -1,6 +1,7 @@ import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions"; import { pokemonEvolutions } from "#balance/pokemon-evolutions"; import { BiomeId } from "#enums/biome-id"; +import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; import { PokemonType } from "#enums/pokemon-type"; import { SpeciesId } from "#enums/species-id"; import { TimeOfDay } from "#enums/time-of-day"; @@ -7621,12 +7622,10 @@ export function initBiomes() { ? biomeLinks[biome] as (BiomeId | [ BiomeId, number ])[] : [ biomeLinks[biome] as BiomeId ]; for (const linkedBiomeEntry of linkedBiomes) { - const linkedBiome = !Array.isArray(linkedBiomeEntry) - ? linkedBiomeEntry as BiomeId - : linkedBiomeEntry[0]; - const biomeChance = !Array.isArray(linkedBiomeEntry) - ? 1 - : linkedBiomeEntry[1]; + const linkedBiome = Array.isArray(linkedBiomeEntry) + ? linkedBiomeEntry[0] : linkedBiomeEntry as BiomeId; + const biomeChance = Array.isArray(linkedBiomeEntry) + ? linkedBiomeEntry[1] : 1; if (!biomeDepths.hasOwnProperty(linkedBiome) || biomeChance < biomeDepths[linkedBiome][1] || (depth < biomeDepths[linkedBiome][0] && biomeChance === biomeDepths[linkedBiome][1])) { biomeDepths[linkedBiome] = [ depth + 1, biomeChance ]; traverseBiome(linkedBiome, depth + 1); @@ -7736,11 +7735,11 @@ export function initBiomes() { for (let s = 1; s < entry.length; s++) { const speciesId = entry[s]; const prevolution = entry.flatMap((s: string | number) => pokemonEvolutions[s]).find(e => e && e.speciesId === speciesId); - const level = prevolution.level - (prevolution.level === 1 ? 1 : 0) + (prevolution.wildDelay * 10) - (tier >= BiomePoolTier.BOSS ? 10 : 0); - if (!newEntry.hasOwnProperty(level)) { - newEntry[level] = [ speciesId ]; - } else { + const level = prevolution.level - (prevolution.level === 1 ? 1 : 0) + EvoLevelThresholdKind.WILD - (tier >= BiomePoolTier.BOSS ? 10 : 0); + if (newEntry.hasOwnProperty(level)) { newEntry[level].push(speciesId); + } else { + newEntry[level] = [ speciesId ]; } } biomeTierTimePool[e] = newEntry; diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 0c2fa4e78fa..b6764f32058 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -1,3 +1,5 @@ +// biome-ignore lint/correctness/noUnusedImports: Used in TSDoc comments +import type { determineEnemySpecies } from "#app/ai/ai-species-gen"; import { defaultStarterSpecies } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { speciesStarterCosts } from "#balance/starters"; @@ -14,20 +16,12 @@ import { TimeOfDay } from "#enums/time-of-day"; import { WeatherType } from "#enums/weather-type"; import type { Pokemon } from "#field/pokemon"; import type { SpeciesStatBoosterItem, SpeciesStatBoosterModifierType } from "#modifiers/modifier-type"; +import type { EvoLevelThreshold } from "#types/species-gen-types"; import { coerceArray, randSeedInt } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import { toCamelCase } from "#utils/strings"; import i18next from "i18next"; -export enum SpeciesWildEvolutionDelay { - NONE, - SHORT, - MEDIUM, - LONG, - VERY_LONG, - NEVER -} - export enum EvolutionItem { NONE, @@ -81,13 +75,6 @@ export enum EvolutionItem { const tyrogueMoves = [MoveId.LOW_SWEEP, MoveId.MACH_PUNCH, MoveId.RAPID_SPIN] as const; type TyrogueMove = (typeof tyrogueMoves)[number]; -/** - * Pokemon Evolution tuple type consisting of: - * @property 0 {@linkcode SpeciesId} The species of the Pokemon. - * @property 1 {@linkcode number} The level at which the Pokemon evolves. - */ -export type EvolutionLevel = [species: SpeciesId, level: number]; - const EvoCondKey = { FRIENDSHIP: 1, TIME: 2, @@ -225,10 +212,15 @@ export class SpeciesFormEvolution { public level: number; public item: EvolutionItem | null; public condition: SpeciesEvolutionCondition | null; - public wildDelay: SpeciesWildEvolutionDelay; + /** + * A triple containing the level thresholds for evolutions based on the encounter sort + * @see {@linkcode EvoLevelThreshold} + * @see {@linkcode determineEnemySpecies} + */ + public evoLevelThreshold?: EvoLevelThreshold; public desc = ""; - constructor(speciesId: SpeciesId, preFormKey: string | null, evoFormKey: string | null, level: number, item: EvolutionItem | null, condition: EvolutionConditionData | EvolutionConditionData[] | null, wildDelay?: SpeciesWildEvolutionDelay) { + constructor(speciesId: SpeciesId, preFormKey: string | null, evoFormKey: string | null, level: number, item: EvolutionItem | null, condition: EvolutionConditionData | EvolutionConditionData[] | null, evoDelay?: EvoLevelThreshold) { this.speciesId = speciesId; this.preFormKey = preFormKey; this.evoFormKey = evoFormKey; @@ -237,7 +229,9 @@ export class SpeciesFormEvolution { if (condition != null) { this.condition = new SpeciesEvolutionCondition(...coerceArray(condition)); } - this.wildDelay = wildDelay ?? SpeciesWildEvolutionDelay.NONE; + if (evoDelay != null) { + this.evoLevelThreshold = evoDelay; + } } get description(): string { @@ -320,8 +314,8 @@ export class SpeciesFormEvolution { } export class SpeciesEvolution extends SpeciesFormEvolution { - constructor(speciesId: SpeciesId, level: number, item: EvolutionItem | null, condition: EvolutionConditionData | EvolutionConditionData[] | null, wildDelay?: SpeciesWildEvolutionDelay) { - super(speciesId, null, null, level, item, condition, wildDelay); + constructor(speciesId: SpeciesId, level: number, item: EvolutionItem | null, condition: EvolutionConditionData | EvolutionConditionData[] | null, evoDelay?: EvoLevelThreshold) { + super(speciesId, null, null, level, item, condition, evoDelay); } } @@ -329,7 +323,7 @@ export class FusionSpeciesFormEvolution extends SpeciesFormEvolution { public primarySpeciesId: SpeciesId; constructor(primarySpeciesId: SpeciesId, evolution: SpeciesFormEvolution) { - super(evolution.speciesId, evolution.preFormKey, evolution.evoFormKey, evolution.level, evolution.item, evolution.condition?.data ?? null, evolution.wildDelay); + super(evolution.speciesId, evolution.preFormKey, evolution.evoFormKey, evolution.level, evolution.item, evolution.condition?.data ?? null, evolution.evoLevelThreshold); this.primarySpeciesId = primarySpeciesId; } @@ -441,7 +435,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [SpeciesId.SLOWPOKE]: [ new SpeciesEvolution(SpeciesId.SLOWBRO, 37, null, null), - new SpeciesEvolution(SpeciesId.SLOWKING, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SLOWKING, 1, EvolutionItem.LINKING_CORD, null, [37, 37, 37]) ], [SpeciesId.MAGNEMITE]: [ new SpeciesEvolution(SpeciesId.MAGNETON, 30, null, null) @@ -658,7 +652,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [SpeciesId.KIRLIA]: [ new SpeciesEvolution(SpeciesId.GARDEVOIR, 30, null, null), - new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}, SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(SpeciesId.GALLADE, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.MALE}, [30, 30, 30]), ], [SpeciesId.SURSKIT]: [new SpeciesEvolution(SpeciesId.MASQUERAIN, 22, null, null)], [SpeciesId.SHROOMISH]: [new SpeciesEvolution(SpeciesId.BRELOOM, 23, null, null)], @@ -739,7 +733,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [SpeciesId.SNORUNT]: [ new SpeciesEvolution(SpeciesId.GLALIE, 42, null, null), - new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}, SpeciesWildEvolutionDelay.LONG), + new SpeciesEvolution(SpeciesId.FROSLASS, 1, EvolutionItem.DAWN_STONE, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}, [42, 42, 42]), ], [SpeciesId.SPHEAL]: [new SpeciesEvolution(SpeciesId.SEALEO, 32, null, null)], [SpeciesId.SEALEO]: [new SpeciesEvolution(SpeciesId.WALREIN, 44, null, null)], @@ -808,7 +802,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.LUMINEON, 31, null, null) ], [SpeciesId.MANTYKE]: [ - new SpeciesEvolution(SpeciesId.MANTINE, 32, null, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.REMORAID}, SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.MANTINE, 28, null, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.REMORAID}, [28, 28, 48]) ], [SpeciesId.SNOVER]: [ new SpeciesEvolution(SpeciesId.ABOMASNOW, 40, null, null) @@ -977,7 +971,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.BISHARP, 52, null, null) ], [SpeciesId.BISHARP]: [ - new SpeciesEvolution(SpeciesId.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, [70, 85, 100]) ], [SpeciesId.RUFFLET]: [ new SpeciesEvolution(SpeciesId.HISUI_BRAVIARY, 54, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}), @@ -1038,7 +1032,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.GOGOAT, 32, null, null) ], [SpeciesId.PANCHAM]: [ - new SpeciesEvolution(SpeciesId.PANGORO, 32, null, {key: EvoCondKey.PARTY_TYPE, pkmnType: PokemonType.DARK}, SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.PANGORO, 32, null, {key: EvoCondKey.PARTY_TYPE, pkmnType: PokemonType.DARK}, [32, 36, 40]) ], [SpeciesId.ESPURR]: [ new SpeciesFormEvolution(SpeciesId.MEOWSTIC, "", "female", 25, null, {key: EvoCondKey.GENDER, gender: Gender.FEMALE}), @@ -1070,7 +1064,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.SLIGGOO, 40, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}) ], [SpeciesId.SLIGGOO]: [ - new SpeciesEvolution(SpeciesId.GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, [50, 60, 70]) ], [SpeciesId.BERGMITE]: [ new SpeciesEvolution(SpeciesId.HISUI_AVALUGG, 37, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}), @@ -1150,11 +1144,11 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.KOMMO_O, 45, null, null) ], [SpeciesId.COSMOG]: [ - new SpeciesEvolution(SpeciesId.COSMOEM, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 43}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.COSMOEM, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 43}, [43, 53, 59]) ], [SpeciesId.COSMOEM]: [ - new SpeciesEvolution(SpeciesId.SOLGALEO, 13, EvolutionItem.SUN_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(SpeciesId.LUNALA, 13, EvolutionItem.MOON_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SOLGALEO, 13, EvolutionItem.SUN_FLUTE, null, [53, 73, 79]), + new SpeciesEvolution(SpeciesId.LUNALA, 13, EvolutionItem.MOON_FLUTE, null, [53, 73, 79]) ], [SpeciesId.MELTAN]: [ new SpeciesEvolution(SpeciesId.MELMETAL, 48, null, null) @@ -1268,11 +1262,12 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.GALAR_RAPIDASH, 40, null, null) ], [SpeciesId.GALAR_FARFETCHD]: [ - new SpeciesEvolution(SpeciesId.SIRFETCHD, 30, null, null, SpeciesWildEvolutionDelay.LONG) + // TODO: Uncomment evo delay when different evo condition is implemented + new SpeciesEvolution(SpeciesId.SIRFETCHD, 30, null, null, /* [30, 35, 40] */) ], [SpeciesId.GALAR_SLOWPOKE]: [ - new SpeciesEvolution(SpeciesId.GALAR_SLOWBRO, 1, EvolutionItem.GALARICA_CUFF, null, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(SpeciesId.GALAR_SLOWKING, 1, EvolutionItem.GALARICA_WREATH, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GALAR_SLOWBRO, 1, EvolutionItem.GALARICA_CUFF, null, ), + new SpeciesEvolution(SpeciesId.GALAR_SLOWKING, 1, EvolutionItem.GALARICA_WREATH, null, [37, 37, 37]) ], [SpeciesId.GALAR_MR_MIME]: [ new SpeciesEvolution(SpeciesId.MR_RIME, 42, null, null) @@ -1293,7 +1288,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.HISUI_ZOROARK, 30, null, null) ], [SpeciesId.HISUI_SLIGGOO]: [ - new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.HISUI_GOODRA, 50, null, {key: EvoCondKey.WEATHER, weather: [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ]}, [60, 70, 80]) ], [SpeciesId.SPRIGATITO]: [ new SpeciesEvolution(SpeciesId.FLORAGATO, 16, null, null) @@ -1400,188 +1395,189 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(SpeciesId.CLODSIRE, 20, null, null) ], [SpeciesId.PIKACHU]: [ - new SpeciesFormEvolution(SpeciesId.ALOLA_RAICHU, "", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesFormEvolution(SpeciesId.ALOLA_RAICHU, "", "", 1, EvolutionItem.SHINY_STONE, null, [30, 35, 40]), + new SpeciesFormEvolution(SpeciesId.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.SHINY_STONE, null, [30, 35, 40]), + new SpeciesFormEvolution(SpeciesId.RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, null, [30, 35, 40]), + new SpeciesFormEvolution(SpeciesId.RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, [30, 35, 40]) ], [SpeciesId.NIDORINA]: [ - new SpeciesEvolution(SpeciesId.NIDOQUEEN, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.NIDOQUEEN, 1, EvolutionItem.MOON_STONE, null, [32, 36, 42]) ], [SpeciesId.NIDORINO]: [ - new SpeciesEvolution(SpeciesId.NIDOKING, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.NIDOKING, 1, EvolutionItem.MOON_STONE, null, [32, 36, 42]) ], [SpeciesId.CLEFAIRY]: [ - new SpeciesEvolution(SpeciesId.CLEFABLE, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.CLEFABLE, 1, EvolutionItem.MOON_STONE, null, [32, 32, 36]) ], [SpeciesId.VULPIX]: [ - new SpeciesEvolution(SpeciesId.NINETALES, 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.NINETALES, 1, EvolutionItem.FIRE_STONE, null, [30, 35, 40]) ], [SpeciesId.JIGGLYPUFF]: [ - new SpeciesEvolution(SpeciesId.WIGGLYTUFF, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.WIGGLYTUFF, 1, EvolutionItem.MOON_STONE, null, [30, 35, 40]) ], [SpeciesId.GLOOM]: [ - new SpeciesEvolution(SpeciesId.VILEPLUME, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.BELLOSSOM, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.VILEPLUME, 1, EvolutionItem.LEAF_STONE, null, [30, 35, 40]), + new SpeciesEvolution(SpeciesId.BELLOSSOM, 1, EvolutionItem.SUN_STONE, null, [30, 35, 40]) ], [SpeciesId.GROWLITHE]: [ - new SpeciesEvolution(SpeciesId.ARCANINE, 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ARCANINE, 1, EvolutionItem.FIRE_STONE, null, [30, 35, 40]) ], [SpeciesId.POLIWHIRL]: [ - new SpeciesEvolution(SpeciesId.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.POLITOED, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, [30, 35, 40]), + new SpeciesEvolution(SpeciesId.POLITOED, 1, EvolutionItem.LINKING_CORD, null, [30, 35, 40]) ], [SpeciesId.WEEPINBELL]: [ - new SpeciesEvolution(SpeciesId.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, [31, 36, 41]) ], [SpeciesId.MAGNETON]: [ - new SpeciesEvolution(SpeciesId.MAGNEZONE, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.MAGNEZONE, 1, EvolutionItem.THUNDER_STONE, null, [50, 55, 60]) ], [SpeciesId.SHELLDER]: [ - new SpeciesEvolution(SpeciesId.CLOYSTER, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.CLOYSTER, 1, EvolutionItem.WATER_STONE, null, [36, 40, 44]) ], [SpeciesId.EXEGGCUTE]: [ - new SpeciesEvolution(SpeciesId.ALOLA_EXEGGUTOR, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ALOLA_EXEGGUTOR, 1, EvolutionItem.SUN_STONE, null, [35, 40, 40]), + new SpeciesEvolution(SpeciesId.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, [35, 40, 40]) ], [SpeciesId.TANGELA]: [ - new SpeciesEvolution(SpeciesId.TANGROWTH, 34, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.TANGROWTH, 34, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, [29, 37, 45]) ], [SpeciesId.LICKITUNG]: [ - new SpeciesEvolution(SpeciesId.LICKILICKY, 32, null, {key: EvoCondKey.MOVE, move: MoveId.ROLLOUT}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.LICKILICKY, 32, null, {key: EvoCondKey.MOVE, move: MoveId.ROLLOUT}, [33, 38, 48]) ], [SpeciesId.STARYU]: [ - new SpeciesEvolution(SpeciesId.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.STARMIE, 1, EvolutionItem.WATER_STONE, null, [20, 25, 30]) ], [SpeciesId.EEVEE]: [ - new SpeciesFormEvolution(SpeciesId.SYLVEON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.FAIRY}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.SYLVEON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.FAIRY}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.ESPEON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.ESPEON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.UMBREON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.UMBREON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.JOLTEON, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.FLAREON, "", "", 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.FLAREON, "partner", "", 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.LEAFEON, "", "", 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.LEAFEON, "partner", "", 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.GLACEON, "", "", 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.GLACEON, "partner", "", 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesFormEvolution(SpeciesId.SYLVEON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.FAIRY}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.SYLVEON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.FAIRY}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.ESPEON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.ESPEON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.UMBREON, "", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.UMBREON, "partner", "", 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.JOLTEON, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.FLAREON, "", "", 1, EvolutionItem.FIRE_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.FLAREON, "partner", "", 1, EvolutionItem.FIRE_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.LEAFEON, "", "", 1, EvolutionItem.LEAF_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.LEAFEON, "partner", "", 1, EvolutionItem.LEAF_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.GLACEON, "", "", 1, EvolutionItem.ICE_STONE, null, [24, 28, 28]), + new SpeciesFormEvolution(SpeciesId.GLACEON, "partner", "", 1, EvolutionItem.ICE_STONE, null, [24, 28, 28]) ], [SpeciesId.TOGETIC]: [ - new SpeciesEvolution(SpeciesId.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, [40, 45, 50]) ], [SpeciesId.AIPOM]: [ - new SpeciesEvolution(SpeciesId.AMBIPOM, 32, null, {key: EvoCondKey.MOVE, move: MoveId.DOUBLE_HIT}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.AMBIPOM, 32, null, {key: EvoCondKey.MOVE, move: MoveId.DOUBLE_HIT}, [33, 38, 38]) ], [SpeciesId.SUNKERN]: [ - new SpeciesEvolution(SpeciesId.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, [16, 18, 20]) ], [SpeciesId.YANMA]: [ - new SpeciesEvolution(SpeciesId.YANMEGA, 33, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.YANMEGA, 33, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, [34, 42, 50]) ], [SpeciesId.MURKROW]: [ - new SpeciesEvolution(SpeciesId.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, [26, 32, 38]) ], [SpeciesId.MISDREAVUS]: [ - new SpeciesEvolution(SpeciesId.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, [22, 26, 30]) ], [SpeciesId.GIRAFARIG]: [ - new SpeciesEvolution(SpeciesId.FARIGIRAF, 32, null, {key: EvoCondKey.MOVE, move: MoveId.TWIN_BEAM}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.FARIGIRAF, 32, null, {key: EvoCondKey.MOVE, move: MoveId.TWIN_BEAM}, [33, 38, 48]) ], [SpeciesId.DUNSPARCE]: [ - new SpeciesFormEvolution(SpeciesId.DUDUNSPARCE, "", "three-segment", 32, null, [{key: EvoCondKey.RANDOM_FORM, value: 4}, {key: EvoCondKey.MOVE, move: MoveId.HYPER_DRILL}], SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.DUDUNSPARCE, "", "two-segment", 32, null, {key: EvoCondKey.MOVE, move: MoveId.HYPER_DRILL}, SpeciesWildEvolutionDelay.LONG) + new SpeciesFormEvolution(SpeciesId.DUDUNSPARCE, "", "three-segment", 32, null, [{key: EvoCondKey.RANDOM_FORM, value: 4}, {key: EvoCondKey.MOVE, move: MoveId.HYPER_DRILL}], [33, 38, 48]), + new SpeciesFormEvolution(SpeciesId.DUDUNSPARCE, "", "two-segment", 32, null, {key: EvoCondKey.MOVE, move: MoveId.HYPER_DRILL}, [33, 38, 48]) ], [SpeciesId.GLIGAR]: [ - new SpeciesEvolution(SpeciesId.GLISCOR, 1, EvolutionItem.RAZOR_FANG, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GLISCOR, 1, EvolutionItem.RAZOR_FANG, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}, [48, 56, 64]) ], [SpeciesId.SNEASEL]: [ - new SpeciesEvolution(SpeciesId.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}, [48, 56, 64]) ], [SpeciesId.HAPPINY]: [ - new SpeciesEvolution(SpeciesId.CHANSEY, 1, EvolutionItem.OVAL_STONE, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}, SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.CHANSEY, 1, EvolutionItem.OVAL_STONE, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}, [20, 25, 25]) ], [SpeciesId.URSARING]: [ - new SpeciesEvolution(SpeciesId.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna + new SpeciesEvolution(SpeciesId.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, [70, 85, 100]) //Ursaring does not evolve into Bloodmoon Ursaluna ], [SpeciesId.PILOSWINE]: [ - new SpeciesEvolution(SpeciesId.MAMOSWINE, 1, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.MAMOSWINE, 1, null, {key: EvoCondKey.MOVE, move: MoveId.ANCIENT_POWER}, [48, 56, 64]) ], [SpeciesId.STANTLER]: [ - new SpeciesEvolution(SpeciesId.WYRDEER, 25, null, {key: EvoCondKey.MOVE, move: MoveId.PSYSHIELD_BASH}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.WYRDEER, 25, null, {key: EvoCondKey.MOVE, move: MoveId.PSYSHIELD_BASH}, [35, 45, 55]) ], [SpeciesId.LOMBRE]: [ - new SpeciesEvolution(SpeciesId.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, [35, 42, 49]) ], [SpeciesId.NUZLEAF]: [ - new SpeciesEvolution(SpeciesId.SHIFTRY, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SHIFTRY, 1, EvolutionItem.LEAF_STONE, null, [35, 42, 49]) ], [SpeciesId.NOSEPASS]: [ - new SpeciesEvolution(SpeciesId.PROBOPASS, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.PROBOPASS, 1, EvolutionItem.THUNDER_STONE, null, [32, 36, 40]) ], [SpeciesId.SKITTY]: [ - new SpeciesEvolution(SpeciesId.DELCATTY, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.DELCATTY, 1, EvolutionItem.MOON_STONE, null, [16, 20, 20]) ], [SpeciesId.ROSELIA]: [ - new SpeciesEvolution(SpeciesId.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, [32, 32, 36]) ], [SpeciesId.BONSLY]: [ - new SpeciesEvolution(SpeciesId.SUDOWOODO, 1, null, {key: EvoCondKey.MOVE, move: MoveId.MIMIC}, SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.SUDOWOODO, 1, null, {key: EvoCondKey.MOVE, move: MoveId.MIMIC}, [19, 24, 24]) ], [SpeciesId.MIME_JR]: [ - new SpeciesEvolution(SpeciesId.GALAR_MR_MIME, 1, null, [{key: EvoCondKey.MOVE, move: MoveId.MIMIC}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(SpeciesId.MR_MIME, 1, null, [{key: EvoCondKey.MOVE, move: MoveId.MIMIC}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.GALAR_MR_MIME, 1, null, [{key: EvoCondKey.MOVE, move: MoveId.MIMIC}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], [19, 24, 24]), + new SpeciesEvolution(SpeciesId.MR_MIME, 1, null, [{key: EvoCondKey.MOVE, move: MoveId.MIMIC}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], [19, 24, 24]) ], [SpeciesId.PANSAGE]: [ - new SpeciesEvolution(SpeciesId.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, [24, 28, 32]) ], [SpeciesId.PANSEAR]: [ - new SpeciesEvolution(SpeciesId.SIMISEAR, 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SIMISEAR, 1, EvolutionItem.FIRE_STONE, null, [24, 28, 32]) ], [SpeciesId.PANPOUR]: [ - new SpeciesEvolution(SpeciesId.SIMIPOUR, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SIMIPOUR, 1, EvolutionItem.WATER_STONE, null, [24, 28, 32]) ], [SpeciesId.MUNNA]: [ - new SpeciesEvolution(SpeciesId.MUSHARNA, 1, EvolutionItem.MOON_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.MUSHARNA, 1, EvolutionItem.MOON_STONE, null, [28, 32, 32]) ], [SpeciesId.COTTONEE]: [ - new SpeciesEvolution(SpeciesId.WHIMSICOTT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.WHIMSICOTT, 1, EvolutionItem.SUN_STONE, null, [28, 32, 32]) ], [SpeciesId.PETILIL]: [ - new SpeciesEvolution(SpeciesId.HISUI_LILLIGANT, 1, EvolutionItem.DAWN_STONE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.HISUI_LILLIGANT, 1, EvolutionItem.DAWN_STONE, null, [28, 32, 32]), + new SpeciesEvolution(SpeciesId.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, [28, 32, 32]) ], [SpeciesId.BASCULIN]: [ - new SpeciesFormEvolution(SpeciesId.BASCULEGION, "white-striped", "female", 40, null, [{key: EvoCondKey.GENDER, gender: Gender.FEMALE}], SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesFormEvolution(SpeciesId.BASCULEGION, "white-striped", "male", 40, null, [{key: EvoCondKey.GENDER, gender: Gender.MALE}], SpeciesWildEvolutionDelay.VERY_LONG) + // TODO: Uncomment evo delay when different evo condition is implemented + new SpeciesFormEvolution(SpeciesId.BASCULEGION, "white-striped", "female", 40, null, [{key: EvoCondKey.GENDER, gender: Gender.FEMALE}], /* [45, 65, 85] */), + new SpeciesFormEvolution(SpeciesId.BASCULEGION, "white-striped", "male", 40, null, [{key: EvoCondKey.GENDER, gender: Gender.MALE}], /* [45, 65, 85 ] */) ], [SpeciesId.MINCCINO]: [ - new SpeciesEvolution(SpeciesId.CINCCINO, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.CINCCINO, 1, EvolutionItem.SHINY_STONE, null, [24, 32, 32]) ], [SpeciesId.EELEKTRIK]: [ - new SpeciesEvolution(SpeciesId.EELEKTROSS, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.EELEKTROSS, 1, EvolutionItem.THUNDER_STONE, null, [49, 54, 54]) ], [SpeciesId.LAMPENT]: [ - new SpeciesEvolution(SpeciesId.CHANDELURE, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.CHANDELURE, 1, EvolutionItem.DUSK_STONE, null, [51, 56, 56]) ], [SpeciesId.FLOETTE]: [ - new SpeciesEvolution(SpeciesId.FLORGES, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.FLORGES, 1, EvolutionItem.SHINY_STONE, null, [29, 34, 39]) ], [SpeciesId.DOUBLADE]: [ - new SpeciesEvolution(SpeciesId.AEGISLASH, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.AEGISLASH, 1, EvolutionItem.DUSK_STONE, null, [50, 60, 70]) ], [SpeciesId.HELIOPTILE]: [ - new SpeciesEvolution(SpeciesId.HELIOLISK, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.HELIOLISK, 1, EvolutionItem.SUN_STONE, null, [32, 36, 36]) ], [SpeciesId.CHARJABUG]: [ - new SpeciesEvolution(SpeciesId.VIKAVOLT, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.VIKAVOLT, 1, EvolutionItem.THUNDER_STONE, null, [40, 45, 45]) ], [SpeciesId.CRABRAWLER]: [ - new SpeciesEvolution(SpeciesId.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.CRABOMINABLE, 1, EvolutionItem.ICE_STONE, null, [35, 40, 40]) ], [SpeciesId.ROCKRUFF]: [ new SpeciesFormEvolution(SpeciesId.LYCANROC, "own-tempo", "dusk", 25, null, null), @@ -1589,232 +1585,232 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesFormEvolution(SpeciesId.LYCANROC, "", "midnight", 25, null, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}) ], [SpeciesId.STEENEE]: [ - new SpeciesEvolution(SpeciesId.TSAREENA, 28, null, {key: EvoCondKey.MOVE, move: MoveId.STOMP}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.TSAREENA, 28, null, {key: EvoCondKey.MOVE, move: MoveId.STOMP}, [29, 34, 39]) ], [SpeciesId.POIPOLE]: [ - new SpeciesEvolution(SpeciesId.NAGANADEL, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_PULSE}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.NAGANADEL, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_PULSE}, [53, 59, 61]) ], [SpeciesId.ALOLA_SANDSHREW]: [ - new SpeciesEvolution(SpeciesId.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, [22, 22, 22]) ], [SpeciesId.ALOLA_VULPIX]: [ - new SpeciesEvolution(SpeciesId.ALOLA_NINETALES, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ALOLA_NINETALES, 1, EvolutionItem.ICE_STONE, null, [30, 35, 40]) ], [SpeciesId.APPLIN]: [ - new SpeciesEvolution(SpeciesId.DIPPLIN, 1, EvolutionItem.SYRUPY_APPLE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.FLAPPLE, 1, EvolutionItem.TART_APPLE, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.DIPPLIN, 1, EvolutionItem.SYRUPY_APPLE, null, [24, 24, 28]), + new SpeciesEvolution(SpeciesId.FLAPPLE, 1, EvolutionItem.TART_APPLE, null, [24, 24, 28]), + new SpeciesEvolution(SpeciesId.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, [24, 24, 28]) ], [SpeciesId.CLOBBOPUS]: [ new SpeciesEvolution(SpeciesId.GRAPPLOCT, 35, null, {key: EvoCondKey.MOVE, move: MoveId.TAUNT}/*Once Taunt is implemented, change evo level to 1 and delay to LONG*/) ], [SpeciesId.SINISTEA]: [ - new SpeciesFormEvolution(SpeciesId.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesFormEvolution(SpeciesId.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, [30, 35, 40]), + new SpeciesFormEvolution(SpeciesId.POLTEAGEIST, "antique", "antique", 1, EvolutionItem.CHIPPED_POT, null, [30, 35, 40]) ], [SpeciesId.MILCERY]: [ new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "vanilla-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.TOWN, BiomeId.PLAINS, BiomeId.GRASS, BiomeId.TALL_GRASS, BiomeId.METROPOLIS ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "ruby-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.BADLANDS, BiomeId.VOLCANO, BiomeId.GRAVEYARD, BiomeId.FACTORY, BiomeId.SLUM ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "matcha-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.FOREST, BiomeId.SWAMP, BiomeId.MEADOW, BiomeId.JUNGLE ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "mint-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.SEA, BiomeId.BEACH, BiomeId.LAKE, BiomeId.SEABED ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "lemon-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.DESERT, BiomeId.POWER_PLANT, BiomeId.DOJO, BiomeId.RUINS, BiomeId.CONSTRUCTION_SITE ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "salted-cream", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.MOUNTAIN, BiomeId.CAVE, BiomeId.ICE_CAVE, BiomeId.FAIRY_CAVE, BiomeId.SNOWY_FOREST ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "ruby-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.WASTELAND, BiomeId.LABORATORY ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "caramel-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.TEMPLE, BiomeId.ISLAND ]}, - SpeciesWildEvolutionDelay.LONG), + [20, 25, 25]), new SpeciesFormEvolution(SpeciesId.ALCREMIE, "", "rainbow-swirl", 1, EvolutionItem.STRAWBERRY_SWEET, {key: EvoCondKey.BIOME, biome: [ BiomeId.ABYSS, BiomeId.SPACE, BiomeId.END ]}, - SpeciesWildEvolutionDelay.LONG) + [20, 25, 25]) ], [SpeciesId.DURALUDON]: [ - new SpeciesFormEvolution(SpeciesId.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(SpeciesId.ARCHALUDON, "", "", 1, EvolutionItem.METAL_ALLOY, null, [65, 80, 95]) ], [SpeciesId.KUBFU]: [ - new SpeciesFormEvolution(SpeciesId.URSHIFU, "", "single-strike", 1, EvolutionItem.SCROLL_OF_DARKNESS, null, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesFormEvolution(SpeciesId.URSHIFU, "", "rapid-strike", 1, EvolutionItem.SCROLL_OF_WATERS, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(SpeciesId.URSHIFU, "", "single-strike", 1, EvolutionItem.SCROLL_OF_DARKNESS, null, [50, 60, 70]), + new SpeciesFormEvolution(SpeciesId.URSHIFU, "", "rapid-strike", 1, EvolutionItem.SCROLL_OF_WATERS, null, [50, 60, 70]) ], [SpeciesId.GALAR_DARUMAKA]: [ - new SpeciesEvolution(SpeciesId.GALAR_DARMANITAN, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.GALAR_DARMANITAN, 1, EvolutionItem.ICE_STONE, null, [35, 35, 35]) ], [SpeciesId.HISUI_GROWLITHE]: [ - new SpeciesEvolution(SpeciesId.HISUI_ARCANINE, 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.HISUI_ARCANINE, 1, EvolutionItem.FIRE_STONE, null, [35, 40, 45]) ], [SpeciesId.HISUI_VOLTORB]: [ - new SpeciesEvolution(SpeciesId.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, [30, 30, 30]) ], [SpeciesId.HISUI_QWILFISH]: [ - new SpeciesEvolution(SpeciesId.OVERQWIL, 28, null, {key: EvoCondKey.MOVE, move: MoveId.BARB_BARRAGE}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.OVERQWIL, 28, null, {key: EvoCondKey.MOVE, move: MoveId.BARB_BARRAGE}, [38, 48, 58]) ], [SpeciesId.HISUI_SNEASEL]: [ - new SpeciesEvolution(SpeciesId.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]} /* Razor claw at day*/, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}, [48, 54, 60]) ], [SpeciesId.CHARCADET]: [ - new SpeciesEvolution(SpeciesId.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(SpeciesId.CERULEDGE, 1, EvolutionItem.MALICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, [30, 35, 35]), + new SpeciesEvolution(SpeciesId.CERULEDGE, 1, EvolutionItem.MALICIOUS_ARMOR, null, [30, 35, 35]) ], [SpeciesId.TADBULB]: [ - new SpeciesEvolution(SpeciesId.BELLIBOLT, 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.BELLIBOLT, 1, EvolutionItem.THUNDER_STONE, null, [20, 28, 28]) ], [SpeciesId.CAPSAKID]: [ - new SpeciesEvolution(SpeciesId.SCOVILLAIN, 1, EvolutionItem.FIRE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SCOVILLAIN, 1, EvolutionItem.FIRE_STONE, null, [25, 30, 30]) ], [SpeciesId.CETODDLE]: [ - new SpeciesEvolution(SpeciesId.CETITAN, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.CETITAN, 1, EvolutionItem.ICE_STONE, null, [40, 44, 48]) ], [SpeciesId.POLTCHAGEIST]: [ - new SpeciesFormEvolution(SpeciesId.SINISTCHA, "counterfeit", "unremarkable", 1, EvolutionItem.UNREMARKABLE_TEACUP, null, SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(SpeciesId.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesFormEvolution(SpeciesId.SINISTCHA, "counterfeit", "unremarkable", 1, EvolutionItem.UNREMARKABLE_TEACUP, null, [30, 35, 40]), + new SpeciesFormEvolution(SpeciesId.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, [30, 35, 40]) ], [SpeciesId.DIPPLIN]: [ - new SpeciesEvolution(SpeciesId.HYDRAPPLE, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_CHEER}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.HYDRAPPLE, 1, null, {key: EvoCondKey.MOVE, move: MoveId.DRAGON_CHEER}, [56, 68, 80]) ], [SpeciesId.KADABRA]: [ - new SpeciesEvolution(SpeciesId.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, [40, 45, 50]) ], [SpeciesId.MACHOKE]: [ - new SpeciesEvolution(SpeciesId.MACHAMP, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.MACHAMP, 1, EvolutionItem.LINKING_CORD, null, [38, 46, 54]) ], [SpeciesId.GRAVELER]: [ - new SpeciesEvolution(SpeciesId.GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GOLEM, 1, EvolutionItem.LINKING_CORD, null, [35, 45, 55]) ], [SpeciesId.HAUNTER]: [ - new SpeciesEvolution(SpeciesId.GENGAR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GENGAR, 1, EvolutionItem.LINKING_CORD, null, [40, 45, 50]) ], [SpeciesId.ONIX]: [ - new SpeciesEvolution(SpeciesId.STEELIX, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.STEEL}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.STEELIX, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.STEEL}, [35, 40, 45]) ], [SpeciesId.RHYDON]: [ - new SpeciesEvolution(SpeciesId.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, [72, 82, 92]) ], [SpeciesId.SEADRA]: [ - new SpeciesEvolution(SpeciesId.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, [42, 52, 62]) ], [SpeciesId.SCYTHER]: [ - new SpeciesEvolution(SpeciesId.SCIZOR, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.STEEL}, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(SpeciesId.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SCIZOR, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.MOVE_TYPE, pkmnType: PokemonType.STEEL}, [40, 50, 60]), + new SpeciesEvolution(SpeciesId.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, [40, 50, 60]) ], [SpeciesId.ELECTABUZZ]: [ - new SpeciesEvolution(SpeciesId.ELECTIVIRE, 1, EvolutionItem.ELECTIRIZER, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ELECTIVIRE, 1, EvolutionItem.ELECTIRIZER, null, [50, 60, 70]) ], [SpeciesId.MAGMAR]: [ - new SpeciesEvolution(SpeciesId.MAGMORTAR, 1, EvolutionItem.MAGMARIZER, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.MAGMORTAR, 1, EvolutionItem.MAGMARIZER, null, [50, 60, 70]) ], [SpeciesId.PORYGON]: [ - new SpeciesEvolution(SpeciesId.PORYGON2, 1, EvolutionItem.UPGRADE, null, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.PORYGON2, 1, EvolutionItem.UPGRADE, null, [32, 40, 48]) ], [SpeciesId.PORYGON2]: [ - new SpeciesEvolution(SpeciesId.PORYGON_Z, 1, EvolutionItem.DUBIOUS_DISC, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.PORYGON_Z, 1, EvolutionItem.DUBIOUS_DISC, null, [64, 72, 80]) ], [SpeciesId.FEEBAS]: [ - new SpeciesEvolution(SpeciesId.MILOTIC, 1, EvolutionItem.PRISM_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.MILOTIC, 1, EvolutionItem.PRISM_SCALE, null, [30, 35, 40]) ], [SpeciesId.DUSCLOPS]: [ - new SpeciesEvolution(SpeciesId.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, [47, 57, 67]) ], [SpeciesId.CLAMPERL]: [ - new SpeciesEvolution(SpeciesId.HUNTAIL, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.HELD_ITEM, itemKey: "DEEP_SEA_TOOTH"}, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesEvolution(SpeciesId.GOREBYSS, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.HELD_ITEM, itemKey: "DEEP_SEA_SCALE"}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.HUNTAIL, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.HELD_ITEM, itemKey: "DEEP_SEA_TOOTH"}, [40, 40, 50]), + new SpeciesEvolution(SpeciesId.GOREBYSS, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.HELD_ITEM, itemKey: "DEEP_SEA_SCALE"}, [40, 40, 50]) ], [SpeciesId.BOLDORE]: [ - new SpeciesEvolution(SpeciesId.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GIGALITH, 1, EvolutionItem.LINKING_CORD, null, [35, 40, 45]) ], [SpeciesId.GURDURR]: [ - new SpeciesEvolution(SpeciesId.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.CONKELDURR, 1, EvolutionItem.LINKING_CORD, null, [35, 40, 45]) ], [SpeciesId.KARRABLAST]: [ - new SpeciesEvolution(SpeciesId.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.SHELMET}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ESCAVALIER, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.SHELMET}, [30, 30, 35]) ], [SpeciesId.SHELMET]: [ - new SpeciesEvolution(SpeciesId.ACCELGOR, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.KARRABLAST}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ACCELGOR, 1, EvolutionItem.LINKING_CORD, {key: EvoCondKey.SPECIES_CAUGHT, speciesCaught: SpeciesId.KARRABLAST}, [30, 30, 35]) ], [SpeciesId.SPRITZEE]: [ - new SpeciesEvolution(SpeciesId.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.AROMATISSE, 1, EvolutionItem.SACHET, null, [25, 30, 35]) ], [SpeciesId.SWIRLIX]: [ - new SpeciesEvolution(SpeciesId.SLURPUFF, 1, EvolutionItem.WHIPPED_DREAM, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SLURPUFF, 1, EvolutionItem.WHIPPED_DREAM, null, [25, 30, 35]) ], [SpeciesId.PHANTUMP]: [ - new SpeciesEvolution(SpeciesId.TREVENANT, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.TREVENANT, 1, EvolutionItem.LINKING_CORD, null, [30, 35, 40]) ], [SpeciesId.PUMPKABOO]: [ - new SpeciesEvolution(SpeciesId.GOURGEIST, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.GOURGEIST, 1, EvolutionItem.LINKING_CORD, null, [30, 35, 40]) ], [SpeciesId.ALOLA_GRAVELER]: [ - new SpeciesEvolution(SpeciesId.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, [35, 45, 55]) ], [SpeciesId.PRIMEAPE]: [ - new SpeciesEvolution(SpeciesId.ANNIHILAPE, 35, null, {key: EvoCondKey.MOVE, move: MoveId.RAGE_FIST}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.ANNIHILAPE, 35, null, {key: EvoCondKey.MOVE, move: MoveId.RAGE_FIST}, [45, 55, 65]) ], [SpeciesId.GOLBAT]: [ - new SpeciesEvolution(SpeciesId.CROBAT, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.CROBAT, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, [42, 48, 54]) ], [SpeciesId.CHANSEY]: [ - new SpeciesEvolution(SpeciesId.BLISSEY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 180}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.BLISSEY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 180}, [40, 45, 45]) ], [SpeciesId.PICHU]: [ - new SpeciesFormEvolution(SpeciesId.PIKACHU, "spiky", "partner", 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, SpeciesWildEvolutionDelay.SHORT), - new SpeciesFormEvolution(SpeciesId.PIKACHU, "", "", 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, SpeciesWildEvolutionDelay.SHORT), + new SpeciesFormEvolution(SpeciesId.PIKACHU, "spiky", "partner", 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, [12, 12, 16]), + new SpeciesFormEvolution(SpeciesId.PIKACHU, "", "", 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, [12, 12, 16]), ], [SpeciesId.CLEFFA]: [ - new SpeciesEvolution(SpeciesId.CLEFAIRY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 160}, SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.CLEFAIRY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 160}, [12, 12, 16]) ], [SpeciesId.IGGLYBUFF]: [ - new SpeciesEvolution(SpeciesId.JIGGLYPUFF, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.JIGGLYPUFF, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, [12, 12, 16]) ], [SpeciesId.TOGEPI]: [ - new SpeciesEvolution(SpeciesId.TOGETIC, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.TOGETIC, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, [20, 25, 25]) ], [SpeciesId.AZURILL]: [ - new SpeciesEvolution(SpeciesId.MARILL, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.MARILL, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 70}, [12, 12, 12]) ], [SpeciesId.BUDEW]: [ - new SpeciesEvolution(SpeciesId.ROSELIA, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 70}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], SpeciesWildEvolutionDelay.SHORT) + new SpeciesEvolution(SpeciesId.ROSELIA, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 70}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], [12, 12, 16]) ], [SpeciesId.BUNEARY]: [ - new SpeciesEvolution(SpeciesId.LOPUNNY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 50}, SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.LOPUNNY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 50}, [25, 25, 30]) ], [SpeciesId.CHINGLING]: [ - new SpeciesEvolution(SpeciesId.CHIMECHO, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 90}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.CHIMECHO, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 90}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], [20, 25, 25]) ], [SpeciesId.MUNCHLAX]: [ - new SpeciesEvolution(SpeciesId.SNORLAX, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.SNORLAX, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, [30, 30, 30]) ], [SpeciesId.RIOLU]: [ - new SpeciesEvolution(SpeciesId.LUCARIO, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.LUCARIO, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 120}, {key: EvoCondKey.TIME, time: [TimeOfDay.DAWN, TimeOfDay.DAY]}], [32, 32, 32]) ], [SpeciesId.WOOBAT]: [ - new SpeciesEvolution(SpeciesId.SWOOBAT, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.SWOOBAT, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 90}, [20, 25, 25]) ], [SpeciesId.SWADLOON]: [ - new SpeciesEvolution(SpeciesId.LEAVANNY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.LEAVANNY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, [30, 35, 35]) ], [SpeciesId.TYPE_NULL]: [ - new SpeciesEvolution(SpeciesId.SILVALLY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 100}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(SpeciesId.SILVALLY, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 100}, [50, 60, 70]) ], [SpeciesId.ALOLA_MEOWTH]: [ - new SpeciesEvolution(SpeciesId.ALOLA_PERSIAN, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(SpeciesId.ALOLA_PERSIAN, 1, null, {key: EvoCondKey.FRIENDSHIP, value: 120}, [28, 28, 28]) ], [SpeciesId.SNOM]: [ - new SpeciesEvolution(SpeciesId.FROSMOTH, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 90}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(SpeciesId.FROSMOTH, 1, null, [{key: EvoCondKey.FRIENDSHIP, value: 90}, {key: EvoCondKey.TIME, time: [TimeOfDay.DUSK, TimeOfDay.NIGHT]}], [20, 25, 25]) ], [SpeciesId.GIMMIGHOUL]: [ - new SpeciesFormEvolution(SpeciesId.GHOLDENGO, "chest", "", 1, null, {key: EvoCondKey.EVO_TREASURE_TRACKER, value: 10}, SpeciesWildEvolutionDelay.VERY_LONG), - new SpeciesFormEvolution(SpeciesId.GHOLDENGO, "roaming", "", 1, null, {key: EvoCondKey.EVO_TREASURE_TRACKER, value: 10}, SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesFormEvolution(SpeciesId.GHOLDENGO, "chest", "", 1, null, {key: EvoCondKey.EVO_TREASURE_TRACKER, value: 10}, [50, 60, 70]), + new SpeciesFormEvolution(SpeciesId.GHOLDENGO, "roaming", "", 1, null, {key: EvoCondKey.EVO_TREASURE_TRACKER, value: 10}, [50, 60, 70]) ] }; diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 01d4659d379..8e901002aa9 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -963,7 +963,7 @@ export function getGoldenBugNetSpecies(level: number): PokemonSpecies { w += speciesWeightPair[1]; if (roll < w) { const initialSpecies = getPokemonSpecies(speciesWeightPair[0]); - return getPokemonSpecies(initialSpecies.getSpeciesForLevel(level, true)); + return getPokemonSpecies(initialSpecies.getWildSpeciesForLevel(level, true, false, globalScene.gameMode)); } } diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 7c00bf5dff7..2804ff62d77 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -1,11 +1,11 @@ +import { determineEnemySpecies } from "#app/ai/ai-species-gen"; import type { AnySound } from "#app/battle-scene"; import type { GameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene"; import { uncatchableSpecies } from "#balance/biomes"; import { speciesEggMoves } from "#balance/egg-moves"; import { starterPassiveAbilities } from "#balance/passives"; -import type { EvolutionLevel } from "#balance/pokemon-evolutions"; -import { pokemonEvolutions, pokemonPrevolutions, SpeciesWildEvolutionDelay } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions"; import type { LevelMoves } from "#balance/pokemon-level-moves"; import { pokemonFormLevelMoves, @@ -17,6 +17,7 @@ import type { GrowthRate } from "#data/exp"; import { Gender } from "#data/gender"; import { AbilityId } from "#enums/ability-id"; import { DexAttr } from "#enums/dex-attr"; +import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; import { PartyMemberStrength } from "#enums/party-member-strength"; import type { PokemonType } from "#enums/pokemon-type"; import { SpeciesFormKey } from "#enums/species-form-key"; @@ -28,7 +29,8 @@ import type { Variant, VariantSet } from "#sprites/variant"; import { populateVariantColorCache, variantColorCache, variantData } from "#sprites/variant"; import type { Localizable } from "#types/locales"; import type { StarterMoveset } from "#types/save-data"; -import { randSeedFloat, randSeedGauss, randSeedInt } from "#utils/common"; +import type { EvolutionLevel, EvolutionLevelWithThreshold } from "#types/species-gen-types"; +import { randSeedFloat, randSeedGauss } from "#utils/common"; import { getPokemonSpecies } from "#utils/pokemon-utils"; import { toCamelCase, toPascalCase } from "#utils/strings"; import { argbFromRgba, QuantizerCelebi, rgbaFromArgb } from "@material/material-color-utilities"; @@ -891,180 +893,36 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { allowEvolving, false, (isBoss ? PartyMemberStrength.WEAKER : PartyMemberStrength.AVERAGE) + (gameMode?.isEndless ? 1 : 0), + isBoss ? EvoLevelThresholdKind.NORMAL : EvoLevelThresholdKind.WILD, ); } + /** + * Determine which species of Pokémon to use for a given level in a trainer battle. + * + * @see {@linkcode getSpeciesForLevel} + */ getTrainerSpeciesForLevel( level: number, allowEvolving = false, - strength: PartyMemberStrength, - currentWave = 0, + strength: PartyMemberStrength = PartyMemberStrength.WEAKER, + encounterKind: EvoLevelThresholdKind = EvoLevelThresholdKind.NORMAL, ): SpeciesId { - return this.getSpeciesForLevel(level, allowEvolving, true, strength, currentWave); + return this.getSpeciesForLevel(level, allowEvolving, true, strength, encounterKind); } /** - * @see {@linkcode getSpeciesForLevel} uses an ease in and ease out sine function: - * @see {@link https://easings.net/#easeInSine} - * @see {@link https://easings.net/#easeOutSine} - * Ease in is similar to an exponential function with slower growth, as in, x is directly related to y, and increase in y is higher for higher x. - * Ease out looks more similar to a logarithmic function shifted to the left. It's still a direct relation but it plateaus instead of increasing in growth. - * - * This function is used to calculate the x given to these functions, which is used for evolution chance. - * - * First is maxLevelDiff, which is a denominator for evolution chance for mons without wild evolution delay. - * This means a lower value of x will lead to a higher evolution chance. - * - * It's also used for preferredMinLevel, which is used when an evolution delay exists. - * The calculation with evolution delay is a weighted average of the easeIn and easeOut functions where preferredMinLevel is the denominator. - * This also means a lower value of x will lead to a higher evolution chance. - * @param strength {@linkcode PartyMemberStrength} The strength of the party member in question - * @returns The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance. + * Determine which species of Pokémon to use for a given level + * @see {@linkcode determineEnemySpecies} */ - private getStrengthLevelDiff(strength: PartyMemberStrength): number { - switch (Math.min(strength, PartyMemberStrength.STRONGER)) { - case PartyMemberStrength.WEAKEST: - return 60; - case PartyMemberStrength.WEAKER: - return 40; - case PartyMemberStrength.WEAK: - return 20; - case PartyMemberStrength.AVERAGE: - return 8; - case PartyMemberStrength.STRONG: - return 4; - default: - return 0; - } - } - getSpeciesForLevel( level: number, allowEvolving = false, forTrainer = false, strength: PartyMemberStrength = PartyMemberStrength.WEAKER, - currentWave = 0, + encounterKind: EvoLevelThresholdKind = EvoLevelThresholdKind.NORMAL, ): SpeciesId { - const prevolutionLevels = this.getPrevolutionLevels(); - - if (prevolutionLevels.length > 0) { - for (let pl = prevolutionLevels.length - 1; pl >= 0; pl--) { - const prevolutionLevel = prevolutionLevels[pl]; - if (level < prevolutionLevel[1]) { - return prevolutionLevel[0]; - } - } - } - - if ( - // If evolutions shouldn't happen, add more cases here :) - !allowEvolving - || !pokemonEvolutions.hasOwnProperty(this.speciesId) - || (globalScene.currentBattle?.waveIndex === 20 - && globalScene.gameMode.isClassic - && globalScene.currentBattle.trainer) - ) { - return this.speciesId; - } - - const evolutions = pokemonEvolutions[this.speciesId]; - - const easeInFunc = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn"); - const easeOutFunc = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut"); - - const evolutionPool: Map = new Map(); - let totalWeight = 0; - let noEvolutionChance = 1; - - for (const ev of evolutions) { - if (ev.level > level) { - continue; - } - - let evolutionChance: number; - - const evolutionSpecies = getPokemonSpecies(ev.speciesId); - const isRegionalEvolution = !this.isRegional() && evolutionSpecies.isRegional(); - - if (!forTrainer && isRegionalEvolution) { - evolutionChance = 0; - } else if (ev.wildDelay === SpeciesWildEvolutionDelay.NONE) { - if (strength === PartyMemberStrength.STRONGER) { - evolutionChance = 1; - } else { - const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve - const minChance: number = 0.875 - 0.125 * strength; - - evolutionChance = Math.min( - minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), - 1, - ); - } - } else { - const preferredMinLevel = Math.max(ev.level - 1 + ev.wildDelay! * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct? - let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1); - - if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) { - const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find( - ev => ev.speciesId === this.speciesId, - )!.level; // TODO: is the bang correct? - if (prevolutionLevel > 1) { - evolutionLevel = prevolutionLevel; - } - } - - evolutionChance = Math.min( - 0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) - + 0.35 - * easeOutFunc( - Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5), - ), - 1, - ); - } - - //TODO: Adjust templates and delays so we don't have to hardcode it - /* TEMPORARY! (Most) Trainers shouldn't be using unevolved Pokemon by the third gym leader / wave 80. Exceptions to this include Breeders, whose large teams are balanced by the use of weaker pokemon */ - if (currentWave >= 80 && forTrainer && strength > PartyMemberStrength.WEAKER) { - evolutionChance = 1; - noEvolutionChance = 0; - } - - if (evolutionChance > 0) { - if (isRegionalEvolution) { - evolutionChance /= evolutionSpecies.isRareRegional() ? 16 : 4; - } - - totalWeight += evolutionChance; - - evolutionPool.set(totalWeight, ev.speciesId); - - if (1 - evolutionChance < noEvolutionChance) { - noEvolutionChance = 1 - evolutionChance; - } - } - } - - if (noEvolutionChance === 1 || randSeedFloat() <= noEvolutionChance) { - return this.speciesId; - } - - const randValue = evolutionPool.size === 1 ? 0 : randSeedInt(totalWeight); - - for (const weight of evolutionPool.keys()) { - if (randValue < weight) { - // TODO: this entire function is dumb and should be changed, adding a `!` here for now until then - return getPokemonSpecies(evolutionPool.get(weight)!).getSpeciesForLevel( - level, - true, - forTrainer, - strength, - currentWave, - ); - } - } - - return this.speciesId; + return determineEnemySpecies(this, level, allowEvolving, forTrainer, strength, encounterKind); } getEvolutionLevels(): EvolutionLevel[] { @@ -1088,21 +946,39 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { return evolutionLevels; } - getPrevolutionLevels(): EvolutionLevel[] { - const prevolutionLevels: EvolutionLevel[] = []; + /** + * Get all prevolution levels for this species + * + * @remarks + * `withThresholds` is used to return the evolution level thresholds for the species, to be used + * when generating + * + * @param withThresholds - Whether to include evolution level thresholds in the returned data; default `false` + */ + getPrevolutionLevels(withThresholds: true): EvolutionLevelWithThreshold[]; + getPrevolutionLevels(withThresholds: false): EvolutionLevel[]; + getPrevolutionLevels( + withThresholds?: boolean, + ): typeof withThresholds extends false ? EvolutionLevel[] : EvolutionLevelWithThreshold[]; + getPrevolutionLevels(withThresholds = false): EvolutionLevelWithThreshold[] | EvolutionLevel[] { + const prevolutionLevels: (EvolutionLevel | EvolutionLevelWithThreshold)[] = []; const allEvolvingPokemon = Object.keys(pokemonEvolutions); for (const p of allEvolvingPokemon) { + const speciesId = Number.parseInt(p) as SpeciesId; for (const e of pokemonEvolutions[p]) { if ( e.speciesId === this.speciesId && (this.forms.length === 0 || !e.evoFormKey || e.evoFormKey === this.forms[this.formIndex].formKey) - && prevolutionLevels.every(pe => pe[0] !== Number.parseInt(p)) + && prevolutionLevels.every(pe => pe[0] !== speciesId) ) { - const speciesId = Number.parseInt(p) as SpeciesId; const level = e.level; - prevolutionLevels.push([speciesId, level]); - const subPrevolutionLevels = getPokemonSpecies(speciesId).getPrevolutionLevels(); + if (withThresholds && e.evoLevelThreshold) { + prevolutionLevels.push([speciesId, level, e.evoLevelThreshold]); + } else { + prevolutionLevels.push([speciesId, level]); + } + const subPrevolutionLevels = getPokemonSpecies(speciesId).getPrevolutionLevels(withThresholds); for (const spl of subPrevolutionLevels) { prevolutionLevels.push(spl); } @@ -1134,7 +1010,11 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { Math.min( Math.max( evolution?.level! - + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) + + Math.round( + randSeedGauss(0.5, 1 + levelDiff * 0.2) + * Math.max(evolution?.evoLevelThreshold?.[EvoLevelThresholdKind.WILD] ?? 0, 0.5) + * 5, + ) - 1, 2, evolution?.level!, @@ -1150,7 +1030,11 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { Math.min( Math.max( lastPrevolutionLevel - + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5), + + Math.round( + randSeedGauss(0.5, 1 + levelDiff * 0.2) + * Math.max(evolution?.evoLevelThreshold?.[EvoLevelThresholdKind.WILD] ?? 0, 0.5) + * 5, + ), lastPrevolutionLevel + 1, evolution?.level!, ), diff --git a/src/data/pokemon/pokemon-data.ts b/src/data/pokemon/pokemon-data.ts index 4fbb70bccb2..8a238a73f93 100644 --- a/src/data/pokemon/pokemon-data.ts +++ b/src/data/pokemon/pokemon-data.ts @@ -1,6 +1,5 @@ import type { BattlerTag } from "#data/battler-tags"; import { loadBattlerTag, SerializableBattlerTag } from "#data/battler-tags"; -import { allSpecies } from "#data/data-lists"; import type { Gender } from "#data/gender"; import { PokemonMove } from "#data/moves/pokemon-move"; import type { PokemonSpeciesForm } from "#data/pokemon-species"; @@ -16,7 +15,7 @@ import type { AttackMoveResult } from "#types/attack-move-result"; import type { IllusionData } from "#types/illusion-data"; import type { TurnMove } from "#types/turn-move"; import type { CoerceNullPropertiesToUndefined } from "#types/type-helpers"; -import { getPokemonSpeciesForm } from "#utils/pokemon-utils"; +import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils"; /** * The type that {@linkcode PokemonSpeciesForm} is converted to when an object containing it serializes it. @@ -173,10 +172,10 @@ export class PokemonSummonData { if (illusionData.fusionSpecies != null) { switch (typeof illusionData.fusionSpecies) { case "object": - illusionData.fusionSpecies = allSpecies[illusionData.fusionSpecies.speciesId]; + illusionData.fusionSpecies = getPokemonSpecies(illusionData.fusionSpecies.speciesId); break; case "number": - illusionData.fusionSpecies = allSpecies[illusionData.fusionSpecies]; + illusionData.fusionSpecies = getPokemonSpecies(illusionData.fusionSpecies); break; default: illusionData.fusionSpecies = undefined; diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index b5786d1f0a2..b6b09d5ba2a 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1011,7 +1011,7 @@ export function getRandomPartyMemberFunc( level, true, strength, - globalScene.currentBattle.waveIndex, + // TODO: What EvoLevelThresholdKind to use here? ); } return globalScene.addEnemyPokemon( @@ -1042,7 +1042,8 @@ function getSpeciesFilterRandomPartyMemberFunc( const species = getPokemonSpecies( globalScene .randomSpecies(waveIndex, level, false, speciesFilter) - .getTrainerSpeciesForLevel(level, true, strength, waveIndex), + // TODO: What EvoLevelThresholdKind to use here? + .getTrainerSpeciesForLevel(level, true, strength), ); return globalScene.addEnemyPokemon(species, level, trainerSlot, undefined, false, undefined, postProcess); diff --git a/src/data/trainers/trainer-party-template.ts b/src/data/trainers/trainer-party-template.ts index 0ad3d36dcfa..450a1260f68 100644 --- a/src/data/trainers/trainer-party-template.ts +++ b/src/data/trainers/trainer-party-template.ts @@ -1,5 +1,6 @@ import { globalScene } from "#app/global-scene"; import { startingWave } from "#app/starting-wave"; +import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { GameModes } from "#enums/game-modes"; import { PartyMemberStrength } from "#enums/party-member-strength"; @@ -9,12 +10,30 @@ export class TrainerPartyTemplate { public strength: PartyMemberStrength; public sameSpecies: boolean; public balanced: boolean; + /** + * Controls which evolution level threshold to use for the trainer. + * Bosses should use `EvoLevelThresholdKind.STRONG`, regular trainers + * should use `EvoLevelThresholdKind.NORMAL`. + * @defaultValue `EvoLevelThresholdKind.NORMAL` + * @see {@link EvoLevelThresholdKind | EvoLevelThresholdKind} + */ + public readonly evoLevelThresholdKind: Exclude; - constructor(size: number, strength: PartyMemberStrength, sameSpecies?: boolean, balanced?: boolean) { + constructor( + size: number, + strength: PartyMemberStrength, + sameSpecies?: boolean, + balanced?: boolean, + evoLevelThresholdKind: Exclude< + EvoLevelThresholdKind, + typeof EvoLevelThresholdKind.WILD + > = EvoLevelThresholdKind.NORMAL, + ) { this.size = size; this.strength = strength; this.sameSpecies = !!sameSpecies; this.balanced = !!balanced; + this.evoLevelThresholdKind = evoLevelThresholdKind; } getStrength(_index: number): PartyMemberStrength { @@ -149,39 +168,39 @@ export const trainerPartyTemplates = { SIX_WEAK_BALANCED: new TrainerPartyTemplate(6, PartyMemberStrength.WEAK, false, true), GYM_LEADER_1: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), ), GYM_LEADER_2: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(1, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), GYM_LEADER_3: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), GYM_LEADER_4: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), GYM_LEADER_5: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(2, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(2, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), ELITE_FOUR: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(3, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(3, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), CHAMPION: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(4, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(2, PartyMemberStrength.STRONGER, false, true), + new TrainerPartyTemplate(4, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(2, PartyMemberStrength.STRONGER, false, true, EvoLevelThresholdKind.STRONG), ), RIVAL: new TrainerPartyCompoundTemplate( diff --git a/src/enums/evo-level-threshold-kind.ts b/src/enums/evo-level-threshold-kind.ts new file mode 100644 index 00000000000..9d9aa0a0165 --- /dev/null +++ b/src/enums/evo-level-threshold-kind.ts @@ -0,0 +1,17 @@ +import type { EvoLevelThreshold } from "#types/species-gen-types"; + +/** + * Enum for the different evolution level threholds based on the encounter type + * @see {@linkcode EvoLevelThreshold} + */ +export const EvoLevelThresholdKind = Object.freeze({ + /** Meant to be used for Gym Leaders and Evil Admins */ + STRONG: 0, + /** Normal trainers and Boss Pokémon */ + NORMAL: 1, + /** Non-boss Pokémon encountered in the wild */ + WILD: 2, +}); + +/** {@inheritdoc EvoLevelThresholdKind} */ +export type EvoLevelThresholdKind = (typeof EvoLevelThresholdKind)[keyof typeof EvoLevelThresholdKind]; diff --git a/src/field/trainer.ts b/src/field/trainer.ts index f5b2e5dad99..c2942739b59 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -421,7 +421,7 @@ export class Trainer extends Phaser.GameObjects.Container { level, false, template.getStrength(offset), - globalScene.currentBattle.waveIndex, + template.evoLevelThresholdKind, ), ) : this.genNewPartyMemberSpecies(level, strength); @@ -429,7 +429,7 @@ export class Trainer extends Phaser.GameObjects.Container { // If the species is from newSpeciesPool, we need to adjust it based on the level and strength if (newSpeciesPool) { species = getPokemonSpecies( - species.getSpeciesForLevel(level, true, true, strength, globalScene.currentBattle.waveIndex), + species.getSpeciesForLevel(level, true, true, strength, template.evoLevelThresholdKind), ); } @@ -480,7 +480,7 @@ export class Trainer extends Phaser.GameObjects.Container { } let ret = getPokemonSpecies( - baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex), + baseSpecies.getTrainerSpeciesForLevel(level, true, strength, template.evoLevelThresholdKind), ); let retry = false; @@ -506,7 +506,7 @@ export class Trainer extends Phaser.GameObjects.Container { let evoAttempt = 0; while (retry && evoAttempt++ < 10) { ret = getPokemonSpecies( - baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex), + baseSpecies.getTrainerSpeciesForLevel(level, true, strength, template.evoLevelThresholdKind), ); console.log(ret.name); if (ret.isOfType(this.config.specialtyType)) { diff --git a/test/ai/ai-moveset-gen.test.ts b/test/ai/ai-moveset-gen.test.ts index faec3856485..1cf33aada1f 100644 --- a/test/ai/ai-moveset-gen.test.ts +++ b/test/ai/ai-moveset-gen.test.ts @@ -11,6 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { EnemyPokemon } from "#field/pokemon"; import { GameManager } from "#test/test-utils/game-manager"; import { NumberHolder } from "#utils/common"; +import { getPokemonSpecies } from "#utils/pokemon-utils"; import { afterEach } from "node:test"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; @@ -50,7 +51,7 @@ function createTestablePokemon( ): EnemyPokemon { const pokemon = new EnemyPokemon(allSpecies[species], level, trainerSlot, boss); if (formIndex !== 0) { - const formIndexLength = allSpecies[species]?.forms.length; + const formIndexLength = getPokemonSpecies(species)?.forms.length; const name = allSpecies[species]?.name; expect(formIndex, `${name} does not have a form with index ${formIndex}`).toBeLessThan(formIndexLength); pokemon.formIndex = formIndex; diff --git a/test/evolution.test.ts b/test/evolution.test.ts index 7079404bdec..80e97820443 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -1,4 +1,4 @@ -import { pokemonEvolutions, SpeciesFormEvolution, SpeciesWildEvolutionDelay } from "#balance/pokemon-evolutions"; +import { pokemonEvolutions } from "#balance/pokemon-evolutions"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; @@ -87,12 +87,6 @@ describe("Evolution", () => { expect(shedinja.metBiome).toBe(-1); }); - it("should set wild delay to NONE by default", () => { - const speciesFormEvo = new SpeciesFormEvolution(SpeciesId.ABRA, null, null, 1000, null, null); - - expect(speciesFormEvo.wildDelay).toBe(SpeciesWildEvolutionDelay.NONE); - }); - it("should increase both HP and max HP when evolving", async () => { game.override .moveset([MoveId.SURF]) diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index a8d91f8f101..6707b7a7667 100644 --- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -1,5 +1,6 @@ import type { BattleScene } from "#app/battle-scene"; import { BiomeId } from "#enums/biome-id"; +import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind"; import { ModifierTier } from "#enums/modifier-tier"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -120,9 +121,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => { ); expect(encounter.enemyPartyConfigs[2].trainerConfig?.partyTemplates[0]).toEqual( new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(3, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(3, PartyMemberStrength.STRONG, undefined, undefined, EvoLevelThresholdKind.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, undefined, undefined, EvoLevelThresholdKind.STRONG), ), ); expect(encounter.spriteConfigs).toBeDefined();