Merge branch 'beta' into deep-sea-items

This commit is contained in:
Madmadness65 2025-03-03 15:21:52 -06:00 committed by GitHub
commit 7d0ff7d6fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 926 additions and 386 deletions

17
.github/CODEOWNERS vendored
View File

@ -4,4 +4,19 @@
* @pagefaultgames/junior-dev-team * @pagefaultgames/junior-dev-team
# github actions/templates etc. - Dev Leads # github actions/templates etc. - Dev Leads
/.github @pagefaultgames/dev-leads /.github @pagefaultgames/senior-dev-team
# Art Team
/public/**/*.png @pagefaultgames/art-team
/public/**/*.json @pagefaultgames/art-team
/public/images @pagefaultgames/art-team
/public/battle-anims @pagefaultgames/art-team
# Audio files
*.mp3 @pagefaultgames/composer-team
*.wav @pagefaultgames/composer-team
*.ogg @pagefaultgames/composer-team
/public/audio @pagefaultgames/composer-team
# Balance Files; contain actual code logic and must also be owned by dev team
/src/data/balance @pagefaultgames/balance-team @pagefaultgames/junior-dev-team

View File

@ -133,7 +133,7 @@
<span class="apad-label">V</span> <span class="apad-label">V</span>
</div> </div>
<!-- buttons to display battle-specific information --> <!-- buttons to display battle-specific information -->
<div id="apadInfo" class="apad-button apad-rectangle apad-small" data-key="V"> <div id="apadInfo" class="apad-button apad-rectangle apad-small" data-key="CYCLE_TERA">
<span class="apad-label">V</span> <span class="apad-label">V</span>
</div> </div>
<div id="apadStats" class="apad-button apad-rectangle apad-small" data-key="STATS"> <div id="apadStats" class="apad-button apad-rectangle apad-small" data-key="STATS">

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.7.0", "version": "1.7.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.7.0", "version": "1.7.6",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.7.0", "version": "1.7.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 528 B

View File

@ -3,12 +3,12 @@
"838394": "4d7dc5", "838394": "4d7dc5",
"62ace6": "8363af", "62ace6": "8363af",
"7bcdff": "9c75c2", "7bcdff": "9c75c2",
"ffec8c": "ddfff9", "fdea88": "ddfff9",
"a1a1c4": "7ab7ec", "a1a1c4": "7ab7ec",
"c9b241": "97d6e2", "c9b241": "97d6e2",
"dfcf77": "bae7e8", "ccbd70": "bae7e8",
"174592": "37408c", "174592": "37408c",
"fdfdfd": "b1e5ff", "f8f8f8": "b1e5ff",
"9c9cc5": "5385c7", "9c9cc5": "5385c7",
"cdcde6": "7eb7e8", "cdcde6": "7eb7e8",
"396a83": "362864", "396a83": "362864",
@ -18,12 +18,12 @@
"838394": "cc6845", "838394": "cc6845",
"62ace6": "c44848", "62ace6": "c44848",
"7bcdff": "dd6155", "7bcdff": "dd6155",
"ffec8c": "ddfff9", "fdea88": "ddfff9",
"a1a1c4": "f7c685", "a1a1c4": "f7c685",
"c9b241": "97d6e2", "c9b241": "97d6e2",
"dfcf77": "bae7e8", "ccbd70": "bae7e8",
"174592": "198158", "174592": "198158",
"fdfdfd": "fff4bd", "f8f8f8": "fff4bd",
"9c9cc5": "c96a48", "9c9cc5": "c96a48",
"cdcde6": "f7b785", "cdcde6": "f7b785",
"396a83": "5c0d33", "396a83": "5c0d33",

View File

@ -1,17 +1,17 @@
{ {
"1": { "1": {
"838394": "4d7dc5", "848496": "4d7dc5",
"7bcdff": "9c75c2", "7bcdff": "9c75c2",
"62ace6": "8363af", "62ace6": "8363af",
"ffffff": "b1e5ff", "ffffff": "b1e5ff",
"396a83": "362864", "396a83": "362864",
"9c9cc5": "5385c7", "9c9cc5": "5385c7",
"cdcde6": "7eb7e8", "cdcde6": "7eb7e8",
"174592": "198158", "174592": "37408c",
"5a94cd": "7054a4" "5a94cd": "7054a4"
}, },
"2": { "2": {
"838394": "cc6845", "848496": "cc6845",
"7bcdff": "dd6155", "7bcdff": "dd6155",
"62ace6": "c44848", "62ace6": "c44848",
"ffffff": "fff4bd", "ffffff": "fff4bd",

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

@ -1 +1 @@
Subproject commit ef43efffe5fe454862c350f1b9393c3ad755bcc2 Subproject commit 0e5c6096ba26f6b87aed1aab3fe9b0b23f6cbb7b

View File

@ -1865,6 +1865,58 @@ export default class BattleScene extends SceneBase {
this.getCurrentPhase()?.constructor.name ?? "", this.getCurrentPhase()?.constructor.name ?? "",
); );
if ( // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
!isEggPhase &&
this.currentBattle?.battleType === BattleType.TRAINER && !isNullOrUndefined(this.currentBattle.trainer) &&
this.currentBattle.trainer.config.hasSpecialtyType()
) {
if (species.speciesId === Species.WORMADAM) {
switch (this.currentBattle.trainer.config.specialtyType) {
case Type.GROUND:
return 1; // Sandy Cloak
case Type.STEEL:
return 2; // Trash Cloak
case Type.GRASS:
return 0; // Plant Cloak
}
} else if (species.speciesId === Species.ROTOM) {
switch (this.currentBattle.trainer.config.specialtyType) {
case Type.FLYING:
return 4; // Fan Rotom
case Type.GHOST:
return 0; // Lightbulb Rotom
case Type.FIRE:
return 1; // Heat Rotom
case Type.GRASS:
return 5; // Mow Rotom
case Type.WATER:
return 2; // Wash Rotom
case Type.ICE:
return 3; // Frost Rotom
}
} else if (species.speciesId === Species.ORICORIO) {
switch (this.currentBattle.trainer.config.specialtyType) {
case Type.GHOST:
return 3; // Sensu Style
case Type.FIRE:
return 0; // Baile Style
case Type.ELECTRIC:
return 1; // Pom-Pom Style
case Type.PSYCHIC:
return 2; // Pa'u Style
}
} else if (species.speciesId === Species.PALDEA_TAUROS) {
switch (this.currentBattle.trainer.config.specialtyType) {
case Type.FIRE:
return 1; // Blaze Breed
case Type.WATER:
return 2; // Aqua Breed
}
} else if (species.speciesId === Species.SILVALLY || species.speciesId === Species.ARCEUS) { // Would probably never happen, but might as well
return this.currentBattle.trainer.config.specialtyType;
}
}
switch (species.speciesId) { switch (species.speciesId) {
case Species.UNOWN: case Species.UNOWN:
case Species.SHELLOS: case Species.SHELLOS:
@ -1872,8 +1924,6 @@ export default class BattleScene extends SceneBase {
case Species.BASCULIN: case Species.BASCULIN:
case Species.DEERLING: case Species.DEERLING:
case Species.SAWSBUCK: case Species.SAWSBUCK:
case Species.FROAKIE:
case Species.FROGADIER:
case Species.SCATTERBUG: case Species.SCATTERBUG:
case Species.SPEWPA: case Species.SPEWPA:
case Species.VIVILLON: case Species.VIVILLON:
@ -1907,9 +1957,14 @@ export default class BattleScene extends SceneBase {
return 0; // No Partner Eevee for Wave 12 Preschoolers return 0; // No Partner Eevee for Wave 12 Preschoolers
} }
return Utils.randSeedInt(2); return Utils.randSeedInt(2);
case Species.FROAKIE:
case Species.FROGADIER:
case Species.GRENINJA: case Species.GRENINJA:
if (this.currentBattle?.battleType === BattleType.TRAINER) { if (
return 0; // Don't give trainers Battle Bond Greninja this.currentBattle?.battleType === BattleType.TRAINER &&
!isEggPhase
) {
return 0; // Don't give trainers Battle Bond Greninja, Froakie or Frogadier
} }
return Utils.randSeedInt(2); return Utils.randSeedInt(2);
case Species.URSHIFU: case Species.URSHIFU:
@ -3448,8 +3503,6 @@ export default class BattleScene extends SceneBase {
for (const modifier of modifiers) { for (const modifier of modifiers) {
this.addEnemyModifier(modifier, true, true); this.addEnemyModifier(modifier, true, true);
} }
this.currentBattle.trainer.genAI(party);
} }
party.forEach((enemyPokemon: EnemyPokemon, i: number) => { party.forEach((enemyPokemon: EnemyPokemon, i: number) => {

View File

@ -12,7 +12,7 @@ import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffect
import { Gender } from "./gender"; import { Gender } from "./gender";
import type Move from "./move"; import type Move from "./move";
import { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move"; import { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
import type { ArenaTrapTag } from "./arena-tag"; import type { ArenaTrapTag, SuppressAbilitiesTag } from "./arena-tag";
import { ArenaTagSide } from "./arena-tag"; import { ArenaTagSide } from "./arena-tag";
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "../modifier/modifier"; import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "../modifier/modifier";
import { TerrainType } from "./terrain"; import { TerrainType } from "./terrain";
@ -2197,6 +2197,34 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr {
} }
} }
/**
* Generic class to add an arena tag upon switching in
*/
export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr {
private readonly tagType: ArenaTagType;
private readonly turnCount: number;
private readonly side?: ArenaTagSide;
private readonly quiet?: boolean;
private sourceId: number;
constructor(tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) {
super(false);
this.tagType = tagType;
this.turnCount = turnCount;
this.side = side;
this.quiet = quiet;
}
public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
this.sourceId = pokemon.id;
if (!simulated) {
globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet);
}
return true;
}
}
export class PostSummonMessageAbAttr extends PostSummonAbAttr { export class PostSummonMessageAbAttr extends PostSummonAbAttr {
private messageFunc: (pokemon: Pokemon) => string; private messageFunc: (pokemon: Pokemon) => string;
@ -2941,6 +2969,26 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr {
} }
} }
/**
* Updates the active {@linkcode SuppressAbilitiesTag} when a pokemon with {@linkcode Abilities.NEUTRALIZING_GAS} leaves the field
*/
export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFieldAbAttr {
constructor() {
super(false);
}
public override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
if (!simulated) {
const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
if (suppressTag) {
suppressTag.onSourceLeave(globalScene.arena);
return true;
}
}
return simulated;
}
}
export class PreStatStageChangeAbAttr extends AbAttr { export class PreStatStageChangeAbAttr extends AbAttr {
applyPreStatStageChange( applyPreStatStageChange(
pokemon: Pokemon | null, pokemon: Pokemon | null,
@ -4692,21 +4740,6 @@ export class MoveAbilityBypassAbAttr extends AbAttr {
} }
} }
export class SuppressFieldAbilitiesAbAttr extends AbAttr {
constructor() {
super(false);
}
apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ability = (args[0] as Ability);
if (!ability.hasAttr(UnsuppressableAbilityAbAttr) && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) {
cancelled.value = true;
return true;
}
return false;
}
}
export class AlwaysHitAbAttr extends AbAttr { } export class AlwaysHitAbAttr extends AbAttr { }
/** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */ /** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */
@ -5021,7 +5054,7 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr {
const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()];
const isCommandFight = turnCommand?.command === Command.FIGHT; const isCommandFight = turnCommand?.command === Command.FIGHT;
const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null;
if (this.condition(pokemon, move!) && isCommandFight) { if (isCommandFight && this.condition(pokemon, move!)) {
bypassSpeed.value = false; bypassSpeed.value = false;
canCheckHeldItems.value = false; canCheckHeldItems.value = false;
return false; return false;
@ -5119,6 +5152,10 @@ function applySingleAbAttrs<TAttr extends AbAttr>(
showAbilityInstant: boolean = false, showAbilityInstant: boolean = false,
messages: string[] = [] messages: string[] = []
) { ) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) {
return;
}
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) { if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) {
return; return;
@ -5412,12 +5449,10 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(
gainedMidTurn: boolean = false gainedMidTurn: boolean = false
) { ) {
for (const passive of [ false, true ]) { for (const passive of [ false, true ]) {
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) { if (pokemon) {
continue; applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
globalScene.clearPhaseQueueSplice();
} }
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
globalScene.clearPhaseQueueSplice();
} }
} }
@ -5946,10 +5981,10 @@ export function applyOnGainAbAttrs(pokemon: Pokemon, passive: boolean = false, s
} }
/** /**
* Clears primal weather during the turn if {@linkcode pokemon}'s ability corresponds to one * Clears primal weather/neutralizing gas during the turn if {@linkcode pokemon}'s ability corresponds to one
*/ */
export function applyOnLoseClearWeatherAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void { export function applyOnLoseAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void {
applySingleAbAttrs<PreLeaveFieldClearWeatherAbAttr>(pokemon, passive, PreLeaveFieldClearWeatherAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated); applySingleAbAttrs<PreLeaveFieldAbAttr>(pokemon, passive, PreLeaveFieldAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated);
} }
function queueShowAbility(pokemon: Pokemon, passive: boolean): void { function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive));
@ -6838,12 +6873,12 @@ export function initAbilities() {
new Ability(Abilities.GORILLA_TACTICS, 8) new Ability(Abilities.GORILLA_TACTICS, 8)
.attr(GorillaTacticsAbAttr), .attr(GorillaTacticsAbAttr),
new Ability(Abilities.NEUTRALIZING_GAS, 8) new Ability(Abilities.NEUTRALIZING_GAS, 8)
.attr(SuppressFieldAbilitiesAbAttr) .attr(PostSummonAddArenaTagAbAttr, ArenaTagType.NEUTRALIZING_GAS, 0)
.attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr)
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .bypassFaint(),
.partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed
new Ability(Abilities.PASTEL_VEIL, 8) new Ability(Abilities.PASTEL_VEIL, 8)
.attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
.attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)

View File

@ -8,7 +8,7 @@ import type Pokemon from "#app/field/pokemon";
import { HitResult, PokemonMove } from "#app/field/pokemon"; import { HitResult, PokemonMove } from "#app/field/pokemon";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import type { BattlerIndex } from "#app/battle"; import type { BattlerIndex } from "#app/battle";
import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability"; import { BlockNonDirectDamageAbAttr, InfiltratorAbAttr, PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, ProtectStatAbAttr, applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs } from "#app/data/ability";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims";
import i18next from "i18next"; import i18next from "i18next";
@ -1221,6 +1221,76 @@ export class FairyLockTag extends ArenaTag {
} }
/**
* Arena tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Neutralizing_Gas_(Ability) Neutralizing Gas}
*
* Keeps track of the number of pokemon on the field with Neutralizing Gas - If it drops to zero, the effect is ended and abilities are reactivated
*
* Additionally ends onLose abilities when it is activated
*/
export class SuppressAbilitiesTag extends ArenaTag {
private sourceCount: number;
private beingRemoved: boolean;
constructor(sourceId: number) {
super(ArenaTagType.NEUTRALIZING_GAS, 0, undefined, sourceId);
this.sourceCount = 1;
this.beingRemoved = false;
}
public override onAdd(arena: Arena): void {
const pokemon = this.getSourcePokemon();
if (pokemon) {
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
for (const fieldPokemon of globalScene.getField()) {
if (fieldPokemon && fieldPokemon.id !== pokemon.id) {
[ true, false ].forEach((passive) => applyOnLoseAbAttrs(fieldPokemon, passive));
}
}
}
}
public override onOverlap(arena: Arena): void {
this.sourceCount++;
}
public onSourceLeave(arena: Arena): void {
this.sourceCount--;
if (this.sourceCount <= 0) {
arena.removeTag(ArenaTagType.NEUTRALIZING_GAS);
} else if (this.sourceCount === 1) {
// With 1 source left, that pokemon's other abilities should reactivate
// This may be confusing for players but would be the most accurate gameplay-wise
// Could have a custom message that plays when a specific pokemon's NG ends? This entire thing exists due to passives after all
const setter = globalScene.getField().filter((p) => p && p.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false))[0];
applyOnGainAbAttrs(setter, setter.getAbility().hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr));
}
}
public override onRemove(arena: Arena, quiet: boolean = false) {
this.beingRemoved = true;
if (!quiet) {
globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove"));
}
for (const pokemon of globalScene.getField()) {
// There is only one pokemon with this attr on the field on removal, so its abilities are already active
if (pokemon && !pokemon.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false)) {
[ true, false ].forEach((passive) => applyOnGainAbAttrs(pokemon, passive));
}
}
}
public shouldApplyToSelf(): boolean {
return this.sourceCount > 1;
}
public isBeingRemoved() {
return this.beingRemoved;
}
}
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter // TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
switch (tagType) { switch (tagType) {
@ -1281,6 +1351,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove
return new GrassWaterPledgeTag(sourceId, side); return new GrassWaterPledgeTag(sourceId, side);
case ArenaTagType.FAIRY_LOCK: case ArenaTagType.FAIRY_LOCK:
return new FairyLockTag(turnCount, sourceId); return new FairyLockTag(turnCount, sourceId);
case ArenaTagType.NEUTRALIZING_GAS:
return new SuppressAbilitiesTag(sourceId);
default: default:
return null; return null;
} }

View File

