From 1c1a98ee459e5a2ec322101e2dc209cc835c6fe8 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 30 Oct 2025 23:14:02 +0100 Subject: [PATCH] Grudge, Destiny Bond and FaintBattler refactor (#8072) Co-authored-by: Bassoonian --- asm/macros/battle_script.inc | 10 +- data/battle_scripts_1.s | 42 ++----- include/battle.h | 5 +- include/battle_scripts.h | 3 +- include/constants/battle.h | 2 +- include/constants/battle_script_commands.h | 2 + include/constants/battle_string_ids.h | 3 +- src/battle_ai_main.c | 2 +- src/battle_end_turn.c | 4 - src/battle_main.c | 4 +- src/battle_message.c | 3 +- src/battle_script_commands.c | 137 ++++++++++----------- src/battle_tv.c | 54 +++++--- src/battle_util.c | 11 +- test/battle/gimmick/dynamax.c | 4 +- test/battle/move_effect/destiny_bond.c | 8 +- test/battle/move_effect/grudge.c | 49 ++++++++ 17 files changed, 183 insertions(+), 160 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 2b60ac0485..fa2b3591d9 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -936,7 +936,7 @@ .4byte \failInstr .endm - .macro trysetdestinybondtohappen + .macro unused_0xab .byte 0xab .endm @@ -1617,14 +1617,6 @@ .4byte \failInstr .endm - .macro tryupdaterecoiltracker - callnative BS_TryUpdateRecoilTracker - .endm - - .macro tryupdateleaderscresttracker - callnative BS_TryUpdateLeadersCrestTracker - .endm - .macro trytidyup clear:req, jumpInstr:req callnative BS_TryTidyUp .byte \clear diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9281b1bcaa..3db6ab87ff 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2306,6 +2306,7 @@ BattleScript_EffectMiracleEye:: accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE goto BattleScript_IdentifiedFoe + BattleScript_EffectGravity:: call BattleScript_EffectGravityInternal goto BattleScript_MoveEnd @@ -2838,7 +2839,6 @@ BattleScript_EffectOHKO:: typecalc jumpifmovehadnoeffect BattleScript_HitFromAtkAnimation tryKO BattleScript_KOFail - trysetdestinybondtohappen goto BattleScript_HitFromAtkAnimation BattleScript_KOFail:: pause B_WAIT_TIME_LONG @@ -2853,6 +2853,7 @@ BattleScript_RecoilIfMiss:: healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE tryfaintmon BS_ATTACKER +BattleScript_RecoilEnd: return BattleScript_EffectMist:: @@ -4393,40 +4394,23 @@ BattleScript_EffectCamouflage:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_FaintAttacker:: - tryillusionoff BS_ATTACKER +BattleScript_FaintBattler:: + tryillusionoff BS_SCRIPTING tryactivategulpmissile - playfaintcry BS_ATTACKER + playfaintcry BS_SCRIPTING pause B_WAIT_TIME_LONG - dofaintanimation BS_ATTACKER - printstring STRINGID_ATTACKERFAINTED - cleareffectsonfaint BS_ATTACKER + dofaintanimation BS_SCRIPTING + printstring STRINGID_BATTLERFAINTED + cleareffectsonfaint BS_SCRIPTING trytoclearprimalweather tryrevertweatherform flushtextbox waitanimation tryactivatesoulheart - tryactivatereceiver BS_ATTACKER - trytrainerslidemsgfirstoff BS_ATTACKER + tryactivatereceiver BS_SCRIPTING + trytrainerslidemsgfirstoff BS_SCRIPTING return -BattleScript_FaintTarget:: - tryillusionoff BS_TARGET - tryactivategulpmissile - tryupdateleaderscresttracker - playfaintcry BS_TARGET - pause B_WAIT_TIME_LONG - dofaintanimation BS_TARGET - printstring STRINGID_TARGETFAINTED - cleareffectsonfaint BS_TARGET - trytoclearprimalweather - tryrevertweatherform - flushtextbox - waitanimation - tryactivatesoulheart - tryactivatereceiver BS_TARGET - trytrainerslidemsgfirstoff BS_TARGET - return BattleScript_GiveExp:: setbyte sGIVEEXP_STATE, 0 @@ -6391,17 +6375,11 @@ BattleScript_MoveEffectConfusion:: return BattleScript_MoveEffectRecoil:: - jumpifmove MOVE_STRUGGLE, BattleScript_DoRecoil - jumpifability BS_ATTACKER, ABILITY_ROCK_HEAD, BattleScript_RecoilEnd - jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_RecoilEnd -BattleScript_DoRecoil:: healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE printstring STRINGID_PKMNHITWITHRECOIL waitmessage B_WAIT_TIME_LONG - tryupdaterecoiltracker tryfaintmon BS_ATTACKER -BattleScript_RecoilEnd:: return BattleScript_ItemSteal:: diff --git a/include/battle.h b/include/battle.h index 96698e4e9a..255b4a8853 100644 --- a/include/battle.h +++ b/include/battle.h @@ -777,11 +777,12 @@ struct BattleStruct u8 numHazards[NUM_BATTLE_SIDES]; u8 hazardsCounter:4; // Counter for applying hazard on switch in enum SubmoveState submoveAnnouncement:2; - u8 padding3:2; + u8 tryDestinyBond:1; + u8 tryGrudge:1; u16 flingItem; u8 incrementEchoedVoice:1; u8 echoedVoiceCounter:3; - u8 padding4:4; + u8 padding3:4; }; struct AiBattleData diff --git a/include/battle_scripts.h b/include/battle_scripts.h index a4c2d2a8a9..c0fdd86645 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -22,8 +22,7 @@ extern const u8 BattleScript_StatDown[]; extern const u8 BattleScript_AlreadyAtFullHp[]; extern const u8 BattleScript_PresentHealTarget[]; extern const u8 BattleScript_MoveUsedMustRecharge[]; -extern const u8 BattleScript_FaintAttacker[]; -extern const u8 BattleScript_FaintTarget[]; +extern const u8 BattleScript_FaintBattler[]; extern const u8 BattleScript_GiveExp[]; extern const u8 BattleScript_HandleFaintedMon[]; extern const u8 BattleScript_LocalTrainerBattleWon[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index 380903acdb..b70df023cf 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -264,7 +264,7 @@ enum SemiInvulnerableExclusion #define HITMARKER_UNUSED_21 (1 << 21) #define HITMARKER_PLAYER_FAINTED (1 << 22) #define HITMARKER_UNUSED_23 (1 << 23) -#define HITMARKER_GRUDGE (1 << 24) +#define HITMARKER_UNUSED_24 (1 << 24) #define HITMARKER_OBEYS (1 << 25) #define HITMARKER_UNUSED_26 (1 << 26) #define HITMARKER_UNUSED_27 (1 << 27) diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 0c53134ce9..16d7f75d56 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -130,6 +130,8 @@ enum MoveEndEffects { MOVEEND_SET_VALUES, MOVEEND_PROTECT_LIKE_EFFECT, + MOVEEND_GRUDGE, + MOVEEND_DESTINY_BOND, MOVEEND_ABSORB, MOVEEND_RAGE, MOVEEND_SYNCHRONIZE_TARGET, diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 43e0e050f4..462c64cda1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -28,8 +28,7 @@ enum StringID STRINGID_STATSWONTINCREASE2, STRINGID_AVOIDEDDAMAGE, STRINGID_ITDOESNTAFFECT, - STRINGID_ATTACKERFAINTED, - STRINGID_TARGETFAINTED, + STRINGID_BATTLERFAINTED, STRINGID_PLAYERGOTMONEY, STRINGID_PLAYERWHITEOUT, STRINGID_PLAYERWHITEOUT2_WILD, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 671565fec5..1c7760fe95 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2250,7 +2250,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_DESTINY_BOND: if (DoesDestinyBondFail(battlerAtk)) ADJUST_SCORE(-10); - if (gBattleMons[battlerDef].volatiles.destinyBond) + if (gBattleMons[battlerAtk].volatiles.destinyBond) ADJUST_SCORE(-10); else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) ADJUST_SCORE(-10); diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index ac1563f1fb..cacbb74e9b 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -1540,7 +1540,6 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = u32 DoEndTurnEffects(void) { u32 battler = MAX_BATTLERS_COUNT; - gHitMarker |= HITMARKER_GRUDGE; for (;;) { @@ -1555,10 +1554,7 @@ u32 DoEndTurnEffects(void) // Jump out if possible after endTurnEventsCounter was increased in the above code block if (gBattleStruct->endTurnEventsCounter == ENDTURN_COUNT) - { - gHitMarker &= ~HITMARKER_GRUDGE; return FALSE; - } battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; diff --git a/src/battle_main.c b/src/battle_main.c index 3e2631a0c1..d0ad83fc51 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -549,7 +549,7 @@ static void CB2_InitBattleInternal(void) gReservedSpritePaletteCount = MAX_BATTLERS_COUNT; SetVBlankCallback(VBlankCB_Battle); SetUpBattleVarsAndBirchZigzagoon(); - + if ((IsMultibattleTest() && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) || (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) || (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)) @@ -5164,6 +5164,8 @@ static void TurnValuesCleanUp(bool8 var0) gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0; gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceller + gBattleStruct->tryDestinyBond = FALSE; + gBattleStruct->tryGrudge = FALSE; ClearPursuitValues(); ClearDamageCalcResults(); } diff --git a/src/battle_message.c b/src/battle_message.c index ddc3cf3f0c..e5aa3a48d8 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -188,8 +188,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_STATSWONTINCREASE2] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s stats won't go any higher!"), [STRINGID_AVOIDEDDAMAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} avoided damage with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_ITDOESNTAFFECT] = COMPOUND_STRING("It doesn't affect {B_DEF_NAME_WITH_PREFIX2}…"), - [STRINGID_ATTACKERFAINTED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} fainted!\p"), - [STRINGID_TARGETFAINTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} fainted!\p"), + [STRINGID_BATTLERFAINTED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} fainted!\p"), [STRINGID_PLAYERGOTMONEY] = COMPOUND_STRING("You got ¥{B_BUFF1} for winning!\p"), [STRINGID_PLAYERWHITEOUT] = COMPOUND_STRING("You have no more Pokémon that can fight!\p"), [STRINGID_PLAYERWHITEOUT2_WILD] = COMPOUND_STRING("You panicked and dropped ¥{B_BUFF1}…"), diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d765474aa1..2c6b03ab7d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -310,7 +310,6 @@ enum GiveCaughtMonStates #define TAG_LVLUP_BANNER_MON_ICON 55130 -static void TrySetDestinyBondToHappen(void); static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr); static bool32 IsMonGettingExpSentOut(void); static void InitLevelUpBanner(void); @@ -509,7 +508,7 @@ static void Cmd_setalwayshitflag(void); static void Cmd_copymovepermanently(void); static void Cmd_unused_0xA9(void); static void Cmd_unused_AA(void); -static void Cmd_trysetdestinybondtohappen(void); +static void Cmd_unused_0xab(void); static void Cmd_settailwind(void); static void Cmd_tryspiteppreduce(void); static void Cmd_healpartystatus(void); @@ -768,7 +767,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_copymovepermanently, //0xA8 Cmd_unused_0xA9, //0xA9 Cmd_unused_AA, //0xAA - Cmd_trysetdestinybondtohappen, //0xAB + Cmd_unused_0xab, //0xAB Cmd_settailwind, //0xAC Cmd_tryspiteppreduce, //0xAD Cmd_healpartystatus, //0xAE @@ -1317,8 +1316,6 @@ static void JumpIfMoveFailed(u32 adder, u32 move, u32 moveType, const u8 *failIn } else { - TrySetDestinyBondToHappen(); - if (CanAbilityAbsorbMove(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), @@ -3323,6 +3320,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai if (GetBattlerAbility(gEffectBattler) == ABILITY_PARENTAL_BOND) recoil *= 2; SetPassiveDamageAmount(gEffectBattler, recoil); + TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil; break; @@ -4175,7 +4173,7 @@ static void Cmd_clearvolatile(void) static void Cmd_tryfaintmon(void) { CMD_ARGS(u8 battler, bool8 isSpikes, const u8 *instr); - u32 battler, destinyBondBattler; + u32 battler; const u8 *faintScript; battler = GetBattlerForBattleScript(cmd->battler); @@ -4202,16 +4200,12 @@ static void Cmd_tryfaintmon(void) gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits; return; } + if (cmd->battler == BS_ATTACKER) - { - destinyBondBattler = gBattlerTarget; - faintScript = BattleScript_FaintAttacker; - } - else - { - destinyBondBattler = gBattlerAttacker; - faintScript = BattleScript_FaintTarget; - } + TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE); + + gBattleScripting.battler = battler; + faintScript = BattleScript_FaintBattler; if (!(gAbsentBattlerFlags & (1u << battler)) && !IsBattlerAlive(battler)) { @@ -4233,30 +4227,11 @@ static void Cmd_tryfaintmon(void) gBattleResults.lastOpponentSpecies = GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES, NULL); gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2; } - if ((gHitMarker & HITMARKER_DESTINYBOND) && IsBattlerAlive(gBattlerAttacker) - && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)) - { - gHitMarker &= ~HITMARKER_DESTINYBOND; - BattleScriptPush(gBattlescriptCurrInstr); - gBattleStruct->passiveHpUpdate[destinyBondBattler] = gBattleMons[destinyBondBattler].hp; - gBattlescriptCurrInstr = BattleScript_DestinyBondTakesLife; - } - if (gBattleMons[gBattlerTarget].volatiles.grudge - && !(gHitMarker & HITMARKER_GRUDGE) - && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker) - && gCurrentMove != MOVE_STRUGGLE) - { - u8 moveIndex = gBattleStruct->chosenMovePositions[gBattlerAttacker]; - gBattleMons[gBattlerAttacker].pp[moveIndex] = 0; - BattleScriptPush(gBattlescriptCurrInstr); - gBattlescriptCurrInstr = BattleScript_GrudgeTakesPp; - BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].pp[moveIndex]), &gBattleMons[gBattlerAttacker].pp[moveIndex]); - MarkBattlerForControllerExec(gBattlerAttacker); - - PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) - } + if (gBattleMons[gBattlerTarget].volatiles.destinyBond) + gBattleStruct->tryDestinyBond = TRUE; + if (gBattleMons[gBattlerTarget].volatiles.grudge) + gBattleStruct->tryGrudge = TRUE; TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); } @@ -4957,8 +4932,6 @@ static void MoveValuesCleanUp(void) gBattleScripting.moveEffect = MOVE_EFFECT_NONE; gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE; gBattleCommunication[MISS_TYPE] = 0; - if (!gMultiHitCounter) - gHitMarker &= ~HITMARKER_DESTINYBOND; } static void Cmd_movevaluescleanup(void) @@ -5819,7 +5792,13 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) case EFFECT_RECOIL: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { + enum Ability ability = GetBattlerAbility(gBattlerAttacker); + if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD) + || IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_MAGIC_GUARD)) + break; + SetPassiveDamageAmount(gBattlerAttacker, gBattleScripting.savedDmg * max(1, GetMoveRecoil(gCurrentMove)) / 100); + TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); effect = TRUE; } @@ -5836,10 +5815,11 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) case EFFECT_MAX_HP_50_RECOIL: if (IsBattlerAlive(gBattlerAttacker) && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + && !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) { s32 recoil = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP SetPassiveDamageAmount(gBattlerAttacker, recoil); + TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MaxHp50Recoil); effect = TRUE; } @@ -5847,8 +5827,14 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) case EFFECT_CHLOROBLAST: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { + enum Ability ability = GetBattlerAbility(gBattlerAttacker); + if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD) + || IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_MAGIC_GUARD)) + break; + s32 recoil = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP SetPassiveDamageAmount(gBattlerAttacker, recoil); + TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); effect = TRUE; } @@ -6044,6 +6030,43 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; + + case MOVEEND_GRUDGE: + if (gBattleStruct->tryGrudge + && IsBattlerTurnDamaged(gBattlerTarget) + && !IsBattlerAlive(gBattlerTarget) + && IsBattlerAlive(gBattlerAttacker) + && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget) + && !IsZMove(gCurrentMove) + && gCurrentMove != MOVE_STRUGGLE) + { + u32 moveIndex = gBattleStruct->chosenMovePositions[gBattlerAttacker]; + + gBattleStruct->tryGrudge = FALSE; + gBattleMons[gBattlerAttacker].pp[moveIndex] = 0; + BattleScriptCall(BattleScript_GrudgeTakesPp); + BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].pp[moveIndex]), &gBattleMons[gBattlerAttacker].pp[moveIndex]); + MarkBattlerForControllerExec(gBattlerAttacker); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) + } + gBattleScripting.moveendState++; + break; + + case MOVEEND_DESTINY_BOND: + if (gBattleStruct->tryDestinyBond + && IsBattlerTurnDamaged(gBattlerTarget) + && !IsBattlerAlive(gBattlerTarget) + && IsBattlerAlive(gBattlerAttacker) + && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) + && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)) + { + gBattleStruct->tryDestinyBond = FALSE; + gBattleStruct->passiveHpUpdate[gBattlerAttacker] = gBattleMons[gBattlerAttacker].hp; + BattleScriptCall(BattleScript_DestinyBondTakesLife); + effect = TRUE; + } + gBattleScripting.moveendState++; + break; case MOVEEND_ABSORB: if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE || !IsBattlerTurnDamaged(gBattlerTarget)) @@ -7705,8 +7728,6 @@ static void UpdateSentMonFlags(u32 battler) static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) { - gBattleMons[battler].volatiles.destinyBond = 0; - gHitMarker &= ~HITMARKER_DESTINYBOND; gBattleScripting.battler = battler; gBattleCommunication[MULTISTRING_CHOOSER] = multistringId; @@ -11596,22 +11617,8 @@ static void Cmd_unused_AA(void) { } -static void TrySetDestinyBondToHappen(void) +static void Cmd_unused_0xab(void) { - if (gBattleMons[gBattlerTarget].volatiles.destinyBond - && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget) - && !(gHitMarker & HITMARKER_GRUDGE)) - { - gHitMarker |= HITMARKER_DESTINYBOND; - } -} - -static void Cmd_trysetdestinybondtohappen(void) -{ - CMD_ARGS(); - - TrySetDestinyBondToHappen(); - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_settailwind(void) @@ -15475,20 +15482,6 @@ static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16 } } -void BS_TryUpdateRecoilTracker(void) -{ - NATIVE_ARGS(); - TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->moveDamage[gBattlerAttacker], MOVE_NONE); - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_TryUpdateLeadersCrestTracker(void) -{ - NATIVE_ARGS(); - TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE); - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_TryTidyUp(void) { NATIVE_ARGS(u8 clear, const u8 *jumpInstr); diff --git a/src/battle_tv.c b/src/battle_tv.c index 5e3939ed5c..661238ae6d 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -14,7 +14,7 @@ static bool8 IsNotSpecialBattleString(enum StringID stringId); static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3); static void TrySetBattleSeminarShow(void); -static void AddPointsOnFainting(bool8 targetFainted); +static void AddPointsOnFainting(void); static void AddPointsBasedOnWeather(u16 weatherFlags, u16 move, u8 moveSlot); static bool8 ShouldCalculateDamage(u16 move, s32 *dmg, u16 *powerOverride); @@ -124,12 +124,23 @@ static const u16 *const sPointsArray[] = // even if current Pokémon does not have corresponding move static const u16 sSpecialBattleStrings[] = { - STRINGID_PKMNPERISHCOUNTFELL, STRINGID_PKMNWISHCAMETRUE, STRINGID_PKMNLOSTPPGRUDGE, - STRINGID_PKMNTOOKFOE, STRINGID_PKMNABSORBEDNUTRIENTS, STRINGID_PKMNANCHOREDITSELF, - STRINGID_PKMNAFFLICTEDBYCURSE, STRINGID_PKMNSAPPEDBYLEECHSEED, STRINGID_PKMNLOCKEDINNIGHTMARE, - STRINGID_PKMNHURTBY, STRINGID_PKMNHURTBYBURN, STRINGID_PKMNHURTBYPOISON, - STRINGID_PKMNHURTBYSPIKES, STRINGID_ATTACKERFAINTED, STRINGID_TARGETFAINTED, - STRINGID_PKMNHITWITHRECOIL, STRINGID_PKMNCRASHED, TABLE_END + STRINGID_PKMNPERISHCOUNTFELL, + STRINGID_PKMNWISHCAMETRUE, + STRINGID_PKMNLOSTPPGRUDGE, + STRINGID_PKMNTOOKFOE, + STRINGID_PKMNABSORBEDNUTRIENTS, + STRINGID_PKMNANCHOREDITSELF, + STRINGID_PKMNAFFLICTEDBYCURSE, + STRINGID_PKMNSAPPEDBYLEECHSEED, + STRINGID_PKMNLOCKEDINNIGHTMARE, + STRINGID_PKMNHURTBY, + STRINGID_PKMNHURTBYBURN, + STRINGID_PKMNHURTBYPOISON, + STRINGID_PKMNHURTBYSPIKES, + STRINGID_BATTLERFAINTED, + STRINGID_PKMNHITWITHRECOIL, + STRINGID_PKMNCRASHED, + TABLE_END }; // code @@ -139,7 +150,7 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId) u32 atkSide, defSide, effSide, scriptingSide; struct Pokemon *atkMon, *defMon; u8 moveSlot; - u32 atkFlank, defFlank, effFlank; + u32 atkFlank, defFlank, effFlank, flank; u8 *perishCount; u16 *statStringId, *finishedMoveId; @@ -452,8 +463,6 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId) tvPtr->pos[atkSide][atkFlank].mudSportMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1; tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = moveSlot; break; - case STRINGID_ATTACKERFAINTED: - AddPointsOnFainting(FALSE); case STRINGID_RETURNMON: if (tvPtr->pos[atkSide][atkFlank].waterSportMonId != 0) { @@ -466,17 +475,22 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId) tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = 0; } break; - case STRINGID_TARGETFAINTED: - AddPointsOnFainting(TRUE); - if (tvPtr->pos[atkSide][defFlank].waterSportMonId != 0) + case STRINGID_BATTLERFAINTED: + AddPointsOnFainting(); + if (gBattlerAttacker == gBattleScripting.battler) + flank = atkFlank; + else + flank = defFlank; + + if (tvPtr->pos[atkSide][flank].waterSportMonId != 0) { - tvPtr->pos[atkSide][defFlank].waterSportMonId = 0; - tvPtr->pos[atkSide][defFlank].waterSportMoveSlot = 0; + tvPtr->pos[atkSide][flank].waterSportMonId = 0; + tvPtr->pos[atkSide][flank].waterSportMoveSlot = 0; } - if (tvPtr->pos[atkSide][defFlank].mudSportMonId != 0) + if (tvPtr->pos[atkSide][flank].mudSportMonId != 0) { - tvPtr->pos[atkSide][defFlank].mudSportMonId = 0; - tvPtr->pos[atkSide][defFlank].mudSportMoveSlot = 0; + tvPtr->pos[atkSide][flank].mudSportMonId = 0; + tvPtr->pos[atkSide][flank].mudSportMoveSlot = 0; } break; case STRINGID_PKMNRAISEDDEF: @@ -1064,7 +1078,7 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3) } } -static void AddPointsOnFainting(bool8 targetFainted) +static void AddPointsOnFainting(void) { struct BattleTv *tvPtr = &gBattleStruct->tv; u32 atkSide = GetBattlerSide(gBattlerAttacker); @@ -1187,7 +1201,7 @@ static void AddPointsOnFainting(bool8 targetFainted) } break; case FNT_RECOIL: - if (targetFainted == TRUE) + if (gBattlerAttacker == gBattleScripting.battler) { AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide, (gBattlerPartyIndexes[gBattlerAttacker]) * 4 + tvPtr->side[atkSide].usedMoveSlot); diff --git a/src/battle_util.c b/src/battle_util.c index abb87e3c4e..4460e72732 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -912,8 +912,7 @@ void HandleAction_NothingIsFainted(void) gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE; - gHitMarker &= ~(HITMARKER_DESTINYBOND - | HITMARKER_ATTACKSTRING_PRINTED + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_OBEYS); } @@ -926,8 +925,7 @@ void HandleAction_ActionFinished(void) gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); - gHitMarker &= ~(HITMARKER_DESTINYBOND - | HITMARKER_ATTACKSTRING_PRINTED + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_OBEYS); @@ -1786,8 +1784,6 @@ void TryToRevertMimicryAndFlags(void) bool32 BattleArenaTurnEnd(void) { - gHitMarker |= HITMARKER_GRUDGE; - if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) && gBattleStruct->arenaTurnCounter == 2 && IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT)) @@ -1799,9 +1795,6 @@ bool32 BattleArenaTurnEnd(void) BattleScriptExecute(BattleScript_ArenaDoJudgment); return TRUE; } - - gHitMarker &= ~HITMARKER_GRUDGE; - return FALSE; } diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 1adf565c3e..d97fb20f01 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -239,8 +239,8 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon are affected by Grudge") } SCENE { MESSAGE("The opposing Wobbuffet used Grudge!"); MESSAGE("Wobbuffet used Max Strike!"); - MESSAGE("Wobbuffet's Scratch lost all its PP due to the grudge!"); MESSAGE("The opposing Wobbuffet fainted!"); + MESSAGE("Wobbuffet's Scratch lost all its PP due to the grudge!"); } } @@ -860,7 +860,7 @@ SINGLE_BATTLE_TEST("Dynamax: Max Hailstorm sets up hail") MESSAGE("It started to hail!"); MESSAGE("The opposing Wobbuffet used Celebrate!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES); -#endif +#endif } } diff --git a/test/battle/move_effect/destiny_bond.c b/test/battle/move_effect/destiny_bond.c index ddebc6bb1f..17cf658772 100644 --- a/test/battle/move_effect/destiny_bond.c +++ b/test/battle/move_effect/destiny_bond.c @@ -10,12 +10,18 @@ SINGLE_BATTLE_TEST("Destiny Bond faints the opposing mon if it fainted from the { GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_DESTINY_BOND); MOVE(opponent, MOVE_SCRATCH); } + TURN { + MOVE(player, MOVE_DESTINY_BOND); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_DESTINY_BOND, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + MESSAGE("Wobbuffet fainted!"); MESSAGE("Wobbuffet took its attacker down with it!"); MESSAGE("The opposing Wobbuffet fainted!"); } diff --git a/test/battle/move_effect/grudge.c b/test/battle/move_effect/grudge.c index f0e0e53b6a..870d39c203 100644 --- a/test/battle/move_effect/grudge.c +++ b/test/battle/move_effect/grudge.c @@ -2,3 +2,52 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Grudge (Move Effect) test titles") + +SINGLE_BATTLE_TEST("Grudge depletes all pp of the move that fainted the target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_POUND, MOVE_SURF); }; + } WHEN { + TURN { + MOVE(player, MOVE_GRUDGE); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRUDGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + MESSAGE("Wobbuffet fainted!"); + } THEN { + EXPECT_GT(opponent->pp[0], 0); + EXPECT_EQ(opponent->pp[1], 0); + EXPECT_GT(opponent->pp[2], 0); + EXPECT_GT(opponent->pp[3], 0); + } +} + +SINGLE_BATTLE_TEST("Grudge does not depletes pp of a z-move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_POUND, MOVE_SURF); }; + } WHEN { + TURN { + MOVE(player, MOVE_GRUDGE); + MOVE(opponent, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); + SEND_OUT(player, 1); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRUDGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, opponent); + MESSAGE("Wobbuffet fainted!"); + } THEN { + EXPECT_GT(opponent->pp[0], 0); + EXPECT_GT(opponent->pp[1], 0); + EXPECT_GT(opponent->pp[2], 0); + EXPECT_GT(opponent->pp[3], 0); + } +}