From 19af9bdb8b85a94facb56a1af3236d21aac48ebb Mon Sep 17 00:00:00 2001 From: "Amani H." <109637146+xsn34kzx@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:22:59 -0400 Subject: [PATCH] [Beta] [Bug] Fix Various Nuzlocke-related Issues (#6261) * [Bug] Fix Various Nuzlocke-related Issues * Update encounter-pokemon-utils.ts * Update attempt-capture-phase.ts --------- Co-authored-by: damocleas Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> --- .../encounters/dark-deal-encounter.ts | 1 + .../utils/encounter-pokemon-utils.ts | 18 +++-- src/phases/attempt-capture-phase.ts | 7 +- src/phases/select-biome-phase.ts | 26 +++++--- src/phases/victory-phase.ts | 66 +++++++------------ src/system/achv.ts | 2 + test/challenges/limited-catch.test.ts | 16 +++++ test/challenges/limited-support.test.ts | 2 + 8 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index d90e207cc9a..820c7823320 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -99,6 +99,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE MysteryEncounterType.DARK_DEAL, ) .withEncounterTier(MysteryEncounterTier.ROGUE) + .withDisallowedChallenges(Challenges.HARDCORE) .withIntroSpriteConfigs([ { spriteKey: "dark_deal_scientist", diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 7617fb5a89e..0c6a8e25452 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -673,6 +673,8 @@ export async function catchPokemon( globalScene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); return new Promise(resolve => { + const addStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); const doPokemonCatchMenu = () => { const end = () => { // Ensure the pokemon is in the enemy party in all situations @@ -708,9 +710,7 @@ export async function catchPokemon( }); }; Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => { - const addStatus = new BooleanHolder(true); - applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); - if (!addStatus.value) { + if (!(isObtain || addStatus.value)) { removePokemon(); end(); return; @@ -807,10 +807,16 @@ export async function catchPokemon( }; if (showCatchObtainMessage) { + let catchMessage: string; + if (isObtain) { + catchMessage = "battle:pokemonObtained"; + } else if (addStatus.value) { + catchMessage = "battle:pokemonCaught"; + } else { + catchMessage = "battle:pokemonCaughtButChallenge"; + } globalScene.ui.showText( - i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { - pokemonName: pokemon.getNameToRender(), - }), + i18next.t(catchMessage, { pokemonName: pokemon.getNameToRender() }), null, doPokemonCatchMenu, 0, diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index aea39cff294..b34ddb0c59a 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -253,8 +253,11 @@ export class AttemptCapturePhase extends PokemonPhase { globalScene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs); + const addStatus = new BooleanHolder(true); + applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); + globalScene.ui.showText( - i18next.t("battle:pokemonCaught", { + i18next.t(addStatus.value ? "battle:pokemonCaught" : "battle:pokemonCaughtButChallenge", { pokemonName: getPokemonNameWithAffix(pokemon), }), null, @@ -290,8 +293,6 @@ export class AttemptCapturePhase extends PokemonPhase { }); }; Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => { - const addStatus = new BooleanHolder(true); - applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus); if (!addStatus.value) { removePokemon(); end(); diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 4089f0c2852..d02d69fc934 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -16,8 +16,10 @@ export class SelectBiomePhase extends BattlePhase { globalScene.resetSeed(); + const gameMode = globalScene.gameMode; const currentBiome = globalScene.arena.biomeType; - const nextWaveIndex = globalScene.currentBattle.waveIndex + 1; + const currentWaveIndex = globalScene.currentBattle.waveIndex; + const nextWaveIndex = currentWaveIndex + 1; const setNextBiome = (nextBiome: BiomeId) => { if (nextWaveIndex % 10 === 1) { @@ -26,6 +28,15 @@ export class SelectBiomePhase extends BattlePhase { applyChallenges(ChallengeType.PARTY_HEAL, healStatus); if (healStatus.value) { globalScene.phaseManager.unshiftNew("PartyHealPhase", false); + } else { + globalScene.phaseManager.unshiftNew( + "SelectModifierPhase", + undefined, + undefined, + gameMode.isFixedBattle(currentWaveIndex) + ? gameMode.getFixedBattle(currentWaveIndex).customModifierRewardSettings + : undefined, + ); } } globalScene.phaseManager.unshiftNew("SwitchBiomePhase", nextBiome); @@ -33,12 +44,12 @@ export class SelectBiomePhase extends BattlePhase { }; if ( - (globalScene.gameMode.isClassic && globalScene.gameMode.isWaveFinal(nextWaveIndex + 9)) || - (globalScene.gameMode.isDaily && globalScene.gameMode.isWaveFinal(nextWaveIndex)) || - (globalScene.gameMode.hasShortBiomes && !(nextWaveIndex % 50)) + (gameMode.isClassic && gameMode.isWaveFinal(nextWaveIndex + 9)) || + (gameMode.isDaily && gameMode.isWaveFinal(nextWaveIndex)) || + (gameMode.hasShortBiomes && !(nextWaveIndex % 50)) ) { setNextBiome(BiomeId.END); - } else if (globalScene.gameMode.hasRandomBiomes) { + } else if (gameMode.hasRandomBiomes) { setNextBiome(this.generateNextBiome(nextWaveIndex)); } else if (Array.isArray(biomeLinks[currentBiome])) { const biomes: BiomeId[] = (biomeLinks[currentBiome] as (BiomeId | [BiomeId, number])[]) @@ -73,9 +84,6 @@ export class SelectBiomePhase extends BattlePhase { } generateNextBiome(waveIndex: number): BiomeId { - if (!(waveIndex % 50)) { - return BiomeId.END; - } - return globalScene.generateRandomBiome(waveIndex); + return waveIndex % 50 === 0 ? BiomeId.END : globalScene.generateRandomBiome(waveIndex); } } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index c0f4a32d7e1..ac567cc99c5 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -3,13 +3,9 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#data/data-lists"; import { BattleType } from "#enums/battle-type"; import type { BattlerIndex } from "#enums/battler-index"; -import { ChallengeType } from "#enums/challenge-type"; import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; -import type { CustomModifierSettings } from "#modifiers/modifier-type"; import { handleMysteryEncounterVictory } from "#mystery-encounters/encounter-phase-utils"; import { PokemonPhase } from "#phases/pokemon-phase"; -import { applyChallenges } from "#utils/challenge-utils"; -import { BooleanHolder } from "#utils/common"; export class VictoryPhase extends PokemonPhase { public readonly phaseName = "VictoryPhase"; @@ -49,15 +45,19 @@ export class VictoryPhase extends PokemonPhase { if (globalScene.currentBattle.battleType === BattleType.TRAINER) { globalScene.phaseManager.pushNew("TrainerVictoryPhase"); } - if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { + + const gameMode = globalScene.gameMode; + const currentWaveIndex = globalScene.currentBattle.waveIndex; + + if (gameMode.isEndless || !gameMode.isWaveFinal(currentWaveIndex)) { globalScene.phaseManager.pushNew("EggLapsePhase"); - if (globalScene.gameMode.isClassic) { - switch (globalScene.currentBattle.waveIndex) { + if (gameMode.isClassic) { + switch (currentWaveIndex) { case ClassicFixedBossWaves.RIVAL_1: case ClassicFixedBossWaves.RIVAL_2: // Get event modifiers for this wave timedEventManager - .getFixedBattleEventRewards(globalScene.currentBattle.waveIndex) + .getFixedBattleEventRewards(currentWaveIndex) .map(r => globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes[r])); break; case ClassicFixedBossWaves.EVIL_BOSS_2: @@ -66,59 +66,53 @@ export class VictoryPhase extends PokemonPhase { break; } } - const healStatus = new BooleanHolder(globalScene.currentBattle.waveIndex % 10 === 0); - applyChallenges(ChallengeType.PARTY_HEAL, healStatus); - if (!healStatus.value) { + if (currentWaveIndex % 10) { globalScene.phaseManager.pushNew( "SelectModifierPhase", undefined, undefined, - this.getFixedBattleCustomModifiers(), + gameMode.isFixedBattle(currentWaveIndex) + ? gameMode.getFixedBattle(currentWaveIndex).customModifierRewardSettings + : undefined, ); - } else if (globalScene.gameMode.isDaily) { + } else if (gameMode.isDaily) { globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.EXP_CHARM); - if ( - globalScene.currentBattle.waveIndex > 10 && - !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex) - ) { + if (currentWaveIndex > 10 && !gameMode.isWaveFinal(currentWaveIndex)) { globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.GOLDEN_POKEBALL); } } else { - const superExpWave = !globalScene.gameMode.isEndless ? (globalScene.offsetGym ? 0 : 20) : 10; - if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex === 10) { + const superExpWave = !gameMode.isEndless ? (globalScene.offsetGym ? 0 : 20) : 10; + if (gameMode.isEndless && currentWaveIndex === 10) { globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.EXP_SHARE); } - if ( - globalScene.currentBattle.waveIndex <= 750 && - (globalScene.currentBattle.waveIndex <= 500 || globalScene.currentBattle.waveIndex % 30 === superExpWave) - ) { + if (currentWaveIndex <= 750 && (currentWaveIndex <= 500 || currentWaveIndex % 30 === superExpWave)) { globalScene.phaseManager.pushNew( "ModifierRewardPhase", - globalScene.currentBattle.waveIndex % 30 !== superExpWave || globalScene.currentBattle.waveIndex > 250 + currentWaveIndex % 30 !== superExpWave || currentWaveIndex > 250 ? modifierTypes.EXP_CHARM : modifierTypes.SUPER_EXP_CHARM, ); } - if (globalScene.currentBattle.waveIndex <= 150 && !(globalScene.currentBattle.waveIndex % 50)) { + if (currentWaveIndex <= 150 && !(currentWaveIndex % 50)) { globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.GOLDEN_POKEBALL); } - if (globalScene.gameMode.isEndless && !(globalScene.currentBattle.waveIndex % 50)) { + if (gameMode.isEndless && !(currentWaveIndex % 50)) { globalScene.phaseManager.pushNew( "ModifierRewardPhase", - !(globalScene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS, + !(currentWaveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS, ); globalScene.phaseManager.pushNew("AddEnemyBuffModifierPhase"); } } - if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + if (gameMode.hasRandomBiomes || globalScene.isNewBiome()) { globalScene.phaseManager.pushNew("SelectBiomePhase"); } globalScene.phaseManager.pushNew("NewBattlePhase"); } else { globalScene.currentBattle.battleType = BattleType.CLEAR; - globalScene.score += globalScene.gameMode.getClearScoreBonus(); + globalScene.score += gameMode.getClearScoreBonus(); globalScene.updateScoreText(); globalScene.phaseManager.pushNew("GameOverPhase", true); } @@ -126,18 +120,4 @@ export class VictoryPhase extends PokemonPhase { this.end(); } - - /** - * If this wave is a fixed battle with special custom modifier rewards, - * will pass those settings to the upcoming {@linkcode SelectModifierPhase}`. - */ - getFixedBattleCustomModifiers(): CustomModifierSettings | undefined { - const gameMode = globalScene.gameMode; - const waveIndex = globalScene.currentBattle.waveIndex; - if (gameMode.isFixedBattle(waveIndex)) { - return gameMode.getFixedBattle(waveIndex).customModifierRewardSettings; - } - - return undefined; - } } diff --git a/src/system/achv.ts b/src/system/achv.ts index 8e312e3d590..f238acbda3a 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -448,6 +448,8 @@ export function getAchievementDescription(localizationKey: string): string { return i18next.t("achv:FLIP_STATS.description", { context: genderStr }); case "FLIP_INVERSE": return i18next.t("achv:FLIP_INVERSE.description", { context: genderStr }); + case "NUZLOCKE": + return i18next.t("achv:NUZLOCKE.description", { context: genderStr }); case "BREEDERS_IN_SPACE": return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr, diff --git a/test/challenges/limited-catch.test.ts b/test/challenges/limited-catch.test.ts index 80be52df2fb..b51732305d0 100644 --- a/test/challenges/limited-catch.test.ts +++ b/test/challenges/limited-catch.test.ts @@ -1,8 +1,10 @@ import { AbilityId } from "#enums/ability-id"; import { Challenges } from "#enums/challenges"; import { MoveId } from "#enums/move-id"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokeballType } from "#enums/pokeball"; import { SpeciesId } from "#enums/species-id"; +import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -52,4 +54,18 @@ describe("Challenges - Limited Catch", () => { expect(game.scene.getPlayerParty()).toHaveLength(1); }); + + it("should allow gift Pokémon from Mystery Encounters to be added to party", async () => { + game.override + .mysteryEncounterChance(100) + .mysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN) + .startingWave(12); + game.scene.money = 20000; + + await game.challengeMode.runToSummon([SpeciesId.NUZLEAF]); + + await runMysteryEncounterToEnd(game, 1); + + expect(game.scene.getPlayerParty()).toHaveLength(2); + }); }); diff --git a/test/challenges/limited-support.test.ts b/test/challenges/limited-support.test.ts index 5c0eb2bd420..35413220550 100644 --- a/test/challenges/limited-support.test.ts +++ b/test/challenges/limited-support.test.ts @@ -3,6 +3,7 @@ import { Challenges } from "#enums/challenges"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { UiMode } from "#enums/ui-mode"; +import { ExpBoosterModifier } from "#modifiers/modifier"; import { GameManager } from "#test/test-utils/game-manager"; import { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import Phaser from "phaser"; @@ -75,6 +76,7 @@ describe("Challenges - Limited Support", () => { await game.doKillOpponents(); await game.toNextWave(); + expect(game.scene.getModifiers(ExpBoosterModifier)).toHaveLength(1); expect(playerPokemon).not.toHaveFullHp(); game.move.use(MoveId.SPLASH);