From 572556b7b9f89fce606abeabc04088886373dcac Mon Sep 17 00:00:00 2001 From: MascaChapas27 <127435083+MascaChapas27@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:59:19 +0100 Subject: [PATCH 1/2] [UI/UX] Changed wrong colors in german type icons (#5494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ángel Co-authored-by: damocleas --- public/images/types_de.png | Bin 2409 -> 2624 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 public/images/types_de.png diff --git a/public/images/types_de.png b/public/images/types_de.png old mode 100644 new mode 100755 index 62ad3a332bc9855661b829836f686d8e1a7442c4..a033554029cda867d0e68f5056f7b0eaf4d83bbc GIT binary patch delta 2161 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 DW!1hX delta 1944 zcmV;J2WR-e6zLL>ngdrY9+8?Mk-oYF2rAwMlY9Yvf5QL(4#NS*Z>VGd00&M7>q$t6vaWEj7}opBM}5an2DOi{{Md=dskII&gFKK zS=n^&uCD&N>YRJlLZWnq<5OLobPmGTQ>Q|hs4Xc2VPYcSFPUct07fy-R=@<)#XMU- z1CMHRe^r1ci@63B2Ii_OFkQ@50pleVJWdl5@md{4iKn}Ya(c3>)8fJ8WSHVOCI$3t zYHD(7DnQv3FTmul!ql(HO(j#p0jr^_@};8z3^k*Li)BD7gx_$*qa;(p0j;j4TeBL# zNQN!T4*-3`WZbHlcAzIA)zOqhFW3;i=9#Xdf1LTMt25$3QyB=;7|AM)06hNx07E>b z`o#bl0st+a0p8^qi?GZw;H&BBX-qCIOBbLa89dQ(O-uBG4dH8^?JCOIue&-c9yBFi z}R;BGqjd(04+nZah%2+TkV=%z9v7Fi43cppz XO>F{rLq17j_}MU_V7wACJh ze@8+k9eGV#hA00}+NiA=iC(KAe9d!RMLGA|uFi=EO&P#fo(;1(;{PW-$iOUQfJS40 zq49ql^H6Rv>t`DAAD|x8Em8$W$19As0KrmFZh>bnrvG$nfRA2@|7E!rpd(SNtyzg$ zv?+Ye^Ib(b|GTcviw8{=fWN?nVxFyme+y7C&klfrB@J3!s4QvNqOzo4(n8NzUd@s< zpk?{|CQH^pLsIt}qYDzfWJCCx7rTmb@%LR_6c3tGAY9Vn%O(5_+|g?wT*A<_MVFug z^K)0|3yp2WeHE=gp`f41;7 zFLxE?@;6;w77v6v*2Qpky)vzE$FX zY@rzzox`l&CjqEPV`ZO|r4{H&NOg2Yq8DrkU-N2LQLg@>tE=KcQx5Re=fgasc^w*4 z37CfpfNdf&P?^>mt-zW!kb6`ue+IzGEyepL?dqC%(3ArqEMUSp({6z$284wLoGnlq`lx|#aYqxpj9uDhAXV4X6i_2A zEYIC2fMFSip3ox$Sd=ZSkM4#5it@o+y5n~BdA+MBf7k!m)phZp zsRG~^SfqtTtVI>Dh!YqWPf<01p=T`s#TTtlvjxx8JVgy!)Rr_B&_=ETpmsJkauuK< z$>?}3O4Nc);cMRLD$0#Nb#+5LXi5RR_Z)rBBK{6@T#Wz1JimHpoI%y zTl%m?2GC&?C}(DVDFcA@f3dVW&Cvw#snso_*_wc!gj7d!61`wU_?kDnigNSMUELH9 znkoR_eu)#&lINfbSfb!MJUIm1F_y*xTA8Pv7FOrP7AaN@TG(5##W+A;f@0)3r~nN~ z1&`B`M625pzUHm2qTIUO)h+R$DY+e%b?Efix62NMW&UL&=Gy^)e+zEa)&N}cj2G5u zEKpsx$bpg`H3u+QQvmSxtMt|TVU?zV4on4Bk$201!dw;5 z-Tj1UU?~i5ft)9?XsQNqd|$8GP!6L4ct_VXie-Q;G@*f>SqcCf(6x^RX%5P2TeH;!WyQzMr)Q&Kv-J~_=7a!f6zRXfQPupBqI+^2|!UT z%s{cEkroeeAE#pg7GXB{Mjw_kfQ8vY-JW3xpfDfKom*QW2ig+jnp%^nm7BuXeAHEx zN8fezNIYoDfDqO()i60=CI!Me5_yaTzFYU3b~XyEv$CGDkJ$jgeZ7W(AoVd>3%l+k znJKpz1=uG8e`tXXrVcl*8R$q-uZGqodbx)1H6M2sI@2WBz;3QsvR z5T0TTj0;8q%rvKlQp{z5_24-57H)$U=%PjzsMzO5fu!UaHl{abS5&#tAGyD2P zjR{);oFHMhj-E>Nat+~Yeoy_y^@HizvuDqrKmQSmfAalbRev+RP;G2%Y;JCDQ9sH{ z0}_(Y^%#;mF+zR0>CQQPsS2|vY*RS6gu)Vbv-o6cQ!}fOA z2JrF~N^qc0c=KWd*S5m;X4nZk@4}AkhOoQ0XKi3_HhB1s{dPFt$=>cR2p#X=?}y!k z58?3Oejx1NzM6!+{eS}<`vmvH(T4*-;m}&V4}mu^pr0_&GaLZ|KuY%C;~ciP`xCXh zi%y3>|9te}=;-M1;1GanZ||R~Jqr7eA3uIj9mpp=_HzSj; Date: Mon, 10 Mar 2025 20:02:51 -0700 Subject: [PATCH 2/2] [Bug] Fix #5358 Abilities that Redirect Moves Consider Move-Typings before Ability Modifiers (#5464) --- src/data/ability.ts | 18 ++++- src/phases/move-phase.ts | 2 +- test/abilities/lightningrod.test.ts | 115 ++++++++++++++++++++++++++++ test/abilities/storm_drain.test.ts | 115 ++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 test/abilities/lightningrod.test.ts create mode 100644 test/abilities/storm_drain.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 0b4e5ddb2c4..25ffa797140 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4571,9 +4571,19 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { } } +/** + * Redirects a move to the pokemon with this ability if it meets the conditions + */ export class RedirectMoveAbAttr extends AbAttr { + /** + * @param pokemon - The Pokemon with the redirection ability + * @param args - The args passed to the `AbAttr`: + * - `[0]` - The id of the {@linkcode Move} used + * - `[1]` - The target's battler index (before redirection) + * - `[2]` - The Pokemon that used the move being redirected + */ apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.canRedirect(args[0] as Moves)) { + if (this.canRedirect(args[0] as Moves, args[2] as Pokemon)) { const target = args[1] as Utils.NumberHolder; const newTarget = pokemon.getBattlerIndex(); if (target.value !== newTarget) { @@ -4585,7 +4595,7 @@ export class RedirectMoveAbAttr extends AbAttr { return false; } - canRedirect(moveId: Moves): boolean { + canRedirect(moveId: Moves, user: Pokemon): boolean { const move = allMoves[moveId]; return !![ MoveTarget.NEAR_OTHER, MoveTarget.OTHER ].find(t => move.moveTarget === t); } @@ -4599,8 +4609,8 @@ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { this.type = type; } - canRedirect(moveId: Moves): boolean { - return super.canRedirect(moveId) && allMoves[moveId].type === this.type; + canRedirect(moveId: Moves, user: Pokemon): boolean { + return super.canRedirect(moveId, user) && user.getMoveType(allMoves[moveId]) === this.type; } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 16802f8e0ff..f8edaa56981 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -504,7 +504,7 @@ export class MovePhase extends BattlePhase { globalScene .getField(true) .filter(p => p !== this.pokemon) - .forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget)); + .forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget, this.pokemon)); /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ let redirectedByAbility = currentTarget !== redirectTarget.value; diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts new file mode 100644 index 00000000000..1ca6c6b1e89 --- /dev/null +++ b/test/abilities/lightningrod.test.ts @@ -0,0 +1,115 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Lightningrod", () => { + 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.SPLASH, Moves.SHOCK_WAVE ]) + .ability(Abilities.BALL_FETCH) + .battleType("double") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should redirect electric type moves", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.LIGHTNING_ROD; + + game.move.select(Moves.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(true); + }); + + it("should not redirect non-electric type moves", async () => { + game.override.moveset([ Moves.SPLASH, Moves.AERIAL_ACE ]); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.LIGHTNING_ROD; + + game.move.select(Moves.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(false); + }); + + it("should boost the user's spatk without damaging", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.LIGHTNING_ROD; + + game.move.select(Moves.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy2.isFullHp()).toBe(true); + expect(enemy2.getStatStage(Stat.SPATK)).toBe(1); + }); + + it("should not redirect moves changed from electric type via ability", async () => { + game.override.ability(Abilities.NORMALIZE); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.LIGHTNING_ROD; + + game.move.select(Moves.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(false); + }); + + it("should redirect moves changed to electric type via ability", async () => { + game.override.ability(Abilities.GALVANIZE) + .moveset(Moves.TACKLE); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.LIGHTNING_ROD; + + game.move.select(Moves.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(true); + expect(enemy2.getStatStage(Stat.SPATK)).toBe(1); + }); +}); diff --git a/test/abilities/storm_drain.test.ts b/test/abilities/storm_drain.test.ts new file mode 100644 index 00000000000..e2a7b3e212e --- /dev/null +++ b/test/abilities/storm_drain.test.ts @@ -0,0 +1,115 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Storm Drain", () => { + 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.SPLASH, Moves.WATER_GUN ]) + .ability(Abilities.BALL_FETCH) + .battleType("double") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should redirect water type moves", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.STORM_DRAIN; + + game.move.select(Moves.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(true); + }); + + it("should not redirect non-water type moves", async () => { + game.override.moveset([ Moves.SPLASH, Moves.AERIAL_ACE ]); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.STORM_DRAIN; + + game.move.select(Moves.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(false); + }); + + it("should boost the user's spatk without damaging", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.STORM_DRAIN; + + game.move.select(Moves.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy2.isFullHp()).toBe(true); + expect(enemy2.getStatStage(Stat.SPATK)).toBe(1); + }); + + it("should not redirect moves changed from water type via ability", async () => { + game.override.ability(Abilities.NORMALIZE); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.STORM_DRAIN; + + game.move.select(Moves.WATER_GUN, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(false); + }); + + it("should redirect moves changed to water type via ability", async () => { + game.override.ability(Abilities.LIQUID_VOICE) + .moveset(Moves.PSYCHIC_NOISE); + await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); + + const enemy1 = game.scene.getEnemyField()[0]; + const enemy2 = game.scene.getEnemyField()[1]; + + enemy2.summonData.ability = Abilities.STORM_DRAIN; + + game.move.select(Moves.PSYCHIC_NOISE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFullHp()).toBe(true); + expect(enemy2.getStatStage(Stat.SPATK)).toBe(1); + }); +});