From a82aa618fb77a5244ce4b54f02bc17e46dbf40ef Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:42:32 +0100 Subject: [PATCH] Adds Ability Shield activation message (#7224) --- asm/macros/battle_script.inc | 5 +++ data/battle_scripts_1.s | 32 +++++++++----- include/battle_scripts.h | 2 + include/battle_util.h | 3 +- include/constants/battle_string_ids.h | 1 + src/battle_message.c | 1 + src/battle_script_commands.c | 26 +++++++++++- src/battle_util.c | 17 +++++--- test/battle/hold_effect/ability_shield.c | 54 +++++++++++++++++++----- 9 files changed, 111 insertions(+), 30 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 11cca995c0..7c6cef0a45 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2461,6 +2461,11 @@ .4byte \jumpInstr .endm + .macro tryactivateabilityshield battler:req + callnative BS_TryActivateAbilityShield + .byte \battler + .endm + .macro setallytonexttarget jumpInstr:req jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f addbyte gBattlerTarget, 0x2 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0a908236f6..fc18ab1e76 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7176,9 +7176,20 @@ BattleScript_AbilityCantRaiseDefenderStat:: restoreattacker return +BattleScript_AbilityShieldProtects:: + saveattacker + copybyte gBattlerAttacker, gBattlerAbility + playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT + waitanimation + printstring STRINGID_ABILITYSHIELDPROTECTS + waitmessage B_WAIT_TIME_LONG + restoreattacker + return + BattleScript_AbilityPopUpTarget:: copybyte gBattlerAbility, gBattlerTarget BattleScript_AbilityPopUp:: + tryactivateabilityshield BS_ABILITY_BATTLER .if B_ABILITY_POP_UP == TRUE showabilitypopup BS_ABILITY_BATTLER pause 40 @@ -7428,14 +7439,10 @@ BattleScript_TryIntimidateHoldEffectsRet: BattleScript_IntimidateActivates:: savetarget -.if B_ABILITY_POP_UP == TRUE - showabilitypopup BS_ATTACKER - pause B_WAIT_TIME_LONG + call BattleScript_AbilityPopUp destroyabilitypopup -.endif setbyte gBattlerTarget, 0 BattleScript_IntimidateLoop: - jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_IntimidateLoopIncrement jumpiftargetally BattleScript_IntimidateLoopIncrement jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement jumpifvolatile BS_TARGET, VOLATILE_SUBSTITUTE, BattleScript_IntimidateLoopIncrement @@ -7488,16 +7495,12 @@ BattleScript_IntimidateInReverse:: BattleScript_SupersweetSyrupActivates:: savetarget -.if B_ABILITY_POP_UP == TRUE - showabilitypopup BS_ATTACKER - pause B_WAIT_TIME_LONG + call BattleScript_AbilityPopUp destroyabilitypopup -.endif printstring STRINGID_SUPERSWEETAROMAWAFTS waitmessage B_WAIT_TIME_LONG setbyte gBattlerTarget, 0 BattleScript_SupersweetSyrupLoop: - jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_SupersweetSyrupLoopIncrement jumpiftargetally BattleScript_SupersweetSyrupLoopIncrement jumpifabsent BS_TARGET, BattleScript_SupersweetSyrupLoopIncrement jumpifvolatile BS_TARGET, VOLATILE_SUBSTITUTE, BattleScript_SupersweetSyrupLoopIncrement @@ -7510,8 +7513,12 @@ BattleScript_SupersweetSyrupEffect: printfromtable gStatDownStringIds BattleScript_SupersweetSyrupEffect_WaitString: waitmessage B_WAIT_TIME_LONG + saveattacker + savetarget copybyte sBATTLER, gBattlerTarget call BattleScript_TryIntimidateHoldEffects + restoreattacker + restoretarget BattleScript_SupersweetSyrupLoopIncrement: addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_SupersweetSyrupLoop @@ -7977,6 +7984,11 @@ BattleScript_ProteanActivates:: waitmessage B_WAIT_TIME_LONG return +BattleScript_AbilityAvoidsDamage:: + call BattleScript_AbilityPopUp + printfromtable gMissStringIds @ waitmessage is executed next so no waitmessage here + return + BattleScript_TeraShellDistortingTypeMatchups:: pause B_WAIT_TIME_SHORTEST call BattleScript_AbilityPopUpScripting diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 80d6693bd5..379e45cd39 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -243,6 +243,8 @@ extern const u8 BattleScript_ConsumableStatRaiseRet[]; extern const u8 BattleScript_BerryFocusEnergyRet[]; extern const u8 BattleScript_BerryFocusEnergyEnd2[]; extern const u8 BattleScript_ActionSelectionItemsCantBeUsed[]; +extern const u8 BattleScript_AbilityAvoidsDamage[]; +extern const u8 BattleScript_AbilityShieldProtects[]; extern const u8 BattleScript_ArenaTurnBeginning[]; extern const u8 BattleScript_PalacePrintFlavorText[]; extern const u8 BattleScript_ArenaDoJudgment[]; diff --git a/include/battle_util.h b/include/battle_util.h index 9b3d8a4b5a..5e86fc615a 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -257,7 +257,8 @@ bool32 TryPrimalReversion(u32 battler); bool32 IsNeutralizingGasOnField(void); bool32 IsMoldBreakerTypeAbility(u32 battler, u32 ability); u32 GetBattlerAbilityIgnoreMoldBreaker(u32 battler); -u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker); +u32 GetBattlerAbilityNoAbilityShield(u32 battler); +u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityShield); u32 GetBattlerAbility(u32 battler); u32 IsAbilityOnSide(u32 battler, u32 ability); u32 IsAbilityOnOpposingSide(u32 battler, u32 ability); diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 57b56d4cbe..57cf6f7021 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -738,6 +738,7 @@ enum StringID STRINGID_TOXICSPIKESBADLYPOISONED, STRINGID_POWERCONSTRUCTPRESENCEOFMANY, STRINGID_POWERCONSTRUCTTRANSFORM, + STRINGID_ABILITYSHIELDPROTECTS, STRINGID_COUNT }; diff --git a/src/battle_message.c b/src/battle_message.c index 35d8b23083..eb44ca96ee 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -897,6 +897,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_QUESTIONFORFEITBATTLE] = COMPOUND_STRING("Would you like to give up on this battle and quit now? Quitting the battle is the same as losing the battle."), [STRINGID_POWERCONSTRUCTPRESENCEOFMANY] = COMPOUND_STRING("You sense the presence of many!"), [STRINGID_POWERCONSTRUCTTRANSFORM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} transformed into its Complete Forme!"), + [STRINGID_ABILITYSHIELDPROTECTS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s Ability is protected by the effects of its {B_LAST_ITEM}!"), }; const u16 gTrainerUsedItemStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7bc8d0664c..2298c20be4 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2879,8 +2879,15 @@ static void Cmd_resultmessage(void) return; } - if (gBattleStruct->missStringId[gBattlerTarget] > B_MSG_AVOIDED_ATK) // Wonder Guard or Levitate - show the ability pop-up - CreateAbilityPopUp(gBattlerTarget, gBattleMons[gBattlerTarget].ability, IsDoubleBattle()); + if (gBattleStruct->missStringId[gBattlerTarget] > B_MSG_AVOIDED_ATK) // Wonder Guard or Levitate + { + gBattlerAbility = gBattlerTarget; + gBattleCommunication[MULTISTRING_CHOOSER] = gBattleStruct->missStringId[gBattlerTarget]; + gBattlescriptCurrInstr = cmd->nextInstr; + BattleScriptCall(BattleScript_AbilityAvoidsDamage); + return; + } + gBattleCommunication[MSG_DISPLAY] = 1; stringId = gMissStringIds[gBattleStruct->missStringId[gBattlerTarget]]; } @@ -18601,3 +18608,18 @@ void BS_JumpIfAbilityCantBeSuppressed(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_TryActivateAbilityShield(void) +{ + NATIVE_ARGS(u8 battler); + u32 battler = GetBattlerForBattleScript(cmd->battler); + + gBattlescriptCurrInstr = cmd->nextInstr; + + if (GetBattlerAbilityNoAbilityShield(battler) != GetBattlerAbility(battler)) + { + gLastUsedItem = gBattleMons[battler].item; + RecordItemEffectBattle(battler, GetItemHoldEffect(gLastUsedItem)); + BattleScriptCall(BattleScript_AbilityShieldProtects); + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 903f6ab3b9..30ee3b5541 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3878,7 +3878,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_INTIMIDATE: if (!gSpecialStatuses[battler].switchInAbilityDone) { - gBattlerAttacker = battler; + gBattlerAbility = gBattlerAttacker = battler; gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_ATK, 1, TRUE); BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivates); @@ -3889,7 +3889,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!gSpecialStatuses[battler].switchInAbilityDone && !gBattleStruct->partyState[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].supersweetSyrup) { - gBattlerAttacker = battler; + gBattlerAbility = gBattlerAttacker = battler; gSpecialStatuses[battler].switchInAbilityDone = TRUE; gBattleStruct->partyState[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].supersweetSyrup = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SupersweetSyrupActivates); @@ -5415,19 +5415,24 @@ static inline bool32 CanBreakThroughAbility(u32 battlerAtk, u32 battlerDef, u32 && gCurrentTurnActionNumber < gBattlersCount); } +u32 GetBattlerAbilityNoAbilityShield(u32 battler) +{ + return GetBattlerAbilityInternal(battler, FALSE, TRUE); +} + u32 GetBattlerAbilityIgnoreMoldBreaker(u32 battler) { - return GetBattlerAbilityInternal(battler, TRUE); + return GetBattlerAbilityInternal(battler, TRUE, FALSE); } u32 GetBattlerAbility(u32 battler) { - return GetBattlerAbilityInternal(battler, FALSE); + return GetBattlerAbilityInternal(battler, FALSE, FALSE); } -u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker) +u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityShield) { - bool32 hasAbilityShield = GetBattlerHoldEffectIgnoreAbility(battler, TRUE) == HOLD_EFFECT_ABILITY_SHIELD; + bool32 hasAbilityShield = !noAbilityShield && GetBattlerHoldEffectIgnoreAbility(battler, TRUE) == HOLD_EFFECT_ABILITY_SHIELD; bool32 abilityCantBeSuppressed = gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed; if (abilityCantBeSuppressed) diff --git a/test/battle/hold_effect/ability_shield.c b/test/battle/hold_effect/ability_shield.c index 3159d0d8a4..ded39ff05b 100644 --- a/test/battle/hold_effect/ability_shield.c +++ b/test/battle/hold_effect/ability_shield.c @@ -6,7 +6,7 @@ ASSUMPTIONS ASSUME(gItemsInfo[ITEM_ABILITY_SHIELD].holdEffect == HOLD_EFFECT_ABILITY_SHIELD); } -SINGLE_BATTLE_TEST("Ability Shield prevents Neutralizing Gas") +SINGLE_BATTLE_TEST("Ability Shield protects against Neutralizing Gas") { u32 item; @@ -22,12 +22,14 @@ SINGLE_BATTLE_TEST("Ability Shield prevents Neutralizing Gas") ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); MESSAGE("Neutralizing gas filled the area!"); if (item == ITEM_ABILITY_SHIELD) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Torkoal's Ability is protected by the effects of its Ability Shield!"); ABILITY_POPUP(player, ABILITY_DROUGHT); - MESSAGE("Torkoal's Drought intensified the sun's rays!"); } else { NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Torkoal's Ability is protected by the effects of its Ability Shield!"); ABILITY_POPUP(player, ABILITY_DROUGHT); - MESSAGE("Torkoal's Drought intensified the sun's rays!"); } } } @@ -41,17 +43,18 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker") PARAMETRIZE { item = ITEM_NONE; } GIVEN { - PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); } - OPPONENT(SPECIES_TINKATON) { Ability(ABILITY_MOLD_BREAKER); } + ASSUME(GetMoveType(MOVE_EARTHQUAKE) == TYPE_GROUND); + PLAYER(SPECIES_FLYGON) { Ability(ABILITY_LEVITATE); Item(item); } + OPPONENT(SPECIES_EXCADRILL) { Ability(ABILITY_MOLD_BREAKER); } } WHEN { - TURN { MOVE(opponent, MOVE_GIGATON_HAMMER); } + TURN { MOVE(opponent, MOVE_EARTHQUAKE); } } SCENE { if (item == ITEM_ABILITY_SHIELD) { - NONE_OF { - MESSAGE("Shedinja fainted!"); - } + ABILITY_POPUP(player, ABILITY_LEVITATE); + NOT HP_BAR(player); } else { - MESSAGE("Shedinja fainted!"); + NOT ABILITY_POPUP(player, ABILITY_LEVITATE); + HP_BAR(player); } } } @@ -64,10 +67,12 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Mycelium Might") PARAMETRIZE { item = ITEM_NONE; } GIVEN { + ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS); + ASSUME(GetMoveNonVolatileStatus(MOVE_SPORE) == MOVE_EFFECT_SLEEP); PLAYER(SPECIES_VIGOROTH) { Ability(ABILITY_VITAL_SPIRIT); Item(item); } OPPONENT(SPECIES_TOEDSCOOL) { Ability(ABILITY_MYCELIUM_MIGHT); } } WHEN { - TURN { MOVE(opponent, MOVE_SPORE); MOVE(player, MOVE_SPORE); } + TURN { MOVE(opponent, MOVE_SPORE); } } SCENE { if (item == ITEM_ABILITY_SHIELD) { @@ -90,6 +95,7 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Sunsteel Strike") PARAMETRIZE { item = ITEM_NONE; } GIVEN { + ASSUME(MoveIgnoresTargetAbility(MOVE_SUNSTEEL_STRIKE)); PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -104,3 +110,29 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Sunsteel Strike") } } } + +SINGLE_BATTLE_TEST("Ability Shield protects against Skill Swap") +{ + u32 item; + + PARAMETRIZE { item = ITEM_ABILITY_SHIELD; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); Item(item); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP); } + } SCENE { + if (item == ITEM_ABILITY_SHIELD) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + } + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + } + } +}