From 958d79140c704e76b0d20165e1130e3bb6e5dff5 Mon Sep 17 00:00:00 2001 From: schmidtc1 <62030095+schmidtc1@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:12:53 -0400 Subject: [PATCH] [P2] Fixes Transform/Imposter not updating type/battle stat changes immediately; set move PP to 5 when transforming (#3462) * Adds updateInfo to transform move/ability, mirrors Transform functionality in Imposter * Implements functionality for reducing pp to 5 or less for each move when transforming * Refactors to async/await pattern, adds back removed anims/sounds from last commit * Eslint fix attempt * Update src/data/ability.ts per DayKev's suggestion Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Merge and fix conflicts * Adds unit tests for pp-change with transform/imposter * Updates to consistency in syntax/deprecated code --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- public/locales | 2 +- src/data/ability.ts | 22 ++++++---- src/data/move.ts | 67 ++++++++++++++++------------- src/test/abilities/imposter.test.ts | 22 +++++++--- src/test/moves/transform.test.ts | 22 +++++++--- 5 files changed, 84 insertions(+), 51 deletions(-) diff --git a/public/locales b/public/locales index fc4a1effd51..3ccef8472dd 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit fc4a1effd5170def3c8314208a52cd0d8e6913ef +Subproject commit 3ccef8472dd7cc7c362538489954cb8fdad27e5f diff --git a/src/data/ability.ts b/src/data/ability.ts index cc95045f8b7..d761657f5cd 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2422,11 +2422,12 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { super(true); } - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + async applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): Promise { const targets = pokemon.getOpponents(); if (simulated || !targets.length) { return simulated; } + const promises: Promise[] = []; let target: Pokemon; if (targets.length > 1) { @@ -2435,7 +2436,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; } - target = target!; // compiler doesn't know its guranteed to be defined + target = target!; pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); pokemon.summonData.ability = target.getAbility().id; @@ -2452,18 +2453,23 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.setStatStage(s, target.getStatStage(s)); } - pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId ?? Moves.NONE, m?.ppUsed, m?.ppUp)); + pokemon.summonData.moveset = target.getMoveset().map(m => { + const pp = m?.getMove().pp ?? 0; + // if PP value is less than 5, do nothing. If greater, we need to reduce the value to 5 using a negative ppUp value. + const ppUp = pp <= 5 ? 0 : (5 - pp) / Math.max(Math.floor(pp / 5), 1); + return new PokemonMove(m?.moveId ?? Moves.NONE, 0, ppUp); + }); pokemon.summonData.types = target.getTypes(); + promises.push(pokemon.updateInfo()); - + pokemon.scene.queueMessage(i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), targetName: target!.name, })); pokemon.scene.playSound("battle_anims/PRSFX- Transform"); - - pokemon.loadAssets(false).then(() => { + promises.push(pokemon.loadAssets(false).then(() => { pokemon.playAnim(); pokemon.updateInfo(); - }); + })); - pokemon.scene.queueMessage(i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), targetName: target.name, })); + await Promise.all(promises); return true; } diff --git a/src/data/move.ts b/src/data/move.ts index cf88fad0ac5..efdd4568927 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6643,42 +6643,49 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { } export class TransformAttr extends MoveEffectAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { - return new Promise(resolve => { - if (!super.apply(user, target, move, args)) { - return resolve(false); - } + async apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + if (!super.apply(user, target, move, args)) { + return false; + } - user.summonData.speciesForm = target.getSpeciesForm(); - user.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); - user.summonData.ability = target.getAbility().id; - user.summonData.gender = target.getGender(); - user.summonData.fusionGender = target.getFusionGender(); + const promises: Promise[] = []; + user.summonData.speciesForm = target.getSpeciesForm(); + user.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); + user.summonData.ability = target.getAbility().id; + user.summonData.gender = target.getGender(); + user.summonData.fusionGender = target.getFusionGender(); - // Power Trick's effect will not preserved after using Transform - user.removeTag(BattlerTagType.POWER_TRICK); + // Power Trick's effect will not preserved after using Transform + user.removeTag(BattlerTagType.POWER_TRICK); - // Copy all stats (except HP) - for (const s of EFFECTIVE_STATS) { - user.setStat(s, target.getStat(s, false), false); - } + // Copy all stats (except HP) + for (const s of EFFECTIVE_STATS) { + user.setStat(s, target.getStat(s, false), false); + } - // Copy all stat stages - for (const s of BATTLE_STATS) { - user.setStatStage(s, target.getStatStage(s)); - } + // Copy all stat stages + for (const s of BATTLE_STATS) { + user.setStatStage(s, target.getStatStage(s)); + } - user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId!, m?.ppUsed, m?.ppUp)); // TODO: is this bang correct? - user.summonData.types = target.getTypes(); - - user.scene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); - - user.loadAssets(false).then(() => { - user.playAnim(); - user.updateInfo(); - resolve(true); - }); + user.summonData.moveset = target.getMoveset().map(m => { + const pp = m?.getMove().pp ?? 0; + // if PP value is less than 5, do nothing. If greater, we need to reduce the value to 5 using a negative ppUp value. + const ppUp = pp <= 5 ? 0 : (5 - pp) / Math.max(Math.floor(pp / 5), 1); + return new PokemonMove(m?.moveId!, 0, ppUp); }); + user.summonData.types = target.getTypes(); + promises.push(user.updateInfo()); + + user.scene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + + promises.push(user.loadAssets(false).then(() => { + user.playAnim(); + user.updateInfo(); + })); + + await Promise.all(promises); + return true; } } diff --git a/src/test/abilities/imposter.test.ts b/src/test/abilities/imposter.test.ts index b7b8e0c5cca..7aaac5ca8c4 100644 --- a/src/test/abilities/imposter.test.ts +++ b/src/test/abilities/imposter.test.ts @@ -36,9 +36,7 @@ describe("Abilities - Imposter", () => { }); it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { - await game.startBattle([ - Species.DITTO - ]); + await game.classicMode.startBattle([ Species.DITTO ]); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to(TurnEndPhase); @@ -78,9 +76,7 @@ describe("Abilities - Imposter", () => { it("should copy in-battle overridden stats", async () => { game.override.enemyMoveset([ Moves.POWER_SPLIT ]); - await game.startBattle([ - Species.DITTO - ]); + await game.classicMode.startBattle([ Species.DITTO ]); const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -97,4 +93,18 @@ describe("Abilities - Imposter", () => { expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); }); + + it("should set each move's pp to a maximum of 5", async () => { + game.override.enemyMoveset([ Moves.SWORDS_DANCE, Moves.GROWL, Moves.SKETCH, Moves.RECOVER ]); + + await game.classicMode.startBattle([ Species.DITTO ]); + const player = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to(TurnEndPhase); + + player.getMoveset().forEach(move => { + expect(move!.getMovePp()).toBeLessThanOrEqual(5); + }); + }); }); diff --git a/src/test/moves/transform.test.ts b/src/test/moves/transform.test.ts index 079fdfa5685..8c0f5eda7b2 100644 --- a/src/test/moves/transform.test.ts +++ b/src/test/moves/transform.test.ts @@ -36,9 +36,7 @@ describe("Moves - Transform", () => { }); it("should copy species, ability, gender, all stats except HP, all stat stages, moveset, and types of target", async () => { - await game.startBattle([ - Species.DITTO - ]); + await game.classicMode.startBattle([ Species.DITTO ]); game.move.select(Moves.TRANSFORM); await game.phaseInterceptor.to(TurnEndPhase); @@ -78,9 +76,7 @@ describe("Moves - Transform", () => { it("should copy in-battle overridden stats", async () => { game.override.enemyMoveset([ Moves.POWER_SPLIT ]); - await game.startBattle([ - Species.DITTO - ]); + await game.classicMode.startBattle([ Species.DITTO ]); const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; @@ -97,4 +93,18 @@ describe("Moves - Transform", () => { expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); }); + + it ("should set each move's pp to a maximum of 5", async () => { + game.override.enemyMoveset([ Moves.SWORDS_DANCE, Moves.GROWL, Moves.SKETCH, Moves.RECOVER ]); + + await game.classicMode.startBattle([ Species.DITTO ]); + const player = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.TRANSFORM); + await game.phaseInterceptor.to(TurnEndPhase); + + player.getMoveset().forEach(move => { + expect(move!.getMovePp()).toBeLessThanOrEqual(5); + }); + }); });