Merge branch 'main' into feature/local-server-mock

This commit is contained in:
Felix Staud 2024-06-09 15:09:36 -07:00
commit 1a561fa0fd
302 changed files with 47064 additions and 6788 deletions

1
.env
View File

@ -1,3 +1,4 @@
VITE_BYPASS_LOGIN=0
VITE_BYPASS_TUTORIAL=0
VITE_SERVER_URL=http://localhost:8001
VITE_MOCK_API=0

View File

@ -1,3 +1,4 @@
VITE_BYPASS_LOGIN=1
VITE_BYPASS_TUTORIAL=0
VITE_SERVER_URL=http://localhost:8001
VITE_MOCK_API=1

View File

@ -46,7 +46,14 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
- Keisuke Ito
- Arata Iiyoshi
- Atsuhiro Ishizuna
- Pokémon HeartGold/SoulSilver
- Pokémon Black/White 2
- Pokémon X/Y
- Pokémon Omega Ruby/Alpha Sapphire
- Pokémon Sun/Moon
- Pokémon Ultra Sun/Ultra Moon
- Pokémon Sword/Shield
- Pokémon Scarlet/Violet
- Firel (Custom Metropolis and Laboratory biome music)
- Lmz (Custom Jungle biome music)

View File

@ -146,13 +146,13 @@ body {
margin-left: 10%;
}
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn,
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadRectBtnContainer > .apadSqBtn:not(.apadBattle),
#touchControls:not([data-ui-mode='STARTER_SELECT']):not([data-ui-mode='SETTINGS']):not([data-ui-mode='SETTINGS_DISPLAY']):not([data-ui-mode='SETTINGS_AUDIO']):not([data-ui-mode='SETTINGS_GAMEPAD']):not([data-ui-mode='SETTINGS_KEYBOARD']) #apad .apadSqBtnContainer > .apadSqBtn:not(.apadBattle)
{
display: none;
}
#touchControls:not([data-ui-mode='COMMAND']):not([data-ui-mode='FIGHT']):not([data-ui-mode='BALL']):not([data-ui-mode='TARGET_SELECT']) #apad .apadBattle {
#touchControls:not([data-ui-mode='COMMAND']):not([data-ui-mode='FIGHT']):not([data-ui-mode='BALL']):not([data-ui-mode='TARGET_SELECT']):not([data-ui-mode='MODIFIER_SELECT']) #apad .apadBattle {
display: none;
}

View File

