From 5e7d44abfdabe78157a3641aaa036eb32a033e56 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:07:02 -0500 Subject: [PATCH 01/14] [Balance] Fix Liquidation, Double-Edge, Body Press compatibility (#4879) --- src/data/balance/tms.ts | 124 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts index 7b65ae65ec4..4882cf4f652 100644 --- a/src/data/balance/tms.ts +++ b/src/data/balance/tms.ts @@ -3302,11 +3302,23 @@ export const tmSpecies: TmSpecies = { Species.FERALIGATR, Species.SENTRET, Species.FURRET, + Species.HOOTHOOT, Species.NOCTOWL, + Species.LEDYBA, + Species.LEDIAN, + Species.SPINARAK, + Species.ARIADOS, Species.CROBAT, Species.CHINCHOU, Species.LANTURN, + Species.PICHU, + Species.CLEFFA, Species.IGGLYBUFF, + Species.TYROGUE, + Species.TOGEPI, + Species.TOGETIC, + Species.NATU, + Species.XATU, Species.MAREEP, Species.FLAAFFY, Species.AMPHAROS, @@ -3328,6 +3340,7 @@ export const tmSpecies: TmSpecies = { Species.UMBREON, Species.MURKROW, Species.SLOWKING, + Species.MISDREAVUS, Species.GIRAFARIG, Species.PINECO, Species.FORRETRESS, @@ -3338,11 +3351,21 @@ export const tmSpecies: TmSpecies = { Species.GRANBULL, Species.QWILFISH, Species.SCIZOR, + Species.SHUCKLE, Species.HERACROSS, + Species.SNEASEL, Species.TEDDIURSA, Species.URSARING, + Species.SLUGMA, + Species.MAGCARGO, Species.SWINUB, Species.PILOSWINE, + Species.CORSOLA, + Species.REMORAID, + Species.OCTILLERY, + Species.DELIBIRD, + Species.MANTINE, + Species.SKARMORY, Species.HOUNDOUR, Species.HOUNDOOM, Species.KINGDRA, @@ -3350,9 +3373,12 @@ export const tmSpecies: TmSpecies = { Species.DONPHAN, Species.PORYGON2, Species.STANTLER, + Species.TYROGUE, Species.HITMONTOP, + Species.SMOOCHUM, Species.ELEKID, Species.MAGBY, + Species.MILTANK, Species.BLISSEY, Species.RAIKOU, Species.ENTEI, @@ -3362,6 +3388,9 @@ export const tmSpecies: TmSpecies = { Species.TYRANITAR, Species.LUGIA, Species.HO_OH, + Species.CELEBI, + Species.TREECKO, + Species.GROVYLE, Species.SCEPTILE, Species.TORCHIC, Species.COMBUSKEN, @@ -3371,41 +3400,116 @@ export const tmSpecies: TmSpecies = { Species.SWAMPERT, Species.POOCHYENA, Species.MIGHTYENA, + Species.ZIGZAGOON, + Species.LINOONE, + Species.BEAUTIFLY, + Species.DUSTOX, Species.LOTAD, Species.LOMBRE, Species.LUDICOLO, Species.SEEDOT, Species.NUZLEAF, Species.SHIFTRY, + Species.TAILLOW, + Species.SWELLOW, + Species.WINGULL, + Species.PELIPPER, + Species.RALTS, + Species.KIRLIA, + Species.GARDEVOIR, + Species.SURSKIT, + Species.MASQUERAIN, + Species.SHROOMISH, + Species.BRELOOM, Species.VIGOROTH, Species.SLAKING, + Species.NINCADA, + Species.NINJASK, + Species.SHEDINJA, + Species.WHISMUR, + Species.LOUDRED, + Species.EXPLOUD, Species.MAKUHITA, Species.HARIYAMA, + Species.AZURILL, Species.NOSEPASS, + Species.SKITTY, + Species.DELCATTY, + Species.SABLEYE, + Species.MAWILE, + Species.ARON, + Species.LAIRON, + Species.AGGRON, + Species.MEDITITE, + Species.MEDICHAM, + Species.ELECTRIKE, + Species.MANECTRIC, + Species.PLUSLE, + Species.MINUN, Species.VOLBEAT, Species.ILLUMISE, + Species.ROSELIA, + Species.GULPIN, Species.SWALOT, + Species.CARVANHA, + Species.SHARPEDO, + Species.WAILMER, + Species.WAILORD, Species.NUMEL, Species.CAMERUPT, Species.TORKOAL, + Species.SPOINK, + Species.GRUMPIG, + Species.SPINDA, + Species.TRAPINCH, + Species.VIBRAVA, Species.FLYGON, + Species.CACNEA, + Species.CACTURNE, + Species.SWABLU, Species.ALTARIA, Species.ZANGOOSE, Species.SEVIPER, + Species.LUNATONE, + Species.SOLROCK, Species.BARBOACH, Species.WHISCASH, Species.CORPHISH, Species.CRAWDAUNT, + Species.BALTOY, + Species.CLAYDOL, + Species.LILEEP, + Species.CRADILY, + Species.ANORITH, + Species.ARMALDO, Species.FEEBAS, Species.MILOTIC, + Species.CASTFORM, + Species.KECLEON, + Species.SHUPPET, + Species.BANETTE, + Species.DUSKULL, + Species.DUSCLOPS, Species.TROPIUS, Species.CHIMECHO, + Species.ABSOL, + Species.SNORUNT, + Species.GLALIE, + Species.SPHEAL, + Species.SEALEO, + Species.WALREIN, + Species.CLAMPERL, + Species.HUNTAIL, + Species.GOREBYSS, + Species.RELICANTH, + Species.LUVDISC, Species.BAGON, Species.SHELGON, Species.SALAMENCE, Species.METANG, Species.METAGROSS, Species.REGIROCK, + Species.REGICE, Species.REGISTEEL, Species.LATIAS, Species.LATIOS, @@ -3413,6 +3517,7 @@ export const tmSpecies: TmSpecies = { Species.GROUDON, Species.RAYQUAZA, Species.JIRACHI, + Species.DEOXYS, Species.TURTWIG, Species.GROTLE, Species.TORTERRA, @@ -64246,12 +64351,16 @@ export const tmSpecies: TmSpecies = { Species.BLOODMOON_URSALUNA, ], [Moves.LIQUIDATION]: [ + Species.SQUIRTLE, + Species.WARTORTLE, Species.BLASTOISE, Species.PSYDUCK, Species.GOLDUCK, Species.POLIWAG, Species.POLIWHIRL, Species.POLIWRATH, + Species.TENTACOOL, + Species.TENTACRUEL, Species.SLOWPOKE, Species.SLOWBRO, Species.DEWGONG, @@ -64267,7 +64376,11 @@ export const tmSpecies: TmSpecies = { Species.KABUTO, Species.KABUTOPS, Species.MEW, + Species.TOTODILE, + Species.CROCONAW, Species.FERALIGATR, + Species.CHINCHOU, + Species.LANTURN, Species.MARILL, Species.AZUMARILL, Species.POLITOED, @@ -64280,6 +64393,9 @@ export const tmSpecies: TmSpecies = { Species.MANTINE, Species.KINGDRA, Species.SUICUNE, + Species.LUGIA, + Species.MUDKIP, + Species.MARSHTOMP, Species.SWAMPERT, Species.WINGULL, Species.PELIPPER, @@ -64296,6 +64412,8 @@ export const tmSpecies: TmSpecies = { Species.WALREIN, Species.RELICANTH, Species.LUVDISC, + Species.LATIAS, + Species.LATIOS, Species.KYOGRE, Species.PIPLUP, Species.PRINPLUP, @@ -64407,11 +64525,13 @@ export const tmSpecies: TmSpecies = { Species.ONIX, Species.HYPNO, Species.LICKITUNG, + Species.RHYHORN, Species.RHYDON, Species.LAPRAS, Species.SNORLAX, Species.DRAGONITE, Species.MEW, + Species.MEGANIUM, Species.SUDOWOODO, Species.QUAGSIRE, Species.FORRETRESS, @@ -64445,6 +64565,8 @@ export const tmSpecies: TmSpecies = { Species.REGISTEEL, Species.GROUDON, Species.TORTERRA, + Species.RAMPARDOS, + Species.BASTIODON, Species.BRONZONG, Species.HIPPOPOTAS, Species.HIPPOWDON, @@ -64459,6 +64581,7 @@ export const tmSpecies: TmSpecies = { Species.HEATRAN, Species.REGIGIGAS, Species.ARCEUS, + Species.EMBOAR, Species.ROGGENROLA, Species.BOLDORE, Species.GIGALITH, @@ -64471,6 +64594,7 @@ export const tmSpecies: TmSpecies = { Species.CUBCHOO, Species.BEARTIC, Species.GOLURK, + Species.COBALION, Species.RESHIRAM, Species.ZEKROM, Species.KYUREM, From 413f2b80c8ba5bc181c603cd7ae45c5b40ab21f8 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:26:58 -0500 Subject: [PATCH 02/14] [Balance] Increase Gimmighoul's evo counter more with Big Nugget/Relic Gold (#4856) * Increase Gimmighoul's evo counter more with Big Nugget/Relic Gold * Update src/modifier/modifier.ts Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com> --------- Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com> --- src/modifier/modifier.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index ac8dc556b98..6b449c38ada 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -2818,7 +2818,7 @@ export class MoneyRewardModifier extends ConsumableModifier { battleScene.getPlayerParty().map(p => { if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) { - p.evoCounter ? p.evoCounter++ : p.evoCounter = 1; + p.evoCounter ? p.evoCounter += Math.min(Math.floor(this.moneyMultiplier), 3) : p.evoCounter = Math.min(Math.floor(this.moneyMultiplier), 3); const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier; battleScene.addModifier(modifier); } From 360a897ed2f53a0f54dc96f3a16fc5bade166956 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:11:46 -0800 Subject: [PATCH 03/14] [Balance] Endure Tokens only endure one hit (#4875) * Endure Tokens only endure one hit * Add tests for Endure * Update docs --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battler-tags.ts | 13 +++++-- src/enums/battler-tag-type.ts | 1 + src/field/pokemon.ts | 2 ++ src/modifier/modifier.ts | 6 ++-- src/test/moves/endure.test.ts | 65 +++++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/test/moves/endure.test.ts diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 28ab5ff2a4f..ce25b56157c 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1502,9 +1502,14 @@ export class ContactBurnProtectedTag extends DamageProtectedTag { } } +/** + * `BattlerTag` class for effects that cause the affected Pokemon to survive lethal attacks at 1 HP. + * Used for {@link https://bulbapedia.bulbagarden.net/wiki/Endure_(move) | Endure} and + * Endure Tokens. + */ export class EnduringTag extends BattlerTag { - constructor(sourceMove: Moves) { - super(BattlerTagType.ENDURING, BattlerTagLapseType.TURN_END, 0, sourceMove); + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: Moves) { + super(tagType, lapseType, 0, sourceMove); } onAdd(pokemon: Pokemon): void { @@ -3009,7 +3014,9 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.BURNING_BULWARK: return new ContactBurnProtectedTag(sourceMove); case BattlerTagType.ENDURING: - return new EnduringTag(sourceMove); + return new EnduringTag(tagType, BattlerTagLapseType.TURN_END, sourceMove); + case BattlerTagType.ENDURE_TOKEN: + return new EnduringTag(tagType, BattlerTagLapseType.AFTER_HIT, sourceMove); case BattlerTagType.STURDY: return new SturdyTag(sourceMove); case BattlerTagType.PERISH_SONG: diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index b2bbc1e6189..bb969386630 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -92,4 +92,5 @@ export enum BattlerTagType { COMMANDED = "COMMANDED", GRUDGE = "GRUDGE", PSYCHO_SHIFT = "PSYCHO_SHIFT", + ENDURE_TOKEN = "ENDURE_TOKEN", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 30d1aceea4b..07a958fe27b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2955,6 +2955,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { surviveDamage.value = this.lapseTag(BattlerTagType.ENDURING); } else if (this.hp > 1 && this.getTag(BattlerTagType.STURDY)) { surviveDamage.value = this.lapseTag(BattlerTagType.STURDY); + } else if (this.hp >= 1 && this.getTag(BattlerTagType.ENDURE_TOKEN)) { + surviveDamage.value = this.lapseTag(BattlerTagType.ENDURE_TOKEN); } if (!surviveDamage.value) { this.scene.applyModifiers(SurviveDamageModifier, this.isPlayer(), this, surviveDamage); diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 6b449c38ada..4e59d6c01f0 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3647,8 +3647,8 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier { } /** - * Applies {@linkcode EnemyEndureChanceModifier} - * @param target {@linkcode Pokemon} to apply the {@linkcode BattlerTagType.ENDURING} chance to + * Applies a chance of enduring a lethal hit of an attack + * @param target the {@linkcode Pokemon} to apply the {@linkcode BattlerTagType.ENDURING} chance to * @returns `true` if {@linkcode Pokemon} endured */ override apply(target: Pokemon): boolean { @@ -3656,7 +3656,7 @@ export class EnemyEndureChanceModifier extends EnemyPersistentModifier { return false; } - target.addTag(BattlerTagType.ENDURING, 1); + target.addTag(BattlerTagType.ENDURE_TOKEN, 1); target.battleData.endured = true; diff --git a/src/test/moves/endure.test.ts b/src/test/moves/endure.test.ts new file mode 100644 index 00000000000..bde5a26f68e --- /dev/null +++ b/src/test/moves/endure.test.ts @@ -0,0 +1,65 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Endure", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.THUNDER, Moves.BULLET_SEED, Moves.TOXIC ]) + .ability(Abilities.SKILL_LINK) + .startingLevel(100) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.NO_GUARD) + .enemyMoveset(Moves.ENDURE); + }); + + it("should let the pokemon survive with 1 HP", async () => { + await game.classicMode.startBattle([ Species.ARCEUS ]); + + game.move.select(Moves.THUNDER); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.getEnemyPokemon()!.hp).toBe(1); + }); + + it("should let the pokemon survive with 1 HP when hit with a multihit move", async () => { + await game.classicMode.startBattle([ Species.ARCEUS ]); + + game.move.select(Moves.BULLET_SEED); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.getEnemyPokemon()!.hp).toBe(1); + }); + + it("shouldn't prevent fainting from indirect damage", async () => { + game.override.enemyLevel(100); + await game.classicMode.startBattle([ Species.ARCEUS ]); + + const enemy = game.scene.getEnemyPokemon()!; + enemy.hp = 2; + + game.move.select(Moves.TOXIC); + await game.phaseInterceptor.to("VictoryPhase"); + + expect(enemy.isFainted()).toBe(true); + }); +}); From 77b94bf45d9b3b92d44fb2c27936a4fd2d8cf390 Mon Sep 17 00:00:00 2001 From: Moka <54149968+MokaStitcher@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:46:02 +0100 Subject: [PATCH 04/14] bump version to 1.2.0 and update locale submodule to latest commit (#4884) --- package-lock.json | 4 ++-- package.json | 2 +- public/locales | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e512884922..792720200a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pokemon-rogue-battle", - "version": "1.1.6", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pokemon-rogue-battle", - "version": "1.1.6", + "version": "1.2.0", "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", diff --git a/package.json b/package.json index f106fb1a773..3c65fe1a8b9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.1.6", + "version": "1.2.0", "type": "module", "scripts": { "start": "vite", diff --git a/public/locales b/public/locales index 5775faa6b31..ed1b1df4776 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 5775faa6b3184082df73f6cdb96b253ea7dae3fe +Subproject commit ed1b1df4776ccd4330e8ac1d2f44de611d04c2bc From 4aff2ffa1d4bfe82494b3489f21e46b6a88adc6a Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sat, 16 Nov 2024 15:30:47 -0800 Subject: [PATCH 05/14] [Dev] Sessions will now sync with the server at the start of every 5 waves (#4808) * Replaced conditional with `true` so that sync-to-server occurs at the start of every wave. * Update src/phases/encounter-phase.ts Co-authored-by: Frederico Santos * Fixed errors from previous commit. * Revised condition to include the first wave of a run. * Apply suggestions from code review Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com> --------- Co-authored-by: frutescens Co-authored-by: Frederico Santos Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com> --- src/phases/encounter-phase.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index fc022ab9647..e58500c934e 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -225,7 +225,8 @@ export class EncounterPhase extends BattlePhase { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (!this.loaded) { this.trySetWeatherIfNewBiome(); // Set weather before session gets saved - this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => { + // Game syncs to server on waves X1 and X6 (As of 1.2.0) + this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 5 === 1 || (this.scene.lastSavePlayTime ?? 0) >= 300).then(success => { this.scene.disableMenu = false; if (!success) { return this.scene.reset(true); From c22b48f9032c2c5930abcc62fe2a593794814a96 Mon Sep 17 00:00:00 2001 From: "Amani H." <109637146+xsn34kzx@users.noreply.github.com> Date: Sat, 16 Nov 2024 18:31:28 -0500 Subject: [PATCH 06/14] [P1] Fix System Data Conversion Failure 3 (#4866) * [P1] Fix System Data Conversion Failure 3 * Use `isNullOrUndefined`, Clean Up Imports * fix pre candy update save data migration for candy * fix pre candy update save data migration for candy part 2... --------- Co-authored-by: Moka --- src/system/game-data.ts | 6 +++--- .../version_migration/versions/v1_0_4.ts | 21 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 4d01ae9998a..70b0fadf0b0 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -506,9 +506,9 @@ export class GameData { const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species); for (const s of starterIds) { - this.starterData[s].candyCount += this.dexData[s].caughtCount; - this.starterData[s].candyCount += this.dexData[s].hatchedCount * 2; - if (this.dexData[s].caughtAttr & DexAttr.SHINY) { + this.starterData[s].candyCount += systemData.dexData[s].caughtCount; + this.starterData[s].candyCount += systemData.dexData[s].hatchedCount * 2; + if (systemData.dexData[s].caughtAttr & DexAttr.SHINY) { this.starterData[s].candyCount += 4; } } diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index f16b6bcb6bb..d6e5baf0c84 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -1,6 +1,7 @@ -import { SettingKeys } from "../../settings/settings"; -import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "../../game-data"; -import { allSpecies } from "../../../data/pokemon-species"; +import { SettingKeys } from "#app/system/settings/settings"; +import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "#app/system/game-data"; +import { allSpecies } from "#app/data/pokemon-species"; +import { isNullOrUndefined } from "#app/utils"; export const systemMigrators = [ /** @@ -46,12 +47,14 @@ export const systemMigrators = [ * @param data {@linkcode SystemSaveData} */ function fixStarterData(data: SystemSaveData) { - for (const starterId of defaultStarterSpecies) { - if (data.starterData[starterId]?.abilityAttr) { - data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; - } - if (data.dexData[starterId]?.caughtAttr) { - data.dexData[starterId].caughtAttr |= DexAttr.FEMALE; + if (!isNullOrUndefined(data.starterData)) { + for (const starterId of defaultStarterSpecies) { + if (data.starterData[starterId]?.abilityAttr) { + data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1; + } + if (data.dexData[starterId]?.caughtAttr) { + data.dexData[starterId].caughtAttr |= DexAttr.FEMALE; + } } } } From c2afac8b02a6ca793263c68dc5a93bf10386b7bc Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sat, 16 Nov 2024 15:32:05 -0800 Subject: [PATCH 07/14] [Bug] Moved code preventing MBH's transfer to post modifier generation (#4858) * Moved code preventing MBH's transfer to after enemy modifiers were generated. * Removed unnecessary ! * Removed unnecessary ? * Created a new enum and functions for retrieving final bosses. * Moved isBattleClassicFinalBoss to game-mode.ts and reverted battle.ts * Preventing item transfer with tryTransferModifier instead * Added filtering to modifier-retrieve methods. * Revised logic in tryTransferHeldItemModifier * Adding what works to the game even though it's not the best. * Added comments * Removing past changes to files. * Added check for Classic Final Boss. --------- Co-authored-by: frutescens --- src/game-mode.ts | 7 +++++++ src/phases/encounter-phase.ts | 13 +++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/game-mode.ts b/src/game-mode.ts index 0f997bf651e..3f12c5b056e 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -230,6 +230,13 @@ export class GameMode implements GameModeConfig { return waveIndex % 10 === 0; } + /** + * @returns `true` if the current battle is against classic mode's final boss + */ + isBattleClassicFinalBoss(waveIndex: number): boolean { + return (this.modeId === GameModes.CLASSIC || this.modeId === GameModes.CHALLENGE) && this.isWaveFinal(waveIndex); + } + /** * Every 50 waves of an Endless mode is a boss * At this time it is paradox pokemon diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index e58500c934e..03126ba81bb 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -141,10 +141,6 @@ export class EncounterPhase extends BattlePhase { } else if (!(battle.waveIndex % 1000)) { enemyPokemon.formIndex = 1; enemyPokemon.updateScale(); - const bossMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier && m.pokemonId === enemyPokemon.id, false) as TurnHeldItemTransferModifier; - this.scene.removeModifier(bossMBH!); - bossMBH?.setTransferrableFalse(); - this.scene.addEnemyModifier(bossMBH!); } } @@ -443,6 +439,15 @@ export class EncounterPhase extends BattlePhase { if (enemyPokemon.isShiny()) { this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); } + /** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */ + if (enemyPokemon.species.speciesId === Species.ETERNATUS && (this.scene.gameMode.isBattleClassicFinalBoss(this.scene.currentBattle.waveIndex) || this.scene.gameMode.isEndlessMajorBoss(this.scene.currentBattle.waveIndex))) { + const enemyMBH = this.scene.findModifier(m => m instanceof TurnHeldItemTransferModifier, false) as TurnHeldItemTransferModifier; + if (enemyMBH) { + this.scene.removeModifier(enemyMBH, true); + enemyMBH.setTransferrableFalse(); + this.scene.addEnemyModifier(enemyMBH); + } + } }); if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) { From 7dc4210f711e0cb92ac668b9a217561731b2e83c Mon Sep 17 00:00:00 2001 From: Daniel Pochert Date: Sun, 17 Nov 2024 00:33:14 +0100 Subject: [PATCH 08/14] [Bug] fix training session encounter not applying nature correctly (#4801) * fix training session encounter not applying nature correctly * implement pokemon.setCustomNature + nature unlock utility method(s) * update doc --------- Co-authored-by: Moka <54149968+MokaStitcher@users.noreply.github.com> --- .../shady-vitamin-dealer-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 4 +-- src/field/pokemon.ts | 5 ++++ src/modifier/modifier.ts | 12 +++------ src/system/game-data.ts | 26 +++++++++++++++++++ 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 798e85868f6..3f1ace47b0f 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -138,7 +138,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = newNature = randSeedInt(25) as Nature; } - chosenPokemon.customPokemonData.nature = newNature; + chosenPokemon.setCustomNature(newNature); encounter.setDialogueToken("newNature", getNatureName(newNature)); queueEncounterMessage(scene, `${namespace}:cheap_side_effects`); setEncounterExp(scene, [ chosenPokemon.id ], 100); diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 4a0a91bf751..725c4ba79eb 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -226,8 +226,8 @@ export const TrainingSessionEncounter: MysteryEncounter = const onBeforeRewardsPhase = () => { queueEncounterMessage(scene, `${namespace}:option.2.finished`); // Add the pokemon back to party with Nature change - playerPokemon.setNature(encounter.misc.chosenNature); - scene.gameData.setPokemonCaught(playerPokemon, false); + playerPokemon.setCustomNature(encounter.misc.chosenNature); + scene.gameData.unlockSpeciesNature(playerPokemon.species, encounter.misc.chosenNature); // Add pokemon and modifiers back scene.getPlayerParty().push(playerPokemon); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 07a958fe27b..f9e7d7d1cad 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1072,6 +1072,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.calculateStats(); } + setCustomNature(nature: Nature): void { + this.customPokemonData.nature = nature; + this.calculateStats(); + } + generateNature(naturePool?: Nature[]): void { if (naturePool === undefined) { naturePool = Utils.getEnumValues(Nature); diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 4e59d6c01f0..bbd1763cd84 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,5 +1,5 @@ import type BattleScene from "#app/battle-scene"; -import { FusionSpeciesFormEvolution, pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; +import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; import { getLevelTotalExp } from "#app/data/exp"; import { allMoves } from "#app/data/move"; @@ -2197,14 +2197,8 @@ export class PokemonNatureChangeModifier extends ConsumablePokemonModifier { * @returns */ override apply(playerPokemon: PlayerPokemon): boolean { - playerPokemon.customPokemonData.nature = this.nature; - let speciesId = playerPokemon.species.speciesId; - playerPokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1); - - while (pokemonPrevolutions.hasOwnProperty(speciesId)) { - speciesId = pokemonPrevolutions[speciesId]; - playerPokemon.scene.gameData.dexData[speciesId].natureAttr |= 1 << (this.nature + 1); - } + playerPokemon.setCustomNature(this.nature); + playerPokemon.scene.gameData.unlockSpeciesNature(playerPokemon.species, this.nature); return true; } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 70b0fadf0b0..3e3a6ce8f8b 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1791,6 +1791,32 @@ export class GameData { }); } + /** + * Checks whether the root species of a given {@PokemonSpecies} has been unlocked in the dex + */ + isRootSpeciesUnlocked(species: PokemonSpecies): boolean { + return !!this.dexData[species.getRootSpeciesId()]?.caughtAttr; + } + + /** + * Unlocks the given {@linkcode Nature} for a {@linkcode PokemonSpecies} and its prevolutions. + * Will fail silently if root species has not been unlocked + */ + unlockSpeciesNature(species: PokemonSpecies, nature: Nature): void { + if (!this.isRootSpeciesUnlocked(species)) { + return; + } + + //recursively unlock nature for species and prevolutions + const _unlockSpeciesNature = (speciesId: Species) => { + this.dexData[speciesId].natureAttr |= 1 << (nature + 1); + if (pokemonPrevolutions.hasOwnProperty(speciesId)) { + _unlockSpeciesNature(pokemonPrevolutions[speciesId]); + } + }; + _unlockSpeciesNature(species.speciesId); + } + updateSpeciesDexIvs(speciesId: Species, ivs: integer[]): void { let dexEntry: DexEntry; do { From ae8efeedf8ca520f2beb17ca8093586de5775ca0 Mon Sep 17 00:00:00 2001 From: Moka <54149968+MokaStitcher@users.noreply.github.com> Date: Sun, 17 Nov 2024 00:34:14 +0100 Subject: [PATCH 09/14] [UI] Make ME text legible when using the legacy UI theme (#4847) --- .../utils/encounter-dialogue-utils.ts | 8 ++-- src/ui/mystery-encounter-ui-handler.ts | 4 +- src/ui/run-info-ui-handler.ts | 3 +- src/ui/text.ts | 37 ++++++++++++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index c4d5e47cb05..acaa7c6244f 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -7,22 +7,22 @@ import i18next from "i18next"; /** * Will inject all relevant dialogue tokens that exist in the {@linkcode BattleScene.currentBattle.mysteryEncounter.dialogueTokens}, into i18n text. * Also adds BBCodeText fragments for colored text, if applicable - * @param scene * @param keyOrString * @param primaryStyle Can define a text style to be applied to the entire string. Must be defined for BBCodeText styles to be applied correctly - * @param uiTheme */ -export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string | null { +export function getEncounterText(scene: BattleScene, keyOrString?: string, primaryStyle?: TextStyle): string | null { if (isNullOrUndefined(keyOrString)) { return null; } + const uiTheme = scene.uiTheme ?? UiTheme.DEFAULT; + let textString: string | null = getTextWithDialogueTokens(scene, keyOrString); // Can only color the text if a Primary Style is defined // primaryStyle is applied to all text that does not have its own specified style if (primaryStyle && textString) { - textString = getTextWithColors(textString, primaryStyle, uiTheme); + textString = getTextWithColors(textString, primaryStyle, uiTheme, true); } return textString; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index b568f8b71de..cdb1c9024c5 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -369,9 +369,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { let text: string | null; if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === MysteryEncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)) { // Options with special requirements that are met are automatically colored green - text = getEncounterText(this.scene, label, TextStyle.SUMMARY_GREEN); + text = getEncounterText(this.scene, label, TextStyle.ME_OPTION_SPECIAL); } else { - text = getEncounterText(this.scene, label, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW); + text = getEncounterText(this.scene, label, optionDialogue.style ? optionDialogue.style : TextStyle.ME_OPTION_DEFAULT); } if (text) { diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 82d390016f7..ace9d956dd9 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -518,7 +518,8 @@ export default class RunInfoUiHandler extends UiHandler { const runTime = Utils.getPlayTimeString(this.runInfo.playTime); runInfoText.appendText(`${i18next.t("runHistory:runLength")}: ${runTime}`, false); const runMoney = Utils.formatMoney(this.scene.moneyFormat, this.runInfo.money); - runInfoText.appendText(`[color=${getTextColor(TextStyle.MONEY)}]${i18next.t("battleScene:moneyOwned", { formattedMoney : runMoney })}[/color]`); + const moneyTextColor = getTextColor(TextStyle.MONEY_WINDOW, false, this.scene.uiTheme); + runInfoText.appendText(`[color=${moneyTextColor}]${i18next.t("battleScene:moneyOwned", { formattedMoney : runMoney })}[/color]`); runInfoText.setPosition(7, 70); runInfoTextContainer.add(runInfoText); // Luck diff --git a/src/ui/text.ts b/src/ui/text.ts index 069aa8680fc..17ae02be9ef 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -22,7 +22,8 @@ export enum TextStyle { SUMMARY_GOLD, SUMMARY_GRAY, SUMMARY_GREEN, - MONEY, + MONEY, // Money default styling (pale yellow) + MONEY_WINDOW, // Money displayed in Windows (needs different colors based on theme) STATS_LABEL, STATS_VALUE, SETTINGS_VALUE, @@ -38,7 +39,9 @@ export enum TextStyle { MOVE_PP_EMPTY, SMALLER_WINDOW_ALT, BGM_BAR, - PERFECT_IV + PERFECT_IV, + ME_OPTION_DEFAULT, // Default style for choices in ME + ME_OPTION_SPECIAL, // Style for choices with special requirements in ME } export interface TextStyleOptions { @@ -139,6 +142,8 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty case TextStyle.SUMMARY_GREEN: case TextStyle.WINDOW: case TextStyle.WINDOW_ALT: + case TextStyle.ME_OPTION_DEFAULT: + case TextStyle.ME_OPTION_SPECIAL: shadowXpos = 3; shadowYpos = 3; break; @@ -177,6 +182,7 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty break; case TextStyle.BATTLE_INFO: case TextStyle.MONEY: + case TextStyle.MONEY_WINDOW: case TextStyle.TOOLTIP_TITLE: styleOptions.fontSize = defaultFontSize - 24; shadowXpos = 3.5; @@ -238,13 +244,22 @@ export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: Ui * - "red text" with TextStyle.SUMMARY_RED applied * @param content string with styling that need to be applied for BBCodeTextObject * @param primaryStyle Primary style is required in order to escape BBCode styling properly. - * @param uiTheme + * @param uiTheme the {@linkcode UiTheme} to get TextStyle for + * @param forWindow set to `true` if the text is to be displayed in a window ({@linkcode BattleScene.addWindow}) + * it will replace all instances of the default MONEY TextStyle by {@linkcode TextStyle.MONEY_WINDOW} */ -export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { +export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme, forWindow?: boolean): string { // Apply primary styling before anything else let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]"; const primaryStyleString = [ ...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))! ][0]; + /* For money text displayed in game windows, we can't use the default {@linkcode TextStyle.MONEY} + * or it will look wrong in legacy mode because of the different window background color + * So, for text to be displayed in windows replace all "@[MONEY]" with "@[MONEY_WINDOW]" */ + if (forWindow) { + text = text.replace(/@\[MONEY\]/g, (_substring: string) => "@[MONEY_WINDOW]"); + } + // Set custom colors text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => { return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) + "[/color][/shadow]" + primaryStyleString; @@ -310,7 +325,12 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui return !shadow ? "#f89890" : "#984038"; case TextStyle.SUMMARY_GOLD: case TextStyle.MONEY: - return !shadow ? "#e8e8a8" : "#a0a060"; + return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold + case TextStyle.MONEY_WINDOW: + if (isLegacyTheme) { + return !shadow ? "#f8b050" : "#c07800"; // Gold + } + return !shadow ? "#e8e8a8" : "#a0a060"; // Pale Yellow/Gold case TextStyle.SETTINGS_LOCKED: case TextStyle.SUMMARY_GRAY: return !shadow ? "#a0a0a0" : "#636363"; @@ -332,6 +352,13 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: Ui return !shadow ? "#484848" : "#d0d0c8"; case TextStyle.BGM_BAR: return !shadow ? "#f8f8f8" : "#6b5a73"; + case TextStyle.ME_OPTION_DEFAULT: + return !shadow ? "#f8f8f8" : "#6b5a73"; // White + case TextStyle.ME_OPTION_SPECIAL: + if (isLegacyTheme) { + return !shadow ? "#f8b050" : "#c07800"; // Gold + } + return !shadow ? "#78c850" : "#306850"; // Green } } From 07f08877c18a027b9ff6c473476796b3d57b992b Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Sun, 17 Nov 2024 12:16:54 -0500 Subject: [PATCH 10/14] [Balance] Multi Lens damage reduction on fixed-damage moves (#4896) --- src/field/pokemon.ts | 4 ++++ src/modifier/modifier.ts | 16 +++++++++++---- src/test/abilities/parental_bond.test.ts | 2 +- src/test/items/multi_lens.test.ts | 25 ++++++++++++++++++++++-- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f9e7d7d1cad..f14fc954c84 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2622,6 +2622,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const fixedDamage = new Utils.IntegerHolder(0); applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); if (fixedDamage.value) { + const multiLensMultiplier = new Utils.NumberHolder(1); + source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, move.id, null, multiLensMultiplier); + fixedDamage.value = Utils.toDmgValue(fixedDamage.value * multiLensMultiplier.value); + return { cancelled: false, result: HitResult.EFFECTIVE, diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index bbd1763cd84..5e5246269a3 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -2727,10 +2727,18 @@ export class PokemonMultiHitModifier extends PokemonHeldItemModifier { * Additional strikes beyond that are given a 0.25x damage multiplier */ private applyDamageModifier(pokemon: Pokemon, damageMultiplier: NumberHolder): boolean { - damageMultiplier.value = (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) - ? (1 - (0.25 * this.getStackCount())) - : 0.25; - return true; + if (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) { + // Reduce first hit by 25% for each stack count + damageMultiplier.value *= 1 - 0.25 * this.getStackCount(); + return true; + } else if (pokemon.turnData.hitCount - pokemon.turnData.hitsLeft !== this.getStackCount() + 1) { + // Deal 25% damage for each remaining Multi Lens hit + damageMultiplier.value *= 0.25; + return true; + } else { + // An extra hit not caused by Multi Lens -- assume it is Parental Bond + return false; + } } getMaxHeldItemCount(pokemon: Pokemon): number { diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 4189941a51e..10048a774cd 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -313,7 +313,7 @@ describe("Abilities - Parental Bond", () => { await game.phaseInterceptor.to("MoveEndPhase", false); - expect(enemyPokemon.hp).toBe(enemyStartingHp - 300); + expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); } ); diff --git a/src/test/items/multi_lens.test.ts b/src/test/items/multi_lens.test.ts index d389ca70555..c5e60c9f9e5 100644 --- a/src/test/items/multi_lens.test.ts +++ b/src/test/items/multi_lens.test.ts @@ -32,8 +32,8 @@ describe("Items - Multi Lens", () => { .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) - .startingLevel(100) - .enemyLevel(100); + .startingLevel(99) // Check for proper rounding on Seismic Toss damage reduction + .enemyLevel(99); }); it.each([ @@ -114,4 +114,25 @@ describe("Items - Multi Lens", () => { expect(magikarp.turnData.hitCount).toBe(2); }); + + it("should enhance fixed-damage moves while also applying damage reduction", async () => { + game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]) + .moveset(Moves.SEISMIC_TOSS); + + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + const spy = vi.spyOn(enemyPokemon, "getAttackDamage"); + + game.move.select(Moves.SEISMIC_TOSS); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + + await game.phaseInterceptor.to("MoveEndPhase"); + const damageResults = spy.mock.results.map(result => result.value?.damage); + + expect(damageResults).toHaveLength(2); + expect(damageResults[0]).toBe(Math.floor(playerPokemon.level * 0.75)); + expect(damageResults[1]).toBe(Math.floor(playerPokemon.level * 0.25)); + }); }); From 1867ac4a9e37a49389dd3db6da6c33806258eb77 Mon Sep 17 00:00:00 2001 From: "Amani H." <109637146+xsn34kzx@users.noreply.github.com> Date: Sun, 17 Nov 2024 12:34:41 -0500 Subject: [PATCH 11/14] [Dev] Minor Version Migration Tweaks (#4727) * [P3] Prevent Unintended ME Migration Application * Change Patch Value from 4 to 5 * Bump Game Version * Fix Imports, Use `isNullOrUndefined` --- .../version_migration/version_converter.ts | 2 +- .../version_migration/versions/v1_0_4.ts | 24 +++++++++++++++ .../version_migration/versions/v1_1_0.ts | 29 +------------------ 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index e96afb5cbd4..d0c69dc3213 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -129,7 +129,7 @@ class SessionVersionConverter extends VersionConverter { if (curMajor === 1) { if (curMinor === 0) { - if (curPatch <= 4) { + if (curPatch <= 5) { console.log("Applying v1.0.4 session data migration!"); this.callMigrators(data, v1_0_4.sessionMigrators); } diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index d6e5baf0c84..95f0337ecdd 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -1,6 +1,7 @@ import { SettingKeys } from "#app/system/settings/settings"; import { AbilityAttr, defaultStarterSpecies, DexAttr, SystemSaveData, SessionSaveData } from "#app/system/game-data"; import { allSpecies } from "#app/data/pokemon-species"; +import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { isNullOrUndefined } from "#app/utils"; export const systemMigrators = [ @@ -134,5 +135,28 @@ export const sessionMigrators = [ m.className = "ResetNegativeStatStageModifier"; } }); + }, + /** + * Converts old Pokemon natureOverride and mysteryEncounterData + * to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead. + * @param data {@linkcode SessionSaveData} + */ + function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) { + // Fix Pokemon nature overrides and custom data migration + data.party.forEach(pokemon => { + if (pokemon["mysteryEncounterPokemonData"]) { + pokemon.customPokemonData = new CustomPokemonData(pokemon["mysteryEncounterPokemonData"]); + pokemon["mysteryEncounterPokemonData"] = null; + } + if (pokemon["fusionMysteryEncounterPokemonData"]) { + pokemon.fusionCustomPokemonData = new CustomPokemonData(pokemon["fusionMysteryEncounterPokemonData"]); + pokemon["fusionMysteryEncounterPokemonData"] = null; + } + pokemon.customPokemonData = pokemon.customPokemonData ?? new CustomPokemonData(); + if (!isNullOrUndefined(pokemon["natureOverride"]) && pokemon["natureOverride"] >= 0) { + pokemon.customPokemonData.nature = pokemon["natureOverride"]; + pokemon["natureOverride"] = -1; + } + }); } ] as const; diff --git a/src/system/version_migration/versions/v1_1_0.ts b/src/system/version_migration/versions/v1_1_0.ts index aac554c4531..5d6247aeaa2 100644 --- a/src/system/version_migration/versions/v1_1_0.ts +++ b/src/system/version_migration/versions/v1_1_0.ts @@ -1,32 +1,5 @@ -import { SessionSaveData } from "../../game-data"; -import { CustomPokemonData } from "#app/data/custom-pokemon-data"; - export const systemMigrators = [] as const; export const settingsMigrators = [] as const; -export const sessionMigrators = [ - /** - * Converts old Pokemon natureOverride and mysteryEncounterData - * to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead. - * @param data {@linkcode SessionSaveData} - */ - function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) { - // Fix Pokemon nature overrides and custom data migration - data.party.forEach(pokemon => { - if (pokemon["mysteryEncounterPokemonData"]) { - pokemon.customPokemonData = new CustomPokemonData(pokemon["mysteryEncounterPokemonData"]); - pokemon["mysteryEncounterPokemonData"] = null; - } - if (pokemon["fusionMysteryEncounterPokemonData"]) { - pokemon.fusionCustomPokemonData = new CustomPokemonData(pokemon["fusionMysteryEncounterPokemonData"]); - pokemon["fusionMysteryEncounterPokemonData"] = null; - } - pokemon.customPokemonData = pokemon.customPokemonData ?? new CustomPokemonData(); - if (pokemon["natureOverride"] && pokemon["natureOverride"] >= 0) { - pokemon.customPokemonData.nature = pokemon["natureOverride"]; - pokemon["natureOverride"] = -1; - } - }); - } -] as const; +export const sessionMigrators = [] as const; From 6b58d51ea2d75b29341526a3122659b5415f86ab Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 17 Nov 2024 12:19:23 -0800 Subject: [PATCH 12/14] Changed conditional to actually consider the wave index. (#4899) Co-authored-by: frutescens --- src/data/ability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index b77b18947be..0572b041c95 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4953,7 +4953,7 @@ class ForceSwitchOutHelper { * For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles). */ } else { - if (!pokemon.scene.currentBattle.waveIndex && pokemon.scene.currentBattle.waveIndex % 10 === 0) { + if (!pokemon.scene.currentBattle.waveIndex || pokemon.scene.currentBattle.waveIndex % 10 === 0) { return false; } From e825e308f9d24eafa0c6b7a0237b01b62863c631 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 17 Nov 2024 13:22:11 -0800 Subject: [PATCH 13/14] [Test] Update wimp out test and comment (#4900) * Changed conditional to actually consider the wave index. * Added PigeonBar's test * Added check for MEs + Documentation * Apply suggestions from code review Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com> --------- Co-authored-by: frutescens Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com> --- src/data/ability.ts | 1 + src/test/abilities/wimp_out.test.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/data/ability.ts b/src/data/ability.ts index 0572b041c95..1697c816902 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4951,6 +4951,7 @@ class ForceSwitchOutHelper { } /** * For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles). + * It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss */ } else { if (!pokemon.scene.currentBattle.waveIndex || pokemon.scene.currentBattle.waveIndex % 10 === 0) { diff --git a/src/test/abilities/wimp_out.test.ts b/src/test/abilities/wimp_out.test.ts index df965fc340d..4283386b248 100644 --- a/src/test/abilities/wimp_out.test.ts +++ b/src/test/abilities/wimp_out.test.ts @@ -613,4 +613,23 @@ describe("Abilities - Wimp Out", () => { confirmNoSwitch(); }); + + it("should not activate on wave X0 bosses", async () => { + game.override.enemyAbility(Abilities.WIMP_OUT) + .startingLevel(5850) + .startingWave(10); + await game.classicMode.startBattle([ Species.GOLISOPOD ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + // Use 2 turns of False Swipe due to opponent's health bar shield + game.move.select(Moves.FALSE_SWIPE); + await game.toNextTurn(); + game.move.select(Moves.FALSE_SWIPE); + await game.toNextTurn(); + + const isVisible = enemyPokemon.visible; + const hasFled = enemyPokemon.switchOutStatus; + expect(isVisible && !hasFled).toBe(true); + }); }); From 33d8db73efa74dbb7fe79468eea6aefe20cce3d4 Mon Sep 17 00:00:00 2001 From: Moka <54149968+MokaStitcher@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:35:14 +0100 Subject: [PATCH 14/14] [P1] Fix crash caused by removing arena tags on a new catch (#4888) --- src/field/pokemon.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f14fc954c84..1dc4972af79 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3101,10 +3101,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } lapseTag(tagType: BattlerTagType): boolean { - const tags = this.summonData?.tags; - if (isNullOrUndefined(tags)) { + if (!this.summonData) { return false; } + const tags = this.summonData.tags; const tag = tags.find(t => t.tagType === tagType); if (tag && !(tag.lapse(this, BattlerTagLapseType.CUSTOM))) { tag.onRemove(this); @@ -3114,6 +3114,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } lapseTags(lapseType: BattlerTagLapseType): void { + if (!this.summonData) { + return; + } const tags = this.summonData.tags; tags.filter(t => lapseType === BattlerTagLapseType.FAINT || ((t.lapseTypes.some(lType => lType === lapseType)) && !(t.lapse(this, lapseType)))).forEach(t => { t.onRemove(this); @@ -3122,6 +3125,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } removeTag(tagType: BattlerTagType): boolean { + if (!this.summonData) { + return false; + } const tags = this.summonData.tags; const tag = tags.find(t => t.tagType === tagType); if (tag) {