@ -46,7 +46,7 @@ export const speciesEggMoves = {
[Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ], [Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ],
[Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.DRAGON_HAMMER, Moves.IVY_CUDGEL, Moves.JET_PUNCH ], [Species.KRABBY]: [ Moves.DIRE_CLAW, Moves.DRAGON_HAMMER, Moves.IVY_CUDGEL, Moves.JET_PUNCH ],
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.FUSION_FLARE, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ], [Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.FUSION_FLARE, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ], [Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.HEAT_WAVE, Moves.LUMINA_CRASH ],
[Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ], [Species.CUBONE]: [ Moves.HEAD_SMASH, Moves.WOOD_HAMMER, Moves.SHADOW_SNEAK, Moves.BITTER_BLADE ],
[Species.LICKITUNG]: [ Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.SLACK_OFF, Moves.MAGICAL_TORQUE ], [Species.LICKITUNG]: [ Moves.CRUSH_GRIP, Moves.FIRE_LASH, Moves.SLACK_OFF, Moves.MAGICAL_TORQUE ],
[Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ], [Species.KOFFING]: [ Moves.SCALD, Moves.RECOVER, Moves.BODY_PRESS, Moves.MALIGNANT_CHAIN ],
@ -59,7 +59,7 @@ export const speciesEggMoves = {
[Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.GEAR_GRIND, Moves.STORM_THROW, Moves.BITTER_BLADE ], [Species.SCYTHER]: [ Moves.MIGHTY_CLEAVE, Moves.GEAR_GRIND, Moves.STORM_THROW, Moves.BITTER_BLADE ],
[Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ], [Species.PINSIR]: [ Moves.HEADLONG_RUSH, Moves.LEECH_LIFE, Moves.CRUSH_GRIP, Moves.EXTREME_SPEED ],
[Species.TAUROS]: [ Moves.SWORDS_DANCE, Moves.FIRE_LASH, Moves.WICKED_TORQUE, Moves.COLLISION_COURSE ], [Species.TAUROS]: [ Moves.SWORDS_DANCE, Moves.FIRE_LASH, Moves.WICKED_TORQUE, Moves.COLLISION_COURSE ],
[Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.DRAGON_ASCENT, Moves.SURGING_STRIKES ], [Species.MAGIKARP]: [ Moves.FLIP_TURN, Moves.ICE_SPINNER, Moves.KNOCK_OFF, Moves.DRAGON_ASCENT ],
[Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SCALD, Moves.SHELL_SMASH ], [Species.LAPRAS]: [ Moves.RECOVER, Moves.FREEZE_DRY, Moves.SCALD, Moves.SHELL_SMASH ],
[Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ], [Species.DITTO]: [ Moves.MIMIC, Moves.SKETCH, Moves.METRONOME, Moves.IMPRISON ],
[Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ], [Species.EEVEE]: [ Moves.WISH, Moves.NO_RETREAT, Moves.ZIPPY_ZAP, Moves.BOOMBURST ],
@ -76,7 +76,7 @@ export const speciesEggMoves = {
[Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ], [Species.CHIKORITA]: [ Moves.SAPPY_SEED, Moves.STONE_AXE, Moves.DRAGON_DANCE, Moves.SPORE ],
[Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ], [Species.CYNDAQUIL]: [ Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.FIERY_DANCE, Moves.ELECTRO_DRIFT ],
[Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.TRIPLE_AXEL, Moves.SURGING_STRIKES ], [Species.TOTODILE]: [ Moves.THUNDER_PUNCH, Moves.DRAGON_DANCE, Moves.PLAY_ROUGH, Moves.SURGING_STRIKES ],
[Species.SENTRET]: [ Moves.TIDY_UP, Moves.FAKE_OUT, Moves.NUZZLE, Moves.EXTREME_SPEED ], [Species.SENTRET]: [ Moves.TIDY_UP, Moves.FAKE_OUT, Moves.NUZZLE, Moves.EXTREME_SPEED ],
[Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ], [Species.HOOTHOOT]: [ Moves.CALM_MIND, Moves.ESPER_WING, Moves.AEROBLAST, Moves.BOOMBURST ],
[Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.MAT_BLOCK, Moves.PARTING_SHOT, Moves.SPORE ], [Species.LEDYBA]: [ Moves.POLLEN_PUFF, Moves.MAT_BLOCK, Moves.PARTING_SHOT, Moves.SPORE ],
@ -159,7 +159,7 @@ export const speciesEggMoves = {
[Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.STICKY_WEB, Moves.DECORATE, Moves.VICTORY_DANCE ], [Species.VOLBEAT]: [ Moves.BATON_PASS, Moves.STICKY_WEB, Moves.DECORATE, Moves.VICTORY_DANCE ],
[Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ], [Species.ILLUMISE]: [ Moves.PARTING_SHOT, Moves.GLITZY_GLOW, Moves.POWDER, Moves.QUIVER_DANCE ],
[Species.GULPIN]: [ Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.STRENGTH_SAP ], [Species.GULPIN]: [ Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.STRENGTH_SAP ],
[Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.SWORDS_DANCE, Moves.OBSTRUCT, Moves.SURGING_STRIKES ], [Species.CARVANHA]: [ Moves.THUNDER_FANG, Moves.GUNK_SHOT, Moves.OBSTRUCT, Moves.SURGING_STRIKES ],
[Species.WAILMER]: [ Moves.TAKE_HEART, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ], [Species.WAILMER]: [ Moves.TAKE_HEART, Moves.COMEUPPANCE, Moves.SLACK_OFF, Moves.STEAM_ERUPTION ],
[Species.NUMEL]: [ Moves.TRICK_ROOM, Moves.ENERGY_BALL, Moves.SLACK_OFF, Moves.BLUE_FLARE ], [Species.NUMEL]: [ Moves.TRICK_ROOM, Moves.ENERGY_BALL, Moves.SLACK_OFF, Moves.BLUE_FLARE ],
[Species.TORKOAL]: [ Moves.MORNING_SUN, Moves.BURNING_BULWARK, Moves.BODY_PRESS, Moves.HYDRO_STEAM ], [Species.TORKOAL]: [ Moves.MORNING_SUN, Moves.BURNING_BULWARK, Moves.BODY_PRESS, Moves.HYDRO_STEAM ],
@ -235,7 +235,7 @@ export const speciesEggMoves = {
[Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.SUNSTEEL_STRIKE ], [Species.RIOLU]: [ Moves.THUNDEROUS_KICK, Moves.TACHYON_CUTTER, Moves.TRIPLE_AXEL, Moves.SUNSTEEL_STRIKE ],
[Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ], [Species.HIPPOPOTAS]: [ Moves.SHORE_UP, Moves.STONE_AXE, Moves.BULK_UP, Moves.SALT_CURE ],
[Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ], [Species.SKORUPI]: [ Moves.COIL, Moves.DIRE_CLAW, Moves.CRABHAMMER, Moves.WICKED_BLOW ],
[Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.ICE_SPINNER, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ], [Species.CROAGUNK]: [ Moves.DIRE_CLAW, Moves.TRIPLE_AXEL, Moves.THUNDEROUS_KICK, Moves.VICTORY_DANCE ],
[Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ], [Species.CARNIVINE]: [ Moves.STRENGTH_SAP, Moves.FIRE_LASH, Moves.COIL, Moves.SAPPY_SEED ],
[Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.SPLISHY_SPLASH, Moves.FREEZE_DRY, Moves.OBLIVION_WING ], [Species.FINNEON]: [ Moves.QUIVER_DANCE, Moves.SPLISHY_SPLASH, Moves.FREEZE_DRY, Moves.OBLIVION_WING ],
[Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], [Species.MANTYKE]: [ Moves.SPLISHY_SPLASH, Moves.FREEZY_FROST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
@ -267,7 +267,7 @@ export const speciesEggMoves = {
[Species.PANSEAR]: [ Moves.NASTY_PLOT, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.ERUPTION ], [Species.PANSEAR]: [ Moves.NASTY_PLOT, Moves.HYDRO_STEAM, Moves.EARTH_POWER, Moves.ERUPTION ],
[Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.EARTH_POWER, Moves.WATER_SPOUT ], [Species.PANPOUR]: [ Moves.NASTY_PLOT, Moves.ENERGY_BALL, Moves.EARTH_POWER, Moves.WATER_SPOUT ],
[Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.LUNAR_BLESSING, Moves.MYSTICAL_POWER ], [Species.MUNNA]: [ Moves.COSMIC_POWER, Moves.AURA_SPHERE, Moves.LUNAR_BLESSING, Moves.MYSTICAL_POWER ],
[Species.PIDOVE]: [ Moves.GUNK_SHOT, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ], [Species.PIDOVE]: [ Moves.SLASH, Moves.TIDY_UP, Moves.FLOATY_FALL, Moves.TRIPLE_ARROWS ],
[Species.BLITZLE]: [ Moves.HORN_LEECH, Moves.SWORDS_DANCE, Moves.FLARE_BLITZ, Moves.BOLT_STRIKE ], [Species.BLITZLE]: [ Moves.HORN_LEECH, Moves.SWORDS_DANCE, Moves.FLARE_BLITZ, Moves.BOLT_STRIKE ],
[Species.ROGGENROLA]: [ Moves.BODY_PRESS, Moves.CURSE, Moves.SHORE_UP, Moves.DIAMOND_STORM ], [Species.ROGGENROLA]: [ Moves.BODY_PRESS, Moves.CURSE, Moves.SHORE_UP, Moves.DIAMOND_STORM ],
[Species.WOOBAT]: [ Moves.ESPER_WING, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ], [Species.WOOBAT]: [ Moves.ESPER_WING, Moves.STORED_POWER, Moves.MYSTICAL_FIRE, Moves.OBLIVION_WING ],
@ -282,7 +282,7 @@ export const speciesEggMoves = {
[Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ], [Species.COTTONEE]: [ Moves.POLLEN_PUFF, Moves.PARTING_SHOT, Moves.SLEEP_POWDER, Moves.SEED_FLARE ],
[Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ], [Species.PETILIL]: [ Moves.THUNDEROUS_KICK, Moves.SPARKLING_ARIA, Moves.FIERY_DANCE, Moves.FLOWER_TRICK ],
[Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ], [Species.BASCULIN]: [ Moves.LAST_RESPECTS, Moves.CLOSE_COMBAT, Moves.SPLISHY_SPLASH, Moves.NO_RETREAT ],
[Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.HEADLONG_RUSH, Moves.FIRE_LASH, Moves.WICKED_BLOW ], [Species.SANDILE]: [ Moves.DIRE_CLAW, Moves.SUCKER_PUNCH, Moves.FIRE_LASH, Moves.HEADLONG_RUSH ],
[Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.HEADLONG_RUSH, Moves.PYRO_BALL ], [Species.DARUMAKA]: [ Moves.DRAIN_PUNCH, Moves.ZIPPY_ZAP, Moves.HEADLONG_RUSH, Moves.PYRO_BALL ],
[Species.MARACTUS]: [ Moves.EARTH_POWER, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ], [Species.MARACTUS]: [ Moves.EARTH_POWER, Moves.QUIVER_DANCE, Moves.FIERY_DANCE, Moves.SEED_FLARE ],
[Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ], [Species.DWEBBLE]: [ Moves.CRABHAMMER, Moves.STONE_AXE, Moves.LEECH_LIFE, Moves.MIGHTY_CLEAVE ],
@ -327,8 +327,8 @@ export const speciesEggMoves = {
[Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ], [Species.DEINO]: [ Moves.FIERY_WRATH, Moves.ESPER_WING, Moves.SLUDGE_BOMB, Moves.FICKLE_BEAM ],
[Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.DAZZLING_GLEAM, Moves.EARTH_POWER, Moves.HYDRO_STEAM ], [Species.LARVESTA]: [ Moves.THUNDERBOLT, Moves.DAZZLING_GLEAM, Moves.EARTH_POWER, Moves.HYDRO_STEAM ],
[Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.COBALION]: [ Moves.BEHEMOTH_BLADE, Moves.MIGHTY_CLEAVE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ],
[Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.TERRAKION]: [ Moves.MIGHTY_CLEAVE, Moves.HEADLONG_RUSH, Moves.KNOCK_OFF, Moves.VICTORY_DANCE ],
[Species.VIRIZION]: [ Moves.SAPPY_SEED, Moves.PSYBLADE, Moves.CEASELESS_EDGE, Moves.VICTORY_DANCE ], [Species.VIRIZION]: [ Moves.SAPPY_SEED, Moves.PSYBLADE, Moves.STONE_AXE, Moves.VICTORY_DANCE ],
[Species.TORNADUS]: [ Moves.SANDSEAR_STORM, Moves.PARTING_SHOT, Moves.SPLISHY_SPLASH, Moves.OBLIVION_WING ], [Species.TORNADUS]: [ Moves.SANDSEAR_STORM, Moves.PARTING_SHOT, Moves.SPLISHY_SPLASH, Moves.OBLIVION_WING ],
[Species.THUNDURUS]: [ Moves.SANDSEAR_STORM, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ], [Species.THUNDURUS]: [ Moves.SANDSEAR_STORM, Moves.HURRICANE, Moves.FROST_BREATH, Moves.ELECTRO_SHOT ],
[Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ], [Species.RESHIRAM]: [ Moves.ENERGY_BALL, Moves.TAKE_HEART, Moves.FICKLE_BEAM, Moves.ERUPTION ],
@ -342,7 +342,7 @@ export const speciesEggMoves = {
[Species.CHESPIN]: [ Moves.COMBAT_TORQUE, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ], [Species.CHESPIN]: [ Moves.COMBAT_TORQUE, Moves.SYNTHESIS, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED ],
[Species.FENNEKIN]: [ Moves.TWIN_BEAM, Moves.FIERY_DANCE, Moves.THUNDERBOLT, Moves.SPARKLY_SWIRL ], [Species.FENNEKIN]: [ Moves.TWIN_BEAM, Moves.FIERY_DANCE, Moves.THUNDERBOLT, Moves.SPARKLY_SWIRL ],
[Species.FROAKIE]: [ Moves.MOONBLAST, Moves.SHELL_SIDE_ARM, Moves.FIERY_WRATH, Moves.STEAM_ERUPTION ], [Species.FROAKIE]: [ Moves.MOONBLAST, Moves.SHELL_SIDE_ARM, Moves.FIERY_WRATH, Moves.STEAM_ERUPTION ],
[Species.BUNNELBY]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.FACADE, Moves.EXTREME_SPEED ], [Species.BUNNELBY]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.LANDS_WRATH, Moves.EXTREME_SPEED ],
[Species.FLETCHLING]: [ Moves.DRILL_RUN, Moves.BURNING_BULWARK, Moves.HEAD_SMASH, Moves.VOLT_TACKLE ], [Species.FLETCHLING]: [ Moves.DRILL_RUN, Moves.BURNING_BULWARK, Moves.HEAD_SMASH, Moves.VOLT_TACKLE ],
[Species.SCATTERBUG]: [ Moves.FOCUS_BLAST, Moves.AFTER_YOU, Moves.DECORATE, Moves.BLIZZARD ], [Species.SCATTERBUG]: [ Moves.FOCUS_BLAST, Moves.AFTER_YOU, Moves.DECORATE, Moves.BLIZZARD ],
[Species.LITLEO]: [ Moves.EARTH_POWER, Moves.NASTY_PLOT, Moves.BURNING_BULWARK, Moves.BLUE_FLARE ], [Species.LITLEO]: [ Moves.EARTH_POWER, Moves.NASTY_PLOT, Moves.BURNING_BULWARK, Moves.BLUE_FLARE ],
@ -372,7 +372,7 @@ export const speciesEggMoves = {
[Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ], [Species.NOIBAT]: [ Moves.AEROBLAST, Moves.OVERDRIVE, Moves.NASTY_PLOT, Moves.CLANGING_SCALES ],
[Species.XERNEAS]: [ Moves.EARTH_POWER, Moves.SPRINGTIDE_STORM, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ], [Species.XERNEAS]: [ Moves.EARTH_POWER, Moves.SPRINGTIDE_STORM, Moves.STRENGTH_SAP, Moves.TAIL_GLOW ],
[Species.YVELTAL]: [ Moves.SLUDGE_WAVE, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ], [Species.YVELTAL]: [ Moves.SLUDGE_WAVE, Moves.POWER_TRIP, Moves.FIERY_WRATH, Moves.CLANGOROUS_SOUL ],
[Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.HEAL_ORDER, Moves.CLANGOROUS_SOUL, Moves.DOUBLE_IRON_BASH ], [Species.ZYGARDE]: [ Moves.DRAGON_DARTS, Moves.V_CREATE, Moves.CLANGOROUS_SOUL, Moves.HEAL_ORDER ],
[Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.FIERY_DANCE, Moves.SHORE_UP, Moves.GEOMANCY ], [Species.DIANCIE]: [ Moves.MAGICAL_TORQUE, Moves.FIERY_DANCE, Moves.SHORE_UP, Moves.GEOMANCY ],
[Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ], [Species.HOOPA]: [ Moves.PHOTON_GEYSER, Moves.SECRET_SWORD, Moves.FIERY_WRATH, Moves.SHELL_SMASH ],
[Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.SEARING_SHOT, Moves.THUNDERCLAP ], [Species.VOLCANION]: [ Moves.HYDRO_STEAM, Moves.CALM_MIND, Moves.SEARING_SHOT, Moves.THUNDERCLAP ],
@ -415,7 +415,7 @@ export const speciesEggMoves = {
[Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ], [Species.JANGMO_O]: [ Moves.BODY_PRESS, Moves.SHELL_SIDE_ARM, Moves.SECRET_SWORD, Moves.GLAIVE_RUSH ],
[Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.SWORDS_DANCE, Moves.BOLT_STRIKE ], [Species.TAPU_KOKO]: [ Moves.MAGICAL_TORQUE, Moves.TRIPLE_AXEL, Moves.SWORDS_DANCE, Moves.BOLT_STRIKE ],
[Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ], [Species.TAPU_LELE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.HEAT_WAVE, Moves.EXPANDING_FORCE ],
[Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.DRAIN_PUNCH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ], [Species.TAPU_BULU]: [ Moves.SAPPY_SEED, Moves.LANDS_WRATH, Moves.MAGICAL_TORQUE, Moves.VICTORY_DANCE ],
[Species.TAPU_FINI]: [ Moves.SPRINGTIDE_STORM, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ], [Species.TAPU_FINI]: [ Moves.SPRINGTIDE_STORM, Moves.EARTH_POWER, Moves.RECOVER, Moves.QUIVER_DANCE ],
[Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ], [Species.COSMOG]: [ Moves.PHOTON_GEYSER, Moves.PRECIPICE_BLADES, Moves.SACRED_FIRE, Moves.ASTRAL_BARRAGE ],
[Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ], [Species.NIHILEGO]: [ Moves.STRENGTH_SAP, Moves.MALIGNANT_CHAIN, Moves.EARTH_POWER, Moves.QUIVER_DANCE ],
@ -505,7 +505,7 @@ export const speciesEggMoves = {
[Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ], [Species.HISUI_VOLTORB]: [ Moves.FROST_BREATH, Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.ELECTRO_DRIFT ],
[Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.FISHIOUS_REND ], [Species.HISUI_QWILFISH]: [ Moves.CEASELESS_EDGE, Moves.KNOCK_OFF, Moves.RECOVER, Moves.FISHIOUS_REND ],
[Species.HISUI_SNEASEL]: [ Moves.DRAIN_PUNCH, Moves.KNOCK_OFF, Moves.PSYCHIC_FANGS, Moves.TRIPLE_AXEL ], [Species.HISUI_SNEASEL]: [ Moves.DRAIN_PUNCH, Moves.KNOCK_OFF, Moves.PSYCHIC_FANGS, Moves.TRIPLE_AXEL ],
[Species.HISUI_ZORUA]: [ Moves.MOONBLAST, Moves.HYPER_VOICE, Moves.PARTING_SHOT, Moves.BLOOD_MOON ], [Species.HISUI_ZORUA]: [ Moves.MOONBLAST, Moves.SECRET_SWORD, Moves.PARTING_SHOT, Moves.BLOOD_MOON ],
[Species.SPRIGATITO]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ], [Species.SPRIGATITO]: [ Moves.FIRE_LASH, Moves.TRIPLE_AXEL, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ],
[Species.FUECOCO]: [ Moves.ALLURING_VOICE, Moves.SLACK_OFF, Moves.OVERDRIVE, Moves.MOONGEIST_BEAM ], [Species.FUECOCO]: [ Moves.ALLURING_VOICE, Moves.SLACK_OFF, Moves.OVERDRIVE, Moves.MOONGEIST_BEAM ],
@ -538,7 +538,7 @@ export const speciesEggMoves = {
[Species.CYCLIZAR]: [ Moves.PARTING_SHOT, Moves.FIRE_LASH, Moves.MAGICAL_TORQUE, Moves.GLAIVE_RUSH ], [Species.CYCLIZAR]: [ Moves.PARTING_SHOT, Moves.FIRE_LASH, Moves.MAGICAL_TORQUE, Moves.GLAIVE_RUSH ],
[Species.ORTHWORM]: [ Moves.SIZZLY_SLIDE, Moves.COIL, Moves.BODY_PRESS, Moves.SHORE_UP ], [Species.ORTHWORM]: [ Moves.SIZZLY_SLIDE, Moves.COIL, Moves.BODY_PRESS, Moves.SHORE_UP ],
[Species.GLIMMET]: [ Moves.CALM_MIND, Moves.GIGA_DRAIN, Moves.FIERY_DANCE, Moves.MALIGNANT_CHAIN ], [Species.GLIMMET]: [ Moves.CALM_MIND, Moves.GIGA_DRAIN, Moves.FIERY_DANCE, Moves.MALIGNANT_CHAIN ],
[Species.GREAVARD]: [ Moves.SHADOW_BONE, Moves.YAWN, Moves.SHORE_UP, Moves.COLLISION_COURSE ], [Species.GREAVARD]: [ Moves.SHADOW_BONE, Moves.SIZZLY_SLIDE, Moves.SHORE_UP, Moves.COLLISION_COURSE ],
[Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ], [Species.FLAMIGO]: [ Moves.THUNDEROUS_KICK, Moves.TRIPLE_AXEL, Moves.FLOATY_FALL, Moves.VICTORY_DANCE ],
[Species.CETODDLE]: [ Moves.ZING_ZAP, Moves.HIGH_HORSEPOWER, Moves.SLACK_OFF, Moves.DRAGON_DANCE ], [Species.CETODDLE]: [ Moves.ZING_ZAP, Moves.HIGH_HORSEPOWER, Moves.SLACK_OFF, Moves.DRAGON_DANCE ],
[Species.VELUZA]: [ Moves.PSYBLADE, Moves.LEAF_BLADE, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE ], [Species.VELUZA]: [ Moves.PSYBLADE, Moves.LEAF_BLADE, Moves.CEASELESS_EDGE, Moves.BITTER_BLADE ],
@ -550,7 +550,7 @@ export const speciesEggMoves = {
[Species.FLUTTER_MANE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.MOONGEIST_BEAM ], [Species.FLUTTER_MANE]: [ Moves.MOONLIGHT, Moves.NASTY_PLOT, Moves.EARTH_POWER, Moves.MOONGEIST_BEAM ],
[Species.SLITHER_WING]: [ Moves.MIGHTY_CLEAVE, Moves.THUNDEROUS_KICK, Moves.FIRE_LASH, Moves.VICTORY_DANCE ], [Species.SLITHER_WING]: [ Moves.MIGHTY_CLEAVE, Moves.THUNDEROUS_KICK, Moves.FIRE_LASH, Moves.VICTORY_DANCE ],
[Species.SANDY_SHOCKS]: [ Moves.MORNING_SUN, Moves.ICE_BEAM, Moves.NASTY_PLOT, Moves.THUNDERCLAP ], [Species.SANDY_SHOCKS]: [ Moves.MORNING_SUN, Moves.ICE_BEAM, Moves.NASTY_PLOT, Moves.THUNDERCLAP ],
[Species.IRON_TREADS]: [ Moves.FUSION_BOLT, Moves.BULK_UP, Moves.SHORE_UP, Moves.SUNSTEEL_STRIKE ], [Species.IRON_TREADS]: [ Moves.FUSION_BOLT, Moves.SHIFT_GEAR, Moves.SHORE_UP, Moves.SUNSTEEL_STRIKE ],
[Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.SPLISHY_SPLASH, Moves.VOLT_SWITCH, Moves.NASTY_PLOT ], [Species.IRON_BUNDLE]: [ Moves.EARTH_POWER, Moves.SPLISHY_SPLASH, Moves.VOLT_SWITCH, Moves.NASTY_PLOT ],
[Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ], [Species.IRON_HANDS]: [ Moves.DRAIN_PUNCH, Moves.BULK_UP, Moves.PLASMA_FISTS, Moves.ICE_HAMMER ],
[Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ], [Species.IRON_JUGULIS]: [ Moves.FIERY_WRATH, Moves.ROOST, Moves.NASTY_PLOT, Moves.OBLIVION_WING ],
@ -562,7 +562,7 @@ export const speciesEggMoves = {
[Species.CHIEN_PAO]: [ Moves.KNOCK_OFF, Moves.PARTING_SHOT, Moves.TRIPLE_AXEL, Moves.BITTER_BLADE ], [Species.CHIEN_PAO]: [ Moves.KNOCK_OFF, Moves.PARTING_SHOT, Moves.TRIPLE_AXEL, Moves.BITTER_BLADE ],
[Species.TING_LU]: [ Moves.SHORE_UP, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED, Moves.PRECIPICE_BLADES ], [Species.TING_LU]: [ Moves.SHORE_UP, Moves.CEASELESS_EDGE, Moves.SAPPY_SEED, Moves.PRECIPICE_BLADES ],
[Species.CHI_YU]: [ Moves.FIERY_WRATH, Moves.HYDRO_STEAM, Moves.MORNING_SUN, Moves.BLUE_FLARE ], [Species.CHI_YU]: [ Moves.FIERY_WRATH, Moves.HYDRO_STEAM, Moves.MORNING_SUN, Moves.BLUE_FLARE ],
[Species.ROARING_MOON]: [ Moves.FIRE_LASH, Moves.DRAGON_HAMMER, Moves.SUCKER_PUNCH, Moves.WICKED_BLOW ], [Species.ROARING_MOON]: [ Moves.FIRE_LASH, Moves.DRAGON_HAMMER, Moves.METEOR_MASH, Moves.DRAGON_ASCENT ],
[Species.IRON_VALIANT]: [ Moves.PLASMA_FISTS, Moves.NO_RETREAT, Moves.SECRET_SWORD, Moves.MAGICAL_TORQUE ], [Species.IRON_VALIANT]: [ Moves.PLASMA_FISTS, Moves.NO_RETREAT, Moves.SECRET_SWORD, Moves.MAGICAL_TORQUE ],
[Species.KORAIDON]: [ Moves.SUNSTEEL_STRIKE, Moves.SOLAR_BLADE, Moves.DRAGON_DARTS, Moves.BITTER_BLADE ], [Species.KORAIDON]: [ Moves.SUNSTEEL_STRIKE, Moves.SOLAR_BLADE, Moves.DRAGON_DARTS, Moves.BITTER_BLADE ],
[Species.MIRAIDON]: [ Moves.ICE_BEAM, Moves.CLANGOROUS_SOUL, Moves.CORE_ENFORCER, Moves.RISING_VOLTAGE ], [Species.MIRAIDON]: [ Moves.ICE_BEAM, Moves.CLANGOROUS_SOUL, Moves.CORE_ENFORCER, Moves.RISING_VOLTAGE ],
@ -577,7 +577,7 @@ export const speciesEggMoves = {
[Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ], [Species.RAGING_BOLT]: [ Moves.NASTY_PLOT, Moves.FLAMETHROWER, Moves.MORNING_SUN, Moves.ELECTRO_DRIFT ],
[Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ], [Species.IRON_BOULDER]: [ Moves.PSYBLADE, Moves.KOWTOW_CLEAVE, Moves.STONE_AXE, Moves.BITTER_BLADE ],
[Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ], [Species.IRON_CROWN]: [ Moves.NASTY_PLOT, Moves.SECRET_SWORD, Moves.PSYSTRIKE, Moves.ELECTRO_DRIFT ],
[Species.TERAPAGOS]: [ Moves.MOONBLAST, Moves.RECOVER, Moves.ICE_BEAM, Moves.SHELL_SMASH ], [Species.TERAPAGOS]: [ Moves.MOONBLAST, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE, Moves.RECOVER ],
[Species.PECHARUNT]: [ Moves.TAKE_HEART, Moves.BODY_PRESS, Moves.SAPPY_SEED, Moves.ASTRAL_BARRAGE ], [Species.PECHARUNT]: [ Moves.TAKE_HEART, Moves.BODY_PRESS, Moves.SAPPY_SEED, Moves.ASTRAL_BARRAGE ],
[Species.PALDEA_TAUROS]: [ Moves.NO_RETREAT, Moves.BLAZING_TORQUE, Moves.AQUA_STEP, Moves.THUNDEROUS_KICK ], [Species.PALDEA_TAUROS]: [ Moves.NO_RETREAT, Moves.BLAZING_TORQUE, Moves.AQUA_STEP, Moves.THUNDEROUS_KICK ],
[Species.PALDEA_WOOPER]: [ Moves.STONE_AXE, Moves.RECOVER, Moves.BANEFUL_BUNKER, Moves.BARB_BARRAGE ], [Species.PALDEA_WOOPER]: [ Moves.STONE_AXE, Moves.RECOVER, Moves.BANEFUL_BUNKER, Moves.BARB_BARRAGE ],

View File

@ -555,7 +555,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.FLUTTER_MANE]: { 0: Abilities.DAZZLING }, [Species.FLUTTER_MANE]: { 0: Abilities.DAZZLING },
[Species.SLITHER_WING]: { 0: Abilities.SCRAPPY }, [Species.SLITHER_WING]: { 0: Abilities.SCRAPPY },
[Species.SANDY_SHOCKS]: { 0: Abilities.ELECTRIC_SURGE }, [Species.SANDY_SHOCKS]: { 0: Abilities.ELECTRIC_SURGE },
[Species.IRON_TREADS]: { 0: Abilities.STEELY_SPIRIT }, [Species.IRON_TREADS]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.IRON_BUNDLE]: { 0: Abilities.SNOW_WARNING }, [Species.IRON_BUNDLE]: { 0: Abilities.SNOW_WARNING },
[Species.IRON_HANDS]: { 0: Abilities.IRON_FIST }, [Species.IRON_HANDS]: { 0: Abilities.IRON_FIST },
[Species.IRON_JUGULIS]: { 0: Abilities.LIGHTNING_ROD }, [Species.IRON_JUGULIS]: { 0: Abilities.LIGHTNING_ROD },
@ -567,7 +567,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.CHIEN_PAO]: { 0: Abilities.INTIMIDATE }, [Species.CHIEN_PAO]: { 0: Abilities.INTIMIDATE },
[Species.TING_LU]: { 0: Abilities.STAMINA }, [Species.TING_LU]: { 0: Abilities.STAMINA },
[Species.CHI_YU]: { 0: Abilities.BERSERK }, [Species.CHI_YU]: { 0: Abilities.BERSERK },
[Species.ROARING_MOON]: { 0: Abilities.TOUGH_CLAWS }, [Species.ROARING_MOON]: { 0: Abilities.INTIMIDATE },
[Species.IRON_VALIANT]: { 0: Abilities.NEUROFORCE }, [Species.IRON_VALIANT]: { 0: Abilities.NEUROFORCE },
[Species.KORAIDON]: { 0: Abilities.OPPORTUNIST }, [Species.KORAIDON]: { 0: Abilities.OPPORTUNIST },
[Species.MIRAIDON]: { 0: Abilities.OPPORTUNIST }, [Species.MIRAIDON]: { 0: Abilities.OPPORTUNIST },
@ -582,7 +582,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.RAGING_BOLT]: { 0: Abilities.BEAST_BOOST }, [Species.RAGING_BOLT]: { 0: Abilities.BEAST_BOOST },
[Species.IRON_BOULDER]: { 0: Abilities.SHARPNESS }, [Species.IRON_BOULDER]: { 0: Abilities.SHARPNESS },
[Species.IRON_CROWN]: { 0: Abilities.SHARPNESS }, [Species.IRON_CROWN]: { 0: Abilities.SHARPNESS },
[Species.TERAPAGOS]: { 0: Abilities.SOUL_HEART }, [Species.TERAPAGOS]: { 0: Abilities.SHIELD_DUST },
[Species.PECHARUNT]: { 0: Abilities.TOXIC_CHAIN }, [Species.PECHARUNT]: { 0: Abilities.TOXIC_CHAIN },
[Species.PALDEA_TAUROS]: { 0: Abilities.ADAPTABILITY }, [Species.PALDEA_TAUROS]: { 0: Abilities.ADAPTABILITY },
[Species.PALDEA_WOOPER]: { 0: Abilities.THICK_FAT }, [Species.PALDEA_WOOPER]: { 0: Abilities.THICK_FAT },

View File

@ -290,7 +290,7 @@ export const speciesStarterCosts = {
[Species.SHAYMIN]: 6, [Species.SHAYMIN]: 6,
[Species.ARCEUS]: 9, [Species.ARCEUS]: 9,
[Species.VICTINI]: 7, [Species.VICTINI]: 6,
[Species.SNIVY]: 3, [Species.SNIVY]: 3,
[Species.TEPIG]: 3, [Species.TEPIG]: 3,
[Species.OSHAWOTT]: 3, [Species.OSHAWOTT]: 3,
@ -461,7 +461,7 @@ export const speciesStarterCosts = {
[Species.GUZZLORD]: 6, [Species.GUZZLORD]: 6,
[Species.NECROZMA]: 8, [Species.NECROZMA]: 8,
[Species.MAGEARNA]: 7, [Species.MAGEARNA]: 7,
[Species.MARSHADOW]: 7, [Species.MARSHADOW]: 8,
[Species.POIPOLE]: 8, [Species.POIPOLE]: 8,
[Species.STAKATAKA]: 6, [Species.STAKATAKA]: 6,
[Species.BLACEPHALON]: 7, [Species.BLACEPHALON]: 7,
@ -611,7 +611,7 @@ export const speciesStarterCosts = {
[Species.RAGING_BOLT]: 7, [Species.RAGING_BOLT]: 7,
[Species.IRON_BOULDER]: 7, [Species.IRON_BOULDER]: 7,
[Species.IRON_CROWN]: 7, [Species.IRON_CROWN]: 7,
[Species.TERAPAGOS]: 8, [Species.TERAPAGOS]: 9,
[Species.PECHARUNT]: 6, [Species.PECHARUNT]: 6,
[Species.PALDEA_TAUROS]: 5, [Species.PALDEA_TAUROS]: 5,
[Species.PALDEA_WOOPER]: 3, [Species.PALDEA_WOOPER]: 3,

View File

@ -1,7 +1,20 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move"; import {
AttackMove,
BeakBlastHeaderAttr,
DelayedAttackAttr,
MoveFlags,
SelfStatusMove,
allMoves,
} from "./move";
import type Pokemon from "../field/pokemon"; import type Pokemon from "../field/pokemon";
import * as Utils from "../utils"; import {
type nil,
getFrameMs,
getEnumKeys,
getEnumValues,
animationFileName,
} from "../utils";
import type { BattlerIndex } from "../battle"; import type { BattlerIndex } from "../battle";
import type { Element } from "json-stable-stringify"; import type { Element } from "json-stable-stringify";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -401,7 +414,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
if (Object.keys(tweenProps).length) { if (Object.keys(tweenProps).length) {
globalScene.tweens.add(Object.assign({ globalScene.tweens.add(Object.assign({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: getFrameMs(this.duration * 3)
}, tweenProps)); }, tweenProps));
} }
return this.duration * 2; return this.duration * 2;
@ -437,7 +450,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
globalScene.tweens.add({ globalScene.tweens.add({
targets: moveAnim.bgSprite, targets: moveAnim.bgSprite,
duration: Utils.getFrameMs(this.duration * 3) duration: getFrameMs(this.duration * 3)
}); });
return this.duration * 2; return this.duration * 2;
@ -455,8 +468,8 @@ export const encounterAnims = new Map<EncounterAnim, AnimConfig>();
export function initCommonAnims(): Promise<void> { export function initCommonAnims(): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimNames = getEnumKeys(CommonAnim);
const commonAnimIds = Utils.getEnumValues(CommonAnim); const commonAnimIds = getEnumValues(CommonAnim);
const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = []; const commonAnimFetches: Promise<Map<CommonAnim, AnimConfig>>[] = [];
for (let ca = 0; ca < commonAnimIds.length; ca++) { for (let ca = 0; ca < commonAnimIds.length; ca++) {
const commonAnimId = commonAnimIds[ca]; const commonAnimId = commonAnimIds[ca];
@ -493,7 +506,7 @@ export function initMoveAnim(move: Moves): Promise<void> {
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP; const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
const fetchAnimAndResolve = (move: Moves) => { const fetchAnimAndResolve = (move: Moves) => {
globalScene.cachedFetch(`./battle-anims/${Utils.animationFileName(move)}.json`) globalScene.cachedFetch(`./battle-anims/${animationFileName(move)}.json`)
.then(response => { .then(response => {
const contentType = response.headers.get("content-type"); const contentType = response.headers.get("content-type");
if (!response.ok || contentType?.indexOf("application/json") === -1) { if (!response.ok || contentType?.indexOf("application/json") === -1) {
@ -550,7 +563,7 @@ function useDefaultAnim(move: Moves, defaultMoveAnim: Moves) {
* @remarks use {@linkcode useDefaultAnim} to use a default animation * @remarks use {@linkcode useDefaultAnim} to use a default animation
*/ */
function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) { function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
const moveName = Utils.animationFileName(move); const moveName = animationFileName(move);
console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams); console.warn(`Could not load animation file for move '${moveName}'`, ...optionalParams);
} }
@ -560,7 +573,7 @@ function logMissingMoveAnim(move: Moves, ...optionalParams: any[]) {
*/ */
export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> { export async function initEncounterAnims(encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ]; const anims = Array.isArray(encounterAnim) ? encounterAnim : [ encounterAnim ];
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimNames = getEnumKeys(EncounterAnim);
const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = []; const encounterAnimFetches: Promise<Map<EncounterAnim, AnimConfig>>[] = [];
for (const anim of anims) { for (const anim of anims) {
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) { if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
@ -922,7 +935,7 @@ export abstract class BattleAnim {
let f = 0; let f = 0;
globalScene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3), duration: getFrameMs(3),
repeat: anim?.frames.length ?? 0, repeat: anim?.frames.length ?? 0,
onRepeat: () => { onRepeat: () => {
if (!f) { if (!f) {
@ -994,47 +1007,39 @@ export abstract class BattleAnim {
const moveSprite = sprites[graphicIndex]; const moveSprite = sprites[graphicIndex];
if (spritePriorities[graphicIndex] !== frame.priority) { if (spritePriorities[graphicIndex] !== frame.priority) {
spritePriorities[graphicIndex] = frame.priority; spritePriorities[graphicIndex] = frame.priority;
/** Move the position that the moveSprite is rendered in based on the priority.
* @param priority The priority level to draw the sprite.
* - 0: Draw the sprite in front of the pokemon on the field.
* - 1: Draw the sprite in front of the user pokemon.
* - 2: Draw the sprite in front of its `bgSprite` (if it has one), or its
* `AnimFocus` (if that is user/target), otherwise behind everything.
* - 3: Draw the sprite behind its `AnimFocus` (if that is user/target), otherwise in front of everything.
*/
const setSpritePriority = (priority: number) => { const setSpritePriority = (priority: number) => {
switch (priority) { /** The sprite we are moving the moveSprite in relation to */
case 0: let targetSprite: Phaser.GameObjects.GameObject | nil;
globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, globalScene.getEnemyPokemon(false) ?? globalScene.getPlayerPokemon(false)!); // TODO: is this bang correct? /** The method that is being used to move the sprite.*/
break; let moveFunc: ((sprite: Phaser.GameObjects.GameObject, target: Phaser.GameObjects.GameObject) => void) |
case 1: ((sprite: Phaser.GameObjects.GameObject) => void) = globalScene.field.bringToTop;
globalScene.field.moveTo(moveSprite, globalScene.field.getAll().length - 1);
break; if (priority === 0) { // Place the sprite in front of the pokemon on the field.
case 2: targetSprite = globalScene.getEnemyField().find(p => p) ?? globalScene.getPlayerField().find(p => p);
switch (frame.focus) { console.log(typeof targetSprite);
case AnimFocus.USER: moveFunc = globalScene.field.moveBelow;
if (this.bgSprite) { } else if (priority === 2 && this.bgSprite) {
globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); moveFunc = globalScene.field.moveAbove;
} else { targetSprite = this.bgSprite;
globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? } else if (priority === 2 || priority === 3) {
} moveFunc = priority === 2 ? globalScene.field.moveBelow : globalScene.field.moveAbove;
break; if (frame.focus === AnimFocus.USER) {
case AnimFocus.TARGET: targetSprite = this.user;
globalScene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? } else if (frame.focus === AnimFocus.TARGET) {
break; targetSprite = this.target;
default: }
setSpritePriority(1);
break;
}
break;
case 3:
switch (frame.focus) {
case AnimFocus.USER:
globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct?
break;
case AnimFocus.TARGET:
globalScene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct?
break;
default:
setSpritePriority(1);
break;
}
break;
default:
setSpritePriority(1);
} }
// If target sprite is not undefined and exists in the field container, then move the sprite using the moveFunc.
// Otherwise, default to just bringing it to the top.
targetSprite && globalScene.field.exists(targetSprite) ? moveFunc.bind(globalScene.field)(moveSprite as Phaser.GameObjects.GameObject, targetSprite) : globalScene.field.bringToTop(moveSprite as Phaser.GameObjects.GameObject);
}; };
setSpritePriority(frame.priority); setSpritePriority(frame.priority);
} }
@ -1052,11 +1057,13 @@ export abstract class BattleAnim {
} }
} }
if (anim?.frameTimedEvents.has(f)) { if (anim?.frameTimedEvents.has(f)) {
for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct? const base = anim.frames.length - f;
r = Math.max((anim.frames.length - f) + event.execute(this), r); // Bang is correct due to `has` check above, which cannot return true for an undefined / null `f`
for (const event of anim.frameTimedEvents.get(f)!) {
r = Math.max(base + event.execute(this), r);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = getEnumValues(AnimFrameTarget);
for (const i of targets) { for (const i of targets) {
const count = i === AnimFrameTarget.GRAPHIC ? g : i === AnimFrameTarget.USER ? u : t; const count = i === AnimFrameTarget.GRAPHIC ? g : i === AnimFrameTarget.USER ? u : t;
if (count < spriteCache[i].length) { if (count < spriteCache[i].length) {
@ -1084,7 +1091,7 @@ export abstract class BattleAnim {
} }
if (r) { if (r) {
globalScene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(r), duration: getFrameMs(r),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
} else { } else {
@ -1166,7 +1173,7 @@ export abstract class BattleAnim {
let existingFieldSprites = globalScene.field.getAll().slice(0); let existingFieldSprites = globalScene.field.getAll().slice(0);
globalScene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(3) * frameTimeMult, duration: getFrameMs(3) * frameTimeMult,
repeat: anim!.frames.length, repeat: anim!.frames.length,
onRepeat: () => { onRepeat: () => {
existingFieldSprites = globalScene.field.getAll().slice(0); existingFieldSprites = globalScene.field.getAll().slice(0);
@ -1215,11 +1222,12 @@ export abstract class BattleAnim {
} }
} }
if (anim?.frameTimedEvents.get(frameCount)) { if (anim?.frameTimedEvents.get(frameCount)) {
const base = anim.frames.length - frameCount;
for (const event of anim.frameTimedEvents.get(frameCount)!) { for (const event of anim.frameTimedEvents.get(frameCount)!) {
totalFrames = Math.max((anim.frames.length - frameCount) + event.execute(this, frameTimedEventPriority), totalFrames); totalFrames = Math.max(base + event.execute(this, frameTimedEventPriority), totalFrames);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = getEnumValues(AnimFrameTarget);
for (const i of targets) { for (const i of targets) {
const count = graphicFrameCount; const count = graphicFrameCount;
if (count < spriteCache[i].length) { if (count < spriteCache[i].length) {
@ -1244,7 +1252,7 @@ export abstract class BattleAnim {
} }
if (totalFrames) { if (totalFrames) {
globalScene.tweens.addCounter({ globalScene.tweens.addCounter({
duration: Utils.getFrameMs(totalFrames), duration: getFrameMs(totalFrames),
onComplete: () => cleanUpAndComplete() onComplete: () => cleanUpAndComplete()
}); });
} else { } else {
@ -1342,15 +1350,15 @@ export class EncounterBattleAnim extends BattleAnim {
} }
export async function populateAnims() { export async function populateAnims() {
const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase()); const commonAnimNames = getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, "")); const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, ""));
const commonAnimIds = Utils.getEnumValues(CommonAnim) as CommonAnim[]; const commonAnimIds = getEnumValues(CommonAnim) as CommonAnim[];
const chargeAnimNames = Utils.getEnumKeys(ChargeAnim).map(k => k.toLowerCase()); const chargeAnimNames = getEnumKeys(ChargeAnim).map(k => k.toLowerCase());
const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, " ")); const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, " "));
const chargeAnimIds = Utils.getEnumValues(ChargeAnim) as ChargeAnim[]; const chargeAnimIds = getEnumValues(ChargeAnim) as ChargeAnim[];
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/; const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
const moveNameToId = {}; const moveNameToId = {};
for (const move of Utils.getEnumValues(Moves).slice(1)) { for (const move of getEnumValues(Moves).slice(1)) {
const moveName = Moves[move].toUpperCase().replace(/\_/g, ""); const moveName = Moves[move].toUpperCase().replace(/\_/g, "");
moveNameToId[moveName] = move; moveNameToId[moveName] = move;
} }

View File

@ -1866,11 +1866,14 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
export class AddSubstituteAttr extends MoveEffectAttr { export class AddSubstituteAttr extends MoveEffectAttr {
/** The ratio of the user's max HP that is required to apply this effect */ /** The ratio of the user's max HP that is required to apply this effect */
private hpCost: number; private hpCost: number;
/** Whether the damage taken should be rounded up (Shed Tail rounds up) */
private roundUp: boolean;
constructor(hpCost: number = 0.25) { constructor(hpCost: number, roundUp: boolean) {
super(true); super(true);
this.hpCost = hpCost; this.hpCost = hpCost;
this.roundUp = roundUp;
} }
/** /**
@ -1886,7 +1889,8 @@ export class AddSubstituteAttr extends MoveEffectAttr {
return false; return false;
} }
user.damageAndUpdate(Math.floor(user.getMaxHp() * this.hpCost), HitResult.OTHER, false, true, true); const damageTaken = this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost);
user.damageAndUpdate(damageTaken, HitResult.OTHER, false, true, true);
user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id); user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id);
return true; return true;
} }
@ -1899,7 +1903,7 @@ export class AddSubstituteAttr extends MoveEffectAttr {
} }
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => !user.getTag(SubstituteTag) && user.hp > Math.floor(user.getMaxHp() * this.hpCost) && user.getMaxHp() > 1; return (user, target, move) => !user.getTag(SubstituteTag) && user.hp > (this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost)) && user.getMaxHp() > 1;
} }
/** /**
@ -5232,7 +5236,7 @@ export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr {
return false; return false;
} }
const combinedPledgeMove = user.turnData.combiningPledge; const combinedPledgeMove = user?.turnData?.combiningPledge;
if (!combinedPledgeMove) { if (!combinedPledgeMove) {
return false; return false;
} }
@ -7811,11 +7815,12 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr {
return false; return false;
} }
target.summonData.abilitySuppressed = true;
globalScene.arena.triggerWeatherBasedFormChangesToNormal();
globalScene.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) })); globalScene.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) }));
target.suppressAbility();
globalScene.arena.triggerWeatherBasedFormChangesToNormal();
return true; return true;
} }
@ -9035,7 +9040,7 @@ export function initMoves() {
.attr(HighCritAttr) .attr(HighCritAttr)
.slicingMove(), .slicingMove(),
new SelfStatusMove(Moves.SUBSTITUTE, Type.NORMAL, -1, 10, -1, 0, 1) new SelfStatusMove(Moves.SUBSTITUTE, Type.NORMAL, -1, 10, -1, 0, 1)
.attr(AddSubstituteAttr), .attr(AddSubstituteAttr, 0.25, false),
new AttackMove(Moves.STRUGGLE, Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1) new AttackMove(Moves.STRUGGLE, Type.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1)
.attr(RecoilAttr, true, 0.25, true) .attr(RecoilAttr, true, 0.25, true)
.attr(TypelessAttr) .attr(TypelessAttr)
@ -9379,7 +9384,7 @@ export function initMoves() {
.attr(BypassBurnDamageReductionAttr), .attr(BypassBurnDamageReductionAttr),
new AttackMove(Moves.FOCUS_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3) new AttackMove(Moves.FOCUS_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3)
.attr(MessageHeaderAttr, (user, move) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) })) .attr(MessageHeaderAttr, (user, move) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) }))
.attr(PreUseInterruptAttr, i18next.t("moveTriggers:lostFocus"), user => !!user.turnData.attacksReceived.find(r => r.damage)) .attr(PreUseInterruptAttr, (user, target, move) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => !!user.turnData.attacksReceived.find(r => r.damage))
.punchingMove(), .punchingMove(),
new AttackMove(Moves.SMELLING_SALTS, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3) new AttackMove(Moves.SMELLING_SALTS, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1)
@ -11381,7 +11386,7 @@ export function initMoves() {
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461 / 4096 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461 / 4096 : 1)
.makesContact(), .makesContact(),
new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9) new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9)
.attr(AddSubstituteAttr, 0.5) .attr(AddSubstituteAttr, 0.5, true)
.attr(ForceSwitchOutAttr, true, SwitchType.SHED_TAIL) .attr(ForceSwitchOutAttr, true, SwitchType.SHED_TAIL)
.condition(failIfLastInPartyCondition), .condition(failIfLastInPartyCondition),
new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9) new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)

View File

@ -749,7 +749,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
if (this.speciesId === Species.ARCEUS) { if (this.speciesId === Species.ARCEUS) {
ret = i18next.t(`pokemonInfo:Type.${formText?.toUpperCase()}`); ret = i18next.t(`pokemonInfo:Type.${formText?.toUpperCase()}`);
} else if ([ SpeciesFormKey.MEGA, SpeciesFormKey.MEGA_X, SpeciesFormKey.MEGA_Y, SpeciesFormKey.PRIMAL, SpeciesFormKey.GIGANTAMAX, SpeciesFormKey.GIGANTAMAX_RAPID, SpeciesFormKey.GIGANTAMAX_SINGLE, SpeciesFormKey.ETERNAMAX ].includes(formKey as SpeciesFormKey)) { } else if ([ SpeciesFormKey.MEGA, SpeciesFormKey.MEGA_X, SpeciesFormKey.MEGA_Y, SpeciesFormKey.PRIMAL, SpeciesFormKey.GIGANTAMAX, SpeciesFormKey.GIGANTAMAX_RAPID, SpeciesFormKey.GIGANTAMAX_SINGLE, SpeciesFormKey.ETERNAMAX ].includes(formKey as SpeciesFormKey)) {
return i18next.t(`battlePokemonForm:${formKey}`, { pokemonName: (append ? this.name : "") }); return append ? i18next.t(`battlePokemonForm:${formKey}`, { pokemonName: this.name }) : i18next.t(`pokemonForm:battleForm.${formKey}`);
} else if (region === Region.NORMAL || (this.speciesId === Species.GALAR_DARMANITAN && formIndex > 0) || this.speciesId === Species.PALDEA_TAUROS) { // More special cases can be added here } else if (region === Region.NORMAL || (this.speciesId === Species.GALAR_DARMANITAN && formIndex > 0) || this.speciesId === Species.PALDEA_TAUROS) { // More special cases can be added here
const i18key = `pokemonForm:${speciesName}${formText}`; const i18key = `pokemonForm:${speciesName}${formText}`;
if (i18next.exists(i18key)) { if (i18next.exists(i18key)) {

View File

@ -26,6 +26,9 @@ import { Gender } from "#app/data/gender";
/** Minimum BST for Pokemon generated onto the Elite Four's teams */ /** Minimum BST for Pokemon generated onto the Elite Four's teams */
const ELITE_FOUR_MINIMUM_BST = 460; const ELITE_FOUR_MINIMUM_BST = 460;
/** The wave at which (non-Paldean) Gym Leaders start having Tera mons*/
const GYM_LEADER_TERA_WAVE = 100;
export enum TrainerPoolTier { export enum TrainerPoolTier {
COMMON, COMMON,
UNCOMMON, UNCOMMON,
@ -211,8 +214,8 @@ export class TrainerAI {
} }
/** /**
* Sets a pokemon on this AI to just instantly tera on first move used * Sets a pokemon on this AI to just instantly Tera on first move used
* @param index The index of the pokemon to instantly tera * @param index The index of the pokemon to instantly tera.
*/ */
public setInstantTera(index: number) { public setInstantTera(index: number) {
this.teraMode = TeraAIMode.INSTANT_TERA; this.teraMode = TeraAIMode.INSTANT_TERA;
@ -251,7 +254,7 @@ export class TrainerConfig {
public partyMemberFuncs: PartyMemberFuncs = {}; public partyMemberFuncs: PartyMemberFuncs = {};
public speciesPools: TrainerTierPools; public speciesPools: TrainerTierPools;
public speciesFilter: PokemonSpeciesFilter; public speciesFilter: PokemonSpeciesFilter;
public specialtyTypes: Type[] = []; public specialtyType: Type;
public hasVoucher: boolean = false; public hasVoucher: boolean = false;
public trainerAI: TrainerAI; public trainerAI: TrainerAI;
@ -583,8 +586,8 @@ export class TrainerConfig {
return this; return this;
} }
setSpecialtyTypes(...specialtyTypes: Type[]): TrainerConfig { setSpecialtyType(specialtyType: Type): TrainerConfig {
this.specialtyTypes = specialtyTypes; this.specialtyType = specialtyType;
return this; return this;
} }
@ -594,18 +597,22 @@ export class TrainerConfig {
} }
/** /**
* Sets random pokemon from the trainers team to instant tera. Uses their specialty types is they have one. * Sets random pokemon from the trainer's team to instant tera. Also sets Tera type to specialty type and checks for Shedinja as appropriate.
* @param count The amount of pokemon to have instant tera * @param count A callback (yucky) to see how many teras should be used
* @param slot Optional, a specified slot that should be terastallized. Wraps to match party size (-1 will get the last slot and so on).
* @returns this * @returns this
*/ */
setRandomTeraModifiers(count: () => integer): TrainerConfig { setRandomTeraModifiers(count: () => number, slot?: number): TrainerConfig {
this.genAIFuncs.push((party: EnemyPokemon[]) => { this.genAIFuncs.push((party: EnemyPokemon[]) => {
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i); const shedinjaCanTera = !this.hasSpecialtyType() || this.specialtyType === Type.BUG; // Better to check one time than 6
const partyMemberIndexes = new Array(party.length).fill(null).map((_, i) => i)
.filter(i => shedinjaCanTera || party[i].species.speciesId !== Species.SHEDINJA); // Shedinja can only Tera on Bug specialty type (or no specialty type)
const setPartySlot = !Utils.isNullOrUndefined(slot) ? Phaser.Math.Wrap(slot, 0, party.length) : -1; // If we have a tera slot defined, wrap it to party size.
for (let t = 0; t < Math.min(count(), party.length); t++) { for (let t = 0; t < Math.min(count(), party.length); t++) {
const randomIndex = Utils.randSeedItem(partyMemberIndexes); const randomIndex = partyMemberIndexes.indexOf(setPartySlot) > -1 ? setPartySlot : Utils.randSeedItem(partyMemberIndexes);
partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1);
if (this.specialtyTypes?.length) { if (this.hasSpecialtyType()) {
party[randomIndex].teraType = Utils.randSeedItem(this.specialtyTypes); party[randomIndex].teraType = this.specialtyType;
} }
this.trainerAI.setInstantTera(randomIndex); this.trainerAI.setInstantTera(randomIndex);
} }
@ -614,8 +621,8 @@ export class TrainerConfig {
} }
/** /**
* Sets a specific pokemon to instant tera * Sets a specific pokemon to instantly Tera
* @param index The index within the team to have instant tera * @param index The index within the team to have instant Tera.
* @returns this * @returns this
*/ */
setInstantTera(index: number): TrainerConfig { setInstantTera(index: number): TrainerConfig {
@ -779,12 +786,18 @@ export class TrainerConfig {
* @param title The title of the evil team admin. * @param title The title of the evil team admin.
* @param poolName The evil team the admin belongs to. * @param poolName The evil team the admin belongs to.
* @param {Species | Species[]} signatureSpecies The signature species for the evil team leader. * @param {Species | Species[]} signatureSpecies The signature species for the evil team leader.
* @param specialtyType The specialty Type of the admin, if they have one
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ * **/
initForEvilTeamAdmin(title: string, poolName: string, signatureSpecies: (Species | Species[])[],): TrainerConfig { initForEvilTeamAdmin(title: string, poolName: string, signatureSpecies: (Species | Species[])[], specialtyType?: Type): TrainerConfig {
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
if (!Utils.isNullOrUndefined(specialtyType)) {
this.setSpecialtyType(specialtyType);
}
this.setPartyTemplates(trainerPartyTemplates.RIVAL_5); this.setPartyTemplates(trainerPartyTemplates.RIVAL_5);
// Set the species pools for the evil team admin. // Set the species pools for the evil team admin.
@ -812,28 +825,16 @@ export class TrainerConfig {
/** /**
* Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter. * Initializes the trainer configuration for a Stat Trainer, as part of the Trainer's Test Mystery Encounter.
* @param {Species | Species[]} signatureSpecies The signature species for the Elite Four member. * @param isMale Whether the stat trainer is Male or Female (for localization of the title).
* @param {Type[]} specialtyTypes The specialty types for the Stat Trainer.
* @param isMale Whether the Elite Four Member is Male or Female (for localization of the title).
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
**/ **/
initForStatTrainer(signatureSpecies: (Species | Species[])[], isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { initForStatTrainer(isMale: boolean = false): TrainerConfig {
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR); this.setPartyTemplates(trainerPartyTemplates.ELITE_FOUR);
signatureSpecies.forEach((speciesPool, s) => {
if (!Array.isArray(speciesPool)) {
speciesPool = [ speciesPool ];
}
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
});
if (specialtyTypes.length) {
this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined);
this.setSpecialtyTypes(...specialtyTypes);
}
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
this.name = i18next.t(`trainerNames:${nameForCall}`); this.name = i18next.t(`trainerNames:${nameForCall}`);
this.setMoneyMultiplier(2); this.setMoneyMultiplier(2);
@ -850,11 +851,11 @@ export class TrainerConfig {
/** /**
* Initializes the trainer configuration for an evil team leader. Temporarily hardcoding evil leader teams though. * Initializes the trainer configuration for an evil team leader. Temporarily hardcoding evil leader teams though.
* @param {Species | Species[]} signatureSpecies The signature species for the evil team leader. * @param {Species | Species[]} signatureSpecies The signature species for the evil team leader.
* @param {Type[]} specialtyTypes The specialty types for the evil team Leader. * @param {Type} specialtyType The specialty type for the evil team Leader.
* @param boolean Whether or not this is the rematch fight * @param boolean Whether or not this is the rematch fight
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ * **/
initForEvilTeamLeader(title: string, signatureSpecies: (Species | Species[])[], rematch: boolean = false, ...specialtyTypes: Type[]): TrainerConfig { initForEvilTeamLeader(title: string, signatureSpecies: (Species | Species[])[], rematch: boolean = false, specialtyType?: Type): TrainerConfig {
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
} }
@ -869,9 +870,9 @@ export class TrainerConfig {
} }
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
if (specialtyTypes.length) { if (!Utils.isNullOrUndefined(specialtyType)) {
this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined); this.setSpeciesFilter(p => p.isOfType(specialtyType));
this.setSpecialtyTypes(...specialtyTypes); this.setSpecialtyType(specialtyType);
} }
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
this.name = i18next.t(`trainerNames:${nameForCall}`); this.name = i18next.t(`trainerNames:${nameForCall}`);
@ -888,12 +889,14 @@ export class TrainerConfig {
/** /**
* Initializes the trainer configuration for a Gym Leader. * Initializes the trainer configuration for a Gym Leader.
* @param {Species | Species[]} signatureSpecies The signature species for the Gym Leader. * @param {Species | Species[]} signatureSpecies The signature species for the Gym Leader. Added to party in reverse order.
* @param {Type[]} specialtyTypes The specialty types for the Gym Leader.
* @param isMale Whether the Gym Leader is Male or Not (for localization of the title). * @param isMale Whether the Gym Leader is Male or Not (for localization of the title).
* @param {Type} specialtyType The specialty type for the Gym Leader.
* @param ignoreMinTeraWave Whether the Gym Leader always uses Tera (true), or only Teras after {@linkcode GYM_LEADER_TERA_WAVE} (false). Defaults to false.
* @param teraSlot Optional, sets the party member in this slot to Terastallize. Wraps based on party size.
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
* **/ * **/
initForGymLeader(signatureSpecies: (Species | Species[])[], isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { initForGymLeader(signatureSpecies: (Species | Species[])[], isMale: boolean, specialtyType: Type, ignoreMinTeraWave: boolean = false, teraSlot?: number): TrainerConfig {
// Check if the internationalization (i18n) system is initialized. // Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
@ -912,11 +915,9 @@ export class TrainerConfig {
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// If specialty types are provided, set species filter and specialty types. // If specialty type is provided, set species filter and specialty type.
if (specialtyTypes.length) { this.setSpeciesFilter(p => p.isOfType(specialtyType));
this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) !== undefined); this.setSpecialtyType(specialtyType);
this.setSpecialtyTypes(...specialtyTypes);
}
// Localize the trainer's name by converting it to lowercase and replacing spaces with underscores. // Localize the trainer's name by converting it to lowercase and replacing spaces with underscores.
const nameForCall = this.name.toLowerCase().replace(/\s/g, "_"); const nameForCall = this.name.toLowerCase().replace(/\s/g, "_");
@ -935,7 +936,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_gym"); this.setBattleBgm("battle_unova_gym");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setRandomTeraModifiers(() => globalScene.currentBattle.waveIndex >= 100 ? 1 : 0); this.setRandomTeraModifiers(() => (ignoreMinTeraWave || globalScene.currentBattle.waveIndex >= GYM_LEADER_TERA_WAVE) ? 1 : 0, teraSlot);
return this; return this;
} }
@ -943,11 +944,12 @@ export class TrainerConfig {
/** /**
* Initializes the trainer configuration for an Elite Four member. * Initializes the trainer configuration for an Elite Four member.
* @param {Species | Species[]} signatureSpecies The signature species for the Elite Four member. * @param {Species | Species[]} signatureSpecies The signature species for the Elite Four member.
* @param {Type[]} specialtyTypes The specialty types for the Elite Four member.
* @param isMale Whether the Elite Four Member is Male or Female (for localization of the title). * @param isMale Whether the Elite Four Member is Male or Female (for localization of the title).
* @param specialtyType {Type} The specialty type for the Elite Four member.
* @param teraSlot Optional, sets the party member in this slot to Terastallize.
* @returns {TrainerConfig} The updated TrainerConfig instance. * @returns {TrainerConfig} The updated TrainerConfig instance.
**/ **/
initForEliteFour(signatureSpecies: (Species | Species[])[], isMale: boolean, ...specialtyTypes: Type[]): TrainerConfig { initForEliteFour(signatureSpecies: (Species | Species[])[], isMale: boolean, specialtyType?: Type, teraSlot?: number): TrainerConfig {
// Check if the internationalization (i18n) system is initialized. // Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) { if (!getIsInitialized()) {
initI18n(); initI18n();
@ -966,10 +968,10 @@ export class TrainerConfig {
this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool));
}); });
// Set species filter and specialty types if provided, otherwise filter by base total. // Set species filter and specialty type if provided, otherwise filter by base total.
if (specialtyTypes.length) { if (!Utils.isNullOrUndefined(specialtyType)) {
this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST); this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST);
this.setSpecialtyTypes(...specialtyTypes); this.setSpecialtyType(specialtyType);
} else { } else {
this.setSpeciesFilter(p => p.baseTotal >= ELITE_FOUR_MINIMUM_BST); this.setSpeciesFilter(p => p.baseTotal >= ELITE_FOUR_MINIMUM_BST);
} }
@ -991,7 +993,7 @@ export class TrainerConfig {
this.setHasVoucher(true); this.setHasVoucher(true);
this.setBattleBgm("battle_unova_elite"); this.setBattleBgm("battle_unova_elite");
this.setVictoryBgm("victory_gym"); this.setVictoryBgm("victory_gym");
this.setRandomTeraModifiers(() => 1); this.setRandomTeraModifiers(() => 1, teraSlot);
return this; return this;
} }
@ -1142,6 +1144,14 @@ export class TrainerConfig {
}); });
} }
/**
* Helper function to check if a specialty type is set
* @returns true if specialtyType is defined and not Type.UNKNOWN
*/
hasSpecialtyType(): boolean {
return !Utils.isNullOrUndefined(this.specialtyType) && this.specialtyType !== Type.UNKNOWN;
}
/** /**
* Creates a shallow copy of a trainer config so that it can be modified without affecting the {@link trainerConfigs} source map * Creates a shallow copy of a trainer config so that it can be modified without affecting the {@link trainerConfigs} source map
*/ */
@ -1183,9 +1193,7 @@ export class TrainerConfig {
clone = this.speciesPools ? clone.setSpeciesPools(this.speciesPools) : clone; clone = this.speciesPools ? clone.setSpeciesPools(this.speciesPools) : clone;
clone = this.speciesFilter ? clone.setSpeciesFilter(this.speciesFilter) : clone; clone = this.speciesFilter ? clone.setSpeciesFilter(this.speciesFilter) : clone;
if (this.specialtyTypes) { clone.specialtyType = this.specialtyType;
clone.specialtyTypes = this.specialtyTypes.slice(0);
}
clone.encounterMessages = this.encounterMessages?.slice(0); clone.encounterMessages = this.encounterMessages?.slice(0);
clone.victoryMessages = this.victoryMessages?.slice(0); clone.victoryMessages = this.victoryMessages?.slice(0);
@ -1352,28 +1360,28 @@ export const signatureSpecies: SignatureSpecies = {
PIERS: [ Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.INKAY ], PIERS: [ Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.INKAY ],
MARNIE: [ Species.IMPIDIMP, Species.PURRLOIN, Species.MORPEKO ], MARNIE: [ Species.IMPIDIMP, Species.PURRLOIN, Species.MORPEKO ],
RAIHAN: [ Species.DURALUDON, Species.TURTONATOR, Species.GOOMY ], RAIHAN: [ Species.DURALUDON, Species.TURTONATOR, Species.GOOMY ],
KATY: [ Species.NYMBLE, Species.TAROUNTULA, Species.HERACROSS ], KATY: [ Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA ], // Tera Bug Teddiursa
BRASSIUS: [ Species.SMOLIV, Species.SHROOMISH, Species.ODDISH ], BRASSIUS: [ Species.SUDOWOODO, Species.BRAMBLIN, Species.SMOLIV ], // Tera Grass Sudowoodo
IONO: [ Species.TADBULB, Species.WATTREL, Species.VOLTORB ], IONO: [ Species.MISDREAVUS, Species.TADBULB, Species.WATTREL ], // Tera Ghost Misdreavus
KOFU: [ Species.VELUZA, Species.WIGLETT, Species.WINGULL ], KOFU: [ Species.CRABRAWLER, Species.VELUZA, Species.WIGLETT, Species.WINGULL ], // Tera Water Crabrawler
LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA ], LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA ], // Tera Normal Starly
RYME: [ Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU ], RYME: [ Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU ], // Tera Ghost Toxel
TULIP: [ Species.GIRAFARIG, Species.FLITTLE, Species.RALTS ], TULIP: [ Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG ], // Tera Psychic Flabebe
GRUSHA: [ Species.CETODDLE, Species.ALOLA_VULPIX, Species.CUBCHOO ], GRUSHA: [ Species.SWABLU, Species.CETODDLE, Species.CUBCHOO, Species.ALOLA_VULPIX ], // Tera Ice Swablu
LORELEI: [ Species.JYNX, [ Species.SLOWBRO, Species.GALAR_SLOWBRO ], Species.LAPRAS, [ Species.ALOLA_SANDSLASH, Species.CLOYSTER ]], LORELEI: [ Species.JYNX, [ Species.SLOWBRO, Species.GALAR_SLOWBRO ], Species.LAPRAS, [ Species.CLOYSTER, Species.ALOLA_SANDSLASH ]],
BRUNO: [ Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [ Species.ALOLA_GOLEM, Species.GOLEM ]], BRUNO: [ Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [ Species.GOLEM, Species.ALOLA_GOLEM ]],
AGATHA: [ Species.GENGAR, [ Species.ARBOK, Species.WEEZING ], Species.CROBAT, Species.ALOLA_MAROWAK ], AGATHA: [ Species.GENGAR, [ Species.ARBOK, Species.WEEZING ], Species.CROBAT, Species.ALOLA_MAROWAK ],
LANCE: [ Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR ], LANCE: [ Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR ],
WILL: [ Species.XATU, Species.JYNX, [ Species.SLOWBRO, Species.SLOWKING ], Species.EXEGGUTOR ], WILL: [ Species.XATU, Species.JYNX, [ Species.SLOWBRO, Species.SLOWKING ], Species.EXEGGUTOR ],
KOGA: [[ Species.WEEZING, Species.MUK ], [ Species.VENOMOTH, Species.ARIADOS ], Species.CROBAT, Species.TENTACRUEL ], KOGA: [[ Species.MUK, Species.WEEZING ], [ Species.VENOMOTH, Species.ARIADOS ], Species.CROBAT, Species.TENTACRUEL ],
KAREN: [ Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE ], KAREN: [ Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE ],
SIDNEY: [[ Species.SHIFTRY, Species.CACTURNE ], [ Species.SHARPEDO, Species.CRAWDAUNT ], Species.ABSOL, Species.MIGHTYENA ], SIDNEY: [[ Species.SHIFTRY, Species.CACTURNE ], [ Species.SHARPEDO, Species.CRAWDAUNT ], Species.ABSOL, Species.MIGHTYENA ],
PHOEBE: [ Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [ Species.MISMAGIUS, Species.DRIFBLIM ]], PHOEBE: [ Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [ Species.DRIFBLIM, Species.MISMAGIUS ]],
GLACIA: [ Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW ], GLACIA: [ Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW ],
DRAKE: [ Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA ], DRAKE: [ Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA ],
AARON: [[ Species.SCIZOR, Species.KLEAVOR ], Species.HERACROSS, [ Species.VESPIQUEN, Species.YANMEGA ], Species.DRAPION ], AARON: [[ Species.SCIZOR, Species.KLEAVOR ], Species.HERACROSS, [ Species.VESPIQUEN, Species.YANMEGA ], Species.DRAPION ],
BERTHA: [ Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR ], BERTHA: [ Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR ],
FLINT: [[ Species.FLAREON, Species.RAPIDASH ], Species.MAGMORTAR, [ Species.STEELIX, Species.LOPUNNY ], Species.INFERNAPE ], FLINT: [[ Species.RAPIDASH, Species.FLAREON ], Species.MAGMORTAR, [ Species.STEELIX, Species.LOPUNNY ], Species.INFERNAPE ], // Tera Fire Steelix or Lopunny
LUCIAN: [ Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [ Species.ALAKAZAM, Species.ESPEON ]], LUCIAN: [ Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [ Species.ALAKAZAM, Species.ESPEON ]],
SHAUNTAL: [ Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT ], SHAUNTAL: [ Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT ],
MARSHAL: [ Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK ], MARSHAL: [ Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK ],
@ -1389,18 +1397,18 @@ export const signatureSpecies: SignatureSpecies = {
ACEROLA: [[ Species.BANETTE, Species.DRIFBLIM ], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND ], ACEROLA: [[ Species.BANETTE, Species.DRIFBLIM ], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND ],
KAHILI: [[ Species.BRAVIARY, Species.MANDIBUZZ ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON ], KAHILI: [[ Species.BRAVIARY, Species.MANDIBUZZ ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON ],
MARNIE_ELITE: [ Species.MORPEKO, Species.LIEPARD, [ Species.TOXICROAK, Species.SCRAFTY ], Species.GRIMMSNARL ], MARNIE_ELITE: [ Species.MORPEKO, Species.LIEPARD, [ Species.TOXICROAK, Species.SCRAFTY ], Species.GRIMMSNARL ],
NESSA_ELITE: [ Species.GOLISOPOD, [ Species.PELIPPER, Species.QUAGSIRE ], Species.TOXAPEX, Species.DREDNAW ], NESSA_ELITE: [ Species.GOLISOPOD, [ Species.QUAGSIRE, Species.PELIPPER ], Species.TOXAPEX, Species.DREDNAW ],
BEA_ELITE: [ Species.HAWLUCHA, [ Species.GRAPPLOCT, Species.SIRFETCHD ], Species.FALINKS, Species.MACHAMP ], BEA_ELITE: [ Species.HAWLUCHA, [ Species.GRAPPLOCT, Species.SIRFETCHD ], Species.FALINKS, Species.MACHAMP ],
ALLISTER_ELITE: [ Species.DUSKNOIR, [ Species.POLTEAGEIST, Species.RUNERIGUS ], Species.CURSOLA, Species.GENGAR ], ALLISTER_ELITE: [ Species.DUSKNOIR, [ Species.POLTEAGEIST, Species.RUNERIGUS ], Species.CURSOLA, Species.GENGAR ],
RAIHAN_ELITE: [ Species.GOODRA, [ Species.TORKOAL, Species.TURTONATOR ], Species.FLYGON, Species.ARCHALUDON ], RAIHAN_ELITE: [ Species.GOODRA, [ Species.TORKOAL, Species.TURTONATOR ], Species.FLYGON, Species.ARCHALUDON ],
RIKA: [ Species.WHISCASH, [ Species.DONPHAN, Species.DUGTRIO ], Species.CAMERUPT, Species.CLODSIRE ], RIKA: [ Species.CLODSIRE, [ Species.DUGTRIO, Species.DONPHAN ], Species.CAMERUPT, Species.WHISCASH ], // Tera Ground Clodsire
POPPY: [ Species.COPPERAJAH, Species.BRONZONG, Species.CORVIKNIGHT, Species.TINKATON ], POPPY: [ Species.TINKATON, Species.BRONZONG, Species.CORVIKNIGHT, Species.COPPERAJAH ], // Tera Steel Tinkaton
LARRY_ELITE: [ Species.STARAPTOR, Species.FLAMIGO, Species.ALTARIA, Species.TROPIUS ], LARRY_ELITE: [ Species.FLAMIGO, Species.STARAPTOR, [ Species.ALTARIA, Species.TROPIUS ], Species.ORICORIO ], // Tera Flying Flamigo; random Oricorio
HASSEL: [ Species.NOIVERN, [ Species.FLAPPLE, Species.APPLETUN ], Species.DRAGALGE, Species.BAXCALIBUR ], HASSEL: [ Species.BAXCALIBUR, [ Species.FLAPPLE, Species.APPLETUN ], Species.DRAGALGE, Species.NOIVERN ], // Tera Dragon Baxcalibur
CRISPIN: [ Species.TALONFLAME, Species.CAMERUPT, Species.MAGMORTAR, Species.BLAZIKEN ], CRISPIN: [ Species.BLAZIKEN, Species.MAGMORTAR, [ Species.CAMERUPT, Species.TALONFLAME ], Species.ROTOM ], // Tera Fire Blaziken; Heat Rotom
AMARYS: [ Species.SKARMORY, Species.EMPOLEON, Species.SCIZOR, Species.METAGROSS ], AMARYS: [ Species.METAGROSS, Species.SCIZOR, Species.EMPOLEON, Species.SKARMORY ], // Tera Steel Metagross
LACEY: [ Species.EXCADRILL, Species.PRIMARINA, [ Species.ALCREMIE, Species.GRANBULL ], Species.WHIMSICOTT ], LACEY: [ Species.EXCADRILL, Species.PRIMARINA, [ Species.WHIMSICOTT, Species.ALCREMIE ], Species.GRANBULL ], // Tera Fairy Excadrill
DRAYTON: [ Species.DRAGONITE, Species.ARCHALUDON, Species.HAXORUS, Species.SCEPTILE ], DRAYTON: [ Species.ARCHALUDON, Species.DRAGONITE, Species.HAXORUS, Species.SCEPTILE ], // Tera Dragon Archaludon
}; };
export const trainerConfigs: TrainerConfigs = { export const trainerConfigs: TrainerConfigs = {
@ -1421,7 +1429,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.BAKER]: new TrainerConfig(++t).setEncounterBgm(TrainerType.CLERK).setMoneyMultiplier(1.35).setSpeciesFilter(s => s.isOfType(Type.GRASS) || s.isOfType(Type.FIRE)), [TrainerType.BAKER]: new TrainerConfig(++t).setEncounterBgm(TrainerType.CLERK).setMoneyMultiplier(1.35).setSpeciesFilter(s => s.isOfType(Type.GRASS) || s.isOfType(Type.FIRE)),
[TrainerType.BEAUTY]: new TrainerConfig(++t).setMoneyMultiplier(1.55).setEncounterBgm(TrainerType.PARASOL_LADY), [TrainerType.BEAUTY]: new TrainerConfig(++t).setMoneyMultiplier(1.55).setEncounterBgm(TrainerType.PARASOL_LADY),
[TrainerType.BIKER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK).setSpeciesFilter(s => s.isOfType(Type.POISON)), [TrainerType.BIKER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK).setSpeciesFilter(s => s.isOfType(Type.POISON)),
[TrainerType.BLACK_BELT]: new TrainerConfig(++t).setHasGenders("Battle Girl", TrainerType.PSYCHIC).setHasDouble("Crush Kin").setEncounterBgm(TrainerType.ROUGHNECK).setSpecialtyTypes(Type.FIGHTING) [TrainerType.BLACK_BELT]: new TrainerConfig(++t).setHasGenders("Battle Girl", TrainerType.PSYCHIC).setHasDouble("Crush Kin").setEncounterBgm(TrainerType.ROUGHNECK).setSpecialtyType(Type.FIGHTING)
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK_ONE_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_STRONG, trainerPartyTemplates.THREE_AVG, trainerPartyTemplates.TWO_AVG_ONE_STRONG) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK_ONE_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_STRONG, trainerPartyTemplates.THREE_AVG, trainerPartyTemplates.TWO_AVG_ONE_STRONG)
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.NIDORAN_F, Species.NIDORAN_M, Species.MACHOP, Species.MAKUHITA, Species.MEDITITE, Species.CROAGUNK, Species.TIMBURR ], [TrainerPoolTier.COMMON]: [ Species.NIDORAN_F, Species.NIDORAN_M, Species.MACHOP, Species.MAKUHITA, Species.MEDITITE, Species.CROAGUNK, Species.TIMBURR ],
@ -1461,7 +1469,7 @@ export const trainerConfigs: TrainerConfigs = {
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.HEAL_PULSE)), .setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.HEAL_PULSE)),
[TrainerType.FIREBREATHER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK) [TrainerType.FIREBREATHER]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK)
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.SMOG) || s.isOfType(Type.FIRE)), .setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.SMOG) || s.isOfType(Type.FIRE)),
[TrainerType.FISHERMAN]: new TrainerConfig(++t).setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.BACKPACKER).setSpecialtyTypes(Type.WATER) [TrainerType.FISHERMAN]: new TrainerConfig(++t).setMoneyMultiplier(1.25).setEncounterBgm(TrainerType.BACKPACKER).setSpecialtyType(Type.WATER)
.setPartyTemplates(trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.THREE_WEAK_SAME, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.SIX_WEAKER) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.THREE_WEAK_SAME, trainerPartyTemplates.ONE_STRONG, trainerPartyTemplates.SIX_WEAKER)
.setSpeciesPools({ .setSpeciesPools({
[TrainerPoolTier.COMMON]: [ Species.TENTACOOL, Species.MAGIKARP, Species.GOLDEEN, Species.STARYU, Species.REMORAID, Species.SKRELP, Species.CLAUNCHER, Species.ARROKUDA ], [TrainerPoolTier.COMMON]: [ Species.TENTACOOL, Species.MAGIKARP, Species.GOLDEEN, Species.STARYU, Species.REMORAID, Species.SKRELP, Species.CLAUNCHER, Species.ARROKUDA ],
@ -1469,7 +1477,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerPoolTier.RARE]: [ Species.CHINCHOU, Species.CORSOLA, Species.WAILMER, Species.BARBOACH, Species.CLAMPERL, Species.LUVDISC, Species.MANTYKE, Species.ALOMOMOLA, Species.TATSUGIRI, Species.VELUZA ], [TrainerPoolTier.RARE]: [ Species.CHINCHOU, Species.CORSOLA, Species.WAILMER, Species.BARBOACH, Species.CLAMPERL, Species.LUVDISC, Species.MANTYKE, Species.ALOMOMOLA, Species.TATSUGIRI, Species.VELUZA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.LAPRAS, Species.FEEBAS, Species.RELICANTH, Species.DONDOZO ] [TrainerPoolTier.SUPER_RARE]: [ Species.LAPRAS, Species.FEEBAS, Species.RELICANTH, Species.DONDOZO ]
}), }),
[TrainerType.GUITARIST]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.ROUGHNECK).setSpecialtyTypes(Type.ELECTRIC).setSpeciesFilter(s => s.isOfType(Type.ELECTRIC)), [TrainerType.GUITARIST]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.ROUGHNECK).setSpecialtyType(Type.ELECTRIC).setSpeciesFilter(s => s.isOfType(Type.ELECTRIC)),
[TrainerType.HARLEQUIN]: new TrainerConfig(++t).setEncounterBgm(TrainerType.PSYCHIC).setSpeciesFilter(s => tmSpecies[Moves.TRICK_ROOM].indexOf(s.speciesId) > -1), [TrainerType.HARLEQUIN]: new TrainerConfig(++t).setEncounterBgm(TrainerType.PSYCHIC).setSpeciesFilter(s => tmSpecies[Moves.TRICK_ROOM].indexOf(s.speciesId) > -1),
[TrainerType.HIKER]: new TrainerConfig(++t).setEncounterBgm(TrainerType.BACKPACKER) [TrainerType.HIKER]: new TrainerConfig(++t).setEncounterBgm(TrainerType.BACKPACKER)
.setPartyTemplates(trainerPartyTemplates.TWO_AVG_SAME_ONE_AVG, trainerPartyTemplates.TWO_AVG_SAME_ONE_STRONG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.FOUR_WEAK, trainerPartyTemplates.ONE_STRONG) .setPartyTemplates(trainerPartyTemplates.TWO_AVG_SAME_ONE_AVG, trainerPartyTemplates.TWO_AVG_SAME_ONE_STRONG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.FOUR_WEAK, trainerPartyTemplates.ONE_STRONG)
@ -1548,7 +1556,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerPoolTier.RARE]: [ Species.TANGELA, Species.EEVEE, Species.YANMA ], [TrainerPoolTier.RARE]: [ Species.TANGELA, Species.EEVEE, Species.YANMA ],
[TrainerPoolTier.SUPER_RARE]: [ Species.TADBULB ] [TrainerPoolTier.SUPER_RARE]: [ Species.TADBULB ]
}), }),
[TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders("Swimmer Female").setHasDouble("Swimmers").setSpecialtyTypes(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)), [TrainerType.SWIMMER]: new TrainerConfig(++t).setMoneyMultiplier(1.3).setEncounterBgm(TrainerType.PARASOL_LADY).setHasGenders("Swimmer Female").setHasDouble("Swimmers").setSpecialtyType(Type.WATER).setSpeciesFilter(s => s.isOfType(Type.WATER)),
[TrainerType.TWINS]: new TrainerConfig(++t).setDoubleOnly().setMoneyMultiplier(0.65).setUseSameSeedForAllMembers() [TrainerType.TWINS]: new TrainerConfig(++t).setDoubleOnly().setMoneyMultiplier(0.65).setUseSameSeedForAllMembers()
.setPartyTemplateFunc(() => getWavePartyTemplate(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_STRONG)) .setPartyTemplateFunc(() => getWavePartyTemplate(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_STRONG))
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PLUSLE, Species.VOLBEAT, Species.PACHIRISU, Species.SILCOON, Species.METAPOD, Species.IGGLYBUFF, Species.PETILIL, Species.EEVEE ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PLUSLE, Species.VOLBEAT, Species.PACHIRISU, Species.SILCOON, Species.METAPOD, Species.IGGLYBUFF, Species.PETILIL, Species.EEVEE ]))
@ -1654,27 +1662,27 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerPoolTier.RARE]: [ Species.MANKEY, Species.PAWNIARD, Species.CHARCADET, Species.FLITTLE, Species.VAROOM, Species.ORTHWORM ], [TrainerPoolTier.RARE]: [ Species.MANKEY, Species.PAWNIARD, Species.CHARCADET, Species.FLITTLE, Species.VAROOM, Species.ORTHWORM ],
[TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.GIMMIGHOUL ] [TrainerPoolTier.SUPER_RARE]: [ Species.DONDOZO, Species.GIMMIGHOUL ]
}), }),
[TrainerType.GIACOMO]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_1", [ Species.KINGAMBIT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()) [TrainerType.GIACOMO]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_1", [ Species.KINGAMBIT ], Type.DARK).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // Segin Starmobile p.formIndex = 1; // Segin Starmobile
p.moveset = [ new PokemonMove(Moves.WICKED_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.WICKED_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.MELA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_2", [ Species.ARMAROUGE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()) [TrainerType.MELA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_2", [ Species.ARMAROUGE ], Type.FIRE).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 2; // Schedar Starmobile p.formIndex = 2; // Schedar Starmobile
p.moveset = [ new PokemonMove(Moves.BLAZING_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.BLAZING_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ATTICUS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_3", [ Species.REVAVROOM ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()) [TrainerType.ATTICUS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_3", [ Species.REVAVROOM ], Type.POISON).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 3; // Navi Starmobile p.formIndex = 3; // Navi Starmobile
p.moveset = [ new PokemonMove(Moves.NOXIOUS_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.NOXIOUS_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ORTEGA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_4", [ Species.DACHSBUN ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()) [TrainerType.ORTEGA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_4", [ Species.DACHSBUN ], Type.FAIRY).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 4; // Ruchbah Starmobile p.formIndex = 4; // Ruchbah Starmobile
p.moveset = [ new PokemonMove(Moves.MAGICAL_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.MAGICAL_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
})), })),
[TrainerType.ERI]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_5", [ Species.ANNIHILAPE ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate()) [TrainerType.ERI]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("star_admin", "star_5", [ Species.ANNIHILAPE ], Type.FIGHTING).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_star_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(() => getEvilGruntPartyTemplate())
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 5; // Caph Starmobile p.formIndex = 5; // Caph Starmobile
p.moveset = [ new PokemonMove(Moves.COMBAT_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ]; p.moveset = [ new PokemonMove(Moves.COMBAT_TORQUE), new PokemonMove(Moves.SPIN_OUT), new PokemonMove(Moves.SHIFT_GEAR), new PokemonMove(Moves.HIGH_HORSEPOWER) ];
@ -1746,14 +1754,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.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.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.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).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).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).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).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).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).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).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).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.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"), [TrainerType.BRUNO]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BRUNO"], true, Type.FIGHTING).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
@ -1768,7 +1776,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.DRAKE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAKE"], true, Type.DRAGON).setMixedBattleBgm("battle_hoenn_elite"), [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.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.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.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.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"), [TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"], true, Type.FIGHTING).setMixedBattleBgm("battle_unova_elite"),
@ -1788,14 +1796,14 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.BEA_ELITE]: new TrainerConfig(++t).setName("Bea").initForEliteFour(signatureSpecies["BEA_ELITE"], false, Type.FIGHTING).setMixedBattleBgm("battle_galar_elite"), [TrainerType.BEA_ELITE]: new TrainerConfig(++t).setName("Bea").initForEliteFour(signatureSpecies["BEA_ELITE"], false, Type.FIGHTING).setMixedBattleBgm("battle_galar_elite"),
[TrainerType.ALLISTER_ELITE]: new TrainerConfig(++t).setName("Allister").initForEliteFour(signatureSpecies["ALLISTER_ELITE"], true, Type.GHOST).setMixedBattleBgm("battle_galar_elite"), [TrainerType.ALLISTER_ELITE]: new TrainerConfig(++t).setName("Allister").initForEliteFour(signatureSpecies["ALLISTER_ELITE"], true, Type.GHOST).setMixedBattleBgm("battle_galar_elite"),
[TrainerType.RAIHAN_ELITE]: new TrainerConfig(++t).setName("Raihan").initForEliteFour(signatureSpecies["RAIHAN_ELITE"], true, Type.DRAGON).setMixedBattleBgm("battle_galar_elite"), [TrainerType.RAIHAN_ELITE]: new TrainerConfig(++t).setName("Raihan").initForEliteFour(signatureSpecies["RAIHAN_ELITE"], true, Type.DRAGON).setMixedBattleBgm("battle_galar_elite"),
[TrainerType.RIKA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["RIKA"], false, Type.GROUND).setMixedBattleBgm("battle_paldea_elite"), [TrainerType.RIKA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["RIKA"], false, Type.GROUND, 5).setMixedBattleBgm("battle_paldea_elite"),
[TrainerType.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"], false, Type.STEEL).setMixedBattleBgm("battle_paldea_elite"), [TrainerType.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"], false, Type.STEEL, 5).setMixedBattleBgm("battle_paldea_elite"),
[TrainerType.LARRY_ELITE]: new TrainerConfig(++t).setName("Larry").initForEliteFour(signatureSpecies["LARRY_ELITE"], true, Type.NORMAL, Type.FLYING).setMixedBattleBgm("battle_paldea_elite"), [TrainerType.LARRY_ELITE]: new TrainerConfig(++t).setName("Larry").initForEliteFour(signatureSpecies["LARRY_ELITE"], true, Type.FLYING, 5).setMixedBattleBgm("battle_paldea_elite"),
[TrainerType.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"], true, Type.DRAGON).setMixedBattleBgm("battle_paldea_elite"), [TrainerType.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"], true, Type.DRAGON, 5).setMixedBattleBgm("battle_paldea_elite"),
[TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"], true, Type.FIRE).setMixedBattleBgm("battle_bb_elite"), [TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"], true, Type.FIRE, 5).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"], false, Type.STEEL).setMixedBattleBgm("battle_bb_elite"), [TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"], false, Type.STEEL, 5).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"], false, Type.FAIRY).setMixedBattleBgm("battle_bb_elite"), [TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"], false, Type.FAIRY, 5).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"], true, Type.DRAGON).setMixedBattleBgm("battle_bb_elite"), [TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"], true, Type.DRAGON, 5).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(true).setBattleBgm("battle_kanto_champion").setMixedBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double") [TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(true).setBattleBgm("battle_kanto_champion").setMixedBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALAKAZAM ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALAKAZAM ]))
@ -1815,7 +1823,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Ground or Rock Rhyperior / Electric Electivire / Fire Magmortar
[TrainerType.RED]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double") [TrainerType.RED]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PIKACHU ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PIKACHU ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 8; // G-Max Pikachu p.formIndex = 8; // G-Max Pikachu
@ -1839,7 +1847,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Grass Meganium / Fire Typhlosion / Water Feraligatr
[TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion") [TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GYARADOS, Species.KINGDRA ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GYARADOS, Species.KINGDRA ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AERODACTYL ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AERODACTYL ]))
@ -1850,14 +1858,15 @@ export const trainerConfigs: TrainerConfigs = {
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CHARIZARD ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.CHARIZARD ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TYRANITAR, Species.GARCHOMP, Species.KOMMO_O ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.TYRANITAR, Species.GARCHOMP, Species.KOMMO_O ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.type1; p.teraType = Type.DRAGON;
p.abilityIndex = p.species.speciesId === Species.KOMMO_O ? 1 : 2; // Soundproof Kommo-o, Unnerve Tyranitar, Rough Skin Garchomp
})) }))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DRAGONITE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DRAGONITE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Dragon Tyranitar / Garchomp / Kommo-o
[TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double") [TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SKARMORY ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SKARMORY ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CRADILY, Species.ARMALDO ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.CRADILY, Species.ARMALDO ]))
@ -1875,7 +1884,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.generateName(); p.generateName();
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Rock Regirock / Ice Regice / Steel Registeel
[TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double") [TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(true).setBattleBgm("battle_hoenn_champion_g5").setMixedBattleBgm("battle_hoenn_champion_g6").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PELIPPER ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PELIPPER ], TrainerSlot.TRAINER, true, p => {
p.abilityIndex = 1; // Drizzle p.abilityIndex = 1; // Drizzle
@ -1898,7 +1907,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Electric Regieleki / Dragon Regidrago
[TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion") [TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SPIRITOMB ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SPIRITOMB ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1921,7 +1930,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Water Milotic / Grass Roserade / Fire Hisuian Arcanine
[TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder") [TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder").setMixedBattleBgm("battle_champion_alder")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BOUFFALANT, Species.BRAVIARY ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BOUFFALANT, Species.BRAVIARY ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_LILLIGANT, Species.HISUI_ZOROARK, Species.BASCULEGION ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_LILLIGANT, Species.HISUI_ZOROARK, Species.BASCULEGION ], TrainerSlot.TRAINER, true, p => {
@ -1944,7 +1953,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Ghost Chandelure / Dark Krookodile / Psychic Reuniclus / Fighting Conkeldurr
[TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double") [TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.DRUDDIGON ])) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.DRUDDIGON ]))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ARCHEOPS ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ARCHEOPS ]))
@ -1966,7 +1975,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Dragon Salamence / Hydreigon / Archaludon
[TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_kalos_champion") [TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_kalos_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.HAWLUCHA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.HAWLUCHA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1979,7 +1988,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TYRANTRUM, Species.AURORUS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.TYRANTRUM, Species.AURORUS ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.abilityIndex = 2; // Rock Head Tyrantrum, Snow Warning Aurorus p.abilityIndex = 2; // Rock Head Tyrantrum, Snow Warning Aurorus
p.teraType = p.species.speciesId === Species.TYRANTRUM ? Type.DRAGON : Type.ICE; p.teraType = p.species.type2!;
})) }))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GOODRA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GOODRA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1991,7 +2000,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.FEMALE; p.gender = Gender.FEMALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Dragon Tyrantrum / Ice Aurorus
[TrainerType.KUKUI]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kukui") [TrainerType.KUKUI]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kukui")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -1999,7 +2008,7 @@ export const trainerConfigs: TrainerConfigs = {
})) }))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MAGNEZONE, Species.ALOLA_NINETALES ])) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MAGNEZONE, Species.ALOLA_NINETALES ]))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.TORNADUS, Species.THUNDURUS, Species.LANDORUS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.TORNADUS, Species.THUNDURUS, Species.LANDORUS ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 1; // Therian Forms p.formIndex = 1; // Therian Formes
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
@ -2015,9 +2024,9 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.HISUI_DECIDUEYE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.HISUI_DECIDUEYE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.teraType = p.species.speciesId === Species.INCINEROAR ? Type.DARK : Type.FIGHTING; p.teraType = p.species.type2!;
})) }))
.setInstantTera(5), .setInstantTera(5), // Tera Dark Incineroar / Fighting Hisuian Decidueye
[TrainerType.HAU]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_alola_champion") [TrainerType.HAU]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_alola_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALOLA_RAICHU ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALOLA_RAICHU ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2042,7 +2051,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2); p.setBoss(true, 2);
p.gender = p.species.speciesId === Species.PRIMARINA ? Gender.FEMALE : Gender.MALE; p.gender = p.species.speciesId === Species.PRIMARINA ? Gender.FEMALE : Gender.MALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Psychic Tapu Lele / Grass Tapu Bulu
[TrainerType.LEON]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_galar_champion") [TrainerType.LEON]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_galar_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AEGISLASH ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AEGISLASH ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2063,7 +2072,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.gender = Gender.MALE; p.gender = Gender.MALE;
})) }))
.setInstantTera(3), .setInstantTera(3), // Tera Dragapult to Ghost or Dragon
[TrainerType.MUSTARD]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_mustard") [TrainerType.MUSTARD]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_mustard")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CORVIKNIGHT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2094,7 +2103,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setInstantTera(2), .setInstantTera(2), // Tera Psychic Galar-Slowbro / Galar-Slowking
[TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_geeta") [TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_geeta")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GLIMMORA ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GLIMMORA ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2113,7 +2122,7 @@ export const trainerConfigs: TrainerConfigs = {
p.abilityIndex = 1; // Supreme Overlord p.abilityIndex = 1; // Supreme Overlord
p.teraType = Type.FLYING; p.teraType = Type.FLYING;
})) }))
.setInstantTera(5), .setInstantTera(5), // Tera Flying Kingambit
[TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_nemona") [TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(false).setMixedBattleBgm("battle_champion_nemona")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LYCANROC ], TrainerSlot.TRAINER, true, p => {
p.formIndex = 0; // Midday form p.formIndex = 0; // Midday form
@ -2126,25 +2135,21 @@ export const trainerConfigs: TrainerConfigs = {
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GHOLDENGO ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GHOLDENGO ]))
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ARMAROUGE, Species.CERULEDGE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.ARMAROUGE, Species.CERULEDGE ], TrainerSlot.TRAINER, true, p => {
p.teraType = p.species.speciesId === Species.ARMAROUGE ? Type.PSYCHIC : Type.GHOST; p.teraType = p.species.type2!;
})) }))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Psychic Armarouge / Ghost Ceruledge
[TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kieran") [TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(true).setMixedBattleBgm("battle_champion_kieran")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.POLIWRATH, Species.POLITOED ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.POLIWRATH, Species.POLITOED ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
})) }))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.GRIMMSNARL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.INCINEROAR, Species.GRIMMSNARL ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
if (p.species.speciesId === Species.INCINEROAR) { p.abilityIndex = p.species.speciesId === Species.INCINEROAR ? 2 : 0; // Intimidate Incineroar, Prankster Grimmsnarl
p.abilityIndex = 2; // Intimidate
} else if (p.species.speciesId === Species.GRIMMSNARL) {
p.abilityIndex = 0; // Prankster
}
})) }))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.TERAPAGOS ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.TERAPAGOS ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2167,7 +2172,7 @@ export const trainerConfigs: TrainerConfigs = {
p.gender = Gender.MALE; p.gender = Gender.MALE;
p.setBoss(true, 2); p.setBoss(true, 2);
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Ogerpon
[TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL) [TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL)
.setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE) .setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE)
@ -2198,7 +2203,7 @@ export const trainerConfigs: TrainerConfigs = {
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true)) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEOT, Species.NOCTOWL, Species.SWELLOW, Species.STARAPTOR, Species.UNFEZANT, Species.TALONFLAME, Species.TOUCANNON, Species.CORVIKNIGHT, Species.KILOWATTREL ], TrainerSlot.TRAINER, true))
.setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450)) .setPartyMemberFunc(2, getSpeciesFilterRandomPartyMemberFunc((species: PokemonSpecies) => !pokemonEvolutions.hasOwnProperty(species.speciesId) && !pokemonPrevolutions.hasOwnProperty(species.speciesId) && species.baseTotal >= 450))
.setSpeciesFilter(species => species.baseTotal >= 540) .setSpeciesFilter(species => species.baseTotal >= 540)
.setInstantTera(0), .setInstantTera(0), // Tera starter to primary type
[TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5) [TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true,
p => { p => {
@ -2215,7 +2220,7 @@ export const trainerConfigs: TrainerConfigs = {
p.shiny = true; p.shiny = true;
p.variant = 1; p.variant = 1;
})) }))
.setInstantTera(0), .setInstantTera(0), // Tera starter to primary type
[TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6) [TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true, .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL ], TrainerSlot.TRAINER, true,
p => { p => {
@ -2240,7 +2245,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1; // Mega Rayquaza p.formIndex = 1; // Mega Rayquaza
p.generateName(); p.generateName();
})) }))
.setInstantTera(0), .setInstantTera(0), // Tera starter to primary type
[TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN ], TrainerSlot.TRAINER, true, p => {
@ -2638,7 +2643,7 @@ export const trainerConfigs: TrainerConfigs = {
})) }))
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.CRAWDAUNT, Species.HISUI_SAMUROTT ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.CRAWDAUNT, Species.HISUI_SAMUROTT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.abilityIndex = 2; // Sharpness Hisui Samurott, Adaptability Crawdaunt p.abilityIndex = 2; // Sharpness Hisuian Samurott, Adaptability Crawdaunt
})) }))
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2738,7 +2743,7 @@ export const trainerConfigs: TrainerConfigs = {
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
p.generateName(); p.generateName();
})) }))
.setInstantTera(4), .setInstantTera(4), // Tera Fairy Sylveon
[TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") [TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2); p.setBoss(true, 2);
@ -2771,13 +2776,8 @@ export const trainerConfigs: TrainerConfigs = {
p.generateName(); p.generateName();
p.pokeball = PokeballType.ULTRA_BALL; p.pokeball = PokeballType.ULTRA_BALL;
})) }))
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.ZAMAZENTA ], TrainerSlot.TRAINER, true, p => { .setInstantTera(0), // Tera Fairy Sylveon
p.setBoss(true, 2); [TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer(true)
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL;
}))
.setInstantTera(0),
[TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLAYDOL ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 3); p.setBoss(true, 3);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2809,7 +2809,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})), })),
[TrainerType.CHERYL]: new TrainerConfig(++t).setName("Cheryl").initForStatTrainer([], false) [TrainerType.CHERYL]: new TrainerConfig(++t).setName("Cheryl").initForStatTrainer()
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BLISSEY ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BLISSEY ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 3); p.setBoss(true, 3);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2837,7 +2837,7 @@ export const trainerConfigs: TrainerConfigs = {
} }
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})), })),
[TrainerType.MARLEY]: new TrainerConfig(++t).setName("Marley").initForStatTrainer([], false) [TrainerType.MARLEY]: new TrainerConfig(++t).setName("Marley").initForStatTrainer()
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCANINE ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCANINE ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 3); p.setBoss(true, 3);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2861,7 +2861,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})), })),
[TrainerType.MIRA]: new TrainerConfig(++t).setName("Mira").initForStatTrainer([], false) [TrainerType.MIRA]: new TrainerConfig(++t).setName("Mira").initForStatTrainer()
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALAKAZAM ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ALAKAZAM ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2); p.setBoss(true, 2);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
@ -2883,7 +2883,7 @@ export const trainerConfigs: TrainerConfigs = {
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();
p.pokeball = PokeballType.MASTER_BALL; p.pokeball = PokeballType.MASTER_BALL;
})), })),
[TrainerType.RILEY]: new TrainerConfig(++t).setName("Riley").initForStatTrainer([], true) [TrainerType.RILEY]: new TrainerConfig(++t).setName("Riley").initForStatTrainer(true)
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LUCARIO ], TrainerSlot.TRAINER, true, p => { .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LUCARIO ], TrainerSlot.TRAINER, true, p => {
p.setBoss(true, 2); p.setBoss(true, 2);
p.generateAndPopulateMoveset(); p.generateAndPopulateMoveset();

View File

@ -29,4 +29,5 @@ export enum ArenaTagType {
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE", WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE", GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
FAIRY_LOCK = "FAIRY_LOCK", FAIRY_LOCK = "FAIRY_LOCK",
NEUTRALIZING_GAS = "NEUTRALIZING_GAS"
} }