@ -11,6 +11,7 @@
"test": "vitest run",
"test:cov": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"test:silent": "vitest run --silent",
"eslint": "eslint --fix .",
"eslint-ci": "eslint .",
"docs": "typedoc"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1745,27 +1745,27 @@
1
],
"641-incarnate": [
1,
0,
0,
0
],
"641-therian": [
1,
0,
0,
0
],
"642-incarnate": [
1,
0,
0,
0
],
"642-therian": [
1,
0,
0,
0
],
"645-incarnate": [
1,
0,
0,
0
],
@ -4608,32 +4608,32 @@
1
],
"641-incarnate": [
1,
0,
0,
0
],
"641-therian": [
1,
0,
0,
0
],
"642-incarnate": [
1,
0,
0,
0
],
"642-therian": [
1,
0,
0,
0
],
"645-incarnate": [
1,
0,
0,
0
],
"645-therian": [
1,
0,
0,
0
],

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,41 @@
{
"textures": [
{
"image": "sailor.png",
"format": "RGBA8888",
"size": {
"w": 72,
"h": 72
},
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 72,
"h": 72
},
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 73,
"h": 73
},
"frame": {
"x": 0,
"y": 0,
"w": 39,
"h": 72
}
}
]
}
],
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:f692676a166fc1915532cd94d5799af4:fb833f76fb6797474657726bb59a7eee:aeb55e30992938f494b6cd2420158dda$"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -13,15 +13,15 @@ import { Biome } from "./data/enums/biome";
import { Arena, ArenaBase } from "./field/arena";
import { GameData } from "./system/game-data";
import { PlayerGender } from "./data/enums/player-gender";
import { TextStyle, addTextObject } from "./ui/text";
import { TextStyle, addTextObject, getTextColor } from "./ui/text";
import { Moves } from "./data/enums/moves";
import { allMoves } from "./data/move";
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getPartyLuckValue } from "./modifier/modifier-type";
import AbilityBar from "./ui/ability-bar";
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
import { allAbilities } from "./data/ability";
import Battle, { BattleType, FixedBattleConfig, fixedBattles } from "./battle";
import { GameMode, GameModes, gameModes } from "./game-mode";
import Battle, { BattleType, FixedBattleConfig } from "./battle";
import { GameMode, GameModes, getGameMode } from "./game-mode";
import FieldSpritePipeline from "./pipelines/field-sprite";
import SpritePipeline from "./pipelines/sprite";
import PartyExpBar from "./ui/party-exp-bar";
@ -62,6 +62,7 @@ import { NewArenaEvent } from "./battle-scene-events";
import { Abilities } from "./data/enums/abilities";
import ArenaFlyout from "./ui/arena-flyout";
import { EaseType } from "./ui/enums/ease-type";
import { ExpNotification } from "./enums/exp-notification";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -125,6 +126,7 @@ export default class BattleScene extends SceneBase {
public uiTheme: UiTheme = UiTheme.DEFAULT;
public windowType: integer = 0;
public experimentalSprites: boolean = false;
public musicPreference: integer = 0;
public moveAnimations: boolean = true;
public expGainsSpeed: integer = 0;
public skipSeenDialogues: boolean = false;
@ -141,7 +143,7 @@ export default class BattleScene extends SceneBase {
* Modes `1` and `2` are still compatible with stats display, level up, new move, etc.
* @default 0 - Uses the default normal experience gain display.
*/
public expParty: integer = 0;
public expParty: ExpNotification = 0;
public hpBarSpeed: integer = 0;
public fusionPaletteSwaps: boolean = true;
public enableTouchControls: boolean = false;
@ -153,12 +155,19 @@ export default class BattleScene extends SceneBase {
*/
public battleStyle: integer = 0;
/**
* Defines whether or not to show type effectiveness hints
* - true: No hints
* - false: Show hints for moves
*/
public typeHints: boolean = false;
public disableMenu: boolean = false;
public gameData: GameData;
public sessionSlotId: integer;
private phaseQueue: Phase[];
public phaseQueue: Phase[];
private phaseQueuePrepend: Phase[];
private phaseQueuePrependSpliceIndex: integer;
private nextCommandPhaseQueue: Phase[];
@ -200,7 +209,7 @@ export default class BattleScene extends SceneBase {
public arenaFlyout: ArenaFlyout;
private fieldOverlay: Phaser.GameObjects.Rectangle;
private modifiers: PersistentModifier[];
public modifiers: PersistentModifier[];
private enemyModifiers: PersistentModifier[];
public uiContainer: Phaser.GameObjects.Container;
public ui: UI;
@ -225,6 +234,7 @@ export default class BattleScene extends SceneBase {
public rngSeedOverride: string = "";
public rngOffset: integer = 0;
public inputMethod: string;
private infoToggles: InfoToggle[] = [];
/**
@ -299,7 +309,8 @@ export default class BattleScene extends SceneBase {
this.fieldSpritePipeline = new FieldSpritePipeline(this.game);
(this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.add("FieldSprite", this.fieldSpritePipeline);
this.time.delayedCall(20, () => this.launchBattle());
this.launchBattle();
}
update() {
@ -320,6 +331,7 @@ export default class BattleScene extends SceneBase {
const field = this.add.container(0, 0);
field.setScale(6);
field.setName("container-field");
this.field = field;
@ -409,28 +421,28 @@ export default class BattleScene extends SceneBase {
this.biomeWaveText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
this.biomeWaveText.setName("text-biome-wave");
this.biomeWaveText.setOrigin(1, 0);
this.biomeWaveText.setOrigin(1, 0.5);
this.fieldUI.add(this.biomeWaveText);
this.moneyText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY);
this.moneyText.setName("text-money");
this.moneyText.setOrigin(1, 0);
this.moneyText.setOrigin(1, 0.5);
this.fieldUI.add(this.moneyText);
this.scoreText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.scoreText.setName("text-score");
this.scoreText.setOrigin(1, 0);
this.scoreText.setOrigin(1, 0.5);
this.fieldUI.add(this.scoreText);
this.luckText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.PARTY, { fontSize: "54px" });
this.luckText.setName("text-luck");
this.luckText.setOrigin(1, 0);
this.luckText.setOrigin(1, 0.5);
this.luckText.setVisible(false);
this.fieldUI.add(this.luckText);
this.luckLabelText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "Luck:", TextStyle.PARTY, { fontSize: "54px" });
this.luckLabelText.setName("text-luck-label");
this.luckLabelText.setOrigin(1, 0);
this.luckLabelText.setOrigin(1, 0.5);
this.luckLabelText.setVisible(false);
this.fieldUI.add(this.luckLabelText);
@ -455,9 +467,13 @@ export default class BattleScene extends SceneBase {
const loadPokemonAssets = [];
this.arenaPlayer = new ArenaBase(this, true);
this.arenaPlayer.setName("container-arena-player");
this.arenaPlayerTransition = new ArenaBase(this, true);
this.arenaPlayerTransition.setName("container-arena-player-transition");
this.arenaEnemy = new ArenaBase(this, false);
this.arenaEnemy.setName("container-arena-enemy");
this.arenaNextEnemy = new ArenaBase(this, false);
this.arenaNextEnemy.setName("container-arena-next-enemy");
this.arenaBgTransition.setVisible(false);
this.arenaPlayerTransition.setVisible(false);
@ -472,6 +488,7 @@ export default class BattleScene extends SceneBase {
const trainer = this.addFieldSprite(0, 0, `trainer_${this.gameData.gender === PlayerGender.FEMALE ? "f" : "m"}_back`);
trainer.setOrigin(0.5, 1);
trainer.setName("sprite-trainer");
field.add(trainer);
@ -844,7 +861,7 @@ export default class BattleScene extends SceneBase {
this.gameData = new GameData(this);
}
this.gameMode = gameModes[GameModes.CLASSIC];
this.gameMode = getGameMode(GameModes.CLASSIC);
this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24));
console.log("Seed:", this.seed);
@ -940,7 +957,8 @@ export default class BattleScene extends SceneBase {
}
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle {
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1);
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
let newDouble: boolean;
let newBattleType: BattleType;
let newTrainer: Trainer;
@ -951,8 +969,8 @@ export default class BattleScene extends SceneBase {
const playerField = this.getPlayerField();
if (this.gameMode.hasFixedBattles && fixedBattles.hasOwnProperty(newWaveIndex) && trainerData === undefined) {
battleConfig = fixedBattles[newWaveIndex];
if (this.gameMode.isFixedBattle(newWaveIndex) && trainerData === undefined) {
battleConfig = this.gameMode.getFixedBattle(newWaveIndex);
newDouble = battleConfig.double;
newBattleType = battleConfig.battleType;
this.executeWithSeedOffset(() => newTrainer = battleConfig.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8);
@ -1000,6 +1018,10 @@ export default class BattleScene extends SceneBase {
if (Overrides.DOUBLE_BATTLE_OVERRIDE) {
newDouble = true;
}
/* Override battles into single only if not fighting with trainers */
if (newBattleType !== BattleType.TRAINER && Overrides.SINGLE_BATTLE_OVERRIDE) {
newDouble = false;
}
const lastBattle = this.currentBattle;
@ -1369,8 +1391,7 @@ export default class BattleScene extends SceneBase {
if (this.money === undefined) {
return;
}
const formattedMoney =
this.moneyFormat === MoneyFormat.ABBREVIATED ? Utils.formatFancyLargeNumber(this.money, 3) : this.money.toLocaleString();
const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money);
this.moneyText.setText(`${formattedMoney}`);
this.fieldUI.moveAbove(this.moneyText, this.luckText);
if (forceVisible) {
@ -1378,6 +1399,22 @@ export default class BattleScene extends SceneBase {
}
}
animateMoneyChanged(positiveChange: boolean): void {
if (this.tweens.getTweensOf(this.moneyText).length > 0) {
return;
}
const deltaScale = this.moneyText.scale * 0.14 * (positiveChange ? 1 : -1);
this.moneyText.setShadowColor(positiveChange ? "#008000" : "#FF0000");
this.tweens.add({
targets: this.moneyText,
duration: 250,
scale: this.moneyText.scale + deltaScale,
loop: 0,
yoyo: true,
onComplete: (_) => this.moneyText.setShadowColor(getTextColor(TextStyle.MONEY, true)),
});
}
updateScoreText(): void {
this.scoreText.setText(`Score: ${this.score.toString()}`);
this.scoreText.setVisible(this.gameMode.isDaily);
@ -1421,7 +1458,10 @@ export default class BattleScene extends SceneBase {
updateUIPositions(): void {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible(this)).length;
this.biomeWaveText.setY(-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0));
const biomeWaveTextHeight = this.biomeWaveText.getBottomLeft().y - this.biomeWaveText.getTopLeft().y;
this.biomeWaveText.setY(
-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0) + (biomeWaveTextHeight / 2)
);
this.moneyText.setY(this.biomeWaveText.y + 10);
this.scoreText.setY(this.moneyText.y + 10);
[ this.luckLabelText, this.luckText ].map(l => l.setY((this.scoreText.visible ? this.scoreText : this.moneyText).y + 10));
@ -1640,55 +1680,145 @@ export default class BattleScene extends SceneBase {
getBgmLoopPoint(bgmName: string): number {
switch (bgmName) {
case "battle_kanto_champion":
case "battle_kanto_champion": //B2W2 Kanto Champion Battle
return 13.950;
case "battle_johto_champion":
case "battle_johto_champion": //B2W2 Johto Champion Battle
return 23.498;
case "battle_hoenn_champion":
case "battle_hoenn_champion": //B2W2 Hoenn Champion Battle
return 11.328;
case "battle_sinnoh_champion":
case "battle_sinnoh_champion": //B2W2 Sinnoh Champion Battle
return 12.235;
case "battle_champion_alder":
case "battle_champion_alder": //BW Unova Champion Battle
return 27.653;
case "battle_champion_iris":
case "battle_champion_iris": //B2W2 Unova Champion Battle
return 10.145;
case "battle_elite":
case "battle_kalos_champion": //XY Kalos Champion Battle
return 10.380;
case "battle_alola_champion": //USUM Alola Champion Battle
return 13.025;
case "battle_galar_champion": //SWSH Galar Champion Battle
return 61.635;
case "battle_champion_geeta": //SV Champion Geeta Battle
return 37.447;
case "battle_champion_nemona": //SV Champion Nemona Battle
return 14.914;
case "battle_champion_kieran": //SV Champion Kieran Battle
return 7.206;
case "battle_hoenn_elite": //ORAS Elite Four Battle
return 11.350;
case "battle_unova_elite": //BW Elite Four Battle
return 17.730;
case "battle_final_encounter":
case "battle_kalos_elite": //XY Elite Four Battle
return 12.340;
case "battle_alola_elite": //SM Elite Four Battle
return 19.212;
case "battle_galar_elite": //SWSH League Tournament Battle
return 164.069;
case "battle_paldea_elite": //SV Elite Four Battle
return 12.770;
case "battle_bb_elite": //SV BB League Elite Four Battle
return 19.434;
case "battle_final_encounter": //PMD RTDX Rayquaza's Domain
return 19.159;
case "battle_final":
case "battle_final": //BW Ghetsis Battle
return 16.453;
case "battle_kanto_gym":
case "battle_kanto_gym": //B2W2 Kanto Gym Battle
return 13.857;
case "battle_johto_gym":
case "battle_johto_gym": //B2W2 Johto Gym Battle
return 12.911;
case "battle_hoenn_gym":
case "battle_hoenn_gym": //B2W2 Hoenn Gym Battle
return 12.379;
case "battle_sinnoh_gym":
case "battle_sinnoh_gym": //B2W2 Sinnoh Gym Battle
return 13.122;
case "battle_unova_gym":
case "battle_unova_gym": //BW Unova Gym Battle
return 19.145;
case "battle_legendary_regis": //B2W2 Legendary Titan Battle
case "battle_kalos_gym": //XY Kalos Gym Battle
return 44.810;
case "battle_galar_gym": //SWSH Galar Gym Battle
return 171.262;
case "battle_paldea_gym": //SV Paldea Gym Battle
return 127.489;
case "battle_legendary_kanto": //XY Kanto Legendary Battle
return 32.966;
case "battle_legendary_raikou": //HGSS Raikou Battle
return 12.632;
case "battle_legendary_entei": //HGSS Entei Battle
return 2.905;
case "battle_legendary_suicune": //HGSS Suicune Battle
return 12.636;
case "battle_legendary_lugia": //HGSS Lugia Battle
return 19.770;
case "battle_legendary_ho_oh": //HGSS Ho-oh Battle
return 17.668;
case "battle_legendary_regis_g5": //B2W2 Legendary Titan Battle
return 49.500;
case "battle_legendary_regis_g6": //ORAS Legendary Titan Battle
return 21.130;
case "battle_legendary_gro_kyo": //ORAS Groudon & Kyogre Battle
return 10.547;
case "battle_legendary_rayquaza": //ORAS Rayquaza Battle
return 10.495;
case "battle_legendary_deoxys": //ORAS Deoxys Battle
return 13.333;
case "battle_legendary_lake_trio": //ORAS Lake Guardians Battle
return 16.887;
case "battle_legendary_sinnoh": //ORAS Sinnoh Legendary Battle
return 22.770;
case "battle_legendary_dia_pal": //ORAS Dialga & Palkia Battle
return 16.009;
case "battle_legendary_giratina": //ORAS Giratina Battle
return 10.451;
case "battle_legendary_arceus": //HGSS Arceus Battle
return 9.595;
case "battle_legendary_unova": //BW Unova Legendary Battle
return 13.855;
case "battle_legendary_kyurem": //BW Kyurem Battle
return 18.314;
case "battle_legendary_res_zek": //BW Reshiram & Zekrom Battle
return 18.329;
case "battle_rival":
case "battle_legendary_xern_yvel": //XY Xerneas & Yveltal Battle
return 26.468;
case "battle_legendary_tapu": //SM Tapu Battle
return 0.000;
case "battle_legendary_sol_lun": //SM Solgaleo & Lunala Battle
return 6.525;
case "battle_legendary_ub": //SM Ultra Beast Battle
return 9.818;
case "battle_legendary_dusk_dawn": //USUM Dusk Mane & Dawn Wings Necrozma Battle
return 5.211;
case "battle_legendary_ultra_nec": //USUM Ultra Necrozma Battle
return 10.344;
case "battle_legendary_zac_zam": //SWSH Zacian & Zamazenta Battle
return 11.424;
case "battle_legendary_glas_spec": //SWSH Glastrier & Spectrier Battle
return 12.503;
case "battle_legendary_calyrex": //SWSH Calyrex Battle
return 50.641;
case "battle_legendary_birds_galar": //SWSH Galarian Legendary Birds Battle
return 0.175;
case "battle_legendary_ruinous": //SV Treasures of Ruin Battle
return 6.333;
case "battle_legendary_loyal_three": //SV Loyal Three Battle
return 6.500;
case "battle_legendary_ogerpon": //SV Ogerpon Battle
return 14.335;
case "battle_legendary_terapagos": //SV Terapagos Battle
return 24.377;
case "battle_legendary_pecharunt": //SV Pecharunt Battle
return 6.508;
case "battle_rival": //BW Rival Battle
return 13.689;
case "battle_rival_2":
case "battle_rival_2": //BW N Battle
return 17.714;
case "battle_rival_3":
case "battle_rival_3": //BW Final N Battle
return 17.586;
case "battle_trainer":
case "battle_trainer": //BW Trainer Battle
return 13.686;
case "battle_wild":
case "battle_wild": //BW Wild Battle
return 12.703;
case "battle_wild_strong":
case "battle_wild_strong": //BW Strong Wild Battle
return 13.940;
case "end_summit":
case "end_summit": //PMD RTDX Sky Tower Summit
return 30.025;
}
@ -1823,6 +1953,7 @@ export default class BattleScene extends SceneBase {
addMoney(amount: integer): void {
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
this.updateMoneyText();
this.animateMoneyChanged(true);
this.validateAchvs(MoneyAchv);
}

View File

@ -197,7 +197,11 @@ export default class Battle {
if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) {
return `encounter_${this.trainer.getEncounterBgm()}`;
}
if (scene.musicPreference === 0) {
return this.trainer.getBattleBgm();
} else {
return this.trainer.getMixedBattleBgm();
}
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
return "end_summit";
}
@ -209,15 +213,13 @@ export default class Battle {
return "battle_final_encounter";
}
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
if (scene.musicPreference === 0) {
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
return "battle_legendary_regis";
return "battle_legendary_regis_g5";
}
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
return "battle_legendary_unova";
}
if (pokemon.species.speciesId === Species.RESHIRAM || pokemon.species.speciesId === Species.ZEKROM) {
return "battle_legendary_res_zek";
}
if (pokemon.species.speciesId === Species.KYUREM) {
return "battle_legendary_kyurem";
}
@ -225,6 +227,102 @@ export default class Battle {
return "battle_legendary_res_zek";
}
return "battle_legendary_unova";
} else {
if (pokemon.species.speciesId === Species.ARTICUNO || pokemon.species.speciesId === Species.ZAPDOS || pokemon.species.speciesId === Species.MOLTRES || pokemon.species.speciesId === Species.MEWTWO || pokemon.species.speciesId === Species.MEW) {
return "battle_legendary_kanto";
}
if (pokemon.species.speciesId === Species.RAIKOU) {
return "battle_legendary_raikou";
}
if (pokemon.species.speciesId === Species.ENTEI) {
return "battle_legendary_entei";
}
if (pokemon.species.speciesId === Species.SUICUNE) {
return "battle_legendary_suicune";
}
if (pokemon.species.speciesId === Species.LUGIA) {
return "battle_legendary_lugia";
}
if (pokemon.species.speciesId === Species.HO_OH) {
return "battle_legendary_ho_oh";
}
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
return "battle_legendary_regis_g6";
}
if (pokemon.species.speciesId === Species.GROUDON || pokemon.species.speciesId === Species.KYOGRE) {
return "battle_legendary_gro_kyo";
}
if (pokemon.species.speciesId === Species.RAYQUAZA) {
return "battle_legendary_rayquaza";
}
if (pokemon.species.speciesId === Species.DEOXYS) {
return "battle_legendary_deoxys";
}
if (pokemon.species.speciesId === Species.UXIE || pokemon.species.speciesId === Species.MESPRIT || pokemon.species.speciesId === Species.AZELF) {
return "battle_legendary_lake_trio";
}
if (pokemon.species.speciesId === Species.HEATRAN || pokemon.species.speciesId === Species.CRESSELIA || pokemon.species.speciesId === Species.DARKRAI || pokemon.species.speciesId === Species.SHAYMIN) {
return "battle_legendary_sinnoh";
}
if (pokemon.species.speciesId === Species.DIALGA || pokemon.species.speciesId === Species.PALKIA) {
return "battle_legendary_dia_pal";
}
if (pokemon.species.speciesId === Species.GIRATINA) {
return "battle_legendary_giratina";
}
if (pokemon.species.speciesId === Species.ARCEUS) {
return "battle_legendary_arceus";
}
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
return "battle_legendary_unova";
}
if (pokemon.species.speciesId === Species.KYUREM) {
return "battle_legendary_kyurem";
}
if (pokemon.species.speciesId === Species.XERNEAS || pokemon.species.speciesId === Species.YVELTAL || pokemon.species.speciesId === Species.ZYGARDE) {
return "battle_legendary_xern_yvel";
}
if (pokemon.species.speciesId === Species.TAPU_KOKO || pokemon.species.speciesId === Species.TAPU_LELE || pokemon.species.speciesId === Species.TAPU_BULU || pokemon.species.speciesId === Species.TAPU_FINI) {
return "battle_legendary_tapu";
}
if (pokemon.species.speciesId === Species.COSMOG || pokemon.species.speciesId === Species.COSMOEM || pokemon.species.speciesId === Species.SOLGALEO || pokemon.species.speciesId === Species.LUNALA || pokemon.species.speciesId === Species.NECROZMA) {
return "battle_legendary_sol_lun";
}
if (pokemon.species.speciesId === Species.NIHILEGO || pokemon.species.speciesId === Species.BUZZWOLE || pokemon.species.speciesId === Species.PHEROMOSA || pokemon.species.speciesId === Species.XURKITREE || pokemon.species.speciesId === Species.CELESTEELA || pokemon.species.speciesId === Species.KARTANA || pokemon.species.speciesId === Species.GUZZLORD || pokemon.species.speciesId === Species.POIPOLE || pokemon.species.speciesId === Species.NAGANADEL || pokemon.species.speciesId === Species.STAKATAKA || pokemon.species.speciesId === Species.BLACEPHALON) {
return "battle_legendary_ub";
}
if (pokemon.species.speciesId === Species.ZACIAN || pokemon.species.speciesId === Species.ZAMAZENTA) {
return "battle_legendary_zac_zam";
}
if (pokemon.species.speciesId === Species.GLASTRIER || pokemon.species.speciesId === Species.SPECTRIER) {
return "battle_legendary_glas_spec";
}
if (pokemon.species.speciesId === Species.CALYREX) {
return "battle_legendary_calyrex";
}
if (pokemon.species.speciesId === Species.GALAR_ARTICUNO || pokemon.species.speciesId === Species.GALAR_ZAPDOS || pokemon.species.speciesId === Species.GALAR_MOLTRES) {
return "battle_legendary_birds_galar";
}
if (pokemon.species.speciesId === Species.WO_CHIEN || pokemon.species.speciesId === Species.CHIEN_PAO || pokemon.species.speciesId === Species.TING_LU || pokemon.species.speciesId === Species.CHI_YU) {
return "battle_legendary_ruinous";
}
if (pokemon.species.speciesId === Species.OKIDOGI || pokemon.species.speciesId === Species.MUNKIDORI || pokemon.species.speciesId === Species.FEZANDIPITI) {
return "battle_legendary_loyal_three";
}
if (pokemon.species.speciesId === Species.OGERPON) {
return "battle_legendary_ogerpon";
}
if (pokemon.species.speciesId === Species.TERAPAGOS) {
return "battle_legendary_terapagos";
}
if (pokemon.species.speciesId === Species.PECHARUNT) {
return "battle_legendary_pecharunt";
}
if (pokemon.species.legendary) {
return "battle_legendary_res_zek";
}
return "battle_legendary_unova";
}
}
}
@ -322,11 +420,11 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[]): Get
};
}
interface FixedBattleConfigs {
export interface FixedBattleConfigs {
[key: integer]: FixedBattleConfig
}
export const fixedBattles: FixedBattleConfigs = {
export const classicFixedBattles: FixedBattleConfigs = {
[5]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)

View File

@ -235,10 +235,10 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr {
}
}
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: PokemonMove) => boolean;
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean;
export class PreDefendAbAttr extends AbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
return false;
}
}
@ -252,7 +252,7 @@ export class PreDefendFormChangeAbAttr extends PreDefendAbAttr {
this.formFunc = formFunc;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const formIndex = this.formFunc(pokemon);
if (formIndex !== pokemon.formIndex) {
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
@ -263,7 +263,7 @@ export class PreDefendFormChangeAbAttr extends PreDefendAbAttr {
}
}
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (pokemon.hp === pokemon.getMaxHp() &&
pokemon.getMaxHp() > 1 && //Checks if pokemon has wonder_guard (which forces 1hp)
(args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp
@ -308,8 +308,8 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
this.powerMultiplier = powerMultiplier;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
}
@ -329,8 +329,8 @@ export class PreDefendMovePowerToOneAbAttr extends ReceivedMoveDamageMultiplierA
super(condition, 1);
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
(args[0] as Utils.NumberHolder).value = 1;
return true;
}
@ -339,6 +339,12 @@ export class PreDefendMovePowerToOneAbAttr extends ReceivedMoveDamageMultiplierA
}
}
/**
* Determines whether a Pokemon is immune to a move because of an ability.
* @extends PreDefendAbAttr
* @see {@linkcode applyPreDefend}
* @see {@linkcode getCondition}
*/
export class TypeImmunityAbAttr extends PreDefendAbAttr {
private immuneType: Type;
private condition: AbAttrCondition;
@ -350,8 +356,17 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
this.condition = condition;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.getMove().type === this.immuneType) {
/**
* @param pokemon {@linkcode Pokemon} the defending Pokemon
* @param passive N/A
* @param attacker {@linkcode Pokemon} the attacking Pokemon
* @param move {@linkcode Move} the attacking move
* @param cancelled N/A
* @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability.
* @param args [1] {@linkcode Utils.NumberHolder} type of move being defended against in case it has changed from default type
*/
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if ((move instanceof AttackMove || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.type === this.immuneType) {
(args[0] as Utils.NumberHolder).value = 0;
return true;
}
@ -369,7 +384,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr {
super(immuneType);
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
if (ret) {
@ -399,7 +414,7 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr {
this.levels = levels;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
if (ret) {
@ -425,7 +440,7 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr {
this.turnCount = turnCount;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
if (ret) {
@ -445,8 +460,8 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
super(null, condition);
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, attacker) < 2) {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.type, attacker) < 2) {
cancelled.value = true;
(args[0] as Utils.NumberHolder).value = 0;
return true;
@ -461,15 +476,15 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
}
export class PostDefendAbAttr extends AbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
return false;
}
}
export class PostDefendDisguiseAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (pokemon.formIndex === 0 && pokemon.battleData.hitCount !== 0 && (move.getMove().category === MoveCategory.SPECIAL || move.getMove().category === MoveCategory.PHYSICAL)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (pokemon.formIndex === 0 && pokemon.battleData.hitCount !== 0 && (move.category === MoveCategory.SPECIAL || move.category === MoveCategory.PHYSICAL)) {
const recoilDamage = Math.ceil((pokemon.getMaxHp() / 8) - attacker.turnData.damageDealt);
if (!recoilDamage) {
@ -494,7 +509,7 @@ export class PostDefendFormChangeAbAttr extends PostDefendAbAttr {
this.formFunc = formFunc;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
const formIndex = this.formFunc(pokemon);
if (formIndex !== pokemon.formIndex) {
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
@ -506,16 +521,16 @@ export class PostDefendFormChangeAbAttr extends PostDefendAbAttr {
}
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const attackPriority = new Utils.IntegerHolder(move.getMove().priority);
applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move.getMove(),attackPriority);
applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move.getMove(), attackPriority);
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const attackPriority = new Utils.IntegerHolder(move.priority);
applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move,attackPriority);
applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move, attackPriority);
if (move.getMove().moveTarget===MoveTarget.USER || move.getMove().moveTarget===MoveTarget.NEAR_ALLY) {
if (move.moveTarget===MoveTarget.USER || move.moveTarget===MoveTarget.NEAR_ALLY) {
return false;
}
if (attackPriority.value > 0 && !move.getMove().isMultiTarget()) {
if (attackPriority.value > 0 && !move.isMultiTarget()) {
cancelled.value = true;
return true;
}
@ -539,7 +554,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr {
this.immuneCondition = immuneCondition;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.immuneCondition(pokemon, attacker, move)) {
cancelled.value = true;
return true;
@ -563,7 +578,7 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
this.levels = levels;
}
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
if (ret) {
const simulated = args.length > 1 && args[1];
@ -593,8 +608,8 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr {
* @args N/A
* @returns true if healing should be reversed on a healing move, false otherwise.
*/
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().hasAttr(HitHealAttr)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.hasAttr(HitHealAttr)) {
pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!"));
return true;
}
@ -619,8 +634,8 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
this.allOthers = allOthers;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
if (this.allOthers) {
const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents();
for (const other of otherPokemon) {
@ -653,10 +668,10 @@ export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr {
this.selfTarget = selfTarget;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate);
const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1];
if (this.condition(pokemon, attacker, move.getMove()) && (pokemon.hp <= hpGateFlat && (pokemon.hp + lastAttackReceived.damage) > hpGateFlat)) {
if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + lastAttackReceived.damage) > hpGateFlat)) {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels));
return true;
}
@ -676,8 +691,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag;
if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) {
pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER);
@ -698,11 +713,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move)) {
if (!pokemon.getTag(this.tagType)) {
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: move.getName() }));
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: move.name }));
}
return true;
}
@ -711,9 +726,9 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
}
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
const type = move.getMove().type;
const type = move.type;
const pokemonTypes = pokemon.getTypes(true);
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
pokemon.summonData.types = [ type ];
@ -738,7 +753,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
this.terrainType = terrainType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (hitResult < HitResult.NO_EFFECT) {
return pokemon.scene.arena.trySetTerrain(this.terrainType, true);
}
@ -758,8 +773,8 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
this.effects = effects;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
return attacker.trySetStatus(effect, true, pokemon);
}
@ -773,7 +788,7 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr
super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP);
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(Type.GRASS)) {
return false;
}
@ -794,9 +809,9 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
this.turnCount = turnCount;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) {
return attacker.addTag(this.tagType, this.turnCount, move.moveId, attacker.id);
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) {
return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id);
}
return false;
@ -814,7 +829,7 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr {
this.levels = levels;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels));
return true;
@ -834,8 +849,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
this.damageRatio = damageRatio;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio));
return true;
@ -864,8 +879,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
this.turns = turns;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) {
return false;
} else {
@ -891,7 +906,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
this.weatherType = weatherType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (!pokemon.scene.arena.weather?.isImmutable()) {
return pokemon.scene.arena.trySetWeather(this.weatherType, true);
}
@ -905,8 +920,8 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
super();
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) {
const tempAbilityId = attacker.getAbility().id;
attacker.summonData.ability = pokemon.getAbility().id;
pokemon.summonData.ability = tempAbilityId;
@ -929,8 +944,8 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
this.ability = ability;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
attacker.summonData.ability = this.ability;
return true;
@ -947,7 +962,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
private chance: integer;
private attacker: Pokemon;
private move: PokemonMove;
private move: Move;
constructor(chance: integer) {
super();
@ -955,13 +970,13 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
this.chance = chance;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (!attacker.summonData.disabledMove) {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) {
this.attacker = attacker;
this.move = move;
attacker.summonData.disabledMove = move.moveId;
attacker.summonData.disabledMove = move.id;
attacker.summonData.disabledTurns = 4;
return true;
}
@ -970,7 +985,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return getPokemonMessage(this.attacker, `'s ${this.move.getName()}\nwas disabled!`);
return getPokemonMessage(this.attacker, `'s ${this.move.name}\nwas disabled!`);
}
}
@ -998,49 +1013,18 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr {
}
export class PreAttackAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise<boolean> {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
return false;
}
}
export class VariableMovePowerAbAttr extends PreAttackAbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder;
return false;
}
}
export class VariableMoveTypeAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
//const power = args[0] as Utils.IntegerHolder;
return false;
}
}
export class MoveTypeChangePowerMultiplierAbAttr extends VariableMoveTypeAbAttr {
private matchType: Type;
private newType: Type;
private powerMultiplier: number;
constructor(matchType: Type, newType: Type, powerMultiplier: number) {
super(true);
this.matchType = matchType;
this.newType = newType;
this.powerMultiplier = powerMultiplier;
}
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const type = (args[0] as Utils.IntegerHolder);
if (type.value === this.matchType) {
type.value = this.newType;
(args[1] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
}
return false;
}
}
export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
cancelled.value = true;
@ -1060,11 +1044,10 @@ export class MoveTypeChangeAttr extends PreAttackAbAttr {
this.condition = condition;
}
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (this.condition(pokemon, defender, move.getMove())) {
const type = (args[0] as Utils.IntegerHolder);
type.value = this.newType;
(args[1] as Utils.NumberHolder).value *= this.powerMultiplier;
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
if (this.condition(pokemon, defender, move)) {
move.type = this.newType;
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
}
@ -1097,8 +1080,8 @@ export class DamageBoostAbAttr extends PreAttackAbAttr {
* @param args Utils.NumberHolder as damage
* @returns true if the function succeeds
*/
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (this.condition(pokemon, defender, move.getMove())) {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
if (this.condition(pokemon, defender, move)) {
const power = args[0] as Utils.NumberHolder;
power.value = Math.floor(power.value * this.damageMultiplier);
return true;
@ -1118,8 +1101,8 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr {
this.powerMultiplier = powerMultiplier;
}
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (this.condition(pokemon, defender, move.getMove())) {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
if (this.condition(pokemon, defender, move)) {
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
@ -1165,8 +1148,8 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
/**
* @override
*/
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
const multiplier = this.mult(pokemon, defender, move.getMove());
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move, args: any[]): boolean {
const multiplier = this.mult(pokemon, defender, move);
if (multiplier !== 1) {
(args[0] as Utils.NumberHolder).value *= multiplier;
return true;
@ -1177,7 +1160,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
}
export class FieldVariableMovePowerAbAttr extends AbAttr {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
//const power = args[0] as Utils.NumberHolder;
return false;
}
@ -1193,8 +1176,8 @@ export class FieldMovePowerBoostAbAttr extends FieldVariableMovePowerAbAttr {
this.powerMultiplier = powerMultiplier;
}
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
if (this.condition(pokemon, defender, move.getMove())) {
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
if (this.condition(pokemon, defender, move)) {
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
@ -1235,7 +1218,7 @@ export class BattleStatMultiplierAbAttr extends AbAttr {
}
export class PostAttackAbAttr extends AbAttr {
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
return false;
}
}
@ -1249,9 +1232,9 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
this.condition = condition;
}
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): Promise<boolean> {
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move.getMove()))) {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move))) {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false));
if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
@ -1287,8 +1270,8 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
this.effects = effects;
}
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
return attacker.trySetStatus(effect, true, pokemon);
}
@ -1305,11 +1288,11 @@ export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackApplySta
export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
private contactRequired: boolean;
private chance: (user: Pokemon, target: Pokemon, move: PokemonMove) => integer;
private chance: (user: Pokemon, target: Pokemon, move: Move) => integer;
private effects: BattlerTagType[];
constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: PokemonMove) => integer, ...effects: BattlerTagType[]) {
constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: Move) => integer, ...effects: BattlerTagType[]) {
super();
this.contactRequired = contactRequired;
@ -1317,8 +1300,8 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
this.effects = effects;
}
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) {
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) {
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
@ -1338,9 +1321,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
this.condition = condition;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): Promise<boolean> {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move.getMove()))) {
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.getTransferrable(false));
if (heldItems.length) {
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
@ -1693,7 +1676,6 @@ export class DownloadAbAttr extends PostSummonAbAttr {
this.enemySpDef = 0;
this.enemyCountTally = 0;
if (pokemon.getOpponents()[0].summonData !== undefined) {
for (const opponent of pokemon.getOpponents()) {
this.enemyCountTally++;
this.enemyDef += opponent.getBattleStat(Stat.DEF);
@ -1701,7 +1683,6 @@ export class DownloadAbAttr extends PostSummonAbAttr {
}
this.enemyDef = Math.round(this.enemyDef / this.enemyCountTally);
this.enemySpDef = Math.round(this.enemySpDef / this.enemyCountTally);
}
if (this.enemyDef < this.enemySpDef) {
this.stats = [BattleStat.ATK];
@ -1728,7 +1709,9 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr {
}
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
if (!pokemon.scene.arena.weather?.isImmutable()) {
if ((this.weatherType === WeatherType.HEAVY_RAIN ||
this.weatherType === WeatherType.HARSH_SUN ||
this.weatherType === WeatherType.STRONG_WINDS) || !pokemon.scene.arena.weather?.isImmutable()) {
return pokemon.scene.arena.trySetWeather(this.weatherType, true);
}
@ -1856,6 +1839,52 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr {
}
}
/**
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out.
*/
export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr {
/**
* @param pokemon The {@linkcode Pokemon} with the ability
* @param passive N/A
* @param args N/A
* @returns {boolean} Returns true if the weather clears, otherwise false.
*/
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
const weatherType = pokemon.scene.arena.weather.weatherType;
let turnOffWeather = false;
// Clear weather only if user's ability matches the weather and no other pokemon has the ability.
switch (weatherType) {
case (WeatherType.HARSH_SUN):
if (pokemon.hasAbility(Abilities.DESOLATE_LAND)
&& pokemon.scene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) {
turnOffWeather = true;
}
break;
case (WeatherType.HEAVY_RAIN):
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
&& pokemon.scene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
turnOffWeather = true;
}
break;
case (WeatherType.STRONG_WINDS):
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
&& pokemon.scene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
turnOffWeather = true;
}
break;
}
if (turnOffWeather) {
pokemon.scene.arena.trySetWeather(WeatherType.NONE, false);
return true;
}
return false;
}
}
export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr {
applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise<boolean> {
if (pokemon.getHpRatio() < 1 ) {
@ -1956,9 +1985,9 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
* @param args [0] {@linkcode StatusEffect} applied by move
* @returns true if defender is confused
*/
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) {
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.moveId, defender.id);
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id);
}
return false;
}
@ -2216,7 +2245,7 @@ function getAnticipationCondition(): AbAttrCondition {
for (const opponent of pokemon.getOpponents()) {
for (const move of opponent.moveset) {
// move is super effective
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent) >= 2) {
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true) >= 2) {
return true;
}
// move is a OHKO
@ -2561,7 +2590,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr {
if (!berryModifier) {
pokemon.scene.addModifier(new BerryModifier(chosenBerry, pokemon.id, chosenBerryType, 1));
} else {
} else if (berryModifier.stackCount < berryModifier.getMaxHeldItemCount(pokemon)) {
berryModifier.stackCount++;
}
@ -2920,6 +2949,7 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
/**
* Checks if enemy Pokemon is trapped by an Arena Trap-esque ability
* If the enemy is a Ghost type, it is not trapped
* If the enemy has the ability Run Away, it is not trapped.
* If the user has Magnet Pull and the enemy is not a Steel type, it is not trapped.
* If the user has Arena Trap and the enemy is not grounded, it is not trapped.
* @param pokemon The {@link Pokemon} with this {@link AbAttr}
@ -2934,6 +2964,9 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr {
if (otherPokemon.getTypes(true).includes(Type.GHOST) || (otherPokemon.getTypes(true).includes(Type.STELLAR) && otherPokemon.getTypes().includes(Type.GHOST))) {
trapped.value = false;
return false;
} else if (otherPokemon.hasAbility(Abilities.RUN_AWAY)) {
trapped.value = false;
return false;
}
trapped.value = true;
return true;
@ -2982,7 +3015,56 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
}
export class PostFaintAbAttr extends AbAttr {
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
return false;
}
}
/**
* Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon fainting
*/
export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
/**
* @param pokemon The {@linkcode Pokemon} with the ability
* @param passive N/A
* @param attacker N/A
* @param move N/A
* @param hitResult N/A
* @param args N/A
* @returns {boolean} Returns true if the weather clears, otherwise false.
*/
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
const weatherType = pokemon.scene.arena.weather.weatherType;
let turnOffWeather = false;
// Clear weather only if user's ability matches the weather and no other pokemon has the ability.
switch (weatherType) {
case (WeatherType.HARSH_SUN):
if (pokemon.hasAbility(Abilities.DESOLATE_LAND)
&& pokemon.scene.getField(true).filter(p => p.hasAbility(Abilities.DESOLATE_LAND)).length === 0) {
turnOffWeather = true;
}
break;
case (WeatherType.HEAVY_RAIN):
if (pokemon.hasAbility(Abilities.PRIMORDIAL_SEA)
&& pokemon.scene.getField(true).filter(p => p.hasAbility(Abilities.PRIMORDIAL_SEA)).length === 0) {
turnOffWeather = true;
}
break;
case (WeatherType.STRONG_WINDS):
if (pokemon.hasAbility(Abilities.DELTA_STREAM)
&& pokemon.scene.getField(true).filter(p => p.hasAbility(Abilities.DELTA_STREAM)).length === 0) {
turnOffWeather = true;
}
break;
}
if (turnOffWeather) {
pokemon.scene.arena.trySetWeather(WeatherType.NONE, false);
return true;
}
return false;
}
}
@ -2996,8 +3078,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
this.damageRatio = damageRatio;
}
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
const cancelled = new Utils.BooleanHolder(false);
pokemon.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
if (cancelled.value) {
@ -3024,7 +3106,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
super ();
}
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
const damage = pokemon.turnData.attacksReceived[0].damage;
attacker.damageAndUpdate((damage), HitResult.OTHER);
attacker.turnData.damageTaken += damage;
@ -3361,7 +3443,7 @@ export class IceFaceMoveImmunityAbAttr extends MoveImmunityAbAttr {
* @param {any[]} args - Additional arguments.
* @returns {boolean} - Whether the immunity was applied.
*/
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const isImmune = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
if (isImmune) {
@ -3471,13 +3553,13 @@ export function applyPostBattleInitAbAttrs(attrType: { new(...args: any[]): Post
}
export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr },
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
const simulated = args.length > 1 && args[1];
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, false, simulated);
}
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args);
}
@ -3492,12 +3574,12 @@ export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[])
}
export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr },
pokemon: Pokemon, defender: Pokemon, move: PokemonMove, ...args: any[]): Promise<void> {
pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args);
}
export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAttackAbAttr },
pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args);
}
@ -3578,7 +3660,7 @@ export function applyPostBattleAbAttrs(attrType: { new(...args: any[]): PostBatt
}
export function applyPostFaintAbAttrs(attrType: { new(...args: any[]): PostFaintAbAttr },
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostFaintAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, attacker, move, hitResult, args), args);
}
@ -3597,7 +3679,7 @@ export const allAbilities = [ new Ability(Abilities.NONE, 3) ];
export function initAbilities() {
allAbilities.push(
new Ability(Abilities.STENCH, 3)
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.getMove().category !== MoveCategory.STATUS && !move.getMove().hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.category !== MoveCategory.STATUS && !move.hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
new Ability(Abilities.DRIZZLE, 3)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
@ -3734,7 +3816,7 @@ export function initAbilities() {
return false;
}),
new Ability(Abilities.SOUNDPROOF, 3)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.SOUND_BASED))
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.SOUND_BASED))
.ignorable(),
new Ability(Abilities.RAIN_DISH, 3)
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN)
@ -4020,13 +4102,13 @@ export function initAbilities() {
)
.partial(),
new Ability(Abilities.TELEPATHY, 5)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.getMove() instanceof AttackMove)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
.ignorable(),
new Ability(Abilities.MOODY, 5)
.attr(MoodyAbAttr),
new Ability(Abilities.OVERCOAT, 5)
.attr(BlockWeatherDamageAttr)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.POWDER_MOVE))
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.POWDER_MOVE))
.ignorable(),
new Ability(Abilities.POISON_TOUCH, 5)
.attr(PostAttackContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON),
@ -4115,14 +4197,14 @@ export function initAbilities() {
new Ability(Abilities.MAGICIAN, 6)
.attr(PostAttackStealHeldItemAbAttr),
new Ability(Abilities.BULLETPROOF, 6)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.BALLBOMB_MOVE))
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.BALLBOMB_MOVE))
.ignorable(),
new Ability(Abilities.COMPETITIVE, 6)
.attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.SPATK], 2),
new Ability(Abilities.STRONG_JAW, 6)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
new Ability(Abilities.REFRIGERATE, 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ICE, 1.2),
.attr(MoveTypeChangeAttr, Type.ICE, 1.2, (user, target, move) => move.type === Type.NORMAL),
new Ability(Abilities.SWEET_VEIL, 6)
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
@ -4145,11 +4227,11 @@ export function initAbilities() {
new Ability(Abilities.TOUGH_CLAWS, 6)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3),
new Ability(Abilities.PIXILATE, 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FAIRY, 1.2),
.attr(MoveTypeChangeAttr, Type.FAIRY, 1.2, (user, target, move) => move.type === Type.NORMAL),
new Ability(Abilities.GOOEY, 6)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false),
new Ability(Abilities.AERILATE, 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FLYING, 1.2),
.attr(MoveTypeChangeAttr, Type.FLYING, 1.2, (user, target, move) => move.type === Type.NORMAL),
new Ability(Abilities.PARENTAL_BOND, 6)
.unimplemented(),
new Ability(Abilities.DARK_AURA, 6)
@ -4163,13 +4245,22 @@ export function initAbilities() {
.unimplemented(),
new Ability(Abilities.PRIMORDIAL_SEA, 6)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN),
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
.attr(PreSwitchOutClearWeatherAbAttr)
.attr(PostFaintClearWeatherAbAttr)
.bypassFaint(),
new Ability(Abilities.DESOLATE_LAND, 6)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HARSH_SUN)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HARSH_SUN),
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HARSH_SUN)
.attr(PreSwitchOutClearWeatherAbAttr)
.attr(PostFaintClearWeatherAbAttr)
.bypassFaint(),
new Ability(Abilities.DELTA_STREAM, 6)
.attr(PostSummonWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.STRONG_WINDS),
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.STRONG_WINDS)
.attr(PreSwitchOutClearWeatherAbAttr)
.attr(PostFaintClearWeatherAbAttr)
.bypassFaint(),
new Ability(Abilities.STAMINA, 7)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattleStat.DEF, 1),
new Ability(Abilities.WIMP_OUT, 7)
@ -4210,7 +4301,7 @@ export function initAbilities() {
new Ability(Abilities.TRIAGE, 7)
.attr(IncrementMovePriorityAbAttr, (pokemon, move) => move.hasFlag(MoveFlags.TRIAGE_MOVE), 3),
new Ability(Abilities.GALVANIZE, 7)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ELECTRIC, 1.2),
.attr(MoveTypeChangeAttr, Type.ELECTRIC, 1.2, (user, target, move) => move.type === Type.NORMAL),
new Ability(Abilities.SURGE_SURFER, 7)
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2),
new Ability(Abilities.SCHOOLING, 7)
@ -4378,7 +4469,7 @@ export function initAbilities() {
.conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0)
// When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
.attr(IceFaceMoveImmunityAbAttr, (target, user, move) => move.getMove().category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE))
.attr(IceFaceMoveImmunityAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE))
.ignorable(),
new Ability(Abilities.POWER_SPOT, 8)
.unimplemented(),
@ -4463,7 +4554,7 @@ export function initAbilities() {
.attr(TypeImmunityStatChangeAbAttr, Type.FIRE, BattleStat.DEF, 2)
.ignorable(),
new Ability(Abilities.WIND_RIDER, 9)
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.WIND_MOVE) && move.getMove().category !== MoveCategory.STATUS, BattleStat.ATK, 1)
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.WIND_MOVE) && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1)
.attr(PostSummonStatChangeOnArenaAbAttr, ArenaTagType.TAILWIND)
.ignorable(),
new Ability(Abilities.GUARD_DOG, 9)
@ -4503,7 +4594,7 @@ export function initAbilities() {
.attr(NoTransformAbilityAbAttr)
.partial(), // While setting the tag, the getbattlestat should ignore all modifiers to stats except stat stages
new Ability(Abilities.GOOD_AS_GOLD, 9)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().category === MoveCategory.STATUS)
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS)
.ignorable()
.partial(),
new Ability(Abilities.VESSEL_OF_RUIN, 9)

