From dc742b30778f016ee0e48ded976a6d795e810857 Mon Sep 17 00:00:00 2001 From: sneed <56992013+Sneed69@users.noreply.github.com> Date: Fri, 7 Jun 2024 22:54:25 +0300 Subject: [PATCH] Fix Berserk, Angel Shell, Wimp Out, Emergency Exit HP threshold (#4724) * Fix HadMoreThanHalfHpNowHasLess * rename the functions * formatting --- src/battle_util.c | 14 +++--- test/battle/ability/anger_shell.c | 18 ++++---- test/battle/ability/berserk.c | 75 +++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 test/battle/ability/berserk.c diff --git a/src/battle_util.c b/src/battle_util.c index 68cf7a1cef5b..8023bdb539e4 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3996,14 +3996,12 @@ static inline uq4_12_t GetSupremeOverlordModifier(u32 battler) return UQ_4_12(1.0) + (UQ_4_12(0.1) * gBattleStruct->supremeOverlordCounter[battler]); } -static inline bool32 HadMoreThanHalfHpNowHasLess(u32 battler) +static inline bool32 HadMoreThanHalfHpNowDoesnt(u32 battler) { u32 cutoff = gBattleMons[battler].maxHP / 2; - if (gBattleMons[battler].maxHP % 2 == 1) - cutoff++; // Had more than half of hp before, now has less - return (gBattleStruct->hpBefore[battler] >= cutoff - && gBattleMons[battler].hp < cutoff); + return (gBattleStruct->hpBefore[battler] > cutoff + && gBattleMons[battler].hp <= cutoff); } u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) @@ -5242,7 +5240,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - && HadMoreThanHalfHpNowHasLess(battler) + && HadMoreThanHalfHpNowDoesnt(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -5260,7 +5258,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) // Had more than half of hp before, now has less - && HadMoreThanHalfHpNowHasLess(battler) + && HadMoreThanHalfHpNowDoesnt(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) @@ -5700,7 +5698,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && TARGET_TURN_DAMAGED && (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move. && IsBattlerAlive(gBattlerTarget) - && HadMoreThanHalfHpNowHasLess(gBattlerTarget) + && HadMoreThanHalfHpNowDoesnt(gBattlerTarget) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))) { gBattlerAttacker = gBattlerTarget; diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c index 3591c4077f26..f0d11d757649 100644 --- a/test/battle/ability/anger_shell.c +++ b/test/battle/ability/anger_shell.c @@ -6,6 +6,7 @@ SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% o bool32 activates = FALSE; u16 maxHp = 500, hp = 0; + PARAMETRIZE { hp = 250; activates = FALSE; } PARAMETRIZE { hp = 249; activates = FALSE; } PARAMETRIZE { hp = 100; activates = FALSE; } PARAMETRIZE { hp = 50; activates = FALSE; } @@ -41,7 +42,7 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd u16 maxHp = 500; GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_TACKLE); } @@ -49,15 +50,15 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); ABILITY_POPUP(player, ABILITY_ANGER_SHELL); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Defense fell!"); + MESSAGE("Klawf's Defense fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Def fell!"); + MESSAGE("Klawf's Sp. Def fell!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Klawf's Attack rose!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); + MESSAGE("Klawf's Sp. Atk rose!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Speed rose!"); + MESSAGE("Klawf's Speed rose!"); } THEN { EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); @@ -73,13 +74,12 @@ SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move") u16 maxHp = 500; GIVEN { ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. } WHEN { TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } } SCENE { - for (j = 0; j < 4; j++) - { + for (j = 0; j < 4; j++) { ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); } diff --git a/test/battle/ability/berserk.c b/test/battle/ability/berserk.c new file mode 100644 index 000000000000..3bf269e1eef1 --- /dev/null +++ b/test/battle/ability/berserk.c @@ -0,0 +1,75 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Berserk activates only if the target had more than 50% of its hp") +{ + bool32 activates = FALSE; + u16 maxHp = 500, hp = 0; + + PARAMETRIZE { hp = 250; activates = FALSE; } + PARAMETRIZE { hp = 249; activates = FALSE; } + PARAMETRIZE { hp = 100; activates = FALSE; } + PARAMETRIZE { hp = 50; activates = FALSE; } + PARAMETRIZE { hp = 251; activates = TRUE; } + PARAMETRIZE { hp = 254; activates = TRUE; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(hp); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + if (activates) { + ABILITY_POPUP(player, ABILITY_BERSERK); + } else { + NOT ABILITY_POPUP(player, ABILITY_BERSERK); + } + } THEN { + if (activates) { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } + } +} + +SINGLE_BATTLE_TEST("Berserk raises Sp.Atk by 1") +{ + u16 maxHp = 500; + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_BERSERK); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Drampa's Sp. Atk rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Berserk activates after all hits from a multi-hit move") +{ + u32 j; + u16 maxHp = 500; + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. + } WHEN { + TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } + } SCENE { + for (j = 0; j < 4; j++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + NOT ABILITY_POPUP(player, ABILITY_BERSERK); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + ABILITY_POPUP(player, ABILITY_BERSERK); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } +}