diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 00149db3d50..3b6e716e436 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -388,6 +388,7 @@ export default class BattleScene extends SceneBase { public inputMethod: string; private infoToggles: InfoToggle[] = []; + public waveIndex: number | null; public eventManager: TimedEventManager; @@ -1554,6 +1555,7 @@ export default class BattleScene extends SceneBase { this.resetSeed(newWaveIndex); const playerField = this.getPlayerField(); + this.waveIndex = newWaveIndex; // temporary wave index so it can be accessed by trainer constructor if ( this.gameMode.isFixedBattle(newWaveIndex) && diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 7725845e33f..5763c16217f 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -867,7 +867,7 @@ export class TrainerConfig { * @param isMale Whether the Gym Leader is Male or Not (for localization of the title). * @returns {TrainerConfig} The updated TrainerConfig instance. * **/ - initForGymLeader(signatureSpecies: (Species | Species[])[], isMale: boolean, specialtyType: Type = Type.UNKNOWN, teraSlot?: number, ignoreMinTeraWave: boolean = false): TrainerConfig { + initForGymLeader(signatureSpecies: (Species | Species[])[], isMale: boolean, specialtyType: Type = Type.UNKNOWN, ignoreMinTeraWave: boolean = false, teraSlot?: number): TrainerConfig { // Check if the internationalization (i18n) system is initialized. if (!getIsInitialized()) { initI18n(); @@ -912,7 +912,9 @@ export class TrainerConfig { if (!ignoreMinTeraWave) { this.minTeraWave = GYM_LEADER_TERA_WAVE; } - this.setInstantTera(teraSlot ?? -Utils.randSeedInt(signatureSpecies.length)); // Random tera slot can be any signature species. Yes, this is the best solution for now! + if (!Utils.isNullOrUndefined(teraSlot)) { + this.setInstantTera(teraSlot); + } return this; } @@ -968,7 +970,9 @@ export class TrainerConfig { this.setHasVoucher(true); this.setBattleBgm("battle_unova_elite"); this.setVictoryBgm("victory_gym"); - this.setInstantTera(teraSlot ?? Utils.randSeedInt(4, 2)); // Random tera can be any signature species + if (!Utils.isNullOrUndefined(teraSlot)) { + this.setInstantTera(teraSlot); + } return this; } @@ -1726,14 +1730,14 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.PIERS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PIERS"], true, Type.DARK).setHasDouble("piers_marnie_double").setDoubleTrainerType(TrainerType.MARNIE).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), [TrainerType.MARNIE]: new TrainerConfig(++t).setName("Marnie").initForGymLeader(signatureSpecies["MARNIE"], false, Type.DARK).setHasDouble("marnie_piers_double").setDoubleTrainerType(TrainerType.PIERS).setDoubleTitle("gym_leader_double").setMixedBattleBgm("battle_galar_gym"), [TrainerType.RAIHAN]: new TrainerConfig(++t).setName("Raihan").initForGymLeader(signatureSpecies["RAIHAN"], true, Type.DRAGON).setMixedBattleBgm("battle_galar_gym"), - [TrainerType.KATY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KATY"], false, Type.BUG, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"], true, Type.GRASS, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"], false, Type.ELECTRIC, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"], true, Type.WATER, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"], true, Type.NORMAL, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"], false, Type.GHOST, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"], false, Type.PSYCHIC, -1, true).setMixedBattleBgm("battle_paldea_gym"), - [TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"], true, Type.ICE, -1, true).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.KATY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KATY"], false, Type.BUG, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"], true, Type.GRASS, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"], false, Type.ELECTRIC, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"], true, Type.WATER, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"], true, Type.NORMAL, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"], false, Type.GHOST, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"], false, Type.PSYCHIC, true, -1).setMixedBattleBgm("battle_paldea_gym"), + [TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"], true, Type.ICE, true, -1).setMixedBattleBgm("battle_paldea_gym"), [TrainerType.LORELEI]: new TrainerConfig((t = TrainerType.LORELEI)).initForEliteFour(signatureSpecies["LORELEI"], false, Type.ICE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), [TrainerType.BRUNO]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BRUNO"], true, Type.FIGHTING).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"), @@ -1748,7 +1752,7 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.DRAKE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAKE"], true, Type.DRAGON).setMixedBattleBgm("battle_hoenn_elite"), [TrainerType.AARON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AARON"], true, Type.BUG).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.BERTHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BERTHA"], false, Type.GROUND).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), - [TrainerType.FLINT]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["FLINT"], true, Type.FIRE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), + [TrainerType.FLINT]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["FLINT"], true, Type.FIRE, 3).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.LUCIAN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LUCIAN"], true, Type.PSYCHIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.SHAUNTAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SHAUNTAL"], false, Type.GHOST).setMixedBattleBgm("battle_unova_elite"), [TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"], true, Type.FIGHTING).setMixedBattleBgm("battle_unova_elite"), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d7263d272a9..21dc211a776 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2365,11 +2365,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { weightMultiplier += 0.4; } const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]); - const checkTera = this.getTeraType() !== Type.UNKNOWN && !this.isOfType(this.getTeraType()); // Whether the mon has an off-type Tera. If so, moves matching Tera type (and Tera Blast) are considered STAB + const checkTera = this.getTeraType() !== Type.UNKNOWN && !this.isOfType(this.getTeraType()); // Whether the mon has an off-type Tera. If so, moves matching Tera type (and Tera Blast) are considered STAB and boosted slightly // Trainers and bosses always force a stab move if (this.hasTrainer() || this.isBoss()) { - const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && (this.isOfType(allMoves[m[0]].type) || (checkTera && (m[0] === Moves.TERA_BLAST || this.getTeraType() === allMoves[m[0]].type)))); + const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && (this.isOfType(allMoves[m[0]].type) || (checkTera && (m[0] === Moves.TERA_BLAST || this.getTeraType() === allMoves[m[0]].type)))).map(m => [ + m[0], + Math.round(m[1] * ((checkTera && (m[0] === Moves.TERA_BLAST || this.getTeraType() === allMoves[m[0]].type)) ? 1.5 : 1)) + ]); if (stabMovePool.length) { const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0); @@ -4812,6 +4815,9 @@ export class EnemyPokemon extends Pokemon { } if (!dataSource) { + if (this.hasTrainer() && globalScene.currentBattle.trainer && globalScene.currentBattle.trainer.config.specialtyType > Type.UNKNOWN) { + this.teraType = globalScene.currentBattle.trainer.config.specialtyType; + } this.generateAndPopulateMoveset(); if (shinyLock || Overrides.OPP_SHINY_OVERRIDE === false) { @@ -4847,9 +4853,6 @@ export class EnemyPokemon extends Pokemon { this.aiType = boss || this.hasTrainer() ? AiType.SMART : AiType.SMART_RANDOM; - if (this.hasTrainer() && globalScene.currentBattle.trainer && globalScene.currentBattle.trainer.config.specialtyType > Type.UNKNOWN) { - this.teraType = globalScene.currentBattle.trainer.config.specialtyType; - } } initBattleInfo(): void { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index f7f480a52d0..10991226dba 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -24,6 +24,7 @@ import i18next from "i18next"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { Species } from "#enums/species"; import { TrainerType } from "#enums/trainer-type"; +import { Type } from "#enums/type"; export enum TrainerVariant { DEFAULT, @@ -38,6 +39,7 @@ export default class Trainer extends Phaser.GameObjects.Container { public name: string; public partnerName: string; public originalIndexes: { [key: number]: number } = {}; + public teraIndexes: number[] = []; constructor(trainerType: TrainerType, variant: TrainerVariant, partyTemplateIndex?: number, name?: string, partnerName?: string, trainerConfigOverride?: TrainerConfig) { super(globalScene, -72, 80); @@ -68,6 +70,14 @@ export default class Trainer extends Phaser.GameObjects.Container { } } + if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA && globalScene.waveIndex && globalScene.waveIndex >= this.config.minTeraWave) { + this.teraIndexes.push(...this.config.trainerAI.instantTeras.map(i => i < 0 ? this.getPartyTemplate().size + i : i)); + console.log("Tera index %d", this.teraIndexes[0]); + if (this.teraIndexes.length === 0) { + this.teraIndexes.push(Utils.randSeedInt(this.getPartyTemplate().size)); + } + } + switch (this.variant) { case TrainerVariant.FEMALE: if (!this.config.hasGenders) { @@ -379,6 +389,11 @@ export default class Trainer extends Phaser.GameObjects.Container { ret = globalScene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); }, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : globalScene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8)); + if (ret!.species.speciesId === Species.SHEDINJA && this.teraIndexes.includes(index) && this.config.specialtyType !== Type.BUG) { + this.teraIndexes.pop(); + this.teraIndexes.push(index + 1); // If it's Shedinja and it's set to tera to something other than Bug, set tera index to the next mon + } + return ret!; // TODO: is this bang correct? } @@ -665,11 +680,11 @@ export default class Trainer extends Phaser.GameObjects.Container { } shouldTera(pokemon: EnemyPokemon): boolean { - if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA && globalScene.currentBattle.waveIndex >= this.config.minTeraWave) { - if (!pokemon.isTerastallized && (this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex) || this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex - globalScene.getEnemyParty().length))) { - return true; - } - } - return false; + return ( + this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA + && globalScene.currentBattle.waveIndex >= this.config.minTeraWave + && !pokemon.isTerastallized + && this.teraIndexes.includes(pokemon.initialTeamIndex) + ); } }