View File

@ -29,6 +29,7 @@ export abstract class ArenaTag {
public sourceId: integer;
public side: ArenaTagSide;
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) {
this.tagType = tagType;
this.turnCount = turnCount;
@ -41,11 +42,13 @@ export abstract class ArenaTag {
return true;
}
onAdd(arena: Arena): void { }
onAdd(arena: Arena, quiet: boolean = false): void { }
onRemove(arena: Arena): void {
onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`${this.getMoveName()}\'s effect wore off${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`);
}
}
onOverlap(arena: Arena): void { }
@ -65,12 +68,14 @@ export class MistTag extends ArenaTag {
super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
arena.scene.queueMessage(getPokemonMessage(source, "'s team became\nshrouded in mist!"));
}
}
apply(arena: Arena, args: any[]): boolean {
(args[0] as Utils.BooleanHolder).value = true;
@ -113,10 +118,12 @@ class ReflectTag extends WeakenMoveScreenTag {
return false;
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`Reflect reduced the damage of physical moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`);
}
}
}
class LightScreenTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
@ -135,20 +142,24 @@ class LightScreenTag extends WeakenMoveScreenTag {
return false;
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`Light Screen reduced the damage of special moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`);
}
}
}
class AuroraVeilTag extends WeakenMoveScreenTag {
constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) {
super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`Aurora Veil reduced the damage of moves${this.side === ArenaTagSide.PLAYER ? "\non your side" : this.side === ArenaTagSide.ENEMY ? "\non the foe's side" : ""}.`);
}
}
}
type ProtectConditionFunc = (...args: any[]) => boolean;
@ -386,12 +397,14 @@ class SpikesTag extends ArenaTrapTag {
super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`);
}
}
activateTrap(pokemon: Pokemon): boolean {
if (pokemon.isGrounded()) {
@ -423,12 +436,14 @@ class ToxicSpikesTag extends ArenaTrapTag {
this.neutralized = false;
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`);
}
}
onRemove(arena: Arena): void {
if (!this.neutralized) {
@ -493,15 +508,17 @@ class StealthRockTag extends ArenaTrapTag {
super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
arena.scene.queueMessage(`Pointed stones float in the air\naround ${source.getOpponentDescriptor()}!`);
}
}
getDamageHpRatio(pokemon: Pokemon): number {
const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK);
const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK, undefined, true);
let damageHpRatio: number;
@ -562,14 +579,16 @@ class StickyWebTag extends ArenaTrapTag {
super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
super.onAdd(arena);
// does not seem to be used anywhere
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const source = arena.scene.getPokemonById(this.sourceId);
if (!quiet) {
arena.scene.queueMessage(`A ${this.getMoveName()} has been laid out on the ground around the opposing team!`);
}
}
activateTrap(pokemon: Pokemon): boolean {
if (pokemon.isGrounded()) {
@ -626,8 +645,10 @@ class TailwindTag extends ArenaTag {
super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side);
}
onAdd(arena: Arena): void {
onAdd(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`The Tailwind blew from behind${this.side === ArenaTagSide.PLAYER ? "\nyour" : this.side === ArenaTagSide.ENEMY ? "\nthe opposing" : ""} team!`);
}
const source = arena.scene.getPokemonById(this.sourceId);
const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField();
@ -646,10 +667,12 @@ class TailwindTag extends ArenaTag {
}
}
onRemove(arena: Arena): void {
onRemove(arena: Arena, quiet: boolean = false): void {
if (!quiet) {
arena.scene.queueMessage(`${this.side === ArenaTagSide.PLAYER ? "Your" : this.side === ArenaTagSide.ENEMY ? "The opposing" : ""} team's Tailwind petered out!`);
}
}
}
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag {
switch (tagType) {

View File

@ -1691,7 +1691,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.SEA]: {
[BiomePoolTier.COMMON]: [ TrainerType.SWIMMER ],
[BiomePoolTier.COMMON]: [ TrainerType.SWIMMER, TrainerType.SAILOR ],
[BiomePoolTier.UNCOMMON]: [],
[BiomePoolTier.RARE]: [],
[BiomePoolTier.SUPER_RARE]: [],
@ -1713,7 +1713,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
[BiomePoolTier.BOSS_ULTRA_RARE]: []
},
[Biome.BEACH]: {
[BiomePoolTier.COMMON]: [ TrainerType.FISHERMAN, TrainerType.PARASOL_LADY ],
[BiomePoolTier.COMMON]: [ TrainerType.FISHERMAN, TrainerType.PARASOL_LADY, TrainerType.SAILOR ],
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER, TrainerType.BREEDER ],
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
[BiomePoolTier.SUPER_RARE]: [],
@ -7308,6 +7308,11 @@ export function initBiomes() {
[ Biome.VOLCANO, BiomePoolTier.COMMON ]
]
],
[ TrainerType.SAILOR, [
[ Biome.SEA, BiomePoolTier.COMMON ],
[ Biome.BEACH, BiomePoolTier.COMMON ]
]
],
[ TrainerType.BROCK, [
[ Biome.CAVE, BiomePoolTier.BOSS ]
]

577
src/data/challenge.ts Normal file
View File

@ -0,0 +1,577 @@
import * as Utils from "../utils";
import { Challenges } from "./enums/challenges";
import i18next from "#app/plugins/i18n.js";
import { GameData } from "#app/system/game-data.js";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
import Pokemon from "#app/field/pokemon.js";
import { BattleType, FixedBattleConfig } from "#app/battle.js";
import { TrainerType } from "./enums/trainer-type";
import Trainer, { TrainerVariant } from "#app/field/trainer.js";
import { GameMode } from "#app/game-mode.js";
import { Species } from "./enums/species";
import { Type } from "./type";
/**
* An enum for all the challenge types. The parameter entries on these describe the
* parameters to use when calling the applyChallenges function.
*/
export enum ChallengeType {
/**
* Challenges which modify what starters you can choose
* @param args [0] {@link PokemonSpecies} The species to check
* [1] {@link Utils.BooleanHolder} Sets to false if illegal, pass in true.
*/
STARTER_CHOICE,
/**
* Challenges which modify how many starter points you have
* @param args [0] {@link Utils.NumberHolder} The amount of starter points you have
*/
STARTER_POINTS,
/**
* Challenges which modify your starters in some way
* Not Fully Implemented
*/
STARTER_MODIFY,
/**
* Challenges which limit which pokemon you can have in battle.
* @param args [0] {@link Pokemon} The pokemon to check
* [1] {@link Utils.BooleanHolder} Sets to false if illegal, pass in true.
*/
POKEMON_IN_BATTLE,
/**
* Adds or modifies the fixed battles in a run
* @param args [0] integer The wave to get a battle for
* [1] {@link FixedBattleConfig} A new fixed battle. It'll be modified if a battle exists.
*/
FIXED_BATTLES,
}
/**
* A challenge object. Exists only to serve as a base class.
*/
export abstract class Challenge {
public id: Challenges; // The id of the challenge
public value: integer; // The "strength" of the challenge, all challenges have a numerical value.
public maxValue: integer; // The maximum strength of the challenge.
public severity: integer; // The current severity of the challenge. Some challenges have multiple severities in addition to strength.
public maxSeverity: integer; // The maximum severity of the challenge.
public conditions: ChallengeCondition[];
public challengeTypes: ChallengeType[];
/**
* @param {Challenges} id The enum value for the challenge
*/
constructor(id: Challenges, maxValue: integer = Number.MAX_SAFE_INTEGER) {
this.id = id;
this.value = 0;
this.maxValue = maxValue;
this.severity = 0;
this.maxSeverity = 0;
this.conditions = [];
this.challengeTypes = [];
}
/**
* Reset the challenge to a base state.
*/
reset(): void {
this.value = 0;
this.severity = 0;
}
/**
* Gets the localisation key for the challenge
* @returns The i18n key for this challenge
*/
geti18nKey(): string {
return Challenges[this.id].split("_").map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
}
/**
* Used for unlockable challenges to check if they're unlocked.
* @param {GameData} data The save data.
* @returns {boolean} Whether this challenge is unlocked.
*/
isUnlocked(data: GameData): boolean {
return this.conditions.every(f => f(data));
}
/**
* Adds an unlock condition to this challenge.
* @param {ChallengeCondition} condition The condition to add.
* @returns {Challenge} This challenge
*/
condition(condition: ChallengeCondition): Challenge {
this.conditions.push(condition);
return this;
}
/**
* If this challenge is of a particular type
* @param {ChallengeType} challengeType The challenge type to check.
* @returns {Challenge} This challenge
*/
isOfType(challengeType: ChallengeType): boolean {
return this.challengeTypes.some(c => c === challengeType);
}
/**
* Adds a challenge type to this challenge.
* @param {ChallengeType} challengeType The challenge type to add.
* @returns {Challenge} This challenge
*/
addChallengeType(challengeType: ChallengeType): Challenge {
this.challengeTypes.push(challengeType);
return this;
}
/**
* @returns {string} The localised name of this challenge.
*/
getName(): string {
return i18next.t(`challenges:${this.geti18nKey()}.name`);
}
/**
* Returns the textual representation of a challenge's current value.
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised name for the current value.
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return i18next.t(`challenges:${this.geti18nKey()}.value.${this.value}`);
}
/**
* Returns the description of a challenge's current value.
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised description for the current value.
*/
getDescription(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return i18next.t(`challenges:${this.geti18nKey()}.desc.${this.value}`);
}
/**
* Increase the value of the challenge
* @returns {boolean} Returns true if the value changed
*/
increaseValue(): boolean {
if (this.value < this.maxValue) {
this.value = Math.min(this.value + 1, this.maxValue);
return true;
}
return false;
}
/**
* Decrease the value of the challenge
* @returns {boolean} Returns true if the value changed
*/
decreaseValue(): boolean {
if (this.value > 0) {
this.value = Math.max(this.value - 1, 0);
return true;
}
return false;
}
/**
* Whether to allow choosing this challenge's severity.
*/
hasSeverity(): boolean {
return this.value !== 0 && this.maxSeverity > 0;
}
/**
* Decrease the severity of the challenge
* @returns {boolean} Returns true if the value changed
*/
decreaseSeverity(): boolean {
if (this.severity > 0) {
this.severity = Math.max(this.severity - 1, 0);
return true;
}
return false;
}
/**
* Increase the severity of the challenge
* @returns {boolean} Returns true if the value changed
*/
increaseSeverity(): boolean {
if (this.severity < this.maxSeverity) {
this.severity = Math.min(this.severity + 1, this.maxSeverity);
return true;
}
return false;
}
/**
* Gets the "difficulty" value of this challenge.
* @returns {integer} The difficulty value.
*/
getDifficulty(): integer {
return this.value;
}
/**
* Gets the minimum difficulty added by this challenge.
* @returns {integer} The difficulty value.
*/
getMinDifficulty(): integer {
return 0;
}
/**
* Modifies the data or game state in some way to apply the challenge.
* @param {ChallengeType} challengeType Which challenge type this is being applied for.
* @param args Irrelevant. See the specific challenge's apply function for additional information.
*/
abstract apply(challengeType: ChallengeType, args: any[]): boolean;
/**
* Clones a challenge, either from another challenge or json. Chainable.
* @param {Challenge | any} source The source challenge of json.
* @returns {Challenge} This challenge.
*/
static loadChallenge(source: Challenge | any): Challenge {
throw new Error("Method not implemented! Use derived class");
}
}
type ChallengeCondition = (data: GameData) => boolean;
/**
* Implements a mono generation challenge.
*/
export class SingleGenerationChallenge extends Challenge {
constructor() {
super(Challenges.SINGLE_GENERATION, 9);
this.addChallengeType(ChallengeType.STARTER_CHOICE);
this.addChallengeType(ChallengeType.POKEMON_IN_BATTLE);
this.addChallengeType(ChallengeType.FIXED_BATTLES);
}
apply(challengeType: ChallengeType, args: any[]): boolean {
if (this.value === 0) {
return false;
}
/**
* We have special code below for victini because it is classed as a generation 4 pokemon in the code
* despite being a generation 5 pokemon. This is due to UI constraints, the starter select screen has
* no more room for pokemon so victini is put in the gen 4 section instead. This code just overrides the
* normal generation check to correctly treat victini as gen 5.
*/
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
const species = args[0] as PokemonSpecies;
const isValidStarter = args[1] as Utils.BooleanHolder;
const starterGeneration = species.speciesId === Species.VICTINI ? 5 : species.generation;
if (starterGeneration !== this.value) {
isValidStarter.value = false;
return true;
}
break;
case ChallengeType.POKEMON_IN_BATTLE:
const pokemon = args[0] as Pokemon;
const isValidPokemon = args[1] as Utils.BooleanHolder;
const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies.speciesId).generation : 0;
if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) {
isValidPokemon.value = false;
return true;
}
break;
case ChallengeType.FIXED_BATTLES:
const waveIndex = args[0] as integer;
const battleConfig = args[1] as FixedBattleConfig;
let trainerTypes: TrainerType[] = [];
switch (waveIndex) {
case 182:
trainerTypes = [ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, Utils.randSeedItem([ TrainerType.HALA, TrainerType.MOLAYNE ]),TrainerType.MARNIE_ELITE, TrainerType.RIKA ];
break;
case 184:
trainerTypes = [ TrainerType.BRUNO, TrainerType.KOGA, TrainerType.PHOEBE, TrainerType.BERTHA, TrainerType.MARSHAL, TrainerType.SIEBOLD, TrainerType.OLIVIA, TrainerType.NESSA_ELITE, TrainerType.POPPY ];
break;
case 186:
trainerTypes = [ TrainerType.AGATHA, TrainerType.BRUNO, TrainerType.GLACIA, TrainerType.FLINT, TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, Utils.randSeedItem([TrainerType.BEA_ELITE,TrainerType.ALLISTER_ELITE]), TrainerType.LARRY_ELITE ];
break;
case 188:
trainerTypes = [ TrainerType.LANCE, TrainerType.KAREN, TrainerType.DRAKE, TrainerType.LUCIAN, TrainerType.CAITLIN, TrainerType.DRASNA, TrainerType.KAHILI, TrainerType.RAIHAN_ELITE, TrainerType.HASSEL ];
break;
case 190:
trainerTypes = [ TrainerType.BLUE, Utils.randSeedItem([ TrainerType.RED, TrainerType.LANCE_CHAMPION ]), Utils.randSeedItem([ TrainerType.STEVEN, TrainerType.WALLACE ]), TrainerType.CYNTHIA, Utils.randSeedItem([ TrainerType.ALDER, TrainerType.IRIS ]), TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, Utils.randSeedItem([ TrainerType.GEETA, TrainerType.NEMONA ]) ];
break;
}
if (trainerTypes.length === 0) {
return false;
} else {
battleConfig.setBattleType(BattleType.TRAINER).setGetTrainerFunc(scene => new Trainer(scene, trainerTypes[this.value - 1], TrainerVariant.DEFAULT));
return true;
}
}
return false;
}
/**
* @overrides
*/
getDifficulty(): number {
return this.value > 0 ? 1 : 0;
}
static loadChallenge(source: SingleGenerationChallenge | any): SingleGenerationChallenge {
const newChallenge = new SingleGenerationChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
interface monotypeOverride {
/** The species to override */
species: Species;
/** The type to count as */
type: Type;
/** If part of a fusion, should we check the fused species instead of the base species? */
fusion: boolean;
}
/**
* Implements a mono type challenge.
*/
export class SingleTypeChallenge extends Challenge {
private static TYPE_OVERRIDES: monotypeOverride[] = [
{species: Species.MELOETTA, type: Type.PSYCHIC, fusion: true},
{species: Species.CASTFORM, type: Type.NORMAL, fusion: false},
];
constructor() {
super(Challenges.SINGLE_TYPE, 18);
this.addChallengeType(ChallengeType.STARTER_CHOICE);
this.addChallengeType(ChallengeType.POKEMON_IN_BATTLE);
}
apply(challengeType: ChallengeType, args: any[]): boolean {
if (this.value === 0) {
return false;
}
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
const species = args[0] as PokemonSpecies;
const isValidStarter = args[1] as Utils.BooleanHolder;
if (!species.isOfType(this.value - 1)) {
isValidStarter.value = false;
return true;
}
break;
case ChallengeType.POKEMON_IN_BATTLE:
const pokemon = args[0] as Pokemon;
const isValidPokemon = args[1] as Utils.BooleanHolder;
if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true)
&& !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies : pokemon.species).speciesId === o.species)) {
isValidPokemon.value = false;
return true;
}
break;
}
return false;
}
/**
* @overrides
*/
getDifficulty(): number {
return this.value > 0 ? 1 : 0;
}
static loadChallenge(source: SingleTypeChallenge | any): SingleTypeChallenge {
const newChallenge = new SingleTypeChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Implements a fresh start challenge.
*/
export class FreshStartChallenge extends Challenge {
constructor() {
super(Challenges.FRESH_START, 1);
this.addChallengeType(ChallengeType.STARTER_CHOICE);
this.addChallengeType(ChallengeType.STARTER_MODIFY);
}
apply(challengeType: ChallengeType, args: any[]): boolean {
if (this.value === 0) {
return false;
}
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
const species = args[0] as PokemonSpecies;
const isValidStarter = args[1] as Utils.BooleanHolder;
if (species) {
isValidStarter.value = false;
return true;
}
break;
}
return false;
}
/**
* @overrides
*/
getDifficulty(): number {
return 0;
}
static loadChallenge(source: FreshStartChallenge | any): FreshStartChallenge {
const newChallenge = new FreshStartChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Lowers the amount of starter points available.
*/
export class LowerStarterMaxCostChallenge extends Challenge {
constructor() {
super(Challenges.LOWER_MAX_STARTER_COST, 9);
this.addChallengeType(ChallengeType.STARTER_CHOICE);
}
/**
* @override
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return (10 - overrideValue).toString();
}
apply(challengeType: ChallengeType, args: any[]): boolean {
if (this.value === 0) {
return false;
}
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
const species = args[0] as PokemonSpecies;
const isValid = args[1] as Utils.BooleanHolder;
if (speciesStarters[species.speciesId] > 10 - this.value) {
isValid.value = false;
return true;
}
}
return false;
}
static loadChallenge(source: LowerStarterMaxCostChallenge | any): LowerStarterMaxCostChallenge {
const newChallenge = new LowerStarterMaxCostChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Lowers the maximum cost of starters available.
*/
export class LowerStarterPointsChallenge extends Challenge {
constructor() {
super(Challenges.LOWER_STARTER_POINTS, 9);
this.addChallengeType(ChallengeType.STARTER_POINTS);
}
/**
* @override
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return (10 - overrideValue).toString();
}
apply(challengeType: ChallengeType, args: any[]): boolean {
if (this.value === 0) {
return false;
}
switch (challengeType) {
case ChallengeType.STARTER_POINTS:
const points = args[0] as Utils.NumberHolder;
points.value -= this.value;
return true;
}
return false;
}
static loadChallenge(source: LowerStarterPointsChallenge | any): LowerStarterPointsChallenge {
const newChallenge = new LowerStarterPointsChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Apply all challenges of a given challenge type.
* @param {BattleScene} scene The current scene
* @param {ChallengeType} challengeType What challenge type to apply
* @param {any[]} args Any args for that challenge type
* @returns {boolean} True if any challenge was successfully applied.
*/
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean {
let ret = false;
gameMode.challenges.forEach(v => {
if (v.isOfType(challengeType)) {
ret ||= v.apply(challengeType, args);
}
});
return ret;
}
export function copyChallenge(source: Challenge | any): Challenge {
switch (source.id) {
case Challenges.SINGLE_GENERATION:
return SingleGenerationChallenge.loadChallenge(source);
case Challenges.SINGLE_TYPE:
return SingleTypeChallenge.loadChallenge(source);
case Challenges.LOWER_MAX_STARTER_COST:
return LowerStarterMaxCostChallenge.loadChallenge(source);
case Challenges.LOWER_STARTER_POINTS:
return LowerStarterPointsChallenge.loadChallenge(source);
}
throw new Error("Unknown challenge copied");
}
export const allChallenges: Challenge[] = [];
export function initChallenges() {
allChallenges.push(
new SingleGenerationChallenge(),
new SingleTypeChallenge(),
// new LowerStarterMaxCostChallenge(),
// new LowerStarterPointsChallenge(),
// new FreshStartChallenge()
);
}

View File

@ -1,6 +1,5 @@
import BattleScene from "../battle-scene";
import { PlayerPokemon } from "../field/pokemon";
import { GameModes, gameModes } from "../game-mode";
import { Starter } from "../ui/starter-select-ui-handler";
import * as Utils from "../utils";
import { Species } from "./enums/species";
@ -29,7 +28,7 @@ export function getDailyRunStarters(scene: BattleScene, seed: string): Starter[]
const starters: Starter[] = [];
scene.executeWithSeedOffset(() => {
const startingLevel = gameModes[GameModes.DAILY].getStartingLevel();
const startingLevel = scene.gameMode.getStartingLevel();
if (/\d{18}$/.test(seed)) {
for (let s = 0; s < 3; s++) {

View File

@ -435,6 +435,20 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
]
}
],
[TrainerType.SAILOR]: [
{
encounter: [
"dialogue:sailor.encounter.1",
"dialogue:sailor.encounter.2",
"dialogue:sailor.encounter.3",
],
victory: [
"dialogue:sailor.victory.1",
"dialogue:sailor.victory.2",
"dialogue:sailor.victory.3",
]
}
],
[TrainerType.BROCK]: {
encounter: [
"dialogue:brock.encounter.1",

View File

@ -1,6 +1,6 @@
import BattleScene from "../battle-scene";
import { Species } from "./enums/species";
import { getPokemonSpecies, speciesStarters } from "./pokemon-species";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "./pokemon-species";
import { EggTier } from "./enums/egg-type";
import i18next from "../plugins/i18n";
@ -111,3 +111,20 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta
return ret;
}
/**
* Check for a given species EggTier Value
* @param species - Species for wich we will check the egg tier it belongs to
* @returns The egg tier of a given pokemon species
*/
export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier {
const speciesBaseValue = speciesStarters[pokemonSpecies.getRootSpeciesId()];
if (speciesBaseValue <= 3) {
return EggTier.COMMON;
} else if (speciesBaseValue <= 5) {
return EggTier.GREAT;
} else if (speciesBaseValue <= 7) {
return EggTier.ULTRA;
}
return EggTier.MASTER;
}

View File

@ -0,0 +1,7 @@
export enum Challenges {
SINGLE_GENERATION,
SINGLE_TYPE,
LOWER_MAX_STARTER_COST,
LOWER_STARTER_POINTS,
FRESH_START
}

View File

@ -40,6 +40,7 @@ export enum TrainerType {
RICH,
RICH_KID,
ROUGHNECK,
SAILOR,
SCIENTIST,
SMASHER,
SNOW_WORKER,

361
src/data/move.ts Executable file → Normal file
View File

@ -42,7 +42,7 @@ export enum MoveTarget {
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_Pok%C3%A9mon Moves that target all adjacent Pokemon} */
ALL_NEAR_OTHERS,
NEAR_ENEMY,
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_foes Moves that taret all adjacent foes} */
/** {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_target_all_adjacent_foes Moves that target all adjacent foes} */
ALL_NEAR_ENEMIES,
RANDOM_NEAR_ENEMY,
ALL_ENEMIES,
@ -99,6 +99,7 @@ export default class Move implements Localizable {
public id: Moves;
public name: string;
public type: Type;
public defaultType: Type;
public category: MoveCategory;
public moveTarget: MoveTarget;
public power: integer;
@ -118,6 +119,7 @@ export default class Move implements Localizable {
this.nameAppend = "";
this.type = type;
this.defaultType = type;
this.category = category;
this.moveTarget = defaultMoveTarget;
this.power = power;
@ -166,10 +168,22 @@ export default class Move implements Localizable {
return this.attrs.some((attr) => attr instanceof attrType);
}
/**
* Takes as input a boolean function and returns the first MoveAttr in attrs that matches true
* @param attrPredicate
* @returns the first {@linkcode MoveAttr} element in attrs that makes the input function return true
*/
findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr {
return this.attrs.find(attrPredicate);
}
/**
* Adds a new MoveAttr to the move (appends to the attr array)
* if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition}
* @param AttrType {@linkcode MoveAttr} the constructor of a MoveAttr class
* @param args the args needed to instantiate a the given class
* @returns the called object {@linkcode Move}
*/
attr<T extends new (...args: any[]) => MoveAttr>(AttrType: T, ...args: ConstructorParameters<T>): this {
const attr = new AttrType(...args);
this.attrs.push(attr);
@ -184,9 +198,16 @@ export default class Move implements Localizable {
return this;
}
addAttr(attr: MoveAttr): this {
this.attrs.push(attr);
let attrCondition = attr.getCondition();
/**
* Adds a new MoveAttr to the move (appends to the attr array)
* if the MoveAttr also comes with a condition, also adds that to the conditions array: {@linkcode MoveCondition}
* Almost identical to {@link attr}, except you are passing in a MoveAttr object, instead of a constructor and it's arguments
* @param attrAdd {@linkcode MoveAttr} the attribute to add
* @returns the called object {@linkcode Move}
*/
addAttr(attrAdd: MoveAttr): this {
this.attrs.push(attrAdd);
let attrCondition = attrAdd.getCondition();
if (attrCondition) {
if (typeof attrCondition === "function") {
attrCondition = new MoveCondition(attrCondition);
@ -197,15 +218,30 @@ export default class Move implements Localizable {
return this;
}
/**
* Sets the move target of this move
* @param moveTarget {@linkcode MoveTarget} the move target to set
* @returns the called object {@linkcode Move}
*/
target(moveTarget: MoveTarget): this {
this.moveTarget = moveTarget;
return this;
}
/**
* Getter function that returns if this Move has a MoveFlag
* @param flag {@linkcode MoveFlags} to check
* @returns boolean
*/
hasFlag(flag: MoveFlags): boolean {
// internally it is taking the bitwise AND (MoveFlags are represented as bit-shifts) and returning False if result is 0 and true otherwise
return !!(this.flags & flag);
}
/**
* Getter function that returns if the move hits multiple targets
* @returns boolean
*/
isMultiTarget(): boolean {
switch (this.moveTarget) {
case MoveTarget.ALL_OTHERS:
@ -222,6 +258,11 @@ export default class Move implements Localizable {
return false;
}
/**
* Getter function that returns if the move targets itself or an ally
* @returns boolean
*/
isAllyTarget(): boolean {
switch (this.moveTarget) {
case MoveTarget.USER:
@ -235,6 +276,12 @@ export default class Move implements Localizable {
return false;
}
/**
* Checks if the move is immune to certain types
* currently only look at case of Grass types and powder moves
* @param type {@linkcode Type} enum
* @returns boolean
*/
isTypeImmune(type: Type): boolean {
switch (type) {
case Type.GRASS:
@ -246,6 +293,11 @@ export default class Move implements Localizable {
return false;
}
/**
* Adds a move condition to the move
* @param condition {@linkcode MoveCondition} or {@linkcode MoveConditionFunc}, appends to conditions array a new MoveCondition object
* @returns the called object {@linkcode Move}
*/
condition(condition: MoveCondition | MoveConditionFunc): this {
if (typeof condition === "function") {
condition = new MoveCondition(condition as MoveConditionFunc);
@ -255,17 +307,31 @@ export default class Move implements Localizable {
return this;
}
/**
* Marks the move as "partial": appends texts to the move name
* @returns the called object {@linkcode Move}
*/
partial(): this {
this.nameAppend += " (P)";
return this;
}
/**
* Marks the move as "unimplemented": appends texts to the move name
* @returns the called object {@linkcode Move}
*/
unimplemented(): this {
this.nameAppend += " (N)";
return this;
}
/**
* Sets the flags of the move
* @param flag {@linkcode MoveFlags}
* @param on a boolean, if True, then "ORs" the flag onto existing ones, if False then "XORs" the flag onto existing ones
*/
private setFlag(flag: MoveFlags, on: boolean): void {
// bitwise OR and bitwise XOR respectively
if (on) {
this.flags |= flag;
} else {
@ -273,51 +339,110 @@ export default class Move implements Localizable {
}
}
/**
* Sets the {@linkcode MoveFlags.MAKES_CONTACT} flag for the calling Move
* @param makesContact The value (boolean) to set the flag to
* @returns The {@linkcode Move} that called this function
*/
makesContact(makesContact?: boolean): this {
this.setFlag(MoveFlags.MAKES_CONTACT, makesContact);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_PROTECT} flag for the calling Move
* @param ignoresProtect The value (boolean) to set the flag to
* example: @see {@linkcode Moves.CURSE}
* @returns The {@linkcode Move} that called this function
*/
ignoresProtect(ignoresProtect?: boolean): this {
this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_VIRTUAL} flag for the calling Move
* @param ignoresVirtual The value (boolean) to set the flag to
* example: @see {@linkcode Moves.NATURE_POWER}
* @returns The {@linkcode Move} that called this function
*/
ignoresVirtual(ignoresVirtual?: boolean): this {
this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual);
return this;
}
/**
* Sets the {@linkcode MoveFlags.SOUND_BASED} flag for the calling Move
* @param soundBased The value (boolean) to set the flag to
* example: @see {@linkcode Moves.UPROAR}
* @returns The {@linkcode Move} that called this function
*/
soundBased(soundBased?: boolean): this {
this.setFlag(MoveFlags.SOUND_BASED, soundBased);
return this;
}
/**
* Sets the {@linkcode MoveFlags.HIDE_USER} flag for the calling Move
* @param hidesUser The value (boolean) to set the flag to
* example: @see {@linkcode Moves.TELEPORT}
* @returns The {@linkcode Move} that called this function
*/
hidesUser(hidesUser?: boolean): this {
this.setFlag(MoveFlags.HIDE_USER, hidesUser);
return this;
}
/**
* Sets the {@linkcode MoveFlags.HIDE_TARGET} flag for the calling Move
* @param hidesTarget The value (boolean) to set the flag to
* example: @see {@linkcode Moves.WHIRLWIND}
* @returns The {@linkcode Move} that called this function
*/
hidesTarget(hidesTarget?: boolean): this {
this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget);
return this;
}
/**
* Sets the {@linkcode MoveFlags.BITING_MOVE} flag for the calling Move
* @param bitingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.BITE}
* @returns The {@linkcode Move} that called this function
*/
bitingMove(bitingMove?: boolean): this {
this.setFlag(MoveFlags.BITING_MOVE, bitingMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.PULSE_MOVE} flag for the calling Move
* @param pulseMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.WATER_PULSE}
* @returns The {@linkcode Move} that called this function
*/
pulseMove(pulseMove?: boolean): this {
this.setFlag(MoveFlags.PULSE_MOVE, pulseMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.PUNCHING_MOVE} flag for the calling Move
* @param punchingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.DRAIN_PUNCH}
* @returns The {@linkcode Move} that called this function
*/
punchingMove(punchingMove?: boolean): this {
this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.SLICING_MOVE} flag for the calling Move
* @param slicingMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.X_SCISSOR}
* @returns The {@linkcode Move} that called this function
*/
slicingMove(slicingMove?: boolean): this {
this.setFlag(MoveFlags.SLICING_MOVE, slicingMove);
return this;
@ -334,42 +459,92 @@ export default class Move implements Localizable {
return this;
}
/**
* Sets the {@linkcode MoveFlags.BALLBOMB_MOVE} flag for the calling Move
* @param ballBombMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.ELECTRO_BALL}
* @returns The {@linkcode Move} that called this function
*/
ballBombMove(ballBombMove?: boolean): this {
this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.POWDER_MOVE} flag for the calling Move
* @param powderMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.STUN_SPORE}
* @returns The {@linkcode Move} that called this function
*/
powderMove(powderMove?: boolean): this {
this.setFlag(MoveFlags.POWDER_MOVE, powderMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.DANCE_MOVE} flag for the calling Move
* @param danceMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.PETAL_DANCE}
* @returns The {@linkcode Move} that called this function
*/
danceMove(danceMove?: boolean): this {
this.setFlag(MoveFlags.DANCE_MOVE, danceMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.WIND_MOVE} flag for the calling Move
* @param windMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.HURRICANE}
* @returns The {@linkcode Move} that called this function
*/
windMove(windMove?: boolean): this {
this.setFlag(MoveFlags.WIND_MOVE, windMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.TRIAGE_MOVE} flag for the calling Move
* @param triageMove The value (boolean) to set the flag to
* example: @see {@linkcode Moves.ABSORB}
* @returns The {@linkcode Move} that called this function
*/
triageMove(triageMove?: boolean): this {
this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove);
return this;
}
/**
* Sets the {@linkcode MoveFlags.IGNORE_ABILITIES} flag for the calling Move
* @param ignoresAbilities sThe value (boolean) to set the flag to
* example: @see {@linkcode Moves.SUNSTEEL_STRIKE}
* @returns The {@linkcode Move} that called this function
*/
ignoresAbilities(ignoresAbilities?: boolean): this {
this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities);
return this;
}
/**
* Sets the {@linkcode MoveFlags.CHECK_ALL_HITS} flag for the calling Move
* @param checkAllHits The value (boolean) to set the flag to
* example: @see {@linkcode Moves.TRIPLE_AXEL}
* @returns The {@linkcode Move} that called this function
*/
checkAllHits(checkAllHits?: boolean): this {
this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits);
return this;
}
/**
* Checks if the move flag applies to the pokemon(s) using/receiving the move
* @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target
* @param user {@linkcode Pokemon} the Pokemon using the move
* @param target {@linkcode Pokemon} the Pokemon receiving the move
* @returns boolean
*/
checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean {
// special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact
switch (flag) {
case MoveFlags.MAKES_CONTACT:
if (user.hasAbilityWithAttr(IgnoreContactAbAttr)) {
@ -389,6 +564,13 @@ export default class Move implements Localizable {
return !!(this.flags & flag);
}
/**
* Applies each {@linkcode MoveCondition} of this move to the params
* @param user {@linkcode Pokemon} to apply conditions to
* @param target {@linkcode Pokemon} to apply conditions to
* @param move {@linkcode Move} to apply conditions to
* @returns boolean: false if any of the apply()'s return false, else true
*/
applyConditions(user: Pokemon, target: Pokemon, move: Move): boolean {
for (const condition of this.conditions) {
if (!condition.apply(user, target, move)) {
@ -399,6 +581,14 @@ export default class Move implements Localizable {
return true;
}
/**
* Sees if, given the target pokemon, a move fails on it (by looking at each {@linkcode MoveAttr} of this move
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @param cancelled {@linkcode Utils.BooleanHolder} to hold boolean value
* @returns string of the failed text, or null
*/
getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
for (const attr of this.attrs) {
const failedText = attr.getFailedText(user, target, move, cancelled);
@ -409,6 +599,13 @@ export default class Move implements Localizable {
return null;
}
/**
* Calculates the userBenefitScore across all the attributes and conditions
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @returns integer representing the total benefitScore
*/
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
@ -423,10 +620,18 @@ export default class Move implements Localizable {
return score;
}
/**
* Calculates the targetBenefitScore across all the attributes
* @param user {@linkcode Pokemon} using the move
* @param target {@linkcode Pokemon} receiving the move
* @param move {@linkcode Move} using the move
* @returns integer representing the total benefitScore
*/
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
let score = 0;
for (const attr of this.attrs) {
// conditionals to check if the move is self targeting (if so then you are applying the move to yourself, not the target)
score += attr.getTargetBenefitScore(user, !attr.selfTarget ? target : user, move) * (target !== user && attr.selfTarget ? -1 : 1);
}
@ -1046,11 +1251,18 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
this.abilityCondition = abilityCondition;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
//The same as MoveEffectAttr.canApply, except it doesn't check for the target's HP.
canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) {
const isTargetValid =
(this.selfTarget && user.hp && !user.getTag(BattlerTagType.FRENZY)) ||
(!this.selfTarget && (!target.getTag(BattlerTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT)));
return !!isTargetValid;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!this.canApply(user, target, move, args)) {
return false;
}
this.addPartyCurePhase(user);
}
@ -1262,7 +1474,7 @@ export class HitHealAttr extends MoveEffectAttr {
message = i18next.t("battle:drainMessage", {pokemonName: target.name});
} else {
// Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc.
healAmount = Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1);
healAmount = Math.max(Math.floor(user.turnData.currDamageDealt * this.healRatio), 1);
message = i18next.t("battle:regainHealth", {pokemonName: user.name});
}
if (reverseDrain) {
@ -1426,7 +1638,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
}
if ((!pokemon.status || (pokemon.status.effect === this.effect && move.chance < 0))
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, new PokemonMove(move.id), null,this.effect);
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect);
return true;
}
}
@ -1992,7 +2204,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
(args[0] as Utils.BooleanHolder).value = true;
user.scene.queueMessage(getPokemonMessage(user, ` ${this.chargeText.replace("{TARGET}", target.name)}`));
user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER });
user.scene.arena.addTag(this.tagType, 3, move.id, user.id, ArenaTagSide.BOTH, target.getBattlerIndex());
user.scene.arena.addTag(this.tagType, 3, move.id, user.id, ArenaTagSide.BOTH, false, target.getBattlerIndex());
resolve(true);
});
@ -3120,23 +3332,22 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.GENESECT)) {
const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies.formIndex;
const type = (args[0] as Utils.IntegerHolder);
switch (form) {
case 1: // Shock Drive
type.value = Type.ELECTRIC;
move.type = Type.ELECTRIC;
break;
case 2: // Burn Drive
type.value = Type.FIRE;
move.type = Type.FIRE;
break;
case 3: // Chill Drive
type.value = Type.ICE;
move.type = Type.ICE;
break;
case 4: // Douse Drive
type.value = Type.WATER;
move.type = Type.WATER;
break;
default:
type.value = Type.NORMAL;
move.type = Type.NORMAL;
break;
}
return true;
@ -3150,14 +3361,13 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) {
const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies.formIndex;
const type = (args[0] as Utils.IntegerHolder);
switch (form) {
case 1: // Hangry Mode
type.value = Type.DARK;
move.type = Type.DARK;
break;
default: // Full Belly Mode
type.value = Type.ELECTRIC;
move.type = Type.ELECTRIC;
break;
}
return true;
@ -3171,17 +3381,16 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.PALDEA_TAUROS)) {
const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies.formIndex;
const type = (args[0] as Utils.IntegerHolder);
switch (form) {
case 1: // Blaze breed
type.value = Type.FIRE;
move.type = Type.FIRE;
break;
case 2: // Aqua breed
type.value = Type.WATER;
move.type = Type.WATER;
break;
default:
type.value = Type.FIGHTING;
move.type = Type.FIGHTING;
break;
}
return true;
@ -3195,32 +3404,31 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) {
const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex;
const type = (args[0] as Utils.IntegerHolder);
switch (form) {
case 1: // Wellspring Mask
type.value = Type.WATER;
move.type = Type.WATER;
break;
case 2: // Hearthflame Mask
type.value = Type.FIRE;
move.type = Type.FIRE;
break;
case 3: // Cornerstone Mask
type.value = Type.ROCK;
move.type = Type.ROCK;
break;
case 4: // Teal Mask Tera
type.value = Type.GRASS;
move.type = Type.GRASS;
break;
case 5: // Wellspring Mask Tera
type.value = Type.WATER;
move.type = Type.WATER;
break;
case 6: // Hearthflame Mask Tera
type.value = Type.FIRE;
move.type = Type.FIRE;
break;
case 7: // Cornerstone Mask Tera
type.value = Type.ROCK;
move.type = Type.ROCK;
break;
default:
type.value = Type.GRASS;
move.type = Type.GRASS;
break;
}
return true;
@ -3233,23 +3441,21 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) {
const type = (args[0] as Utils.IntegerHolder);
switch (user.scene.arena.weather?.weatherType) {
case WeatherType.SUNNY:
case WeatherType.HARSH_SUN:
type.value = Type.FIRE;
move.type = Type.FIRE;
break;
case WeatherType.RAIN:
case WeatherType.HEAVY_RAIN:
type.value = Type.WATER;
move.type = Type.WATER;
break;
case WeatherType.SANDSTORM:
type.value = Type.ROCK;
move.type = Type.ROCK;
break;
case WeatherType.HAIL:
case WeatherType.SNOW:
type.value = Type.ICE;
move.type = Type.ICE;
break;
default:
return false;
@ -3281,20 +3487,18 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
}
const currentTerrain = user.scene.arena.getTerrainType();
const type = (args[0] as Utils.IntegerHolder);
switch (currentTerrain) {
case TerrainType.MISTY:
type.value = Type.FAIRY;
move.type = Type.FAIRY;
break;
case TerrainType.ELECTRIC:
type.value = Type.ELECTRIC;
move.type = Type.ELECTRIC;
break;
case TerrainType.GRASSY:
type.value = Type.GRASS;
move.type = Type.GRASS;
break;
case TerrainType.PSYCHIC:
type.value = Type.PSYCHIC;
move.type = Type.PSYCHIC;
break;
default:
return false;
@ -3305,8 +3509,6 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const type = (args[0] as Utils.IntegerHolder);
const iv_val = Math.floor(((user.ivs[Stat.HP] & 1)
+(user.ivs[Stat.ATK] & 1) * 2
+(user.ivs[Stat.DEF] & 1) * 4
@ -3314,7 +3516,7 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
+(user.ivs[Stat.SPATK] & 1) * 16
+(user.ivs[Stat.SPDEF] & 1) * 32) * 15/63);
type.value = [
move.type = [
Type.FIGHTING, Type.FLYING, Type.POISON, Type.GROUND,
Type.ROCK, Type.BUG, Type.GHOST, Type.STEEL,
Type.FIRE, Type.WATER, Type.GRASS, Type.ELECTRIC,
@ -3326,16 +3528,14 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
export class MatchUserTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const type = (args[0] as Utils.IntegerHolder);
const userTypes = user.getTypes(true);
if (userTypes.includes(Type.STELLAR)) { // will not change to stellar type
const nonTeraTypes = user.getTypes();
type.value = nonTeraTypes[0];
move.type = nonTeraTypes[0];
return true;
} else if (userTypes.length > 0) {
type.value = userTypes[0];
move.type = userTypes[0];
return true;
} else {
return false;
@ -4021,6 +4221,48 @@ export class RemoveScreensAttr extends MoveEffectAttr {
}
}
/*Swaps arena effects between the player and enemy side
* @extends MoveEffectAttr
* @see {@linkcode apply}
*/
export class SwapArenaTagsAttr extends MoveEffectAttr {
public SwapTags: ArenaTagType[];
constructor(SwapTags: ArenaTagType[]) {
super(true, MoveEffectTrigger.POST_APPLY);
this.SwapTags = SwapTags;
}
apply(user:Pokemon, target:Pokemon, move:Move, args: any[]): boolean {
if (!super.apply(user, target, move, args)) {
return false;
}
const tagPlayerTemp = user.scene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.PLAYER);
const tagEnemyTemp = user.scene.arena.findTagsOnSide((t => this.SwapTags.includes(t.tagType)), ArenaTagSide.ENEMY);
if (tagPlayerTemp) {
for (const swapTagsType of tagPlayerTemp) {
user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.PLAYER, true);
user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.ENEMY, true);
}
}
if (tagEnemyTemp) {
for (const swapTagsType of tagEnemyTemp) {
user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.ENEMY, true);
user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.PLAYER, true);
}
}
user.scene.queueMessage( `${user.name} swapped the battle effects affecting each side of the field!`);
return true;
}
}
/**
* Attribute used for Revival Blessing.
* @extends MoveEffectAttr
@ -4100,7 +4342,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
// Check if the move category is not STATUS or if the switch out condition is not met
if (!this.getSwitchOutCondition()(user, target, move)) {
//Apply effects before switch out i.e. poison point, flame body, etc
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null);
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, move, null);
return resolve(false);
}
@ -4181,7 +4423,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
}
const party = player ? user.scene.getParty() : user.scene.getEnemyParty();
return (!player && !user.scene.currentBattle.battleType) || party.filter(p => !p.isFainted() && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
return (!player && !user.scene.currentBattle.battleType) || party.filter(p => p.isAllowedInBattle() && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
};
}
@ -5710,6 +5952,7 @@ export function initMoves() {
.attr(BypassSleepAttr)
.attr(RandomMovesetMoveAttr)
.condition(userSleptOrComatoseCondition)
.target(MoveTarget.ALL_ENEMIES)
.ignoresVirtual(),
new StatusMove(Moves.HEAL_BELL, Type.NORMAL, -1, 5, -1, 0, 2)
.attr(PartyStatusCureAttr, "A bell chimed!", Abilities.SOUNDPROOF)
@ -5769,7 +6012,7 @@ export function initMoves() {
], true)
.attr(RemoveArenaTrapAttr),
new StatusMove(Moves.SWEET_SCENT, Type.NORMAL, 100, 20, -1, 0, 2)
.attr(StatChangeAttr, BattleStat.EVA, -1)
.attr(StatChangeAttr, BattleStat.EVA, -2)
.target(MoveTarget.ALL_NEAR_ENEMIES),
new AttackMove(Moves.IRON_TAIL, Type.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, 30, 0, 2)
.attr(StatChangeAttr, BattleStat.DEF, -1),
@ -6683,7 +6926,11 @@ export function initMoves() {
.condition((user, target, move) => user.battleData.berriesEaten.length > 0),
new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, 100, 0, 6)
.target(MoveTarget.ALL)
.unimplemented(),
.condition((user,target,move) => {
// If any fielded pokémon is grass-type and grounded.
return [...user.scene.getEnemyParty(),...user.scene.getParty()].some((poke) => poke.isOfType(Type.GRASS) && poke.isGrounded());
})
.attr(StatChangeAttr, [BattleStat.ATK, BattleStat.SPATK], 1, false, (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded()),
new StatusMove(Moves.STICKY_WEB, Type.BUG, -1, 20, -1, 0, 6)
.attr(AddArenaTrapTagAttr, ArenaTagType.STICKY_WEB)
.target(MoveTarget.ENEMY_SIDE),
@ -7253,9 +7500,7 @@ export function initMoves() {
.attr(FirstAttackDoublePowerAttr)
.bitingMove(),
new StatusMove(Moves.COURT_CHANGE, Type.NORMAL, 100, 10, -1, 0, 8)
.target(MoveTarget.BOTH_SIDES)
.unimplemented(),
/* Unused */
.attr(SwapArenaTagsAttr, [ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES]),
new AttackMove(Moves.MAX_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8)
.target(MoveTarget.NEAR_ENEMY)
.unimplemented()

View File

@ -363,7 +363,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.PIDGEOT, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.PIDGEOTITE))
],
[Species.PIKACHU]: [
new SpeciesFormChange(Species.PIKACHU, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
new SpeciesFormChange(Species.PIKACHU, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
new SpeciesFormChange(Species.PIKACHU, "partner", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
],
[Species.MEOWTH]: [
new SpeciesFormChange(Species.MEOWTH, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
@ -397,7 +398,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.LAPRAS, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
],
[Species.EEVEE]: [
new SpeciesFormChange(Species.EEVEE, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
new SpeciesFormChange(Species.EEVEE, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
new SpeciesFormChange(Species.EEVEE, "partner", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
],
[Species.SNORLAX]: [
new SpeciesFormChange(Species.SNORLAX, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))

View File

@ -917,7 +917,7 @@ export function initSpecies() {
new PokemonSpecies(Species.ARBOK, 1, false, false, false, "Cobra Pokémon", Type.POISON, null, 3.5, 65, Abilities.INTIMIDATE, Abilities.SHED_SKIN, Abilities.UNNERVE, 448, 60, 95, 69, 65, 79, 80, 90, 70, 157, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.PIKACHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, GrowthRate.MEDIUM_FAST, 50, true, true,
new PokemonForm("Normal", "", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 320, 35, 55, 40, 50, 50, 90, 190, 50, 112, true, null, true),
new PokemonForm("Partner", "partner", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, "", true),
new PokemonForm("Partner", "partner", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true),
new PokemonForm("Cosplay", "cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
new PokemonForm("Cool Cosplay", "cool-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
new PokemonForm("Beauty Cosplay", "beauty-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
@ -1066,7 +1066,7 @@ export function initSpecies() {
new PokemonSpecies(Species.DITTO, 1, false, false, false, "Transform Pokémon", Type.NORMAL, null, 0.3, 4, Abilities.LIMBER, Abilities.NONE, Abilities.IMPOSTER, 288, 48, 48, 48, 48, 48, 48, 35, 50, 101, GrowthRate.MEDIUM_FAST, null, false),
new PokemonSpecies(Species.EEVEE, 1, false, false, false, "Evolution Pokémon", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, GrowthRate.MEDIUM_FAST, 87.5, false, true,
new PokemonForm("Normal", "", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, false, null, true),
new PokemonForm("Partner", "partner", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 435, 65, 75, 70, 65, 85, 75, 45, 50, 65, false, "", true),
new PokemonForm("Partner", "partner", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 435, 65, 75, 70, 65, 85, 75, 45, 50, 65, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 18, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 425, 70, 75, 80, 60, 95, 45, 45, 50, 65),
),
new PokemonSpecies(Species.VAPOREON, 1, false, false, false, "Bubble Jet Pokémon", Type.WATER, null, 1, 29, Abilities.WATER_ABSORB, Abilities.NONE, Abilities.HYDRATION, 525, 130, 65, 60, 110, 95, 65, 45, 50, 184, GrowthRate.MEDIUM_FAST, 87.5, false),
@ -1286,7 +1286,7 @@ export function initSpecies() {
new PokemonSpecies(Species.PELIPPER, 3, false, false, false, "Water Bird Pokémon", Type.WATER, Type.FLYING, 1.2, 28, Abilities.KEEN_EYE, Abilities.DRIZZLE, Abilities.RAIN_DISH, 440, 60, 50, 100, 95, 70, 65, 45, 50, 154, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.RALTS, 3, false, false, false, "Feeling Pokémon", Type.PSYCHIC, Type.FAIRY, 0.4, 6.6, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 198, 28, 25, 25, 45, 35, 40, 235, 35, 40, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.KIRLIA, 3, false, false, false, "Emotion Pokémon", Type.PSYCHIC, Type.FAIRY, 0.8, 20.2, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 278, 38, 35, 35, 65, 55, 50, 120, 35, 97, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.GARDEVOIR, 3, false, false, false, "Embrace Pokémon", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, GrowthRate.SLOW, 0, false, true,
new PokemonSpecies(Species.GARDEVOIR, 3, false, false, false, "Embrace Pokémon", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, GrowthRate.SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.SYNCHRONIZE, Abilities.TRACE, Abilities.TELEPATHY, 518, 68, 65, 65, 125, 115, 80, 45, 35, 259, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.PSYCHIC, Type.FAIRY, 1.6, 48.4, Abilities.PIXILATE, Abilities.PIXILATE, Abilities.PIXILATE, 618, 68, 85, 65, 165, 135, 100, 45, 35, 259),
),
@ -1404,7 +1404,7 @@ export function initSpecies() {
),
new PokemonSpecies(Species.WYNAUT, 3, false, false, false, "Bright Pokémon", Type.PSYCHIC, null, 0.6, 14, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.TELEPATHY, 260, 95, 23, 48, 23, 48, 23, 125, 50, 52, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.SNORUNT, 3, false, false, false, "Snow Hat Pokémon", Type.ICE, null, 0.7, 16.8, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 300, 50, 50, 50, 50, 50, 50, 190, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.GLALIE, 3, false, false, false, "Face Pokémon", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, GrowthRate.MEDIUM_FAST, 100, false, true,
new PokemonSpecies(Species.GLALIE, 3, false, false, false, "Face Pokémon", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.ICE, null, 1.5, 256.5, Abilities.INNER_FOCUS, Abilities.ICE_BODY, Abilities.MOODY, 480, 80, 80, 80, 80, 80, 80, 75, 50, 168, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.ICE, null, 2.1, 350.2, Abilities.REFRIGERATE, Abilities.REFRIGERATE, Abilities.REFRIGERATE, 580, 80, 120, 80, 120, 80, 100, 75, 50, 168),
),
@ -1829,7 +1829,7 @@ export function initSpecies() {
new PokemonForm("Ordinary Form", "ordinary", Type.WATER, Type.FIGHTING, 1.4, 48.5, Abilities.JUSTIFIED, Abilities.NONE, Abilities.NONE, 580, 91, 72, 90, 129, 90, 108, 3, 35, 290, false, null, true),
new PokemonForm("Resolute", "resolute", Type.WATER, Type.FIGHTING, 1.4, 48.5, Abilities.JUSTIFIED, Abilities.NONE, Abilities.NONE, 580, 91, 72, 90, 129, 90, 108, 3, 35, 290),
),
new PokemonSpecies(Species.MELOETTA, 5, false, false, true, "Melody Pokémon", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, GrowthRate.SLOW, 0, false, true,
new PokemonSpecies(Species.MELOETTA, 5, false, false, true, "Melody Pokémon", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, GrowthRate.SLOW, null, false, true,
new PokemonForm("Aria Forme", "aria", Type.NORMAL, Type.PSYCHIC, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 77, 77, 128, 128, 90, 3, 100, 270, false, null, true),
new PokemonForm("Pirouette Forme", "pirouette", Type.NORMAL, Type.FIGHTING, 0.6, 6.5, Abilities.SERENE_GRACE, Abilities.NONE, Abilities.NONE, 600, 100, 128, 90, 77, 77, 128, 3, 100, 270),
),

View File

@ -187,6 +187,7 @@ export class TrainerConfig {
public isBoss: boolean = false;
public hasStaticParty: boolean = false;
public useSameSeedForAllMembers: boolean = false;
public mixedBattleBgm: string;
public battleBgm: string;
public encounterBgm: string;
public femaleEncounterBgm: string;
@ -217,6 +218,7 @@ export class TrainerConfig {
this.trainerType = trainerType;
this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]);
this.battleBgm = "battle_trainer";
this.mixedBattleBgm = "battle_trainer";
this.victoryBgm = "victory_trainer";
this.partyTemplates = [trainerPartyTemplates.TWO_AVG];
this.speciesFilter = species => (allowLegendaries || (!species.legendary && !species.subLegendary && !species.mythical)) && !species.isTrainerForbidden();
@ -440,6 +442,11 @@ export class TrainerConfig {
return this;
}
setMixedBattleBgm(mixedBattleBgm: string): TrainerConfig {
this.mixedBattleBgm = mixedBattleBgm;
return this;
}
setBattleBgm(battleBgm: string): TrainerConfig {
this.battleBgm = battleBgm;
return this;
@ -605,7 +612,7 @@ export class TrainerConfig {
this.setMoneyMultiplier(3.25);
this.setBoss();
this.setStaticParty();
this.setBattleBgm("battle_elite");
this.setBattleBgm("battle_unova_elite");
this.setVictoryBgm("victory_gym");
this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null));
@ -876,22 +883,22 @@ export const signatureSpecies: SignatureSpecies = {
RYME: [Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU],
TULIP: [Species.GIRAFARIG, Species.FLITTLE, Species.RALTS],
GRUSHA: [Species.CETODDLE, Species.ALOLA_VULPIX, Species.CUBCHOO],
LORELEI: [Species.SLOWBRO, Species.LAPRAS, Species.DEWGONG, Species.ALOLA_SANDSLASH],
BRUNO: [Species.ONIX, Species.HITMONCHAN, Species.HITMONLEE, Species.ALOLA_GOLEM],
AGATHA: [Species.GENGAR, Species.ARBOK, Species.CROBAT, Species.ALOLA_MAROWAK],
LORELEI: [Species.JYNX, [Species.SLOWBRO, Species.GALAR_SLOWBRO], Species.LAPRAS, [Species.ALOLA_SANDSLASH, Species.CLOYSTER]],
BRUNO: [Species.MACHAMP, Species.HITMONCHAN, Species.HITMONLEE, [Species.ALOLA_GOLEM, Species.GOLEM]],
AGATHA: [Species.GENGAR, [Species.ARBOK, Species.WEEZING], Species.CROBAT, Species.ALOLA_MAROWAK],
LANCE: [Species.DRAGONITE, Species.GYARADOS, Species.AERODACTYL, Species.ALOLA_EXEGGUTOR],
WILL: [Species.XATU, Species.JYNX, Species.SLOWBRO, Species.EXEGGUTOR],
KOGA: [Species.WEEZING, Species.VENOMOTH, Species.CROBAT, Species.TENTACRUEL],
WILL: [Species.XATU, Species.JYNX, [Species.SLOWBRO, Species.SLOWKING], Species.EXEGGUTOR],
KOGA: [[Species.WEEZING, Species.MUK], [Species.VENOMOTH, Species.ARIADOS], Species.CROBAT, Species.TENTACRUEL],
KAREN: [Species.UMBREON, Species.HONCHKROW, Species.HOUNDOOM, Species.WEAVILE],
SIDNEY: [Species.SHIFTRY, Species.SHARPEDO, Species.ABSOL, Species.ZOROARK],
PHOEBE: [Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, Species.CHANDELURE],
SIDNEY: [[Species.SHIFTRY, Species.CACTURNE], [Species.SHARPEDO, Species.CRAWDAUNT], Species.ABSOL, Species.MIGHTYENA],
PHOEBE: [Species.SABLEYE, Species.DUSKNOIR, Species.BANETTE, [Species.MISMAGIUS, Species.DRIFBLIM]],
GLACIA: [Species.GLALIE, Species.WALREIN, Species.FROSLASS, Species.ABOMASNOW],
DRAKE: [Species.ALTARIA, Species.SALAMENCE, Species.FLYGON, Species.KINGDRA],
AARON: [Species.SCIZOR, Species.HERACROSS, Species.VESPIQUEN, Species.DRAPION],
AARON: [[Species.SCIZOR, Species.KLEAVOR], Species.HERACROSS, [Species.VESPIQUEN, Species.YANMEGA], Species.DRAPION],
BERTHA: [Species.WHISCASH, Species.HIPPOWDON, Species.GLISCOR, Species.RHYPERIOR],
FLINT: [Species.FLAREON, Species.HOUNDOOM, Species.RAPIDASH, Species.INFERNAPE],
LUCIAN: [Species.MR_MIME, Species.GALLADE, Species.BRONZONG, Species.ALAKAZAM],
SHAUNTAL: [Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.DRIFBLIM],
FLINT: [[Species.FLAREON, Species.RAPIDASH], Species.MAGMORTAR, [Species.STEELIX, Species.LOPUNNY], Species.INFERNAPE],
LUCIAN: [Species.MR_MIME, Species.GALLADE, Species.BRONZONG, [Species.ALAKAZAM, Species.ESPEON]],
SHAUNTAL: [Species.COFAGRIGUS, Species.CHANDELURE, Species.GOLURK, Species.JELLICENT],
MARSHAL: [Species.CONKELDURR, Species.MIENSHAO, Species.THROH, Species.SAWK],
GRIMSLEY: [Species.LIEPARD, Species.KINGAMBIT, Species.SCRAFTY, Species.KROOKODILE],
CAITLIN: [Species.MUSHARNA, Species.GOTHITELLE, Species.SIGILYPH, Species.REUNICLUS],
@ -899,35 +906,35 @@ export const signatureSpecies: SignatureSpecies = {
SIEBOLD: [Species.CLAWITZER, Species.GYARADOS, Species.BARBARACLE, Species.STARMIE],
WIKSTROM: [Species.KLEFKI, Species.PROBOPASS, Species.SCIZOR, Species.AEGISLASH],
DRASNA: [Species.DRAGALGE, Species.DRUDDIGON, Species.ALTARIA, Species.NOIVERN],
HALA: [Species.HARIYAMA, Species.BEWEAR, Species.CRABOMINABLE, Species.POLIWRATH],
HALA: [Species.HARIYAMA, Species.BEWEAR, Species.CRABOMINABLE, [Species.POLIWRATH, Species.ANNIHILAPE]],
MOLAYNE: [Species.KLEFKI, Species.MAGNEZONE, Species.METAGROSS, Species.ALOLA_DUGTRIO],
OLIVIA: [Species.ARMALDO, Species.CRADILY, Species.ALOLA_GOLEM, Species.LYCANROC],
ACEROLA: [Species.BANETTE, Species.DRIFBLIM, Species.DHELMISE, Species.PALOSSAND],
KAHILI: [Species.BRAVIARY, Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON],
MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, Species.TOXICROAK, Species.SCRAFTY, Species.GRIMMSNARL],
NESSA_ELITE: [Species.GOLISOPOD, Species.PELIPPER, Species.QUAGSIRE, Species.TOXAPEX, Species.DREDNAW],
BEA_ELITE: [Species.HAWLUCHA, Species.GRAPPLOCT, Species.SIRFETCHD, Species.FALINKS, Species.MACHAMP],
ALLISTER_ELITE:[Species.DUSKNOIR, Species.CHANDELURE, Species.CURSOLA, Species.RUNERIGUS, Species.GENGAR],
RAIHAN_ELITE: [Species.TORKOAL, Species.GOODRA, Species.TURTONATOR, Species.FLYGON, Species.DURALUDON],
RIKA: [Species.WHISCASH, Species.DONPHAN, Species.CAMERUPT, Species.CLODSIRE],
OLIVIA: [Species.RELICANTH, Species.CARBINK, Species.ALOLA_GOLEM, Species.LYCANROC],
ACEROLA: [[Species.BANETTE, Species.DRIFBLIM], Species.MIMIKYU, Species.DHELMISE, Species.PALOSSAND],
KAHILI: [[Species.BRAVIARY, Species.MANDIBUZZ], Species.HAWLUCHA, Species.ORICORIO, Species.TOUCANNON],
MARNIE_ELITE: [Species.MORPEKO, Species.LIEPARD, [Species.TOXICROAK, Species.SCRAFTY], Species.GRIMMSNARL],
NESSA_ELITE: [Species.GOLISOPOD, [Species.PELIPPER, Species.QUAGSIRE], Species.TOXAPEX, Species.DREDNAW],
BEA_ELITE: [Species.HAWLUCHA, [Species.GRAPPLOCT, Species.SIRFETCHD], Species.FALINKS, Species.MACHAMP],
ALLISTER_ELITE:[Species.DUSKNOIR, [Species.POLTEAGEIST, Species.RUNERIGUS], Species.CURSOLA, Species.GENGAR],
RAIHAN_ELITE: [Species.GOODRA, [Species.TORKOAL, Species.TURTONATOR], Species.FLYGON, Species.ARCHALUDON],
RIKA: [Species.WHISCASH, [Species.DONPHAN, Species.DUGTRIO], Species.CAMERUPT, Species.CLODSIRE],
POPPY: [Species.COPPERAJAH, Species.BRONZONG, Species.CORVIKNIGHT, Species.TINKATON],
LARRY_ELITE: [Species.STARAPTOR, Species.FLAMIGO, Species.ALTARIA, Species.TROPIUS],
HASSEL: [Species.NOIVERN, Species.HAXORUS, Species.DRAGALGE, Species.BAXCALIBUR],
HASSEL: [Species.NOIVERN, [Species.FLAPPLE, Species.APPLETUN], Species.DRAGALGE, Species.BAXCALIBUR],
CRISPIN: [Species.TALONFLAME, Species.CAMERUPT, Species.MAGMORTAR, Species.BLAZIKEN],
AMARYS: [Species.SKARMORY, Species.EMPOLEON, Species.SCIZOR, Species.METAGROSS],
LACEY: [Species.EXCADRILL, Species.PRIMARINA, Species.ALCREMIE, Species.GALAR_SLOWBRO],
DRAYTON: [Species.DRAGONITE, Species.ARCHALUDON, Species.FLYGON, Species.SCEPTILE],
LACEY: [Species.EXCADRILL, Species.PRIMARINA, [Species.ALCREMIE, Species.GRANBULL], Species.WHIMSICOTT],
DRAYTON: [Species.DRAGONITE, Species.ARCHALUDON, Species.HAXORUS, Species.SCEPTILE],
BLUE: [[Species.GYARADOS, Species.EXEGGUTOR, Species.ARCANINE], Species.HO_OH, [Species.RHYPERIOR, Species.MAGNEZONE]], // Alakazam lead, Mega Pidgeot
RED: [Species.LUGIA, Species.SNORLAX, [Species.ESPEON, Species.UMBREON, Species.SYLVEON]], // GMax Pikachu lead, Mega gen 1 starter
LANCE_CHAMPION: [Species.DRAGONITE, Species.KINGDRA, Species.ALOLA_EXEGGUTOR], // Aerodactyl lead, Mega Lati@s
STEVEN: [Species.AGGRON, [Species.ARMALDO, Species.CRADILY], Species.DIALGA], // Skarmorly lead, Mega Metagross
LANCE_CHAMPION: [Species.DRAGONITE, Species.KINGDRA, Species.ALOLA_EXEGGUTOR], // Aerodactyl lead, Mega Latias/Latios
STEVEN: [Species.AGGRON, [Species.ARMALDO, Species.CRADILY], Species.DIALGA], // Skarmory lead, Mega Metagross
WALLACE: [Species.MILOTIC, Species.PALKIA, Species.LUDICOLO], // Pelipper lead, Mega Swampert
CYNTHIA: [Species.GIRATINA, Species.LUCARIO, Species.TOGEKISS], // Spiritomb lead, Mega Garchomp
ALDER: [Species.VOLCARONA, Species.ZEKROM, [Species.ACCELGOR, Species.ESCAVALIER], Species.KELDEO], // Bouffalant/Braviary lead
IRIS: [Species.HAXORUS, Species.RESHIRAM, Species.ARCHEOPS], // Druddigon lead, Gmax Lapras
DIANTHA: [Species.HAWLUCHA, Species.XERNEAS, Species.GOODRA], // Gourgeist lead, Mega Gardevoir
HAU: [[Species.SOLGALEO, Species.LUNALA], Species.NOIVERN, [Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA], [Species.TAPU_BULU, Species.TAPU_FINI, Species.TAPU_KOKO, Species.TAPU_LELE]], // Alola Raichu lead
LEON: [Species.DRAGAPULT, [Species.ZACIAN, Species.ZAMAZENTA], Species.AEGISLASH], // Rillaboom/Cinderace/Inteleon lead
LEON: [Species.DRAGAPULT, [Species.ZACIAN, Species.ZAMAZENTA], Species.AEGISLASH], // Rillaboom/Cinderace/Inteleon lead, GMax Charizard
GEETA: [Species.MIRAIDON, [Species.ESPATHRA, Species.VELUZA], [Species.AVALUGG, Species.HISUI_AVALUGG], Species.KINGAMBIT], // Glimmora lead
NEMONA: [Species.KORAIDON, Species.PAWMOT, [Species.DUDUNSPARCE, Species.ORTHWORM], [Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL]], // Lycanroc lead
KIERAN: [[Species.GRIMMSNARL, Species.INCINEROAR, Species.PORYGON_Z], Species.OGERPON, Species.TERAPAGOS, Species.HYDRAPPLE], // Poliwrath/Politoed lead
@ -1059,6 +1066,7 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.RICH]: new TrainerConfig(++t).setMoneyMultiplier(5).setName("Gentleman").setHasGenders("Madame").setHasDouble("Rich Couple"),
[TrainerType.RICH_KID]: new TrainerConfig(++t).setMoneyMultiplier(3.75).setName("Rich Boy").setHasGenders("Lady").setHasDouble("Rich Kids").setEncounterBgm(TrainerType.RICH),
[TrainerType.ROUGHNECK]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.ROUGHNECK).setSpeciesFilter(s => s.isOfType(Type.DARK)),
[TrainerType.SAILOR]: new TrainerConfig(++t).setMoneyMultiplier(1.4).setEncounterBgm(TrainerType.BACKPACKER).setSpeciesFilter(s => s.isOfType(Type.WATER) || s.isOfType(Type.FIGHTING)),
[TrainerType.SCIENTIST]: new TrainerConfig(++t).setHasGenders("Scientist Female").setHasDouble("Scientists").setMoneyMultiplier(1.7).setEncounterBgm(TrainerType.SCIENTIST)
.setSpeciesPools({
[TrainerPoolTier.COMMON]: [Species.MAGNEMITE, Species.GRIMER, Species.DROWZEE, Species.VOLTORB, Species.KOFFING],
@ -1095,124 +1103,124 @@ export const trainerConfigs: TrainerConfigs = {
.setSpeciesPools(
[Species.CATERPIE, Species.WEEDLE, Species.RATTATA, Species.SENTRET, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.BIDOOF, Species.PATRAT, Species.LILLIPUP]
),
[TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)).initForGymLeader(signatureSpecies["BROCK"],true, Type.ROCK).setBattleBgm("battle_kanto_gym"),
[TrainerType.MISTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MISTY"],false, Type.WATER).setBattleBgm("battle_kanto_gym"),
[TrainerType.LT_SURGE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LT_SURGE"],true, Type.ELECTRIC).setBattleBgm("battle_kanto_gym"),
[TrainerType.ERIKA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ERIKA"],false, Type.GRASS).setBattleBgm("battle_kanto_gym"),
[TrainerType.JANINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JANINE"],false, Type.POISON).setBattleBgm("battle_kanto_gym"),
[TrainerType.SABRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SABRINA"],false, Type.PSYCHIC).setBattleBgm("battle_kanto_gym"),
[TrainerType.BLAINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BLAINE"],true, Type.FIRE).setBattleBgm("battle_kanto_gym"),
[TrainerType.GIOVANNI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GIOVANNI"],true, Type.DARK).setBattleBgm("battle_kanto_gym"),
[TrainerType.FALKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FALKNER"],true, Type.FLYING).setBattleBgm("battle_johto_gym"),
[TrainerType.BUGSY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BUGSY"],true, Type.BUG).setBattleBgm("battle_johto_gym"),
[TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WHITNEY"],false, Type.NORMAL).setBattleBgm("battle_johto_gym"),
[TrainerType.MORTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MORTY"],true, Type.GHOST).setBattleBgm("battle_johto_gym"),
[TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHUCK"],true, Type.FIGHTING).setBattleBgm("battle_johto_gym"),
[TrainerType.JASMINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JASMINE"],false, Type.STEEL).setBattleBgm("battle_johto_gym"),
[TrainerType.PRYCE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PRYCE"],true, Type.ICE).setBattleBgm("battle_johto_gym"),
[TrainerType.CLAIR]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAIR"],false, Type.DRAGON).setBattleBgm("battle_johto_gym"),
[TrainerType.ROXANNE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXANNE"],false, Type.ROCK).setBattleBgm("battle_hoenn_gym"),
[TrainerType.BRAWLY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRAWLY"],true, Type.FIGHTING).setBattleBgm("battle_hoenn_gym"),
[TrainerType.WATTSON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WATTSON"],true, Type.ELECTRIC).setBattleBgm("battle_hoenn_gym"),
[TrainerType.FLANNERY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FLANNERY"],false, Type.FIRE).setBattleBgm("battle_hoenn_gym"),
[TrainerType.NORMAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["NORMAN"],true, Type.NORMAL).setBattleBgm("battle_hoenn_gym"),
[TrainerType.WINONA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WINONA"],false, Type.FLYING).setBattleBgm("battle_hoenn_gym"),
[TrainerType.TATE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TATE"],true, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setHasDouble("tate_liza_double").setDoubleTrainerType(TrainerType.LIZA).setDoubleTitle("gym_leader_double"),
[TrainerType.LIZA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LIZA"],false, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setHasDouble("liza_tate_double").setDoubleTrainerType(TrainerType.TATE).setDoubleTitle("gym_leader_double"),
[TrainerType.JUAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JUAN"],true, Type.WATER).setBattleBgm("battle_hoenn_gym"),
[TrainerType.ROARK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROARK"],true, Type.ROCK).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.GARDENIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GARDENIA"],false, Type.GRASS).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.MAYLENE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MAYLENE"],false, Type.FIGHTING).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.CRASHER_WAKE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRASHER_WAKE"],true, Type.WATER).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.FANTINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FANTINA"],false, Type.GHOST).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.BYRON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BYRON"],true, Type.STEEL).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.CANDICE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CANDICE"],false, Type.ICE).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.VOLKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VOLKNER"],true, Type.ELECTRIC).setBattleBgm("battle_sinnoh_gym"),
[TrainerType.CILAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CILAN"],true, Type.GRASS),
[TrainerType.CHILI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHILI"],true, Type.FIRE),
[TrainerType.CRESS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRESS"],true, Type.WATER),
[TrainerType.CHEREN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHEREN"],true, Type.NORMAL),
[TrainerType.LENORA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LENORA"],false, Type.NORMAL),
[TrainerType.ROXIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXIE"],false, Type.POISON),
[TrainerType.BURGH]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BURGH"],true, Type.BUG),
[TrainerType.ELESA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ELESA"],false, Type.ELECTRIC),
[TrainerType.CLAY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAY"],true, Type.GROUND),
[TrainerType.SKYLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SKYLA"],false, Type.FLYING),
[TrainerType.BRYCEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRYCEN"],true, Type.ICE),
[TrainerType.DRAYDEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["DRAYDEN"],true, Type.DRAGON),
[TrainerType.MARLON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MARLON"],true, Type.WATER),
[TrainerType.VIOLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VIOLA"],false, Type.BUG),
[TrainerType.GRANT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRANT"],true, Type.ROCK),
[TrainerType.KORRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KORRINA"],false, Type.FIGHTING),
[TrainerType.RAMOS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RAMOS"],true, Type.GRASS),
[TrainerType.CLEMONT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLEMONT"],true, Type.ELECTRIC),
[TrainerType.VALERIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VALERIE"],false, Type.FAIRY),
[TrainerType.OLYMPIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OLYMPIA"],false, Type.PSYCHIC),
[TrainerType.WULFRIC]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WULFRIC"],true, Type.ICE),
[TrainerType.MILO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MILO"],true, Type.GRASS),
[TrainerType.NESSA]: new TrainerConfig(++t).setName("Nessa").initForGymLeader(signatureSpecies["NESSA"],false, Type.WATER),
[TrainerType.KABU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KABU"],true, Type.FIRE),
[TrainerType.BEA]: new TrainerConfig(++t).setName("Bea").initForGymLeader(signatureSpecies["BEA"],false, Type.FIGHTING),
[TrainerType.ALLISTER]: new TrainerConfig(++t).setName("Allister").initForGymLeader(signatureSpecies["ALLISTER"],true, Type.GHOST),
[TrainerType.OPAL]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OPAL"],false, Type.FAIRY),
[TrainerType.BEDE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BEDE"],true, Type.FAIRY),
[TrainerType.GORDIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GORDIE"],true, Type.ROCK),
[TrainerType.MELONY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MELONY"],false, Type.ICE),
[TrainerType.PIERS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PIERS"],true, Type.DARK).setHasDouble("piers_marnie_double").setDoubleTrainerType(TrainerType.MARNIE).setDoubleTitle("gym_leader_double"),
[TrainerType.MARNIE]: new TrainerConfig(++t).setName("Marnie").initForGymLeader(signatureSpecies["MARNIE"],false, Type.DARK).setHasDouble("marnie_piers_double").setDoubleTrainerType(TrainerType.PIERS).setDoubleTitle("gym_leader_double"),
[TrainerType.RAIHAN]: new TrainerConfig(++t).setName("Raihan").initForGymLeader(signatureSpecies["RAIHAN"],true, Type.DRAGON),
[TrainerType.KATY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KATY"],false, Type.BUG),
[TrainerType.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"],true, Type.GRASS),
[TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"],false, Type.ELECTRIC),
[TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"],true, Type.WATER),
[TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"],true, Type.NORMAL),
[TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"],false, Type.GHOST),
[TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"],false, Type.PSYCHIC),
[TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"],true, Type.ICE),
[TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)).initForGymLeader(signatureSpecies["BROCK"],true, Type.ROCK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.MISTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MISTY"],false, Type.WATER).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.LT_SURGE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LT_SURGE"],true, Type.ELECTRIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.ERIKA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ERIKA"],false, Type.GRASS).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.JANINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JANINE"],false, Type.POISON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.SABRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SABRINA"],false, Type.PSYCHIC).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.BLAINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BLAINE"],true, Type.FIRE).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.GIOVANNI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GIOVANNI"],true, Type.DARK).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.FALKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FALKNER"],true, Type.FLYING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.BUGSY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BUGSY"],true, Type.BUG).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WHITNEY"],false, Type.NORMAL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.MORTY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MORTY"],true, Type.GHOST).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHUCK"],true, Type.FIGHTING).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.JASMINE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JASMINE"],false, Type.STEEL).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.PRYCE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["PRYCE"],true, Type.ICE).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.CLAIR]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAIR"],false, Type.DRAGON).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.ROXANNE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXANNE"],false, Type.ROCK).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.BRAWLY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRAWLY"],true, Type.FIGHTING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.WATTSON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WATTSON"],true, Type.ELECTRIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.FLANNERY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FLANNERY"],false, Type.FIRE).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.NORMAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["NORMAN"],true, Type.NORMAL).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.WINONA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WINONA"],false, Type.FLYING).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.TATE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TATE"],true, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("tate_liza_double").setDoubleTrainerType(TrainerType.LIZA).setDoubleTitle("gym_leader_double"),
[TrainerType.LIZA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LIZA"],false, Type.PSYCHIC).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym").setHasDouble("liza_tate_double").setDoubleTrainerType(TrainerType.TATE).setDoubleTitle("gym_leader_double"),
[TrainerType.JUAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["JUAN"],true, Type.WATER).setBattleBgm("battle_hoenn_gym").setMixedBattleBgm("battle_hoenn_gym"),
[TrainerType.ROARK]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROARK"],true, Type.ROCK).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.GARDENIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GARDENIA"],false, Type.GRASS).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.MAYLENE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MAYLENE"],false, Type.FIGHTING).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.CRASHER_WAKE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRASHER_WAKE"],true, Type.WATER).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.FANTINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["FANTINA"],false, Type.GHOST).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.BYRON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BYRON"],true, Type.STEEL).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.CANDICE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CANDICE"],false, Type.ICE).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.VOLKNER]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VOLKNER"],true, Type.ELECTRIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.CILAN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CILAN"],true, Type.GRASS).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.CHILI]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHILI"],true, Type.FIRE).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.CRESS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CRESS"],true, Type.WATER).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.CHEREN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CHEREN"],true, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.LENORA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["LENORA"],false, Type.NORMAL).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.ROXIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ROXIE"],false, Type.POISON).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.BURGH]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BURGH"],true, Type.BUG).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.ELESA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["ELESA"],false, Type.ELECTRIC).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.CLAY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLAY"],true, Type.GROUND).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.SKYLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["SKYLA"],false, Type.FLYING).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.BRYCEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRYCEN"],true, Type.ICE).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.DRAYDEN]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["DRAYDEN"],true, Type.DRAGON).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.MARLON]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MARLON"],true, Type.WATER).setMixedBattleBgm("battle_unova_gym"),
[TrainerType.VIOLA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VIOLA"],false, Type.BUG).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.GRANT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRANT"],true, Type.ROCK).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.KORRINA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KORRINA"],false, Type.FIGHTING).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.RAMOS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RAMOS"],true, Type.GRASS).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.CLEMONT]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["CLEMONT"],true, Type.ELECTRIC).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.VALERIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["VALERIE"],false, Type.FAIRY).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.OLYMPIA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OLYMPIA"],false, Type.PSYCHIC).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.WULFRIC]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["WULFRIC"],true, Type.ICE).setMixedBattleBgm("battle_kalos_gym"),
[TrainerType.MILO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MILO"],true, Type.GRASS).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.NESSA]: new TrainerConfig(++t).setName("Nessa").initForGymLeader(signatureSpecies["NESSA"],false, Type.WATER).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.KABU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KABU"],true, Type.FIRE).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.BEA]: new TrainerConfig(++t).setName("Bea").initForGymLeader(signatureSpecies["BEA"],false, Type.FIGHTING).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.ALLISTER]: new TrainerConfig(++t).setName("Allister").initForGymLeader(signatureSpecies["ALLISTER"],true, Type.GHOST).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.OPAL]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["OPAL"],false, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.BEDE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BEDE"],true, Type.FAIRY).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.GORDIE]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GORDIE"],true, Type.ROCK).setMixedBattleBgm("battle_galar_gym"),
[TrainerType.MELONY]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["MELONY"],false, Type.ICE).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.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.BRASSIUS]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["BRASSIUS"],true, Type.GRASS).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.IONO]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["IONO"],false, Type.ELECTRIC).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.KOFU]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["KOFU"],true, Type.WATER).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.LARRY]: new TrainerConfig(++t).setName("Larry").initForGymLeader(signatureSpecies["LARRY"],true, Type.NORMAL).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.RYME]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["RYME"],false, Type.GHOST).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.TULIP]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["TULIP"],false, Type.PSYCHIC).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.GRUSHA]: new TrainerConfig(++t).initForGymLeader(signatureSpecies["GRUSHA"],true, Type.ICE).setMixedBattleBgm("battle_paldea_gym"),
[TrainerType.LORELEI]: new TrainerConfig((t = TrainerType.LORELEI)).initForEliteFour(signatureSpecies["LORELEI"],false, Type.ICE),
[TrainerType.BRUNO]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BRUNO"], true, Type.FIGHTING),
[TrainerType.AGATHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AGATHA"], false,Type.GHOST),
[TrainerType.LANCE]: new TrainerConfig(++t).setName("Lance").initForEliteFour(signatureSpecies["LANCE"],true, Type.DRAGON),
[TrainerType.WILL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WILL"],true, Type.PSYCHIC),
[TrainerType.KOGA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KOGA"], true, Type.POISON),
[TrainerType.KAREN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAREN"],false, Type.DARK),
[TrainerType.SIDNEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIDNEY"],true, Type.DARK),
[TrainerType.PHOEBE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["PHOEBE"],false, Type.GHOST),
[TrainerType.GLACIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GLACIA"],false, Type.ICE),
[TrainerType.DRAKE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAKE"],true, Type.DRAGON),
[TrainerType.AARON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AARON"],true, Type.BUG),
[TrainerType.BERTHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["BERTHA"],false, Type.GROUND),
[TrainerType.FLINT]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["FLINT"],true, Type.FIRE),
[TrainerType.LUCIAN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LUCIAN"], true,Type.PSYCHIC),
[TrainerType.SHAUNTAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SHAUNTAL"],false, Type.GHOST),
[TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"],true, Type.FIGHTING),
[TrainerType.GRIMSLEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GRIMSLEY"],true, Type.DARK),
[TrainerType.CAITLIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CAITLIN"],false, Type.PSYCHIC),
[TrainerType.MALVA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MALVA"], false,Type.FIRE),
[TrainerType.SIEBOLD]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIEBOLD"], true,Type.WATER),
[TrainerType.WIKSTROM]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WIKSTROM"],true, Type.STEEL),
[TrainerType.DRASNA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRASNA"],false, Type.DRAGON),
[TrainerType.HALA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HALA"],true, Type.FIGHTING),
[TrainerType.MOLAYNE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MOLAYNE"],true, Type.STEEL),
[TrainerType.OLIVIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["OLIVIA"],false, Type.ROCK),
[TrainerType.ACEROLA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["ACEROLA"],false, Type.GHOST),
[TrainerType.KAHILI]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAHILI"],false, Type.FLYING),
[TrainerType.MARNIE_ELITE]: new TrainerConfig(++t).setName("Marnie").initForEliteFour(signatureSpecies["MARNIE_ELITE"],false, Type.DARK),
[TrainerType.NESSA_ELITE]: new TrainerConfig(++t).setName("Nessa").initForEliteFour(signatureSpecies["NESSA_ELITE"],false, Type.WATER),
[TrainerType.BEA_ELITE]: new TrainerConfig(++t).setName("Bea").initForEliteFour(signatureSpecies["BEA_ELITE"],false, Type.FIGHTING),
[TrainerType.ALLISTER_ELITE]: new TrainerConfig(++t).setName("Allister").initForEliteFour(signatureSpecies["ALLISTER_ELITE"],true, Type.GHOST),
[TrainerType.RAIHAN_ELITE]: new TrainerConfig(++t).setName("Raihan").initForEliteFour(signatureSpecies["RAIHAN_ELITE"],true, Type.DRAGON),
[TrainerType.RIKA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["RIKA"],false, Type.GROUND),
[TrainerType.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"],false, Type.STEEL),
[TrainerType.LARRY_ELITE]: new TrainerConfig(++t).setName("Larry").initForEliteFour(signatureSpecies["LARRY_ELITE"],true, Type.NORMAL, Type.FLYING),
[TrainerType.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"],true, Type.DRAGON),
[TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"],true, Type.FIRE),
[TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"],false, Type.STEEL),
[TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"],false, Type.FAIRY),
[TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"],true, Type.DRAGON),
[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.AGATHA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AGATHA"], false,Type.GHOST).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.LANCE]: new TrainerConfig(++t).setName("Lance").initForEliteFour(signatureSpecies["LANCE"],true, Type.DRAGON).setBattleBgm("battle_kanto_gym").setMixedBattleBgm("battle_kanto_gym"),
[TrainerType.WILL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WILL"],true, Type.PSYCHIC).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.KOGA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KOGA"], true, Type.POISON).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.KAREN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAREN"],false, Type.DARK).setBattleBgm("battle_johto_gym").setMixedBattleBgm("battle_johto_gym"),
[TrainerType.SIDNEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIDNEY"],true, Type.DARK).setMixedBattleBgm("battle_hoenn_elite"),
[TrainerType.PHOEBE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["PHOEBE"],false, Type.GHOST).setMixedBattleBgm("battle_hoenn_elite"),
[TrainerType.GLACIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GLACIA"],false, Type.ICE).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.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.LUCIAN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LUCIAN"], true,Type.PSYCHIC).setBattleBgm("battle_sinnoh_gym").setMixedBattleBgm("battle_sinnoh_gym"),
[TrainerType.SHAUNTAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SHAUNTAL"],false, Type.GHOST).setMixedBattleBgm("battle_unova_elite"),
[TrainerType.MARSHAL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MARSHAL"],true, Type.FIGHTING).setMixedBattleBgm("battle_unova_elite"),
[TrainerType.GRIMSLEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["GRIMSLEY"],true, Type.DARK).setMixedBattleBgm("battle_unova_elite"),
[TrainerType.CAITLIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CAITLIN"],false, Type.PSYCHIC).setMixedBattleBgm("battle_unova_elite"),
[TrainerType.MALVA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MALVA"], false,Type.FIRE).setMixedBattleBgm("battle_kalos_elite"),
[TrainerType.SIEBOLD]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["SIEBOLD"], true,Type.WATER).setMixedBattleBgm("battle_kalos_elite"),
[TrainerType.WIKSTROM]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["WIKSTROM"],true, Type.STEEL).setMixedBattleBgm("battle_kalos_elite"),
[TrainerType.DRASNA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRASNA"],false, Type.DRAGON).setMixedBattleBgm("battle_kalos_elite"),
[TrainerType.HALA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HALA"],true, Type.FIGHTING).setMixedBattleBgm("battle_alola_elite"),
[TrainerType.MOLAYNE]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["MOLAYNE"],true, Type.STEEL).setMixedBattleBgm("battle_alola_elite"),
[TrainerType.OLIVIA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["OLIVIA"],false, Type.ROCK).setMixedBattleBgm("battle_alola_elite"),
[TrainerType.ACEROLA]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["ACEROLA"],false, Type.GHOST).setMixedBattleBgm("battle_alola_elite"),
[TrainerType.KAHILI]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["KAHILI"],false, Type.FLYING).setMixedBattleBgm("battle_alola_elite"),
[TrainerType.MARNIE_ELITE]: new TrainerConfig(++t).setName("Marnie").initForEliteFour(signatureSpecies["MARNIE_ELITE"],false, Type.DARK).setMixedBattleBgm("battle_galar_elite"),
[TrainerType.NESSA_ELITE]: new TrainerConfig(++t).setName("Nessa").initForEliteFour(signatureSpecies["NESSA_ELITE"],false, Type.WATER).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.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.POPPY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["POPPY"],false, Type.STEEL).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.HASSEL]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["HASSEL"],true, Type.DRAGON).setMixedBattleBgm("battle_paldea_elite"),
[TrainerType.CRISPIN]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["CRISPIN"],true, Type.FIRE).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.AMARYS]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["AMARYS"],false, Type.STEEL).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.LACEY]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["LACEY"],false, Type.FAIRY).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.DRAYTON]: new TrainerConfig(++t).initForEliteFour(signatureSpecies["DRAYTON"],true, Type.DRAGON).setMixedBattleBgm("battle_bb_elite"),
[TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(signatureSpecies["BLUE"],true).setBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double")
[TrainerType.BLUE]: new TrainerConfig((t = TrainerType.BLUE)).initForChampion(signatureSpecies["BLUE"],true).setBattleBgm("battle_kanto_champion").setMixedBattleBgm("battle_kanto_champion").setHasDouble("blue_red_double").setDoubleTrainerType(TrainerType.RED).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.ALAKAZAM], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1220,7 +1228,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.RED]: new TrainerConfig(++t).initForChampion(signatureSpecies["RED"],true).setBattleBgm("battle_johto_champion").setHasDouble("red_blue_double").setDoubleTrainerType(TrainerType.BLUE).setDoubleTitle("champion_double")
[TrainerType.RED]: new TrainerConfig(++t).initForChampion(signatureSpecies["RED"],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 => {
p.formIndex = 8;
p.generateAndPopulateMoveset();
@ -1229,7 +1237,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(signatureSpecies["LANCE_CHAMPION"],true).setBattleBgm("battle_johto_champion")
[TrainerType.LANCE_CHAMPION]: new TrainerConfig(++t).setName("Lance").initForChampion(signatureSpecies["LANCE_CHAMPION"],true).setBattleBgm("battle_johto_champion").setMixedBattleBgm("battle_johto_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.AERODACTYL], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1237,7 +1245,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(signatureSpecies["STEVEN"],true).setBattleBgm("battle_hoenn_champion").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double")
[TrainerType.STEVEN]: new TrainerConfig(++t).initForChampion(signatureSpecies["STEVEN"],true).setBattleBgm("battle_hoenn_champion").setMixedBattleBgm("battle_hoenn_champion").setHasDouble("steven_wallace_double").setDoubleTrainerType(TrainerType.WALLACE).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SKARMORY], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1245,7 +1253,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(signatureSpecies["WALLACE"],true).setBattleBgm("battle_hoenn_champion").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double")
[TrainerType.WALLACE]: new TrainerConfig(++t).initForChampion(signatureSpecies["WALLACE"],true).setBattleBgm("battle_hoenn_champion").setMixedBattleBgm("battle_hoenn_champion").setHasDouble("wallace_steven_double").setDoubleTrainerType(TrainerType.STEVEN).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.PELIPPER], TrainerSlot.TRAINER, true, p => {
p.abilityIndex = 1; // Drizzle
p.generateAndPopulateMoveset();
@ -1254,7 +1262,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(signatureSpecies["CYNTHIA"],false).setBattleBgm("battle_sinnoh_champion")
[TrainerType.CYNTHIA]: new TrainerConfig(++t).initForChampion(signatureSpecies["CYNTHIA"],false).setBattleBgm("battle_sinnoh_champion").setMixedBattleBgm("battle_sinnoh_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.SPIRITOMB], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1262,11 +1270,11 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(signatureSpecies["ALDER"],true).setHasDouble("alder_iris_double").setDoubleTrainerType(TrainerType.IRIS).setDoubleTitle("champion_double").setBattleBgm("battle_champion_alder")
[TrainerType.ALDER]: new TrainerConfig(++t).initForChampion(signatureSpecies["ALDER"],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], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
})),
[TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(signatureSpecies["IRIS"],false).setBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double")
[TrainerType.IRIS]: new TrainerConfig(++t).initForChampion(signatureSpecies["IRIS"],false).setBattleBgm("battle_champion_iris").setMixedBattleBgm("battle_champion_iris").setHasDouble("iris_alder_double").setDoubleTrainerType(TrainerType.ALDER).setDoubleTitle("champion_double")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.DRUDDIGON], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1274,7 +1282,7 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(signatureSpecies["DIANTHA"],false)
[TrainerType.DIANTHA]: new TrainerConfig(++t).initForChampion(signatureSpecies["DIANTHA"],false).setMixedBattleBgm("battle_kalos_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.GOURGEIST], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1282,11 +1290,11 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 1;
p.generateAndPopulateMoveset();
})),
[TrainerType.HAU]: new TrainerConfig(++t).initForChampion(signatureSpecies["HAU"],true)
[TrainerType.HAU]: new TrainerConfig(++t).initForChampion(signatureSpecies["HAU"],true).setMixedBattleBgm("battle_alola_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.ALOLA_RAICHU], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
})),
[TrainerType.LEON]: new TrainerConfig(++t).initForChampion(signatureSpecies["LEON"],true)
[TrainerType.LEON]: new TrainerConfig(++t).initForChampion(signatureSpecies["LEON"],true).setMixedBattleBgm("battle_galar_champion")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.RILLABOOM, Species.CINDERACE, Species.INTELEON], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
}))
@ -1294,15 +1302,16 @@ export const trainerConfigs: TrainerConfigs = {
p.formIndex = 3;
p.generateAndPopulateMoveset();
})),
[TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(signatureSpecies["GEETA"],false)
[TrainerType.GEETA]: new TrainerConfig(++t).initForChampion(signatureSpecies["GEETA"],false).setMixedBattleBgm("battle_champion_geeta")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.GLIMMORA], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
})),
[TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(signatureSpecies["NEMONA"],false)
[TrainerType.NEMONA]: new TrainerConfig(++t).initForChampion(signatureSpecies["NEMONA"],false).setMixedBattleBgm("battle_champion_nemona")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.LYCANROC], TrainerSlot.TRAINER, true, p => {
p.formIndex = 0; // Midday form
p.generateAndPopulateMoveset();
})),
[TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(signatureSpecies["KIERAN"],true)
[TrainerType.KIERAN]: new TrainerConfig(++t).initForChampion(signatureSpecies["KIERAN"],true).setMixedBattleBgm("battle_champion_kieran")
.setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.POLIWRATH, Species.POLITOED], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();
})),

Some files were not shown because too many files have changed in this diff Show More