View File

@ -588,8 +588,8 @@ export class Arena {
// creates a new tag object // creates a new tag object
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side);
if (newTag) { if (newTag) {
this.tags.push(newTag);
newTag.onAdd(this, quiet); newTag.onAdd(this, quiet);
this.tags.push(newTag);
const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {};

View File

@ -63,8 +63,9 @@ import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags"; import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
import type { SuppressAbilitiesTag } from "#app/data/arena-tag";
import type { Ability, AbAttr } from "#app/data/ability"; import type { Ability, AbAttr } from "#app/data/ability";
import { StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, applyOnGainAbAttrs, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs, applyOnLoseClearWeatherAbAttrs } from "#app/data/ability"; import { StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, applyOnGainAbAttrs, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs, applyOnLoseAbAttrs, PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr } from "#app/data/ability";
import type PokemonData from "#app/system/pokemon-data"; import type PokemonData from "#app/system/pokemon-data";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { Mode } from "#app/ui/ui"; import { Mode } from "#app/ui/ui";
@ -1225,10 +1226,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** /**
* Checks if the {@linkcode Pokemon} has is the specified {@linkcode Species} or is fused with it. * Checks if the {@linkcode Pokemon} has is the specified {@linkcode Species} or is fused with it.
* @param species the pokemon {@linkcode Species} to check * @param species the pokemon {@linkcode Species} to check
* @param formKey If provided, requires the species to be in that form
* @returns `true` if the pokemon is the species or is fused with it, `false` otherwise * @returns `true` if the pokemon is the species or is fused with it, `false` otherwise
*/ */
hasSpecies(species: Species): boolean { hasSpecies(species: Species, formKey?: string): boolean {
return this.species.speciesId === species || this.fusionSpecies?.speciesId === species; if (Utils.isNullOrUndefined(formKey)) {
return this.species.speciesId === species || this.fusionSpecies?.speciesId === species;
}
return (this.species.speciesId === species && this.getFormKey() === formKey) || (this.fusionSpecies?.speciesId === species && this.getFusionFormKey() === formKey);
} }
abstract isBoss(): boolean; abstract isBoss(): boolean;
@ -1487,7 +1493,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param ability New Ability * @param ability New Ability
*/ */
public setTempAbility(ability: Ability, passive: boolean = false): void { public setTempAbility(ability: Ability, passive: boolean = false): void {
applyOnLoseClearWeatherAbAttrs(this, passive); applyOnLoseAbAttrs(this, passive);
if (passive) { if (passive) {
this.summonData.passiveAbility = ability.id; this.summonData.passiveAbility = ability.id;
} else { } else {
@ -1496,6 +1502,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyOnGainAbAttrs(this, passive); applyOnGainAbAttrs(this, passive);
} }
/**
* Suppresses an ability and calls its onlose attributes
*/
public suppressAbility() {
[ true, false ].forEach((passive) => applyOnLoseAbAttrs(this, passive));
this.summonData.abilitySuppressed = true;
}
/** /**
* Checks if a pokemon has a passive either from: * Checks if a pokemon has a passive either from:
* - bought with starter candy * - bought with starter candy
@ -1553,17 +1567,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (this.summonData?.abilitySuppressed && !ability.hasAttr(UnsuppressableAbilityAbAttr)) { if (this.summonData?.abilitySuppressed && !ability.hasAttr(UnsuppressableAbilityAbAttr)) {
return false; return false;
} }
if (this.isOnField() && !ability.hasAttr(SuppressFieldAbilitiesAbAttr)) { const suppressAbilitiesTag = arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag;
const suppressed = new Utils.BooleanHolder(false); if (this.isOnField() && suppressAbilitiesTag && !suppressAbilitiesTag.isBeingRemoved()) {
globalScene.getField(true).filter(p => p !== this).map(p => { const thisAbilitySuppressing = ability.hasAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr);
if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) { const hasSuppressingAbility = this.hasAbilityWithAttr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, false);
p.getAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, false, false, suppressed, [ ability ])); // Neutralizing gas is up - suppress abilities unless they are unsuppressable or this pokemon is responsible for the gas
} // (Balance decided that the other ability of a neutralizing gas pokemon should not be neutralized)
if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) { // If the ability itself is neutralizing gas, don't suppress it (handled through arena tag)
p.getPassiveAbility().getAttrs(SuppressFieldAbilitiesAbAttr).map(a => a.apply(this, true, false, suppressed, [ ability ])); const unsuppressable = ability.hasAttr(UnsuppressableAbilityAbAttr) || thisAbilitySuppressing || (hasSuppressingAbility && !suppressAbilitiesTag.shouldApplyToSelf());
} if (!unsuppressable) {
});
if (suppressed.value) {
return false; return false;
} }
} }
@ -3197,6 +3209,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!));
} }
isMega(): boolean {
const megaForms = [ SpeciesFormKey.MEGA, SpeciesFormKey.MEGA_X, SpeciesFormKey.MEGA_Y, SpeciesFormKey.PRIMAL ] as string[];
return megaForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && megaForms.includes(this.getFusionFormKey()!));
}
canAddTag(tagType: BattlerTagType): boolean { canAddTag(tagType: BattlerTagType): boolean {
if (this.getTag(tagType)) { if (this.getTag(tagType)) {
return false; return false;

View File

@ -416,14 +416,16 @@ export default class Trainer extends Phaser.GameObjects.Container {
} }
} }
if (!retry && this.config.specialtyTypes.length && !this.config.specialtyTypes.find(t => ret.isOfType(t))) { // Prompts reroll of party member species if doesn't fit specialty type.
// Can be removed by adding a type parameter to getTrainerSpeciesForLevel and filtering the list of evolutions for that type.
if (!retry && this.config.hasSpecialtyType() && !ret.isOfType(this.config.specialtyType)) {
retry = true; retry = true;
console.log("Attempting reroll of species evolution to fit specialty type..."); console.log("Attempting reroll of species evolution to fit specialty type...");
let evoAttempt = 0; let evoAttempt = 0;
while (retry && evoAttempt++ < 10) { while (retry && evoAttempt++ < 10) {
ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex)); ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, globalScene.currentBattle.waveIndex));
console.log(ret.name); console.log(ret.name);
if (this.config.specialtyTypes.find(t => ret.isOfType(t))) { if (ret.isOfType(this.config.specialtyType)) {
retry = false; retry = false;
} }
} }
@ -677,6 +679,11 @@ export default class Trainer extends Phaser.GameObjects.Container {
}); });
} }
/**
* Determines whether a Trainer should Terastallize their Pokemon
* @param pokemon {@linkcode EnemyPokemon} Trainer Pokemon in question
* @returns boolean Whether the EnemyPokemon should Terastalize this turn
*/
shouldTera(pokemon: EnemyPokemon): boolean { shouldTera(pokemon: EnemyPokemon): boolean {
if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA) { if (this.config.trainerAI.teraMode === TeraAIMode.INSTANT_TERA) {
if (!pokemon.isTerastallized && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)) { if (!pokemon.isTerastallized && this.config.trainerAI.instantTeras.includes(pokemon.initialTeamIndex)) {

View File

@ -564,6 +564,15 @@ export class InputsController {
if (!this.configs[selectedDevice]) { if (!this.configs[selectedDevice]) {
this.configs[selectedDevice] = {}; this.configs[selectedDevice] = {};
} }
// A proper way of handling migrating keybinds would be much better
const mappingOverrides = {
"BUTTON_CYCLE_VARIANT": "BUTTON_CYCLE_TERA",
};
for (const key in mappingConfigs.custom) {
if (mappingConfigs.custom[key] in mappingOverrides) {
mappingConfigs.custom[key] = mappingOverrides[mappingConfigs.custom[key]];
}
}
this.configs[selectedDevice].custom = mappingConfigs.custom; this.configs[selectedDevice].custom = mappingConfigs.custom;
} }

View File

@ -101,6 +101,7 @@ export class LoadingScene extends SceneBase {
this.loadImage("icon_lock", "ui", "icon_lock.png"); this.loadImage("icon_lock", "ui", "icon_lock.png");
this.loadImage("icon_stop", "ui", "icon_stop.png"); this.loadImage("icon_stop", "ui", "icon_stop.png");
this.loadImage("icon_tera", "ui"); this.loadImage("icon_tera", "ui");
this.loadImage("cursor_tera", "ui");
this.loadImage("type_tera", "ui"); this.loadImage("type_tera", "ui");
this.loadAtlas("type_bgs", "ui"); this.loadAtlas("type_bgs", "ui");
this.loadAtlas("button_tera", "ui"); this.loadAtlas("button_tera", "ui");
@ -250,9 +251,9 @@ export class LoadingScene extends SceneBase {
} }
const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ]; const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ];
if (lang && availableLangs.includes(lang)) { if (lang && availableLangs.includes(lang)) {
this.loadImage("valentines2025event-" + lang, "events"); this.loadImage("pkmnday2025event-" + lang, "events");
} else { } else {
this.loadImage("valentines2025event-en", "events"); this.loadImage("pkmnday2025event-en", "events");
} }
this.loadAtlas("statuses", ""); this.loadAtlas("statuses", "");

View File

@ -2561,7 +2561,7 @@ export function getPartyLuckValue(party: Pokemon[]): number {
return DailyLuck.value; return DailyLuck.value;
} }
const eventSpecies = globalScene.eventManager.getEventLuckBoostedSpecies(); const eventSpecies = globalScene.eventManager.getEventLuckBoostedSpecies();
const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 3 : 0) : 0) const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 1 : 0) : 0)
.reduce((total: number, value: number) => total += value, 0), 0, 14); .reduce((total: number, value: number) => total += value, 0), 0, 14);
return Math.min(globalScene.eventManager.getEventLuckBoost() + (luck ?? 0), 14); return Math.min(globalScene.eventManager.getEventLuckBoost() + (luck ?? 0), 14);
} }

