From 8df3383f2480e75c834a75937b75d85b906f0df2 Mon Sep 17 00:00:00 2001 From: Lugiad Date: Mon, 3 Nov 2025 23:35:24 +0100 Subject: [PATCH 01/18] 1.11.3 assets and locales update 1.11.3 assets and locales update --- assets | 2 +- locales | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index d2d9309cd1a..03b11be3b4a 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d2d9309cd1acfcebeefbf4c1c63e1104a1294ed8 +Subproject commit 03b11be3b4a46f1738874e5ab37336abf0158ac1 diff --git a/locales b/locales index ddf9509e1c6..00c44a11c19 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit ddf9509e1c6abe8fc93b455d79bfaa0202e05ede +Subproject commit 00c44a11c1987b07477502591341bdea6a1c1c5e From 17ac20ef0b8647b68089166df9bac89c7b25ad8b Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:49:19 -0600 Subject: [PATCH 02/18] [Hotfix] Fix Flare Admins using wrong pool (#6742) * Update evil-admin-trainer-pools.ts * fixes * add comment --------- Co-authored-by: damocleas --- src/data/trainers/evil-admin-trainer-pools.ts | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/data/trainers/evil-admin-trainer-pools.ts b/src/data/trainers/evil-admin-trainer-pools.ts index ae7ff106f8a..27b9a70dcce 100644 --- a/src/data/trainers/evil-admin-trainer-pools.ts +++ b/src/data/trainers/evil-admin-trainer-pools.ts @@ -223,57 +223,47 @@ const PLASMA_COLRESS: TrainerTierPools = { const FLARE: TrainerTierPools = { [TrainerPoolTier.COMMON]: [ - SpeciesId.FOONGUS, SpeciesId.SCRAGGY, - SpeciesId.DRUDDIGON, + SpeciesId.FOONGUS, SpeciesId.BUNNELBY, SpeciesId.FLETCHLING, SpeciesId.PANCHAM, SpeciesId.ESPURR, - SpeciesId.PUMPKABOO, - SpeciesId.PHANTUMP, + SpeciesId.SKRELP, SpeciesId.CLAUNCHER, - SpeciesId.HELIOPTILE, SpeciesId.KLEFKI, + SpeciesId.PHANTUMP, + SpeciesId.PUMPKABOO, ], [TrainerPoolTier.UNCOMMON]: [ SpeciesId.LITWICK, SpeciesId.HEATMOR, SpeciesId.BINACLE, - SpeciesId.SKRELP, - SpeciesId.BERGMITE, + SpeciesId.HELIOPTILE, + SpeciesId.AVALUGG, SpeciesId.CAPSAKID, ], - [TrainerPoolTier.RARE]: [SpeciesId.GOODRA, SpeciesId.HONEDGE], + [TrainerPoolTier.RARE]: [SpeciesId.AERODACTYL, SpeciesId.HONEDGE, SpeciesId.GOOMY], }; const FLARE_XEROSIC: TrainerTierPools = { [TrainerPoolTier.COMMON]: [ + SpeciesId.EKANS, + SpeciesId.LITWICK, SpeciesId.PANCHAM, SpeciesId.BINACLE, + [SpeciesId.SKRELP, SpeciesId.CLAUNCHER], SpeciesId.HELIOPTILE, - SpeciesId.CLAUNCHER, - SpeciesId.BUNNELBY, - SpeciesId.FLETCHLING, - SpeciesId.LITLEO, - SpeciesId.PANGORO, - SpeciesId.ESPURR, - SpeciesId.INKAY, - SpeciesId.CLAUNCHER, - SpeciesId.HELIOPTILE, + SpeciesId.KLEFKI, ], [TrainerPoolTier.UNCOMMON]: [ - [SpeciesId.AMAURA, SpeciesId.TYRUNT], - SpeciesId.SNEASEL, - SpeciesId.LITWICK, - SpeciesId.LITLEO, - SpeciesId.BINACLE, - SpeciesId.SKRELP, + SpeciesId.ROTOM, // Always Rotom-Heat, Xerosic has their specialty type set to fire + [SpeciesId.TYRUNT, SpeciesId.AMAURA], SpeciesId.NOIBAT, - SpeciesId.PHANTUMP, - SpeciesId.PUMPKABOO, + SpeciesId.SIZZLIPEDE, + SpeciesId.CAPSAKID, ], - [TrainerPoolTier.RARE]: [SpeciesId.HISUI_GOODRA, SpeciesId.HONEDGE], + [TrainerPoolTier.RARE]: [SpeciesId.BELDUM, SpeciesId.HISUI_SLIGGOO, SpeciesId.HISUI_AVALUGG], }; const AETHER: TrainerTierPools = { From c0e988a37baf6edee4604bad29435b84c966599e Mon Sep 17 00:00:00 2001 From: Jimmybald1 <122436263+Jimmybald1@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:12:03 +0100 Subject: [PATCH 03/18] [Hotfix] Fixed Candy Jar in Daily run event items disappearing after refresh (#6749) Added id func to daily run event starting items Co-authored-by: Jimmybald1 <147992650+IBBCalc@users.noreply.github.com> --- src/phases/title-phase.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index a18be85374f..ceac7ed1958 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -268,7 +268,13 @@ export class TitlePhase extends Phase { globalScene.addModifier(m, true, false, false, true); } for (const m of timedEventManager.getEventDailyStartingItems()) { - globalScene.addModifier(modifierTypes[m]().newModifier(), true, false, false, true); + globalScene.addModifier( + modifierTypes[m]().withIdFromFunc(modifierTypes[m]).newModifier(), + true, + false, + false, + true, + ); } globalScene.updateModifiers(true, true); From 5a8ff3a47f579b82f373daf57c8571d2cf8683c2 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Tue, 4 Nov 2025 11:11:16 -0600 Subject: [PATCH 04/18] [Balance Hotfix] Remove Evil Team Admin special properties at Wave 164 (#6746) * Update trainer-config.ts * Update trainer-config.ts * Update trainer-config.ts --------- Co-authored-by: damocleas --- src/data/trainers/trainer-config.ts | 63 ++++------------------------- 1 file changed, 7 insertions(+), 56 deletions(-) diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 8d95bad2767..8c19789754c 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -592,15 +592,9 @@ export class TrainerConfig { * @param poolName - The evil team the admin belongs to. * @param signatureSpecies - The signature species for the evil team leader. * @param specialtyType - The specialty Type of the admin, if they have one - * @param starAdminInstantTeraSlot - (default `4`); If the admin is a Star Admin, the slot that should instantly Tera in {@linkcode ClassicFixedBossWaves.EVIL_ADMIN_3} * @returns The updated TrainerConfig instance. */ - initForEvilTeamAdmin( - title: string, - poolName: EvilTeam, - specialtyType?: PokemonType, - starAdminInstantTeraSlot = 4, - ): TrainerConfig { + initForEvilTeamAdmin(title: string, poolName: EvilTeam, specialtyType?: PokemonType): TrainerConfig { if (!getIsInitialized()) { initI18n(); } @@ -609,13 +603,6 @@ export class TrainerConfig { this.setSpecialtyType(specialtyType); } - if (title === "star_admin") { - this.setInstantTera( - starAdminInstantTeraSlot, - () => globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3, - ); - } - this.setPartyTemplates(trainerPartyTemplates.RIVAL_5); // Set the species pools for the evil team admin. @@ -2103,9 +2090,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.ARBOK], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 0; // Intimidate p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2124,9 +2108,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.CROBAT], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; }), @@ -2200,9 +2181,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.CAMERUPT], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 1; // Solid Rock p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2221,9 +2199,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.CAMERUPT], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 1; // Solid Rock p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2285,9 +2260,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.SHARPEDO], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; }), @@ -2305,9 +2277,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.SHARPEDO], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; }), @@ -2366,9 +2335,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.SKUNTANK], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; }), @@ -2386,9 +2352,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.PURUGLY], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 0; // Thick Fat p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2407,9 +2370,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.TOXICROAK], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 1; // Dry Skin p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2489,9 +2449,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.KLINKLANG], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 2; // Clear Body p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2564,9 +2521,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.MALAMAR], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 0; // Contrary p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2636,9 +2590,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.HYPNO], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 1; // FOREWARN p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2703,9 +2654,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.SALAZZLE], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; }), @@ -2768,9 +2716,6 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([SpeciesId.GARBODOR], TrainerSlot.TRAINER, true, p => { - if (globalScene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_ADMIN_3) { - p.setBoss(true, 2); - } p.abilityIndex = 1; // Weak Armor p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -2853,6 +2798,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); } else { p.formIndex = 1; // Segin Starmobile + p.gender = Gender.GENDERLESS; p.moveset = [ new PokemonMove(MoveId.WICKED_TORQUE), new PokemonMove(MoveId.SPIN_OUT), @@ -2880,6 +2826,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); } else { p.formIndex = 2; // Schedar Starmobile + p.gender = Gender.GENDERLESS; p.moveset = [ new PokemonMove(MoveId.BLAZING_TORQUE), new PokemonMove(MoveId.SPIN_OUT), @@ -2907,6 +2854,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); } else { p.formIndex = 3; // Navi Starmobile + p.gender = Gender.GENDERLESS; p.moveset = [ new PokemonMove(MoveId.NOXIOUS_TORQUE), new PokemonMove(MoveId.SPIN_OUT), @@ -2934,6 +2882,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); } else { p.formIndex = 4; // Ruchbah Starmobile + p.gender = Gender.GENDERLESS; p.moveset = [ new PokemonMove(MoveId.MAGICAL_TORQUE), new PokemonMove(MoveId.SPIN_OUT), @@ -2961,6 +2910,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); } else { p.formIndex = 5; // Caph Starmobile + p.gender = Gender.GENDERLESS; p.moveset = [ new PokemonMove(MoveId.COMBAT_TORQUE), new PokemonMove(MoveId.SPIN_OUT), @@ -5825,6 +5775,7 @@ export const trainerConfigs: TrainerConfigs = { getRandomPartyMemberFunc([SpeciesId.REVAVROOM], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.formIndex = randSeedInt(5, 1); // Random Starmobile form + p.gender = Gender.GENDERLESS; p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ROGUE_BALL; }), From aea463ab4b8b0ca1c328602b98d78f89c664a156 Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:07:19 -0800 Subject: [PATCH 05/18] [Hotfix] Prevent crash with two NG Pokemon fainting at the same time (#6751) Prevent crash with two NG Pokemon fainting at the same time --- src/data/arena-tag.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index f8829ee4344..b35029af4a1 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1532,6 +1532,11 @@ export class SuppressAbilitiesTag extends SerializableArenaTag { const setter = globalScene .getField(true) .filter(p => p.hasAbilityWithAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr", false))[0]; + // Setter may not exist if both NG Pokemon faint simultaneously + if (setter == null) { + return; + } + applyOnGainAbAttrs({ pokemon: setter, passive: setter.getAbility().hasAttr("PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr"), From ef3d950464ee2ccad2cce6dd3311ace7bda941a8 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:52:22 -0600 Subject: [PATCH 06/18] [Balance Hotfix] Miscellaneous Trainer Changes (#6747) * Update trainer-config.ts * Update trainer-config.ts * fix sinischa now shares party slot with Polteageist anyways * Linting, remove redundant evil team admin money multipliers The money multiplier is already set via initForEvilTeamAdmin, and this just removes redundancy. * Update trainer-config.ts * Buzzwole is no longer Rogue Ball gendered --------- Co-authored-by: damocleas Co-authored-by: Madmadness65 Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> --- src/data/trainers/rival-party-config.ts | 6 +- src/data/trainers/trainer-config.ts | 97 +++++++++++-------------- 2 files changed, 45 insertions(+), 58 deletions(-) diff --git a/src/data/trainers/rival-party-config.ts b/src/data/trainers/rival-party-config.ts index 67b3f50d379..0a014764bbc 100644 --- a/src/data/trainers/rival-party-config.ts +++ b/src/data/trainers/rival-party-config.ts @@ -420,7 +420,7 @@ const SLOT_4_FIGHT_3 = [ SpeciesId.GOLISOPOD, SpeciesId.MIMIKYU, SpeciesId.DHELMISE, - SpeciesId.POLTEAGEIST, + [SpeciesId.POLTEAGEIST, SpeciesId.SINISTCHA], SpeciesId.COPPERAJAH, SpeciesId.KLEAVOR, SpeciesId.BASCULIN, @@ -431,7 +431,6 @@ const SLOT_4_FIGHT_3 = [ SpeciesId.DONDOZO, SpeciesId.DUDUNSPARCE, SpeciesId.GHOLDENGO, - SpeciesId.POLTCHAGEIST, [SpeciesId.GALAR_SLOWBRO, SpeciesId.GALAR_SLOWKING], SpeciesId.HISUI_ARCANINE, SpeciesId.PALDEA_TAUROS, @@ -485,7 +484,7 @@ const SLOT_4_FINAL = [ SpeciesId.GOLISOPOD, SpeciesId.MIMIKYU, SpeciesId.DHELMISE, - SpeciesId.POLTEAGEIST, + [SpeciesId.POLTEAGEIST, SpeciesId.SINISTCHA], SpeciesId.COPPERAJAH, SpeciesId.KLEAVOR, SpeciesId.BASCULEGION, // Ensure gender does not change @@ -496,7 +495,6 @@ const SLOT_4_FINAL = [ SpeciesId.DONDOZO, SpeciesId.DUDUNSPARCE, SpeciesId.GHOLDENGO, - SpeciesId.POLTCHAGEIST, [SpeciesId.GALAR_SLOWBRO, SpeciesId.GALAR_SLOWKING], SpeciesId.HISUI_ARCANINE, SpeciesId.PALDEA_TAUROS, diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 8c19789754c..2cdcaf1552f 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -2057,7 +2057,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.DRATINI, SpeciesId.LARVITAR], }), [TrainerType.ARCHER]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("rocket_admin", "rocket_archer") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2078,7 +2077,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.ARIANA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("rocket_admin_female", "rocket_ariana") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2096,7 +2094,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.PROTON]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("rocket_admin", "rocket_proton") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2113,7 +2110,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.PETREL]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("rocket_admin", "rocket_petrel") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2169,7 +2165,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.RHYHORN, SpeciesId.ARON], }), [TrainerType.TABITHA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("magma_admin", "magma") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2187,7 +2182,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.COURTNEY]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("magma_admin_female", "magma") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2248,7 +2242,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.FEEBAS, SpeciesId.DONDOZO], }), [TrainerType.MATT]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("aqua_admin", "aqua") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2265,7 +2258,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.SHELLY]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("aqua_admin_female", "aqua") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2323,7 +2315,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.SPIRITOMB, SpeciesId.ROTOM], }), [TrainerType.JUPITER]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("galactic_commander_female", "galactic_jupiter") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2340,7 +2331,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.MARS]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("galactic_commander_female", "galactic_mars") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2358,7 +2348,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.SATURN]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("galactic_commander", "galactic_saturn") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2420,7 +2409,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.AXEW, SpeciesId.DRUDDIGON, SpeciesId.DEINO, SpeciesId.HISUI_ZORUA], }), [TrainerType.ZINZOLIN]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("plasma_sage", "plasma_zinzolin") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2437,7 +2425,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.COLRESS]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("plasma_boss", "plasma_colress") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_colress") @@ -2494,7 +2481,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.GOOMY, SpeciesId.HONEDGE], }), [TrainerType.BRYONY]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("flare_admin_female", "flare") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2510,7 +2496,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.XEROSIC]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("flare_admin", "flare_xerosic", PokemonType.FIRE) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2578,7 +2563,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.PORYGON, SpeciesId.JANGMO_O], }), [TrainerType.FABA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("aether_admin", "aether") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2642,7 +2626,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.PAWNIARD, SpeciesId.GRUBBIN], }), [TrainerType.PLUMERIA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("skull_admin", "skull") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2704,7 +2687,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.DURALUDON, SpeciesId.DREEPY], }), [TrainerType.OLEANA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("macro_admin", "macro_cosmos") .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2781,7 +2763,6 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.SUPER_RARE]: [SpeciesId.DONDOZO, SpeciesId.GIMMIGHOUL], }), [TrainerType.GIACOMO]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("star_admin", "star_dark", PokemonType.DARK) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2809,7 +2790,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.MELA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("star_admin", "star_fire", PokemonType.FIRE) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2837,7 +2817,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.ATTICUS]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("star_admin", "star_poison", PokemonType.POISON) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2865,7 +2844,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.ORTEGA]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("star_admin", "star_fairy", PokemonType.FAIRY) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -2893,7 +2871,6 @@ export const trainerConfigs: TrainerConfigs = { }), ), [TrainerType.ERI]: new TrainerConfig(++t) - .setMoneyMultiplier(1.5) .initForEvilTeamAdmin("star_admin", "star_fighting", PokemonType.FIGHTING) .setEncounterBgm(TrainerType.PLASMA_GRUNT) .setBattleBgm("battle_plasma_grunt") @@ -4335,6 +4312,8 @@ export const trainerConfigs: TrainerConfigs = { getRandomPartyMemberFunc([SpeciesId.TOGEKISS], TrainerSlot.TRAINER, true, p => { p.abilityIndex = 1; // Serene Grace p.generateAndPopulateMoveset(); + p.moveset[0] = new PokemonMove(MoveId.DAZZLING_GLEAM); + p.moveset[1] = new PokemonMove(MoveId.AIR_SLASH); p.teraType = p.species.type1; }), ) @@ -4351,9 +4330,13 @@ export const trainerConfigs: TrainerConfigs = { getRandomPartyMemberFunc([SpeciesId.GARCHOMP], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.formIndex = 1; // Mega Garchomp - p.generateAndPopulateMoveset(); p.generateName(); p.gender = Gender.FEMALE; + p.generateAndPopulateMoveset(); + if (!p.moveset.some(move => move != null && move.moveId === MoveId.SANDSTORM)) { + // Check if Sandstorm is in the moveset, if not, replace the fourth move with Sandstorm. + p.moveset[3] = new PokemonMove(MoveId.SANDSTORM); + } }), ) .setInstantTera(2), // Tera Fairy Togekiss @@ -4509,16 +4492,10 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 2, - getRandomPartyMemberFunc( - [SpeciesId.TORNADUS, SpeciesId.THUNDURUS, SpeciesId.LANDORUS], - TrainerSlot.TRAINER, - true, - p => { - p.formIndex = 1; // Therian Formes - p.generateAndPopulateMoveset(); - p.pokeball = PokeballType.ROGUE_BALL; - }, - ), + getRandomPartyMemberFunc([SpeciesId.SNORLAX], TrainerSlot.TRAINER, true, p => { + p.formIndex = 1; // G-Max + p.generateAndPopulateMoveset(); + }), ) .setPartyMemberFunc( 3, @@ -4530,9 +4507,9 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 4, - getRandomPartyMemberFunc([SpeciesId.SNORLAX], TrainerSlot.TRAINER, true, p => { - p.formIndex = 1; // G-Max Snorlax + getRandomPartyMemberFunc([SpeciesId.LUNALA], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); + p.pokeball = PokeballType.MASTER_BALL; }), ) .setPartyMemberFunc( @@ -5472,9 +5449,12 @@ export const trainerConfigs: TrainerConfigs = { 5, getRandomPartyMemberFunc([SpeciesId.NECROZMA], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); - p.formIndex = 2; // Dawn Wings - p.generateAndPopulateMoveset(); p.pokeball = PokeballType.MASTER_BALL; + p.generateAndPopulateMoveset(); + if (!p.moveset.some(move => move != null && move.moveId === MoveId.PHOTON_GEYSER)) { + // Check if Photon Geyser is in the moveset, if not, replace the first move with Photon Geyser. + p.moveset[0] = new PokemonMove(MoveId.PHOTON_GEYSER); + } }), ), [TrainerType.GUZMA]: new TrainerConfig(++t) @@ -5493,9 +5473,8 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); }), ) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([SpeciesId.HERACROSS])) .setPartyMemberFunc( - 2, + 1, getRandomPartyMemberFunc([SpeciesId.SCIZOR, SpeciesId.KLEAVOR], TrainerSlot.TRAINER, true, p => { if (p.species.speciesId === SpeciesId.SCIZOR) { p.abilityIndex = 1; // Technician @@ -5505,6 +5484,7 @@ export const trainerConfigs: TrainerConfigs = { p.generateAndPopulateMoveset(); }), ) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([SpeciesId.HERACROSS])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([SpeciesId.GALVANTULA, SpeciesId.VIKAVOLT])) .setPartyMemberFunc( 4, @@ -5519,11 +5499,11 @@ export const trainerConfigs: TrainerConfigs = { 5, getRandomPartyMemberFunc([SpeciesId.GOLISOPOD], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); + p.gender = Gender.MALE; p.generateAndPopulateMoveset(); if (!p.moveset.some(move => move != null && move.moveId === MoveId.FIRST_IMPRESSION)) { // Check if First Impression is in the moveset, if not, replace the third move with First Impression. p.moveset[2] = new PokemonMove(MoveId.FIRST_IMPRESSION); - p.gender = Gender.MALE; } }), ), @@ -5536,27 +5516,40 @@ export const trainerConfigs: TrainerConfigs = { 0, getRandomPartyMemberFunc([SpeciesId.GOLISOPOD], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); + p.abilityIndex = 2; // Anticipation + p.gender = Gender.MALE; + p.level = 153; // Made higher level due to being an ace p.generateAndPopulateMoveset(); if (!p.moveset.some(move => move != null && move.moveId === MoveId.FIRST_IMPRESSION)) { // Check if First Impression is in the moveset, if not, replace the third move with First Impression. p.moveset[2] = new PokemonMove(MoveId.FIRST_IMPRESSION); - p.abilityIndex = 2; // Anticipation - p.gender = Gender.MALE; } }), ) .setPartyMemberFunc( 1, - getRandomPartyMemberFunc([SpeciesId.BUZZWOLE], TrainerSlot.TRAINER, true, p => { + getRandomPartyMemberFunc([SpeciesId.SCIZOR, SpeciesId.KLEAVOR], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); - p.pokeball = PokeballType.ROGUE_BALL; + if (p.species.speciesId === SpeciesId.SCIZOR) { + p.abilityIndex = 1; // Technician + p.moveset[0] = new PokemonMove(MoveId.BUG_BITE); + p.moveset[1] = new PokemonMove(MoveId.BULLET_PUNCH); + } else if (p.species.speciesId === SpeciesId.KLEAVOR) { + p.abilityIndex = 2; // Sharpness + p.moveset[0] = new PokemonMove(MoveId.X_SCISSOR); + p.moveset[1] = new PokemonMove(MoveId.STONE_AXE); + } }), ) .setPartyMemberFunc( 2, - getRandomPartyMemberFunc([SpeciesId.CRAWDAUNT, SpeciesId.HISUI_SAMUROTT], TrainerSlot.TRAINER, true, p => { - p.abilityIndex = 2; // Sharpness Hisuian Samurott, Adaptability Crawdaunt + getRandomPartyMemberFunc([SpeciesId.TOXAPEX], TrainerSlot.TRAINER, true, p => { + p.abilityIndex = 2; // Regenerator p.generateAndPopulateMoveset(); + if (!p.moveset.some(move => move != null && move.moveId === MoveId.BANEFUL_BUNKER)) { + // Check if Baneful Bunker is in the moveset, if not, replace the fourth move with Baneful Bunker. + p.moveset[3] = new PokemonMove(MoveId.BANEFUL_BUNKER); + } }), ) .setPartyMemberFunc( @@ -5568,15 +5561,11 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 4, - getRandomPartyMemberFunc([SpeciesId.GENESECT], TrainerSlot.TRAINER, true, p => { + getRandomPartyMemberFunc([SpeciesId.BUZZWOLE], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); - p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ROGUE_BALL; - p.formIndex = randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive - if (!p.moveset.some(move => move != null && move.moveId === MoveId.TECHNO_BLAST)) { - // Check if Techno Blast is in the moveset, if not, replace the third move with Techno Blast. - p.moveset[2] = new PokemonMove(MoveId.TECHNO_BLAST); - } + p.level = 150; // Made lower level due to not being an ace + p.generateAndPopulateMoveset(); }), ) .setPartyMemberFunc( From 604b0f3146f8662b9444fd10e8dd71d209af5877 Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:55:44 -0800 Subject: [PATCH 07/18] [Hotfix] Fix Pollen Puff crash and incorrect failure (#6752) Fix Pollen Puff crash and incorrect failure Co-authored-by: damocleas --- src/data/moves/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 8637c65966b..6e29f6b0ac9 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -10700,7 +10700,7 @@ export function initMoves() { .attr(HealOnAllyAttr, 0.5, true, false) .ballBombMove() // Fail if used against an ally that is affected by heal block, during the second failure check - .condition((user, target) => target.isOpponent(user) || !!target.getTag(BattlerTagType.HEAL_BLOCK), 2), + .condition((user, target) => target == null || target.isOpponent(user) || !target.getTag(BattlerTagType.HEAL_BLOCK), 2), new AttackMove(MoveId.ANCHOR_SHOT, PokemonType.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, 100, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true), new StatusMove(MoveId.PSYCHIC_TERRAIN, PokemonType.PSYCHIC, -1, 10, -1, 0, 7) From fbba23714245cbeadf537ecaa5b5cd0f8621a4ed Mon Sep 17 00:00:00 2001 From: damocleas Date: Tue, 4 Nov 2025 19:34:53 -0500 Subject: [PATCH 08/18] update submodules again --- assets | 2 +- locales | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 03b11be3b4a..36ef8a8fb64 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 03b11be3b4a46f1738874e5ab37336abf0158ac1 +Subproject commit 36ef8a8fb64f4aed0a409bc578e0b87b0f012ddc diff --git a/locales b/locales index 00c44a11c19..3f7cda56c99 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 00c44a11c1987b07477502591341bdea6a1c1c5e +Subproject commit 3f7cda56c99710fac946779674de7252a739d41c From 5c132f290abcbac60d006f476e82714fc2f17cfe Mon Sep 17 00:00:00 2001 From: Fabi <192151969+fabske0@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:04:24 +0100 Subject: [PATCH 09/18] [i18n] Fix freeze healByMove message (#6754) --- src/phases/move-phase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 5115e3da595..d113451f0a6 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -205,7 +205,7 @@ export class MovePhase extends PokemonPhase { user.cureStatus( StatusEffect.FREEZE, i18next.t("statusEffect:freeze.healByMove", { - pokemonName: getPokemonNameWithAffix(user), + pokemonNameWithAffix: getPokemonNameWithAffix(user), moveName: this.move.getMove().name, }), ); From 74783b120008c7cf01510bae64046d0033831acc Mon Sep 17 00:00:00 2001 From: Fabi <192151969+fabske0@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:42:33 +0100 Subject: [PATCH 10/18] [i18n] Fix mist message (#6757) fix mist message --- src/data/arena-tag.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index b35029af4a1..763ac3621b9 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -319,7 +319,11 @@ export class MistTag extends SerializableArenaTag { cancelled.value = true; if (!simulated) { - globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mistApply")); + globalScene.phaseManager.queueMessage( + i18next.t("arenaTag:mistApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(this.getSourcePokemon()), + }), + ); } return true; From c7b563e498843e361beccf6b657a34b8274137f7 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:08:11 -0600 Subject: [PATCH 11/18] [Balance Hotfix] Slight Alterations to PR#6747 (Trainers) (#6755) Update trainer-config.ts Co-authored-by: damocleas --- src/data/trainers/trainer-config.ts | 56 +++++++++++++---------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 2cdcaf1552f..44ff3b8092b 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -5475,13 +5475,10 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 1, - getRandomPartyMemberFunc([SpeciesId.SCIZOR, SpeciesId.KLEAVOR], TrainerSlot.TRAINER, true, p => { - if (p.species.speciesId === SpeciesId.SCIZOR) { - p.abilityIndex = 1; // Technician - } else if (p.species.speciesId === SpeciesId.KLEAVOR) { - p.abilityIndex = 2; // Sharpness - } + getRandomPartyMemberFunc([SpeciesId.SCIZOR], TrainerSlot.TRAINER, true, p => { + p.abilityIndex = 1; // Technician p.generateAndPopulateMoveset(); + p.gender = Gender.MALE; }), ) .setPartyMemberFunc(2, getRandomPartyMemberFunc([SpeciesId.HERACROSS])) @@ -5492,6 +5489,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; // Mega Pinsir p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.MALE; p.generateName(); }), ) @@ -5518,7 +5516,6 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.abilityIndex = 2; // Anticipation p.gender = Gender.MALE; - p.level = 153; // Made higher level due to being an ace p.generateAndPopulateMoveset(); if (!p.moveset.some(move => move != null && move.moveId === MoveId.FIRST_IMPRESSION)) { // Check if First Impression is in the moveset, if not, replace the third move with First Impression. @@ -5528,27 +5525,24 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 1, - getRandomPartyMemberFunc([SpeciesId.SCIZOR, SpeciesId.KLEAVOR], TrainerSlot.TRAINER, true, p => { + getRandomPartyMemberFunc([SpeciesId.SCIZOR], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); - if (p.species.speciesId === SpeciesId.SCIZOR) { - p.abilityIndex = 1; // Technician - p.moveset[0] = new PokemonMove(MoveId.BUG_BITE); - p.moveset[1] = new PokemonMove(MoveId.BULLET_PUNCH); - } else if (p.species.speciesId === SpeciesId.KLEAVOR) { - p.abilityIndex = 2; // Sharpness - p.moveset[0] = new PokemonMove(MoveId.X_SCISSOR); - p.moveset[1] = new PokemonMove(MoveId.STONE_AXE); - } + p.abilityIndex = 1; // Technician + p.moveset[0] = new PokemonMove(MoveId.BUG_BITE); + p.moveset[1] = new PokemonMove(MoveId.BULLET_PUNCH); + p.gender = Gender.MALE; + p.pokeball = PokeballType.ULTRA_BALL; }), ) .setPartyMemberFunc( 2, - getRandomPartyMemberFunc([SpeciesId.TOXAPEX], TrainerSlot.TRAINER, true, p => { - p.abilityIndex = 2; // Regenerator + getRandomPartyMemberFunc([SpeciesId.CRAWDAUNT, SpeciesId.HISUI_SAMUROTT], TrainerSlot.TRAINER, true, p => { + p.abilityIndex = 2; // Adaptability Crawdaunt, Sharpness Samurott + p.pokeball = PokeballType.ULTRA_BALL; p.generateAndPopulateMoveset(); - if (!p.moveset.some(move => move != null && move.moveId === MoveId.BANEFUL_BUNKER)) { - // Check if Baneful Bunker is in the moveset, if not, replace the fourth move with Baneful Bunker. - p.moveset[3] = new PokemonMove(MoveId.BANEFUL_BUNKER); + if (!p.moveset.some(move => move != null && move.moveId === MoveId.AQUA_JET)) { + // Check if Aqua Jet is in the moveset, if not, replace the third move with Aqua Jet. + p.moveset[2] = new PokemonMove(MoveId.AQUA_JET); } }), ) @@ -5561,22 +5555,22 @@ export const trainerConfigs: TrainerConfigs = { ) .setPartyMemberFunc( 4, - getRandomPartyMemberFunc([SpeciesId.BUZZWOLE], TrainerSlot.TRAINER, true, p => { - p.setBoss(true, 2); - p.pokeball = PokeballType.ROGUE_BALL; - p.level = 150; // Made lower level due to not being an ace - p.generateAndPopulateMoveset(); - }), - ) - .setPartyMemberFunc( - 5, getRandomPartyMemberFunc([SpeciesId.PINSIR], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.formIndex = 1; // Mega Pinsir p.generateAndPopulateMoveset(); p.generateName(); + p.gender = Gender.MALE; p.pokeball = PokeballType.ULTRA_BALL; }), + ) + .setPartyMemberFunc( + 5, + getRandomPartyMemberFunc([SpeciesId.BUZZWOLE], TrainerSlot.TRAINER, true, p => { + p.setBoss(true, 2); + p.generateAndPopulateMoveset(); + p.pokeball = PokeballType.ROGUE_BALL; + }), ), [TrainerType.ROSE]: new TrainerConfig(++t) .setName("Rose") From e1aded9504ab1642fb1135cb2e4d637b737315de Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:03:32 -0500 Subject: [PATCH 12/18] [Bug] Fix Parental Bond reducing damage of spread moves on 2nd pokemon https://github.com/pagefaultgames/pokerogue/pull/6743 * Fix Pollen Puff interaction with Parental Bond --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/abilities/ability.ts | 59 +++++++++++----------------- src/data/moves/move.ts | 50 +++++++++++++---------- src/field/pokemon.ts | 12 +----- src/phases/move-effect-phase.ts | 2 +- test/abilities/parental-bond.test.ts | 20 ++++++++++ test/items/multi-lens.test.ts | 54 +++++++------------------ 6 files changed, 90 insertions(+), 107 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index cd18bbcfb9c..21a00e53aed 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1833,13 +1833,13 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { } /** - * Parameters for abilities that modify the hit count and damage of a move + * Parameters for abilities that modify the hit count of a move. */ export interface AddSecondStrikeAbAttrParams extends Omit { - /** Holder for the number of hits. May be modified by ability application */ - hitCount?: NumberHolder; - /** Holder for the damage multiplier _of the current hit_ */ - multiplier?: NumberHolder; + /** Holder for the number of hits. Modified by ability application */ + hitCount: NumberHolder; + /** The Pokemon on the other side of this interaction */ + opponent: Pokemon | undefined; } /** @@ -1847,35 +1847,12 @@ export interface AddSecondStrikeAbAttrParams extends Omit (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5) + .attr(MoveDamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5) .build(), new AbBuilder(AbilityId.FILTER, 4) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75) @@ -7636,7 +7615,15 @@ export function initAbilities() { .attr(MoveTypeChangeAbAttr, PokemonType.FLYING, 1.2, (_user, _target, move) => move.type === PokemonType.NORMAL) .build(), new AbBuilder(AbilityId.PARENTAL_BOND, 6) - .attr(AddSecondStrikeAbAttr, 0.25) + .attr(AddSecondStrikeAbAttr) + // Only multiply damage on the last strike of multi-strike moves + .attr(MoveDamageBoostAbAttr, 0.25, (user, target, move) => ( + !!user + && user.turnData.hitCount > 1 // move was originally multi hit + && user.turnData.hitsLeft === 1 // move is on its final strike + && move.canBeMultiStrikeEnhanced(user, true, target) + ) + ) .build(), new AbBuilder(AbilityId.DARK_AURA, 6) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonDarkAura", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 6e29f6b0ac9..382ee0ce68e 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -100,7 +100,6 @@ import i18next from "i18next"; import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier"; import { inSpeedOrder } from "#utils/speed-order-generator"; import { canSpeciesTera, willTerastallize } from "#utils/pokemon-utils"; -import type { ReadonlyGenericUint8Array } from "#types/typed-arrays"; import { MovePriorityInBracket } from "#enums/move-priority-in-bracket"; /** @@ -1117,20 +1116,34 @@ export abstract class Move implements Localizable { } /** - * Returns `true` if this move can be given additional strikes - * by enhancing effects. + * Check whether this Move can be given additional strikes from enhancing effects. * Currently used for {@link https://bulbapedia.bulbagarden.net/wiki/Parental_Bond_(Ability) | Parental Bond} - * and {@linkcode PokemonMultiHitModifier | Multi-Lens}. - * @param user The {@linkcode Pokemon} using the move - * @param restrictSpread `true` if the enhancing effect - * should not affect multi-target moves (default `false`) + * and {@linkcode PokemonMultiHitModifier | Multi Lens}. + * @param user - The {@linkcode Pokemon} using the move + * @param restrictSpread - Whether the enhancing effect should ignore multi-target moves; default `false` + * @returns Whether this Move can be given additional strikes. */ - canBeMultiStrikeEnhanced(user: Pokemon, restrictSpread: boolean = false): boolean { + // TODO: Remove target parameter used solely to circumvent Pollen Puff shenanigans - the entire move needs to be fixed anyhow + public canBeMultiStrikeEnhanced(user: Pokemon, restrictSpread: boolean = false, target?: Pokemon | null): boolean { // Multi-strike enhancers... - // ...cannot enhance moves that hit multiple targets + // ...cannot enhance charging or 2-turn moves + if (this.isChargingMove()) { + return false; + } + + // ...cannot enhance moves hitting multiple targets unless specified const { targets, multiple } = getMoveTargets(user, this.id); - const isMultiTarget = multiple && targets.length > 1; + if (restrictSpread && multiple && targets.length > 1) { + return false; + }; + + // ...cannot enhance status moves, including ally-targeting Pollen Puff + if ( + this.category === MoveCategory.STATUS + || (target != null && user.getMoveCategory(target, this) === MoveCategory.STATUS)) { + return false; + } // ...cannot enhance multi-hit or sacrificial moves const exceptAttrs: MoveAttrString[] = [ @@ -1138,6 +1151,9 @@ export abstract class Move implements Localizable { "SacrificialAttr", "SacrificialAttrOnHit" ]; + if (exceptAttrs.some(attr => this.hasAttr(attr))) { + return false; + } // ...and cannot enhance these specific moves const exceptMoves: MoveId[] = [ @@ -1147,17 +1163,11 @@ export abstract class Move implements Localizable { MoveId.ICE_BALL, MoveId.ENDEAVOR ]; + if (exceptMoves.includes(this.id)) { + return false; + } - // ...and cannot enhance Pollen Puff when targeting an ally. - const ally = user.getAlly(); - const exceptPollenPuffAlly: boolean = this.id === MoveId.POLLEN_PUFF && ally != null && targets.includes(ally.getBattlerIndex()) - - return (!restrictSpread || !isMultiTarget) - && !this.isChargingMove() - && !exceptAttrs.some(attr => this.hasAttr(attr)) - && !exceptMoves.some(id => this.id === id) - && !exceptPollenPuffAlly - && this.category !== MoveCategory.STATUS; + return true; } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 1d58f7de883..9f6b3b099e9 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3672,15 +3672,6 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { multiStrikeEnhancementMultiplier, ); - if (!ignoreSourceAbility) { - applyAbAttrs("AddSecondStrikeAbAttr", { - pokemon: source, - move, - simulated, - multiplier: multiStrikeEnhancementMultiplier, - }); - } - /** Doubles damage if this Pokemon's last move was Glaive Rush */ const glaiveRushMultiplier = new NumberHolder(1); if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) { @@ -3769,9 +3760,8 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * mistyTerrainMultiplier, ); - /** Doubles damage if the attacker has Tinted Lens and is using a resisted move */ if (!ignoreSourceAbility) { - applyAbAttrs("DamageBoostAbAttr", { + applyAbAttrs("MoveDamageBoostAbAttr", { pokemon: source, opponent: this, move, diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 476f0de4a36..3209298a265 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -276,7 +276,7 @@ export class MoveEffectPhase extends PokemonPhase { // Assume single target for multi hit applyMoveAttrs("MultiHitAttr", user, this.getFirstTarget() ?? null, move, hitCount); // If Parental Bond is applicable, add another hit - applyAbAttrs("AddSecondStrikeAbAttr", { pokemon: user, move, hitCount }); + applyAbAttrs("AddSecondStrikeAbAttr", { pokemon: user, move, hitCount, opponent: this.getFirstTarget() }); // If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount); // Set the user's relevant turnData fields to reflect the final hit count diff --git a/test/abilities/parental-bond.test.ts b/test/abilities/parental-bond.test.ts index a72fc82260f..95f0e8d4159 100644 --- a/test/abilities/parental-bond.test.ts +++ b/test/abilities/parental-bond.test.ts @@ -384,4 +384,24 @@ describe("Abilities - Parental Bond", () => { // TODO: Update hit count to 1 once Future Sight is fixed to not activate abilities if user is off the field expect(enemyPokemon.damageAndUpdate).toHaveBeenCalledTimes(2); }); + + it("should not reduce damage against the remaining target if the first one faints", async () => { + game.override.battleStyle("double").enemySpecies(SpeciesId.MAGIKARP); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + const feebas = game.field.getPlayerPokemon(); + const [karp1, karp2] = game.scene.getEnemyField(); + + // Mock base damage for both mons for consistent results + vi.spyOn(karp1, "getBaseDamage").mockReturnValue(100); + vi.spyOn(karp2, "getBaseDamage").mockReturnValue(100); + karp1.hp = 1; + + game.move.use(MoveId.HYPER_VOICE); + await game.toEndOfTurn(); + + expect(karp1).toHaveFainted(); + expect(feebas).not.toHaveAbilityApplied(AbilityId.PARENTAL_BOND); + expect(karp2).toHaveTakenDamage(100); + }); }); diff --git a/test/items/multi-lens.test.ts b/test/items/multi-lens.test.ts index 3686aff0fcf..bdf93a4ae12 100644 --- a/test/items/multi-lens.test.ts +++ b/test/items/multi-lens.test.ts @@ -26,6 +26,7 @@ describe("Items - Multi Lens", () => { game.override .moveset([MoveId.TACKLE, MoveId.TRAILBLAZE, MoveId.TACHYON_CUTTER, MoveId.FUTURE_SIGHT]) .ability(AbilityId.BALL_FETCH) + .passiveAbility(AbilityId.NO_GUARD) .startingHeldItems([{ name: "MULTI_LENS" }]) .battleStyle("single") .criticalHits(false) @@ -135,61 +136,36 @@ describe("Items - Multi Lens", () => { expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25)); }); - it("should result in correct damage for hp% attacks with 1 lens", async () => { + it.each([1, 2])("should result in original damage for HP-cutting attacks with %d lenses", async lensCount => { game.override - .startingHeldItems([{ name: "MULTI_LENS", count: 1 }]) - .moveset(MoveId.SUPER_FANG) - .ability(AbilityId.COMPOUND_EYES) + .startingHeldItems([{ name: "MULTI_LENS", count: lensCount }]) .enemyLevel(1000) .enemySpecies(SpeciesId.BLISSEY); // allows for unrealistically high levels of accuracy + await game.classicMode.startBattle([SpeciesId.FEEBAS]); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + const blissey = game.field.getEnemyPokemon(); - const enemyPokemon = game.field.getEnemyPokemon(); + game.move.use(MoveId.SUPER_FANG); + await game.toEndOfTurn(); - game.move.select(MoveId.SUPER_FANG); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("MoveEndPhase"); - expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5); + expect(blissey.getHpRatio()).toBeCloseTo(0.5, 5); }); - it("should result in correct damage for hp% attacks with 2 lenses", async () => { + it("should result in original damage for HP-cutting attacks with 2 lenses + Parental Bond", async () => { game.override .startingHeldItems([{ name: "MULTI_LENS", count: 2 }]) - .moveset(MoveId.SUPER_FANG) - .ability(AbilityId.COMPOUND_EYES) - .enemyMoveset(MoveId.SPLASH) - .enemyLevel(1000) - .enemySpecies(SpeciesId.BLISSEY); // allows for unrealistically high levels of accuracy - - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - - const enemyPokemon = game.field.getEnemyPokemon(); - - game.move.select(MoveId.SUPER_FANG); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("MoveEndPhase"); - expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.5, 5); - }); - - it("should result in correct damage for hp% attacks with 2 lenses + Parental Bond", async () => { - game.override - .startingHeldItems([{ name: "MULTI_LENS", count: 2 }]) - .moveset(MoveId.SUPER_FANG) .ability(AbilityId.PARENTAL_BOND) - .passiveAbility(AbilityId.COMPOUND_EYES) - .enemyMoveset(MoveId.SPLASH) .enemyLevel(1000) .enemySpecies(SpeciesId.BLISSEY); // allows for unrealistically high levels of accuracy - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemyPokemon = game.field.getEnemyPokemon(); + const blissey = game.field.getEnemyPokemon(); - game.move.select(MoveId.SUPER_FANG); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("MoveEndPhase"); - expect(enemyPokemon.getHpRatio()).toBeCloseTo(0.25, 5); + game.move.use(MoveId.SUPER_FANG); + await game.toEndOfTurn(); + + expect(blissey.getHpRatio()).toBeCloseTo(0.25, 5); }); it("should not allow Future Sight to hit infinitely many times if the user switches out", async () => { From e2f7cd91182daee010bede32ac45270b10b9a324 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 6 Nov 2025 18:11:04 -0800 Subject: [PATCH 13/18] Update submodules --- assets | 2 +- locales | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets b/assets index 36ef8a8fb64..9d391bd666f 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 36ef8a8fb64f4aed0a409bc578e0b87b0f012ddc +Subproject commit 9d391bd666f339c31db3d48a9907139950c14d1e diff --git a/locales b/locales index 3f7cda56c99..41f614f9fde 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 3f7cda56c99710fac946779674de7252a739d41c +Subproject commit 41f614f9fde9a883e586c8d55e7e708f8b0a4ee6 From a0aa25ab75f52516864ee6b925ad893c8debf747 Mon Sep 17 00:00:00 2001 From: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> Date: Thu, 6 Nov 2025 22:25:20 -0600 Subject: [PATCH 14/18] [P3 Bug] Fix Struggle's message not displaying properly (#6760) --- src/data/moves/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 382ee0ce68e..36e88bf5f41 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -9043,7 +9043,7 @@ export function initMoves() { new AttackMove(MoveId.STRUGGLE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1) .attr(RecoilAttr, true, 0.25, true) .attr(TypelessAttr) - .attr(PreMoveMessageAttr, (user: Pokemon) => i18next.t("moveTriggers:struggleMessage", { pokemonName: getPokemonNameWithAffix(user) })) + .attr(PreMoveMessageAttr, (user: Pokemon) => i18next.t("moveTriggers:struggle", { pokemonName: getPokemonNameWithAffix(user) })) .target(MoveTarget.RANDOM_NEAR_ENEMY), new StatusMove(MoveId.SKETCH, PokemonType.NORMAL, -1, 1, -1, 0, 2) .ignoresSubstitute() From d8d5c12bbc575778907cadf7bc063c284afaa594 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Thu, 6 Nov 2025 23:27:07 -0500 Subject: [PATCH 15/18] [Bug] Fix 2-turn/charging moves breaking on lack of targets (#6759) --- src/phases/move-phase.ts | 3 +++ test/moves/dig.test.ts | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index d113451f0a6..48fc5f6f960 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -509,6 +509,9 @@ export class MovePhase extends PokemonPhase { ) { this.showFailedText(); this.fail(); + // clear out 2 turn moves + // TODO: Make a helper for this atp + this.pokemon.getMoveQueue().shift(); this.pokemon.pushMoveHistory(this.moveHistoryEntry); return true; } diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index 28cbf2882a6..4e87fb3204e 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -131,4 +131,26 @@ describe("Moves - Dig", () => { expect(postDigEarthquakeDmg).toBeGreaterThanOrEqual(2 * preDigEarthquakeDmg); expect(postDigEarthquakeDmg).toBeLessThan(2 * (preDigEarthquakeDmg + 1)); }); + + it("should not softlock when used against a dying enemy 2 in Doubles", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + + const feebas = game.field.getPlayerPokemon(); + const enemy2 = game.scene.getEnemyField()[1]; + + // use dig and make the targeted enemy faint post charge + game.move.use(MoveId.DIG, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2); + await game.toEndOfTurn(); + await game.killPokemon(enemy2); + await game.phaseInterceptor.to("CommandPhase"); + + expect(feebas.getMoveQueue()[0]?.targets).toEqual([BattlerIndex.ENEMY_2]); + expect(enemy2).toHaveFainted(); + + await game.toEndOfTurn(); + + // TODO: Does this redirect to the other enemy? + expect(feebas.getMoveQueue()).toHaveLength(0); + }); }); From 022c372c122221552558d326386e913c349eb768 Mon Sep 17 00:00:00 2001 From: damocleas Date: Fri, 7 Nov 2025 20:10:35 -0500 Subject: [PATCH 16/18] Update locales --- locales | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales b/locales index 41f614f9fde..b5b0d94eee7 160000 --- a/locales +++ b/locales @@ -1 +1 @@ -Subproject commit 41f614f9fde9a883e586c8d55e7e708f8b0a4ee6 +Subproject commit b5b0d94eee7cbcf0e055f8074ca1ebedb920e59e From b47106a1dc08bb7c292b7d227f623c9e90da2bf3 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 7 Nov 2025 17:20:00 -0800 Subject: [PATCH 17/18] Update version to 1.11.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e08e5a393a4..588177e86ab 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.11.2", + "version": "1.11.3", "type": "module", "scripts": { "start:prod": "vite --mode production", From 5efcc55aa09431de062d18c94673a014164696a0 Mon Sep 17 00:00:00 2001 From: damocleas Date: Fri, 7 Nov 2025 20:23:08 -0500 Subject: [PATCH 18/18] extendo --- src/timed-event-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 32a49f0a704..efc05cd78d3 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -396,7 +396,7 @@ const timedEvents: readonly TimedEvent[] = [ name: "Halloween 25", eventType: EventType.SHINY, startDate: new Date(Date.UTC(2025, 9, 30)), - endDate: new Date(Date.UTC(2025, 10, 10)), + endDate: new Date(Date.UTC(2025, 10, 12)), bannerKey: "halloween2025", scale: 0.19, availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "es-419", "pt-BR", "zh-Hans", "zh-Hant", "da", "ru"],