diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 39aa2bc9b60..a4c7df9dc00 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -712,7 +712,7 @@ export class TrainerConfig { return this; } - initPartyMemberFuncFromConfig(cfgs: PokemonPregenData[], postProcess?: (Pokemon) => void) { + getRandomPartyMemberFuncFromConfig(cfgs: PokemonPregenData[], postProcess?: (Pokemon) => void) { return (level: number, strength: PartyMemberStrength) => { let cfg: PokemonPregenData = cfgs[0]; if (cfgs.length > 1) { @@ -722,7 +722,7 @@ export class TrainerConfig { if (cfg.teraType) { // Defined tera type: instant tera cfg.instantTera = true; } - else if (cfg.instantTera) { // Instant tera with undefined type will be specialty type or undefined + else if (cfg.instantTera && this.hasSpecialtyType()) { // Instant tera with undefined type will be specialty type cfg.teraType = this.specialtyType; } @@ -758,7 +758,10 @@ export class TrainerConfig { // Set the party templates for the Elite Four. this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR); - let teraSlot: number | undefined; + + // Set species filter and specialty type, otherwise filter by base total. + this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST); + this.setSpecialtyType(specialtyType); trainerPartyConfigs[this.trainerType].forEach((slot, s) => { const cfg = Array.isArray(slot[1]) ? slot[1] : [slot[1]]; @@ -769,20 +772,14 @@ export class TrainerConfig { }); } cfg.forEach(c => { - c.gender = c.gender ?? isMale ? Gender.MALE: Gender.FEMALE; - c.pokeball = c.pokeball ?? PokeballType.ULTRA_BALL; + c.preferredGender = c.preferredGender ?? isMale ? Gender.MALE: Gender.FEMALE; }); if (cfg.some(c => c.teraType || c.instantTera)) { this.setInstantTera(s); - teraSlot = s; } - this.setPartyMemberFunc(slot[0], this.initPartyMemberFuncFromConfig(cfg)); + this.setPartyMemberFunc(slot[0], this.getRandomPartyMemberFuncFromConfig(cfg)); }); - // Set species filter and specialty type, otherwise filter by base total. - this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST); - this.setSpecialtyType(specialtyType); - // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); this.name = i18next.t(`trainerNames:${nameForCall}`); @@ -800,7 +797,6 @@ export class TrainerConfig { this.setHasVoucher(true); this.setBattleBgm("battle_unova_elite"); this.setVictoryBgm("victory_gym"); - this.setRandomTeraModifiers(() => 1, teraSlot); return this; } @@ -820,6 +816,23 @@ export class TrainerConfig { // Set the party templates for the Champion. this.setPartyTemplates(trainerPartyTemplates.CHAMPION); + trainerPartyConfigs[this.trainerType].forEach((slot, s) => { + const cfg = Array.isArray(slot[1]) ? slot[1] : [slot[1]]; + if (s === 5) { // Last party member is always a boss with 2 segments + cfg.forEach(c => { + c.boss = true; + c.bossSegments = 2; + }); + } + cfg.forEach(c => { + c.preferredGender = c.preferredGender ?? isMale ? Gender.MALE: Gender.FEMALE; + }); + if (cfg.some(c => c.teraType || c.instantTera)) { + this.setInstantTera(s); + } + this.setPartyMemberFunc(slot[0], this.getRandomPartyMemberFuncFromConfig(cfg)); + }); + // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); this.name = i18next.t(`trainerNames:${nameForCall}`); @@ -1031,6 +1044,7 @@ export function getRandomPartyMemberFunc( trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution = false, postProcess?: (enemyPokemon: EnemyPokemon) => void, + pregenData?: PokemonPregenData, ) { return (level: number, strength: PartyMemberStrength) => { let species = randSeedItem(speciesPool); @@ -1050,6 +1064,7 @@ export function getRandomPartyMemberFunc( false, undefined, postProcess, + pregenData, ); }; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 8c8a057e1bb..140d801e5bc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -481,12 +481,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (this.formIndex === undefined) { - this.formIndex = globalScene.getSpeciesFormIndex( - species, - this.gender, - this.nature, - this.isPlayer(), - ); + if (pregenData.randomForms) { + this.formIndex = randSeedItem(pregenData.randomForms); + } + else { + this.formIndex = globalScene.getSpeciesFormIndex( + species, + this.gender, + this.nature, + this.isPlayer(), + ); + } } if (this.shiny === undefined) { @@ -1757,10 +1762,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return precise ? this.hp / this.getMaxHp() : Math.round((this.hp / this.getMaxHp()) * 100) / 100; } - generateGender(): void { + generateGender(preferred?: Gender): void { if (this.species.malePercent === null) { this.gender = Gender.GENDERLESS; - } else { + } + else if (!isNullOrUndefined(preferred)) { + if (preferred === Gender.MALE) { + this.gender = this.species.malePercent >= 50 ? Gender.MALE : Gender.FEMALE; + } + else { + this.gender = this.species.malePercent <= 50? Gender.FEMALE : Gender.MALE; + } + } + else { const genderChance = (this.id % 256) * 0.390625; if (genderChance < this.species.malePercent) { this.gender = Gender.MALE; @@ -6170,7 +6184,12 @@ export class EnemyPokemon extends Pokemon { this.formIndex = Overrides.OPP_FORM_OVERRIDES[speciesId]; } - if (pregenData && pregenData.presetMoves) { + // Push any predefined PokemonMove Objects (including PP info etc) to the moveset first + if (pregenData?.moveset) { + this.moveset.push(...pregenData.moveset); + } + + if (pregenData?.presetMoves) { this.generateAndPopulateMoveset(...pregenData.presetMoves); } else if (!dataSource) { diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 93adf12518f..1a101f3230a 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -18,7 +18,7 @@ export interface PokemonPregenData { species: Species; player?: boolean; nickname?: string; - formIndex?: number; + formIndex?: number; // Specific desired form index abilityIndex?: number; passive?: boolean; shiny?: boolean; @@ -28,15 +28,16 @@ export interface PokemonPregenData { gender?: Gender; ivs?: number[]; nature?: Nature; - moveset?: PokemonMove[]; - presetMoves?: Moves[]; + moveset?: PokemonMove[]; // Fully built PokemonMove objects including PP info + presetMoves?: Moves[]; // Moves to include first during moveset generation friendship?: number; luck?: number; pokerus?: boolean; teraType?: PokemonType; shinyLock?: boolean; instantTera?: boolean; - randomForms?: number[]; + randomForms?: number[]; // Form indexes to choose from at random + preferredGender?: Gender; // The PREFERRED gender for the mon to have, but can be overridden by the majority gender for mons w/ uneven ratios fusionSpecies?: Species; fusionFormIndex?: number; @@ -47,8 +48,8 @@ export interface PokemonPregenData { fusionLuck?: number; fusionTeraType?: PokemonType; - boss?: boolean; - bossSegments?: number; + boss?: boolean; // Defaults to 2 boss bars + bossSegments?: number; // If this is set, boss will be set to true customPokemonData?: CustomPokemonData; fusionCustomPokemonData?: CustomPokemonData;