From a48952e354e69a341f12982cbf46dd6ca05debbf Mon Sep 17 00:00:00 2001 From: damocleas Date: Thu, 30 Oct 2025 21:08:00 -0400 Subject: [PATCH 1/8] [Balance] [Bug] Fix Rhydon not being a Rhyhorn in Rival 2 --- src/data/trainers/rival-party-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/trainers/rival-party-config.ts b/src/data/trainers/rival-party-config.ts index b5a8cb532b3..fa040ff66de 100644 --- a/src/data/trainers/rival-party-config.ts +++ b/src/data/trainers/rival-party-config.ts @@ -253,7 +253,7 @@ const SLOT_3_FIGHT_2 = [ SpeciesId.MACHOP, SpeciesId.GASTLY, SpeciesId.MAGNEMITE, - SpeciesId.RHYDON, + SpeciesId.RHYHORN, SpeciesId.TANGELA, SpeciesId.PORYGON, SpeciesId.ELEKID, From 9ea5a014a1d0f700fffcd7cc007a436b39d50762 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:14:31 -0700 Subject: [PATCH 2/8] [Bug] Allow fainted Pokemon to be released post-battle in hardcore https://github.com/pagefaultgames/pokerogue/pull/6723 --- src/ui/handlers/party-ui-handler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui/handlers/party-ui-handler.ts b/src/ui/handlers/party-ui-handler.ts index 7806a6111c1..6fbb4052aeb 100644 --- a/src/ui/handlers/party-ui-handler.ts +++ b/src/ui/handlers/party-ui-handler.ts @@ -1586,9 +1586,8 @@ export class PartyUiHandler extends MessageUiHandler { this.updateOptionsWithModifierTransferMode(pokemon); break; case PartyUiMode.SWITCH: - this.options.push(PartyOption.RELEASE); - break; case PartyUiMode.RELEASE: + case PartyUiMode.CHECK: this.options.push(PartyOption.RELEASE); break; } From 1d4a9259b83441c358a6ca591afb282a2e7c88ca Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:14:48 -0700 Subject: [PATCH 3/8] [Hotfix] Fix Queenly Majesty/Dazzling affecting user's moves instead of enemy's (#6722) Fix QM/Dazzling --- 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 6350791e9bb..5115e3da595 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -286,7 +286,7 @@ export class MovePhase extends PokemonPhase { // Apply queenly majesty / dazzling if (!failed) { - const defendingSidePlayField = user.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); + const defendingSidePlayField = user.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField(); const cancelled = new BooleanHolder(false); defendingSidePlayField.forEach((pokemon: Pokemon) => { applyAbAttrs("FieldPriorityMoveImmunityAbAttr", { From d7cfb2087cbcea8aeaaa003e8b074bf7dbfb1215 Mon Sep 17 00:00:00 2001 From: Wlowscha <54003515+Wlowscha@users.noreply.github.com> Date: Fri, 31 Oct 2025 04:21:26 +0100 Subject: [PATCH 4/8] [Bug][UI/UX] Make ribbon option in dex selectable with classic wins https://github.com/pagefaultgames/pokerogue/pull/6720 --- src/ui/handlers/pokedex-page-ui-handler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/handlers/pokedex-page-ui-handler.ts b/src/ui/handlers/pokedex-page-ui-handler.ts index fa10c88952c..c3be9f87d21 100644 --- a/src/ui/handlers/pokedex-page-ui-handler.ts +++ b/src/ui/handlers/pokedex-page-ui-handler.ts @@ -776,7 +776,8 @@ export class PokedexPageUiHandler extends MessageUiHandler { || (this.tmMoves.length === 0 && o === MenuOptions.TM_MOVES) || (!globalScene.gameData.dexData[this.species.speciesId].ribbons.getRibbons() && o === MenuOptions.RIBBONS - && !globalScene.showMissingRibbons); + && !globalScene.showMissingRibbons + && !globalScene.gameData.starterData[this.species.speciesId]?.classicWinCount); const color = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, false); const shadow = getTextColor(isDark ? TextStyle.SHADOW_TEXT : TextStyle.SETTINGS_VALUE, true); return `[shadow=${shadow}][color=${color}]${label}[/color][/shadow]`; @@ -1778,6 +1779,7 @@ export class PokedexPageUiHandler extends MessageUiHandler { } else if ( !globalScene.gameData.dexData[this.species.speciesId].ribbons.getRibbons() && !globalScene.showMissingRibbons + && !globalScene.gameData.starterData[this.species.speciesId]?.classicWinCount ) { ui.showText(i18next.t("pokedexUiHandler:noRibbons")); error = true; From 893333ff3bb0699ac3843dc41169c31e3dc08681 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:24:39 -0700 Subject: [PATCH 5/8] Update version to 1.11.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b23b9b9b75d..102c76830b1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.11.0", + "version": "1.11.1", "type": "module", "scripts": { "start:prod": "vite --mode production", From df98e506ad4fdc5fac1f70dec875072081e11f21 Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:38:49 -0700 Subject: [PATCH 6/8] [Bug] Prevent self speed ties (#6719) * Prevent self speed ties * Remove outdated parameter doc --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- .../post-summon-phase-priority-queue.ts | 2 +- src/utils/speed-order.ts | 44 +++++++++----- test/utils/speed-order.test.ts | 58 +++++++++++++++++++ 3 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 test/utils/speed-order.test.ts diff --git a/src/queues/post-summon-phase-priority-queue.ts b/src/queues/post-summon-phase-priority-queue.ts index 28e727de01b..fe08713e501 100644 --- a/src/queues/post-summon-phase-priority-queue.ts +++ b/src/queues/post-summon-phase-priority-queue.ts @@ -11,7 +11,7 @@ import { sortInSpeedOrder } from "#app/utils/speed-order"; */ export class PostSummonPhasePriorityQueue extends PokemonPhasePriorityQueue { protected override reorder(): void { - this.queue = sortInSpeedOrder(this.queue, false); + this.queue = sortInSpeedOrder(this.queue); this.queue.sort((phaseA, phaseB) => phaseB.getPriority() - phaseA.getPriority()); } diff --git a/src/utils/speed-order.ts b/src/utils/speed-order.ts index f2733a74998..aaf3a4526c7 100644 --- a/src/utils/speed-order.ts +++ b/src/utils/speed-order.ts @@ -12,15 +12,13 @@ interface hasPokemon { /** * Sorts an array of {@linkcode Pokemon} by speed, taking Trick Room into account. * @param pokemonList - The list of Pokemon or objects containing Pokemon - * @param shuffleFirst - Whether to shuffle the list before sorting (to handle speed ties). Default `true`. * @returns The sorted array of {@linkcode Pokemon} */ -export function sortInSpeedOrder(pokemonList: T[], shuffleFirst = true): T[] { - if (shuffleFirst) { - shufflePokemonList(pokemonList); - } - sortBySpeed(pokemonList); - return pokemonList; +export function sortInSpeedOrder(pokemonList: T[]): T[] { + const grouped = groupPokemon(pokemonList); + shufflePokemonList(grouped); + sortBySpeed(grouped); + return grouped.flat(); } /** @@ -28,7 +26,7 @@ export function sortInSpeedOrder(pokemonList: T[ * @param pokemonList - The array of Pokemon or objects containing Pokemon * @returns The same array instance that was passed in, shuffled. */ -function shufflePokemonList(pokemonList: T[]): T[] { +function shufflePokemonList(pokemonList: T[][]): void { // This is seeded with the current turn to prevent an inconsistency where it // was varying based on how long since you last reloaded globalScene.executeWithSeedOffset( @@ -36,7 +34,6 @@ function shufflePokemonList(pokemonList: T[]): T globalScene.currentBattle.turn * 1000 + pokemonList.length, globalScene.waveSeed, ); - return pokemonList; } /** Type guard for {@linkcode sortBySpeed} to avoid importing {@linkcode Pokemon} */ @@ -44,11 +41,15 @@ function isPokemon(p: Pokemon | hasPokemon): p is Pokemon { return typeof (p as hasPokemon).getPokemon !== "function"; } +function getPokemon(p: Pokemon | hasPokemon): Pokemon { + return isPokemon(p) ? p : p.getPokemon(); +} + /** Sorts an array of {@linkcode Pokemon} by speed (without shuffling) */ -function sortBySpeed(pokemonList: T[]): void { - pokemonList.sort((a, b) => { - const aSpeed = (isPokemon(a) ? a : a.getPokemon()).getEffectiveStat(Stat.SPD); - const bSpeed = (isPokemon(b) ? b : b.getPokemon()).getEffectiveStat(Stat.SPD); +function sortBySpeed(groupedPokemonList: T[][]): void { + groupedPokemonList.sort((a, b) => { + const aSpeed = getPokemon(a[0]).getEffectiveStat(Stat.SPD); + const bSpeed = getPokemon(b[0]).getEffectiveStat(Stat.SPD); return bSpeed - aSpeed; }); @@ -57,6 +58,21 @@ function sortBySpeed(pokemonList: T[]): void { const speedReversed = new BooleanHolder(false); globalScene.arena.applyTags(ArenaTagType.TRICK_ROOM, speedReversed); if (speedReversed.value) { - pokemonList.reverse(); + groupedPokemonList.reverse(); } } + +function groupPokemon(pokemonList: T[]): T[][] { + const runs: T[][] = []; + for (const pkmn of pokemonList) { + const pokemon = getPokemon(pkmn); + const lastGroup = runs.at(-1); + if (lastGroup != null && lastGroup.length > 0 && getPokemon(lastGroup[0]) === pokemon) { + lastGroup.push(pkmn); + } else { + runs.push([pkmn]); + } + } + + return runs; +} diff --git a/test/utils/speed-order.test.ts b/test/utils/speed-order.test.ts new file mode 100644 index 00000000000..e408c5823c4 --- /dev/null +++ b/test/utils/speed-order.test.ts @@ -0,0 +1,58 @@ +import { AbilityId } from "#enums/ability-id"; +import { MoveId } from "#enums/move-id"; +import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; +import { GameManager } from "#test/test-utils/game-manager"; +import { sortInSpeedOrder } from "#utils/speed-order"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Utils - Speed Order", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleStyle("single") + .startingLevel(100) + .enemyLevel(100) + .enemyMoveset(MoveId.SPLASH) + .enemyAbility(AbilityId.BALL_FETCH) + .ability(AbilityId.BALL_FETCH) + .enemySpecies(SpeciesId.REGIELEKI); + }); + + it("Sorts correctly in the basic case", async () => { + await game.classicMode.startBattle([SpeciesId.SLOWPOKE, SpeciesId.MEW]); + const [slowpoke, mew] = game.field.getPlayerParty(); + const regieleki = game.field.getEnemyPokemon(); + const pkmnList = [slowpoke, regieleki, mew]; + + expect(sortInSpeedOrder(pkmnList)).toEqual([regieleki, mew, slowpoke]); + }); + + it("Correctly sorts grouped pokemon", async () => { + await game.classicMode.startBattle([SpeciesId.SLOWPOKE, SpeciesId.MEW, SpeciesId.DITTO]); + const [slowpoke, mew, ditto] = game.field.getPlayerParty(); + const regieleki = game.field.getEnemyPokemon(); + ditto.stats[Stat.SPD] = slowpoke.getStat(Stat.SPD); + + const pkmnList = [slowpoke, slowpoke, ditto, ditto, mew, regieleki, regieleki]; + const sorted = sortInSpeedOrder(pkmnList); + + expect([ + [regieleki, regieleki, mew, slowpoke, slowpoke, ditto, ditto], + [regieleki, regieleki, mew, ditto, ditto, slowpoke, slowpoke], + ]).toContainEqual(sorted); + }); +}); From 76453a31d1ee6bd42f4ea37b93a88e7a008e5240 Mon Sep 17 00:00:00 2001 From: damocleas Date: Fri, 31 Oct 2025 00:47:07 -0400 Subject: [PATCH 7/8] [Balance][Bug] Rival Fight 3 Fix, Move Noibat in Biomes Electivire corrected to be Electabuzz in Slot 3 on Fight 3 (55) Noibat has been moved from Uncommon -> Rare in Grassy Field --- src/data/balance/biomes.ts | 8 ++++---- src/data/balance/init-biomes.ts | 2 +- src/data/trainers/rival-party-config.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index 562c07396e0..f81f76058ec 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -163,15 +163,15 @@ export const biomePokemonPools: BiomePokemonPools = { [BiomePoolTier.UNCOMMON]: { [TimeOfDay.DAWN]: [ SpeciesId.SUNKERN, SpeciesId.COMBEE ], [TimeOfDay.DAY]: [ SpeciesId.SUNKERN, SpeciesId.COMBEE ], - [TimeOfDay.DUSK]: [ SpeciesId.SEEDOT, SpeciesId.NOIBAT ], - [TimeOfDay.NIGHT]: [ SpeciesId.SEEDOT, SpeciesId.NOIBAT ], + [TimeOfDay.DUSK]: [ SpeciesId.SEEDOT ], + [TimeOfDay.NIGHT]: [ SpeciesId.SEEDOT ], [TimeOfDay.ALL]: [ SpeciesId.MILTANK, SpeciesId.CHERUBI, SpeciesId.FOONGUS, ] }, [BiomePoolTier.RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], - [TimeOfDay.DUSK]: [], - [TimeOfDay.NIGHT]: [], + [TimeOfDay.DUSK]: [ SpeciesId.NOIBAT ], + [TimeOfDay.NIGHT]: [ SpeciesId.NOIBAT ], [TimeOfDay.ALL]: [ SpeciesId.BULBASAUR, SpeciesId.GROWLITHE, SpeciesId.TURTWIG, SpeciesId.BONSLY ] }, [BiomePoolTier.SUPER_RARE]: { [TimeOfDay.DAWN]: [], [TimeOfDay.DAY]: [], [TimeOfDay.DUSK]: [], [TimeOfDay.NIGHT]: [], [TimeOfDay.ALL]: [] }, diff --git a/src/data/balance/init-biomes.ts b/src/data/balance/init-biomes.ts index 1b410e637e8..eb2d4151fbf 100644 --- a/src/data/balance/init-biomes.ts +++ b/src/data/balance/init-biomes.ts @@ -3547,7 +3547,7 @@ export function initBiomes() { ], [SpeciesId.NOIBAT, PokemonType.FLYING, PokemonType.DRAGON, [ [BiomeId.CAVE, BiomePoolTier.UNCOMMON], - [BiomeId.GRASS, BiomePoolTier.UNCOMMON, [TimeOfDay.DUSK, TimeOfDay.NIGHT]] + [BiomeId.GRASS, BiomePoolTier.RARE, [TimeOfDay.DUSK, TimeOfDay.NIGHT]] ] ], [SpeciesId.NOIVERN, PokemonType.FLYING, PokemonType.DRAGON, [ diff --git a/src/data/trainers/rival-party-config.ts b/src/data/trainers/rival-party-config.ts index fa040ff66de..67b3f50d379 100644 --- a/src/data/trainers/rival-party-config.ts +++ b/src/data/trainers/rival-party-config.ts @@ -298,7 +298,7 @@ const SLOT_3_FIGHT_3 = [ SpeciesId.RHYDON, SpeciesId.TANGROWTH, SpeciesId.PORYGON2, - SpeciesId.ELECTIVIRE, + SpeciesId.ELECTABUZZ, SpeciesId.MAGMAR, SpeciesId.AZUMARILL, SpeciesId.URSARING, From babeb32edb9a1e6906d04a2d1e2bd19151d737a8 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:49:37 -0700 Subject: [PATCH 8/8] [Bug] Shinies won't be forced to match event boss shiny if set (#6724) --- src/field/pokemon.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index be85e2ebe3a..6ce5c1ffc2c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -6388,12 +6388,14 @@ export class EnemyPokemon extends Pokemon { } const eventBossVariant = getDailyEventSeedBossVariant(globalScene.seed); - if (eventBossVariant != null && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { + const eventBossVariantEnabled = + eventBossVariant != null && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex); + if (eventBossVariantEnabled) { this.shiny = true; } if (this.shiny) { - this.variant = eventBossVariant ?? this.generateShinyVariant(); + this.variant = eventBossVariantEnabled ? eventBossVariant : this.generateShinyVariant(); if (Overrides.ENEMY_VARIANT_OVERRIDE !== null) { this.variant = Overrides.ENEMY_VARIANT_OVERRIDE; }