mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-15 14:25:32 +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 { globalScene } from "#app/global-scene";
|
||||||
import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions";
|
import type { SpeciesFormEvolution } from "#balance/pokemon-evolutions";
|
||||||
import { pokemonEvolutions, pokemonPrevolutions } 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 type { PokemonSpecies } from "#data/pokemon-species";
|
||||||
import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind";
|
import { EvoLevelThresholdKind } from "#enums/evo-level-threshold-kind";
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
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);
|
const levelThreshold = Math.max(ev.level, ev.evoLevelThreshold?.[encounterKind] ?? 0);
|
||||||
// Disallow evolution if the level is below its required threshold.
|
// Disallow evolution if the level is below its required threshold.
|
||||||
if (level < ev.level || level < levelThreshold) {
|
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 0;
|
||||||
}
|
}
|
||||||
return levelThreshold;
|
return levelThreshold;
|
||||||
@ -83,14 +77,6 @@ function getRequiredPrevo(
|
|||||||
const threshold = evoThreshold?.[encounterKind] ?? levelReq;
|
const threshold = evoThreshold?.[encounterKind] ?? levelReq;
|
||||||
const req = levelReq === 1 ? threshold : Math.min(levelReq, threshold);
|
const req = levelReq === 1 ? threshold : Math.min(levelReq, threshold);
|
||||||
if (level < req) {
|
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 prevoSpecies;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,13 +113,6 @@ export function determineEnemySpecies(
|
|||||||
encounterKind: EvoLevelThresholdKind = forTrainer ? EvoLevelThresholdKind.NORMAL : EvoLevelThresholdKind.WILD,
|
encounterKind: EvoLevelThresholdKind = forTrainer ? EvoLevelThresholdKind.NORMAL : EvoLevelThresholdKind.WILD,
|
||||||
tryForcePrevo = true,
|
tryForcePrevo = true,
|
||||||
): SpeciesId {
|
): SpeciesId {
|
||||||
console.info(
|
|
||||||
"%c Determining species for %s at level %d with encounter kind %s",
|
|
||||||
"color: blue",
|
|
||||||
species.name,
|
|
||||||
level,
|
|
||||||
encounterKind,
|
|
||||||
);
|
|
||||||
const requiredPrevo =
|
const requiredPrevo =
|
||||||
tryForcePrevo
|
tryForcePrevo
|
||||||
&& pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
&& pokemonPrevolutions.hasOwnProperty(species.speciesId)
|
||||||
@ -162,7 +141,6 @@ export function determineEnemySpecies(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (evoPool.length === 0) {
|
if (evoPool.length === 0) {
|
||||||
console.log("%c No evolutions available, returning base species", "color: blue");
|
|
||||||
return species.speciesId;
|
return species.speciesId;
|
||||||
}
|
}
|
||||||
const [choice, evoSpecies] = randSeedItem(evoPool);
|
const [choice, evoSpecies] = randSeedItem(evoPool);
|
||||||
@ -184,14 +162,7 @@ export function determineEnemySpecies(
|
|||||||
break;
|
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));
|
const randomLevel = randSeedInt(choice, Math.round(choice * multiplier));
|
||||||
console.info("%c Random level is %d", "color: blue", randomLevel);
|
|
||||||
if (randomLevel <= level) {
|
if (randomLevel <= level) {
|
||||||
return determineEnemySpecies(
|
return determineEnemySpecies(
|
||||||
getPokemonSpecies(evoSpecies),
|
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()) {
|
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) {
|
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 { globalScene } from "#app/global-scene";
|
||||||
import { pokemonEvolutions, pokemonPrevolutions } from "#balance/pokemon-evolutions";
|
|
||||||
import { signatureSpecies } from "#balance/signature-species";
|
import { signatureSpecies } from "#balance/signature-species";
|
||||||
import { tmSpecies } from "#balance/tms";
|
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 { modifierTypes } from "#data/data-lists";
|
||||||
import { doubleBattleDialogue } from "#data/double-battle-dialogue";
|
import { doubleBattleDialogue } from "#data/double-battle-dialogue";
|
||||||
import { Gender } from "#data/gender";
|
import { Gender } from "#data/gender";
|
||||||
@ -25,6 +22,14 @@ import { PokemonMove } from "#moves/pokemon-move";
|
|||||||
import { getIsInitialized, initI18n } from "#plugins/i18n";
|
import { getIsInitialized, initI18n } from "#plugins/i18n";
|
||||||
import type { EvilTeam } from "#trainers/evil-admin-trainer-pools";
|
import type { EvilTeam } from "#trainers/evil-admin-trainer-pools";
|
||||||
import { evilAdminTrainerPools } 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 {
|
import {
|
||||||
getEvilGruntPartyTemplate,
|
getEvilGruntPartyTemplate,
|
||||||
getGymLeaderPartyTemplate,
|
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.
|
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
|
||||||
* Then adds Pokemon to globalScene.
|
* Then adds Pokemon to globalScene.
|
||||||
* @param speciesPool
|
* @param speciesPool - The pool of species to choose from. Can be a list of `SpeciesId` or a list of lists of `SpeciesId`.
|
||||||
* @param trainerSlot
|
* @param trainerSlot - (default {@linkcode TrainerSlot.TRAINER | TRAINER}); The trainer slot to generate for.
|
||||||
* @param ignoreEvolution
|
* @param ignoreEvolution - (default `false`); Whether to ignore evolution when determining the species to use.
|
||||||
* @param postProcess
|
* @param postProcess - An optional function to post-process the generated `EnemyPokemon`
|
||||||
*/
|
*/
|
||||||
export function getRandomPartyMemberFunc(
|
export function getRandomPartyMemberFunc(
|
||||||
speciesPool: SpeciesId[],
|
speciesPool: (SpeciesId | SpeciesId[])[],
|
||||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||||
ignoreEvolution = false,
|
ignoreEvolution = false,
|
||||||
postProcess?: (enemyPokemon: EnemyPokemon) => void,
|
postProcess?: (enemyPokemon: EnemyPokemon) => void,
|
||||||
) {
|
): (level: number, strength: PartyMemberStrength) => EnemyPokemon {
|
||||||
return (level: number, strength: PartyMemberStrength) => {
|
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) {
|
if (!ignoreEvolution) {
|
||||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(
|
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength);
|
||||||
level,
|
|
||||||
true,
|
|
||||||
strength,
|
|
||||||
// TODO: What EvoLevelThresholdKind to use here?
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return globalScene.addEnemyPokemon(
|
return globalScene.addEnemyPokemon(
|
||||||
getPokemonSpecies(species),
|
getPokemonSpecies(species),
|
||||||
level,
|
level,
|
||||||
@ -1026,6 +1029,7 @@ export function getRandomPartyMemberFunc(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// biome-ignore lint/correctness/noUnusedVariables: potentially useful
|
||||||
function getSpeciesFilterRandomPartyMemberFunc(
|
function getSpeciesFilterRandomPartyMemberFunc(
|
||||||
originalSpeciesFilter: PokemonSpeciesFilter,
|
originalSpeciesFilter: PokemonSpeciesFilter,
|
||||||
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
trainerSlot: TrainerSlot = TrainerSlot.TRAINER,
|
||||||
@ -1050,6 +1054,7 @@ function getSpeciesFilterRandomPartyMemberFunc(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let t = 0;
|
||||||
export const trainerConfigs: TrainerConfigs = {
|
export const trainerConfigs: TrainerConfigs = {
|
||||||
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
|
[TrainerType.UNKNOWN]: new TrainerConfig(0).setHasGenders(),
|
||||||
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t)
|
[TrainerType.ACE_TRAINER]: new TrainerConfig(++t)
|
||||||
@ -4548,61 +4553,8 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
() => modifierTypes.SUPER_EXP_CHARM,
|
() => modifierTypes.SUPER_EXP_CHARM,
|
||||||
() => modifierTypes.EXP_SHARE,
|
() => modifierTypes.EXP_SHARE,
|
||||||
)
|
)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_1_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_1_POOL, 1)),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
[TrainerType.RIVAL_2]: new TrainerConfig(++t)
|
[TrainerType.RIVAL_2]: new TrainerConfig(++t)
|
||||||
.setName("Finn")
|
.setName("Finn")
|
||||||
.setHasGenders("Ivy")
|
.setHasGenders("Ivy")
|
||||||
@ -4615,70 +4567,9 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setMixedBattleBgm("battle_rival")
|
.setMixedBattleBgm("battle_rival")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
|
||||||
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
|
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 1))
|
||||||
getRandomPartyMemberFunc(
|
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_2_POOL, 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,
|
|
||||||
],
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
[TrainerType.RIVAL_3]: new TrainerConfig(++t)
|
[TrainerType.RIVAL_3]: new TrainerConfig(++t)
|
||||||
.setName("Finn")
|
.setName("Finn")
|
||||||
.setHasGenders("Ivy")
|
.setHasGenders("Ivy")
|
||||||
@ -4690,71 +4581,10 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setBattleBgm("battle_rival")
|
.setBattleBgm("battle_rival")
|
||||||
.setMixedBattleBgm("battle_rival")
|
.setMixedBattleBgm("battle_rival")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_3)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_3)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 1))
|
||||||
getRandomPartyMemberFunc(
|
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 2))
|
||||||
[
|
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_3_POOL, 3)),
|
||||||
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),
|
|
||||||
[TrainerType.RIVAL_4]: new TrainerConfig(++t)
|
[TrainerType.RIVAL_4]: new TrainerConfig(++t)
|
||||||
.setName("Finn")
|
.setName("Finn")
|
||||||
.setHasGenders("Ivy")
|
.setHasGenders("Ivy")
|
||||||
@ -4768,74 +4598,11 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setMixedBattleBgm("battle_rival_2")
|
.setMixedBattleBgm("battle_rival_2")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_4)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_4)
|
||||||
.setModifierRewardFuncs(() => modifierTypes.TERA_ORB)
|
.setModifierRewardFuncs(() => modifierTypes.TERA_ORB)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 1))
|
||||||
getRandomPartyMemberFunc(
|
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 2))
|
||||||
[
|
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 3))
|
||||||
SpeciesId.VENUSAUR,
|
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_4_POOL, 4))
|
||||||
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)
|
|
||||||
.setInstantTera(0), // Tera starter to primary type
|
.setInstantTera(0), // Tera starter to primary type
|
||||||
[TrainerType.RIVAL_5]: new TrainerConfig(++t)
|
[TrainerType.RIVAL_5]: new TrainerConfig(++t)
|
||||||
.setName("Finn")
|
.setName("Finn")
|
||||||
@ -4844,89 +4611,17 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setTitle("Rival")
|
.setTitle("Rival")
|
||||||
.setBoss()
|
.setBoss()
|
||||||
.setStaticParty()
|
.setStaticParty()
|
||||||
.setMoneyMultiplier(2.25)
|
.setMoneyMultiplier(2.5)
|
||||||
.setEncounterBgm(TrainerType.RIVAL)
|
.setEncounterBgm(TrainerType.RIVAL)
|
||||||
.setBattleBgm("battle_rival_3")
|
.setBattleBgm("battle_rival_3")
|
||||||
.setMixedBattleBgm("battle_rival_3")
|
.setMixedBattleBgm("battle_rival_3")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_5)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_5)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 1))
|
||||||
getRandomPartyMemberFunc(
|
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 2))
|
||||||
[
|
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 3))
|
||||||
SpeciesId.VENUSAUR,
|
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 4))
|
||||||
SpeciesId.CHARIZARD,
|
.setPartyMemberFunc(5, getRandomRivalPartyMemberFunc(RIVAL_5_POOL, 5))
|
||||||
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;
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.setInstantTera(0), // Tera starter to primary type
|
.setInstantTera(0), // Tera starter to primary type
|
||||||
[TrainerType.RIVAL_6]: new TrainerConfig(++t)
|
[TrainerType.RIVAL_6]: new TrainerConfig(++t)
|
||||||
.setName("Finn")
|
.setName("Finn")
|
||||||
@ -4940,94 +4635,13 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setBattleBgm("battle_rival_3")
|
.setBattleBgm("battle_rival_3")
|
||||||
.setMixedBattleBgm("battle_rival_3")
|
.setMixedBattleBgm("battle_rival_3")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_6)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_6)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(0, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 0))
|
||||||
0,
|
.setPartyMemberFunc(1, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 1))
|
||||||
getRandomPartyMemberFunc(
|
.setPartyMemberFunc(2, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 2))
|
||||||
[
|
.setPartyMemberFunc(3, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 3))
|
||||||
SpeciesId.VENUSAUR,
|
.setPartyMemberFunc(4, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 4))
|
||||||
SpeciesId.CHARIZARD,
|
.setPartyMemberFunc(5, getRandomRivalPartyMemberFunc(RIVAL_6_POOL, 5))
|
||||||
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();
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.setInstantTera(0), // Tera starter to primary type
|
.setInstantTera(0), // Tera starter to primary type
|
||||||
|
|
||||||
[TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig((t = TrainerType.ROCKET_BOSS_GIOVANNI_1))
|
[TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig((t = TrainerType.ROCKET_BOSS_GIOVANNI_1))
|
||||||
.setName("Giovanni")
|
.setName("Giovanni")
|
||||||
.initForEvilTeamLeader("Rocket Boss", [])
|
.initForEvilTeamLeader("Rocket Boss", [])
|
||||||
|
|||||||
@ -1304,7 +1304,6 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
|||||||
const variableTypeAttr = move.getAttrs("VariableMoveTypeAttr")[0];
|
const variableTypeAttr = move.getAttrs("VariableMoveTypeAttr")[0];
|
||||||
const types = variableTypeAttr?.getTypesForItemSpawn(p, move) ?? [move.type];
|
const types = variableTypeAttr?.getTypesForItemSpawn(p, move) ?? [move.type];
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
console.info("%cConsidering type " + PokemonType[type], "color: orange");
|
|
||||||
const currentWeight = attackMoveTypeWeights.get(type) ?? 0;
|
const currentWeight = attackMoveTypeWeights.get(type) ?? 0;
|
||||||
if (currentWeight < 3) {
|
if (currentWeight < 3) {
|
||||||
attackMoveTypeWeights.set(type, currentWeight + 1);
|
attackMoveTypeWeights.set(type, currentWeight + 1);
|
||||||
@ -1319,11 +1318,9 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const randInt = randSeedInt(totalWeight);
|
const randInt = randSeedInt(totalWeight);
|
||||||
console.log("%cTotal weight " + totalWeight + ", rolled " + randInt, "color: orange");
|
|
||||||
let weight = 0;
|
let weight = 0;
|
||||||
|
|
||||||
for (const [type, typeWeight] of attackMoveTypeWeights.entries()) {
|
for (const [type, typeWeight] of attackMoveTypeWeights.entries()) {
|
||||||
console.log("%cWeighted type " + PokemonType[type] + " with weight " + typeWeight, "color: orange");
|
|
||||||
if (randInt < weight + typeWeight) {
|
if (randInt < weight + typeWeight) {
|
||||||
return new AttackTypeBoosterModifierType(type, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
return new AttackTypeBoosterModifierType(type, TYPE_BOOST_ITEM_BOOST_PERCENT);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -292,7 +292,10 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalScene.ui.setMode(UiMode.MESSAGE).then(() => {
|
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
|
this.trySetWeatherIfNewBiome(); // Set weather before session gets saved
|
||||||
// Game syncs to server on waves X1 and X6 (As of 1.2.0)
|
// Game syncs to server on waves X1 and X6 (As of 1.2.0)
|
||||||
globalScene.gameData
|
globalScene.gameData
|
||||||
@ -305,9 +308,6 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
this.doEncounter();
|
this.doEncounter();
|
||||||
globalScene.resetSeed();
|
globalScene.resetSeed();
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
this.doEncounter();
|
|
||||||
globalScene.resetSeed();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -490,36 +490,25 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
this.end();
|
this.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (showEncounterMessage) {
|
const introDialogue = encounter.dialogue.intro;
|
||||||
const introDialogue = encounter.dialogue.intro;
|
if (showEncounterMessage && introDialogue) {
|
||||||
if (!introDialogue) {
|
const FIRST_DIALOGUE_PROMPT_DELAY = 750;
|
||||||
doShowEncounterOptions();
|
let i = 0;
|
||||||
} else {
|
const showNextDialogue = () => {
|
||||||
const FIRST_DIALOGUE_PROMPT_DELAY = 750;
|
const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue;
|
||||||
let i = 0;
|
const dialogue = introDialogue[i];
|
||||||
const showNextDialogue = () => {
|
const title = getEncounterText(dialogue?.speaker);
|
||||||
const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue;
|
const text = getEncounterText(dialogue.text)!;
|
||||||
const dialogue = introDialogue[i];
|
i++;
|
||||||
const title = getEncounterText(dialogue?.speaker);
|
if (title) {
|
||||||
const text = getEncounterText(dialogue.text)!;
|
globalScene.ui.showDialogue(text, title, null, nextAction, 0, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0);
|
||||||
i++;
|
} else {
|
||||||
if (title) {
|
globalScene.ui.showText(text, null, nextAction, i === 1 ? FIRST_DIALOGUE_PROMPT_DELAY : 0, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (introDialogue.length > 0) {
|
|
||||||
showNextDialogue();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (introDialogue.length > 0) {
|
||||||
|
showNextDialogue();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
doShowEncounterOptions();
|
doShowEncounterOptions();
|
||||||
@ -528,13 +517,13 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
|
|
||||||
const encounterMessage = i18next.t("battle:mysteryEncounterAppeared");
|
const encounterMessage = i18next.t("battle:mysteryEncounterAppeared");
|
||||||
|
|
||||||
if (!encounterMessage) {
|
if (encounterMessage) {
|
||||||
doEncounter();
|
|
||||||
} else {
|
|
||||||
doTrainerExclamation();
|
doTrainerExclamation();
|
||||||
globalScene.ui.showDialogue(encounterMessage, "???", null, () => {
|
globalScene.ui.showDialogue(encounterMessage, "???", null, () => {
|
||||||
globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doEncounter()));
|
globalScene.charSprite.hide().then(() => globalScene.hideFieldOverlay(250).then(() => doEncounter()));
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
doEncounter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user