View File

@ -253,7 +253,10 @@ export class EncounterPhase extends BattlePhase {
globalScene.getEnemyField().forEach(enemy => { globalScene.getEnemyField().forEach(enemy => {
overrideHeldItems(enemy, false); overrideHeldItems(enemy, false);
}); });
}
if (battle.battleType === BattleType.TRAINER) {
globalScene.currentBattle.trainer!.genAI(globalScene.getEnemyParty());
} }
globalScene.ui.setMode(Mode.MESSAGE).then(() => { globalScene.ui.setMode(Mode.MESSAGE).then(() => {

View File

@ -1645,11 +1645,19 @@ export class GameData {
} else if (formIndex === 3) { } else if (formIndex === 3) {
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(1); dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(1);
} }
} } else if (pokemon.species.speciesId === Species.ZYGARDE) {
const allFormChanges = pokemonFormChanges.hasOwnProperty(species.speciesId) ? pokemonFormChanges[species.speciesId] : []; if (formIndex === 4) {
const toCurrentFormChanges = allFormChanges.filter(f => (f.formKey === formKey)); dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(2);
if (toCurrentFormChanges.length > 0) { } else if (formIndex === 5) {
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0); dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(3);
}
} else {
const allFormChanges = pokemonFormChanges.hasOwnProperty(species.speciesId) ? pokemonFormChanges[species.speciesId] : [];
const toCurrentFormChanges = allFormChanges.filter(f => (f.formKey === formKey));
if (toCurrentFormChanges.length > 0) {
// Needs to do this or Castform can unlock the wrong form, etc.
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0);
}
} }
} }

