diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index aaa03f0ac6..f66a698eed 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3429,51 +3429,15 @@ BattleScript_EffectSuperFang:: damagetohalftargethp goto BattleScript_HitFromAtkAnimation -BattleScript_EffectRecoilIfMiss:: - attackcanceler - accuracycheck BattleScript_MoveMissedDoDamage, ACC_CURR_MOVE -.if B_CRASH_IF_TARGET_IMMUNE >= GEN_4 - typecalc - jumpifmoveresultflags 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 - jumpifmoveresultflags 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 +BattleScript_RecoilIfMiss:: 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 - clearmoveresultflags MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE -.else - clearmoveresultflags MOVE_RESULT_MISSED -.endif - orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_IGNORE_DISGUISE + 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 -.if B_CRASH_IF_TARGET_IMMUNE >= GEN_4 - setmoveresultflags MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE -.else - setmoveresultflags MOVE_RESULT_MISSED -.endif - goto BattleScript_MoveEnd + return BattleScript_EffectMist:: attackcanceler diff --git a/include/battle.h b/include/battle.h index c9517b9b71..a6ba6d92e5 100644 --- a/include/battle.h +++ b/include/battle.h @@ -830,7 +830,7 @@ struct BattleStruct u8 printedStrongWindsWeakenedAttack:1; u8 numSpreadTargets:2; u8 bypassMoldBreakerChecks:1; // for ABILITYEFFECT_IMMUNITY - u8 padding3:1; + u8 noTargetPresent:1; u8 usedEjectItem; u8 usedMicleBerry; struct MessageStatus slideMessageStatus; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index ffa18d0343..f67ab67ec9 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -597,7 +597,7 @@ 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_RecoilIfMiss[]; extern const u8 BattleScript_EffectMist[]; extern const u8 BattleScript_EffectFocusEnergy[]; extern const u8 BattleScript_EffectConfuse[]; diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 2aa5799a9e..1b302a4347 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -219,13 +219,11 @@ enum CmdVarious // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 1 -#define DMG_RECOIL_FROM_MISS 2 -#define DMG_DOUBLED 3 -#define DMG_1_8_TARGET_HP 4 -#define DMG_FULL_ATTACKER_HP 5 -#define DMG_CURR_ATTACKER_HP 6 -#define DMG_BIG_ROOT 7 -#define DMG_RECOIL_FROM_IMMUNE 8 // Used to calculate recoil for the Gen 4 version of Jump Kick +#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 // Cmd_jumpifcantswitch #define SWITCH_IGNORE_ESCAPE_PREVENTION (1 << 7) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0742c79aab..4db25d4743 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1216,6 +1216,8 @@ static void Cmd_attackcanceler(void) && (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))) || (IsMoveNotAllowedInSkyBattles(gCurrentMove))) { + gBattleStruct->noTargetPresent = TRUE; + if (effect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling. gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem; else @@ -6360,6 +6362,25 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } + else if (gMovesInfo[gCurrentMove].effect == EFFECT_RECOIL_IF_MISS + && (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) + && !gBattleStruct->noTargetPresent + && IsBattlerAlive(gBattlerAttacker)) + { + if (B_RECOIL_IF_MISS_DMG >= GEN_5 || (B_CRASH_IF_TARGET_IMMUNE == GEN_4 && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_DOESNT_AFFECT_FOE)) + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; + else if (B_RECOIL_IF_MISS_DMG == GEN_4 && (GetNonDynamaxMaxHP(gBattlerTarget) / 2) < gBattleStruct->moveDamage[gBattlerTarget]) + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2; + else // Fallback if B_RECOIL_IF_MISS_DMG is set to gen3 or lower. + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2; + + if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) + gBattleStruct->moveDamage[gBattlerAttacker] = 1; + + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RecoilIfMiss; + effect = TRUE; + } else if (moveRecoil > 0 && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) @@ -7253,6 +7274,7 @@ static void Cmd_moveend(void) gBattleStruct->fickleBeamBoosted = FALSE; gBattleStruct->redCardActivates = FALSE; gBattleStruct->battlerState[gBattlerAttacker].usedMicleBerry = FALSE; + gBattleStruct->noTargetPresent = FALSE; if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) gBattleStruct->pledgeMove = FALSE; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) @@ -11925,23 +11947,6 @@ static void Cmd_manipulatedamage(void) case DMG_CHANGE_SIGN: gBattleStruct->moveDamage[gBattlerAttacker] *= -1; break; - case DMG_RECOIL_FROM_MISS: - if (B_RECOIL_IF_MISS_DMG >= GEN_5) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; - } - else if (B_RECOIL_IF_MISS_DMG == GEN_4) - { - if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleStruct->moveDamage[gBattlerTarget]) - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2; - } - else - { - gBattleStruct->moveDamage[gBattlerAttacker] = gBattleStruct->moveDamage[gBattlerTarget] /= 2; - } - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - break; case DMG_DOUBLED: gBattleStruct->moveDamage[gBattlerTarget] *= 2; break; @@ -11959,9 +11964,6 @@ static void Cmd_manipulatedamage(void) case DMG_CURR_ATTACKER_HP: gBattleStruct->moveDamage[gBattlerTarget] = GetNonDynamaxHP(gBattlerAttacker); break; - case DMG_RECOIL_FROM_IMMUNE: - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerTarget) / 2; - break; } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index e2970afcaf..d5b3d5dc54 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 8695156dd0..0b65eb8541 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(GetMoveEffect(MOVE_JUMP_KICK) == 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,7 +22,7 @@ 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(!MoveIgnoresProtect(MOVE_JUMP_KICK)); @@ -38,7 +38,7 @@ 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 has no recoil if no target") { GIVEN { ASSUME(B_HEALING_WISH_SWITCH >= GEN_5); @@ -54,7 +54,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 +98,60 @@ SINGLE_BATTLE_TEST("Jump Kick's recoil happens after Spiky Shield damage and Pok } } } + +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: 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); + } +} + +SINGLE_BATTLE_TEST("Recoil if miss: Disguise doesn't prevent crash damage from Jump Kick into ghost types") +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_EARLY_BIRD; } + PARAMETRIZE { ability = ABILITY_SCRAPPY; } + + GIVEN { + PLAYER(SPECIES_KANGASKHAN) { Ability(ability); }; + OPPONENT(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + } WHEN { + TURN { MOVE(player, MOVE_JUMP_KICK); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + MESSAGE("Kangaskhan used Jump Kick!"); + if (ability == ABILITY_SCRAPPY) { + NONE_OF { + MESSAGE("Kangaskhan kept going and crashed!"); + HP_BAR(player, damage: maxHP / 2); + } + } + } +} +