[Balance] Adjust evolution delay requirements (#6595)

* balance: adjust evolution delay requirements

* Remove evo delay test

* Update mystery encounter test matcher

* fix: callsite errors

* Fix desynced edit in ai-species-gen.ts

* Formatting

So the linter stops complaining

* [WIP] Add temporary logging statements and address evolution

* Fix off by one error related to species ID

* Fix issue with pokemon species

* Fix biome outputs generating with NaN for evolution levels

Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com>
This commit is contained in:
Sirz Benjie 2025-09-30 12:59:08 -05:00 committed by GitHub
parent 4a719e48c7
commit 1a0cfaf13d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 558 additions and 415 deletions

View File

@ -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];

207
src/ai/ai-species-gen.ts Normal file
View File

@ -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;
}

View File

@ -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;

View File

@ -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])
]
};

View File

@ -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));
}
}

View File

@ -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<number, SpeciesId> = 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!,
),

View File

@ -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;

View File

@ -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);

View File

@ -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<EvoLevelThresholdKind, typeof EvoLevelThresholdKind.WILD>;
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(

View File

@ -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];

View File

@ -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)) {

View File

@ -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;

View File

@ -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])

View File

@ -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();