View File

@ -1,8 +1,29 @@
import { getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
import type { SessionSaveData } from "#app/system/game-data"; import { globalScene } from "#app/global-scene";
import { DexAttr, type SessionSaveData, type SystemSaveData } from "#app/system/game-data";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
export const systemMigrators = [] as const; export const systemMigrators = [
/**
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
* unlock the default form.
* @param data {@linkcode SystemSaveData}
*/
function migrateUnselectableForms(data: SystemSaveData) {
if (data.starterData && data.dexData) {
Object.keys(data.starterData).forEach(sd => {
const caughtAttr = data.dexData[sd]?.caughtAttr;
const species = getPokemonSpecies(Number(sd));
if (caughtAttr && species.forms?.length > 1) {
const selectableForms = species.forms.filter((form, formIndex) => form.isStarterSelectable && (caughtAttr & globalScene.gameData.getFormAttr(formIndex)));
if (selectableForms.length === 0) {
data.dexData[sd].caughtAttr += DexAttr.DEFAULT_FORM;
}
}
});
}
},
] as const;
export const settingsMigrators = [] as const; export const settingsMigrators = [] as const;

View File

@ -169,7 +169,7 @@ const timedEvents: TimedEvent[] = [
{ species: Species.WOOBAT }, { species: Species.WOOBAT },
{ species: Species.FRILLISH }, { species: Species.FRILLISH },
{ species: Species.ALOMOMOLA }, { species: Species.ALOMOMOLA },
{ species: Species.FURFROU, formIndex: 1 }, // Heart trim { species: Species.FURFROU, formIndex: 1 }, // Heart Trim
{ species: Species.ESPURR }, { species: Species.ESPURR },
{ species: Species.SPRITZEE }, { species: Species.SPRITZEE },
{ species: Species.SWIRLIX }, { species: Species.SWIRLIX },
@ -180,6 +180,33 @@ const timedEvents: TimedEvent[] = [
{ species: Species.ENAMORUS } { species: Species.ENAMORUS }
], ],
luckBoostedSpecies: [ Species.LUVDISC ] luckBoostedSpecies: [ Species.LUVDISC ]
},
{
name: "PKMNDAY2025",
eventType: EventType.LUCK,
startDate: new Date(Date.UTC(2025, 1, 27)),
endDate: new Date(Date.UTC(2025, 2, 4)),
classicFriendshipMultiplier: 4,
bannerKey: "pkmnday2025event-",
scale: 0.21,
availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ],
eventEncounters: [
{ species: Species.PIKACHU, formIndex: 1, blockEvolution: true }, // Partner Form
{ species: Species.EEVEE, formIndex: 1, blockEvolution: true }, // Partner Form
{ species: Species.CHIKORITA },
{ species: Species.TOTODILE },
{ species: Species.TEPIG }
],
luckBoostedSpecies: [
Species.PICHU, Species.PIKACHU, Species.RAICHU, Species.ALOLA_RAICHU,
Species.PSYDUCK, Species.GOLDUCK,
Species.EEVEE, Species.FLAREON, Species.JOLTEON, Species.VAPOREON, Species.ESPEON, Species.UMBREON, Species.LEAFEON, Species.GLACEON, Species.SYLVEON,
Species.CHIKORITA, Species.BAYLEEF, Species.MEGANIUM,
Species.TOTODILE, Species.CROCONAW, Species.FERALIGATR,
Species.TEPIG, Species.PIGNITE, Species.EMBOAR,
Species.ZYGARDE,
Species.ETERNAL_FLOETTE
]
} }
]; ];

