diff --git a/include/battle.h b/include/battle.h index 79cc591980..cd3126ba6b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -169,7 +169,8 @@ struct ProtectStruct u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy u16 usedAllySwitch:1; u16 lashOutAffected:1; - u16 padding:4; + u16 assuranceDoubled:1; + u16 padding:3; // End of 16-bit bitfield u16 physicalDmg; u16 specialDmg; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0f3fbdba61..870f21d7a8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2272,6 +2272,9 @@ static void Cmd_adjustdamage(void) gBattleStruct->moveDamage[battlerDef] = gBattleMons[battlerDef].hp - 1; gSpecialStatuses[battlerDef].enduredDamage = TRUE; } + + if (gSpecialStatuses[battlerDef].enduredDamage) + gProtectStructs[battlerDef].assuranceDoubled = TRUE; } if (calcSpreadMoveDamage) @@ -2726,6 +2729,7 @@ static void Cmd_datahpupdate(void) gProtectStructs[battler].physicalBattlerId = gBattlerAttacker; else gProtectStructs[battler].physicalBattlerId = gBattlerTarget; + gProtectStructs[battler].assuranceDoubled = TRUE; } else if (!IsBattleMovePhysical(gCurrentMove) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE) && effect != EFFECT_PAIN_SPLIT) { @@ -2736,6 +2740,7 @@ static void Cmd_datahpupdate(void) gProtectStructs[battler].specialBattlerId = gBattlerAttacker; else gProtectStructs[battler].specialBattlerId = gBattlerTarget; + gProtectStructs[battler].assuranceDoubled = TRUE; } } gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE; @@ -3722,7 +3727,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (gBattleMons[gEffectBattler].statStages[i] != DEFAULT_STAT_STAGE) break; } - if ((gSpecialStatuses[gEffectBattler].physicalDmg || gSpecialStatuses[gEffectBattler].specialDmg) && i != NUM_BATTLE_STATS) + if (IsBattlerTurnDamaged(gEffectBattler) && i != NUM_BATTLE_STATS) { for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[gEffectBattler].statStages[i] = DEFAULT_STAT_STAGE; diff --git a/src/battle_util.c b/src/battle_util.c index 31c5229d0f..f8fb5faa86 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7155,10 +7155,8 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn) case HOLD_EFFECT_LIFE_ORB: if (IsBattlerAlive(gBattlerAttacker) && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && !IsBattleMoveStatus(gCurrentMove) - && (IsBattlerTurnDamaged(gBattlerTarget) || !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) // Needs the second check in case of Substitute + && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleScripting.savedDmg > 0) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD - && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && !IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget, gCurrentMove)) { gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 10; @@ -8157,7 +8155,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData basePower = gBattleMons[battlerDef].hp * basePower / gBattleMons[battlerDef].maxHP; break; case EFFECT_ASSURANCE: - if (gProtectStructs[battlerDef].physicalDmg != 0 || gProtectStructs[battlerDef].specialDmg != 0 || gProtectStructs[battlerDef].confusionSelfDmg) + if (gProtectStructs[battlerDef].assuranceDoubled) basePower *= 2; break; case EFFECT_TRUMP_CARD: diff --git a/test/battle/hold_effect/life_orb.c b/test/battle/hold_effect/life_orb.c index 92c545dfad..2f1514538c 100644 --- a/test/battle/hold_effect/life_orb.c +++ b/test/battle/hold_effect/life_orb.c @@ -81,3 +81,53 @@ SINGLE_BATTLE_TEST("Life Orb doesn't cause any HP loss if user is unable to atta } } } + +SINGLE_BATTLE_TEST("Life Orb does not activate if on a confusion hit") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND, WITH_RNG(RNG_CONFUSION, TRUE)); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent); + HP_BAR(player); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player); + HP_BAR(opponent); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } + } +} + +SINGLE_BATTLE_TEST("Life Orb does not activate if move was absorbed by target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(player, MOVE_SHOCK_WAVE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, player); + HP_BAR(opponent); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } + } +} + +SINGLE_BATTLE_TEST("Life Orb activates if move connected but no damage was dealt") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_FALSE_SWIPE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, player); + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + } +} diff --git a/test/battle/move_effect/assurance.c b/test/battle/move_effect/assurance.c index e6cbf72b9f..6eccb48f3f 100644 --- a/test/battle/move_effect/assurance.c +++ b/test/battle/move_effect/assurance.c @@ -6,3 +6,30 @@ TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Crash"); TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Confusion"); TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Rocky Helmet"); + +DOUBLE_BATTLE_TEST("Assurance doubles in power if False Swipe connected but didn't do any damage") +{ + s16 hits[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_ASSURANCE, target: playerRight); } + TURN { + MOVE(opponentLeft, MOVE_FALSE_SWIPE, target: playerLeft); + MOVE(playerLeft, MOVE_RECOVER); + MOVE(opponentRight, MOVE_ASSURANCE, target: playerLeft); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSURANCE, opponentLeft); + HP_BAR(playerRight, captureDamage: &hits[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSURANCE, opponentRight); + HP_BAR(playerLeft, captureDamage: &hits[1]); + } THEN { + EXPECT_MUL_EQ(hits[0], Q_4_12(2.0), hits[1]); + } +} diff --git a/test/battle/move_effect_secondary/clear_smog.c b/test/battle/move_effect_secondary/clear_smog.c new file mode 100644 index 0000000000..f8d7b0eb2f --- /dev/null +++ b/test/battle/move_effect_secondary/clear_smog.c @@ -0,0 +1,19 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Clear Smog removes stat changes even if it did no damage") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SWORDS_DANCE); } + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_CLEAR_SMOG); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CLEAR_SMOG, opponent); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } +}