From 628eb959250fac8d42e793ea6ea1736fd613f106 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 10 Dec 2023 12:33:25 +0100 Subject: [PATCH] Adds Rage Fist (#3573) * Adds Rage Fist * Fix initial implementation * fix merge * review changes * endure test * add field enduredDamage * rage fist: transform, disguise and field change * merge fix --- asm/macros/battle_script.inc | 10 +- data/battle_scripts_1.s | 1 + include/battle.h | 4 +- include/constants/battle_move_effects.h | 3 +- include/constants/battle_script_commands.h | 242 ++++++++--------- src/battle_script_commands.c | 59 +++-- src/battle_util.c | 4 + src/data/battle_moves.h | 2 +- test/battle/move_effect/rage_fist.c | 294 +++++++++++++++++++++ 9 files changed, 471 insertions(+), 148 deletions(-) create mode 100644 test/battle/move_effect/rage_fist.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index f6d5dc8fee..7592fce313 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1391,6 +1391,11 @@ .4byte \sidestatus .endm + .macro trycopycat failInstr:req + callnative BS_TryCopycat + .4byte \failInstr + .endm + .macro setzeffect callnative BS_SetZEffect .endm @@ -1805,11 +1810,6 @@ .4byte \jumpInstr .endm - .macro trycopycat failInstr:req - various BS_ATTACKER, VARIOUS_TRY_COPYCAT - .4byte \failInstr - .endm - .macro showabilitypopup battler:req various \battler, VARIOUS_ABILITY_POPUP .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index bed903cab2..81902fb756 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -443,6 +443,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectMaxMove @ EFFECT_MAX_MOVE .4byte BattleScript_EffectGlaiveRush @ EFFECT_GLAIVE_RUSH .4byte BattleScript_EffectBrickBreak @ EFFECT_RAGING_BULL + .4byte BattleScript_EffectHit @ EFFECT_RAGE_FIST BattleScript_EffectGlaiveRush:: call BattleScript_EffectHit_Ret diff --git a/include/battle.h b/include/battle.h index 9d83e0d9f8..99a9465cb3 100644 --- a/include/battle.h +++ b/include/battle.h @@ -732,6 +732,8 @@ struct BattleStruct bool8 transformZeroToHero[PARTY_SIZE][NUM_BATTLE_SIDES]; u8 pledgeMove:1; bool8 isSkyBattle:1; + u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE]; + u8 enduredDamage; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, @@ -764,7 +766,7 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER #define IS_MOVE_RECOIL(move)(IS_EFFECT_RECOIL(gBattleMoves[move].effect)) #define BATTLER_MAX_HP(battlerId)(gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP) -#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0)) +#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[gBattlerTarget])) #define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0)) #define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 19118ca4e9..47edeb55bf 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -420,7 +420,8 @@ #define EFFECT_MAX_MOVE 414 #define EFFECT_GLAIVE_RUSH 415 #define EFFECT_RAGING_BULL 416 +#define EFFECT_RAGE_FIST 417 -#define NUM_BATTLE_MOVE_EFFECTS 417 +#define NUM_BATTLE_MOVE_EFFECTS 418 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 5474e3b242..eabc965e57 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -143,106 +143,105 @@ #define VARIOUS_ARGUMENT_STATUS_EFFECT 51 #define VARIOUS_TRY_HIT_SWITCH_TARGET 52 #define VARIOUS_TRY_AUTOTOMIZE 53 -#define VARIOUS_TRY_COPYCAT 54 -#define VARIOUS_ABILITY_POPUP 55 -#define VARIOUS_DEFOG 56 -#define VARIOUS_JUMP_IF_TARGET_ALLY 57 -#define VARIOUS_TRY_SYNCHRONOISE 58 -#define VARIOUS_PSYCHO_SHIFT 59 -#define VARIOUS_CURE_STATUS 60 -#define VARIOUS_POWER_TRICK 61 -#define VARIOUS_AFTER_YOU 62 -#define VARIOUS_BESTOW 63 -#define VARIOUS_ARGUMENT_TO_MOVE_EFFECT 64 -#define VARIOUS_JUMP_IF_NOT_GROUNDED 65 -#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 66 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 67 -#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 68 -#define VARIOUS_SET_AURORA_VEIL 69 -#define VARIOUS_TRY_THIRD_TYPE 70 -#define VARIOUS_ACUPRESSURE 71 -#define VARIOUS_SET_POWDER 72 -#define VARIOUS_SPECTRAL_THIEF 73 -#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 74 -#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 75 -#define VARIOUS_JUMP_IF_ROAR_FAILS 76 -#define VARIOUS_TRY_INSTRUCT 77 -#define VARIOUS_JUMP_IF_NOT_BERRY 78 -#define VARIOUS_TRACE_ABILITY 79 -#define VARIOUS_UPDATE_NICK 80 -#define VARIOUS_TRY_ILLUSION_OFF 81 -#define VARIOUS_SET_SPRITEIGNORE0HP 82 -#define VARIOUS_HANDLE_FORM_CHANGE 83 -#define VARIOUS_GET_STAT_VALUE 84 -#define VARIOUS_JUMP_IF_FULL_HP 85 -#define VARIOUS_LOSE_TYPE 86 -#define VARIOUS_TRY_ACTIVATE_SOULHEART 87 -#define VARIOUS_TRY_ACTIVATE_RECEIVER 88 -#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 89 -#define VARIOUS_TRY_FRISK 90 -#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 91 -#define VARIOUS_TRY_FAIRY_LOCK 92 -#define VARIOUS_JUMP_IF_NO_ALLY 93 -#define VARIOUS_POISON_TYPE_IMMUNITY 94 -#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 95 -#define VARIOUS_INFATUATE_WITH_BATTLER 96 -#define VARIOUS_SET_LAST_USED_ITEM 97 -#define VARIOUS_PARALYZE_TYPE_IMMUNITY 98 -#define VARIOUS_JUMP_IF_ABSENT 99 -#define VARIOUS_DESTROY_ABILITY_POPUP 100 -#define VARIOUS_TOTEM_BOOST 101 -#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 102 -#define VARIOUS_MOVEEND_ITEM_EFFECTS 103 -#define VARIOUS_TERRAIN_SEED 104 -#define VARIOUS_MAKE_INVISIBLE 105 -#define VARIOUS_ROOM_SERVICE 106 -#define VARIOUS_EERIE_SPELL_PP_REDUCE 107 -#define VARIOUS_JUMP_IF_TEAM_HEALTHY 108 -#define VARIOUS_TRY_HEAL_QUARTER_HP 109 -#define VARIOUS_REMOVE_TERRAIN 110 -#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 111 -#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 112 -#define VARIOUS_GET_ROTOTILLER_TARGETS 113 -#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 114 -#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 115 -#define VARIOUS_CONSUME_BERRY 116 -#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 117 -#define VARIOUS_JUMP_IF_SPECIES 118 -#define VARIOUS_UPDATE_ABILITY_POPUP 119 -#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 120 -#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 121 -#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 122 -#define VARIOUS_PHOTON_GEYSER_CHECK 123 -#define VARIOUS_SHELL_SIDE_ARM_CHECK 124 -#define VARIOUS_TRY_NO_RETREAT 125 -#define VARIOUS_TRY_TAR_SHOT 126 -#define VARIOUS_CAN_TAR_SHOT_WORK 127 -#define VARIOUS_CHECK_POLTERGEIST 128 -#define VARIOUS_CUT_1_3_HP_RAISE_STATS 129 -#define VARIOUS_TRY_END_NEUTRALIZING_GAS 130 -#define VARIOUS_JUMP_IF_UNDER_200 131 -#define VARIOUS_SET_SKY_DROP 132 -#define VARIOUS_CLEAR_SKY_DROP 133 -#define VARIOUS_SKY_DROP_YAWN 134 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 135 -#define VARIOUS_CURE_CERTAIN_STATUSES 136 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 137 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 138 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 139 -#define VARIOUS_SAVE_BATTLER_ITEM 140 -#define VARIOUS_RESTORE_BATTLER_ITEM 141 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 142 -#define VARIOUS_SET_BEAK_BLAST 143 -#define VARIOUS_SWAP_SIDE_STATUSES 144 -#define VARIOUS_SWAP_STATS 145 -#define VARIOUS_TEATIME_INVUL 146 -#define VARIOUS_TEATIME_TARGETS 147 -#define VARIOUS_TRY_WIND_RIDER_POWER 148 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 149 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 150 -#define VARIOUS_STORE_HEALING_WISH 151 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 152 -#define VARIOUS_TRY_REVIVAL_BLESSING 153 +#define VARIOUS_ABILITY_POPUP 54 +#define VARIOUS_DEFOG 55 +#define VARIOUS_JUMP_IF_TARGET_ALLY 56 +#define VARIOUS_TRY_SYNCHRONOISE 57 +#define VARIOUS_PSYCHO_SHIFT 58 +#define VARIOUS_CURE_STATUS 59 +#define VARIOUS_POWER_TRICK 60 +#define VARIOUS_AFTER_YOU 61 +#define VARIOUS_BESTOW 62 +#define VARIOUS_ARGUMENT_TO_MOVE_EFFECT 63 +#define VARIOUS_JUMP_IF_NOT_GROUNDED 64 +#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 65 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 66 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 67 +#define VARIOUS_SET_AURORA_VEIL 68 +#define VARIOUS_TRY_THIRD_TYPE 69 +#define VARIOUS_ACUPRESSURE 70 +#define VARIOUS_SET_POWDER 71 +#define VARIOUS_SPECTRAL_THIEF 72 +#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 73 +#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 74 +#define VARIOUS_JUMP_IF_ROAR_FAILS 75 +#define VARIOUS_TRY_INSTRUCT 76 +#define VARIOUS_JUMP_IF_NOT_BERRY 77 +#define VARIOUS_TRACE_ABILITY 78 +#define VARIOUS_UPDATE_NICK 79 +#define VARIOUS_TRY_ILLUSION_OFF 80 +#define VARIOUS_SET_SPRITEIGNORE0HP 81 +#define VARIOUS_HANDLE_FORM_CHANGE 82 +#define VARIOUS_GET_STAT_VALUE 83 +#define VARIOUS_JUMP_IF_FULL_HP 84 +#define VARIOUS_LOSE_TYPE 85 +#define VARIOUS_TRY_ACTIVATE_SOULHEART 86 +#define VARIOUS_TRY_ACTIVATE_RECEIVER 87 +#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 88 +#define VARIOUS_TRY_FRISK 89 +#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 90 +#define VARIOUS_TRY_FAIRY_LOCK 91 +#define VARIOUS_JUMP_IF_NO_ALLY 92 +#define VARIOUS_POISON_TYPE_IMMUNITY 93 +#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 94 +#define VARIOUS_INFATUATE_WITH_BATTLER 95 +#define VARIOUS_SET_LAST_USED_ITEM 96 +#define VARIOUS_PARALYZE_TYPE_IMMUNITY 97 +#define VARIOUS_JUMP_IF_ABSENT 98 +#define VARIOUS_DESTROY_ABILITY_POPUP 99 +#define VARIOUS_TOTEM_BOOST 100 +#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 101 +#define VARIOUS_MOVEEND_ITEM_EFFECTS 102 +#define VARIOUS_TERRAIN_SEED 103 +#define VARIOUS_MAKE_INVISIBLE 104 +#define VARIOUS_ROOM_SERVICE 105 +#define VARIOUS_EERIE_SPELL_PP_REDUCE 106 +#define VARIOUS_JUMP_IF_TEAM_HEALTHY 107 +#define VARIOUS_TRY_HEAL_QUARTER_HP 108 +#define VARIOUS_REMOVE_TERRAIN 109 +#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 110 +#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 111 +#define VARIOUS_GET_ROTOTILLER_TARGETS 112 +#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 113 +#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 114 +#define VARIOUS_CONSUME_BERRY 115 +#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 116 +#define VARIOUS_JUMP_IF_SPECIES 117 +#define VARIOUS_UPDATE_ABILITY_POPUP 118 +#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 119 +#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 120 +#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 121 +#define VARIOUS_PHOTON_GEYSER_CHECK 122 +#define VARIOUS_SHELL_SIDE_ARM_CHECK 123 +#define VARIOUS_TRY_NO_RETREAT 124 +#define VARIOUS_TRY_TAR_SHOT 125 +#define VARIOUS_CAN_TAR_SHOT_WORK 126 +#define VARIOUS_CHECK_POLTERGEIST 127 +#define VARIOUS_CUT_1_3_HP_RAISE_STATS 128 +#define VARIOUS_TRY_END_NEUTRALIZING_GAS 129 +#define VARIOUS_JUMP_IF_UNDER_200 130 +#define VARIOUS_SET_SKY_DROP 131 +#define VARIOUS_CLEAR_SKY_DROP 132 +#define VARIOUS_SKY_DROP_YAWN 133 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 134 +#define VARIOUS_CURE_CERTAIN_STATUSES 135 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 136 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 137 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 138 +#define VARIOUS_SAVE_BATTLER_ITEM 139 +#define VARIOUS_RESTORE_BATTLER_ITEM 140 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 141 +#define VARIOUS_SET_BEAK_BLAST 142 +#define VARIOUS_SWAP_SIDE_STATUSES 143 +#define VARIOUS_SWAP_STATS 144 +#define VARIOUS_TEATIME_INVUL 145 +#define VARIOUS_TEATIME_TARGETS 146 +#define VARIOUS_TRY_WIND_RIDER_POWER 147 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 148 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 149 +#define VARIOUS_STORE_HEALING_WISH 150 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 151 +#define VARIOUS_TRY_REVIVAL_BLESSING 152 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 @@ -299,27 +298,28 @@ #define MOVEEND_MOVE_EFFECTS2 13 #define MOVEEND_ITEM_EFFECTS_ALL 14 #define MOVEEND_KINGSROCK 15 // These item effects will occur each strike of a multi-hit move -#define MOVEEND_SUBSTITUTE 16 -#define MOVEEND_SKY_DROP_CONFUSE 17 -#define MOVEEND_UPDATE_LAST_MOVES 18 -#define MOVEEND_MIRROR_MOVE 19 -#define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move -#define MOVEEND_MULTIHIT_MOVE 21 -#define MOVEEND_DEFROST 22 -#define MOVEEND_RECOIL 23 -#define MOVEEND_MAGICIAN 24 // Occurs after final multi-hit strike, and after other items/abilities would activate -#define MOVEEND_EJECT_BUTTON 25 -#define MOVEEND_RED_CARD 26 -#define MOVEEND_EJECT_PACK 27 -#define MOVEEND_LIFEORB_SHELLBELL 28 // Includes shell bell, throat spray, etc -#define MOVEEND_CHANGED_ITEMS 29 -#define MOVEEND_PICKPOCKET 30 -#define MOVEEND_DANCER 31 -#define MOVEEND_EMERGENCY_EXIT 32 -#define MOVEEND_SYMBIOSIS 33 -#define MOVEEND_OPPORTUNIST 34 // Occurs after other stat change items/abilities to try and copy the boosts -#define MOVEEND_CLEAR_BITS 35 -#define MOVEEND_COUNT 36 +#define MOVEEND_NUM_HITS 16 +#define MOVEEND_SUBSTITUTE 17 +#define MOVEEND_SKY_DROP_CONFUSE 18 +#define MOVEEND_UPDATE_LAST_MOVES 19 +#define MOVEEND_MIRROR_MOVE 20 +#define MOVEEND_NEXT_TARGET 21 // Everything up until here is handled for each strike of a multi-hit move +#define MOVEEND_MULTIHIT_MOVE 22 +#define MOVEEND_DEFROST 23 +#define MOVEEND_RECOIL 24 +#define MOVEEND_MAGICIAN 25 // Occurs after final multi-hit strike, and after other items/abilities would activate +#define MOVEEND_EJECT_BUTTON 26 +#define MOVEEND_RED_CARD 27 +#define MOVEEND_EJECT_PACK 28 +#define MOVEEND_LIFEORB_SHELLBELL 29 // Includes shell bell, throat spray, etc +#define MOVEEND_CHANGED_ITEMS 30 +#define MOVEEND_PICKPOCKET 31 +#define MOVEEND_DANCER 32 +#define MOVEEND_EMERGENCY_EXIT 33 +#define MOVEEND_SYMBIOSIS 34 +#define MOVEEND_OPPORTUNIST 35 // Occurs after other stat change items/abilities to try and copy the boosts +#define MOVEEND_CLEAR_BITS 36 +#define MOVEEND_COUNT 37 // switch cases #define B_SWITCH_NORMAL 0 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7fe2dba4b5..06a455e03e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2001,7 +2001,10 @@ static void Cmd_adjustdamage(void) if (DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) goto END; if (DoesDisguiseBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) + { + gBattleStruct->enduredDamage |= gBitTable[gBattlerTarget]; goto END; + } if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) goto END; @@ -2043,6 +2046,7 @@ static void Cmd_adjustdamage(void) // Handle reducing the dmg to 1 hp. gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + gBattleStruct->enduredDamage |= gBitTable[gBattlerTarget]; if (gProtectStructs[gBattlerTarget].endured) { @@ -5535,6 +5539,16 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; + case MOVEEND_NUM_HITS: + if (gBattlerAttacker != gBattlerTarget + && gBattleMoves[gCurrentMove].split != SPLIT_STATUS + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && TARGET_TURN_DAMAGED) + { + gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]++; + } + gBattleScripting.moveendState++; + break; case MOVEEND_SUBSTITUTE: // update substitute for (i = 0; i < gBattlersCount; i++) { @@ -6050,6 +6064,7 @@ static void Cmd_moveend(void) gBattleStruct->zmove.effect = EFFECT_HIT; gBattleStruct->hitSwitchTargetFailed = FALSE; gBattleStruct->isAtkCancelerForCalledMove = FALSE; + gBattleStruct->enduredDamage = 0; gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -9499,25 +9514,6 @@ static void Cmd_various(void) } return; } - case VARIOUS_TRY_COPYCAT: - { - VARIOUS_ARGS(const u8 *failInstr); - if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gBattleMoves[gLastUsedMove].copycatBanned) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - if (IsMaxMove(gLastUsedMove)) - gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove; - else - gCalledMove = gLastUsedMove; - gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; - gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); - gBattlescriptCurrInstr = cmd->nextInstr; - } - return; - } case VARIOUS_TRY_INSTRUCT: { VARIOUS_ARGS(const u8 *failInstr); @@ -12269,6 +12265,7 @@ static void Cmd_transformdataexecution(void) { s32 i; u8 *battleMonAttacker, *battleMonTarget; + u8 timesGotHit; gBattleMons[gBattlerAttacker].status2 |= STATUS2_TRANSFORMED; gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE; @@ -12278,6 +12275,9 @@ static void Cmd_transformdataexecution(void) gDisableStructs[gBattlerAttacker].mimickedMoves = 0; gDisableStructs[gBattlerAttacker].usedMoves = 0; + timesGotHit = gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]; + gBattleStruct->timesGotHit[GetBattlerSide(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = timesGotHit; + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].species) battleMonAttacker = (u8 *)(&gBattleMons[gBattlerAttacker]); @@ -16437,3 +16437,24 @@ void BS_TryHealPulse(void) gBattlescriptCurrInstr = cmd->nextInstr; } } + +void BS_TryCopycat(void) +{ + NATIVE_ARGS(const u8 *failInstr); + + if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gBattleMoves[gLastUsedMove].copycatBanned) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else + { + if (IsMaxMove(gLastUsedMove)) + gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove; + else + gCalledMove = gLastUsedMove; + + gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; + gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 3b413cde58..4f7717ee1c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8853,6 +8853,10 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 case EFFECT_MAX_MOVE: basePower = GetMaxMovePower(gBattleMons[battlerAtk].moves[gBattleStruct->chosenMovePositions[battlerAtk]]); break; + case EFFECT_RAGE_FIST: + basePower += 50 * gBattleStruct->timesGotHit[GetBattlerSide(battlerAtk)][gBattlerPartyIndexes[battlerAtk]]; + basePower = (basePower > 350) ? 350 : basePower; + break; } // Move-specific base power changes diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 078f32f5ac..3820b24fe6 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -14135,7 +14135,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] = [MOVE_RAGE_FIST] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_RAGE_FIST + .effect = EFFECT_RAGE_FIST, .power = 50, .type = TYPE_GHOST, .accuracy = 100, diff --git a/test/battle/move_effect/rage_fist.c b/test/battle/move_effect/rage_fist.c new file mode 100644 index 0000000000..7a09461a4f --- /dev/null +++ b/test/battle/move_effect/rage_fist.c @@ -0,0 +1,294 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_RAGE_FIST].effect == EFFECT_RAGE_FIST); + ASSUME(gBattleMoves[MOVE_RAGE_FIST].power == 50); +} + +SINGLE_BATTLE_TEST("Rage Fist base power is increased by 50 if the user takes damage") +{ + u8 turns; + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + for (turns = 0; turns < 2; turns++) { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_TACKLE); } + } + } SCENE { + for (turns = 0; turns < 2; turns++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[turns]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player); + } + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(2.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is increased by each multi hit") +{ + u8 turns; + s16 timesGotHit[2]; + + GIVEN { + ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + for (turns = 0; turns < 2; turns++) { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_BULLET_SEED); } + } + } SCENE { + for (turns = 0; turns < 2; turns++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[turns]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, opponent); + HP_BAR(player); + } + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(6.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is not increased by a confusion hit") +{ + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_CONFUSE_RAY); } + TURN {} + TURN { MOVE(player, MOVE_RAGE_FIST, WITH_RNG(RNG_CONFUSION, FALSE)); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player); + MESSAGE("Wobbuffet became confused!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player); + MESSAGE("It hurt itself in its confusion!"); + HP_BAR(player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_EQ(timesGotHit[0], timesGotHit[1]); + } +} + +DOUBLE_BATTLE_TEST("Rage Fist maximum base power is 350") +{ + u16 turns; + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_REGIROCK); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + for (turns = 1; turns <= 3; turns++) { + TURN { + if (turns == 1) + MOVE(playerLeft, MOVE_RAGE_FIST, target: opponentLeft); + + MOVE(playerRight, MOVE_TACKLE, target: playerLeft); + MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); + MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); + } + } + TURN { MOVE(playerLeft, MOVE_RAGE_FIST, target: opponentLeft); } + } SCENE { + for (turns = 1; turns <= 3; turns++) { + if (turns == 1) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, playerLeft); + HP_BAR(opponentLeft, captureDamage: ×GotHit[0]); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + HP_BAR(playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + HP_BAR(playerLeft); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, playerLeft); + HP_BAR(opponentLeft, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(7.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is not increased if a substitute was hit") +{ + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); } + TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_CRUNCH); } + TURN { MOVE(player, MOVE_RAGE_FIST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRUNCH, opponent); + MESSAGE("The SUBSTITUTE took damage for Wobbuffet!"); + MESSAGE("Wobbuffet's SUBSTITUTE faded!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_EQ(timesGotHit[0], timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is not lost if user switches out") +{ + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); } + TURN { SWITCH(player, 0); } + TURN { MOVE(player, MOVE_RAGE_FIST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + MESSAGE("Wobbuffet, that's enough! Come back!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("Wynaut, that's enough! Come back!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(2.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is increased by 50 even if a damaging move does no damage - False Swipe") +{ + u8 turns; + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_REGIROCK); + } WHEN { + for (turns = 0; turns < 2; turns++) { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_FALSE_SWIPE); } + } + } SCENE { + for (turns = 0; turns < 2; turns++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[turns]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, opponent); + HP_BAR(player); + } + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(2.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is increased by 50 even if a damaging move does no damage - Endure") +{ + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(2); } + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_FALSE_SWIPE); } + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_RAGE_FIST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(3.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is not increased if move had no affect") +{ + u8 turns; + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_GASTLY); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + for (turns = 0; turns < 2; turns++) { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_TACKLE); } + } + } SCENE { + for (turns = 0; turns < 2; turns++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[turns]); + MESSAGE("Foe Regirock used Tackle!"); + MESSAGE("It doesn't affect Gastly…"); + } + } THEN { + EXPECT_EQ(timesGotHit[0], timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist base power is increased if Disguise breaks") +{ + s16 timesGotHit[2]; + + GIVEN { + PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_ROCK_THROW); } + TURN { MOVE(player, MOVE_RAGE_FIST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_THROW, opponent); + ABILITY_POPUP(player, ABILITY_DISGUISE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(2.0), timesGotHit[1]); + } +} + +SINGLE_BATTLE_TEST("Rage Fist number of hits is copied by Transform") +{ + s16 timesGotHit[2]; + + // KNOWN_FAILING; // After Transform , wrong move is used by transformed mon + GIVEN { + PLAYER(SPECIES_REGIROCK); + OPPONENT(SPECIES_REGIROCK) { Moves(MOVE_RAGE_FIST, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_RAGE_FIST); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGE_FIST, player); + HP_BAR(opponent, captureDamage: ×GotHit[1]); + } THEN { + EXPECT_MUL_EQ(timesGotHit[0], Q_4_12(2.0), timesGotHit[1]); + } +}