From 1cd4d2396943b1176630e9c719501de841c72cfe Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:46:41 +0200 Subject: [PATCH] Fixes Z and Max moves dmg reduction against side protection (#6697) --- include/battle.h | 13 +++-- src/battle_script_commands.c | 2 +- test/battle/move_effect/protect.c | 81 ++++++++++++++++++++++++++----- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/include/battle.h b/include/battle.h index 185e565ed2..86b71eb89f 100644 --- a/include/battle.h +++ b/include/battle.h @@ -906,10 +906,6 @@ static inline bool32 IsBattleMoveRecoil(u32 move) } #define IS_BATTLER_PROTECTED(battlerId)(gProtectStructs[battlerId].protected \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_QUICK_GUARD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD \ - || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_MAT_BLOCK \ || gProtectStructs[battlerId].spikyShielded \ || gProtectStructs[battlerId].kingsShielded \ || gProtectStructs[battlerId].banefulBunkered \ @@ -1294,5 +1290,12 @@ static inline bool32 IsBattlerInvalidForSpreadMove(u32 battlerAtk, u32 battlerDe || (battlerDef == BATTLE_PARTNER(battlerAtk) && (moveTarget == MOVE_TARGET_BOTH)); } -#endif // GUARD_BATTLE_H +static inline bool32 IsBattlerSideProtected(u32 battler) +{ + return gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_WIDE_GUARD + | SIDE_STATUS_QUICK_GUARD + | SIDE_STATUS_CRAFTY_SHIELD + | SIDE_STATUS_MAT_BLOCK); +} +#endif // GUARD_BATTLE_H diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 72e05cfb68..87cb89f58d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3893,7 +3893,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } break; case MOVE_EFFECT_FEINT: - if (IS_BATTLER_PROTECTED(gBattlerTarget)) + if (IS_BATTLER_PROTECTED(gBattlerTarget) || IsBattlerSideProtected(gBattlerTarget)) { gProtectStructs[gBattlerTarget].protected = FALSE; gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_WIDE_GUARD; diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 82a29ef81b..b0cd3e424a 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -20,7 +20,7 @@ ASSUMPTIONS ASSUME(!(MoveMakesContact(MOVE_WATER_GUN))); } -SINGLE_BATTLE_TEST("Protect, Detect, Spiky Shield, Baneful Bunker and Burning Bulwark protect from all moves") +SINGLE_BATTLE_TEST("Protect: Protect, Detect, Spiky Shield, Baneful Bunker and Burning Bulwark protect from all moves") { u32 j; static const u16 protectMoves[] = { @@ -59,7 +59,7 @@ SINGLE_BATTLE_TEST("Protect, Detect, Spiky Shield, Baneful Bunker and Burning Bu } } -SINGLE_BATTLE_TEST("King's Shield, Silk Trap and Obstruct protect from damaging moves and lower stats on contact") +SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct protect from damaging moves and lower stats on contact") { u32 j; static const u16 protectMoves[][3] = @@ -125,7 +125,7 @@ SINGLE_BATTLE_TEST("King's Shield, Silk Trap and Obstruct protect from damaging } } -SINGLE_BATTLE_TEST("Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them") +SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them") { u16 usedMove = MOVE_NONE; u16 hp = 400, maxHp = 400; @@ -162,7 +162,7 @@ SINGLE_BATTLE_TEST("Spiky Shield does 1/8 dmg of max hp of attackers making cont } } -SINGLE_BATTLE_TEST("Baneful Bunker poisons pokemon for moves making contact") +SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons pokemon for moves making contact") { u16 usedMove = MOVE_NONE; @@ -194,7 +194,7 @@ SINGLE_BATTLE_TEST("Baneful Bunker poisons pokemon for moves making contact") } } -SINGLE_BATTLE_TEST("Burning Bulwark burns pokemon for moves making contact") +SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns pokemon for moves making contact") { u16 usedMove = MOVE_NONE; @@ -226,7 +226,7 @@ SINGLE_BATTLE_TEST("Burning Bulwark burns pokemon for moves making contact") } } -SINGLE_BATTLE_TEST("Recoil damage is not applied if target was protected") +SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected") { u32 j, k; static const u16 protectMoves[] = { MOVE_PROTECT, MOVE_DETECT, MOVE_KINGS_SHIELD, MOVE_BANEFUL_BUNKER, MOVE_SILK_TRAP, MOVE_OBSTRUCT, MOVE_SPIKY_SHIELD }; @@ -269,7 +269,7 @@ SINGLE_BATTLE_TEST("Recoil damage is not applied if target was protected") } } -SINGLE_BATTLE_TEST("Multi-hit moves don't hit a protected target and fail only once") +SINGLE_BATTLE_TEST("Protect: Multi-hit moves don't hit a protected target and fail only once") { u16 move = MOVE_NONE; @@ -316,7 +316,7 @@ SINGLE_BATTLE_TEST("Multi-hit moves don't hit a protected target and fail only o } } -DOUBLE_BATTLE_TEST("Wide Guard protects self and ally from multi-target moves") +DOUBLE_BATTLE_TEST("Protect: Wide Guard protects self and ally from multi-target moves") { u16 move = MOVE_NONE; @@ -358,7 +358,7 @@ DOUBLE_BATTLE_TEST("Wide Guard protects self and ally from multi-target moves") } } -DOUBLE_BATTLE_TEST("Wide Guard can not fail on consecutive turns") +DOUBLE_BATTLE_TEST("Protect: Wide Guard can not fail on consecutive turns") { u8 turns; @@ -386,7 +386,7 @@ DOUBLE_BATTLE_TEST("Wide Guard can not fail on consecutive turns") } } -DOUBLE_BATTLE_TEST("Quick Guard protects self and ally from priority moves") +DOUBLE_BATTLE_TEST("Protect: Quick Guard protects self and ally from priority moves") { u16 move = MOVE_NONE; struct BattlePokemon *targetOpponent = NULL; @@ -421,7 +421,7 @@ DOUBLE_BATTLE_TEST("Quick Guard protects self and ally from priority moves") } } -DOUBLE_BATTLE_TEST("Quick Guard can not fail on consecutive turns") +DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns") { u8 turns; @@ -446,7 +446,7 @@ DOUBLE_BATTLE_TEST("Quick Guard can not fail on consecutive turns") } } -DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from status moves") +DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves") { u16 move = MOVE_NONE; struct BattlePokemon *targetOpponent = NULL; @@ -487,7 +487,7 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from status moves") } } -SINGLE_BATTLE_TEST("Protect does not block Confide or Decorate") +SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate") { u32 move; PARAMETRIZE { move = MOVE_CONFIDE; } @@ -563,3 +563,58 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all MESSAGE("The opposing Sunflora's Defense rose!"); } } + +SINGLE_BATTLE_TEST("Protect: Quick Guard, Wide Guard and Crafty Shield don't reduce Max Move demage", s16 damage) +{ + s16 dmg[2]; + u32 move; + + PARAMETRIZE { move = MOVE_WIDE_GUARD; } + PARAMETRIZE { move = MOVE_QUICK_GUARD; } + PARAMETRIZE { move = MOVE_CRAFTY_SHIELD; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); } + TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, move); } + } SCENE { + HP_BAR(opponent, captureDamage: &dmg[0]); + HP_BAR(opponent, captureDamage: &dmg[1]); + } FINALLY { + EXPECT_EQ(dmg[0], dmg[1]); + } +} + +SINGLE_BATTLE_TEST("Protect: Quick Guard, Wide Guard and Crafty Shield don't reduce Z-Move demage", s16 damage) +{ + bool32 protected; + u32 move; + + PARAMETRIZE { protected = TRUE; move = MOVE_WIDE_GUARD; } + PARAMETRIZE { protected = FALSE; move = MOVE_WIDE_GUARD; } + + PARAMETRIZE { protected = TRUE; move = MOVE_QUICK_GUARD; } + PARAMETRIZE { protected = FALSE; move = MOVE_QUICK_GUARD; } + + PARAMETRIZE { protected = TRUE; move = MOVE_CRAFTY_SHIELD; } + PARAMETRIZE { protected = FALSE; move = MOVE_CRAFTY_SHIELD; } + + GIVEN { + ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (protected) + TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); MOVE(opponent, move); } + else + TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_EQ(results[2].damage, results[3].damage); + EXPECT_EQ(results[4].damage, results[5].damage); + } +}