diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b38dc4b7f8d2..653c8920481f 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3435,52 +3435,6 @@ BattleScript_EffectSuperFang:: damagetohalftargethp goto BattleScript_HitFromAtkAnimation -BattleScript_EffectRecoilIfMiss:: - attackcanceler - accuracycheck BattleScript_MoveMissedDoDamage, ACC_CURR_MOVE -.if B_CRASH_IF_TARGET_IMMUNE >= GEN_4 - typecalc - jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveMissedDoDamage -.endif - goto BattleScript_HitFromAtkString -BattleScript_MoveMissedDoDamage:: - jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_PrintMoveMissed - attackstring - ppreduce - pause B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG -.if B_CRASH_IF_TARGET_IMMUNE < GEN_4 - jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveEnd -.endif - moveendcase MOVEEND_PROTECT_LIKE_EFFECT @ Spiky Shield's damage happens before recoil. - jumpifhasnohp BS_ATTACKER, BattleScript_MoveEnd - printstring STRINGID_PKMNCRASHED - waitmessage B_WAIT_TIME_LONG - damagecalc - typecalc - adjustdamage -.if B_CRASH_IF_TARGET_IMMUNE == GEN_4 - manipulatedamage DMG_RECOIL_FROM_IMMUNE -.else - manipulatedamage DMG_RECOIL_FROM_MISS -.endif -.if B_CRASH_IF_TARGET_IMMUNE >= GEN_4 - bichalfword gMoveResultFlags, MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE -.else - bichalfword gMoveResultFlags, MOVE_RESULT_MISSED -.endif - orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE - healthbarupdate BS_ATTACKER - datahpupdate BS_ATTACKER - tryfaintmon BS_ATTACKER -.if B_CRASH_IF_TARGET_IMMUNE >= GEN_4 - orhalfword gMoveResultFlags, MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE -.else - orhalfword gMoveResultFlags, MOVE_RESULT_MISSED -.endif - goto BattleScript_MoveEnd - BattleScript_EffectMist:: attackcanceler attackstring @@ -7550,6 +7504,16 @@ BattleScript_MoveEffectConfusion:: waitmessage B_WAIT_TIME_LONG return +BattleScript_MoveEffectRecoilIfMiss:: + printstring STRINGID_PKMNCRASHED + waitmessage B_WAIT_TIME_LONG + jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_RecoilEnd + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE | HITMARKER_IGNORE_DISGUISE + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER + tryfaintmon BS_ATTACKER + return + BattleScript_MoveEffectRecoil:: jumpifmove MOVE_STRUGGLE, BattleScript_DoRecoil jumpifability BS_ATTACKER, ABILITY_ROCK_HEAD, BattleScript_RecoilEnd diff --git a/include/battle.h b/include/battle.h index 37f23596411f..8c141506946a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -829,7 +829,8 @@ struct BattleStruct u16 commanderActive[MAX_BATTLERS_COUNT]; u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side u8 redCardActivates:1; - u8 padding:7; + u8 noTargetPresent:1; + u8 padding:6; u8 usedEjectItem; u8 usedMicleBerry; }; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3c58b28e1e57..32df67d8d15e 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -160,6 +160,7 @@ extern const u8 BattleScript_MoveEffectToxic[]; extern const u8 BattleScript_MoveEffectPayDay[]; extern const u8 BattleScript_MoveEffectWrap[]; extern const u8 BattleScript_MoveEffectConfusion[]; +extern const u8 BattleScript_MoveEffectRecoilIfMiss[]; extern const u8 BattleScript_MoveEffectRecoil[]; extern const u8 BattleScript_FaintAttackerForExplosion[]; extern const u8 BattleScript_MaxHp50Recoil[]; @@ -596,7 +597,6 @@ extern const u8 BattleScript_EffectOHKO[]; extern const u8 BattleScript_EffectSuperFang[]; extern const u8 BattleScript_EffectFixedDamageArg[]; extern const u8 BattleScript_EffectHealBlock[]; -extern const u8 BattleScript_EffectRecoilIfMiss[]; extern const u8 BattleScript_EffectMist[]; extern const u8 BattleScript_EffectFocusEnergy[]; extern const u8 BattleScript_EffectConfuse[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index ed19a72d9ce6..fe910c06cf48 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -281,6 +281,7 @@ #define MOVE_RESULT_FOE_HUNG_ON (1 << 7) #define MOVE_RESULT_STURDIED (1 << 8) #define MOVE_RESULT_FOE_ENDURED_AFFECTION (1 << 9) +#define MOVE_RESULT_ABSORBED (1 << 10) #define MOVE_RESULT_NO_EFFECT (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED) // Battle Weather flags diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 050a20d933f7..47ba97a697e6 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -223,13 +223,11 @@ // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 -#define DMG_RECOIL_FROM_MISS 1 -#define DMG_DOUBLED 2 -#define DMG_1_8_TARGET_HP 3 -#define DMG_FULL_ATTACKER_HP 4 -#define DMG_CURR_ATTACKER_HP 5 -#define DMG_BIG_ROOT 6 -#define DMG_RECOIL_FROM_IMMUNE 7 // Used to calculate recoil for the Gen 4 version of Jump Kick +#define DMG_DOUBLED 1 +#define DMG_1_8_TARGET_HP 2 +#define DMG_FULL_ATTACKER_HP 3 +#define DMG_CURR_ATTACKER_HP 4 +#define DMG_BIG_ROOT 5 // Cmd_jumpifcantswitch #define SWITCH_IGNORE_ESCAPE_PREVENTION (1 << 7) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 12a2e215bffc..3b7ae51c12b9 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1281,7 +1281,7 @@ static void Cmd_attackcanceler(void) gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem; else gBattlescriptCurrInstr = BattleScript_FailedFromAtkString; - + gBattleStruct->noTargetPresent = TRUE; if (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) CancelMultiTurnMoves(gBattlerAttacker); return; @@ -1359,6 +1359,7 @@ static void Cmd_attackcanceler(void) if (gSpecialStatuses[gBattlerTarget].lightningRodRedirected) { + gMoveResultFlags |= MOVE_RESULT_ABSORBED; gSpecialStatuses[gBattlerTarget].lightningRodRedirected = FALSE; gLastUsedAbility = ABILITY_LIGHTNING_ROD; BattleScriptPushCursor(); @@ -1367,6 +1368,7 @@ static void Cmd_attackcanceler(void) } else if (gSpecialStatuses[gBattlerTarget].stormDrainRedirected) { + gMoveResultFlags |= MOVE_RESULT_ABSORBED; gSpecialStatuses[gBattlerTarget].stormDrainRedirected = FALSE; gLastUsedAbility = ABILITY_STORM_DRAIN; BattleScriptPushCursor(); @@ -1419,7 +1421,10 @@ static bool32 JumpIfMoveFailed(u8 adder, u16 move) { TrySetDestinyBondToHappen(); if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, move)) + { + gMoveResultFlags |= MOVE_RESULT_ABSORBED; return TRUE; + } } gBattlescriptCurrInstr += adder; return FALSE; @@ -5756,6 +5761,25 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } + else if (gMovesInfo[gCurrentMove].effect == EFFECT_RECOIL_IF_MISS + && gMoveResultFlags & (MOVE_RESULT_NO_EFFECT | MOVE_RESULT_ABSORBED) + && !gBattleStruct->noTargetPresent + && IsBattlerAlive(gBattlerAttacker)) + { + if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE)) + gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; + else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage) + gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2; + else + gBattleMoveDamage /= 2; + gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveEffectRecoilIfMiss; + effect = TRUE; + } else if (gMovesInfo[gCurrentMove].recoil > 0 && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) @@ -6641,6 +6665,7 @@ static void Cmd_moveend(void) gBattleStruct->fickleBeamBoosted = FALSE; gBattleStruct->redCardActivates = FALSE; gBattleStruct->usedMicleBerry &= ~(1u << gBattlerAttacker); + gBattleStruct->noTargetPresent = FALSE; if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) gBattleStruct->pledgeMove = FALSE; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) @@ -11308,23 +11333,6 @@ static void Cmd_manipulatedamage(void) case DMG_CHANGE_SIGN: gBattleMoveDamage *= -1; break; - case DMG_RECOIL_FROM_MISS: - if (B_RECOIL_IF_MISS_DMG >= GEN_5) - { - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; - } - else if (B_RECOIL_IF_MISS_DMG == GEN_4) - { - if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage) - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2; - } - else - { - gBattleMoveDamage /= 2; - } - if (gBattleMoveDamage == 0) - gBattleMoveDamage = 1; - break; case DMG_DOUBLED: gBattleMoveDamage *= 2; break; @@ -11336,14 +11344,11 @@ static void Cmd_manipulatedamage(void) case DMG_FULL_ATTACKER_HP: gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker); break; - case DMG_BIG_ROOT: - gBattleMoveDamage = GetDrainedBigRootHp(gBattlerAttacker, gBattleMoveDamage); - break; case DMG_CURR_ATTACKER_HP: gBattleMoveDamage = GetNonDynamaxHP(gBattlerAttacker); break; - case DMG_RECOIL_FROM_IMMUNE: - gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2; + case DMG_BIG_ROOT: + gBattleMoveDamage = GetDrainedBigRootHp(gBattlerAttacker, gBattleMoveDamage); break; } diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index b5b3e539c87e..5904865b6a00 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -238,7 +238,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_RECOIL_IF_MISS] = { - .battleScript = BattleScript_EffectRecoilIfMiss, + .battleScript = BattleScript_EffectHit, .battleTvScore = 1, }, diff --git a/test/battle/move_effect/recoil_if_miss.c b/test/battle/move_effect/recoil_if_miss.c index a5f011109548..755fc1b42924 100644 --- a/test/battle/move_effect/recoil_if_miss.c +++ b/test/battle/move_effect/recoil_if_miss.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gMovesInfo[MOVE_JUMP_KICK].effect == EFFECT_RECOIL_IF_MISS); } -SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss") +SINGLE_BATTLE_TEST("Recoil If Miss: Jump Kick has 50% recoil on miss") { GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -22,14 +22,14 @@ SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss") } } -SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect") +SINGLE_BATTLE_TEST("Recoil If Miss: Jump Kick has 50% recoil on protect") { GIVEN { ASSUME(!gMovesInfo[MOVE_JUMP_KICK].ignoresProtect); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); } + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_JUMP_KICK); } } SCENE { s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); @@ -38,7 +38,25 @@ SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect") } } -SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target") +SINGLE_BATTLE_TEST("Recoil If Miss: Jump Kick recoil happens after Spiky Shield damage") +{ + GIVEN { + ASSUME(!gMovesInfo[MOVE_JUMP_KICK].ignoresProtect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SPIKY_SHIELD); MOVE(player, MOVE_JUMP_KICK); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_JUMP_KICK, player); + MESSAGE("Wobbuffet was hurt by the opposing Wobbuffet's Spiky Shield!"); + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player, damage: maxHP / 2); + } +} + +SINGLE_BATTLE_TEST("Recoil If Miss: Jump Kick has no recoil if no target") { GIVEN { ASSUME(B_HEALING_WISH_SWITCH >= GEN_5); @@ -54,7 +72,7 @@ SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target") } } -SINGLE_BATTLE_TEST("Jump Kick's recoil happens after Spiky Shield damage and Pokemon can faint from either of these") +SINGLE_BATTLE_TEST("Recoil If Miss: Jump Kick's recoil happens after Spiky Shield damage and Pokemon can faint from either of these") { s16 hp, maxHp = 256; bool32 faintOnSpiky = FALSE, faintOnJumpKick = FALSE; @@ -98,3 +116,18 @@ SINGLE_BATTLE_TEST("Jump Kick's recoil happens after Spiky Shield damage and Pok } } } + +SINGLE_BATTLE_TEST("Recoil If Miss: Supercell Slam causes recoil if it is absorbed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(player, MOVE_SUPERCELL_SLAM); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD); + MESSAGE("Wobbuffet kept going and crashed!"); + HP_BAR(player, damage: maxHP / 2); + } +}