From 5fad32deccffbaaa42a395d0834e2972ce40e80f Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:00:34 +0100 Subject: [PATCH] Fixes Helping Hand boosts not stacking with each other (#7775) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/battle.h | 6 +- src/battle_main.c | 2 +- src/battle_script_commands.c | 5 +- src/battle_util.c | 3 +- test/battle/move_effect/helping_hand.c | 121 +++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 8 deletions(-) diff --git a/include/battle.h b/include/battle.h index 393da86ad1..13316eada9 100644 --- a/include/battle.h +++ b/include/battle.h @@ -141,7 +141,6 @@ struct ProtectStruct { u32 protected:7; // 126 protect options u32 noValidMoves:1; - u32 helpingHand:1; u32 bounceMove:1; u32 stealMove:1; u32 nonVolatileStatusImmobility:1; @@ -163,13 +162,14 @@ struct ProtectStruct u32 shellTrap:1; u32 eatMirrorHerb:1; u32 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy - // End of 32-bit bitfield u16 usedAllySwitch:1; + // End of 32-bit bitfield + u32 helpingHand:3; u16 lashOutAffected:1; u16 assuranceDoubled:1; u16 myceliumMight:1; u16 laggingTail:1; - u16 padding:11; + u16 padding:9; // End of 16-bit bitfield u16 physicalDmg; u16 specialDmg; diff --git a/src/battle_main.c b/src/battle_main.c index f51b063ba6..c39ea52905 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3325,7 +3325,7 @@ const u8* FaintClearSetData(u32 battler) gProtectStructs[battler].quash = FALSE; gProtectStructs[battler].noValidMoves = FALSE; - gProtectStructs[battler].helpingHand = FALSE; + gProtectStructs[battler].helpingHand = 0; gProtectStructs[battler].bounceMove = FALSE; gProtectStructs[battler].stealMove = FALSE; gProtectStructs[battler].nonVolatileStatusImmobility = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 56aeb7fee7..03c9d028fd 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12760,10 +12760,9 @@ static void Cmd_trysethelpinghand(void) if (IsDoubleBattle() && !(gAbsentBattlerFlags & (1u << gBattlerTarget)) - && !gProtectStructs[gBattlerAttacker].helpingHand - && !gProtectStructs[gBattlerTarget].helpingHand) + && gCurrentTurnActionNumber < GetBattlerTurnOrderNum(gBattlerTarget)) { - gProtectStructs[gBattlerTarget].helpingHand = TRUE; + gProtectStructs[gBattlerTarget].helpingHand++; gBattlescriptCurrInstr = cmd->nextInstr; } else diff --git a/src/battle_util.c b/src/battle_util.c index f5f40d4496..6d1f4efa4f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8329,8 +8329,9 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) } // various effects - if (gProtectStructs[battlerAtk].helpingHand) + for (u32 i = 0; i < gProtectStructs[battlerAtk].helpingHand; i++) modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + if (gSpecialStatuses[battlerAtk].gemBoost) modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(gSpecialStatuses[battlerAtk].gemParam))); if (gBattleMons[battlerAtk].volatiles.charge && moveType == TYPE_ELECTRIC) diff --git a/test/battle/move_effect/helping_hand.c b/test/battle/move_effect/helping_hand.c index 81d64e113d..6d4d6e0eb5 100644 --- a/test/battle/move_effect/helping_hand.c +++ b/test/battle/move_effect/helping_hand.c @@ -1,4 +1,125 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_HELPING_HAND) == EFFECT_HELPING_HAND); +} + +SINGLE_BATTLE_TEST("Helping Hand fails in a Single Battle") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HELPING_HAND); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HELPING_HAND, player); + MESSAGE("But it failed!"); + } +} + +DOUBLE_BATTLE_TEST("Helping Hand fails if ally already acted") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_HELPING_HAND, target: playerRight); MOVE(playerRight, MOVE_HELPING_HAND, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HELPING_HAND, playerLeft); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HELPING_HAND, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Helping Hand boosts the power of attacking moves by 50%", s16 damage) +{ + bool32 useHelpingHand; + + PARAMETRIZE { useHelpingHand = FALSE; } + PARAMETRIZE { useHelpingHand = TRUE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useHelpingHand) + TURN { MOVE(playerRight, MOVE_HELPING_HAND, target: playerLeft); MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + else + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Helping Hand still boosts moves used due to Instruct", s16 damage) +{ + bool32 useHelpingHand; + + PARAMETRIZE { useHelpingHand = FALSE; } + PARAMETRIZE { useHelpingHand = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useHelpingHand) + { + TURN { MOVE(playerRight, MOVE_HELPING_HAND, target: playerLeft); + MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); + MOVE(opponentLeft, MOVE_INSTRUCT, target: playerLeft); } + } + else + { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); + MOVE(opponentLeft, MOVE_INSTRUCT, target: playerLeft); } + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_INSTRUCT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Helping Hand boosts the power of attacking moves by 125% if Instructed into using it again", s16 damage) +{ + bool32 useHelpingHandTwice; + + PARAMETRIZE { useHelpingHandTwice = FALSE; } + PARAMETRIZE { useHelpingHandTwice = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_INSTRUCT) == EFFECT_INSTRUCT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useHelpingHandTwice) + TURN { MOVE(playerRight, MOVE_HELPING_HAND, target: playerLeft); + MOVE(opponentLeft, MOVE_INSTRUCT, target: playerRight); + MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + else + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.25), results[1].damage); + } +} + TO_DO_BATTLE_TEST("TODO: Write Helping Hand (Move Effect) test titles")