diff --git a/src/data/custom-pokemon-data.ts b/src/data/custom-pokemon-data.ts index 60b390a057c..5b3cf370e54 100644 --- a/src/data/custom-pokemon-data.ts +++ b/src/data/custom-pokemon-data.ts @@ -14,7 +14,7 @@ export class CustomPokemonData { public types: PokemonType[]; constructor(data?: CustomPokemonData | Partial) { - this.spriteScale = data?.spriteScale ?? 1; + this.spriteScale = data?.spriteScale ?? -1; this.ability = data?.ability ?? -1; this.passive = data?.passive ?? -1; this.nature = data?.nature ?? -1; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 950356f04ba..c091e688904 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -650,7 +650,7 @@ export default class Move implements Localizable { break; case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { - const abilityEffectsIgnored = new BooleanHolder(false); + const abilityEffectsIgnored = new BooleanHolder(false); applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; @@ -2663,7 +2663,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { * Attribute that causes targets of the move to eat a berry. Used for Teatime, Stuff Cheeks */ export class EatBerryAttr extends MoveEffectAttr { - protected chosenBerry: BerryModifier | undefined; + protected chosenBerry: BerryModifier; constructor() { super(true, { trigger: MoveEffectTrigger.HIT }); } @@ -2713,13 +2713,13 @@ export class EatBerryAttr extends MoveEffectAttr { eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer) { // consumer eats berry, owner triggers unburden and similar effects - getBerryEffectFunc(this.chosenBerry!.berryType)(consumer); + // These are the same under normal circumstances + getBerryEffectFunc(this.chosenBerry.berryType)(consumer); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner, false); - applyAbAttrs(HealFromBerryUseAbAttr, consumer, new BooleanHolder(false)); // Harvest doesn't track berries eaten by other pokemon - consumer.recordEatenBerry(this.chosenBerry!.berryType, berryOwner !== consumer); + consumer.recordEatenBerry(this.chosenBerry.berryType, berryOwner !== consumer); } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 0900dd992d5..6f6ce78d1b1 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -331,14 +331,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ private summonDataPrimer: PokemonSummonData | null; - /* Pokemon data types, in vague order of precedence */ + /* Pokemon data types, in vaguely decreasing order of precedence */ - /** Data that resets on switch (stat stages, battler tags, etc.) */ + /** + * Data that resets only on *battle* end (hit count, harvest berries, etc.) + * Kept between waves. + */ + public battleData: PokemonBattleData = new PokemonBattleData; + /** Data that resets on switch or battle end (stat stages, battler tags, etc.) */ public summonData: PokemonSummonData = new PokemonSummonData; /** Wave data correponding to moves/ability information revealed */ public waveData: PokemonWaveData = new PokemonWaveData; - /** Data that resets only on battle end (hit count, harvest berries, etc.) */ - public battleData: PokemonBattleData = new PokemonBattleData; /** Per-turn data like hit count & flinch tracking */ public turnData: PokemonTurnData = new PokemonTurnData; @@ -5844,9 +5847,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - Reset a {@linkcode Pokemon}'s {@linkcode PokemonWaveData | waveData}. - Called once per new wave start as well as by {@linkcode resetBattleAndWaveData}. - */ + * Reset a {@linkcode Pokemon}'s {@linkcode PokemonWaveData | waveData}. + * Called once per new wave start as well as by {@linkcode resetBattleAndWaveData}. + */ resetWaveData(): void { this.waveData = new PokemonWaveData(); } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index de4b1461324..022d8a865b3 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -334,8 +334,9 @@ export class EncounterPhase extends BattlePhase { } for (const pokemon of globalScene.getPlayerParty()) { + // Only reset wave data, not battle data if (pokemon) { - pokemon.resetBattleAndWaveData(); + pokemon.resetWaveData(); } } diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index e4d0daad5a9..e3af7b0edfe 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -74,17 +74,12 @@ export default class PokemonData { public customPokemonData: CustomPokemonData; public fusionCustomPokemonData: CustomPokemonData; - // Deprecated attributes, needed for now to allow SessionData migration (see PR#4619 comments). - // TODO: These can probably be safely deleted (what with the upgrade scripts and all) - public natureOverride: Nature | -1; - public mysteryEncounterPokemonData: CustomPokemonData | null; - public fusionMysteryEncounterPokemonData: CustomPokemonData | null; - /** * Construct a new {@linkcode PokemonData} instance out of a {@linkcode Pokemon} * or JSON representation thereof. * @param source The {@linkcode Pokemon} to convert into data (or a JSON object representing one) */ + // TODO: Remove any from type signature constructor(source: Pokemon | any) { const sourcePokemon = source instanceof Pokemon ? source : undefined; this.id = source.id; @@ -141,7 +136,7 @@ export default class PokemonData { this.evoCounter = source.evoCounter ?? 0; this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss); - this.bossSegments = source.bossSegments; + this.bossSegments = source.bossSegments ?? 0; this.status = sourcePokemon?.status ?? (source.status @@ -149,11 +144,11 @@ export default class PokemonData { : null); this.summonData = source.summonData; + this.battleData = source.battleData; this.summonDataSpeciesFormIndex = sourcePokemon ? this.getSummonDataSpeciesFormIndex() : source.summonDataSpeciesFormIndex; - this.battleData = sourcePokemon?.battleData ?? source.battleData; } toPokemon(battleType?: BattleType, partyMemberIndex = 0, double = false): Pokemon { diff --git a/src/system/version_migration/versions/v1_9_0.ts b/src/system/version_migration/versions/v1_9_0.ts index fd9c6e3ee49..06e0e6de221 100644 --- a/src/system/version_migration/versions/v1_9_0.ts +++ b/src/system/version_migration/versions/v1_9_0.ts @@ -14,7 +14,7 @@ import { PokeballType } from "#enums/pokeball"; const migratePartyData: SessionSaveMigrator = { version: "1.9.0", migrate: (data: SessionSaveData): void => { - data.party = data.party.map(pkmnData => { + const mapParty = (pkmnData: PokemonData) => { // this stuff is copied straight from the constructor fwiw pkmnData.moveset = pkmnData.moveset.filter(m => !!m) ?? [ new PokemonMove(Moves.TACKLE), @@ -28,8 +28,11 @@ const migratePartyData: SessionSaveMigrator = { ) { pkmnData.battleData.hitCount = pkmnData.customPokemonData?.["hitsRecCount"]; } - return new PokemonData(pkmnData); - }); + pkmnData = new PokemonData(pkmnData); + }; + + data.party.forEach(mapParty); + data.enemyParty.forEach(mapParty); }, }; diff --git a/test/field/pokemon.test.ts b/test/field/pokemon.test.ts index 60ccb460a3e..d0701037471 100644 --- a/test/field/pokemon.test.ts +++ b/test/field/pokemon.test.ts @@ -213,7 +213,8 @@ describe("Spec - Pokemon", () => { }); }); - it("should be more or less equivalent when converting to and from PokemonData", async () => { + // TODO: Remove/rework after save data overhaul + it("should preserve common fields when converting to and from PokemonData", async () => { await game.classicMode.startBattle([Species.ALAKAZAM]); const alakazam = game.scene.getPlayerPokemon()!; expect(alakazam).toBeDefined(); @@ -233,7 +234,7 @@ describe("Spec - Pokemon", () => { alakaData, ); for (const key of Object.keys(alakazam).filter(k => k in alakaData)) { - expect(alakazam[key]).toEqual(alaka2["key"]); + expect(alakazam[key]).toEqual(alaka2[key]); } }); }); diff --git a/test/moves/rage_fist.test.ts b/test/moves/rage_fist.test.ts index a3f38c3133b..60ddf4b5deb 100644 --- a/test/moves/rage_fist.test.ts +++ b/test/moves/rage_fist.test.ts @@ -72,21 +72,18 @@ describe("Moves - Rage Fist", () => { }); it("should not count subsitute hits or confusion damage", async () => { - game.override.enemySpecies(Species.MAGIKARP).startingWave(4).enemyMoveset([Moves.CONFUSE_RAY, Moves.DOUBLE_KICK]); + game.override.enemySpecies(Species.SHUCKLE).enemyMoveset([Moves.CONFUSE_RAY, Moves.DOUBLE_KICK]); await game.classicMode.startBattle([Species.MAGIKARP]); game.move.select(Moves.SUBSTITUTE); await game.forceEnemyMove(Moves.DOUBLE_KICK); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("BerryPhase"); + await game.toNextTurn(); // no increase due to substitute - expect(move.calculateBattlePower).toHaveLastReturnedWith(50); expect(game.scene.getPlayerPokemon()?.battleData.hitCount).toBe(0); - await game.toNextTurn(); - // remove substitute and get confused game.move.select(Moves.TIDY_UP); await game.forceEnemyMove(Moves.CONFUSE_RAY); @@ -94,8 +91,8 @@ describe("Moves - Rage Fist", () => { await game.toNextTurn(); game.move.select(Moves.RAGE_FIST); - await game.move.forceStatusActivation(true); - await game.forceEnemyMove(Moves.SPLASH); + await game.move.forceConfusionActivation(true); + await game.forceEnemyMove(Moves.DOUBLE_KICK); await game.phaseInterceptor.to("BerryPhase"); // didn't go up @@ -104,7 +101,8 @@ describe("Moves - Rage Fist", () => { await game.toNextTurn(); game.move.select(Moves.RAGE_FIST); - await game.move.forceStatusActivation(false); + await game.forceEnemyMove(Moves.DOUBLE_KICK); + await game.move.forceConfusionActivation(false); await game.toNextTurn(); expect(move.calculateBattlePower).toHaveLastReturnedWith(150);