View File

@ -109,7 +109,7 @@ export class UiInputs {
[Button.CYCLE_GENDER]: () => undefined, [Button.CYCLE_GENDER]: () => undefined,
[Button.CYCLE_ABILITY]: () => undefined, [Button.CYCLE_ABILITY]: () => undefined,
[Button.CYCLE_NATURE]: () => undefined, [Button.CYCLE_NATURE]: () => undefined,
[Button.CYCLE_TERA]: () => undefined, [Button.CYCLE_TERA]: () => this.buttonInfo(false),
[Button.SPEED_UP]: () => undefined, [Button.SPEED_UP]: () => undefined,
[Button.SLOW_DOWN]: () => undefined, [Button.SLOW_DOWN]: () => undefined,
}; };

View File

@ -147,7 +147,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
itemIcon.setScale(3 * this.scale); itemIcon.setScale(3 * this.scale);
this.optionSelectIcons.push(itemIcon); this.optionSelectIcons.push(itemIcon);
this.optionSelectContainer.add(itemIcon); this.optionSelectTextContainer.add(itemIcon);
itemIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); itemIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3));
@ -156,7 +156,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
itemOverlayIcon.setScale(3 * this.scale); itemOverlayIcon.setScale(3 * this.scale);
this.optionSelectIcons.push(itemOverlayIcon); this.optionSelectIcons.push(itemOverlayIcon);
this.optionSelectContainer.add(itemOverlayIcon); this.optionSelectTextContainer.add(itemOverlayIcon);
itemOverlayIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); itemOverlayIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3));

