mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-15 06:15:20 +01:00
[Balance] Rival Fight Rework (#6603)
* Basic Implementation of new Rival Fights * Set abilities for birds, Set levels for slots 3-5 * Left an accidental TODO * Remove duplicate Paldea Tauros * Allow species in getRandomPartyMemberFunc to be an array * Use switch statement instead of if/else chain * docs: add doc comments * Misc cleanup * Misc cleanup * Implement superior rival fight teamgen * Remove latent console logs from other PR * Fix unrelated typo in pokemon-move-no-pp * Tweak type overlap logic * Fix off-by-one-error for limits * Address Kev's comments from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Fix typo * Fix docs in `rival-party-config.ts` * Add missing default in doc in `rival-team-gen.ts` * Update src/ai/rival-team-gen.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/ai/rival-team-gen.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Address Kev's comments from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: Madmadness65 <blaze.the.fireman@gmail.com> Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
68f65da233
commit
0da202c26e
@ -1,7 +1,6 @@
|
||||
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";
|
||||
@ -37,11 +36,6 @@ function calcEvoChance(ev: SpeciesFormEvolution, level: number, encounterKind: E
|
||||
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;
|
||||
@ -83,14 +77,6 @@ function getRequiredPrevo(
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -127,13 +113,6 @@ export function determineEnemySpecies(
|
||||
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)
|
||||
@ -162,7 +141,6 @@ export function determineEnemySpecies(
|
||||
}
|
||||
}
|
||||
if (evoPool.length === 0) {
|
||||
console.log("%c No evolutions available, returning base species", "color: blue");
|
||||
return species.speciesId;
|
||||
}
|
||||
const [choice, evoSpecies] = randSeedItem(evoPool);
|
||||
@ -184,14 +162,7 @@ export function determineEnemySpecies(
|
||||
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),
|
||||
|
||||
331
src/ai/rival-team-gen.ts
Normal file
331
src/ai/rival-team-gen.ts
Normal file
@ -0,0 +1,331 @@
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { PokemonSpecies } from "#data/pokemon-species";
|
||||
import { getTypeDamageMultiplier } from "#data/type";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { ChallengeType } from "#enums/challenge-type";
|
||||
import type { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import { TrainerSlot } from "#enums/trainer-slot";
|
||||
import type { EnemyPokemon } from "#field/pokemon";
|
||||
import { RIVAL_6_POOL, type RivalPoolConfig } from "#trainers/rival-party-config";
|
||||
import { applyChallenges } from "#utils/challenge-utils";
|
||||
import { NumberHolder, randSeedItem } from "#utils/common";
|
||||
import { getEnumValues } from "#utils/enums";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
|
||||
/**
|
||||
* The maximum number of shared weaknesses to tolerate when balancing weakness
|
||||
*
|
||||
* @remarks
|
||||
* When generating a slot that has weakness balancing enabled, the pool will
|
||||
* exclude any species that would cause a type to be a weakness for more than
|
||||
* this number of party members.
|
||||
* Note that it is assumed that slot 0 is always going to Terastallize to its primary type,
|
||||
* so slot 0's secondary type is excluded from weakness calculations.
|
||||
*/
|
||||
const MAX_SHARED_WEAKNESSES = 2;
|
||||
/**
|
||||
* The maximum number of shared types to tolerate when balancing types
|
||||
*
|
||||
* @remarks
|
||||
* When generating a slot that has type balancing enabled, the pool will
|
||||
* exclude any species that would cause a type to be present in more than
|
||||
* this number of party members.
|
||||
*/
|
||||
const MAX_SHARED_TYPES = 1;
|
||||
|
||||
/** Record of the chosen indices in the rival species pool, for type balancing based on the final fight */
|
||||
const CHOSEN_RIVAL_ROLLS: (undefined | [number] | [number, number])[] = new Array(6);
|
||||
|
||||
/**
|
||||
* Return the species from the rival species pool based on a previously chosen roll
|
||||
* @param param0 - The chosen rolls for the rival species pool
|
||||
* @param pool - The rival species pool for the slot
|
||||
* @returns - A `SpeciesId` if found, or `undefined` if no species exists at the chosen roll
|
||||
*/
|
||||
function rivalRollToSpecies(
|
||||
[roll1, roll2]: [number] | [number, number],
|
||||
pool: readonly (SpeciesId | readonly SpeciesId[])[],
|
||||
): SpeciesId | undefined {
|
||||
const pull1 = pool[roll1];
|
||||
if (typeof pull1 === "number") {
|
||||
return pull1;
|
||||
}
|
||||
if (roll2 == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return pull1[roll2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the types that the given species is weak to
|
||||
*
|
||||
* @remarks
|
||||
* - Considers Levitate as a guaranteed ability if the species has it as all 3 possible abilities.
|
||||
* - Accounts for type effectiveness challenges via {@linkcode applyChallenges}
|
||||
*
|
||||
* @privateRemarks
|
||||
* Despite potentially being a useful utility method, this is intentionally *not*
|
||||
* exported because it uses logic specific to this file, such as excluding the second type for Tera starters
|
||||
*
|
||||
* @param species - The species to calculate weaknesses for
|
||||
* @param exclude2ndType - (Default `false`) Whether to exclude the second type when calculating weaknesses
|
||||
* Intended to be used for starters since they will terastallize to their primary type.
|
||||
* @returns The set of types that the species is weak to
|
||||
*/
|
||||
function getWeakTypes(species: PokemonSpecies, exclude2ndType = false): Set<PokemonType> {
|
||||
const weaknesses = new Set<PokemonType>();
|
||||
// If the species is always immune to ground, skip ground type checks
|
||||
// Note that there are no other Pokémon with guaranteed immunities due to all 3 of their abilities providing
|
||||
// an immunity.
|
||||
// At this point, we do not have an ability to know which ability the Pokémon generated with, so we can only
|
||||
// work with guaranteed immunities.
|
||||
const groundImmunityAbilities: readonly AbilityId[] = [AbilityId.LEVITATE, AbilityId.EARTH_EATER];
|
||||
const isAlwaysGroundImmune =
|
||||
groundImmunityAbilities.includes(species.ability1)
|
||||
&& (species.ability2 == null || groundImmunityAbilities.includes(species.ability2))
|
||||
&& (species.abilityHidden == null || groundImmunityAbilities.includes(species.ability2));
|
||||
for (const ty of getEnumValues(PokemonType)) {
|
||||
if (
|
||||
ty === PokemonType.UNKNOWN
|
||||
|| ty === PokemonType.STELLAR
|
||||
|| (ty === PokemonType.GROUND && isAlwaysGroundImmune)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const multiplier = new NumberHolder(getTypeDamageMultiplier(ty, species.type1));
|
||||
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||
|
||||
if (multiplier.value >= 2 && !exclude2ndType) {
|
||||
const type2 = species.type2;
|
||||
if (type2 != null) {
|
||||
const multiplier2 = new NumberHolder(getTypeDamageMultiplier(ty, type2));
|
||||
applyChallenges(ChallengeType.TYPE_EFFECTIVENESS, multiplier2);
|
||||
multiplier.value *= multiplier2.value;
|
||||
}
|
||||
}
|
||||
if (multiplier.value >= 2) {
|
||||
weaknesses.add(ty);
|
||||
}
|
||||
}
|
||||
|
||||
return weaknesses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the existing types and weaknesses in the party up to the target slot
|
||||
* @remarks
|
||||
* At least one of either `balanceTypes` or `balanceWeaknesses` should be `true`,
|
||||
* otherwise the function does nothing.
|
||||
* @param targetSlot - The slot we are calculating up to (exclusive)
|
||||
* @param pokemonTypes - A map that will hold the types present in the party
|
||||
* @param pokemonWeaknesses - A map that will hold the weaknesses present in the party, and their counts
|
||||
* @param balanceTypes - (Default `false`) Whether to include type balancing
|
||||
* @param balanceWeaknesses - (Default `false`) Whether to attempt to add the party's existing weaknesses for the purpose of weakness balancing.
|
||||
* @param referenceConfig - (Default {@linkcode RIVAL_6_POOL}); The reference rival pool configuration to use for type considerations
|
||||
*
|
||||
* @see {@linkcode MAX_SHARED_WEAKNESSES}
|
||||
*/
|
||||
function calcPartyTypings(
|
||||
targetSlot: number,
|
||||
pokemonTypes: Map<PokemonType, number>,
|
||||
pokemonWeaknesses: Map<PokemonType, number>,
|
||||
balanceTypes = false,
|
||||
balanceWeaknesses = false,
|
||||
referenceConfig: RivalPoolConfig = RIVAL_6_POOL,
|
||||
): void {
|
||||
if (!balanceTypes && !balanceWeaknesses) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < targetSlot; i++) {
|
||||
const chosenRoll = CHOSEN_RIVAL_ROLLS[i];
|
||||
const refConfig = referenceConfig[i];
|
||||
// In case pokemon are somehow generating out of order, break early
|
||||
if (chosenRoll == null || refConfig == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the species from the roll
|
||||
const refSpecies = rivalRollToSpecies(chosenRoll, refConfig.pool);
|
||||
if (refSpecies == null) {
|
||||
continue;
|
||||
}
|
||||
const refPokeSpecies = getPokemonSpecies(refSpecies);
|
||||
const type1 = refPokeSpecies.type1;
|
||||
const type2 = refPokeSpecies.type2;
|
||||
if (balanceTypes) {
|
||||
pokemonTypes.set(type1, (pokemonTypes.get(type1) ?? 0) + 1);
|
||||
if (type2 != null) {
|
||||
pokemonTypes.set(type2, (pokemonTypes.get(type2) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
if (balanceWeaknesses) {
|
||||
for (const weakType of getWeakTypes(refPokeSpecies)) {
|
||||
pokemonWeaknesses.set(weakType, (pokemonWeaknesses.get(weakType) ?? 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the species can be added to the party without violating type or weakness constraints
|
||||
* @param species - The species to check
|
||||
* @param existingTypes - The existing types in the party
|
||||
* @param existingWeaknesses - The existing weaknesses in the party
|
||||
* @param balanceTypes - (Default `false`) Whether to include type balancing
|
||||
* @param balanceWeaknesses - (Default `false`) Whether to include weakness balancing
|
||||
* @returns Whether the species meets the constraints
|
||||
*/
|
||||
function checkTypingConstraints(
|
||||
species: SpeciesId,
|
||||
existingTypes: ReadonlyMap<PokemonType, number>,
|
||||
existingWeaknesses: ReadonlyMap<PokemonType, number>,
|
||||
balanceTypes = false,
|
||||
balanceWeaknesses = false,
|
||||
): boolean {
|
||||
if (!balanceTypes && !balanceWeaknesses) {
|
||||
return true;
|
||||
}
|
||||
const { type1, type2 } = getPokemonSpecies(species);
|
||||
|
||||
if (
|
||||
balanceTypes
|
||||
&& ((existingTypes.get(type1) ?? 0) >= MAX_SHARED_TYPES
|
||||
|| (type2 != null && (existingTypes.get(type2) ?? 0) >= MAX_SHARED_TYPES))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (balanceWeaknesses) {
|
||||
const weaknesses = getWeakTypes(getPokemonSpecies(species));
|
||||
for (const weakType of weaknesses) {
|
||||
if ((existingWeaknesses.get(weakType) ?? 0) >= MAX_SHARED_WEAKNESSES) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a species pool to a list of choices after filtering by type and weakness constraints
|
||||
* @param pool - The pool to convert to choices
|
||||
* @param existingTypes - The existing types in the party
|
||||
* @param existingWeaknesses - The existing weaknesses in the party
|
||||
* @param balanceTypes - (Default `false`) Whether to include type balancing
|
||||
* @param balanceWeaknesses - (Default `false`) Whether to include weakness balancing
|
||||
* @returns A list of choices, where each choice is either a single index or a tuple of indices for sub-pools
|
||||
*/
|
||||
function convertPoolToChoices(
|
||||
pool: readonly (SpeciesId | readonly SpeciesId[])[],
|
||||
existingTypes: ReadonlyMap<PokemonType, number>,
|
||||
existingWeaknesses: ReadonlyMap<PokemonType, number>,
|
||||
balanceTypes = false,
|
||||
balanceWeaknesses = false,
|
||||
): (number | [number, number])[] {
|
||||
const choices: (number | [number, number])[] = [];
|
||||
|
||||
if (balanceTypes || balanceWeaknesses) {
|
||||
for (const [i, entry] of pool.entries()) {
|
||||
// Determine if there is a type overlap
|
||||
if (
|
||||
typeof entry === "number"
|
||||
&& checkTypingConstraints(entry, existingTypes, existingWeaknesses, balanceTypes, balanceWeaknesses)
|
||||
) {
|
||||
choices.push(i);
|
||||
} else if (typeof entry !== "number") {
|
||||
for (const [j, subEntry] of entry.entries()) {
|
||||
if (checkTypingConstraints(subEntry, existingTypes, existingWeaknesses, balanceTypes, balanceWeaknesses)) {
|
||||
choices.push([i, j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (choices.length === 0) {
|
||||
for (const [i, entry] of pool.entries()) {
|
||||
if (typeof entry === "number") {
|
||||
choices.push(i);
|
||||
} else {
|
||||
for (const j of entry.keys()) {
|
||||
choices.push([i, j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
|
||||
* Then adds Pokemon to `globalScene`.
|
||||
* @param config - The configuration for the rival pool fight
|
||||
* @param slot - The slot being generated for (0-5)
|
||||
* @param referenceConfig - (Default {@linkcode RIVAL_6_POOL}); The final rival pool configuration to use if `config` is `RIVAL_POOL_CONFIG.FINAL`
|
||||
*
|
||||
* @throws
|
||||
* If no configuration is found for the specified slot.
|
||||
*/
|
||||
export function getRandomRivalPartyMemberFunc(
|
||||
config: RivalPoolConfig,
|
||||
slot: number,
|
||||
referenceConfig: RivalPoolConfig = RIVAL_6_POOL,
|
||||
): (level: number, strength: PartyMemberStrength) => EnemyPokemon {
|
||||
// Protect against out of range slots.
|
||||
// Only care about this in dev to be caught during development; it will be excluded in production builds.
|
||||
if (import.meta.env.DEV && slot > config.length) {
|
||||
throw new Error(`Slot ${slot} is out of range for the provided config of length ${config.length}`);
|
||||
}
|
||||
return (level: number, _strength: PartyMemberStrength) => {
|
||||
const { pool, postProcess, balanceTypes, balanceWeaknesses } = config[slot];
|
||||
|
||||
const existingTypes = new Map<PokemonType, number>();
|
||||
const existingWeaknesses = new Map<PokemonType, number>();
|
||||
|
||||
if (slot === 0) {
|
||||
// Clear out the rolls from previous rival generations
|
||||
CHOSEN_RIVAL_ROLLS.fill(undefined);
|
||||
} else if (balanceTypes || balanceWeaknesses) {
|
||||
calcPartyTypings(slot, existingTypes, existingWeaknesses, balanceTypes, balanceWeaknesses, referenceConfig);
|
||||
}
|
||||
|
||||
// Filter the pool to its choices, or map it
|
||||
|
||||
let species: SpeciesId | SpeciesId[];
|
||||
|
||||
// When converting pool to choices, base off of the reference config
|
||||
// to use for type balancing, as we only narrow based on what the slot
|
||||
// will be in its final stage
|
||||
const choices = convertPoolToChoices(
|
||||
referenceConfig[slot].pool,
|
||||
existingTypes,
|
||||
existingWeaknesses,
|
||||
balanceTypes,
|
||||
balanceWeaknesses,
|
||||
);
|
||||
|
||||
const choice = randSeedItem(choices);
|
||||
if (typeof choice === "number") {
|
||||
species = pool[choice] as SpeciesId;
|
||||
CHOSEN_RIVAL_ROLLS[slot] = [choice];
|
||||
} else {
|
||||
species = pool[choice[0]][choice[1]];
|
||||
CHOSEN_RIVAL_ROLLS[slot] = choice;
|
||||
}
|
||||
|
||||
return globalScene.addEnemyPokemon(
|
||||
getPokemonSpecies(species),
|
||||
level,
|
||||
TrainerSlot.TRAINER,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
postProcess,
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -59,7 +59,7 @@ export class PokemonMove {
|
||||
}
|
||||
|
||||
if (!ignorePp && move.pp !== -1 && this.ppUsed >= this.getMovePp()) {
|
||||
return [false, i18next.t("battle:moveNoPP", { moveName: move.name })];
|
||||
return [false, i18next.t("battle:moveNoPp", { moveName: move.name })];
|
||||
}
|
||||
|
||||
if (forSelection) {
|
||||
|
||||
692
src/data/trainers/rival-party-config.ts
Normal file
692
src/data/trainers/rival-party-config.ts
Normal file
@ -0,0 +1,692 @@
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type { EnemyPokemon } from "#field/pokemon";
|
||||
import { randSeedIntRange, randSeedItem } from "#utils/common";
|
||||
|
||||
//#region constants
|
||||
|
||||
// Levels for slots 1 and 2 do not need post-processing logic
|
||||
|
||||
// Fight 1 doesn't have slot 3
|
||||
const SLOT_3_FIGHT_2_LEVEL = 16;
|
||||
const SLOT_3_FIGHT_3_LEVEL = 36;
|
||||
const SLOT_3_FIGHT_4_LEVEL = 71;
|
||||
const SLOT_3_FIGHT_5_LEVEL = 125;
|
||||
const SLOT_3_FIGHT_6_LEVEL = 189;
|
||||
|
||||
// Fights 1 and 2 don't have slot 4
|
||||
const SLOT_4_FIGHT_3_LEVEL = 38;
|
||||
const SLOT_4_FIGHT_4_LEVEL = 71;
|
||||
const SLOT_4_FIGHT_5_LEVEL = 125;
|
||||
const SLOT_4_FIGHT_6_LEVEL = 189;
|
||||
|
||||
// Fights 1-3 don't have slot 5
|
||||
const SLOT_5_FIGHT_4_LEVEL = 69;
|
||||
const SLOT_5_FIGHT_5_LEVEL = 127;
|
||||
const SLOT_5_FIGHT_6_LEVEL = 189;
|
||||
|
||||
// Fights 1-4 don't have slot 6
|
||||
const SLOT_6_FIGHT_5_LEVEL = 129;
|
||||
const SLOT_6_FIGHT_6_LEVEL = 200;
|
||||
|
||||
//#endregion constants
|
||||
|
||||
//#region Slot 1
|
||||
|
||||
/**
|
||||
* Set the abiltiy index to 0 and the tera type to the primary type
|
||||
*
|
||||
* @param pokemon - The pokemon to force traits for
|
||||
* @param bars - (default `0`) The number of boss bar segments to set. If `zero`, the pokemon will not be a boss
|
||||
*/
|
||||
|
||||
function forceRivalStarterTraits(pokemon: EnemyPokemon, bars = 0): void {
|
||||
pokemon.abilityIndex = 0;
|
||||
pokemon.teraType = pokemon.species.type1;
|
||||
if (bars > 0) {
|
||||
pokemon.setBoss(true, bars);
|
||||
pokemon.generateAndPopulateMoveset();
|
||||
}
|
||||
}
|
||||
|
||||
/** Rival's slot 1 species pool for fight 1 */
|
||||
const SLOT_1_FIGHT_1 = [
|
||||
SpeciesId.BULBASAUR,
|
||||
SpeciesId.CHARMANDER,
|
||||
SpeciesId.SQUIRTLE,
|
||||
SpeciesId.CHIKORITA,
|
||||
SpeciesId.CYNDAQUIL,
|
||||
SpeciesId.TOTODILE,
|
||||
SpeciesId.TREECKO,
|
||||
SpeciesId.TORCHIC,
|
||||
SpeciesId.MUDKIP,
|
||||
SpeciesId.TURTWIG,
|
||||
SpeciesId.CHIMCHAR,
|
||||
SpeciesId.PIPLUP,
|
||||
SpeciesId.SNIVY,
|
||||
SpeciesId.TEPIG,
|
||||
SpeciesId.OSHAWOTT,
|
||||
SpeciesId.CHESPIN,
|
||||
SpeciesId.FENNEKIN,
|
||||
SpeciesId.FROAKIE,
|
||||
SpeciesId.ROWLET,
|
||||
SpeciesId.LITTEN,
|
||||
SpeciesId.POPPLIO,
|
||||
SpeciesId.GROOKEY,
|
||||
SpeciesId.SCORBUNNY,
|
||||
SpeciesId.SOBBLE,
|
||||
SpeciesId.SPRIGATITO,
|
||||
SpeciesId.FUECOCO,
|
||||
SpeciesId.QUAXLY,
|
||||
];
|
||||
|
||||
/** Rival's slot 1 species pool for fight 2 */
|
||||
const SLOT_1_FIGHT_2 = [
|
||||
SpeciesId.IVYSAUR,
|
||||
SpeciesId.CHARMELEON,
|
||||
SpeciesId.WARTORTLE,
|
||||
SpeciesId.BAYLEEF,
|
||||
SpeciesId.QUILAVA,
|
||||
SpeciesId.CROCONAW,
|
||||
SpeciesId.GROVYLE,
|
||||
SpeciesId.COMBUSKEN,
|
||||
SpeciesId.MARSHTOMP,
|
||||
SpeciesId.GROTLE,
|
||||
SpeciesId.MONFERNO,
|
||||
SpeciesId.PRINPLUP,
|
||||
SpeciesId.SERVINE,
|
||||
SpeciesId.PIGNITE,
|
||||
SpeciesId.DEWOTT,
|
||||
SpeciesId.QUILLADIN,
|
||||
SpeciesId.BRAIXEN,
|
||||
SpeciesId.FROGADIER,
|
||||
SpeciesId.DARTRIX,
|
||||
SpeciesId.TORRACAT,
|
||||
SpeciesId.BRIONNE,
|
||||
SpeciesId.THWACKEY,
|
||||
SpeciesId.RABOOT,
|
||||
SpeciesId.DRIZZILE,
|
||||
SpeciesId.FLORAGATO,
|
||||
SpeciesId.CROCALOR,
|
||||
SpeciesId.QUAXWELL,
|
||||
];
|
||||
|
||||
/** Rival's slot 1 species pool for fight 3 and beyond */
|
||||
const SLOT_1_FINAL = [
|
||||
SpeciesId.VENUSAUR,
|
||||
SpeciesId.CHARIZARD,
|
||||
SpeciesId.BLASTOISE,
|
||||
SpeciesId.MEGANIUM,
|
||||
SpeciesId.TYPHLOSION,
|
||||
SpeciesId.FERALIGATR,
|
||||
SpeciesId.SCEPTILE,
|
||||
SpeciesId.BLAZIKEN,
|
||||
SpeciesId.SWAMPERT,
|
||||
SpeciesId.TORTERRA,
|
||||
SpeciesId.INFERNAPE,
|
||||
SpeciesId.EMPOLEON,
|
||||
SpeciesId.SERPERIOR,
|
||||
SpeciesId.EMBOAR,
|
||||
SpeciesId.SAMUROTT,
|
||||
SpeciesId.CHESNAUGHT,
|
||||
SpeciesId.DELPHOX,
|
||||
SpeciesId.GRENINJA,
|
||||
SpeciesId.DECIDUEYE,
|
||||
SpeciesId.INCINEROAR,
|
||||
SpeciesId.PRIMARINA,
|
||||
SpeciesId.RILLABOOM,
|
||||
SpeciesId.CINDERACE,
|
||||
SpeciesId.INTELEON,
|
||||
SpeciesId.MEOWSCARADA,
|
||||
SpeciesId.SKELEDIRGE,
|
||||
SpeciesId.QUAQUAVAL,
|
||||
];
|
||||
//#endregion slot 1
|
||||
|
||||
//#region Slot 2
|
||||
/**
|
||||
* Post-process rival birds to override their sets
|
||||
*
|
||||
* @remarks
|
||||
* Currently used to force ability indices
|
||||
*
|
||||
* @param pokemon - The rival bird pokemon to force an ability for
|
||||
* @param bars - (default `0`) The number of boss bar segments to set. If `zero`, the pokemon will not be a boss
|
||||
*/
|
||||
|
||||
function forceRivalBirdAbility(pokemon: EnemyPokemon, bars = 0): void {
|
||||
switch (pokemon.species.speciesId) {
|
||||
// Guts for Tailow line
|
||||
case SpeciesId.TAILLOW:
|
||||
case SpeciesId.SWELLOW:
|
||||
// Intimidate for Starly line
|
||||
case SpeciesId.STARLY:
|
||||
case SpeciesId.STARAVIA:
|
||||
case SpeciesId.STARAPTOR: {
|
||||
pokemon.abilityIndex = 0;
|
||||
break;
|
||||
}
|
||||
// Tangled Feet for Pidgey line
|
||||
case SpeciesId.PIDGEY:
|
||||
case SpeciesId.PIDGEOTTO:
|
||||
case SpeciesId.PIDGEOT:
|
||||
// Super Luck for pidove line
|
||||
case SpeciesId.PIDOVE:
|
||||
case SpeciesId.TRANQUILL:
|
||||
case SpeciesId.UNFEZANT:
|
||||
// Volt Absorb for Wattrel line
|
||||
case SpeciesId.WATTREL:
|
||||
case SpeciesId.KILOWATTREL: {
|
||||
pokemon.abilityIndex = 1;
|
||||
break;
|
||||
}
|
||||
// Tinted lens for Hoothoot line
|
||||
case SpeciesId.HOOTHOOT:
|
||||
case SpeciesId.NOCTOWL:
|
||||
// Skill link for Pikipek line
|
||||
case SpeciesId.PIKIPEK:
|
||||
case SpeciesId.TRUMBEAK:
|
||||
case SpeciesId.TOUCANNON:
|
||||
// Gale Wings for Fletchling line
|
||||
case SpeciesId.FLETCHLING:
|
||||
case SpeciesId.FLETCHINDER:
|
||||
case SpeciesId.TALONFLAME: {
|
||||
pokemon.abilityIndex = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bars > 0) {
|
||||
pokemon.setBoss(true, bars);
|
||||
pokemon.generateAndPopulateMoveset();
|
||||
}
|
||||
}
|
||||
/** Rival's slot 2 species pool for fight 1 */
|
||||
const SLOT_2_FIGHT_1 = [
|
||||
SpeciesId.PIDGEY,
|
||||
SpeciesId.HOOTHOOT,
|
||||
SpeciesId.TAILLOW,
|
||||
SpeciesId.STARLY,
|
||||
SpeciesId.PIDOVE,
|
||||
SpeciesId.FLETCHLING,
|
||||
SpeciesId.PIKIPEK,
|
||||
SpeciesId.ROOKIDEE,
|
||||
SpeciesId.WATTREL,
|
||||
];
|
||||
|
||||
/** Rival's slot 2 species pool for fight 2 */
|
||||
const SLOT_2_FIGHT_2 = [
|
||||
SpeciesId.PIDGEOTTO,
|
||||
SpeciesId.HOOTHOOT,
|
||||
SpeciesId.TAILLOW,
|
||||
SpeciesId.STARAVIA,
|
||||
SpeciesId.TRANQUILL,
|
||||
SpeciesId.FLETCHINDER,
|
||||
SpeciesId.TRUMBEAK,
|
||||
SpeciesId.CORVISQUIRE,
|
||||
SpeciesId.WATTREL,
|
||||
];
|
||||
|
||||
/** Rival's slot 2 species pool for fight 3 and beyond */
|
||||
const SLOT_2_FINAL = [
|
||||
SpeciesId.PIDGEOT,
|
||||
SpeciesId.NOCTOWL,
|
||||
SpeciesId.SWELLOW,
|
||||
SpeciesId.STARAPTOR,
|
||||
SpeciesId.UNFEZANT,
|
||||
SpeciesId.TALONFLAME,
|
||||
SpeciesId.TOUCANNON,
|
||||
SpeciesId.CORVIKNIGHT,
|
||||
SpeciesId.KILOWATTREL,
|
||||
];
|
||||
//#endregion Slot 2
|
||||
|
||||
//#region Slot 3
|
||||
/** Rival's slot 3 species pool for fight 2 */
|
||||
const SLOT_3_FIGHT_2 = [
|
||||
SpeciesId.NIDORINA,
|
||||
SpeciesId.NIDORINO,
|
||||
SpeciesId.MANKEY,
|
||||
SpeciesId.GROWLITHE,
|
||||
SpeciesId.ABRA,
|
||||
SpeciesId.MACHOP,
|
||||
SpeciesId.GASTLY,
|
||||
SpeciesId.MAGNEMITE,
|
||||
SpeciesId.RHYDON,
|
||||
SpeciesId.TANGELA,
|
||||
SpeciesId.PORYGON,
|
||||
SpeciesId.ELEKID,
|
||||
SpeciesId.MAGBY,
|
||||
SpeciesId.MARILL,
|
||||
SpeciesId.TEDDIURSA,
|
||||
SpeciesId.SWINUB,
|
||||
SpeciesId.SLAKOTH,
|
||||
SpeciesId.ARON,
|
||||
SpeciesId.SPHEAL,
|
||||
SpeciesId.FEEBAS,
|
||||
SpeciesId.MUNCHLAX,
|
||||
SpeciesId.ROGGENROLA,
|
||||
SpeciesId.TIMBURR,
|
||||
SpeciesId.TYMPOLE,
|
||||
SpeciesId.SANDILE,
|
||||
SpeciesId.YAMASK,
|
||||
SpeciesId.SOLOSIS,
|
||||
SpeciesId.VANILLITE,
|
||||
SpeciesId.TYMPOLE,
|
||||
SpeciesId.LITWICK,
|
||||
SpeciesId.MUDBRAY,
|
||||
SpeciesId.DEWPIDER,
|
||||
SpeciesId.WIMPOD,
|
||||
SpeciesId.HATENNA,
|
||||
SpeciesId.IMPIDIMP,
|
||||
SpeciesId.SMOLIV,
|
||||
SpeciesId.NACLI,
|
||||
[SpeciesId.CHARCADET, SpeciesId.CHARCADET],
|
||||
SpeciesId.TINKATINK,
|
||||
SpeciesId.GLIMMET,
|
||||
];
|
||||
|
||||
/** Rival's slot 3 species pool for fight 3 */
|
||||
const SLOT_3_FIGHT_3 = [
|
||||
SpeciesId.NIDOQUEEN,
|
||||
SpeciesId.NIDOKING,
|
||||
SpeciesId.PRIMEAPE,
|
||||
SpeciesId.ARCANINE,
|
||||
SpeciesId.KADABRA,
|
||||
SpeciesId.MACHOKE,
|
||||
SpeciesId.HAUNTER,
|
||||
SpeciesId.MAGNETON,
|
||||
SpeciesId.RHYDON,
|
||||
SpeciesId.TANGROWTH,
|
||||
SpeciesId.PORYGON2,
|
||||
SpeciesId.ELECTIVIRE,
|
||||
SpeciesId.MAGMAR,
|
||||
SpeciesId.AZUMARILL,
|
||||
SpeciesId.URSARING,
|
||||
SpeciesId.PILOSWINE,
|
||||
SpeciesId.VIGOROTH,
|
||||
SpeciesId.LAIRON,
|
||||
SpeciesId.SEALEO,
|
||||
SpeciesId.MILOTIC,
|
||||
SpeciesId.SNORLAX,
|
||||
SpeciesId.BOLDORE,
|
||||
SpeciesId.GURDURR,
|
||||
SpeciesId.PALPITOAD,
|
||||
SpeciesId.KROKOROK,
|
||||
SpeciesId.COFAGRIGUS,
|
||||
SpeciesId.DUOSION,
|
||||
SpeciesId.VANILLISH,
|
||||
SpeciesId.EELEKTRIK,
|
||||
SpeciesId.LAMPENT,
|
||||
SpeciesId.MUDSDALE,
|
||||
SpeciesId.ARAQUANID,
|
||||
SpeciesId.GOLISOPOD,
|
||||
SpeciesId.HATTREM,
|
||||
SpeciesId.MORGREM,
|
||||
SpeciesId.DOLLIV,
|
||||
SpeciesId.NACLSTACK,
|
||||
[SpeciesId.ARMAROUGE, SpeciesId.CERULEDGE],
|
||||
SpeciesId.TINKATUFF,
|
||||
SpeciesId.GLIMMORA,
|
||||
];
|
||||
|
||||
/** Rival's slot 3 species pool for fight 4 and beyond */
|
||||
const SLOT_3_FINAL = [
|
||||
SpeciesId.NIDOQUEEN,
|
||||
SpeciesId.NIDOKING,
|
||||
SpeciesId.ANNIHILAPE,
|
||||
SpeciesId.ARCANINE,
|
||||
SpeciesId.ALAKAZAM,
|
||||
SpeciesId.MACHAMP,
|
||||
SpeciesId.GENGAR,
|
||||
SpeciesId.MAGNEZONE,
|
||||
SpeciesId.RHYPERIOR,
|
||||
SpeciesId.TANGROWTH,
|
||||
SpeciesId.PORYGON_Z,
|
||||
SpeciesId.ELECTIVIRE,
|
||||
SpeciesId.MAGMORTAR,
|
||||
SpeciesId.AZUMARILL,
|
||||
SpeciesId.URSALUNA,
|
||||
SpeciesId.MAMOSWINE,
|
||||
SpeciesId.SLAKING,
|
||||
SpeciesId.AGGRON,
|
||||
SpeciesId.WALREIN,
|
||||
SpeciesId.MILOTIC,
|
||||
SpeciesId.SNORLAX,
|
||||
SpeciesId.GIGALITH,
|
||||
SpeciesId.CONKELDURR,
|
||||
SpeciesId.SEISMITOAD,
|
||||
SpeciesId.KROOKODILE,
|
||||
SpeciesId.COFAGRIGUS,
|
||||
SpeciesId.REUNICLUS,
|
||||
SpeciesId.VANILLUXE,
|
||||
SpeciesId.EELEKTROSS,
|
||||
SpeciesId.CHANDELURE,
|
||||
SpeciesId.MUDSDALE,
|
||||
SpeciesId.ARAQUANID,
|
||||
SpeciesId.GOLISOPOD,
|
||||
SpeciesId.HATTERENE,
|
||||
SpeciesId.GRIMMSNARL,
|
||||
SpeciesId.ARBOLIVA,
|
||||
SpeciesId.GARGANACL,
|
||||
[SpeciesId.ARMAROUGE, SpeciesId.CERULEDGE],
|
||||
SpeciesId.TINKATON,
|
||||
SpeciesId.GLIMMORA,
|
||||
];
|
||||
//#endregion Slot 3
|
||||
|
||||
//#region Slot 4
|
||||
|
||||
/**
|
||||
* Post-process logic for rival slot 4, fight 4
|
||||
* @remarks
|
||||
* Set level to 38 and specific forms for certain species
|
||||
* @param pokemon - The pokemon to post-process
|
||||
*/
|
||||
function postProcessSlot4Fight3(pokemon: EnemyPokemon): void {
|
||||
pokemon.level = SLOT_4_FIGHT_3_LEVEL;
|
||||
switch (pokemon.species.speciesId) {
|
||||
case SpeciesId.BASCULIN:
|
||||
pokemon.formIndex = 2; // White
|
||||
return;
|
||||
case SpeciesId.ROTOM:
|
||||
// Heat, Wash, Mow
|
||||
pokemon.formIndex = randSeedItem([1, 2, 5]);
|
||||
return;
|
||||
case SpeciesId.PALDEA_TAUROS:
|
||||
pokemon.formIndex = randSeedIntRange(1, 2); // Blaze, Aqua
|
||||
return;
|
||||
}
|
||||
}
|
||||
/** Rival's slot 4 species pool for fight 3 */
|
||||
const SLOT_4_FIGHT_3 = [
|
||||
SpeciesId.CLEFABLE,
|
||||
[SpeciesId.SLOWBRO, SpeciesId.SLOWKING],
|
||||
SpeciesId.PINSIR,
|
||||
SpeciesId.LAPRAS,
|
||||
SpeciesId.SCIZOR,
|
||||
SpeciesId.HERACROSS,
|
||||
SpeciesId.SNEASEL,
|
||||
SpeciesId.GARDEVOIR,
|
||||
SpeciesId.ROSERADE,
|
||||
SpeciesId.SPIRITOMB,
|
||||
SpeciesId.LUCARIO,
|
||||
SpeciesId.DRAPION,
|
||||
SpeciesId.GALLADE,
|
||||
SpeciesId.ROTOM,
|
||||
SpeciesId.EXCADRILL,
|
||||
[SpeciesId.ZOROARK, SpeciesId.HISUI_ZOROARK],
|
||||
SpeciesId.FERROTHORN,
|
||||
SpeciesId.DURANT,
|
||||
SpeciesId.FLORGES,
|
||||
SpeciesId.DOUBLADE,
|
||||
SpeciesId.VIKAVOLT,
|
||||
SpeciesId.MIMIKYU,
|
||||
SpeciesId.DHELMISE,
|
||||
SpeciesId.POLTEAGEIST,
|
||||
SpeciesId.COPPERAJAH,
|
||||
SpeciesId.KLEAVOR,
|
||||
SpeciesId.BASCULIN,
|
||||
SpeciesId.HISUI_SNEASEL,
|
||||
SpeciesId.HISUI_QWILFISH,
|
||||
SpeciesId.PAWMOT,
|
||||
SpeciesId.CETITAN,
|
||||
SpeciesId.DONDOZO,
|
||||
SpeciesId.DUDUNSPARCE,
|
||||
SpeciesId.GHOLDENGO,
|
||||
SpeciesId.POLTCHAGEIST,
|
||||
[SpeciesId.GALAR_SLOWBRO, SpeciesId.GALAR_SLOWKING],
|
||||
SpeciesId.HISUI_ARCANINE,
|
||||
SpeciesId.PALDEA_TAUROS,
|
||||
];
|
||||
|
||||
/**
|
||||
* Set level and and specific forms for the species in slot 4
|
||||
* @param pokemon - The pokemon to post-process
|
||||
* @param level - (default {@linkcode SLOT_4_FIGHT_4_LEVEL}) The level to set the pokemon to
|
||||
*/
|
||||
function postProcessSlot4Fight4(pokemon: EnemyPokemon, level = SLOT_4_FIGHT_4_LEVEL): void {
|
||||
pokemon.level = level;
|
||||
switch (pokemon.species.speciesId) {
|
||||
case SpeciesId.BASCULEGION:
|
||||
pokemon.formIndex = randSeedIntRange(0, 1);
|
||||
return;
|
||||
case SpeciesId.ROTOM:
|
||||
// Heat, Wash, Mow
|
||||
pokemon.formIndex = randSeedItem([1, 2, 5]);
|
||||
return;
|
||||
case SpeciesId.PALDEA_TAUROS:
|
||||
pokemon.formIndex = randSeedIntRange(1, 2); // Blaze, Aqua
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/** Rival's slot 4 species pool for fight 4 and beyond */
|
||||
const SLOT_4_FINAL = [
|
||||
SpeciesId.CLEFABLE,
|
||||
[SpeciesId.SLOWBRO, SpeciesId.SLOWKING],
|
||||
SpeciesId.PINSIR,
|
||||
SpeciesId.LAPRAS,
|
||||
SpeciesId.SCIZOR,
|
||||
SpeciesId.HERACROSS,
|
||||
SpeciesId.WEAVILE,
|
||||
SpeciesId.GARDEVOIR,
|
||||
SpeciesId.ROSERADE,
|
||||
SpeciesId.SPIRITOMB,
|
||||
SpeciesId.LUCARIO,
|
||||
SpeciesId.DRAPION,
|
||||
SpeciesId.GALLADE,
|
||||
SpeciesId.ROTOM,
|
||||
SpeciesId.EXCADRILL,
|
||||
[SpeciesId.ZOROARK, SpeciesId.HISUI_ZOROARK],
|
||||
SpeciesId.FERROTHORN,
|
||||
SpeciesId.DURANT,
|
||||
SpeciesId.FLORGES,
|
||||
SpeciesId.AEGISLASH,
|
||||
SpeciesId.VIKAVOLT,
|
||||
SpeciesId.MIMIKYU,
|
||||
SpeciesId.DHELMISE,
|
||||
SpeciesId.POLTEAGEIST,
|
||||
SpeciesId.COPPERAJAH,
|
||||
SpeciesId.KLEAVOR,
|
||||
SpeciesId.BASCULEGION, // Ensure gender does not change
|
||||
SpeciesId.SNEASLER,
|
||||
SpeciesId.OVERQWIL,
|
||||
SpeciesId.PAWMOT,
|
||||
SpeciesId.CETITAN,
|
||||
SpeciesId.DONDOZO,
|
||||
SpeciesId.DUDUNSPARCE,
|
||||
SpeciesId.GHOLDENGO,
|
||||
SpeciesId.POLTCHAGEIST,
|
||||
[SpeciesId.GALAR_SLOWBRO, SpeciesId.GALAR_SLOWKING],
|
||||
SpeciesId.HISUI_ARCANINE,
|
||||
SpeciesId.PALDEA_TAUROS,
|
||||
];
|
||||
//#endregion Slot 4
|
||||
|
||||
//#region Slot 5
|
||||
/** Rival's slot 5 species pool for fight 4 and beyond */
|
||||
const SLOT_5_FINAL = [
|
||||
SpeciesId.DRAGONITE,
|
||||
SpeciesId.KINGDRA,
|
||||
SpeciesId.TYRANITAR,
|
||||
SpeciesId.SALAMENCE,
|
||||
SpeciesId.METAGROSS,
|
||||
SpeciesId.GARCHOMP,
|
||||
SpeciesId.HAXORUS,
|
||||
SpeciesId.HYDREIGON,
|
||||
SpeciesId.VOLCARONA,
|
||||
SpeciesId.GOODRA,
|
||||
SpeciesId.KOMMO_O,
|
||||
SpeciesId.DRAGAPULT,
|
||||
SpeciesId.KINGAMBIT,
|
||||
SpeciesId.BAXCALIBUR,
|
||||
SpeciesId.GHOLDENGO,
|
||||
SpeciesId.ARCHALUDON,
|
||||
SpeciesId.HYDRAPPLE,
|
||||
SpeciesId.HISUI_GOODRA,
|
||||
];
|
||||
//#endregion Slot 5
|
||||
|
||||
//#region Slot 6
|
||||
/**
|
||||
* Post-process logic for rival slot 6, fight 5
|
||||
*
|
||||
* @remarks
|
||||
* Sets the level to the provided argument, sets the Pokémon to be caught in a Master Ball, sets boss
|
||||
* @param pokemon - The pokemon to post-process
|
||||
* @param level - (default {@linkcode SLOT_6_FIGHT_5_LEVEL}) The level to set the pokemon to
|
||||
* @param overrideSegments - If `true`, will force the pokemon to have 3 boss bar segments
|
||||
*/
|
||||
function postProcessSlot6Fight5(pokemon: EnemyPokemon, level = SLOT_6_FIGHT_5_LEVEL, overrideSegments = true): void {
|
||||
pokemon.level = level;
|
||||
pokemon.pokeball = PokeballType.MASTER_BALL;
|
||||
if (timedEventManager.getClassicTrainerShinyChance() === 0) {
|
||||
pokemon.shiny = true;
|
||||
pokemon.variant = 1;
|
||||
}
|
||||
// When called for fight 5, uses 3 segments.
|
||||
// For fight 6, uses the logic from `getEncounterBossSegments`
|
||||
pokemon.setBoss(true, overrideSegments ? 3 : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post-process logic for rival slot 6, fight 6
|
||||
*
|
||||
* @remarks
|
||||
* Applies {@linkcode postProcessSlot6Fight5} with an updated level
|
||||
* and also sets the `formIndex` to `1` for Mega Rayquaza
|
||||
* @param pokemon
|
||||
*/
|
||||
function postProcessSlot6Fight6(pokemon: EnemyPokemon): void {
|
||||
// Guard just in case species gets overridden
|
||||
if (pokemon.species.speciesId === SpeciesId.RAYQUAZA) {
|
||||
pokemon.formIndex = 1; // Mega
|
||||
}
|
||||
postProcessSlot6Fight5(pokemon, SLOT_6_FIGHT_6_LEVEL, false);
|
||||
pokemon.generateName();
|
||||
}
|
||||
|
||||
/** Rival's slot 6 species pool for fight 5 and beyond */
|
||||
const SLOT_6_FINAL = [SpeciesId.RAYQUAZA];
|
||||
//#endregion Slot 6
|
||||
|
||||
export interface RivalSlotConfig {
|
||||
/**
|
||||
* The pool of `SpeciesId`s to choose from
|
||||
*
|
||||
* @remarks
|
||||
* An entry may be either a single `SpeciesId` or an array of `SpeciesId`s. An
|
||||
* array entry indicates that another roll is required, and is used for split
|
||||
* evolution lines such as Charcadet to Armarouge/Ceruledge.
|
||||
*/
|
||||
readonly pool: readonly (SpeciesId | readonly SpeciesId[])[];
|
||||
|
||||
/** A function that will post-process the Pokémon after it has fully generated */
|
||||
readonly postProcess: (enemyPokemon: EnemyPokemon) => void;
|
||||
|
||||
/**
|
||||
* Whether to try to balance types in this slot to avoid sharing types with previous slots
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
readonly balanceTypes?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to try to balance weaknesses in this slot to avoid adding too many weaknesses to the overall party
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
readonly balanceWeaknesses?: boolean;
|
||||
}
|
||||
|
||||
export type RivalPoolConfig = RivalSlotConfig[];
|
||||
|
||||
/** Pools for the first rival fight */
|
||||
export const RIVAL_1_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FIGHT_1, postProcess: forceRivalStarterTraits },
|
||||
{ pool: SLOT_2_FIGHT_1, postProcess: forceRivalBirdAbility },
|
||||
];
|
||||
|
||||
/** Pools for the second rival fight */
|
||||
export const RIVAL_2_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FIGHT_2, postProcess: forceRivalStarterTraits },
|
||||
{ pool: SLOT_2_FIGHT_2, postProcess: forceRivalBirdAbility },
|
||||
{
|
||||
pool: SLOT_3_FIGHT_2,
|
||||
postProcess: p => (p.level = SLOT_3_FIGHT_2_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
];
|
||||
|
||||
/** Pools for the third rival fight */
|
||||
export const RIVAL_3_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FINAL, postProcess: forceRivalStarterTraits },
|
||||
{ pool: SLOT_2_FINAL, postProcess: forceRivalBirdAbility },
|
||||
{
|
||||
pool: SLOT_3_FIGHT_3,
|
||||
postProcess: p => (p.level = SLOT_3_FIGHT_3_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{ pool: SLOT_4_FIGHT_3, postProcess: postProcessSlot4Fight3, balanceTypes: true, balanceWeaknesses: true },
|
||||
];
|
||||
|
||||
/** Pools for the fourth rival fight */
|
||||
export const RIVAL_4_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FINAL, postProcess: forceRivalStarterTraits },
|
||||
{ pool: SLOT_2_FINAL, postProcess: forceRivalBirdAbility },
|
||||
{
|
||||
pool: SLOT_3_FINAL,
|
||||
postProcess: p => (p.level = SLOT_3_FIGHT_4_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{
|
||||
pool: SLOT_4_FINAL,
|
||||
postProcess: p => postProcessSlot4Fight4(p, SLOT_4_FIGHT_4_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{ pool: SLOT_5_FINAL, postProcess: p => (p.level = SLOT_5_FIGHT_4_LEVEL) },
|
||||
];
|
||||
|
||||
/** Pools for the fifth rival fight */
|
||||
export const RIVAL_5_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FINAL, postProcess: p => forceRivalStarterTraits(p, 2) },
|
||||
{ pool: SLOT_2_FINAL, postProcess: forceRivalBirdAbility },
|
||||
{
|
||||
pool: SLOT_3_FINAL,
|
||||
postProcess: p => (p.level = SLOT_3_FIGHT_5_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{
|
||||
pool: SLOT_4_FINAL,
|
||||
postProcess: p => postProcessSlot4Fight4(p, SLOT_4_FIGHT_5_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{ pool: SLOT_5_FINAL, postProcess: p => (p.level = SLOT_5_FIGHT_5_LEVEL) },
|
||||
{ pool: SLOT_6_FINAL, postProcess: postProcessSlot6Fight5 },
|
||||
];
|
||||
|
||||
/** Pools for the sixth rival fight */
|
||||
export const RIVAL_6_POOL: RivalPoolConfig = [
|
||||
{ pool: SLOT_1_FINAL, postProcess: p => forceRivalStarterTraits(p, 3) },
|
||||
{ pool: SLOT_2_FINAL, postProcess: p => forceRivalBirdAbility(p, 2) },
|
||||
{
|
||||
pool: SLOT_3_FINAL,
|
||||
postProcess: p => (p.level = SLOT_3_FIGHT_6_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{
|
||||
pool: SLOT_4_FINAL,
|
||||
postProcess: p => postProcessSlot4Fight4(p, SLOT_4_FIGHT_6_LEVEL),
|
||||
balanceTypes: true,
|
||||
balanceWeaknesses: true,
|
||||
},
|
||||
{ pool: SLOT_5_FINAL, postProcess: p => (p.level = SLOT_5_FIGHT_6_LEVEL) },
|
||||
{ pool: SLOT_6_FINAL, postProcess: postProcessSlot6Fight6 },
|
||||
];
|
||||
@ -1,10 +1,7 @@
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
import { getRandomRivalPartyMemberFunc } from "#app/ai/rival-team-gen";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions";
|
||||
import { signatureSpecies } from "#balance/signature-species";
|
||||
import { tmSpecies } from "#balance/tms";
|
||||
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
|
||||
import type { RARE_EGG_MOVE_LEVEL_REQUIREMENT } from "#data/balance/moveset-generation";
|
||||
import { modifierTypes } from "#data/data-lists";
|
||||
import { doubleBattleDialogue } from "#data/double-battle-dialogue";
|
||||
import { Gender } from "#data/gender";
|
||||
@ -25,6 +22,14 @@ import { PokemonMove } from "#moves/pokemon-move";
|
||||
import { getIsInitialized, initI18n } from "#plugins/i18n";
|
||||
import type { EvilTeam } from "#trainers/evil-admin-trainer-pools";
|
||||
import { evilAdminTrainerPools } from "#trainers/evil-admin-trainer-pools";
|
||||
import {
|
||||
RIVAL_1_POOL,
|
||||
RIVAL_2_POOL,
|
||||
RIVAL_3_POOL,
|
||||
RIVAL_4_POOL,
|
||||
RIVAL_5_POOL,
|
||||
RIVAL_6_POOL,
|
||||
} from "#trainers/rival-party-config";
|
||||
import {
|
||||
getEvilGruntPartyTemplate,
|
||||
getGymLeaderPartyTemplate,
|
||||
@ -988,32 +993,30 @@ export class TrainerConfig {
|
||||
}
|
||||
}
|
||||
|
||||
let t = 0;
|
||||
|
||||
/**
|
||||
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
|
||||
* Then adds Pokemon to globalScene.
|
||||
* @param speciesPool
|
||||
* @param trainerSlot
|
||||
* @param ignoreEvolution
|
||||
* @param postProcess
|
||||
* @param speciesPool - The pool of species to choose from. Can be a list of `SpeciesId` or a list of lists of `SpeciesId`.
|
||||
* @param trainerSlot - (default {@linkcode TrainerSlot.TRAINER | TRAINER}); The trainer slot to generate for.
|
||||
* @param ignoreEvolution - (default `false`); Whether to ignore evolution when determining the species to use.
|
||||
* @param postProcess - An optional function to post-process the generated `EnemyPokemon`
|
||||
*/
|
||||
export function getRandomPartyMemberFunc(
|
||||
speciesPool: SpeciesId[],
|
||||
speciesPool: (SpeciesId | SpeciesId[])[],
|
||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||
ignoreEvolution = false,
|
||||
postProcess?: (enemyPokemon: EnemyPokemon) => void,
|
||||
) {
|
||||
): (level: number, strength: PartyMemberStrength) => EnemyPokemon {
|
||||
return (level: number, strength: PartyMemberStrength) => {
|
||||
let species = randSeedItem(speciesPool);
|
||||
let species: SpeciesId | SpeciesId[] | typeof speciesPool = speciesPool;
|
||||
do {
|
||||
species = randSeedItem(species);
|
||||
} while (Array.isArray(species));
|
||||
|
||||
if (!ignoreEvolution) {
|
||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(
|
||||
level,
|
||||
true,
|
||||
strength,
|
||||
// TODO: What EvoLevelThresholdKind to use here?
|
||||
);
|
||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength);
|
||||
}
|
||||
|
||||
return globalScene.addEnemyPokemon(
|
||||
getPokemonSpecies(species),
|
||||
level,
|
||||
@ -1026,6 +1029,7 @@ export function getRandomPartyMemberFunc(
|
||||
};
|
||||
}
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedVariables: potentially useful
|
||||
function getSpeciesFilterRandomPartyMemberFunc(
|
||||
originalSpeciesFilter: PokemonSpeciesFilter,
|
||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||
@ -1050,6 +1054,7 @@ function getSpeciesFilterRandomPartyMemberFunc(
|
||||
};
|
||||
}
|
||||
|
||||
let t = 0;
|
||||
export const trainerConfigs: TrainerConfigs = {
|
||||
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
|
||||
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t)
|
||||
@ -4548,61 +4553,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
() => modifierTypes.SUPER_EXP_CHARM,
|
||||
() => modifierTypes.EXP_SHARE,
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.BULBASAUR,
|
||||
SpeciesId.CHARMANDER,
|
||||
SpeciesId.SQUIRTLE,
|
||||
SpeciesId.CHIKORITA,
|
||||
SpeciesId.CYNDAQUIL,
|
||||
SpeciesId.TOTODILE,
|
||||
SpeciesId.TREECKO,
|
||||
SpeciesId.TORCHIC,
|
||||
SpeciesId.MUDKIP,
|
||||
SpeciesId.TURTWIG,
|
||||
SpeciesId.CHIMCHAR,
|
||||
SpeciesId.PIPLUP,
|
||||
SpeciesId.SNIVY,
|
||||
SpeciesId.TEPIG,
|
||||
SpeciesId.OSHAWOTT,
|
||||
SpeciesId.CHESPIN,
|
||||
SpeciesId.FENNEKIN,
|
||||
SpeciesId.FROAKIE,
|
||||
SpeciesId.ROWLET,
|
||||
SpeciesId.LITTEN,
|
||||
SpeciesId.POPPLIO,
|
||||
SpeciesId.GROOKEY,
|
||||
SpeciesId.SCORBUNNY,
|
||||
SpeciesId.SOBBLE,
|
||||
SpeciesId.SPRIGATITO,
|
||||
SpeciesId.FUECOCO,
|
||||
SpeciesId.QUAXLY,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => (p.abilityIndex = 0),
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEY,
|
||||
SpeciesId.HOOTHOOT,
|
||||
SpeciesId.TAILLOW,
|
||||
SpeciesId.STARLY,
|
||||
SpeciesId.PIDOVE,
|
||||
SpeciesId.FLETCHLING,
|
||||
SpeciesId.PIKIPEK,
|
||||
SpeciesId.ROOKIDEE,
|
||||
SpeciesId.WATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
),
|
||||
),
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_1_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_1_POOL, 1)),
|
||||
[TrainerType.RIVAL_2]: new TrainerConfig(++t)
|
||||
.setName("Finn")
|
||||
.setHasGenders("Ivy")
|
||||
@ -4615,70 +4567,9 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setMixedBattleBgm("battle_rival")
|
||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
|
||||
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.IVYSAUR,
|
||||
SpeciesId.CHARMELEON,
|
||||
SpeciesId.WARTORTLE,
|
||||
SpeciesId.BAYLEEF,
|
||||
SpeciesId.QUILAVA,
|
||||
SpeciesId.CROCONAW,
|
||||
SpeciesId.GROVYLE,
|
||||
SpeciesId.COMBUSKEN,
|
||||
SpeciesId.MARSHTOMP,
|
||||
SpeciesId.GROTLE,
|
||||
SpeciesId.MONFERNO,
|
||||
SpeciesId.PRINPLUP,
|
||||
SpeciesId.SERVINE,
|
||||
SpeciesId.PIGNITE,
|
||||
SpeciesId.DEWOTT,
|
||||
SpeciesId.QUILLADIN,
|
||||
SpeciesId.BRAIXEN,
|
||||
SpeciesId.FROGADIER,
|
||||
SpeciesId.DARTRIX,
|
||||
SpeciesId.TORRACAT,
|
||||
SpeciesId.BRIONNE,
|
||||
SpeciesId.THWACKEY,
|
||||
SpeciesId.RABOOT,
|
||||
SpeciesId.DRIZZILE,
|
||||
SpeciesId.FLORAGATO,
|
||||
SpeciesId.CROCALOR,
|
||||
SpeciesId.QUAXWELL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => (p.abilityIndex = 0),
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEOTTO,
|
||||
SpeciesId.HOOTHOOT,
|
||||
SpeciesId.TAILLOW,
|
||||
SpeciesId.STARAVIA,
|
||||
SpeciesId.TRANQUILL,
|
||||
SpeciesId.FLETCHINDER,
|
||||
SpeciesId.TRUMBEAK,
|
||||
SpeciesId.CORVISQUIRE,
|
||||
SpeciesId.WATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
),
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 1))
|
||||
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 2)),
|
||||
[TrainerType.RIVAL_3]: new TrainerConfig(++t)
|
||||
.setName("Finn")
|
||||
.setHasGenders("Ivy")
|
||||
@ -4690,71 +4581,10 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setBattleBgm("battle_rival")
|
||||
.setMixedBattleBgm("battle_rival")
|
||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_3)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.VENUSAUR,
|
||||
SpeciesId.CHARIZARD,
|
||||
SpeciesId.BLASTOISE,
|
||||
SpeciesId.MEGANIUM,
|
||||
SpeciesId.TYPHLOSION,
|
||||
SpeciesId.FERALIGATR,
|
||||
SpeciesId.SCEPTILE,
|
||||
SpeciesId.BLAZIKEN,
|
||||
SpeciesId.SWAMPERT,
|
||||
SpeciesId.TORTERRA,
|
||||
SpeciesId.INFERNAPE,
|
||||
SpeciesId.EMPOLEON,
|
||||
SpeciesId.SERPERIOR,
|
||||
SpeciesId.EMBOAR,
|
||||
SpeciesId.SAMUROTT,
|
||||
SpeciesId.CHESNAUGHT,
|
||||
SpeciesId.DELPHOX,
|
||||
SpeciesId.GRENINJA,
|
||||
SpeciesId.DECIDUEYE,
|
||||
SpeciesId.INCINEROAR,
|
||||
SpeciesId.PRIMARINA,
|
||||
SpeciesId.RILLABOOM,
|
||||
SpeciesId.CINDERACE,
|
||||
SpeciesId.INTELEON,
|
||||
SpeciesId.MEOWSCARADA,
|
||||
SpeciesId.SKELEDIRGE,
|
||||
SpeciesId.QUAQUAVAL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => (p.abilityIndex = 0),
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEOT,
|
||||
SpeciesId.NOCTOWL,
|
||||
SpeciesId.SWELLOW,
|
||||
SpeciesId.STARAPTOR,
|
||||
SpeciesId.UNFEZANT,
|
||||
SpeciesId.TALONFLAME,
|
||||
SpeciesId.TOUCANNON,
|
||||
SpeciesId.CORVIKNIGHT,
|
||||
SpeciesId.KILOWATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540),
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 1))
|
||||
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 2))
|
||||
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 3)),
|
||||
[TrainerType.RIVAL_4]: new TrainerConfig(++t)
|
||||
.setName("Finn")
|
||||
.setHasGenders("Ivy")
|
||||
@ -4768,74 +4598,11 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setMixedBattleBgm("battle_rival_2")
|
||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_4)
|
||||
.setModifierRewardFuncs(() => modifierTypes.TERA_ORB)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.VENUSAUR,
|
||||
SpeciesId.CHARIZARD,
|
||||
SpeciesId.BLASTOISE,
|
||||
SpeciesId.MEGANIUM,
|
||||
SpeciesId.TYPHLOSION,
|
||||
SpeciesId.FERALIGATR,
|
||||
SpeciesId.SCEPTILE,
|
||||
SpeciesId.BLAZIKEN,
|
||||
SpeciesId.SWAMPERT,
|
||||
SpeciesId.TORTERRA,
|
||||
SpeciesId.INFERNAPE,
|
||||
SpeciesId.EMPOLEON,
|
||||
SpeciesId.SERPERIOR,
|
||||
SpeciesId.EMBOAR,
|
||||
SpeciesId.SAMUROTT,
|
||||
SpeciesId.CHESNAUGHT,
|
||||
SpeciesId.DELPHOX,
|
||||
SpeciesId.GRENINJA,
|
||||
SpeciesId.DECIDUEYE,
|
||||
SpeciesId.INCINEROAR,
|
||||
SpeciesId.PRIMARINA,
|
||||
SpeciesId.RILLABOOM,
|
||||
SpeciesId.CINDERACE,
|
||||
SpeciesId.INTELEON,
|
||||
SpeciesId.MEOWSCARADA,
|
||||
SpeciesId.SKELEDIRGE,
|
||||
SpeciesId.QUAQUAVAL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => {
|
||||
p.abilityIndex = 0;
|
||||
p.teraType = p.species.type1;
|
||||
},
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEOT,
|
||||
SpeciesId.NOCTOWL,
|
||||
SpeciesId.SWELLOW,
|
||||
SpeciesId.STARAPTOR,
|
||||
SpeciesId.UNFEZANT,
|
||||
SpeciesId.TALONFLAME,
|
||||
SpeciesId.TOUCANNON,
|
||||
SpeciesId.CORVIKNIGHT,
|
||||
SpeciesId.KILOWATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 1))
|
||||
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 2))
|
||||
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 3))
|
||||
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 4))
|
||||
.setInstantTera(0), // Tera starter to primary type
|
||||
[TrainerType.RIVAL_5]: new TrainerConfig(++t)
|
||||
.setName("Finn")
|
||||
@ -4844,89 +4611,17 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setTitle("Rival")
|
||||
.setBoss()
|
||||
.setStaticParty()
|
||||
.setMoneyMultiplier(2.25)
|
||||
.setMoneyMultiplier(2.5)
|
||||
.setEncounterBgm(TrainerType.RIVAL)
|
||||
.setBattleBgm("battle_rival_3")
|
||||
.setMixedBattleBgm("battle_rival_3")
|
||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_5)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.VENUSAUR,
|
||||
SpeciesId.CHARIZARD,
|
||||
SpeciesId.BLASTOISE,
|
||||
SpeciesId.MEGANIUM,
|
||||
SpeciesId.TYPHLOSION,
|
||||
SpeciesId.FERALIGATR,
|
||||
SpeciesId.SCEPTILE,
|
||||
SpeciesId.BLAZIKEN,
|
||||
SpeciesId.SWAMPERT,
|
||||
SpeciesId.TORTERRA,
|
||||
SpeciesId.INFERNAPE,
|
||||
SpeciesId.EMPOLEON,
|
||||
SpeciesId.SERPERIOR,
|
||||
SpeciesId.EMBOAR,
|
||||
SpeciesId.SAMUROTT,
|
||||
SpeciesId.CHESNAUGHT,
|
||||
SpeciesId.DELPHOX,
|
||||
SpeciesId.GRENINJA,
|
||||
SpeciesId.DECIDUEYE,
|
||||
SpeciesId.INCINEROAR,
|
||||
SpeciesId.PRIMARINA,
|
||||
SpeciesId.RILLABOOM,
|
||||
SpeciesId.CINDERACE,
|
||||
SpeciesId.INTELEON,
|
||||
SpeciesId.MEOWSCARADA,
|
||||
SpeciesId.SKELEDIRGE,
|
||||
SpeciesId.QUAQUAVAL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => {
|
||||
p.setBoss(true, 2);
|
||||
p.abilityIndex = 0;
|
||||
p.teraType = p.species.type1;
|
||||
},
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEOT,
|
||||
SpeciesId.NOCTOWL,
|
||||
SpeciesId.SWELLOW,
|
||||
SpeciesId.STARAPTOR,
|
||||
SpeciesId.UNFEZANT,
|
||||
SpeciesId.TALONFLAME,
|
||||
SpeciesId.TOUCANNON,
|
||||
SpeciesId.CORVIKNIGHT,
|
||||
SpeciesId.KILOWATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
.setPartyMemberFunc(
|
||||
5,
|
||||
getRandomPartyMemberFunc([SpeciesId.RAYQUAZA], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 3);
|
||||
p.pokeball = PokeballType.MASTER_BALL;
|
||||
p.shiny = timedEventManager.getClassicTrainerShinyChance() === 0;
|
||||
p.variant = 1;
|
||||
}),
|
||||
)
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 1))
|
||||
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 2))
|
||||
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 3))
|
||||
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 4))
|
||||
.setPartyMemberFunc(5, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 5))
|
||||
.setInstantTera(0), // Tera starter to primary type
|
||||
[TrainerType.RIVAL_6]: new TrainerConfig(++t)
|
||||
.setName("Finn")
|
||||
@ -4940,94 +4635,13 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setBattleBgm("battle_rival_3")
|
||||
.setMixedBattleBgm("battle_rival_3")
|
||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_6)
|
||||
.setPartyMemberFunc(
|
||||
0,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.VENUSAUR,
|
||||
SpeciesId.CHARIZARD,
|
||||
SpeciesId.BLASTOISE,
|
||||
SpeciesId.MEGANIUM,
|
||||
SpeciesId.TYPHLOSION,
|
||||
SpeciesId.FERALIGATR,
|
||||
SpeciesId.SCEPTILE,
|
||||
SpeciesId.BLAZIKEN,
|
||||
SpeciesId.SWAMPERT,
|
||||
SpeciesId.TORTERRA,
|
||||
SpeciesId.INFERNAPE,
|
||||
SpeciesId.EMPOLEON,
|
||||
SpeciesId.SERPERIOR,
|
||||
SpeciesId.EMBOAR,
|
||||
SpeciesId.SAMUROTT,
|
||||
SpeciesId.CHESNAUGHT,
|
||||
SpeciesId.DELPHOX,
|
||||
SpeciesId.GRENINJA,
|
||||
SpeciesId.DECIDUEYE,
|
||||
SpeciesId.INCINEROAR,
|
||||
SpeciesId.PRIMARINA,
|
||||
SpeciesId.RILLABOOM,
|
||||
SpeciesId.CINDERACE,
|
||||
SpeciesId.INTELEON,
|
||||
SpeciesId.MEOWSCARADA,
|
||||
SpeciesId.SKELEDIRGE,
|
||||
SpeciesId.QUAQUAVAL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => {
|
||||
p.setBoss(true, 3);
|
||||
p.abilityIndex = 0;
|
||||
p.teraType = p.species.type1;
|
||||
p.generateAndPopulateMoveset();
|
||||
},
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
1,
|
||||
getRandomPartyMemberFunc(
|
||||
[
|
||||
SpeciesId.PIDGEOT,
|
||||
SpeciesId.NOCTOWL,
|
||||
SpeciesId.SWELLOW,
|
||||
SpeciesId.STARAPTOR,
|
||||
SpeciesId.UNFEZANT,
|
||||
SpeciesId.TALONFLAME,
|
||||
SpeciesId.TOUCANNON,
|
||||
SpeciesId.CORVIKNIGHT,
|
||||
SpeciesId.KILOWATTREL,
|
||||
],
|
||||
TrainerSlot.TRAINER,
|
||||
true,
|
||||
p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
},
|
||||
),
|
||||
)
|
||||
.setPartyMemberFunc(
|
||||
2,
|
||||
getSpeciesFilterRandomPartyMemberFunc(
|
||||
(species: PokemonSpecies) =>
|
||||
!pokemonEvolutions.hasOwnProperty(species.speciesId)
|
||||
&& !pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||
&& species.baseTotal >= 450,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(species => species.baseTotal >= 540)
|
||||
.setPartyMemberFunc(
|
||||
5,
|
||||
getRandomPartyMemberFunc([SpeciesId.RAYQUAZA], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss();
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.MASTER_BALL;
|
||||
p.shiny = timedEventManager.getClassicTrainerShinyChance() === 0;
|
||||
p.variant = 1;
|
||||
p.formIndex = 1; // Mega Rayquaza
|
||||
p.generateName();
|
||||
}),
|
||||
)
|
||||
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 0))
|
||||
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 1))
|
||||
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 2))
|
||||
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 3))
|
||||
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 4))
|
||||
.setPartyMemberFunc(5, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 5))
|
||||
.setInstantTera(0), // Tera starter to primary type
|
||||
|
||||
[TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig((t = TrainerType.ROCKET_BOSS_GIOVANNI_1))
|
||||
.setName("Giovanni")
|
||||
.initForEvilTeamLeader("Rocket Boss", [])
|
||||
|
||||
@ -1304,7 +1304,6 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
const variableTypeAttr = move.getAttrs("VariableMoveTypeAttr")[0];
|
||||
const types = variableTypeAttr?.getTypesForItemSpawn(p, move) ?? [move.type];
|
||||
for (const type of types) {
|
||||
console.info("%cConsidering type " + PokemonType[type], "color: orange");
|
||||
const currentWeight = attackMoveTypeWeights.get(type) ?? 0;
|
||||
if (currentWeight < 3) {
|
||||
attackMoveTypeWeights.set(type, currentWeight + 1);
|
||||
@ -1319,11 +1318,9 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
||||
}
|
||||
|
||||
const randInt = randSeedInt(totalWeight);
|
||||
console.log("%cTotal weight " + totalWeight + ", rolled " + randInt, "color: orange");
|
||||
let weight = 0;
|
||||
|
||||
for (const [type, typeWeight] of attackMoveTypeWeights.entries()) {
|
||||
console.log("%cWeighted type " + PokemonType[type] + " with weight " + typeWeight, "color: orange");
|
||||
if (randInt < weight + typeWeight) {
|
||||
return new AttackTypeBoosterModifierType(type, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
||||
}
|
||||
|
||||
@ -292,7 +292,10 @@ export class EncounterPhase extends BattlePhase {
|
||||
}
|
||||
|
||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
|
||||
if (!this.loaded) {
|
||||
if (this.loaded) {
|
||||
this.doEncounter();
|
||||
globalScene.resetSeed();
|
||||
} else {
|
||||
this.trySetWeatherIfNewBiome(); // Set weather before session gets saved
|
||||
// Game syncs to server on waves X1 and X6 (As of 1.2.0)
|
||||
globalScene.gameData
|
||||
@ -305,9 +308,6 @@ export class EncounterPhase extends BattlePhase {
|
||||
this.doEncounter();
|
||||
globalScene.resetSeed();
|
||||
});
|
||||
} else {
|
||||
this.doEncounter();
|
||||
globalScene.resetSeed();
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -490,11 +490,8 @@ export class EncounterPhase extends BattlePhase {
|
||||
this.end();
|
||||
};
|
||||
|
||||
if (showEncounterMessage) {
|
||||
const introDialogue = encounter.dialogue.intro;
|
||||
if (!introDialogue) {
|
||||
doShowEncounterOptions();
|
||||
} else {
|
||||
if (showEncounterMessage && introDialogue) {
|
||||
const FIRST_DIALOGUE_PROMPT_DELAY = 750;
|
||||
let i = 0;
|
||||
const showNextDialogue = () => {
|
||||
@ -504,14 +501,7 @@ export class EncounterPhase extends BattlePhase {
|
||||
const text = getEncounterText(dialogue.text)!;
|
||||
i++;
|
||||
if (title) {
|
||||
globalScene.ui.showDialogue(
|
||||
text,
|
||||
title,
|
||||
null,
|
||||
nextAction,
|
||||
0,
|
||||
i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0,
|
||||
);
|
||||
globalScene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||
} else {
|
||||
globalScene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||
}
|
||||
@ -520,7 +510,6 @@ export class EncounterPhase extends BattlePhase {
|
||||
if (introDialogue.length > 0) {
|
||||
showNextDialogue();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doShowEncounterOptions();
|
||||
}
|
||||
@ -528,13 +517,13 @@ export class EncounterPhase extends BattlePhase {
|
||||
|
||||
const encounterMessage = i18next.t("battle:mysteryEncounterAppeared");
|
||||
|
||||
if (!encounterMessage) {
|
||||
doEncounter();
|
||||
} else {
|
||||
if (encounterMessage) {
|
||||
doTrainerExclamation();
|
||||
globalScene.ui.showDialogue(encounterMessage, "???", null, () => {
|
||||
globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doEncounter()));
|
||||
});
|
||||
} else {
|
||||
doEncounter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user