Fixes crash damage move against absorbing abilities (#6361)

This commit is contained in:
Alex 2025-03-09 20:54:58 +01:00 committed by GitHub
parent 2a0661db19
commit 3def0e751b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 74 deletions

View File

@ -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

View File

@ -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;

View File

@ -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[];

View File

@ -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)

View File

@ -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;

View File

@ -238,7 +238,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_RECOIL_IF_MISS] =
{
.battleScript = BattleScript_EffectRecoilIfMiss,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 1,
},

View File

@ -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);
}
}
}
}