View File

@ -10,6 +10,7 @@ import { globalScene } from "#app/global-scene";
import { TerastallizeAccessModifier } from "#app/modifier/modifier"; import { TerastallizeAccessModifier } from "#app/modifier/modifier";
import { Type } from "#app/enums/type"; import { Type } from "#app/enums/type";
import { getTypeRgb } from "#app/data/type"; import { getTypeRgb } from "#app/data/type";
import { Species } from "#enums/species";
export enum Command { export enum Command {
FIGHT = 0, FIGHT = 0,
@ -180,9 +181,11 @@ export default class CommandUiHandler extends UiHandler {
canTera(): boolean { canTera(): boolean {
const hasTeraMod = !!globalScene.getModifiers(TerastallizeAccessModifier).length; const hasTeraMod = !!globalScene.getModifiers(TerastallizeAccessModifier).length;
const activePokemon = globalScene.getField()[this.fieldIndex];
const isBlockedForm = activePokemon.isMega() || activePokemon.isMax() || activePokemon.hasSpecies(Species.NECROZMA, "ultra");
const currentTeras = globalScene.arena.playerTerasUsed; const currentTeras = globalScene.arena.playerTerasUsed;
const plannedTera = globalScene.currentBattle.preTurnCommands[0]?.command === Command.TERA && this.fieldIndex > 0 ? 1 : 0; const plannedTera = globalScene.currentBattle.preTurnCommands[0]?.command === Command.TERA && this.fieldIndex > 0 ? 1 : 0;
return hasTeraMod && (currentTeras + plannedTera) < 1; return hasTeraMod && !isBlockedForm && (currentTeras + plannedTera) < 1;
} }
toggleTeraButton() { toggleTeraButton() {

View File

@ -629,6 +629,8 @@ export class DropDown extends Phaser.GameObjects.Container {
} }
} }
} }
this.onChange();
} }
} }

View File

@ -226,7 +226,9 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
} }
if (!this.cursorObj) { if (!this.cursorObj) {
this.cursorObj = globalScene.add.image(0, 0, "cursor"); const isTera = this.fromCommand === Command.TERA;
this.cursorObj = globalScene.add.image(0, 0, isTera ? "cursor_tera" : "cursor");
this.cursorObj.setScale(isTera ? 0.7 : 1);
ui.add(this.cursorObj); ui.add(this.cursorObj);
} }

View File

@ -86,6 +86,15 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.dropDowns[this.columns.indexOf(col)]; return this.dropDowns[this.columns.indexOf(col)];
} }
/**
* Get the DropDownColumn associated to a given index
* @param index the index of the column to retrieve
* @returns the associated DropDownColumn if it exists, undefined otherwise
*/
public getColumn(index: number): DropDownColumn {
return this.columns[index];
}
/** /**
* Highlight the labels of the FilterBar if the filters are different from their default values * Highlight the labels of the FilterBar if the filters are different from their default values
*/ */
@ -185,6 +194,11 @@ export class FilterBar extends Phaser.GameObjects.Container {
return this.getFilter(col).getVals(); return this.getFilter(col).getVals();
} }
public resetSelection(col: DropDownColumn): void {
this.dropDowns[col].resetToDefault();
this.updateFilterLabels();
}
setValsToDefault(): void { setValsToDefault(): void {
for (const dropDown of this.dropDowns) { for (const dropDown of this.dropDowns) {
dropDown.resetToDefault(); dropDown.resetToDefault();

View File

@ -422,7 +422,10 @@ export default class PartyUiHandler extends MessageUiHandler {
if (option === PartyOption.TRANSFER) { if (option === PartyOption.TRANSFER) {
if (this.transferCursor !== this.cursor) { if (this.transferCursor !== this.cursor) {
if (this.transferAll) { if (this.transferAll) {
getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor]).forEach((_, i) => (this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, i, this.transferQuantitiesMax[i], this.cursor)); getTransferrableItemsFromPokemon(globalScene.getPlayerParty()[this.transferCursor]).forEach((_, i, array) => {
const invertedIndex = array.length - 1 - i;
(this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, invertedIndex, this.transferQuantitiesMax[invertedIndex], this.cursor);
});
} else { } else {
(this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, this.transferOptionCursor, this.transferQuantities[this.transferOptionCursor], this.cursor); (this.selectCallback as PartyModifierTransferSelectCallback)(this.transferCursor, this.transferOptionCursor, this.transferQuantities[this.transferOptionCursor], this.cursor);
} }
@ -1187,7 +1190,6 @@ class PartySlot extends Phaser.GameObjects.Container {
public slotHpText: Phaser.GameObjects.Text; public slotHpText: Phaser.GameObjects.Text;
public slotDescriptionLabel: Phaser.GameObjects.Text; // this is used to show text instead of the HP bar i.e. for showing "Able"/"Not Able" for TMs when you try to learn them public slotDescriptionLabel: Phaser.GameObjects.Text; // this is used to show text instead of the HP bar i.e. for showing "Able"/"Not Able" for TMs when you try to learn them
private pokemonIcon: Phaser.GameObjects.Container; private pokemonIcon: Phaser.GameObjects.Container;
private iconAnimHandler: PokemonIconAnimHandler; private iconAnimHandler: PokemonIconAnimHandler;
@ -1208,6 +1210,10 @@ class PartySlot extends Phaser.GameObjects.Container {
} }
setup(partyUiMode: PartyUiMode, tmMoveId: Moves) { setup(partyUiMode: PartyUiMode, tmMoveId: Moves) {
const currentLanguage = i18next.resolvedLanguage ?? "en";
const offsetJa = currentLanguage === "ja";
const battlerCount = globalScene.currentBattle.getBattlerCount(); const battlerCount = globalScene.currentBattle.getBattlerCount();
const slotKey = `party_slot${this.slotIndex >= battlerCount ? "" : "_main"}`; const slotKey = `party_slot${this.slotIndex >= battlerCount ? "" : "_main"}`;
@ -1246,15 +1252,15 @@ class PartySlot extends Phaser.GameObjects.Container {
nameSizeTest.destroy(); nameSizeTest.destroy();
this.slotName = addTextObject(0, 0, displayName, TextStyle.PARTY); this.slotName = addTextObject(0, 0, displayName, TextStyle.PARTY);
this.slotName.setPositionRelative(slotBg, this.slotIndex >= battlerCount ? 21 : 24, this.slotIndex >= battlerCount ? 2 : 10); this.slotName.setPositionRelative(slotBg, this.slotIndex >= battlerCount ? 21 : 24, (this.slotIndex >= battlerCount ? 2 : 10) + (offsetJa ? 2 : 0));
this.slotName.setOrigin(0, 0); this.slotName.setOrigin(0, 0);
const slotLevelLabel = globalScene.add.image(0, 0, "party_slot_overlay_lv"); const slotLevelLabel = globalScene.add.image(0, 0, "party_slot_overlay_lv");
slotLevelLabel.setPositionRelative(this.slotName, 8, 12); slotLevelLabel.setPositionRelative(slotBg, (this.slotIndex >= battlerCount ? 21 : 24) + 8, (this.slotIndex >= battlerCount ? 2 : 10) + 12);
slotLevelLabel.setOrigin(0, 0); slotLevelLabel.setOrigin(0, 0);
const slotLevelText = addTextObject(0, 0, this.pokemon.level.toString(), this.pokemon.level < globalScene.getMaxExpLevel() ? TextStyle.PARTY : TextStyle.PARTY_RED); const slotLevelText = addTextObject(0, 0, this.pokemon.level.toString(), this.pokemon.level < globalScene.getMaxExpLevel() ? TextStyle.PARTY : TextStyle.PARTY_RED);
slotLevelText.setPositionRelative(slotLevelLabel, 9, 0); slotLevelText.setPositionRelative(slotLevelLabel, 9, offsetJa ? 1.5 : 0);
slotLevelText.setOrigin(0, 0.25); slotLevelText.setOrigin(0, 0.25);
slotInfoContainer.add([ this.slotName, slotLevelLabel, slotLevelText ]); slotInfoContainer.add([ this.slotName, slotLevelLabel, slotLevelText ]);
@ -1331,7 +1337,7 @@ class PartySlot extends Phaser.GameObjects.Container {
this.slotHpOverlay.setVisible(false); this.slotHpOverlay.setVisible(false);
this.slotHpText = addTextObject(0, 0, `${this.pokemon.hp}/${this.pokemon.getMaxHp()}`, TextStyle.PARTY); this.slotHpText = addTextObject(0, 0, `${this.pokemon.hp}/${this.pokemon.getMaxHp()}`, TextStyle.PARTY);
this.slotHpText.setPositionRelative(this.slotHpBar, this.slotHpBar.width - 3, this.slotHpBar.height - 2); this.slotHpText.setPositionRelative(this.slotHpBar, this.slotHpBar.width - 3, this.slotHpBar.height - 2 + (offsetJa ? 2 : 0));
this.slotHpText.setOrigin(1, 0); this.slotHpText.setOrigin(1, 0);
this.slotHpText.setVisible(false); this.slotHpText.setVisible(false);

View File

@ -250,6 +250,8 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
private availableVariants: number; private availableVariants: number;
private unlockedVariants: boolean[]; private unlockedVariants: boolean[];
private canUseCandies: boolean;
constructor() { constructor() {
super(Mode.POKEDEX_PAGE); super(Mode.POKEDEX_PAGE);
} }
@ -556,6 +558,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
show(args: any[]): boolean { show(args: any[]): boolean {
// Allow the use of candies if we are in one of the whitelisted phases
this.canUseCandies = [ "TitlePhase", "SelectStarterPhase", "CommandPhase" ].includes(globalScene.getCurrentPhase()?.constructor.name ?? "");
if (args.length >= 1 && args[0] === "refresh") { if (args.length >= 1 && args[0] === "refresh") {
return false; return false;
} else { } else {
@ -597,6 +602,14 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.battleForms = []; this.battleForms = [];
const species = this.species; const species = this.species;
let formKey = this.species?.forms.length > 0 ? this.species.forms[this.formIndex].formKey : "";
this.isFormGender = formKey === "male" || formKey === "female";
if (this.isFormGender && ((this.savedStarterAttributes.female === true && formKey === "male") || (this.savedStarterAttributes.female === false && formKey === "female"))) {
this.formIndex = (this.formIndex + 1) % 2;
formKey = this.species.forms[this.formIndex].formKey;
}
const formIndex = this.formIndex ?? 0; const formIndex = this.formIndex ?? 0;
this.starterId = this.getStarterSpeciesId(this.species.speciesId); this.starterId = this.getStarterSpeciesId(this.species.speciesId);
@ -630,12 +643,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
this.eggMoves = speciesEggMoves[this.starterId] ?? []; this.eggMoves = speciesEggMoves[this.starterId] ?? [];
this.hasEggMoves = Array.from({ length: 4 }, (_, em) => (globalScene.gameData.starterData[this.starterId].eggMoves & (1 << em)) !== 0); this.hasEggMoves = Array.from({ length: 4 }, (_, em) => (globalScene.gameData.starterData[this.starterId].eggMoves & (1 << em)) !== 0);
const formKey = this.species?.forms.length > 0 ? this.species.forms[this.formIndex].formKey : "";
this.tmMoves = speciesTmMoves[species.speciesId]?.filter(m => Array.isArray(m) ? (m[0] === formKey ? true : false ) : true) this.tmMoves = speciesTmMoves[species.speciesId]?.filter(m => Array.isArray(m) ? (m[0] === formKey ? true : false ) : true)
.map(m => Array.isArray(m) ? m[1] : m).sort((a, b) => allMoves[a].name > allMoves[b].name ? 1 : -1) ?? []; .map(m => Array.isArray(m) ? m[1] : m).sort((a, b) => allMoves[a].name > allMoves[b].name ? 1 : -1) ?? [];
this.isFormGender = formKey === "male" || formKey === "female";
const passiveId = starterPassiveAbilities.hasOwnProperty(species.speciesId) ? species.speciesId : const passiveId = starterPassiveAbilities.hasOwnProperty(species.speciesId) ? species.speciesId :
starterPassiveAbilities.hasOwnProperty(this.starterId) ? this.starterId : pokemonPrevolutions[this.starterId]; starterPassiveAbilities.hasOwnProperty(this.starterId) ? this.starterId : pokemonPrevolutions[this.starterId];
const passives = starterPassiveAbilities[passiveId]; const passives = starterPassiveAbilities[passiveId];
@ -779,6 +789,10 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
const formIndex = otherFormIndex !== undefined ? otherFormIndex : this.formIndex; const formIndex = otherFormIndex !== undefined ? otherFormIndex : this.formIndex;
const caughtAttr = this.isCaught(species); const caughtAttr = this.isCaught(species);
if (caughtAttr && (!species.forms.length || species.forms.length === 1)) {
return true;
}
const isFormCaught = (caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n; const isFormCaught = (caughtAttr & globalScene.gameData.getFormAttr(formIndex ?? 0)) > 0n;
return isFormCaught; return isFormCaught;
} }
@ -1570,15 +1584,14 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
starterAttributes.variant = newVariant; // store the selected variant starterAttributes.variant = newVariant; // store the selected variant
this.savedStarterAttributes.variant = starterAttributes.variant; this.savedStarterAttributes.variant = starterAttributes.variant;
if (newVariant > props.variant) { if ((this.isCaught() & DexAttr.NON_SHINY) && (newVariant <= props.variant)) {
this.setSpeciesDetails(this.species, { variant: newVariant as Variant });
success = true;
} else {
this.setSpeciesDetails(this.species, { shiny: false, variant: 0 }); this.setSpeciesDetails(this.species, { shiny: false, variant: 0 });
success = true; success = true;
starterAttributes.shiny = false; starterAttributes.shiny = false;
this.savedStarterAttributes.shiny = starterAttributes.shiny; this.savedStarterAttributes.shiny = starterAttributes.shiny;
} else {
this.setSpeciesDetails(this.species, { variant: newVariant as Variant });
success = true;
} }
} }
} }
@ -1626,7 +1639,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
} }
break; break;
case Button.STATS: case Button.STATS:
if (!isCaught || !isFormCaught) { if (!isCaught || !isFormCaught || !this.canUseCandies) {
error = true; error = true;
} else { } else {
const ui = this.getUi(); const ui = this.getUi();
@ -1888,7 +1901,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
if (this.isCaught()) { if (this.isCaught()) {
if (isFormCaught) { if (isFormCaught) {
this.updateButtonIcon(SettingKeyboard.Button_Stats, gamepadType, this.candyUpgradeIconElement, this.candyUpgradeLabel); if (this.canUseCandies) {
this.updateButtonIcon(SettingKeyboard.Button_Stats, gamepadType, this.candyUpgradeIconElement, this.candyUpgradeLabel);
}
if (this.canCycleShiny) { if (this.canCycleShiny) {
this.updateButtonIcon(SettingKeyboard.Button_Cycle_Shiny, gamepadType, this.shinyIconElement, this.shinyLabel); this.updateButtonIcon(SettingKeyboard.Button_Cycle_Shiny, gamepadType, this.shinyIconElement, this.shinyLabel);
} }
@ -2189,7 +2204,8 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY); const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY); const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
this.canCycleShiny = isNonShinyCaught && isShinyCaught; const caughtVariants = [ DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3 ].filter(v => caughtAttr & v);
this.canCycleShiny = (isNonShinyCaught && isShinyCaught) || (isShinyCaught && caughtVariants.length > 1);
const isMaleCaught = !!(caughtAttr & DexAttr.MALE); const isMaleCaught = !!(caughtAttr & DexAttr.MALE);
const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE); const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE);

View File

@ -898,16 +898,11 @@ export default class PokedexUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) { if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it // CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor); this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible pokemon go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true; success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBarCursor).hasDefaultValues()) {
this.filterBar.resetSelection(this.filterBarCursor);
this.updateStarters();
success = true;
} else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) { } else if (this.filterTextMode && !(this.filterText.getValue(this.filterTextCursor) === this.filterText.defaultText)) {
this.filterText.resetSelection(this.filterTextCursor); this.filterText.resetSelection(this.filterTextCursor);
success = true; success = true;
@ -991,7 +986,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
this.updateScroll(); this.updateScroll();
const proportion = this.filterBarCursor / Math.max(1, this.filterBar.numFilters - 1); const proportion = this.filterBarCursor / Math.max(1, this.filterBar.numFilters - 1);
const targetCol = Math.min(8, proportion < 0.5 ? Math.floor(proportion * 8) : Math.ceil(proportion * 8)); const targetCol = Math.min(8, proportion < 0.5 ? Math.floor(proportion * 8) : Math.ceil(proportion * 8));
this.setCursor(Math.min(targetCol, numberOfStarters)); this.setCursor(Math.min(targetCol, numberOfStarters - 1));
success = true; success = true;
} }
break; break;
@ -1113,7 +1108,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
} }
break; break;
case Button.DOWN: case Button.DOWN:
if (currentRow < numOfRows - 1) { // not last row if ((currentRow < numOfRows - 1) && (this.cursor + 9 < this.filteredPokemonData.length)) { // not last row
if (currentRow - this.scrollCursor === 8) { // last row of visible pokemon if (currentRow - this.scrollCursor === 8) { // last row of visible pokemon
this.scrollCursor++; this.scrollCursor++;
this.updateScroll(); this.updateScroll();
@ -1582,6 +1577,37 @@ export default class PokedexUiHandler extends MessageUiHandler {
container.icon.setTint(0); container.icon.setTint(0);
} }
if (data.eggMove1) {
container.eggMove1Icon.setVisible(true);
} else {
container.eggMove1Icon.setVisible(false);
}
if (data.eggMove2) {
container.eggMove2Icon.setVisible(true);
} else {
container.eggMove2Icon.setVisible(false);
}
if (data.tmMove1) {
container.tmMove1Icon.setVisible(true);
} else {
container.tmMove1Icon.setVisible(false);
}
if (data.tmMove2) {
container.tmMove2Icon.setVisible(true);
} else {
container.tmMove2Icon.setVisible(false);
}
if (data.passive1) {
container.passive1Icon.setVisible(true);
} else {
container.passive1Icon.setVisible(false);
}
if (data.passive2) {
container.passive2Icon.setVisible(true);
} else {
container.passive2Icon.setVisible(false);
}
if (this.showDecorations) { if (this.showDecorations) {
if (this.pokerusSpecies.includes(data.species)) { if (this.pokerusSpecies.includes(data.species)) {

View File

@ -8,6 +8,7 @@ import i18next from "i18next";
import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext";
import { starterColors } from "#app/battle-scene"; import { starterColors } from "#app/battle-scene";
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { Ability } from "#app/data/ability";
import { allAbilities } from "#app/data/ability"; import { allAbilities } from "#app/data/ability";
import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { speciesEggMoves } from "#app/data/balance/egg-moves";
import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp";
@ -1080,10 +1081,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
/** /**
* Set the selections for all filters to their default starting value * Set the selections for all filters to their default starting value
*/ */
resetFilters() : void { public resetFilters(): void {
this.filterBar.setValsToDefault();
this.resetCaughtDropdown();
}
/**
* Set default value for the caught dropdown, which only shows caught mons
*/
public resetCaughtDropdown(): void {
const caughtDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.CAUGHT); const caughtDropDown: DropDown = this.filterBar.getFilter(DropDownColumn.CAUGHT);
this.filterBar.setValsToDefault(); caughtDropDown.resetToDefault();
// initial setting, in caught filter, select the options excluding the uncaught option // initial setting, in caught filter, select the options excluding the uncaught option
for (let i = 0; i < caughtDropDown.options.length; i++) { for (let i = 0; i < caughtDropDown.options.length; i++) {
@ -1323,16 +1332,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (this.filterMode && this.filterBar.openDropDown) { if (this.filterMode && this.filterBar.openDropDown) {
// CANCEL with a filter menu open > close it // CANCEL with a filter menu open > close it
this.filterBar.toggleDropDown(this.filterBarCursor); this.filterBar.toggleDropDown(this.filterBarCursor);
// if there are possible starters go the first one of the list
if (numberOfStarters > 0) {
this.setFilterMode(false);
this.scrollCursor = 0;
this.updateScroll();
this.setCursor(0);
}
success = true; success = true;
} else if (this.filterMode && !this.filterBar.getFilter(this.filterBar.getColumn(this.filterBarCursor)).hasDefaultValues()) {
if (this.filterBar.getColumn(this.filterBarCursor) === DropDownColumn.CAUGHT) {
this.resetCaughtDropdown();
} else {
this.filterBar.resetSelection(this.filterBarCursor);
}
this.updateStarters();
success = true;
} else if (this.statsMode) { } else if (this.statsMode) {
this.toggleStatsMode(false); this.toggleStatsMode(false);
success = true; success = true;
@ -2060,20 +2068,20 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
} while (newVariant !== props.variant); } while (newVariant !== props.variant);
starterAttributes.variant = newVariant; // store the selected variant starterAttributes.variant = newVariant; // store the selected variant
// If going to a higher variant, display that if ((this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY) && (newVariant <= props.variant)) {
if (newVariant > props.variant) { // If we have run out of variants, go back to non shiny
this.setSpeciesDetails(this.lastSpecies, { shiny: false, variant: 0 });
this.pokemonShinyIcon.setVisible(false);
success = true;
starterAttributes.shiny = false;
} else {
// If going to a higher variant, or only shiny forms are caught, go to next variant
this.setSpeciesDetails(this.lastSpecies, { variant: newVariant as Variant }); this.setSpeciesDetails(this.lastSpecies, { variant: newVariant as Variant });
// Cycle tint based on current sprite tint // Cycle tint based on current sprite tint
const tint = getVariantTint(newVariant as Variant); const tint = getVariantTint(newVariant as Variant);
this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant));
this.pokemonShinyIcon.setTint(tint); this.pokemonShinyIcon.setTint(tint);
success = true; success = true;
// If we have run out of variants, go back to non shiny
} else {
this.setSpeciesDetails(this.lastSpecies, { shiny: false, variant: 0 });
this.pokemonShinyIcon.setVisible(false);
success = true;
starterAttributes.shiny = false;
} }
} }
} }
@ -3320,7 +3328,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY); const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY); const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
this.canCycleShiny = isNonShinyCaught && isShinyCaught; const caughtVariants = [ DexAttr.DEFAULT_VARIANT, DexAttr.VARIANT_2, DexAttr.VARIANT_3 ].filter(v => caughtAttr & v);
this.canCycleShiny = (isNonShinyCaught && isShinyCaught) || (isShinyCaught && caughtVariants.length > 1);
const isMaleCaught = !!(caughtAttr & DexAttr.MALE); const isMaleCaught = !!(caughtAttr & DexAttr.MALE);
const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE); const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE);
@ -3344,7 +3353,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey)) this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey))
.map((_, f) => dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f)).filter(f => f).length > 1; .map((_, f) => dexEntry.caughtAttr & globalScene.gameData.getFormAttr(f)).filter(f => f).length > 1;
this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1; this.canCycleNature = globalScene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
this.canCycleTera = globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && !Utils.isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2); this.canCycleTera = !this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && !Utils.isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2);
} }
if (dexEntry.caughtAttr && species.malePercent !== null) { if (dexEntry.caughtAttr && species.malePercent !== null) {
@ -3357,7 +3366,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
if (dexEntry.caughtAttr) { if (dexEntry.caughtAttr) {
const ability = allAbilities[this.lastSpecies.getAbility(abilityIndex!)]; // TODO: is this bang correct? let ability: Ability;
if (this.lastSpecies.forms?.length > 1) {
ability = allAbilities[this.lastSpecies.forms[formIndex ?? 0].getAbility(abilityIndex!)];
} else {
ability = allAbilities[this.lastSpecies.getAbility(abilityIndex!)]; // TODO: is this bang correct?
}
this.pokemonAbilityText.setText(ability.name); this.pokemonAbilityText.setText(ability.name);
const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1); const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1);
@ -3844,12 +3858,20 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.showStats(); this.showStats();
this.statsMode = true; this.statsMode = true;
this.pokemonSprite.setVisible(false); this.pokemonSprite.setVisible(false);
this.teraIcon.setVisible(false);
this.canCycleTera = false;
this.updateInstructions();
} else { } else {
this.statsMode = false; this.statsMode = false;
this.statsContainer.setVisible(false); this.statsContainer.setVisible(false);
this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr);
//@ts-ignore //@ts-ignore
this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!?
this.teraIcon.setVisible(globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id));
const props = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId));
const formIndex = props.formIndex;
this.canCycleTera = !this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && !Utils.isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2);
this.updateInstructions();
} }
} }

View File

@ -28,6 +28,7 @@ import { modifierSortFunc } from "#app/modifier/modifier";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { Stat, PERMANENT_STATS, getStatKey } from "#enums/stat"; import { Stat, PERMANENT_STATS, getStatKey } from "#enums/stat";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { achvs } from "#app/system/achv";
enum Page { enum Page {
PROFILE, PROFILE,
@ -783,9 +784,6 @@ export default class SummaryUiHandler extends UiHandler {
if (types.length > 1) { if (types.length > 1) {
profileContainer.add(getTypeIcon(1, types[1])); profileContainer.add(getTypeIcon(1, types[1]));
} }
if (this.pokemon?.isTerastallized) {
profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true));
}
if (this.pokemon?.getLuck()) { if (this.pokemon?.getLuck()) {
const luckLabelText = addTextObject(141, 28, i18next.t("common:luckIndicator"), TextStyle.SUMMARY_ALT); const luckLabelText = addTextObject(141, 28, i18next.t("common:luckIndicator"), TextStyle.SUMMARY_ALT);
@ -798,6 +796,13 @@ export default class SummaryUiHandler extends UiHandler {
profileContainer.add(luckText); profileContainer.add(luckText);
} }
if (globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && !Utils.isNullOrUndefined(this.pokemon)) {
const teraIcon = globalScene.add.sprite(123, 26, "button_tera");
teraIcon.setName("terrastallize-icon");
teraIcon.setFrame(Type[this.pokemon.getTeraType()].toLowerCase());
profileContainer.add(teraIcon);
}
this.abilityContainer = { this.abilityContainer = {
labelImage: globalScene.add.image(0, 0, "summary_profile_ability"), labelImage: globalScene.add.image(0, 0, "summary_profile_ability"),
ability: this.pokemon?.getAbility(true)!, // TODO: is this bang correct? ability: this.pokemon?.getAbility(true)!, // TODO: is this bang correct?

View File

@ -0,0 +1,165 @@
import { BattlerIndex } from "#app/battle";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Moves } from "#enums/moves";
import { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Abilities - Neutralizing Gas", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([ Moves.SPLASH ])
.ability(Abilities.NEUTRALIZING_GAS)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH);
});
it("should prevent other abilities from activating", async () => {
game.override.enemyAbility(Abilities.INTIMIDATE);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed, so the attack stat should not be lowered
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
});
it("should allow the user's passive to activate", async () => {
game.override.passiveAbility(Abilities.INTREPID_SWORD);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1);
});
it.todo("should activate before other abilities", async () => {
game.override.enemySpecies(Species.ACCELGOR)
.enemyLevel(100)
.enemyAbility(Abilities.INTIMIDATE);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed even when the user's speed is lower
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
});
it("should activate other abilities when removed", async () => {
game.override.enemyAbility(Abilities.INTREPID_SWORD)
.enemyPassiveAbility(Abilities.DAUNTLESS_SHIELD)
.enemyMoveset(Moves.ENTRAINMENT);
await game.classicMode.startBattle([ Species.FEEBAS ]);
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(0);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Enemy removes user's ability, so both abilities are activated
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(1);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(1);
});
it("should not activate the user's other ability when removed", async () => {
game.override.passiveAbility(Abilities.INTIMIDATE)
.enemyMoveset(Moves.ENTRAINMENT);
await game.classicMode.startBattle([ Species.FEEBAS ]);
// Neutralising gas user's passive is still active
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Intimidate did not reactivate after neutralizing gas was removed
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
});
it("should only deactivate when all setters are off the field", async () => {
game.override.enemyMoveset([ Moves.ENTRAINMENT, Moves.SPLASH ])
.battleType("double");
await game.classicMode.startBattle([ Species.ACCELGOR, Species.ACCELGOR ]);
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); // Now one neut gas user is left
game.move.select(Moves.SPLASH, 0);
game.move.select(Moves.SPLASH, 1);
await game.forceEnemyMove(Moves.ENTRAINMENT, BattlerIndex.PLAYER_2);
await game.forceEnemyMove(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined(); // No neut gas users are left
});
it("should deactivate when suppressed by gastro acid", async () => {
game.override.enemyMoveset(Moves.GASTRO_ACID);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
});
it("should deactivate when the pokemon faints", async () => {
game.override.ability(Abilities.BALL_FETCH)
.enemyAbility(Abilities.NEUTRALIZING_GAS);
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.SPLASH);
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
await game.doKillOpponents();
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
});
it("should deactivate upon catching a wild pokemon", async () => {
game.override
.battleType("single")
.enemyAbility(Abilities.NEUTRALIZING_GAS)
.ability(Abilities.BALL_FETCH);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
game.scene.pokeballCounts[PokeballType.MASTER_BALL] = 1;
game.doThrowPokeball(PokeballType.MASTER_BALL);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
});
});

View File

@ -140,6 +140,6 @@ describe("Moves - Focus Punch", () => {
await game.phaseInterceptor.to("MessagePhase", false); await game.phaseInterceptor.to("MessagePhase", false);
const consoleSpy = vi.spyOn(console, "log"); const consoleSpy = vi.spyOn(console, "log");
await game.phaseInterceptor.to("MoveEndPhase", true); await game.phaseInterceptor.to("MoveEndPhase", true);
expect(consoleSpy).nthCalledWith(1, i18next.t("moveTriggers:lostFocus")); expect(consoleSpy).nthCalledWith(1, i18next.t("moveTriggers:lostFocus", { pokemonName: "Charizard" }));
}); });
}); });

View File

@ -46,11 +46,8 @@ describe("Moves - Shed Tail", () => {
expect(feebas).not.toBe(magikarp); expect(feebas).not.toBe(magikarp);
expect(feebas.hp).toBe(feebas.getMaxHp()); expect(feebas.hp).toBe(feebas.getMaxHp());
// Note: Shed Tail's HP cost is currently not accurate to mainline, as it // Note: Altered the test to be consistent with the correct HP cost :yipeevee_static:
// should cost ceil(maxHP / 2) instead of max(floor(maxHp / 2), 1). The current expect(magikarp.hp).toBe(Math.floor(magikarp.getMaxHp() / 2));
// implementation is consistent with Substitute's HP cost logic, but that's not
// the case in mainline for some reason :regiDespair:.
expect(magikarp.hp).toBe(Math.ceil(magikarp.getMaxHp() / 2));
expect(substituteTag).toBeDefined(); expect(substituteTag).toBeDefined();
expect(substituteTag?.hp).toBe(Math.floor(magikarp.getMaxHp() / 4)); expect(substituteTag?.hp).toBe(Math.floor(magikarp.getMaxHp() / 4));
}); });

View File

@ -223,14 +223,14 @@ describe("Mystery Encounter Utils", () => {
}); });
it("excludes species from search", () => { it("excludes species from search", () => {
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian // Only 9 tiers are: Kyogre, Groudon, Rayquaza, Arceus, Zacian, Koraidon, Miraidon, Terapagos
const result = getRandomSpeciesByStarterCost(9, [ Species.KORAIDON, Species.MIRAIDON, Species.ARCEUS, Species.RAYQUAZA, Species.KYOGRE, Species.GROUDON ]); const result = getRandomSpeciesByStarterCost(9, [ Species.KYOGRE, Species.GROUDON, Species.RAYQUAZA, Species.ARCEUS, Species.KORAIDON, Species.MIRAIDON, Species.TERAPAGOS ]);
const pokeSpecies = getPokemonSpecies(result); const pokeSpecies = getPokemonSpecies(result);
expect(pokeSpecies.speciesId).toBe(Species.ZACIAN); expect(pokeSpecies.speciesId).toBe(Species.ZACIAN);
}); });
it("gets species of specified types", () => { it("gets species of specified types", () => {
// Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian // Only 9 tiers are: Kyogre, Groudon, Rayquaza, Arceus, Zacian, Koraidon, Miraidon, Terapagos
const result = getRandomSpeciesByStarterCost(9, undefined, [ Type.GROUND ]); const result = getRandomSpeciesByStarterCost(9, undefined, [ Type.GROUND ]);
const pokeSpecies = getPokemonSpecies(result); const pokeSpecies = getPokemonSpecies(result);
expect(pokeSpecies.speciesId).toBe(Species.GROUDON); expect(pokeSpecies.speciesId).toBe(Species.GROUDON);