From eee546df06332c217961517163e7ab425290dd51 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 12 Oct 2025 08:59:33 +0200 Subject: [PATCH] Item battle effect refactor (#7857) --- asm/macros/battle_script.inc | 9 +- data/battle_scripts_1.s | 159 +- include/battle.h | 6 +- include/battle_ai_util.h | 6 +- include/battle_hold_effects.h | 52 + include/battle_main.h | 5 +- include/battle_script_commands.h | 4 +- include/battle_scripts.h | 18 +- include/battle_util.h | 62 +- include/config/battle.h | 2 - include/constants/battle_script_commands.h | 22 +- include/constants/battle_string_ids.h | 10 +- include/constants/hold_effects.h | 12 +- include/item.h | 2 +- src/battle_ai_field_statuses.c | 1 - src/battle_ai_main.c | 7 +- src/battle_ai_switch_items.c | 11 +- src/battle_ai_util.c | 25 +- src/battle_controller_player.c | 1 - src/battle_debug.c | 9 +- src/battle_dynamax.c | 3 +- src/battle_end_turn.c | 38 +- src/battle_hold_effects.c | 1293 +++++++++++++ src/battle_main.c | 46 +- src/battle_message.c | 22 +- src/battle_script_commands.c | 327 ++-- src/battle_terastal.c | 3 +- src/battle_util.c | 1657 +---------------- src/battle_z_move.c | 7 +- src/data/hold_effects.h | 647 +++++++ src/data/items.h | 8 +- src/daycare.c | 1 - src/item.c | 2 +- src/pokemon.c | 3 +- src/pokemon_summary_screen.c | 1 - test/battle/ability/unburden.c | 2 +- test/battle/hold_effect/kee_berry.c | 16 + test/battle/hold_effect/maranga_berry.c | 16 + test/battle/hold_effect/restore_hp.c | 17 - test/battle/hold_effect/seeds.c | 8 +- test/battle/move_effect/magic_room.c | 39 + .../battle/move_effect_secondary/steal_item.c | 22 +- test/test_runner_battle.c | 2 +- 43 files changed, 2566 insertions(+), 2037 deletions(-) create mode 100644 include/battle_hold_effects.h create mode 100644 src/battle_hold_effects.c create mode 100644 src/data/hold_effects.h diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 19a4da0e95..32eef8a357 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -13,7 +13,7 @@ .byte 0x2 .endm - .macro unused0x3 + .macro unused_0x3 .byte 0x3 .endm @@ -1011,9 +1011,10 @@ .4byte \jumpInstr .endm - .macro tryrestorehpberry battler:req + .macro tryactivateitem battler:req, flag:req .byte 0xbb .byte \battler + .byte \flag .endm .macro halvehp failInstr:req @@ -1762,6 +1763,10 @@ .2byte \flags .endm + .macro clearspecialstatuses + callnative BS_ClearSpecialStatuses + .endm + .macro clearmoveresultflags flags:req callnative BS_ClearMoveResultFlags .2byte \flags diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b4bc6e9e69..760d421696 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -722,7 +722,7 @@ BattleScript_EffectFlingConsumeBerry: battleritemtolastuseditem setbyte sBERRY_OVERRIDE, 1 @ override the requirements for eating berries orword gHitMarker, HITMARKER_DISABLE_ANIMATION - consumeberry BS_TARGET, TRUE + consumeberry BS_TARGET, FALSE bicword gHitMarker, HITMARKER_DISABLE_ANIMATION setbyte sBERRY_OVERRIDE, 0 restorebattleritem @@ -1404,7 +1404,7 @@ BattleScript_FlowerShieldMoveTargetEnd: moveendto MOVEEND_NEXT_TARGET jumpifnexttargetvalid BattleScript_FlowerShieldLoop restoretarget - moveendfrom MOVEEND_ITEM_EFFECTS_ATTACKER + moveendfrom MOVEEND_ITEM_EFFECTS_ATTACKER_1 end BattleScript_EffectRototiller:: @@ -1460,6 +1460,7 @@ BattleScript_EffectBestow:: waitanimation printstring STRINGID_BESTOWITEMGIVING waitmessage B_WAIT_TIME_LONG + tryactivateitem BS_TARGET, ON_ITEM_USABLE_AGAIN trysymbiosis BS_ATTACKER goto BattleScript_MoveEnd @@ -4970,7 +4971,7 @@ BattleScript_MagicRoomEnds:: setbyte gBattlerTarget, 0 BattleScript_MagicRoomHealingItemsLoop: copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattlerTarget, 1 - tryrestorehpberry BS_ATTACKER + tryactivateitem BS_ATTACKER, ON_ITEM_USABLE_AGAIN addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_MagicRoomHealingItemsLoop end2 @@ -5416,7 +5417,6 @@ BattleScript_BerserkActivates:: call BattleScript_StatUp BattleScript_BerserkActivatesTryBerry: restoreattacker - tryrestorehpberry BS_EFFECT_BATTLER return BattleScript_AngerShellActivates:: @@ -5437,7 +5437,6 @@ BattleScript_AngerShellTrySpAtk: BattleScript_AngerShellTrySpeed: modifybattlerstatstage BS_EFFECT_BATTLER, STAT_SPEED, INCREASE, 1, BattleScript_AngerShellRet, ANIM_ON BattleScript_AngerShellRet: - tryrestorehpberry BS_EFFECT_BATTLER return BattleScript_WindPowerActivates:: @@ -5574,15 +5573,17 @@ BattleScript_FutureAttackEnd:: moveendcase MOVEEND_RAGE moveendcase MOVEEND_ABILITIES moveendcase MOVEEND_COLOR_CHANGE - moveendfromto MOVEEND_ITEM_EFFECTS_ALL, MOVEEND_UPDATE_LAST_MOVES - setmoveresultflags 0 - end2 + moveendcase MOVEEND_ITEM_EFFECTS_TARGET + moveendfromto MOVEEND_SYMBIOSIS, MOVEEND_UPDATE_LAST_MOVES + goto BattleScript_FutureAttackClearResults BattleScript_FutureAttackMiss:: pause B_WAIT_TIME_SHORT setmoveresultflags MOVE_RESULT_FAILED resultmessage waitmessage B_WAIT_TIME_LONG +BattleScript_FutureAttackClearResults: setmoveresultflags 0 + clearspecialstatuses end2 BattleScript_NoMovesLeft:: @@ -6157,7 +6158,7 @@ BattleScript_DoTurnDmg: datahpupdate BS_ATTACKER tryfaintmon BS_ATTACKER checkteamslost BattleScript_DoTurnDmgEnd - tryrestorehpberry BS_ATTACKER + tryactivateitem BS_ATTACKER, ON_ITEM_HP_THRESHOLD BattleScript_DoTurnDmgEnd: end2 @@ -6399,7 +6400,7 @@ BattleScript_YawnEnd: BattleScript_EmbargoEndTurn:: printstring STRINGID_EMBARGOENDS waitmessage B_WAIT_TIME_LONG - tryrestorehpberry BS_ATTACKER + tryactivateitem BS_ATTACKER, ON_ITEM_USABLE_AGAIN end2 BattleScript_TelekinesisEndTurn:: @@ -6689,7 +6690,7 @@ BattleScript_PickupActivates:: call BattleScript_AbilityPopUp printstring STRINGID_XFOUNDONEY waitmessage B_WAIT_TIME_LONG - tryrestorehpberry BS_ATTACKER + tryactivateitem BS_ATTACKER, ON_ITEM_PICK_UP BattleScript_PickupActivatesEnd: end2 @@ -6699,7 +6700,7 @@ BattleScript_HarvestActivates:: call BattleScript_AbilityPopUp printstring STRINGID_HARVESTBERRY waitmessage B_WAIT_TIME_LONG - tryrestorehpberry BS_ATTACKER + tryactivateitem BS_ATTACKER, ON_BERRY_HARVEST BattleScript_HarvestActivatesEnd: end2 @@ -7222,6 +7223,7 @@ BattleScript_FlinchPrevention:: BattleScript_OwnTempoPrevents:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp + copybyte sBATTLER, gBattlerTarget printstring STRINGID_PKMNPREVENTSCONFUSIONWITH waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd @@ -7271,11 +7273,15 @@ BattleScript_AbilityNoSpecificStatLoss:: return BattleScript_StickyHoldActivates:: + call BattleScript_StickyHoldActivatesRet + goto BattleScript_MoveEnd + +BattleScript_StickyHoldActivatesRet:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_PKMNSXMADEYINEFFECTIVE waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return BattleScript_ColorChangeActivates:: call BattleScript_AbilityPopUp @@ -7696,73 +7702,13 @@ BattleScript_SubstituteFade:: printstring STRINGID_PKMNSUBSTITUTEFADED return -BattleScript_BerryCurePrlzEnd2:: - call BattleScript_BerryCureParRet +BattleScript_BerryCureStatusEnd2:: + call BattleScript_BerryCureStatusRet end2 -BattleScript_BerryCureParRet:: +BattleScript_BerryCureStatusRet:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMCUREDPARALYSIS - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - -BattleScript_BerryCurePsnEnd2:: - call BattleScript_BerryCurePsnRet - end2 - -BattleScript_BerryCurePsnRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMCUREDPOISON - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - -BattleScript_BerryCureBrnEnd2:: - call BattleScript_BerryCureBrnRet - end2 - -BattleScript_BerryCureBrnRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMHEALEDBURN - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - -BattleScript_BerryCureFrzEnd2:: - call BattleScript_BerryCureFrzRet - end2 - -BattleScript_BerryCureFrzRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMDEFROSTEDIT - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - -BattleScript_BerryCureFrbEnd2:: - call BattleScript_BerryCureFrzRet - end2 - -BattleScript_BerryCureFrbRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMHEALEDFROSTBITE - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - -BattleScript_BerryCureSlpEnd2:: - call BattleScript_BerryCureSlpRet - end2 - -BattleScript_BerryCureSlpRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMWOKEIT + printfromtable CureStatusBerryEffectStringID waitmessage B_WAIT_TIME_LONG updatestatusicon BS_SCRIPTING removeitem BS_SCRIPTING @@ -7797,25 +7743,12 @@ BattleScript_BerryCureConfusionRet:: removeitem BS_SCRIPTING return -BattleScript_BerryCureChosenStatusEnd2:: - call BattleScript_BerryCureChosenStatusRet - end2 - -BattleScript_BerryCureChosenStatusRet:: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printfromtable gBerryEffectStringIds - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - removeitem BS_SCRIPTING - return - BattleScript_MentalHerbCureRet:: - playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printfromtable gMentalHerbCureStringIds waitmessage B_WAIT_TIME_LONG updatestatusicon BS_SCRIPTING removeitem BS_SCRIPTING - copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe return BattleScript_MentalHerbCureEnd2:: @@ -7837,7 +7770,7 @@ BattleScript_ItemHealHP_RemoveItemRet:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_ItemHealHP_RemoveItemRet_AbilityPopUp goto BattleScript_ItemHealHP_RemoveItemRet_Anim BattleScript_ItemHealHP_RemoveItemRet_AbilityPopUp: - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting BattleScript_ItemHealHP_RemoveItemRet_Anim: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_PKMNSITEMRESTOREDHEALTH @@ -7852,7 +7785,7 @@ BattleScript_ItemHealHP_RemoveItemEnd2:: jumpifability BS_ATTACKER, ABILITY_RIPEN, BattleScript_ItemHealHP_RemoveItemEnd2_AbilityPopUp goto BattleScript_ItemHealHP_RemoveItemEnd2_Anim BattleScript_ItemHealHP_RemoveItemEnd2_AbilityPopUp: - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting BattleScript_ItemHealHP_RemoveItemEnd2_Anim: playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_PKMNSITEMRESTOREDHEALTH @@ -7888,6 +7821,11 @@ BattleScript_AirBaloonMsgIn:: waitmessage B_WAIT_TIME_LONG end3 +BattleScript_AirBalloonMsgInRet:: + printstring STRINGID_AIRBALLOONFLOAT + waitmessage B_WAIT_TIME_LONG + return + BattleScript_AirBaloonMsgPop:: printstring STRINGID_AIRBALLOONPOP waitmessage B_WAIT_TIME_LONG @@ -7996,24 +7934,22 @@ BattleScript_ConsumableStatRaiseEnd2:: end2 BattleScript_ConsumableStatRaiseRet:: - @ to ensure `statbuffchange` has correct battler id, backup and use target - savetarget - copybyte gBattlerTarget, sBATTLER jumpifnotberry BS_SCRIPTING, BattleScript_ConsumableStatRaiseRet_Anim - @ check ripen popup if consuming berry jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_ConsumableStatRaiseRet_AbilityPopup goto BattleScript_ConsumableStatRaiseRet_Anim BattleScript_ConsumableStatRaiseRet_AbilityPopup: call BattleScript_AbilityPopUp BattleScript_ConsumableStatRaiseRet_Anim: - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_ConsumableStatRaiseRet_End + statbuffchange BS_SCRIPTING, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_ConsumableStatRaiseRet_End playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_ConsumableStatRaiseRet_End + statbuffchange BS_SCRIPTING, STAT_CHANGE_ALLOW_PTR, BattleScript_ConsumableStatRaiseRet_End setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_CHANGED_ITEM + savetarget + copybyte gBattlerTarget, sBATTLER @ BattleScript_StatUp uses target as a message arg call BattleScript_StatUp + restoretarget removeitem BS_SCRIPTING BattleScript_ConsumableStatRaiseRet_End: - restoretarget return BattleScript_BerryFocusEnergyRet:: @@ -8177,7 +8113,7 @@ BattleScript_MirrorHerbCopyStatChange:: removeitem BS_SCRIPTING BattleScript_MirrorHerbStartCopyStats: copyfoesstatincrease BS_SCRIPTING, BattleScript_MirrorHerbStartReturn - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorHerbStartReturn + statbuffchange BS_SCRIPTING, STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorHerbStartReturn setbyte sSTAT_ANIM_PLAYED, TRUE @ play stat change animation only once goto BattleScript_MirrorHerbStartCopyStats BattleScript_MirrorHerbStartReturn: @@ -8283,7 +8219,7 @@ BattleScript_MicleBerryActivateRet:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_MicleBerryActivateRet_Ripen goto BattleScript_MicleBerryActivateRet_Anim BattleScript_MicleBerryActivateRet_Ripen: - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting BattleScript_MicleBerryActivateRet_Anim: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_MICLEBERRYACTIVATES @@ -8950,18 +8886,15 @@ BattleScript_CouldntFullyProtect:: return BattleScript_BerserkGeneRet:: - saveattacker - savetarget - copybyte gBattlerTarget, sBATTLER - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_BerserkGeneRet_TryConfuse - playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_BerserkGeneRet_TryConfuse + statbuffchange BS_SCRIPTING, STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_ONLY_CHECKING, BattleScript_BerserkGeneRet_TryConfuse + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 + statbuffchange BS_SCRIPTING, STAT_CHANGE_ALLOW_PTR, BattleScript_BerserkGeneRet_TryConfuse setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_CHANGED_ITEM call BattleScript_StatUp BattleScript_BerserkGeneRet_TryConfuse: - jumpifability BS_ATTACKER, ABILITY_OWN_TEMPO, BattleScript_BerserkGeneRet_OwnTempoPrevents + jumpifability BS_SCRIPTING, ABILITY_OWN_TEMPO, BattleScript_BerserkGeneRet_OwnTempoPrevents jumpifsafeguard BattleScript_BerserkGeneRet_SafeguardProtected - seteffectprimary BS_ATTACKER, BS_ATTACKER, MOVE_EFFECT_CONFUSION + seteffectprimary BS_SCRIPTING, BS_SCRIPTING, MOVE_EFFECT_CONFUSION goto BattleScript_BerserkGeneRet_End BattleScript_BerserkGeneRet_SafeguardProtected:: pause B_WAIT_TIME_SHORT @@ -8970,13 +8903,11 @@ BattleScript_BerserkGeneRet_SafeguardProtected:: goto BattleScript_BerserkGeneRet_End BattleScript_BerserkGeneRet_OwnTempoPrevents: pause B_WAIT_TIME_SHORT - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting printstring STRINGID_PKMNPREVENTSCONFUSIONWITH waitmessage B_WAIT_TIME_LONG BattleScript_BerserkGeneRet_End: - restoreattacker - restoretarget - removeitem BS_ATTACKER + removeitem BS_SCRIPTING return BattleScript_BerserkGeneRetEnd2:: diff --git a/include/battle.h b/include/battle.h index 8f6498061b..74e9f1c611 100644 --- a/include/battle.h +++ b/include/battle.h @@ -5,6 +5,7 @@ #include "constants/abilities.h" #include "constants/battle.h" #include "constants/form_change_types.h" +#include "constants/hold_effects.h" #include "battle_main.h" #include "battle_message.h" #include "battle_util.h" @@ -581,10 +582,9 @@ struct BattlerState u32 pursuitTarget:1; u32 stompingTantrumTimer:2; u32 canPickupItem:1; - u32 itemCanBeKnockedOff:1; u32 ateBoost:1; u32 commanderSpecies:11; - u32 padding:4; + u32 padding:5; // End of Word }; @@ -780,7 +780,7 @@ struct BattleStruct u8 numHazards[NUM_BATTLE_SIDES]; u8 hazardsCounter:4; // Counter for applying hazard on switch in enum SubmoveState submoveAnnouncement:2; - u8 padding2:2; + u8 padding1:2; }; struct AiBattleData diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index fb51dde639..f175f88c40 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -115,7 +115,7 @@ u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits); bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod); enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId); -enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId); +enum HoldEffect AI_DecideHoldEffectForTurn(u32 battlerId); bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, enum Ability atkAbility, u32 move); u32 AI_GetWeather(void); u32 AI_GetSwitchinWeather(struct BattlePokemon battleMon); @@ -141,7 +141,7 @@ bool32 IsStatBoostingBerry(u32 item); bool32 CanKnockOffItem(u32 battler, u32 item); bool32 IsAbilityOfRating(enum Ability ability, s8 rating); bool32 AI_IsAbilityOnSide(u32 battlerId, enum Ability ability); -bool32 AI_MoveMakesContact(enum Ability ability, enum ItemHoldEffect holdEffect, u32 move); +bool32 AI_MoveMakesContact(enum Ability ability, enum HoldEffect holdEffect, u32 move); bool32 IsConsideringZMove(u32 battlerAtk, u32 battlerDef, u32 move); bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove); void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use); @@ -159,7 +159,7 @@ u32 CountNegativeStatStages(u32 battlerId); // move checks bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData); -bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect); +bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum HoldEffect holdEffect); bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category); enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo); struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef); diff --git a/include/battle_hold_effects.h b/include/battle_hold_effects.h new file mode 100644 index 0000000000..5ee1d4a494 --- /dev/null +++ b/include/battle_hold_effects.h @@ -0,0 +1,52 @@ +#ifndef GUARD_BATTLE_HOLD_EFFECTS +#define GUARD_BATTLE_HOLD_EFFECTS + +struct HoldEffectInfo +{ + u32 onSwitchIn:1; + u32 onSwitchInFirstTurn:1; + u32 mirrorHerb:1; + u32 mirrorHerbFirstTurn:1; + u32 whiteHerb:1; + u32 whiteHerbFirstTurn:1; + u32 whiteHerbEndTurn:1; + u32 onStatusChange:1; + u32 onHpThreshold:1; + u32 keeMarangaBerry:1; + u32 MentalHerb:1; + u32 onTargetAfterHit:1; + u32 onAttackerAfterHit:1; + u32 lifeOrbShellBell:1; + u32 leftovers:1; + u32 orbs:1; + u32 onEffect:1; + u32 padding:15; +}; + +extern const struct HoldEffectInfo gHoldEffectsInfo[]; + +typedef bool32 (*ActivationTiming)(enum HoldEffect holdEffect); +enum ItemEffect ItemBattleEffects(u32 primaryBattler, u32 secondaryBattler, enum HoldEffect holdEffect, ActivationTiming timing); +enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTiming timing); +enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, ActivationTiming timing); + +bool32 IsOnSwitchInActivation(enum HoldEffect holdEffect); +bool32 IsOnSwitchInFirstTurnActivation(enum HoldEffect holdEffect); +bool32 IsMirrorHerbActivation(enum HoldEffect holdEffect); +bool32 IsMirrorHerbFirstTurnActivation(enum HoldEffect holdEffect); +bool32 IsWhiteHerbActivation(enum HoldEffect holdEffect); +bool32 IsWhiteHerbFirstTurnActivation(enum HoldEffect holdEffect); +bool32 IsWhiteHerbEndTurnActivation(enum HoldEffect holdEffect); +bool32 IsOnStatusChangeActivation(enum HoldEffect holdEffect); +bool32 IsOnHpThresholdActivation(enum HoldEffect holdEffect); +bool32 IsKeeMarangaBerryActivation(enum HoldEffect holdEffect); +bool32 IsOnTargetHitActivation(enum HoldEffect holdEffect); +bool32 IsOnAttackerAfterHitActivation(enum HoldEffect holdEffect); +bool32 IsLifeOrbShellBellActivation(enum HoldEffect holdEffect); +bool32 IsLeftoversActivation(enum HoldEffect holdEffect); +bool32 IsOrbsActivation(enum HoldEffect holdEffect); +bool32 IsOnEffectActivation(enum HoldEffect holdEffect); +bool32 IsActivationForceTriggerItem(enum HoldEffect holdEffect); +bool32 IsActivationOnBerry(enum HoldEffect holdEffect); + +#endif // GUARD_BATTLE_HOLD_EFFECTS diff --git a/include/battle_main.h b/include/battle_main.h index 58b44504d9..844da53033 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -97,17 +97,16 @@ u8 IsRunningFromBattleImpossible(u32 battler); void SwitchTwoBattlersInParty(u32 battler, u32 battler2); void SwitchPartyOrder(u32 battler); void SwapTurnOrder(u8 id1, u8 id2); -u32 GetBattlerTotalSpeedStatArgs(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect); +u32 GetBattlerTotalSpeedStatArgs(u32 battler, enum Ability ability, enum HoldEffect holdEffect); u32 GetBattlerTotalSpeedStat(u32 battler); s32 GetChosenMovePriority(u32 battler, enum Ability ability); s32 GetBattleMovePriority(u32 battler, enum Ability ability, u32 move); s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, enum Ability ability1, enum Ability ability2, - enum ItemHoldEffect holdEffectBattler1, enum ItemHoldEffect holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2); + enum HoldEffect holdEffectBattler1, enum HoldEffect holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2); s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenMoves); s32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves); void RunBattleScriptCommands_PopCallbacksStack(void); void RunBattleScriptCommands(void); -void SpecialStatusesClear(void); u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState monInBattle); void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk); bool32 IsWildMonSmart(void); diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 17ccb58c59..df30037f4c 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -40,8 +40,8 @@ union TRANSPARENT StatChangeFlags }; }; -s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum ItemHoldEffect holdEffectAtk); -s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum ItemHoldEffect holdEffectAtk); +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk); +s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk); s32 GetCritHitOdds(s32 critChanceIndex); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, enum Ability atkAbility, enum Ability defAbility, u32 atkHoldEffect, u32 defHoldEffect); bool32 HasBattlerActedThisTurn(u32 battler); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 28c24f6eff..d179ad880c 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -191,6 +191,7 @@ extern const u8 BattleScript_OwnTempoPrevents[]; extern const u8 BattleScript_SoundproofProtected[]; extern const u8 BattleScript_AbilityNoSpecificStatLoss[]; extern const u8 BattleScript_StickyHoldActivates[]; +extern const u8 BattleScript_StickyHoldActivatesRet[]; extern const u8 BattleScript_ColorChangeActivates[]; extern const u8 BattleScript_RoughSkinActivates[]; extern const u8 BattleScript_CuteCharmActivates[]; @@ -206,22 +207,10 @@ extern const u8 BattleScript_TruantLoafingAround[]; extern const u8 BattleScript_IgnoresAndFallsAsleep[]; extern const u8 BattleScript_IgnoresAndHitsItself[]; extern const u8 BattleScript_SubstituteFade[]; -extern const u8 BattleScript_BerryCurePrlzEnd2[]; -extern const u8 BattleScript_BerryCureParRet[]; -extern const u8 BattleScript_BerryCurePsnEnd2[]; -extern const u8 BattleScript_BerryCurePsnRet[]; -extern const u8 BattleScript_BerryCureBrnEnd2[]; -extern const u8 BattleScript_BerryCureBrnRet[]; -extern const u8 BattleScript_BerryCureFrzEnd2[]; -extern const u8 BattleScript_BerryCureFrzRet[]; -extern const u8 BattleScript_BerryCureFrbEnd2[]; -extern const u8 BattleScript_BerryCureFrbRet[]; -extern const u8 BattleScript_BerryCureSlpEnd2[]; -extern const u8 BattleScript_BerryCureSlpRet[]; +extern const u8 BattleScript_BerryCureStatusEnd2[]; +extern const u8 BattleScript_BerryCureStatusRet[]; extern const u8 BattleScript_BerryCureConfusionEnd2[]; extern const u8 BattleScript_BerryCureConfusionRet[]; -extern const u8 BattleScript_BerryCureChosenStatusEnd2[]; -extern const u8 BattleScript_BerryCureChosenStatusRet[]; extern const u8 BattleScript_WhiteHerbEnd2[]; extern const u8 BattleScript_WhiteHerbRet[]; extern const u8 BattleScript_ItemHealHP_RemoveItemRet[]; @@ -356,6 +345,7 @@ extern const u8 BattleScript_TargetItemStatRaise[]; extern const u8 BattleScript_RockyHelmetActivates[]; extern const u8 BattleScript_ItemHurtEnd2[]; extern const u8 BattleScript_AirBaloonMsgIn[]; +extern const u8 BattleScript_AirBalloonMsgInRet[]; extern const u8 BattleScript_AirBaloonMsgPop[]; extern const u8 BattleScript_ItemHurtRet[]; extern const u8 BattleScript_ToxicOrb[]; diff --git a/include/battle_util.h b/include/battle_util.h index 47c5238f19..819b4a5b65 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -3,6 +3,7 @@ #include "move.h" #include "constants/battle_string_ids.h" +#include "constants/hold_effects.h" #define MOVE_LIMITATION_ZEROMOVE (1 << 0) #define MOVE_LIMITATION_PP (1 << 1) @@ -61,28 +62,6 @@ enum { ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, }; -// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try -enum ItemCaseId -{ - ITEMEFFECT_NONE, - ITEMEFFECT_ON_SWITCH_IN, - ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN, - ITEMEFFECT_LEFTOVERS, // Leftovers, Black Sludge - ITEMEFFECT_NORMAL, - ITEMEFFECT_TRY_HEALING, - ITEMEFFECT_MOVE_END, - ITEMEFFECT_KINGSROCK, - ITEMEFFECT_TARGET, - ITEMEFFECT_ORBS, - ITEMEFFECT_LIFEORB_SHELLBELL, - ITEMEFFECT_USE_LAST_ITEM, // move end effects for just the battler, not whole field - ITEMEFFECT_WHITE_HERB, - ITEMEFFECT_WHITE_HERB_ENDTURN, - ITEMEFFECT_WHITE_HERB_FIRST_TURN, - ITEMEFFECT_MIRROR_HERB, - ITEMEFFECT_MIRROR_HERB_FIRST_TURN, -}; - enum ItemEffect { ITEM_NO_EFFECT, @@ -185,8 +164,8 @@ struct DamageContext uq4_12_t typeEffectivenessModifier; enum Ability abilityAtk; enum Ability abilityDef; - enum ItemHoldEffect holdEffectAtk:16; - enum ItemHoldEffect holdEffectDef:16; + enum HoldEffect holdEffectAtk; + enum HoldEffect holdEffectDef; }; struct BattleContext @@ -217,9 +196,17 @@ enum SkyDropState #define SKY_DROP_NO_TARGET 0xFF #define SKY_DROP_RELEASED_TARGET 0xFE +enum EjectPackTiming +{ + FIRST_TURN, + END_TURN, + OTHER, +}; + void HandleAction_ThrowBall(void); u32 GetCurrentBattleWeather(void); bool32 EndOrContinueWeather(void); +bool32 IsUnnerveBlocked(u32 battler, u32 itemId); bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move); bool32 HandleMoveTargetRedirection(void); void HandleAction_UseMove(void); @@ -283,20 +270,19 @@ u32 GetProtectType(enum ProtectMethod method); bool32 CanBattlerEscape(u32 battler); // no ability check void BattleScriptExecute(const u8 *BS_ptr); void BattleScriptPushCursorAndCallback(const u8 *BS_ptr); -u32 ItemBattleEffects(enum ItemCaseId, u32 battler); void ClearVariousBattlerFlags(u32 battler); void HandleAction_RunBattleScript(void); u32 SetRandomTarget(u32 battler); u32 GetBattleMoveTarget(u16 move, u8 setTarget); u8 GetAttackerObedienceForAction(); -enum ItemHoldEffect GetBattlerHoldEffect(u32 battler); -enum ItemHoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler); -enum ItemHoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler); -enum ItemHoldEffect GetBattlerHoldEffectInternal(u32 battler, u32 ability); +enum HoldEffect GetBattlerHoldEffect(u32 battler); +enum HoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler); +enum HoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler); +enum HoldEffect GetBattlerHoldEffectInternal(u32 battler, u32 ability); u32 GetBattlerHoldEffectParam(u32 battler); -bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum ItemHoldEffect holdEffectAtk, u32 move); -bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum ItemHoldEffect holdEffectAtk, u32 move); -bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect); +bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 move); +bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 move); +bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect); u32 GetMoveSlot(u16 *moves, u32 move); u32 GetBattlerWeight(u32 battler); u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer); @@ -338,7 +324,7 @@ bool32 CanFling(u32 battler); bool32 IsTelekinesisBannedSpecies(u16 species); bool32 IsHealBlockPreventingMove(u32 battler, u32 move); bool32 IsBelchPreventingMove(u32 battler, u32 move); -bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId); +bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, u32 itemId); bool32 IsPartnerMonFromSameTrainer(u32 battler); enum DamageCategory GetCategoryBasedOnStats(u32 battler); void SetShellSideArmCategory(void); @@ -348,11 +334,9 @@ void TryRestoreHeldItems(void); bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item); void TrySaveExchangedItem(u32 battler, u16 stolenItem); bool32 IsPartnerMonFromSameTrainer(u32 battler); -enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID); bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes); void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind); -bool32 TryRoomService(u32 battler); bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); u16 GetUsedHeldItem(u32 battler); bool32 PickupHasValidTarget(u32 battler); @@ -382,7 +366,7 @@ bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum FunctionCallOption option); bool32 CanBeConfused(u32 battler); -bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect, u32 terrainFlag); +bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 terrainFlag); u32 GetBattlerAffectionHearts(u32 battler); void TryToRevertMimicryAndFlags(void); bool32 BattleArenaTurnEnd(void); @@ -414,12 +398,11 @@ bool32 HasWeatherEffect(void); bool32 IsAnyTargetAffected(u32 battlerAtk); bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move); bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); +void ChooseStatBoostAnimation(u32 battler); void UpdateStallMons(void); -bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId); -bool32 TrySwitchInEjectPack(enum ItemCaseId caseID); +bool32 TrySwitchInEjectPack(enum EjectPackTiming timing); u32 GetBattlerVolatile(u32 battler, enum Volatile _volatile); void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue); -u32 TryBoosterEnergy(u32 battler, enum Ability ability, enum ItemCaseId caseID); bool32 ItemHealMonVolatile(u32 battler, u16 itemId); void PushHazardTypeToQueue(u32 side, enum Hazards hazardType); bool32 IsHazardOnSide(u32 side, enum Hazards hazardType); @@ -436,5 +419,6 @@ u32 GetNaturePowerMove(u32 battler); void RemoveAbilityFlags(u32 battler); bool32 IsDazzlingAbility(enum Ability ability); bool32 IsAllowedToUseBag(void); +bool32 IsAnyTargetTurnDamaged(u32 battlerAtk); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/config/battle.h b/include/config/battle.h index 20fcc85109..c16f930950 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -168,8 +168,6 @@ #define B_DEFIANT_STICKY_WEB GEN_LATEST // In Gen9+, Defiant activates on Sticky Web regardless of who set it up. In Gen8, Defiant does not activate on Sticky Web set up by an ally after Court Change swaps its side. // Item settings -#define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. -#define B_BERRIES_INSTANT GEN_LATEST // In Gen4+, most berries activate on battle start/switch-in if applicable. In Gen3, they only activate either at the move end or turn end. #define B_CONFUSE_BERRIES_HEAL GEN_LATEST // Before Gen7, Figy and similar berries restore 1/8th of HP and trigger at half HP. In Gen7 they restore half HP, triggering at 25% HP. In Gen8 they heal 1/3rd of HP. #define B_X_ITEMS_BUFF GEN_LATEST // In Gen7+, the X Items raise a stat by 2 stages instead of 1. #define B_MENTAL_HERB GEN_LATEST // In Gen5+, the Mental Herb cures Taunt, Encore, Torment, Heal Block, and Disable in addition to Infatuation from before. diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 49b83b3a19..8d8517df7f 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -135,39 +135,40 @@ enum MoveEndEffects MOVEEND_SYNCHRONIZE_TARGET, MOVEEND_ABILITIES, MOVEEND_ABILITIES_ATTACKER, - MOVEEND_STATUS_IMMUNITY_ABILITIES, + MOVEEND_STATUS_IMMUNITY_ABILITIES, // TODO: Do berries come before???? MOVEEND_SYNCHRONIZE_ATTACKER, MOVEEND_ATTACKER_INVISIBLE, MOVEEND_ATTACKER_VISIBLE, MOVEEND_TARGET_VISIBLE, MOVEEND_ITEM_EFFECTS_TARGET, - MOVEEND_ITEM_EFFECTS_ALL, + MOVEEND_ITEM_EFFECTS_ATTACKER_1, MOVEEND_SYMBIOSIS, - MOVEEND_KINGSROCK, // These item effects will occur each strike of a multi-hit move MOVEEND_SUBSTITUTE, MOVEEND_SKY_DROP_CONFUSE, MOVEEND_UPDATE_LAST_MOVES, MOVEEND_MIRROR_MOVE, MOVEEND_DEFROST, MOVEEND_NEXT_TARGET, // Everything up until here is handled for each strike of a spread move + MOVEEND_HP_THRESHHOLD_ITEMS_TARGET, // Activation only during a multi hit move / ability (Parental Bond)) MOVEEND_MULTIHIT_MOVE, MOVEEND_MOVE_BLOCK, - MOVEEND_ITEM_EFFECTS_ATTACKER, - MOVEEND_ITEM_THROAT_SPRAY, + MOVEEND_ITEM_EFFECTS_ATTACKER_2, MOVEEND_ABILITY_BLOCK, - MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip until Opportunist + MOVEEND_SHEER_FORCE, // If move is Sheer Force affected, skip to Eject Pack MOVEEND_COLOR_CHANGE, // Color Change / Berserk / Anger Shell - MOVEEND_RED_CARD, + MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET, + MOVEEND_RED_CARD, MOVEEND_EJECT_BUTTON, - MOVEEND_LIFEORB_SHELLBELL, + MOVEEND_LIFE_ORB_SHELL_BELL, MOVEEND_FORM_CHANGE, MOVEEND_EMERGENCY_EXIT, MOVEEND_EJECT_PACK, MOVEEND_HIT_ESCAPE, + MOVEEND_ITEMS_EFFECTS_ALL, + MOVEEND_WHITE_HERB, MOVEEND_OPPORTUNIST, MOVEEND_MIRROR_HERB, MOVEEND_PICKPOCKET, - MOVEEND_WHITE_HERB, MOVEEND_THIRD_MOVE_BLOCK, MOVEEND_CHANGED_ITEMS, MOVEEND_SAME_MOVE_TURNS, @@ -175,6 +176,9 @@ enum MoveEndEffects MOVEEND_DANCER, MOVEEND_PURSUIT_NEXT_ACTION, MOVEEND_COUNT, + + // This guarantees a correct jump if new moveends are added directly after MOVEEND_HIT_ESCAPE + MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE = (MOVEEND_HIT_ESCAPE + 1), }; // switch cases diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 35bf757580..43e0e050f4 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -977,9 +977,15 @@ enum FlashFireStringID B_MSG_FLASH_FIRE_NO_BOOST, }; -// gBerryEffectStringIds -enum BerryEffectStringID +// CureStatusBerryEffectStringID +enum CureStatusBerryEffectStringID { + B_MSG_CURED_PARALYSIS, + B_MSG_CURED_POISON, + B_MSG_CURED_BURN, + B_MSG_CURED_FREEEZE, + B_MSG_CURED_FROSTBITE, + B_MSG_CURED_SLEEP, B_MSG_CURED_PROBLEM, B_MSG_NORMALIZED_STATUS, }; diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index 101a198b81..f91ea6d5c9 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -1,7 +1,7 @@ #ifndef GUARD_HOLD_EFFECTS_H #define GUARD_HOLD_EFFECTS_H -enum ItemHoldEffect +enum __attribute__((packed)) HoldEffect { HOLD_EFFECT_NONE, HOLD_EFFECT_RESTORE_HP, @@ -120,7 +120,7 @@ enum ItemHoldEffect // Gen7 hold effects HOLD_EFFECT_PROTECTIVE_PADS, HOLD_EFFECT_TERRAIN_EXTENDER, - HOLD_EFFECT_SEEDS, + HOLD_EFFECT_TERRAIN_SEED, HOLD_EFFECT_ADRENALINE_ORB, HOLD_EFFECT_MEMORY, HOLD_EFFECT_Z_CRYSTAL, @@ -151,4 +151,12 @@ enum ItemHoldEffect #define HOLD_EFFECT_PARAM_MISTY_TERRAIN 2 #define HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN 3 +enum ActivationState +{ + ON_ITEM_USABLE_AGAIN, + ON_ITEM_PICK_UP, + ON_BERRY_HARVEST, + ON_ITEM_HP_THRESHOLD, +}; + #endif // GUARD_HOLD_EFFECTS_H diff --git a/include/item.h b/include/item.h index 1f3b1fab1b..eaec831fe5 100644 --- a/include/item.h +++ b/include/item.h @@ -245,6 +245,6 @@ u32 GetItemFlingPower(u32 itemId); u32 GetItemStatus1Mask(u16 itemId); bool32 ItemHasVolatileFlag(u16 itemId, enum Volatile volatile); u32 GetItemSellPrice(u32 itemId); -bool32 IsHoldEffectChoice(enum ItemHoldEffect holdEffect); +bool32 IsHoldEffectChoice(enum HoldEffect holdEffect); #endif // GUARD_ITEM_H diff --git a/src/battle_ai_field_statuses.c b/src/battle_ai_field_statuses.c index 7a1e1cbd58..81d80d29c5 100644 --- a/src/battle_ai_field_statuses.c +++ b/src/battle_ai_field_statuses.c @@ -20,7 +20,6 @@ #include "constants/abilities.h" #include "constants/battle_ai.h" #include "constants/battle_move_effects.h" -#include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/items.h" diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 1cd06fb952..3bd1e3a164 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -23,7 +23,6 @@ #include "constants/abilities.h" #include "constants/battle_ai.h" #include "constants/battle_move_effects.h" -#include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/items.h" #include "constants/trainers.h" @@ -38,7 +37,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battler); static u32 ChooseMoveOrAction_Doubles(u32 battler); static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 battlerAtk, u32 battlerDef); static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef); -static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect); +static bool32 IsPinchBerryItemEffect(enum HoldEffect holdEffect); static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef); // ewram @@ -3781,7 +3780,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) return score; } -static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect) +static bool32 IsPinchBerryItemEffect(enum HoldEffect holdEffect) { switch (holdEffect) { @@ -4014,7 +4013,7 @@ static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef) static s32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData) { - enum ItemHoldEffect holdEffect = aiData->holdEffects[battlerAtk]; + enum HoldEffect holdEffect = aiData->holdEffects[battlerAtk]; s32 score = 0; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index bc2dedbcab..8099ea9c0d 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -7,7 +7,6 @@ #include "battle_anim.h" #include "battle_controllers.h" #include "battle_main.h" -#include "constants/hold_effects.h" #include "battle_setup.h" #include "data.h" #include "item.h" @@ -668,7 +667,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler) { bool32 switchMon = FALSE; enum Ability monAbility = gAiLogicData->abilities[battler]; - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battler]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[battler]; u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); u8 opposingBattler = GetBattlerAtPosition(opposingPosition); bool32 hasStatRaised = AnyStatIsRaised(battler); @@ -1027,7 +1026,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler) static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); u32 lastUsedMove = gAiLogicData->lastUsedMove[battler]; u32 opposingBattler = GetOppositeBattler(battler); bool32 moveAffectsTarget = TRUE; @@ -1616,7 +1615,7 @@ static s32 GetSwitchinWeatherImpact(void) { s32 weatherImpact = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP; enum Ability ability = gAiLogicData->switchinCandidate.battleMon.ability; - enum ItemHoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); + enum HoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); if (HasWeatherEffect()) { @@ -1681,7 +1680,7 @@ static u32 GetSwitchinRecurringHealing(void) { u32 recurringHealing = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP; enum Ability ability = gAiLogicData->switchinCandidate.battleMon.ability; - enum ItemHoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); + enum HoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); // Items if (ability != ABILITY_KLUTZ) @@ -1716,7 +1715,7 @@ static u32 GetSwitchinRecurringDamage(void) { u32 passiveDamage = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP; enum Ability ability = gAiLogicData->switchinCandidate.battleMon.ability; - enum ItemHoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); + enum HoldEffect holdEffect = GetItemHoldEffect(gAiLogicData->switchinCandidate.battleMon.item); // Items if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 63060e0995..07c98fba3a 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -20,7 +20,6 @@ #include "constants/abilities.h" #include "constants/battle_ai.h" #include "constants/battle_move_effects.h" -#include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/items.h" @@ -473,7 +472,7 @@ bool32 AI_BattlerAtMaxHp(u32 battlerId) bool32 AI_CanBattlerEscape(u32 battler) { - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battler]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[battler]; if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) return TRUE; @@ -542,7 +541,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) } // move checks -bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect) +bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum HoldEffect holdEffect) { if (ability == ABILITY_OVERCOAT || (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) @@ -1359,8 +1358,8 @@ uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef) s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 aiMoveConsidered, u32 playerMoveConsidered, enum ConsiderPriority considerPriority) { u32 speedBattlerAI, speedBattler; - enum ItemHoldEffect holdEffectAI = gAiLogicData->holdEffects[battlerAI]; - enum ItemHoldEffect holdEffectPlayer = gAiLogicData->holdEffects[battler]; + enum HoldEffect holdEffectAI = gAiLogicData->holdEffects[battlerAI]; + enum HoldEffect holdEffectPlayer = gAiLogicData->holdEffects[battler]; enum Ability abilityAI = gAiLogicData->abilities[battlerAI]; enum Ability abilityPlayer = gAiLogicData->abilities[battler]; @@ -1698,9 +1697,9 @@ enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId) return ABILITY_NONE; // Unknown. } -enum ItemHoldEffect AI_DecideHoldEffectForTurn(u32 battlerId) +enum HoldEffect AI_DecideHoldEffectForTurn(u32 battlerId) { - enum ItemHoldEffect holdEffect = HOLD_EFFECT_NONE; + enum HoldEffect holdEffect = HOLD_EFFECT_NONE; if (gBattleMons[battlerId].item == ITEM_NONE) // Failsafe for when user recorded an item but it was consumed return holdEffect; @@ -1959,7 +1958,7 @@ bool32 IsMoveRedirectionPrevented(u32 battlerAtk, u32 move, enum Ability atkAbil bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, enum Ability defAbility, u32 move) { - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battlerDef]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[battlerDef]; u32 accuracy = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex]; gPotentialItemEffectBattler = battlerDef; @@ -3056,7 +3055,7 @@ static u32 GetTrapDamage(u32 battlerId) { // ai has no knowledge about turns remaining u32 damage = 0; - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[gBattleStruct->wrappedBy[battlerId]]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[gBattleStruct->wrappedBy[battlerId]]; if (gBattleMons[battlerId].volatiles.wrapped) { if (holdEffect == HOLD_EFFECT_BINDING_BAND) @@ -3120,7 +3119,7 @@ static bool32 BattlerAffectedByHail(u32 battlerId, enum Ability ability) static u32 GetWeatherDamage(u32 battlerId) { enum Ability ability = gAiLogicData->abilities[battlerId]; - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battlerId]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[battlerId]; u32 damage = 0; u32 weather = AI_GetWeather(); if (!weather) @@ -3218,7 +3217,7 @@ static bool32 PartyBattlerShouldAvoidHazards(u32 currBattler, u32 switchBattler) { struct Pokemon *mon = &GetBattlerParty(currBattler)[switchBattler]; enum Ability ability = GetMonAbility(mon); // we know our own party data - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; u32 species = GetMonData(mon, MON_DATA_SPECIES); s32 hazardDamage = 0; u32 type1 = GetSpeciesType(species, 0); @@ -3747,7 +3746,7 @@ bool32 HasChoiceEffect(u32 battler) if (ability == ABILITY_KLUTZ) return FALSE; - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battler]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[battler]; switch (holdEffect) { case HOLD_EFFECT_CHOICE_BAND: @@ -5023,7 +5022,7 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score } } -bool32 AI_MoveMakesContact(enum Ability ability, enum ItemHoldEffect holdEffect, u32 move) +bool32 AI_MoveMakesContact(enum Ability ability, enum HoldEffect holdEffect, u32 move) { if (MoveMakesContact(move) && ability != ABILITY_LONG_REACH diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 564afa3446..3b572198c3 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -35,7 +35,6 @@ #include "constants/battle_anim.h" #include "constants/battle_move_effects.h" #include "constants/battle_partner.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/party_menu.h" diff --git a/src/battle_debug.c b/src/battle_debug.c index 5c2e0735a1..6a96b52e88 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -35,7 +35,6 @@ #include "constants/moves.h" #include "constants/items.h" #include "constants/rgb.h" -#include "constants/hold_effects.h" #define MAX_MODIFY_DIGITS 4 @@ -230,7 +229,7 @@ enum }; // Static Declarations -static const u8 *GetHoldEffectName(enum ItemHoldEffect holdEffect); +static const u8 *GetHoldEffectName(enum HoldEffect holdEffect); // const rom data static const u8 sText_Ability[] = _("Ability"); @@ -921,7 +920,7 @@ static void PutAiInfoText(struct BattleDebugMenu *data) if (IsOnPlayerSide(i) && IsBattlerAlive(i)) { enum Ability ability = gAiLogicData->abilities[i]; - enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[i]; + enum HoldEffect holdEffect = gAiLogicData->holdEffects[i]; u16 item = gAiLogicData->items[i]; u8 x = (i == B_POSITION_PLAYER_LEFT) ? 83 + (i) * 75 : 83 + (i-1) * 75; AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, gAbilitiesInfo[ability].name, x, 0, 0, NULL); @@ -2272,7 +2271,7 @@ static const u8 *const sHoldEffectNames[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_PRIMAL_ORB] = COMPOUND_STRING("Primal Orb"), [HOLD_EFFECT_PROTECTIVE_PADS] = COMPOUND_STRING("Protective Pads"), [HOLD_EFFECT_TERRAIN_EXTENDER] = COMPOUND_STRING("Terrain Extender"), - [HOLD_EFFECT_SEEDS] = COMPOUND_STRING("Seeds"), + [HOLD_EFFECT_TERRAIN_SEED] = COMPOUND_STRING("Seeds"), [HOLD_EFFECT_ADRENALINE_ORB] = COMPOUND_STRING("Adrenaline Orb"), [HOLD_EFFECT_MEMORY] = COMPOUND_STRING("Memory"), [HOLD_EFFECT_Z_CRYSTAL] = COMPOUND_STRING("Z-Crystal"), @@ -2292,7 +2291,7 @@ static const u8 *const sHoldEffectNames[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_OGERPON_MASK] = COMPOUND_STRING("Ogerpon Mask"), [HOLD_EFFECT_BERSERK_GENE] = COMPOUND_STRING("Berserk Gene"), }; -static const u8 *GetHoldEffectName(enum ItemHoldEffect holdEffect) +static const u8 *GetHoldEffectName(enum HoldEffect holdEffect) { if (sHoldEffectNames[holdEffect] == NULL) return sHoldEffectNames[0]; diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 1ddc2b2b01..a95225bf44 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -19,7 +19,6 @@ #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" #include "constants/flags.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" @@ -74,7 +73,7 @@ static const struct GMaxMove sGMaxMoveTable[] = bool32 CanDynamax(u32 battler) { u16 species = GetBattlerVisualSpecies(battler); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); // Prevents Zigzagoon from dynamaxing in vanilla. if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE && !IsOnPlayerSide(battler)) diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 2041f9ecca..4b42d05efd 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle.h" +#include "battle_hold_effects.h" #include "battle_util.h" #include "battle_controllers.h" #include "battle_ai_util.h" @@ -7,7 +8,6 @@ #include "battle_scripts.h" #include "constants/battle.h" #include "constants/battle_string_ids.h" -#include "constants/hold_effects.h" #include "constants/abilities.h" #include "constants/items.h" #include "constants/moves.h" @@ -20,7 +20,6 @@ enum EndTurnResolutionOrder ENDTURN_VARIOUS, ENDTURN_WEATHER, ENDTURN_WEATHER_DAMAGE, - ENDTURN_GEN_3_BERRY_ACTIVATION, ENDTURN_EMERGENCY_EXIT_1, ENDTURN_AFFECTION, ENDTURN_FUTURE_SIGHT, @@ -270,20 +269,6 @@ static bool32 HandleEndTurnWeatherDamage(u32 battler) return effect; } -static bool32 HandleEndTurnGenThreeBerryActivation(u32 battler) -{ - bool32 effect = FALSE; - - if (B_HP_BERRIES >= GEN_4) // Skip handler for > Gen3 - { - gBattleStruct->endTurnEventsCounter++; - return effect; - } - gBattleStruct->turnEffectsBattlerId++; - effect = TryRestoreHPBerries(battler, ITEMEFFECT_NORMAL); - return effect; -} - static bool32 HandleEndTurnEmergencyExit(u32 battler) { bool32 effect = FALSE; @@ -514,7 +499,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler) break; } case FIRST_EVENT_BLOCK_HEAL_ITEMS: - if (ItemBattleEffects(ITEMEFFECT_LEFTOVERS, battler)) + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsLeftoversActivation)) effect = TRUE; gBattleStruct->eventBlockCounter = 0; gBattleStruct->turnEffectsBattlerId++; @@ -849,6 +834,7 @@ static bool32 HandleEndTurnTaunt(u32 battler) if (gDisableStructs[battler].tauntTimer && --gDisableStructs[battler].tauntTimer == 0) { + gBattleScripting.battler = battler; BattleScriptExecute(BattleScript_BufferEndTurn); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); effect = TRUE; @@ -866,6 +852,7 @@ static bool32 HandleEndTurnTorment(u32 battler) if (gDisableStructs[battler].tormentTimer == gBattleTurnCounter) { gBattleMons[battler].volatiles.torment = FALSE; + gBattleScripting.battler = battler; BattleScriptExecute(BattleScript_TormentEnds); effect = TRUE; } @@ -891,6 +878,7 @@ static bool32 HandleEndTurnEncore(u32 battler) { gDisableStructs[battler].encoredMove = 0; gDisableStructs[battler].encoreTimer = 0; + gBattleScripting.battler = battler; BattleScriptExecute(BattleScript_EncoredNoMore); effect = TRUE; } @@ -921,6 +909,7 @@ static bool32 HandleEndTurnDisable(u32 battler) else if (--gDisableStructs[battler].disableTimer == 0) // disable ends { gDisableStructs[battler].disabledMove = 0; + gBattleScripting.battler = battler; BattleScriptExecute(BattleScript_DisabledNoMore); effect = TRUE; } @@ -971,6 +960,7 @@ static bool32 HandleEndTurnHealBlock(u32 battler) if (gBattleMons[battler].volatiles.healBlock && gDisableStructs[battler].healBlockTimer == gBattleTurnCounter) { gBattleMons[battler].volatiles.healBlock = FALSE; + gBattleScripting.battler = battler; BattleScriptExecute(BattleScript_BufferEndTurn); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_HEAL_BLOCK); effect = TRUE; @@ -1014,7 +1004,7 @@ static bool32 HandleEndTurnYawn(u32 battler) && !IsLeafGuardProtected(battler, ability)) { gEffectBattler = gBattlerTarget = battler; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (IsBattlerTerrainAffected(battler, ability, holdEffect, STATUS_FIELD_ELECTRIC_TERRAIN)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC; @@ -1424,17 +1414,18 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler) } case THIRD_EVENT_BLOCK_ITEMS: { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + // TODO: simplify + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); switch (holdEffect) { case HOLD_EFFECT_FLAME_ORB: case HOLD_EFFECT_STICKY_BARB: case HOLD_EFFECT_TOXIC_ORB: - if (ItemBattleEffects(ITEMEFFECT_ORBS, battler)) + if (ItemBattleEffects(battler, 0, holdEffect, IsOrbsActivation)) effect = TRUE; break; case HOLD_EFFECT_WHITE_HERB: - if (ItemBattleEffects(ITEMEFFECT_WHITE_HERB_ENDTURN, battler)) + if (ItemBattleEffects(battler, 0, holdEffect, IsWhiteHerbEndTurnActivation)) effect = TRUE; break; default: @@ -1475,8 +1466,8 @@ static bool32 HandleEndTurnFormChangeAbilities(u32 battler) static bool32 HandleEndTurnEjectPack(u32 battler) { - gBattleStruct->turnEffectsBattlerId++; - return TrySwitchInEjectPack(ITEMEFFECT_NORMAL); + gBattleStruct->endTurnEventsCounter++; + return TrySwitchInEjectPack(END_TURN); } static bool32 HandleEndTurnDynamax(u32 battler) @@ -1515,7 +1506,6 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = [ENDTURN_VARIOUS] = HandleEndTurnVarious, [ENDTURN_WEATHER] = HandleEndTurnWeather, [ENDTURN_WEATHER_DAMAGE] = HandleEndTurnWeatherDamage, - [ENDTURN_GEN_3_BERRY_ACTIVATION] = HandleEndTurnGenThreeBerryActivation, [ENDTURN_EMERGENCY_EXIT_1] = HandleEndTurnEmergencyExit, [ENDTURN_AFFECTION] = HandleEndTurnAffection, [ENDTURN_FUTURE_SIGHT] = HandleEndTurnFutureSight, diff --git a/src/battle_hold_effects.c b/src/battle_hold_effects.c new file mode 100644 index 0000000000..fc8678a613 --- /dev/null +++ b/src/battle_hold_effects.c @@ -0,0 +1,1293 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_ai_util.h" // maybe move some stuff over to battle.h +#include "battle_controllers.h" +#include "battle_util.h" +#include "battle_hold_effects.h" +#include "battle_scripts.h" +#include "item.h" +#include "string_util.h" +#include "data/hold_effects.h" +#include "constants/berry.h" + +bool32 IsOnSwitchInActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onSwitchIn; } +bool32 IsOnSwitchInFirstTurnActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onSwitchInFirstTurn; } +bool32 IsMirrorHerbActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].mirrorHerb; } +bool32 IsMirrorHerbFirstTurnActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].mirrorHerbFirstTurn; } +bool32 IsWhiteHerbActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].whiteHerb; } +bool32 IsWhiteHerbFirstTurnActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].whiteHerbFirstTurn; } +bool32 IsWhiteHerbEndTurnActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].whiteHerbEndTurn; } +bool32 IsOnStatusChangeActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onStatusChange; } +bool32 IsOnHpThresholdActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onHpThreshold; } +bool32 IsKeeMarangaBerryActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].keeMarangaBerry; } +bool32 IsOnTargetHitActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onTargetAfterHit; } +bool32 IsOnAttackerAfterHitActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onAttackerAfterHit; } +bool32 IsLifeOrbShellBellActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].lifeOrbShellBell; } +bool32 IsLeftoversActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].leftovers; } +bool32 IsOrbsActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].orbs; } +bool32 IsOnEffectActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onEffect; } +bool32 IsActivationOnBerry(enum HoldEffect holdEffect) { return GetItemPocket(gLastUsedItem) == POCKET_BERRIES; } + +bool32 IsActivationForceTriggerItem(enum HoldEffect holdEffect) +{ + return gHoldEffectsInfo[holdEffect].onSwitchIn + || gHoldEffectsInfo[holdEffect].whiteHerb + || gHoldEffectsInfo[holdEffect].onStatusChange + || gHoldEffectsInfo[holdEffect].onHpThreshold; +} + +static enum ItemEffect TryDoublePrize(u32 battler) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsOnPlayerSide(battler) && !gBattleStruct->moneyMultiplierItem) + { + gBattleStruct->moneyMultiplier *= 2; + gBattleStruct->moneyMultiplierItem = TRUE; + } + + return effect; +} + +enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gDisableStructs[battler].boosterEnergyActivated || gBattleMons[battler].volatiles.transformed) + return ITEM_NO_EFFECT; + + if (((ability == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && HasWeatherEffect())) + || ((ability == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); + gBattlerAbility = gBattleScripting.battler = battler; + gDisableStructs[battler].boosterEnergyActivated = TRUE; + gLastUsedItem = ITEM_BOOSTER_ENERGY; + RecordAbilityBattle(battler, ability); + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BoosterEnergyEnd2); + else + BattleScriptCall(BattleScript_BoosterEnergyRet); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing) +{ + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) + { + gEffectBattler = gBattleScripting.battler = battler; + SET_STATCHANGER(STAT_SPEED, 1, TRUE); + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_SPEED; + gBattleScripting.animArg2 = 0; + gLastUsedItem = gBattleMons[battler].item; + + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); + else + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + + return ITEM_STATS_CHANGE; + } + + return ITEM_NO_EFFECT; +} + +enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, ActivationTiming timing) +{ + if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + { + gLastUsedItem = itemId; // For surge abilities + gEffectBattler = gBattleScripting.battler = battler; + SET_STATCHANGER(statId, 1, FALSE); + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; + gBattleScripting.animArg2 = 0; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); + else + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + return ITEM_STATS_CHANGE; + } + return ITEM_NO_EFFECT; +} + +static enum ItemEffect TryTerrainSeeds(u32 battler, u32 item, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + switch (GetItemHoldEffectParam(item)) + { + case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: + effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, item, timing); + break; + case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: + effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, item, timing); + break; + case HOLD_EFFECT_PARAM_MISTY_TERRAIN: + effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, item, timing); + break; + case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: + effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, timing); + break; + } + + return effect; +} + +static bool32 CanBeInfinitelyConfused(u32 battler) +{ + enum Ability ability = GetBattlerAbility(battler); + if (ability == ABILITY_OWN_TEMPO + || IsBattlerTerrainAffected(battler, ability, GetBattlerHoldEffect(battler), STATUS_FIELD_MISTY_TERRAIN) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) + return FALSE; + return TRUE; +} + +static enum ItemEffect TryBerserkGene(u32 battler, ActivationTiming timing) +{ + if (CanBeInfinitelyConfused(battler)) + gBattleMons[battler].volatiles.infiniteConfusion = TRUE; + + SET_STATCHANGER(STAT_ATK, 2, FALSE); + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK; + gBattleScripting.animArg2 = 0; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerserkGeneRetEnd2); + else + BattleScriptCall(BattleScript_BerserkGeneRet); + + return ITEM_STATS_CHANGE; +} + +static enum ItemEffect RestoreWhiteHerbStats(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + for (u32 i = 0; i < NUM_BATTLE_STATS; i++) + { + if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) + { + gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; + effect = ITEM_STATS_CHANGE; + } + } + if (effect != ITEM_NO_EFFECT) + { + if (timing == IsWhiteHerbActivation) + BattleScriptCall(BattleScript_WhiteHerbRet); + else + BattleScriptExecute(BattleScript_WhiteHerbEnd2); + } + + return effect; +} + +static enum ItemEffect TryConsumeMirrorHerb(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gProtectStructs[battler].eatMirrorHerb) + { + gProtectStructs[battler].eatMirrorHerb = 0; + ChooseStatBoostAnimation(battler); + if (timing == IsMirrorHerbFirstTurnActivation) + BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); + else + BattleScriptCall(BattleScript_MirrorHerbCopyStatChange); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryKingsRock(u32 battlerAtk, u32 battlerDef, u32 item) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battlerAtk); + u32 holdEffectParam = GetItemHoldEffectParam(item); + + if (B_SERENE_GRACE_BOOST >= GEN_5 && ability == ABILITY_SERENE_GRACE) + holdEffectParam *= 2; + if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_RAINBOW && gCurrentMove != MOVE_SECRET_POWER) + holdEffectParam *= 2; + if (IsBattlerTurnDamaged(battlerDef) + && !MoveIgnoresKingsRock(gCurrentMove) + && IsBattlerAlive(battlerDef) + && RandomPercentage(RNG_HOLD_EFFECT_FLINCH, holdEffectParam) + && ability != ABILITY_STENCH) + { + gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; + BattleScriptPushCursor(); + SetMoveEffect(battlerAtk, battlerDef, FALSE, FALSE); + BattleScriptPop(); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect TryAirBallon(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (timing == IsOnTargetHitActivation) + { + if (IsBattlerTurnDamaged(battler)) + { + BattleScriptCall(BattleScript_AirBaloonMsgPop); + effect = ITEM_EFFECT_OTHER; + } + } + else if (!gSpecialStatuses[battler].switchInItemDone) + { + gSpecialStatuses[battler].switchInItemDone = TRUE; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn); + else + BattleScriptCall(BattleScript_AirBalloonMsgInRet); + RecordItemEffectBattle(battler, HOLD_EFFECT_AIR_BALLOON); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect TryRockyHelmet(u32 battlerDef, u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battlerAtk); + + if (IsBattlerTurnDamaged(battlerDef) + && IsBattlerAlive(battlerAtk) + && !CanBattlerAvoidContactEffects(battlerAtk, battlerDef, ability, GetBattlerHoldEffect(battlerAtk), gCurrentMove) + && !IsAbilityAndRecord(battlerAtk, ability, ABILITY_MAGIC_GUARD)) + { + gBattleStruct->moveDamage[battlerAtk] = GetNonDynamaxMaxHP(battlerAtk) / 6; + if (gBattleStruct->moveDamage[battlerAtk] == 0) + gBattleStruct->moveDamage[battlerAtk] = 1; + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[battlerDef].item); + BattleScriptCall(BattleScript_RockyHelmetActivates); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryWeaknessPolicy(u32 battlerDef) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && IsBattlerTurnDamaged(battlerDef) + && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SUPER_EFFECTIVE) + { + BattleScriptCall(BattleScript_WeaknessPolicy); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TrySnowball(u32 battlerDef) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && IsBattlerTurnDamaged(battlerDef) + && GetBattleMoveType(gCurrentMove) == TYPE_ICE) + { + BattleScriptCall(BattleScript_TargetItemStatRaise); + SET_STATCHANGER(STAT_ATK, 1, FALSE); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryLuminousMoss(u32 battlerDef) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && IsBattlerTurnDamaged(battlerDef) + && GetBattleMoveType(gCurrentMove) == TYPE_WATER) + { + BattleScriptCall(BattleScript_TargetItemStatRaise); + SET_STATCHANGER(STAT_SPDEF, 1, FALSE); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCellBattery(u32 battlerDef) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && IsBattlerTurnDamaged(battlerDef) + && GetBattleMoveType(gCurrentMove) == TYPE_ELECTRIC) + { + BattleScriptCall(BattleScript_TargetItemStatRaise); + SET_STATCHANGER(STAT_ATK, 1, FALSE); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryAbsorbBulb(u32 battlerDef) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && IsBattlerTurnDamaged(battlerDef) + && GetBattleMoveType(gCurrentMove) == TYPE_WATER) + { + effect = ITEM_STATS_CHANGE; + BattleScriptCall(BattleScript_TargetItemStatRaise); + SET_STATCHANGER(STAT_SPATK, 1, FALSE); + } + + return effect; +} + +static enum ItemEffect TryJabocaBerry(u32 battlerDef, u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerAtk) + && IsBattlerTurnDamaged(battlerDef) + && !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove) + && IsBattleMovePhysical(gCurrentMove) + && !IsAbilityAndRecord(battlerAtk, GetBattlerAbility(battlerAtk), ABILITY_MAGIC_GUARD)) + { + gBattleStruct->moveDamage[battlerAtk] = GetNonDynamaxMaxHP(battlerAtk) / 8; + if (gBattleStruct->moveDamage[battlerAtk] == 0) + gBattleStruct->moveDamage[battlerAtk] = 1; + if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN) + gBattleStruct->moveDamage[gBattlerAttacker] *= 2; + + BattleScriptCall(BattleScript_JabocaRowapBerryActivates); + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[battlerDef].item); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryRowapBerry(u32 battlerDef, u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerAtk) + && IsBattlerTurnDamaged(battlerDef) + && !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove) + && IsBattleMoveSpecial(gCurrentMove) + && !IsAbilityAndRecord(battlerAtk, GetBattlerAbility(battlerAtk), ABILITY_MAGIC_GUARD)) + { + gBattleStruct->moveDamage[battlerAtk] = GetNonDynamaxMaxHP(battlerAtk) / 8; + if (gBattleStruct->moveDamage[battlerAtk] == 0) + gBattleStruct->moveDamage[battlerAtk] = 1; + if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN) + gBattleStruct->moveDamage[battlerAtk] *= 2; + + BattleScriptCall(BattleScript_JabocaRowapBerryActivates); + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[battlerDef].item); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TrySetEnigmaBerry(u32 battlerDef, u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerDef) + && !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove) + && ((IsBattlerTurnDamaged(battlerDef) && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements) + && !(gBattleScripting.overrideBerryRequirements && gBattleMons[battlerDef].hp == gBattleMons[battlerDef].maxHP) + && !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battlerDef].volatiles.healBlock)) + { + gBattleStruct->moveDamage[battlerDef] = (gBattleMons[battlerDef].maxHP * 25 / 100) * -1; + if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN) + gBattleStruct->moveDamage[battlerDef] *= 2; + + BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryBlunderPolicy(u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleStruct->blunderPolicy + && IsBattlerAlive(battlerAtk) + && CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + { + gBattleStruct->blunderPolicy = FALSE; + SET_STATCHANGER(STAT_SPEED, 2, FALSE); + BattleScriptCall(BattleScript_AttackerItemStatRaise); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryMentalHerb(u32 battler) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + // Check infatuation + if (gBattleMons[battler].volatiles.infatuation) + { + gBattleMons[battler].volatiles.infatuation = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; + StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + effect = ITEM_EFFECT_OTHER; + } + if (B_MENTAL_HERB >= GEN_5) + { + // Check taunt + if (gDisableStructs[battler].tauntTimer != 0) + { + gDisableStructs[battler].tauntTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TAUNT; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); + effect = ITEM_EFFECT_OTHER; + } + // Check encore + if (gDisableStructs[battler].encoreTimer != 0) + { + gDisableStructs[battler].encoredMove = 0; + gDisableStructs[battler].encoreTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; + effect = ITEM_EFFECT_OTHER; + } + // Check torment + if (gBattleMons[battler].volatiles.torment == TRUE) + { + gBattleMons[battler].volatiles.torment = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT; + effect = ITEM_EFFECT_OTHER; + } + // Check heal block + if (gBattleMons[battler].volatiles.healBlock) + { + gBattleMons[battler].volatiles.healBlock = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_HEALBLOCK; + effect = ITEM_EFFECT_OTHER; + } + // Check disable + if (gDisableStructs[battler].disableTimer != 0) + { + gDisableStructs[battler].disableTimer = 0; + gDisableStructs[battler].disabledMove = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE; + effect = ITEM_EFFECT_OTHER; + } + } + + if (effect) + BattleScriptCall(BattleScript_MentalHerbCureRet); + + return effect; +} + +static enum ItemEffect TryThroatSray(u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsSoundMove(gCurrentMove) + && gMultiHitCounter == 0 + && IsBattlerAlive(battlerAtk) + && IsAnyTargetTurnDamaged(battlerAtk) + && CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && !NoAliveMonsForEitherParty()) // Don't activate if battle will end + { + SET_STATCHANGER(STAT_SPATK, 1, FALSE); + BattleScriptCall(BattleScript_AttackerItemStatRaise); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect DamagedStatBoostBerryEffect(u32 battlerDef, u32 battlerAtk, u32 statId, enum DamageCategory category) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (!IsBattlerAlive(battlerDef) || !CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + return effect; + + if (gBattleScripting.overrideBerryRequirements + || (!DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove) + && GetBattleMoveCategory(gCurrentMove) == category + && IsBattlerTurnDamaged(battlerDef))) + { + if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN) + SET_STATCHANGER(statId, 2, FALSE); + else + SET_STATCHANGER(statId, 1, FALSE); + + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; + gBattleScripting.animArg2 = 0; + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryShellBell(u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleScripting.savedDmg > 0 + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && (IsAnyTargetTurnDamaged(battlerAtk) || gBattleScripting.savedDmg > 0) + && !IsBattlerAtMaxHp(battlerAtk) + && IsBattlerAlive(battlerAtk) + && GetMoveEffect(gCurrentMove) != EFFECT_PAIN_SPLIT + && !IsFutureSightAttackerInParty(battlerAtk, gBattlerTarget, gCurrentMove) + && !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battlerAtk].volatiles.healBlock)) + { + gBattleStruct->moveDamage[battlerAtk] = (gBattleScripting.savedDmg / GetBattlerHoldEffectParam(battlerAtk)) * -1; + if (gBattleStruct->moveDamage[battlerAtk] == 0) + gBattleStruct->moveDamage[battlerAtk] = -1; + BattleScriptCall(BattleScript_ItemHealHP_Ret); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryLifeOrb(u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerAlive(battlerAtk) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && (IsAnyTargetTurnDamaged(battlerAtk) || gBattleScripting.savedDmg > 0) + && !IsAbilityAndRecord(battlerAtk, GetBattlerAbility(battlerAtk), ABILITY_MAGIC_GUARD) + && GetMoveEffect(gCurrentMove) != EFFECT_PAIN_SPLIT + && !IsFutureSightAttackerInParty(battlerAtk, gBattlerTarget, gCurrentMove)) + { + DebugPrintf("move %d", gCurrentMove); + gBattleStruct->moveDamage[battlerAtk] = GetNonDynamaxMaxHP(battlerAtk) / 10; + if (gBattleStruct->moveDamage[battlerAtk] == 0) + gBattleStruct->moveDamage[battlerAtk] = 1; + BattleScriptCall(BattleScript_ItemHurtRet); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryStickyBarbOnTargetHit(u32 battlerDef, u32 battlerAtk) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (IsBattlerTurnDamaged(battlerDef) + && !CanBattlerAvoidContactEffects(battlerAtk, battlerDef, GetBattlerAbility(battlerAtk), GetBattlerHoldEffect(battlerAtk), gCurrentMove) + && !DoesSubstituteBlockMove(battlerAtk, battlerDef, gCurrentMove) + && IsBattlerAlive(battlerAtk) + && CanStealItem(battlerAtk, battlerDef, gBattleMons[battlerDef].item) + && gBattleMons[battlerAtk].item == ITEM_NONE) + { + // No sticky hold checks. + gEffectBattler = battlerDef; + StealTargetItem(battlerAtk, battlerDef); // Attacker takes target's barb + BattleScriptCall(BattleScript_StickyBarbTransfer); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect TryStickyBarbOnEndTurn(u32 battler) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (!IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[battler].item); + BattleScriptExecute(BattleScript_ItemHurtEnd2); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryToxicOrb(u32 battler) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battler); + + if (CanBePoisoned(battler, battler, ability, ability)) // Can corrosion trigger toxic orb on itself? + { + gBattleMons[battler].status1 = STATUS1_TOXIC_POISON; + BattleScriptExecute(BattleScript_ToxicOrb); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryFlameOrb(u32 battler) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battler); + + if (CanBeBurned(battler, battler, ability)) + { + gBattleMons[battler].status1 = STATUS1_BURN; + BattleScriptExecute(BattleScript_FlameOrb); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryLeftovers(u32 battler, enum HoldEffect holdEffect) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].hp < gBattleMons[battler].maxHP + && !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battler].volatiles.healBlock)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleStruct->moveDamage[battler] *= -1; + RecordItemEffectBattle(battler, holdEffect); + BattleScriptExecute(BattleScript_ItemHealHP_End2); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryBlackSludge(u32 battler, enum HoldEffect holdEffect) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (!IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD)) + { + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + RecordItemEffectBattle(battler, holdEffect); + BattleScriptExecute(BattleScript_ItemHurtEnd2); + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCureParalysis(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) + { + gBattleMons[battler].status1 &= ~STATUS1_PARALYSIS; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PARALYSIS; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCurePoison(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].status1 & STATUS1_PSN_ANY) + { + gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_POISON; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCureBurn(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].status1 & STATUS1_BURN) + { + gBattleMons[battler].status1 &= ~STATUS1_BURN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_BURN; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCureFreezeOrFrostbite(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].status1 & STATUS1_FREEZE) + { + gBattleMons[battler].status1 &= ~STATUS1_FREEZE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_FREEEZE; + effect = ITEM_STATUS_CHANGE; + } + else if (gBattleMons[battler].status1 & STATUS1_FROSTBITE) + { + gBattleMons[battler].status1 &= ~STATUS1_FROSTBITE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_FROSTBITE; + effect = ITEM_STATUS_CHANGE; + } + + if (effect == ITEM_STATUS_CHANGE) + { + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + } + + return effect; +} + +static enum ItemEffect TryCureSleep(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + gBattleMons[battler].status1 &= ~STATUS1_SLEEP; + gBattleMons[battler].volatiles.nightmare = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_SLEEP; + TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TryCureConfusion(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (gBattleMons[battler].volatiles.confusionTurns > 0) + { + RemoveConfusionStatus(battler); + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); + else + BattleScriptCall(BattleScript_BerryCureConfusionRet); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect TryCureAnyStatus(u32 battler, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + u32 string = 0; + + if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].volatiles.confusionTurns > 0)) + { + if (gBattleMons[battler].status1 & STATUS1_PSN_ANY) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + string++; + } + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + gBattleMons[battler].volatiles.nightmare = FALSE; + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); + string++; + } + if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + string++; + } + if (gBattleMons[battler].status1 & STATUS1_BURN) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + string++; + } + if (gBattleMons[battler].status1 & STATUS1_FREEZE || gBattleMons[battler].status1 & STATUS1_FROSTBITE) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + string++; + } + if (gBattleMons[battler].volatiles.confusionTurns > 0) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); + string++; + } + if (string <= 1) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; + else + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_NORMALIZED_STATUS; + gBattleMons[battler].status1 = 0; + RemoveConfusionStatus(battler); + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryCureStatusEnd2); + else + BattleScriptCall(BattleScript_BerryCureStatusRet); + effect = ITEM_STATUS_CHANGE; + } + + return effect; +} + +enum HealAmount +{ + FIXED_HEAL_AMOUNT, + PERCENT_HEAL_AMOUNT, +}; + +static u32 ItemHealHp(u32 battler, u32 itemId, enum HealAmount percentHeal, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battler); + + if (!(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) + && !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battler].volatiles.healBlock) + && HasEnoughHpToEatBerry(battler, ability, 2, itemId)) + { + if (percentHeal == PERCENT_HEAL_AMOUNT) + gBattleStruct->moveDamage[battler] = (GetNonDynamaxMaxHP(battler) * GetItemHoldEffectParam(itemId) / 100) * -1; + else + gBattleStruct->moveDamage[battler] = GetItemHoldEffectParam(itemId) * -1; + + if (ability == ABILITY_RIPEN && GetItemPocket(itemId) == POCKET_BERRIES) + gBattleStruct->moveDamage[battler] *= 2; + + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); + else + BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); + + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static u32 ItemRestorePp(u32 battler, u32 itemId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + struct Pokemon *mon = GetBattlerMon(battler); + u32 i, changedPP = 0; + enum Ability ability = GetBattlerAbility(battler); + + for (i = 0; i < MAX_MON_MOVES; i++) + { + u32 move = GetMonData(mon, MON_DATA_MOVE1 + i); + u32 currentPP = GetMonData(mon, MON_DATA_PP1 + i); + u32 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); + u32 maxPP = CalculatePPWithBonus(move, ppBonuses, i); + if (move && (currentPP == 0 || (gBattleScripting.overrideBerryRequirements && currentPP != maxPP))) + { + u32 ppRestored = GetItemHoldEffectParam(itemId); + + if (ability == ABILITY_RIPEN) + { + ppRestored *= 2; + gBattlerAbility = battler; + } + if (currentPP + ppRestored > maxPP) + changedPP = maxPP; + else + changedPP = currentPP + ppRestored; + + PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); + + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryPPHealEnd2); + else + BattleScriptCall(BattleScript_BerryPPHealRet); + + gBattleScripting.battler = battler; + BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); + MarkBattlerForControllerExec(battler); + if (MOVE_IS_PERMANENT(battler, i)) + gBattleMons[battler].pp[i] = changedPP; + effect = ITEM_PP_CHANGE; + } + } + return effect; +} + +static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + u32 hpFraction = B_CONFUSE_BERRIES_HEAL >= GEN_7 ? 4 : 2; + u32 ability = GetBattlerAbility(battler); + + if (HasEnoughHpToEatBerry(battler, ability, hpFraction, itemId) + && !(B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battler].volatiles.healBlock)) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, flavorId); + + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / GetItemHoldEffectParam(itemId); + if (gBattleStruct->moveDamage[battler] == 0) + gBattleStruct->moveDamage[battler] = 1; + gBattleStruct->moveDamage[battler] *= -1; + + if (ability == ABILITY_RIPEN) + gBattleStruct->moveDamage[battler] *= 2; + + if (timing == IsOnSwitchInFirstTurnActivation) + { + if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); + } + else + { + if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) + BattleScriptCall(BattleScript_BerryConfuseHealRet); + else + BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); + } + + effect = ITEM_HP_CHANGE; + } + + return effect; +} + +static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, u32 statId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + enum Ability ability = GetBattlerAbility(battler); + + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) + && HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId)) + { + gEffectBattler = gBattleScripting.battler = battler; + SET_STATCHANGER(statId, ability == ABILITY_RIPEN ? 2 : 1, FALSE); + gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; + gBattleScripting.animArg2 = 0; + + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); + else + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect CriticalHitRatioUp(u32 battler, u32 itemId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (!gBattleMons[battler].volatiles.focusEnergy + && !gBattleMons[battler].volatiles.dragonCheer + && HasEnoughHpToEatBerry(battler, GetBattlerAbility(battler), GetItemHoldEffectParam(itemId), itemId)) + { + gBattleMons[battler].volatiles.focusEnergy = TRUE; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); + else + BattleScriptCall(BattleScript_BerryFocusEnergyRet); + effect = ITEM_EFFECT_OTHER; + } + + return effect; +} + +static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + s32 stat; + + for (stat = STAT_ATK; stat < NUM_STATS; stat++) + { + if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + break; + } + + if (stat == NUM_STATS) + return effect; + + enum Ability ability = GetBattlerAbility(battler); + if (HasEnoughHpToEatBerry(battler, ability, GetItemHoldEffectParam(itemId), itemId)) + { + u32 savedAttacker = gBattlerAttacker; + // MoodyCantRaiseStat requires that the battler is set to gBattlerAttacker + gBattlerAttacker = gBattleScripting.battler = battler; + gBattleScripting.statChanger = 0; + if (ability != ABILITY_CONTRARY) + stat = RandomUniformExcept(RNG_RANDOM_STAT_UP, STAT_ATK, NUM_STATS - 1, MoodyCantRaiseStat); + else + stat = RandomUniformExcept(RNG_RANDOM_STAT_UP, STAT_ATK, NUM_STATS - 1, MoodyCantLowerStat); + gBattlerAttacker = savedAttacker; + + PREPARE_STAT_BUFFER(gBattleTextBuff1, stat); + SET_STATCHANGER(stat, ability == ABILITY_RIPEN ? 4 : 2, FALSE); + gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + stat; + gBattleScripting.animArg2 = 0; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); + else + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + effect = ITEM_STATS_CHANGE; + } + + return effect; +} + +static enum ItemEffect TrySetMicleBerry(u32 battler, u32 itemId, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + + if (HasEnoughHpToEatBerry(battler, GetBattlerAbility(battler), 4, itemId)) + { + gBattleStruct->battlerState[battler].usedMicleBerry = TRUE; + if (timing == IsOnSwitchInFirstTurnActivation) + BattleScriptExecute(BattleScript_MicleBerryActivateEnd2); + else + BattleScriptCall(BattleScript_MicleBerryActivateRet); + effect = ITEM_EFFECT_OTHER; + } + return effect; +} + +enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect holdEffect, ActivationTiming timing) +{ + enum ItemEffect effect = ITEM_NO_EFFECT; + u32 item = (timing == IsActivationOnBerry) ? gLastUsedItem : gBattleMons[itemBattler].item; + + if (holdEffect == HOLD_EFFECT_NONE + || !timing(holdEffect) + || IsUnnerveBlocked(itemBattler, item)) + return effect; + + if (!IsBattlerAlive(itemBattler) + && holdEffect != HOLD_EFFECT_ROWAP_BERRY // Hacky workaround for them right now + && holdEffect != HOLD_EFFECT_JABOCA_BERRY + && holdEffect != HOLD_EFFECT_ROCKY_HELMET) + return effect; + + switch (holdEffect) + { + case HOLD_EFFECT_DOUBLE_PRIZE: + effect = TryDoublePrize(itemBattler); + break; + case HOLD_EFFECT_ROOM_SERVICE: + effect = TryRoomService(itemBattler, timing); + break; + case HOLD_EFFECT_TERRAIN_SEED: + effect = TryTerrainSeeds(itemBattler, item, timing); + break; + case HOLD_EFFECT_BERSERK_GENE: + effect = TryBerserkGene(itemBattler, timing); + break; + case HOLD_EFFECT_BOOSTER_ENERGY: + effect = TryBoosterEnergy(itemBattler, GetBattlerAbility(itemBattler), timing); + break; + case HOLD_EFFECT_WHITE_HERB: + effect = RestoreWhiteHerbStats(itemBattler, timing); + break; + case HOLD_EFFECT_MIRROR_HERB: + effect = TryConsumeMirrorHerb(itemBattler, timing); + break; + case HOLD_EFFECT_FLINCH: // Kings Rock + effect = TryKingsRock(itemBattler, battler, item); + break; + case HOLD_EFFECT_AIR_BALLOON: + effect = TryAirBallon(itemBattler, timing); + break; + case HOLD_EFFECT_ROCKY_HELMET: + effect = TryRockyHelmet(itemBattler, battler); + break; + case HOLD_EFFECT_WEAKNESS_POLICY: + effect = TryWeaknessPolicy(itemBattler); + break; + case HOLD_EFFECT_SNOWBALL: + effect = TrySnowball(itemBattler); + break; + case HOLD_EFFECT_LUMINOUS_MOSS: + effect = TryLuminousMoss(itemBattler); + break; + case HOLD_EFFECT_CELL_BATTERY: + effect = TryCellBattery(itemBattler); + break; + case HOLD_EFFECT_ABSORB_BULB: + effect = TryAbsorbBulb(itemBattler); + break; + case HOLD_EFFECT_JABOCA_BERRY: + effect = TryJabocaBerry(itemBattler, battler); + break; + case HOLD_EFFECT_ROWAP_BERRY: + effect = TryRowapBerry(itemBattler, battler); + break; + case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move + effect = TrySetEnigmaBerry(itemBattler, battler); + break; + case HOLD_EFFECT_BLUNDER_POLICY: + effect = TryBlunderPolicy(itemBattler); + break; + case HOLD_EFFECT_MENTAL_HERB: + effect = TryMentalHerb(itemBattler); + break; + case HOLD_EFFECT_THROAT_SPRAY: + effect = TryThroatSray(itemBattler); + break; + case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move + effect = DamagedStatBoostBerryEffect(itemBattler, battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); + break; + case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move + effect = DamagedStatBoostBerryEffect(itemBattler, battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); + break; + case HOLD_EFFECT_SHELL_BELL: + effect = TryShellBell(itemBattler); + break; + case HOLD_EFFECT_LIFE_ORB: + effect = TryLifeOrb(itemBattler); + break; + case HOLD_EFFECT_STICKY_BARB: + if (timing == IsOnTargetHitActivation) + effect = TryStickyBarbOnTargetHit(itemBattler, battler); + else + effect = TryStickyBarbOnEndTurn(itemBattler); + break; + case HOLD_EFFECT_TOXIC_ORB: + effect = TryToxicOrb(itemBattler); + break; + case HOLD_EFFECT_FLAME_ORB: + effect = TryFlameOrb(itemBattler); + break; + case HOLD_EFFECT_LEFTOVERS: + effect = TryLeftovers(itemBattler, holdEffect); + break; + case HOLD_EFFECT_BLACK_SLUDGE: + if (IS_BATTLER_OF_TYPE(itemBattler, TYPE_POISON)) + effect = TryLeftovers(itemBattler, holdEffect); + else + effect = TryBlackSludge(itemBattler, holdEffect); + break; + case HOLD_EFFECT_CURE_PAR: // Cheri Berry + effect = TryCureParalysis(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_PSN: // Pecha Berry + effect = TryCurePoison(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_BRN: // Rawst Berry + effect = TryCureBurn(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_FRZ: // Aspear Berry + effect = TryCureFreezeOrFrostbite(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_SLP: // Chesto Berry + effect = TryCureSleep(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_CONFUSION: // Persim Berry + effect = TryCureConfusion(itemBattler, timing); + break; + case HOLD_EFFECT_CURE_STATUS: // Lum Berry + effect = TryCureAnyStatus(itemBattler, timing); + break; + case HOLD_EFFECT_RESTORE_HP: // Oran Berry / Berry Juice + effect = ItemHealHp(itemBattler, item, FIXED_HEAL_AMOUNT, timing); + break; + case HOLD_EFFECT_RESTORE_PCT_HP: // Sitrus Berry + effect = ItemHealHp(itemBattler, item, PERCENT_HEAL_AMOUNT, timing); + break; + case HOLD_EFFECT_RESTORE_PP: // Leppa Berry + effect = ItemRestorePp(itemBattler, item, timing); + break; + case HOLD_EFFECT_CONFUSE_SPICY: // Figy Berry + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SPICY, timing); + break; + case HOLD_EFFECT_CONFUSE_DRY: // Wiki Berry + effect = HealConfuseBerry(itemBattler, item, FLAVOR_DRY, timing); + break; + case HOLD_EFFECT_CONFUSE_SWEET: // Mago Berry + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SWEET, timing); + break; + case HOLD_EFFECT_CONFUSE_BITTER: // Aguav Berry + effect = HealConfuseBerry(itemBattler, item, FLAVOR_BITTER, timing); + break; + case HOLD_EFFECT_CONFUSE_SOUR: // Iapapa Berry + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SOUR, timing); + break; + case HOLD_EFFECT_ATTACK_UP: // Liechi Berry + effect = StatRaiseBerry(itemBattler, item, STAT_ATK, timing); + break; + case HOLD_EFFECT_DEFENSE_UP: // Ganlon Berry + effect = StatRaiseBerry(itemBattler, item, STAT_DEF, timing); + break; + case HOLD_EFFECT_SPEED_UP: // Salac Berry + effect = StatRaiseBerry(itemBattler, item, STAT_SPEED, timing); + break; + case HOLD_EFFECT_SP_ATTACK_UP: // Petaya Berry + effect = StatRaiseBerry(itemBattler, item, STAT_SPATK, timing); + break; + case HOLD_EFFECT_SP_DEFENSE_UP: // Apicot Berry + effect = StatRaiseBerry(itemBattler, item, STAT_SPDEF, timing); + break; + case HOLD_EFFECT_CRITICAL_UP: // Lansat Berry + effect = CriticalHitRatioUp(itemBattler, item, timing); + break; + case HOLD_EFFECT_RANDOM_STAT_UP: // Starf Berry + effect = RandomStatRaiseBerry(itemBattler, item, timing); + break; + case HOLD_EFFECT_MICLE_BERRY: + effect = TrySetMicleBerry(itemBattler, item, timing); + break; + default: + break; + } + + if (effect == ITEM_STATUS_CHANGE) + { + BtlController_EmitSetMonData(itemBattler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[itemBattler].status1); + MarkBattlerForControllerExec(itemBattler); + } + + if (effect) + { + gLastUsedItem = item; + gBattleScripting.battler = gPotentialItemEffectBattler = itemBattler; + if ((item >= FIRST_BERRY_INDEX && item <= LAST_BERRY_INDEX)) + GetBattlerPartyState(itemBattler)->ateBerry = TRUE; + } + + return effect; +} diff --git a/src/battle_main.c b/src/battle_main.c index 5e64ca8cb8..81a5205250 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -6,6 +6,7 @@ #include "battle_arena.h" #include "battle_controllers.h" #include "battle_end_turn.h" +#include "battle_hold_effects.h" #include "battle_interface.h" #include "battle_main.h" #include "battle_message.h" @@ -66,7 +67,6 @@ #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" #include "constants/battle_partner.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/party_menu.h" @@ -3012,7 +3012,7 @@ static void BattleStartClearSetData(void) s32 i; TurnValuesCleanUp(FALSE); - SpecialStatusesClear(); + memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); memset(&gDisableStructs, 0, sizeof(gDisableStructs)); memset(&gFieldTimers, 0, sizeof(gFieldTimers)); @@ -3884,7 +3884,8 @@ static void TryDoEventsBeforeFirstTurn(void) case FIRST_TURN_EVENTS_ITEM_EFFECTS: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest { - if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++])) + u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnSwitchInFirstTurnActivation)) return; } gBattleStruct->switchInBattlerCounter = 0; @@ -3893,7 +3894,8 @@ static void TryDoEventsBeforeFirstTurn(void) case FIRST_TURN_EVENTS_WHITE_HERB: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest { - if (ItemBattleEffects(ITEMEFFECT_WHITE_HERB_FIRST_TURN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++])) + u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsWhiteHerbFirstTurnActivation)) return; } gBattleStruct->switchInBattlerCounter = 0; @@ -3912,7 +3914,8 @@ static void TryDoEventsBeforeFirstTurn(void) case FIRST_TURN_EVENTS_MIRROR_HERB: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest { - if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB_FIRST_TURN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++])) + u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbFirstTurnActivation)) return; } gBattleStruct->switchInBattlerCounter = 0; @@ -3920,7 +3923,7 @@ static void TryDoEventsBeforeFirstTurn(void) break; case FIRST_TURN_EVENTS_EJECT_PACK: gBattleStruct->eventsBeforeFirstTurnState++; - if (TrySwitchInEjectPack(ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN)) + if (TrySwitchInEjectPack(FIRST_TURN)) return; break; case FIRST_TURN_EVENTS_END: @@ -3931,7 +3934,7 @@ static void TryDoEventsBeforeFirstTurn(void) gChosenMoveByBattler[i] = MOVE_NONE; } TurnValuesCleanUp(FALSE); - SpecialStatusesClear(); + memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); AssignUsableGimmicks(); gBattleMainFunc = HandleTurnActionSelectionState; @@ -4083,7 +4086,7 @@ void BattleTurnPassed(void) u8 IsRunningFromBattleImpossible(u32 battler) { - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; u32 i; if (FlagGet(B_FLAG_NO_RUNNING)) @@ -4756,7 +4759,7 @@ void SwapTurnOrder(u8 id1, u8 id2) } // For AI, so it doesn't 'cheat' by knowing player's ability -u32 GetBattlerTotalSpeedStatArgs(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect) +u32 GetBattlerTotalSpeedStatArgs(u32 battler, enum Ability ability, enum HoldEffect holdEffect) { u32 speed = gBattleMons[battler].speed; @@ -4826,7 +4829,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, enum Ability ability, enum ItemHol u32 GetBattlerTotalSpeedStat(u32 battler) { enum Ability ability = GetBattlerAbility(battler); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); return GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); } @@ -4884,7 +4887,7 @@ s32 GetBattleMovePriority(u32 battler, enum Ability ability, u32 move) } s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, enum Ability ability1, enum Ability ability2, - enum ItemHoldEffect holdEffectBattler1, enum ItemHoldEffect holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2) + enum HoldEffect holdEffectBattler1, enum HoldEffect holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2) { u32 strikesFirst = 0; @@ -4951,9 +4954,9 @@ s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenM s32 priority1 = 0, priority2 = 0; enum Ability ability1 = GetBattlerAbility(battler1); u32 speedBattler1 = GetBattlerTotalSpeedStat(battler1); - enum ItemHoldEffect holdEffectBattler1 = GetBattlerHoldEffect(battler1); + enum HoldEffect holdEffectBattler1 = GetBattlerHoldEffect(battler1); u32 speedBattler2 = GetBattlerTotalSpeedStat(battler2); - enum ItemHoldEffect holdEffectBattler2 = GetBattlerHoldEffect(battler2); + enum HoldEffect holdEffectBattler2 = GetBattlerHoldEffect(battler2); enum Ability ability2 = GetBattlerAbility(battler2); if (!ignoreChosenMoves) @@ -5180,11 +5183,6 @@ static void TurnValuesCleanUp(bool8 var0) ClearDamageCalcResults(); } -void SpecialStatusesClear(void) -{ - memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); -} - static void PopulateArrayWithBattlers(u8 *battlers) { u32 i; @@ -5292,8 +5290,8 @@ static void TryChangeTurnOrder(void) static void TryChangingTurnOrderEffects(u32 battler1, u32 battler2, u32 *quickClawRandom, u32 *quickDrawRandom) { enum Ability ability1 = GetBattlerAbility(battler1); - enum ItemHoldEffect holdEffectBattler1 = GetBattlerHoldEffect(battler1); - enum ItemHoldEffect holdEffectBattler2 = GetBattlerHoldEffect(battler2); + enum HoldEffect holdEffectBattler1 = GetBattlerHoldEffect(battler1); + enum HoldEffect holdEffectBattler2 = GetBattlerHoldEffect(battler2); enum Ability ability2 = GetBattlerAbility(battler2); // Battler 1 @@ -5303,7 +5301,7 @@ static void TryChangingTurnOrderEffects(u32 battler1, u32 battler2, u32 *quickCl // Quick Claw and Custap Berry if (!gProtectStructs[battler1].quickDraw && ((holdEffectBattler1 == HOLD_EFFECT_QUICK_CLAW && quickClawRandom[battler1]) - || (holdEffectBattler1 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler1, 4, gBattleMons[battler1].item)))) + || (holdEffectBattler1 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler1, ability1, 4, gBattleMons[battler1].item)))) gProtectStructs[battler1].usedCustapBerry = TRUE; // Battler 2 @@ -5313,7 +5311,7 @@ static void TryChangingTurnOrderEffects(u32 battler1, u32 battler2, u32 *quickCl // Quick Claw and Custap Berry if (!gProtectStructs[battler2].quickDraw && ((holdEffectBattler2 == HOLD_EFFECT_QUICK_CLAW && quickClawRandom[battler2]) - || (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, 4, gBattleMons[battler2].item)))) + || (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, ability2, 4, gBattleMons[battler2].item)))) gProtectStructs[battler2].usedCustapBerry = TRUE; } @@ -5885,7 +5883,7 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState enum BattleMoveEffects moveEffect = GetMoveEffect(move); u32 species, heldItem, type1, type2, type3; enum Ability ability; - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; enum Gimmick gimmick = GetActiveGimmick(battler); if (move == MOVE_STRUGGLE) @@ -6120,7 +6118,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battler) { u32 moveType; u32 heldItem = gBattleMons[battler].item; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); gBattleStruct->dynamicMoveType = 0; gBattleStruct->battlerState[battler].ateBoost = FALSE; diff --git a/src/battle_message.c b/src/battle_message.c index 591f28f7fc..087c436d91 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -289,10 +289,10 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNRAGEBUILDING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s rage is building!"), [STRINGID_PKMNMOVEWASDISABLED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled!"), [STRINGID_PKMNMOVEISDISABLED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_CURRENT_MOVE} is disabled!\p"), - [STRINGID_PKMNMOVEDISABLEDNOMORE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s move is no longer disabled!"), + [STRINGID_PKMNMOVEDISABLEDNOMORE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s move is no longer disabled!"), [STRINGID_PKMNGOTENCORE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} must do an encore!"), [STRINGID_PKMNGOTENCOREDMOVE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} can only use {B_CURRENT_MOVE}!\p"), - [STRINGID_PKMNENCOREENDED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} ended its encore!"), + [STRINGID_PKMNENCOREENDED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} ended its encore!"), [STRINGID_PKMNTOOKAIM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} took aim at {B_DEF_NAME_WITH_PREFIX2}!"), [STRINGID_PKMNSKETCHEDMOVE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} sketched {B_BUFF1}!"), [STRINGID_PKMNTRYINGTOTAKEFOE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is hoping to take its attacker down with it!"), @@ -354,7 +354,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} restored HP using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_EFF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSROMANCEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents romance!"), //not in gen 5+, ability popup - [STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents confusion!"), //not in gen 5+, ability popup + [STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} prevents confusion!"), //not in gen 5+, ability popup [STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup [STRINGID_PKMNANCHORSITSELFWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} anchors itself with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNCUTSATTACKWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} cuts {B_DEF_NAME_WITH_PREFIX2}'s Attack!"), //not in gen 5+, ability popup @@ -574,7 +574,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_TRANSFERHELDITEM] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} received {B_LAST_ITEM} from {B_ATK_NAME_WITH_PREFIX2}"), [STRINGID_EMBARGOENDS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} can use items again!"), [STRINGID_ELECTROMAGNETISM] = COMPOUND_STRING("electromagnetism"), - [STRINGID_BUFFERENDS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} wore off!"), + [STRINGID_BUFFERENDS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_BUFF1} wore off!"), [STRINGID_TELEKINESISENDS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} was freed from the telekinesis!"), [STRINGID_TAILWINDENDS] = COMPOUND_STRING("{B_ATK_TEAM1} team's Tailwind petered out!"), [STRINGID_LUCKYCHANTENDS] = COMPOUND_STRING("{B_ATK_TEAM1} team's Lucky Chant wore off!"), @@ -710,9 +710,9 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNBURNHEALED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s burn was cured!"), [STRINGID_REDCARDACTIVATE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} held up its Red Card against {B_ATK_NAME_WITH_PREFIX2}!"), [STRINGID_EJECTBUTTONACTIVATE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} is switched out with the {B_LAST_ITEM}!"), - [STRINGID_ATKGOTOVERINFATUATION] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} got over its infatuation!"), - [STRINGID_TORMENTEDNOMORE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is no longer tormented!"), - [STRINGID_HEALBLOCKEDNOMORE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is cured of its heal block!"), + [STRINGID_ATKGOTOVERINFATUATION] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} got over its infatuation!"), + [STRINGID_TORMENTEDNOMORE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} is no longer tormented!"), + [STRINGID_HEALBLOCKEDNOMORE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} is cured of its heal block!"), [STRINGID_ATTACKERBECAMEFULLYCHARGED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} became fully charged due to its bond with its trainer!\p"), [STRINGID_ATTACKERBECAMEASHSPECIES] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} became Ash-Greninja!\p"), [STRINGID_EXTREMELYHARSHSUNLIGHT] = COMPOUND_STRING("The sunlight turned extremely harsh!"), @@ -1292,8 +1292,14 @@ const u16 gSafariPokeblockResultStringIds[] = [B_MSG_MON_IGNORED] = STRINGID_PKMNIGNOREDX }; -const u16 gBerryEffectStringIds[] = +const u16 CureStatusBerryEffectStringID[] = { + [B_MSG_CURED_PARALYSIS] = STRINGID_PKMNSITEMCUREDPARALYSIS, + [B_MSG_CURED_POISON] = STRINGID_PKMNSITEMCUREDPOISON, + [B_MSG_CURED_BURN] = STRINGID_PKMNSITEMHEALEDBURN, + [B_MSG_CURED_FREEEZE] = STRINGID_PKMNSITEMDEFROSTEDIT, + [B_MSG_CURED_FROSTBITE] = STRINGID_PKMNSITEMHEALEDFROSTBITE, + [B_MSG_CURED_SLEEP] = STRINGID_PKMNSITEMWOKEIT, [B_MSG_CURED_PROBLEM] = STRINGID_PKMNSITEMCUREDPROBLEM, [B_MSG_NORMALIZED_STATUS] = STRINGID_PKMNSITEMNORMALIZEDSTATUS }; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d61b9363dd..fedc5cfe73 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle.h" +#include "battle_hold_effects.h" #include "battle_message.h" #include "battle_anim.h" #include "battle_ai_main.h" @@ -53,7 +54,6 @@ #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" #include "constants/battle_partner.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/item_effects.h" #include "constants/moves.h" @@ -340,7 +340,7 @@ static bool32 CanAbilityShieldActivateForBattler(u32 battler); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); static void Cmd_printattackstring(void); -static void Cmd_unused0x3(void); +static void Cmd_unused_0x3(void); static void Cmd_critcalc(void); static void Cmd_damagecalc(void); static void Cmd_typecalc(void); @@ -524,7 +524,7 @@ static void Cmd_presentdamagecalculation(void); static void Cmd_setsafeguard(void); static void Cmd_magnitudedamagecalculation(void); static void Cmd_jumpifnopursuitswitchdmg(void); -static void Cmd_tryrestorehpberry(void); +static void Cmd_tryactivateitem(void); static void Cmd_halvehp(void); static void Cmd_copyfoestats(void); static void Cmd_rapidspinfree(void); @@ -599,7 +599,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_attackcanceler, //0x0 Cmd_accuracycheck, //0x1 Cmd_printattackstring, //0x2 - Cmd_unused0x3, //0x3 + Cmd_unused_0x3, //0x3 Cmd_critcalc, //0x4 Cmd_damagecalc, //0x5 Cmd_typecalc, //0x6 @@ -783,7 +783,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_setsafeguard, //0xB8 Cmd_magnitudedamagecalculation, //0xB9 Cmd_jumpifnopursuitswitchdmg, //0xBA - Cmd_tryrestorehpberry, //0xBB + Cmd_tryactivateitem, //0xBB Cmd_halvehp, //0xBC Cmd_copyfoestats, //0xBD Cmd_rapidspinfree, //0xBE @@ -1363,7 +1363,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u enum BattleMoveEffects effect = GetMoveEffect(move); enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker); - enum ItemHoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker); + enum HoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker); if (move == NO_ACC_CALC_CHECK_LOCK_ON) { @@ -1503,7 +1503,7 @@ static void Cmd_printattackstring(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused0x3(void) +static void Cmd_unused_0x3(void) { } @@ -1525,7 +1525,7 @@ static inline u32 GetCriticalHitOdds(u32 critChance) return sCriticalHitOdds[critChance]; } -static inline u32 IsBattlerLeekAffected(u32 battler, enum ItemHoldEffect holdEffect) +static inline u32 IsBattlerLeekAffected(u32 battler, enum HoldEffect holdEffect) { if (holdEffect == HOLD_EFFECT_LEEK) { @@ -1535,7 +1535,7 @@ static inline u32 IsBattlerLeekAffected(u32 battler, enum ItemHoldEffect holdEff return FALSE; } -static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum ItemHoldEffect holdEffect) +static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum HoldEffect holdEffect) { u32 critStageIncrease = 0; @@ -1560,7 +1560,7 @@ static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum ItemHoldEffe return critStageIncrease; } -s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum ItemHoldEffect holdEffectAtk) +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk) { s32 critChance = 0; @@ -1609,7 +1609,7 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA // Threshold = Base Speed / 2 // High crit move = 8 * (Base Speed / 2) // Focus Energy = 4 * (Base Speed / 2) -s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum ItemHoldEffect holdEffectAtk) +s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectAtk) { s32 critChance = 0; s32 moveCritStage = GetMoveCriticalHitStage(gCurrentMove); @@ -1677,7 +1677,7 @@ static void Cmd_critcalc(void) moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove), battlerDef; bool32 calcSpreadMoveDamage = IsSpreadMove(moveTarget) && !IsBattleMoveStatus(gCurrentMove); - enum ItemHoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker); + enum HoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker); gPotentialItemEffectBattler = gBattlerAttacker; for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) @@ -1810,7 +1810,7 @@ static void Cmd_adjustdamage(void) { CMD_ARGS(); - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; u8 param; u32 battlerDef; u32 rand = Random() % 100; @@ -2386,15 +2386,6 @@ static void Cmd_datahpupdate(void) && IsBattlerTurnDamaged(gBattlerTarget)) GetBattlerPartyState(gBattlerTarget)->timesGotHit++; - if (GetMoveEffect(gCurrentMove) == EFFECT_KNOCK_OFF - && !isPassiveHpUpdate - && IsBattlerTurnDamaged(gBattlerTarget) - && gBattleMons[gBattlerTarget].item != ITEM_NONE - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerTarget].item) - && !NoAliveMonsForEitherParty()) - gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = TRUE; - if (disguiseActivates) return; } @@ -4454,7 +4445,7 @@ static bool32 BattleTypeAllowsExp(void) static u32 GetMonHoldEffect(struct Pokemon *mon) { - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); if (item == ITEM_ENIGMA_BERRY_E_READER) @@ -4473,7 +4464,7 @@ static void Cmd_getexp(void) { CMD_ARGS(u8 battler); - enum ItemHoldEffect holdEffect; + enum HoldEffect holdEffect; s32 i; // also used as stringId u8 *expMonId = &gBattleStruct->expGetterMonId; u32 currLvl; @@ -5492,7 +5483,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) return battler; } -static inline bool32 IsProtectivePadsProtected(u32 battler, enum ItemHoldEffect holdEffect) +static inline bool32 IsProtectivePadsProtected(u32 battler, enum HoldEffect holdEffect) { if (holdEffect != HOLD_EFFECT_PROTECTIVE_PADS) return FALSE; @@ -5658,12 +5649,23 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) switch (moveEffect) { case EFFECT_KNOCK_OFF: - if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff - && gBattleMons[gBattlerTarget].item != ITEM_NONE + if (gBattleMons[gBattlerTarget].item != ITEM_NONE + && IsBattlerAlive(gBattlerAttacker) && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker)) + && !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove) + && CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerTarget].item) + && !NoAliveMonsForEitherParty()) { u32 side = GetBattlerSide(gBattlerTarget); + + if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) + { + gBattlerAbility = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_StickyHoldActivatesRet; + effect = TRUE; + break; + } gLastUsedItem = gBattleMons[gBattlerTarget].item; gBattleMons[gBattlerTarget].item = 0; if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS) @@ -5681,11 +5683,9 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) gWishFutureKnock.knockedOffMons[side] |= 1u << gBattlerPartyIndexes[gBattlerTarget]; } - gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = FALSE; BattleScriptCall(BattleScript_KnockedOff); effect = TRUE; } - gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = FALSE; break; case EFFECT_STEAL_ITEM: if (!CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item) @@ -6097,10 +6097,24 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_ITEM_EFFECTS_TARGET: - if (ItemBattleEffects(ITEMEFFECT_TARGET, gBattlerTarget)) + { + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); + if (ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnTargetHitActivation) + || ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnStatusChangeActivation)) effect = TRUE; gBattleScripting.moveendState++; break; + } + case MOVEEND_ITEM_EFFECTS_ATTACKER_1: + { + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); + if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnAttackerAfterHitActivation) + || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) + || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) + effect = TRUE; + gBattleScripting.moveendState++; + break; + } case MOVEEND_SYMBIOSIS: for (i = 0; i < gBattlersCount; i++) { @@ -6120,12 +6134,6 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_ITEM_EFFECTS_ALL: // item effects for all battlers - if (ItemBattleEffects(ITEMEFFECT_MOVE_END, 0)) - effect = TRUE; - else - gBattleScripting.moveendState++; - break; case MOVEEND_ATTACKER_INVISIBLE: // make attacker sprite invisible if (IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) && gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)) @@ -6163,12 +6171,6 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_KINGSROCK: // King's rock - // These effects will occur at each hit in a multi-strike move - if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0)) - effect = TRUE; - gBattleScripting.moveendState++; - break; case MOVEEND_SUBSTITUTE: for (i = 0; i < gBattlersCount; i++) { @@ -6392,6 +6394,12 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } + case MOVEEND_HP_THRESHHOLD_ITEMS_TARGET: + if (gMultiHitCounter + && ItemBattleEffects(gBattlerTarget, gBattlerAttacker, GetBattlerHoldEffect(gBattlerTarget), IsOnHpThresholdActivation)) + effect = TRUE; + gBattleScripting.moveendState++; + break; case MOVEEND_MULTIHIT_MOVE: { if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) @@ -6456,31 +6464,15 @@ static void Cmd_moveend(void) effect = HandleMoveEndMoveBlock(moveEffect); gBattleScripting.moveendState++; break; - case MOVEEND_ITEM_EFFECTS_ATTACKER: - // ITEMEFFECT_MOVE_END loops over all battlers, not just attacker. - // It will executre only the first mon with an applicable item. - // So presumably it is a bug - if (ItemBattleEffects(ITEMEFFECT_MOVE_END, gBattlerAttacker)) + case MOVEEND_ITEM_EFFECTS_ATTACKER_2: + { + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); + if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) + || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) effect = TRUE; gBattleScripting.moveendState++; break; - case MOVEEND_ITEM_THROAT_SPRAY: - if (IsSoundMove(gCurrentMove) - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_THROAT_SPRAY - && IsBattlerAlive(gBattlerAttacker) - && IsAnyTargetAffected(gBattlerAttacker) - && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) - && !NoAliveMonsForEitherParty()) // Don't activate if battle will end - { - gLastUsedItem = gBattleMons[gBattlerAttacker].item; - gBattleScripting.battler = gBattlerAttacker; - SET_STATCHANGER(STAT_SPATK, 1, FALSE); - effect = TRUE; - BattleScriptCall(BattleScript_AttackerItemStatRaise); - } - gBattleScripting.moveendState++; - break; + } case MOVEEND_ABILITY_BLOCK: effect = HandleMoveEndAbilityBlock(gBattlerAttacker, gBattlerTarget, gCurrentMove); gBattleScripting.moveendState++; @@ -6503,6 +6495,19 @@ static void Cmd_moveend(void) gBattleStruct->moveEndBattlerId = 0; gBattleScripting.moveendState++; break; + case MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET: + while (gBattleStruct->moveEndBattlerId < gBattlersCount) + { + u32 battlerDef = gBattleStruct->moveEndBattlerId++; + if (battlerDef == gBattlerAttacker) + continue; + enum HoldEffect holdEffect = GetBattlerHoldEffect(battlerDef); + if (ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsKeeMarangaBerryActivation) + || ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsOnHpThresholdActivation)) + return; + } + gBattleScripting.moveendState++; + break; case MOVEEND_RED_CARD: { u32 redCardBattlers = 0, i; @@ -6550,7 +6555,7 @@ static void Cmd_moveend(void) } } if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; else gBattleScripting.moveendState++; break; @@ -6599,12 +6604,12 @@ static void Cmd_moveend(void) } } if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; else gBattleScripting.moveendState++; break; - case MOVEEND_LIFEORB_SHELLBELL: - if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0)) + case MOVEEND_LIFE_ORB_SHELL_BELL: + if (ItemBattleEffects(gBattlerAttacker, 0, GetBattlerHoldEffect(gBattlerAttacker), IsLifeOrbShellBellActivation)) effect = TRUE; gBattleScripting.moveendState++; break; @@ -6664,10 +6669,22 @@ static void Cmd_moveend(void) } } if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; else gBattleScripting.moveendState++; break; + case MOVEEND_HIT_ESCAPE: + if (moveEffect == EFFECT_HIT_ESCAPE + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && IsBattlerTurnDamaged(gBattlerTarget) + && IsBattlerAlive(gBattlerAttacker) + && !NoAliveMonsForBattlerSide(gBattlerTarget)) + { + effect = TRUE; + BattleScriptCall(BattleScript_EffectHitEscape); + } + gBattleScripting.moveendState++; + break; case MOVEEND_EJECT_PACK: { // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. @@ -6703,10 +6720,6 @@ static void Cmd_moveend(void) if (!(ejectPackBattlers & 1u << battler)) continue; - // Hit escape moves activate before Eject Pack for user - if (moveEffect == EFFECT_HIT_ESCAPE && gBattlerAttacker == battler) - continue; - gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; effect = TRUE; @@ -6716,21 +6729,32 @@ static void Cmd_moveend(void) break; // Only the fastest Eject item activates } } - if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; - else - gBattleScripting.moveendState++; + gBattleScripting.moveendState++; break; - case MOVEEND_HIT_ESCAPE: - if (moveEffect == EFFECT_HIT_ESCAPE - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker) - && !NoAliveMonsForBattlerSide(gBattlerTarget)) + + case MOVEEND_ITEMS_EFFECTS_ALL: + while (gBattleStruct->moveEndBattlerId < gBattlersCount) { - effect = TRUE; - BattleScriptCall(BattleScript_EffectHitEscape); + u32 battler = gBattleStruct->moveEndBattlerId++; + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); + if (ItemBattleEffects(battler, 0, holdEffect, IsOnStatusChangeActivation) + || ItemBattleEffects(battler, 0, holdEffect, IsOnHpThresholdActivation)) + return; } + gBattleStruct->moveEndBattlerId = 0; + gBattleScripting.moveendState++; + break; + case MOVEEND_WHITE_HERB: + while (gBattleStruct->moveEndBattlerId < gBattlersCount) + { + u32 battler = gBattleStruct->moveEndBattlerId++; + if (!IsBattlerAlive(battler)) + continue; + + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsWhiteHerbActivation)) + return; + } + gBattleStruct->moveEndBattlerId = 0; gBattleScripting.moveendState++; break; case MOVEEND_OPPORTUNIST: @@ -6751,7 +6775,8 @@ static void Cmd_moveend(void) u32 battler = gBattleStruct->moveEndBattlerId++; if (!IsBattlerAlive(battler)) continue; - if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB, battler)) + + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbActivation)) return; } gBattleStruct->moveEndBattlerId = 0; @@ -6792,17 +6817,6 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_WHITE_HERB: - for (i = 0; i < gBattlersCount; i++) - { - if (!IsBattlerAlive(i)) - continue; - - if (ItemBattleEffects(ITEMEFFECT_WHITE_HERB, i)) - return; - } - gBattleScripting.moveendState++; - break; case MOVEEND_THIRD_MOVE_BLOCK: // Special case for Steel Roller since it has to check the chosen move if (GetMoveEffect(gChosenMove) == EFFECT_STEEL_ROLLER && IsBattlerTurnDamaged(gBattlerTarget)) @@ -7813,7 +7827,10 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) gDisableStructs[battler].truantSwitchInHack = 0; - if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler)) + if (DoSwitchInAbilities(battler)) + return TRUE; + + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnSwitchInActivation)) return TRUE; for (i = 0; i < gBattlersCount; i++) @@ -7844,7 +7861,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) for (i = 0; i < gBattlersCount; i++) { - if (ItemBattleEffects(ITEMEFFECT_WHITE_HERB, i)) + if (ItemBattleEffects(i, 0, GetBattlerHoldEffect(i), IsWhiteHerbActivation)) return TRUE; } for (i = 0; i < gBattlersCount; i++) @@ -7854,7 +7871,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) } for (i = 0; i < gBattlersCount; i++) { - if (ItemBattleEffects(ITEMEFFECT_MIRROR_HERB, i)) + if (ItemBattleEffects(i, 0, GetBattlerHoldEffect(i), IsMirrorHerbActivation)) return TRUE; } @@ -7866,6 +7883,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; } + gSpecialStatuses[battler].switchInItemDone = FALSE; gDisableStructs[battler].hazardsDone = FALSE; gBattleStruct->battlerState[battler].forcedSwitch = FALSE; return FALSE; @@ -7918,7 +7936,7 @@ static void Cmd_switchineffects(void) return; } } - if (TrySwitchInEjectPack(ITEMEFFECT_NONE)) + if (TrySwitchInEjectPack(OTHER)) return; // All battlers done, end for (i = 0; i < gBattlersCount; i++) @@ -7930,7 +7948,7 @@ static void Cmd_switchineffects(void) break; default: UpdateSentMonFlags(battler); - if (!DoSwitchInEffectsForBattler(battler) && !TrySwitchInEjectPack(ITEMEFFECT_NONE)) + if (!DoSwitchInEffectsForBattler(battler) && !TrySwitchInEjectPack(OTHER)) gBattlescriptCurrInstr = cmd->nextInstr; break; } @@ -9767,7 +9785,7 @@ static void Cmd_trysetrest(void) gBattlerTarget = gBattlerAttacker; gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].maxHP * (-1); enum Ability ability = GetBattlerAbility(gBattlerTarget); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); if (IsBattlerTerrainAffected(gBattlerTarget, ability, holdEffect, STATUS_FIELD_ELECTRIC_TERRAIN)) { @@ -10095,7 +10113,7 @@ static void TryPlayStatChangeAnimation(u32 battler, enum Ability ability, u32 st static u32 ChangeStatBuffs(u32 battler, s8 statValue, u32 statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr) { u32 index, battlerAbility; - enum ItemHoldEffect battlerHoldEffect; + enum HoldEffect battlerHoldEffect; battlerAbility = GetBattlerAbility(battler); battlerHoldEffect = GetBattlerHoldEffect(battler); gSpecialStatuses[battler].changedStatsBattlerId = gBattlerAttacker; @@ -10796,7 +10814,7 @@ static void Cmd_tryKO(void) CMD_ARGS(const u8 *failInstr); enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); enum Ability targetAbility = GetBattlerAbility(gBattlerTarget); u32 rand = Random() % 100; u32 affectionScore = GetBattlerAffectionHearts(gBattlerTarget); @@ -12099,12 +12117,28 @@ static void Cmd_jumpifnopursuitswitchdmg(void) } } -static void Cmd_tryrestorehpberry(void) +static void Cmd_tryactivateitem(void) { - CMD_ARGS(u8 battler); + CMD_ARGS(u8 battler, u8 flag); u32 battler = GetBattlerForBattleScript(cmd->battler); - if (TryRestoreHPBerries(battler, ITEMEFFECT_TRY_HEALING)) - return; + + switch (cmd->flag) + { + case ON_ITEM_USABLE_AGAIN: + case ON_ITEM_PICK_UP: + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsActivationForceTriggerItem)) + return; + break; + case ON_BERRY_HARVEST: + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsActivationOnBerry)) + return; + break; + case ON_ITEM_HP_THRESHOLD: + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnHpThresholdActivation)) + return; + break; + } + gBattlescriptCurrInstr = cmd->nextInstr; } @@ -12584,7 +12618,7 @@ static void Cmd_trysethelpinghand(void) } } -// Trick +// Trick // TODO: Sticky Hold static void Cmd_tryswapitems(void) { CMD_ARGS(const u8 *failInstr); @@ -12804,7 +12838,7 @@ static void Cmd_setyawn(void) { CMD_ARGS(const u8 *failInstr); enum Ability ability = GetBattlerAbility(gBattlerTarget); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); + enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); if (gBattleMons[gBattlerTarget].volatiles.yawn || gBattleMons[gBattlerTarget].status1 & STATUS1_ANY) @@ -14734,7 +14768,7 @@ u8 GetFirstFaintedPartyIndex(u8 battler) void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler) { - enum ItemHoldEffect holdEffect = GetMonHoldEffect(&gPlayerParty[expGetterMonId]); + enum HoldEffect holdEffect = GetMonHoldEffect(&gPlayerParty[expGetterMonId]); if (IsTradedMon(&gPlayerParty[expGetterMonId])) *expAmount = (*expAmount * 150) / 100; @@ -15025,7 +15059,7 @@ void BS_SetTerrain(void) default: break; } - enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker); + enum HoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker); gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; @@ -15992,6 +16026,13 @@ void BS_ClearMoveResultFlags(void) gBattlescriptCurrInstr = cmd->nextInstr; } +void BS_ClearSpecialStatuses(void) +{ + NATIVE_ARGS(); + memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); + gBattlescriptCurrInstr = cmd->nextInstr; +} + void BS_JumpIfMoveResultFlags(void) { NATIVE_ARGS(u16 value, const u8 *jumpInstr); @@ -16337,7 +16378,7 @@ void BS_TryBoosterEnergy(void) && !(ability == ABILITY_QUARK_DRIVE && cmd->onFieldStatus != ON_WEATHER)) continue; - if (TryBoosterEnergy(battlerByTurnOrder, ability, ITEMEFFECT_NONE)) + if (TryBoosterEnergy(battlerByTurnOrder, ability, IsOnEffectActivation)) return; } @@ -17514,8 +17555,14 @@ void BS_GetTotemBoost(void) void BS_ActivateItemEffects(void) { NATIVE_ARGS(); - if (ItemBattleEffects(ITEMEFFECT_TRY_HEALING, gBattlerTarget)) - return; + for (u32 battler = 0; battler < gBattlersCount; battler++) + { + if (!IsBattlerAlive(battler)) + continue; + + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsActivationForceTriggerItem)) + return; + } gBattlescriptCurrInstr = cmd->nextInstr; } @@ -17523,37 +17570,33 @@ void BS_TryRoomService(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); u32 battler = GetBattlerForBattleScript(cmd->battler); - if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_ROOM_SERVICE && TryRoomService(battler)) - { - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; - } + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); + if (holdEffect == HOLD_EFFECT_ROOM_SERVICE && ItemBattleEffects(battler, 0, holdEffect, IsOnEffectActivation)) + return; + gBattlescriptCurrInstr = cmd->failInstr; } void BS_TryTerrainSeed(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); u32 battler = GetBattlerForBattleScript(cmd->battler); - if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_SEEDS) + if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_TERRAIN_SEED) { enum ItemEffect effect = ITEM_NO_EFFECT; u16 item = gBattleMons[battler].item; switch (GetBattlerHoldEffectParam(battler)) { case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, item, ITEMEFFECT_NONE); + effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, item, IsOnEffectActivation); break; case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, item, ITEMEFFECT_NONE); + effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, item, IsOnEffectActivation); break; case HOLD_EFFECT_PARAM_MISTY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, item, ITEMEFFECT_NONE); + effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, item, IsOnEffectActivation); break; case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, ITEMEFFECT_NONE); + effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, IsOnEffectActivation); break; } @@ -17793,23 +17836,29 @@ void BS_JumpIfNotRototillerAffected(void) } } +// TODO: There might be a way to do it without a flag void BS_ConsumeBerry(void) { NATIVE_ARGS(u8 battler, bool8 fromBattler); u32 battler = GetBattlerForBattleScript(cmd->battler); - if (gBattleScripting.overrideBerryRequirements == 2) - { - gBattlescriptCurrInstr = cmd->nextInstr; - return; - } if (cmd->fromBattler) gLastUsedItem = gBattleMons[battler].item; - GetBattlerPartyState(battler)->ateBerry = TRUE; - gBattleScripting.battler = gEffectBattler = gBattlerTarget = battler; // Cover all berry effect battler cases. e.g. ChangeStatBuffs uses target ID - if (ItemBattleEffects(ITEMEFFECT_USE_LAST_ITEM, battler)) + if (GetItemPocket(gLastUsedItem) != POCKET_BERRIES || gBattleScripting.overrideBerryRequirements == 2) + { + gBattleScripting.overrideBerryRequirements = 0; + gBattlescriptCurrInstr = cmd->nextInstr; return; + } + + gBattleScripting.overrideBerryRequirements = 1; + GetBattlerPartyState(battler)->ateBerry = TRUE; + if (ItemBattleEffects(battler, 0, GetItemHoldEffect(gLastUsedItem), IsActivationOnBerry)) + { + gBattleScripting.overrideBerryRequirements = 2; + return; + } gBattlescriptCurrInstr = cmd->nextInstr; } @@ -17934,6 +17983,8 @@ void BS_CureCertainStatuses(void) gDisableStructs[gBattlerTarget].disabledMove = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE; } + + gBattleScripting.battler = gBattlerTarget; gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 05b27a48ff..0c552953b8 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -14,7 +14,6 @@ #include "sprite.h" #include "util.h" #include "constants/abilities.h" -#include "constants/hold_effects.h" #include "constants/rgb.h" // Sets flags and variables upon a battler's Terastallization. @@ -62,7 +61,7 @@ void ApplyBattlerVisualsForTeraAnim(u32 battler) // Returns whether a battler can Terastallize. bool32 CanTerastallize(u32 battler) { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); if (gBattleMons[battler].volatiles.transformed && GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_TERAPAGOS) return FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index 7ca5e86b50..b95fdd3a28 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10,6 +10,7 @@ #include "battle_setup.h" #include "battle_z_move.h" #include "battle_gimmick.h" +#include "battle_hold_effects.h" #include "generational_changes.h" #include "party_menu.h" #include "pokemon.h" @@ -43,7 +44,6 @@ #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" #include "constants/battle_string_ids.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/item_effects.h" #include "constants/moves.h" @@ -64,8 +64,6 @@ static bool32 TryRemoveScreens(u32 battler); static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler); static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); -static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); -static bool32 CanBeInfinitelyConfused(u32 battler); static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, enum Ability abilityAffected, const u8 *battleScript, enum FunctionCallOption option); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option); @@ -302,6 +300,45 @@ static inline bool32 IsDragonDartsSecondHit(u32 effect) return FALSE; } +bool32 IsUnnerveBlocked(u32 battler, u32 itemId) +{ + if (GetItemPocket(itemId) != POCKET_BERRIES) + return FALSE; + + if (gBattleScripting.overrideBerryRequirements > 0) // Berries that aren't eaten naturally ignore unnerve + return FALSE; + + if (IsUnnerveAbilityOnOpposingSide(battler)) + return TRUE; + + return FALSE; +} + +static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) +{ + for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + { + if (battler == battlerDef || IsBattlerAlly(battler, battlerDef)) + continue; + + if (!IsBattlerAlive(battlerDef)) + continue; + + enum Ability ability = GetBattlerAbility(battlerDef); + switch (ability) + { + case ABILITY_UNNERVE: + case ABILITY_AS_ONE_ICE_RIDER: + case ABILITY_AS_ONE_SHADOW_RIDER: + return TRUE; + default: + break; + } + } + + return FALSE; +} + bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { enum Ability ability = GetBattlerAbility(battlerAtk); @@ -891,7 +928,7 @@ void HandleAction_ActionFinished(void) gBattleStruct->monToSwitchIntoId[gBattlerByTurnOrder[gCurrentTurnActionNumber]] = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; - SpecialStatusesClear(); + memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED @@ -1336,7 +1373,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) u32 limitations = 0; u8 moveId = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK; u32 move = gBattleMons[battler].moves[moveId]; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -1598,7 +1635,7 @@ u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) { u32 move; enum BattleMoveEffects moveEffect; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; s32 i; @@ -2325,7 +2362,7 @@ static enum MoveCanceller CancellerZMoves(struct BattleContext *ctx) static enum MoveCanceller CancellerChoiceLock(struct BattleContext *ctx) { u16 *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); + enum HoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); if (gChosenMove != MOVE_STRUGGLE && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) @@ -3291,7 +3328,7 @@ bool32 HadMoreThanHalfHpNowDoesnt(u32 battler) #define ANIM_STAT_SPEED 5 #define ANIM_STAT_ACC 6 #define ANIM_STAT_EVASION 7 -static void ChooseStatBoostAnimation(u32 battler) +void ChooseStatBoostAnimation(u32 battler) { u32 stat; bool32 statBuffMoreThan1 = FALSE; @@ -3820,6 +3857,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec } break; case ABILITYEFFECT_ON_SWITCHIN: + if (gHoldEffectsInfo[HOLD_EFFECT_MIRROR_HERB].onSwitchIn) + break; gBattleScripting.battler = battler; switch (gLastUsedAbility) { @@ -4777,16 +4816,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, enum Ability ability, u32 spec case ABILITYEFFECT_MOVE_END: // Think contact abilities. switch (gLastUsedAbility) { - case ABILITY_STICKY_HOLD: - if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff && IsBattlerAlive(gBattlerTarget)) - { - gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff = FALSE; - gBattlerAbility = gBattlerTarget; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; - effect++; - } - break; case ABILITY_JUSTIFIED: if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) @@ -5774,7 +5803,7 @@ void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) gBattleMainFunc = RunBattleScriptCommands; } -bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect, u32 terrainFlag) +bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 terrainFlag) { if (!(gFieldStatuses & terrainFlag)) return FALSE; @@ -6092,21 +6121,16 @@ bool32 CanBeConfused(u32 battler) } // second argument is 1/X of current hp compared to max hp -bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId) +bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, u32 itemId) { - bool32 isBerry = (GetItemPocket(itemId) == POCKET_BERRIES); - if (!IsBattlerAlive(battler)) return FALSE; if (gBattleScripting.overrideBerryRequirements) return TRUE; - // Unnerve prevents consumption of opponents' berries. - if (isBerry && IsUnnerveAbilityOnOpposingSide(battler)) - return FALSE; if (gBattleMons[battler].hp <= gBattleMons[battler].maxHP / hpFraction) return TRUE; - if (hpFraction <= 4 && isBerry + if (hpFraction <= 4 && GetItemPocket(itemId) == POCKET_BERRIES && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 2 && IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_GLUTTONY)) return TRUE; @@ -6114,1454 +6138,6 @@ bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId) return FALSE; } -static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, enum ItemCaseId caseID) -{ - if (HasEnoughHpToEatBerry(battler, (B_CONFUSE_BERRIES_HEAL >= GEN_7 ? 4 : 2), itemId) - && (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock)) - { - PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, flavorId); - - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / GetBattlerItemHoldEffectParam(battler, itemId); - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleStruct->moveDamage[battler] *= -1; - - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - { - gBattleStruct->moveDamage[battler] *= 2; - gBattlerAbility = battler; - } - gBattleScripting.battler = battler; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - { - if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) - BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); - else - BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); - } - else - { - if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) - BattleScriptCall(BattleScript_BerryConfuseHealRet); - else - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); - } - - return ITEM_HP_CHANGE; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, u32 statId, enum ItemCaseId caseID) -{ - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId)) - { - gEffectBattler = gBattleScripting.battler = battler; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - SET_STATCHANGER(statId, 2, FALSE); - else - SET_STATCHANGER(statId, 1, FALSE); - - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; - gBattleScripting.animArg2 = 0; - - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - else - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - return ITEM_STATS_CHANGE; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, enum ItemCaseId caseID) -{ - s32 stat; - - for (stat = STAT_ATK; stat < NUM_STATS; stat++) - { - if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) - break; - } - if (stat != NUM_STATS && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId)) - { - enum Ability battlerAbility = GetBattlerAbility(battler); - u32 savedAttacker = gBattlerAttacker; - // MoodyCantRaiseStat requires that the battler is set to gBattlerAttacker - gBattlerAttacker = gBattleScripting.battler = battler; - gBattleScripting.statChanger = 0; - if (battlerAbility != ABILITY_CONTRARY) - stat = RandomUniformExcept(RNG_RANDOM_STAT_UP, STAT_ATK, NUM_STATS - 1, MoodyCantRaiseStat); - else - stat = RandomUniformExcept(RNG_RANDOM_STAT_UP, STAT_ATK, NUM_STATS - 1, MoodyCantLowerStat); - gBattlerAttacker = savedAttacker; - - PREPARE_STAT_BUFFER(gBattleTextBuff1, stat); - gEffectBattler = battler; - if (battlerAbility == ABILITY_RIPEN) - SET_STATCHANGER(stat, 4, FALSE); - else - SET_STATCHANGER(stat, 2, FALSE); - - gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + stat; - gBattleScripting.animArg2 = 0; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - else - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - return ITEM_STATS_CHANGE; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect TrySetMicleBerry(u32 battler, u32 itemId, enum ItemCaseId caseID) -{ - if (HasEnoughHpToEatBerry(battler, 4, itemId)) - { - gBattleStruct->battlerState[battler].usedMicleBerry = TRUE; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - { - BattleScriptExecute(BattleScript_MicleBerryActivateEnd2); - } - else - { - BattleScriptCall(BattleScript_MicleBerryActivateRet); - } - return ITEM_EFFECT_OTHER; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect TrySetEnigmaBerry(u32 battler) -{ - if (IsBattlerAlive(battler) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && ((IsBattlerTurnDamaged(battler) && gBattleStruct->moveResultFlags[battler] & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements) - && !(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) - && (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock)) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = (gBattleMons[battler].maxHP * 25 / 100) * -1; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - gBattleStruct->moveDamage[battler] *= 2; - - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); - return ITEM_HP_CHANGE; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum DamageCategory category) -{ - if (IsBattlerAlive(battler) - && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) - && (gBattleScripting.overrideBerryRequirements - || (!DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && GetBattleMoveCategory(gCurrentMove) == category - && battler != gBattlerAttacker - && IsBattlerTurnDamaged(battler))) - ) - { - gEffectBattler = battler; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - SET_STATCHANGER(statId, 2, FALSE); - else - SET_STATCHANGER(statId, 1, FALSE); - - gBattleScripting.battler = battler; - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; - gBattleScripting.animArg2 = 0; - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - return ITEM_STATS_CHANGE; - } - return ITEM_NO_EFFECT; -} - -enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID) -{ - if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) - { - gLastUsedItem = itemId; // For surge abilities - gEffectBattler = gBattleScripting.battler = battler; - SET_STATCHANGER(statId, 1, FALSE); - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; - gBattleScripting.animArg2 = 0; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - else - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - return ITEM_STATS_CHANGE; - } - return ITEM_NO_EFFECT; -} - -static enum ItemEffect ConsumeBerserkGene(u32 battler, enum ItemCaseId caseID) -{ - if (CanBeInfinitelyConfused(battler)) - gBattleMons[battler].volatiles.infiniteConfusion = TRUE; - - gBattlerAttacker = gEffectBattler = battler; - SET_STATCHANGER(STAT_ATK, 2, FALSE); - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK; - gBattleScripting.animArg2 = 0; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_BerserkGeneRetEnd2); - else - BattleScriptCall(BattleScript_BerserkGeneRet); - return ITEM_STATS_CHANGE; -} - -static u32 ItemRestorePp(u32 battler, u32 itemId, enum ItemCaseId caseID) -{ - struct Pokemon *mon = GetBattlerMon(battler); - u32 i, changedPP = 0; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - u32 move = GetMonData(mon, MON_DATA_MOVE1 + i); - u32 currentPP = GetMonData(mon, MON_DATA_PP1 + i); - u32 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); - u32 maxPP = CalculatePPWithBonus(move, ppBonuses, i); - if (move && (currentPP == 0 || (gBattleScripting.overrideBerryRequirements && currentPP != maxPP))) - { - u32 ppRestored = GetBattlerItemHoldEffectParam(battler, itemId); - - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - { - ppRestored *= 2; - gBattlerAbility = battler; - } - if (currentPP + ppRestored > maxPP) - changedPP = maxPP; - else - changedPP = currentPP + ppRestored; - - PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); - - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_BerryPPHealEnd2); - else - BattleScriptCall(BattleScript_BerryPPHealRet); - - gBattleScripting.battler = battler; - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); - MarkBattlerForControllerExec(battler); - if (MOVE_IS_PERMANENT(battler, i)) - gBattleMons[battler].pp[i] = changedPP; - return ITEM_PP_CHANGE; - } - } - return 0; -} - -static u32 ItemHealHp(u32 battler, u32 itemId, enum ItemCaseId caseID, bool32 percentHeal) -{ - if (!(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) - && (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock) - && HasEnoughHpToEatBerry(battler, 2, itemId)) - { - if (percentHeal) - gBattleStruct->moveDamage[battler] = (GetNonDynamaxMaxHP(battler) * GetBattlerItemHoldEffectParam(battler, itemId) / 100) * -1; - else - gBattleStruct->moveDamage[battler] = GetBattlerItemHoldEffectParam(battler, itemId) * -1; - - // check ripen - if (GetItemPocket(itemId) == POCKET_BERRIES && GetBattlerAbility(battler) == ABILITY_RIPEN) - gBattleStruct->moveDamage[battler] *= 2; - - gBattlerAbility = battler; // in SWSH, berry juice shows ability pop up but has no effect. This is mimicked here - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); - else - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); - - return ITEM_HP_CHANGE; - } - return 0; -} - -static bool32 UnnerveOn(u32 battler, u32 itemId) -{ - if (gBattleScripting.overrideBerryRequirements > 0) // Berries that aren't eaten naturally ignore unnerve - return FALSE; - - if (GetItemPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battler)) - return TRUE; - return FALSE; -} - -static bool32 GetMentalHerbEffect(u32 battler) -{ - bool32 ret = FALSE; - - // Check infatuation - if (gBattleMons[battler].volatiles.infatuation) - { - gBattleMons[battler].volatiles.infatuation = 0; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION - StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); - ret = TRUE; - } - if (B_MENTAL_HERB >= GEN_5) - { - // Check taunt - if (gDisableStructs[battler].tauntTimer != 0) - { - gDisableStructs[battler].tauntTimer = 0; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TAUNT; - PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); - ret = TRUE; - } - // Check encore - if (gDisableStructs[battler].encoreTimer != 0) - { - gDisableStructs[battler].encoredMove = 0; - gDisableStructs[battler].encoreTimer = 0; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; // STRINGID_PKMNENCOREENDED - ret = TRUE; - } - // Check torment - if (gBattleMons[battler].volatiles.torment == TRUE) - { - gBattleMons[battler].volatiles.torment = FALSE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT; - ret = TRUE; - } - // Check heal block - if (gBattleMons[battler].volatiles.healBlock) - { - gBattleMons[battler].volatiles.healBlock = FALSE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_HEALBLOCK; - ret = TRUE; - } - // Check disable - if (gDisableStructs[battler].disableTimer != 0) - { - gDisableStructs[battler].disableTimer = 0; - gDisableStructs[battler].disabledMove = 0; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE; - ret = TRUE; - } - } - return ret; -} - -static u32 TryConsumeMirrorHerb(u32 battler, enum ItemCaseId caseID) -{ - u32 effect = 0; - - if (gProtectStructs[battler].eatMirrorHerb) - { - gLastUsedItem = gBattleMons[battler].item; - gBattleScripting.battler = battler; - gProtectStructs[battler].eatMirrorHerb = 0; - ChooseStatBoostAnimation(battler); - if (caseID == ITEMEFFECT_MIRROR_HERB_FIRST_TURN) - BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); - else - BattleScriptCall(BattleScript_MirrorHerbCopyStatChange); - effect = ITEM_STATS_CHANGE; - } - return effect; -} - -u32 TryBoosterEnergy(u32 battler, enum Ability ability, enum ItemCaseId caseID) -{ - if (gDisableStructs[battler].boosterEnergyActivated || gBattleMons[battler].volatiles.transformed) - return ITEM_NO_EFFECT; - - if (((ability == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && HasWeatherEffect())) - || ((ability == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))) - { - PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); - gBattlerAbility = gBattleScripting.battler = battler; - gDisableStructs[battler].boosterEnergyActivated = TRUE; - gLastUsedItem = ITEM_BOOSTER_ENERGY; - RecordAbilityBattle(battler, ability); - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseID == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_BoosterEnergyEnd2); - else - BattleScriptCall(BattleScript_BoosterEnergyRet); - return ITEM_EFFECT_OTHER; - } - - return ITEM_NO_EFFECT; -} - -static u32 RestoreWhiteHerbStats(u32 battler, enum ItemCaseId caseID) -{ - u32 i, effect = 0; - - for (i = 0; i < NUM_BATTLE_STATS; i++) - { - if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) - { - gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; - effect = ITEM_STATS_CHANGE; - } - } - if (effect != 0) - { - gLastUsedItem = gBattleMons[battler].item; - gBattleScripting.battler = battler; - gPotentialItemEffectBattler = battler; - if (caseID == ITEMEFFECT_WHITE_HERB) - BattleScriptCall(BattleScript_WhiteHerbRet); - else - BattleScriptExecute(BattleScript_WhiteHerbEnd2); -} - - return effect; -} - -static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect) -{ - u8 effect = 0; - - switch (holdEffect) - { - case HOLD_EFFECT_MICLE_BERRY: - if (B_HP_BERRIES >= GEN_4) - effect = TrySetMicleBerry(battler, gLastUsedItem, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_RESTORE_HP: - if (B_HP_BERRIES >= GEN_4) - effect = ItemHealHp(battler, gLastUsedItem, ITEMEFFECT_NONE, FALSE); - break; - case HOLD_EFFECT_RESTORE_PCT_HP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = ItemHealHp(battler, gLastUsedItem, ITEMEFFECT_NONE, TRUE); - break; - case HOLD_EFFECT_RESTORE_PP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = ItemRestorePp(battler, gLastUsedItem, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CONFUSE_SPICY: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SPICY, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CONFUSE_DRY: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_DRY, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CONFUSE_SWEET: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SWEET, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CONFUSE_BITTER: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_BITTER, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CONFUSE_SOUR: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SOUR, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_ATTACK_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_ATK, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_DEFENSE_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_DEF, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_SPEED_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPEED, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_SP_ATTACK_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPATK, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_SP_DEFENSE_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move - effect = TrySetEnigmaBerry(battler); - break; - case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move - effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); - break; - case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move - effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); - break; - case HOLD_EFFECT_RANDOM_STAT_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = RandomStatRaiseBerry(battler, gLastUsedItem, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_CURE_PAR: - if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_PARALYSIS; - BattleScriptCall(BattleScript_BerryCureParRet); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_PSN: - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); - BattleScriptCall(BattleScript_BerryCurePsnRet); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_BRN: - if (gBattleMons[battler].status1 & STATUS1_BURN && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_BURN; - BattleScriptCall(BattleScript_BerryCureBrnRet); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_FRZ: - if (gBattleMons[battler].status1 & STATUS1_FREEZE && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FREEZE; - BattleScriptCall(BattleScript_BerryCureFrzRet); - effect = ITEM_STATUS_CHANGE; - } - if (gBattleMons[battler].status1 & STATUS1_FROSTBITE && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FROSTBITE; - BattleScriptCall(BattleScript_BerryCureFrbRet); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_SLP: - if (gBattleMons[battler].status1 & STATUS1_SLEEP && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_SLEEP; - gBattleMons[battler].volatiles.nightmare = FALSE; - BattleScriptCall(BattleScript_BerryCureSlpRet); - effect = ITEM_STATUS_CHANGE; - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - } - break; - case HOLD_EFFECT_CURE_CONFUSION: - if (gBattleMons[battler].volatiles.confusionTurns > 0 && !UnnerveOn(battler, gLastUsedItem)) - { - RemoveConfusionStatus(battler); - BattleScriptCall(BattleScript_BerryCureConfusionRet); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_MENTAL_HERB: - if (GetMentalHerbEffect(battler)) - { - gBattleScripting.savedBattler = gBattlerAttacker; - gBattlerAttacker = battler; - BattleScriptCall(BattleScript_MentalHerbCureRet); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_CURE_STATUS: - if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].volatiles.confusionTurns > 0) && !UnnerveOn(battler, gLastUsedItem)) - { - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY) - StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); - - if (gBattleMons[battler].status1 & STATUS1_SLEEP) - { - gBattleMons[battler].volatiles.nightmare = FALSE; - StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - } - - if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) - StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); - - if (gBattleMons[battler].status1 & STATUS1_BURN) - StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); - - if (gBattleMons[battler].status1 & STATUS1_FREEZE || gBattleMons[battler].status1 & STATUS1_FROSTBITE) - StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); - - if (gBattleMons[battler].volatiles.confusionTurns > 0) - StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); - - gBattleMons[battler].status1 = 0; - RemoveConfusionStatus(battler); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; - BattleScriptCall(BattleScript_BerryCureChosenStatusRet); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CRITICAL_UP: // lansat berry - if (B_BERRIES_INSTANT >= GEN_4 - && !(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy) - && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) - { - gBattleMons[battler].volatiles.focusEnergy = TRUE; - gBattleScripting.battler = battler; - gPotentialItemEffectBattler = battler; - BattleScriptCall(BattleScript_BerryFocusEnergyRet); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_BERSERK_GENE: - effect = ConsumeBerserkGene(battler, ITEMEFFECT_NONE); - break; - case HOLD_EFFECT_THROAT_SPRAY: - if (IsSoundMove(gCurrentMove) - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && IsBattlerAlive(gBattlerAttacker) - && IsAnyTargetAffected(gBattlerAttacker) - && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) - && !NoAliveMonsForEitherParty()) // Don't activate if battle will end - { - gLastUsedItem = gBattleMons[gBattlerAttacker].item; - gBattleScripting.battler = gBattlerAttacker; - SET_STATCHANGER(STAT_SPATK, 1, FALSE); - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_AttackerItemStatRaise); - } - break; - case HOLD_EFFECT_MIRROR_HERB: - effect = TryConsumeMirrorHerb(battler, ITEMEFFECT_NONE); - break; - default: - break; - } - - return effect; -} - -static inline bool32 TryCureStatus(u32 battler, enum ItemCaseId caseId) -{ - u32 effect = ITEM_NO_EFFECT; - u32 string = 0; - - if ((gBattleMons[battler].status1 & STATUS1_ANY || gBattleMons[battler].volatiles.confusionTurns > 0) && !UnnerveOn(battler, gLastUsedItem)) - { - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); - string++; - } - if (gBattleMons[battler].status1 & STATUS1_SLEEP) - { - gBattleMons[battler].volatiles.nightmare = FALSE; - StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); - string++; - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - } - if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); - string++; - } - if (gBattleMons[battler].status1 & STATUS1_BURN) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); - string++; - } - if (gBattleMons[battler].status1 & STATUS1_FREEZE || gBattleMons[battler].status1 & STATUS1_FROSTBITE) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); - string++; - } - if (gBattleMons[battler].volatiles.confusionTurns > 0) - { - StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); - string++; - } - if (string <= 1) - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; - else - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_NORMALIZED_STATUS; - gBattleMons[battler].status1 = 0; - RemoveConfusionStatus(battler); - if (caseId == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN || caseId == ITEMEFFECT_NORMAL) - BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); - else - BattleScriptCall(BattleScript_BerryCureChosenStatusRet); - effect = ITEM_STATUS_CHANGE; - } - - return effect; -} - -// This is hacky and could potentially be improved because ideally this function wouldn't exist but there is some inconsitency with timings. -static bool32 ShouldWaitWithActivation(u32 battler, enum ItemHoldEffect holdEffect) -{ - if (holdEffect != HOLD_EFFECT_RESTORE_HP - && holdEffect != HOLD_EFFECT_RESTORE_PCT_HP - && holdEffect != HOLD_EFFECT_RESTORE_PP - && holdEffect != HOLD_EFFECT_CONFUSE_SPICY - && holdEffect != HOLD_EFFECT_CONFUSE_DRY - && holdEffect != HOLD_EFFECT_CONFUSE_SWEET - && holdEffect != HOLD_EFFECT_CONFUSE_BITTER - && holdEffect != HOLD_EFFECT_CONFUSE_SOUR) - return FALSE; - - // Restore HP berries can be knocked off - if (gBattleStruct->battlerState[battler].itemCanBeKnockedOff) - return TRUE; - - if (IsBattlerTurnDamaged(battler) - && gMultiHitCounter == 0 // Non multi hit moves don't wait for Berserk / Anger Shell activation - && IsBattlerAlive(battler) - && !TestIfSheerForceAffected(battler, gCurrentMove) - && HadMoreThanHalfHpNowDoesnt(battler)) - { - switch (GetBattlerAbility(battler)) - { - case ABILITY_BERSERK: - return CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN); - case ABILITY_ANGER_SHELL: - return TRUE; - default: - return FALSE; - } - } - - return FALSE; -} - -u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler) -{ - u32 moveType = 0; - enum ItemEffect effect = ITEM_NO_EFFECT; - enum ItemHoldEffect battlerHoldEffect = 0, atkHoldEffect = 0; - u32 atkHoldEffectParam = 0; - u32 atkItem = 0; - - if (caseID != ITEMEFFECT_USE_LAST_ITEM) - { - gLastUsedItem = gBattleMons[battler].item; - battlerHoldEffect = GetBattlerHoldEffect(battler); - } - - atkItem = gBattleMons[gBattlerAttacker].item; - atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker); - atkHoldEffectParam = GetBattlerHoldEffectParam(gBattlerAttacker); - - switch (caseID) - { - case ITEMEFFECT_NONE: - break; - case ITEMEFFECT_ON_SWITCH_IN: - case ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN: - if (!gSpecialStatuses[battler].switchInItemDone) - { - switch (battlerHoldEffect) - { - case HOLD_EFFECT_DOUBLE_PRIZE: - if (IsOnPlayerSide(battler) && !gBattleStruct->moneyMultiplierItem) - { - gBattleStruct->moneyMultiplier *= 2; - gBattleStruct->moneyMultiplierItem = 1; - } - break; - case HOLD_EFFECT_CONFUSE_SPICY: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SPICY, caseID); - break; - case HOLD_EFFECT_CONFUSE_DRY: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_DRY, caseID); - break; - case HOLD_EFFECT_CONFUSE_SWEET: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SWEET, caseID); - break; - case HOLD_EFFECT_CONFUSE_BITTER: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_BITTER, caseID); - break; - case HOLD_EFFECT_CONFUSE_SOUR: - if (B_BERRIES_INSTANT >= GEN_4) - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SOUR, caseID); - break; - case HOLD_EFFECT_ATTACK_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_ATK, caseID); - break; - case HOLD_EFFECT_DEFENSE_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_DEF, caseID); - break; - case HOLD_EFFECT_SPEED_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPEED, caseID); - break; - case HOLD_EFFECT_SP_ATTACK_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPATK, caseID); - break; - case HOLD_EFFECT_SP_DEFENSE_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, caseID); - break; - case HOLD_EFFECT_CRITICAL_UP: - if (B_BERRIES_INSTANT >= GEN_4 - && !(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy) - && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) - { - gBattleMons[battler].volatiles.focusEnergy = TRUE; - gBattleScripting.battler = battler; - BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_RANDOM_STAT_UP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = RandomStatRaiseBerry(battler, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_CURE_PAR: - if (B_BERRIES_INSTANT >= GEN_4 - && gBattleMons[battler].status1 & STATUS1_PARALYSIS - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_PARALYSIS; - BattleScriptExecute(BattleScript_BerryCurePrlzEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_PSN: - if (B_BERRIES_INSTANT >= GEN_4 - && (gBattleMons[battler].status1 & STATUS1_PSN_ANY) - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); - BattleScriptExecute(BattleScript_BerryCurePsnEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_BRN: - if (B_BERRIES_INSTANT >= GEN_4 - && (gBattleMons[battler].status1 & STATUS1_BURN) - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_BURN; - BattleScriptExecute(BattleScript_BerryCureBrnEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_FRZ: - if (B_BERRIES_INSTANT >= GEN_4 - && (gBattleMons[battler].status1 & STATUS1_FREEZE) - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FREEZE; - BattleScriptExecute(BattleScript_BerryCureFrzEnd2); - effect = ITEM_STATUS_CHANGE; - } - if (B_BERRIES_INSTANT >= GEN_4 - && (gBattleMons[battler].status1 & STATUS1_FROSTBITE) - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FROSTBITE; - BattleScriptExecute(BattleScript_BerryCureFrbEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_SLP: - if (B_BERRIES_INSTANT >= GEN_4 - && (gBattleMons[battler].status1 & STATUS1_SLEEP) - && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_SLEEP; - gBattleMons[battler].volatiles.nightmare = FALSE; - BattleScriptExecute(BattleScript_BerryCureSlpEnd2); - effect = ITEM_STATUS_CHANGE; - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - } - break; - case HOLD_EFFECT_CURE_STATUS: - if (B_BERRIES_INSTANT >= GEN_4) - effect = TryCureStatus(battler, caseID); - break; - case HOLD_EFFECT_RESTORE_HP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = ItemHealHp(battler, gLastUsedItem, caseID, FALSE); - break; - case HOLD_EFFECT_RESTORE_PCT_HP: - if (B_BERRIES_INSTANT >= GEN_4) - effect = ItemHealHp(battler, gLastUsedItem, caseID, TRUE); - break; - case HOLD_EFFECT_RESTORE_PP: - effect = ItemRestorePp(battler, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_AIR_BALLOON: - effect = ITEM_EFFECT_OTHER; - gBattleScripting.battler = battler; - BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn); - RecordItemEffectBattle(battler, HOLD_EFFECT_AIR_BALLOON); - break; - case HOLD_EFFECT_ROOM_SERVICE: - if (TryRoomService(battler)) - { - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - effect = ITEM_STATS_CHANGE; - } - break; - case HOLD_EFFECT_SEEDS: - switch (GetBattlerHoldEffectParam(battler)) - { - case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_PARAM_MISTY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, gLastUsedItem, caseID); - break; - } - break; - case HOLD_EFFECT_BERSERK_GENE: - effect = ConsumeBerserkGene(battler, caseID); - break; - case HOLD_EFFECT_BOOSTER_ENERGY: - effect = TryBoosterEnergy(battler, GetBattlerAbility(battler), caseID); - break; - default: - break; - } - if (effect != 0) - { - gSpecialStatuses[battler].switchInItemDone = TRUE; - gBattlerAttacker = gPotentialItemEffectBattler = gBattleScripting.battler = battler; - if (effect == ITEM_STATUS_CHANGE) - { - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - } - } - break; - case ITEMEFFECT_LEFTOVERS: - switch (battlerHoldEffect) - { - case HOLD_EFFECT_BLACK_SLUDGE: - if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) - { - goto LEFTOVERS; - } - else if (!IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD)) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_ItemHurtEnd2); - effect = ITEM_HP_CHANGE; - RecordItemEffectBattle(battler, battlerHoldEffect); - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - } - break; - case HOLD_EFFECT_LEFTOVERS: - LEFTOVERS: - if (gBattleMons[battler].hp < gBattleMons[battler].maxHP - && (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock)) - { - gBattleScripting.battler = battler; - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - gBattleStruct->moveDamage[battler] *= -1; - BattleScriptExecute(BattleScript_ItemHealHP_End2); - effect = ITEM_HP_CHANGE; - RecordItemEffectBattle(battler, battlerHoldEffect); - } - break; - default: - break; - } - break; - case ITEMEFFECT_NORMAL: - case ITEMEFFECT_TRY_HEALING: - if (IsBattlerAlive(battler)) - { - switch (battlerHoldEffect) - { - case HOLD_EFFECT_RESTORE_HP: - effect = ItemHealHp(battler, gLastUsedItem, caseID, FALSE); - break; - case HOLD_EFFECT_RESTORE_PCT_HP: - effect = ItemHealHp(battler, gLastUsedItem, caseID, TRUE); - break; - case HOLD_EFFECT_RESTORE_PP: - effect = ItemRestorePp(battler, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_CONFUSE_SPICY: - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SPICY, caseID); - break; - case HOLD_EFFECT_CONFUSE_DRY: - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_DRY, caseID); - break; - case HOLD_EFFECT_CONFUSE_SWEET: - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SWEET, caseID); - break; - case HOLD_EFFECT_CONFUSE_BITTER: - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_BITTER, caseID); - break; - case HOLD_EFFECT_CONFUSE_SOUR: - effect = HealConfuseBerry(battler, gLastUsedItem, FLAVOR_SOUR, caseID); - break; - case HOLD_EFFECT_ATTACK_UP: - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_ATK, caseID); - break; - case HOLD_EFFECT_DEFENSE_UP: - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_DEF, caseID); - break; - case HOLD_EFFECT_SPEED_UP: - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPEED, caseID); - break; - case HOLD_EFFECT_SP_ATTACK_UP: - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPATK, caseID); - break; - case HOLD_EFFECT_SP_DEFENSE_UP: - effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, caseID); - break; - case HOLD_EFFECT_CRITICAL_UP: - if (!(gBattleMons[battler].volatiles.dragonCheer || gBattleMons[battler].volatiles.focusEnergy) - && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) - { - gBattleMons[battler].volatiles.focusEnergy = TRUE; - gBattleScripting.battler = battler; - BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_RANDOM_STAT_UP: - effect = RandomStatRaiseBerry(battler, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_CURE_PAR: - if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_PARALYSIS; - BattleScriptExecute(BattleScript_BerryCurePrlzEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_PSN: - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); - BattleScriptExecute(BattleScript_BerryCurePsnEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_BRN: - if (gBattleMons[battler].status1 & STATUS1_BURN && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_BURN; - BattleScriptExecute(BattleScript_BerryCureBrnEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_FRZ: - if (gBattleMons[battler].status1 & STATUS1_FREEZE && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FREEZE; - BattleScriptExecute(BattleScript_BerryCureFrzEnd2); - effect = ITEM_STATUS_CHANGE; - } - if (gBattleMons[battler].status1 & STATUS1_FROSTBITE && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_FROSTBITE; - BattleScriptExecute(BattleScript_BerryCureFrbEnd2); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_CURE_SLP: - if (gBattleMons[battler].status1 & STATUS1_SLEEP && !UnnerveOn(battler, gLastUsedItem)) - { - gBattleMons[battler].status1 &= ~STATUS1_SLEEP; - gBattleMons[battler].volatiles.nightmare = FALSE; - BattleScriptExecute(BattleScript_BerryCureSlpEnd2); - effect = ITEM_STATUS_CHANGE; - TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); - } - break; - case HOLD_EFFECT_CURE_CONFUSION: - if (gBattleMons[battler].volatiles.confusionTurns > 0 && !UnnerveOn(battler, gLastUsedItem)) - { - RemoveConfusionStatus(battler); - BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_CURE_STATUS: - effect = TryCureStatus(battler, caseID); - break; - case HOLD_EFFECT_MENTAL_HERB: - if (GetMentalHerbEffect(battler)) - { - gBattleScripting.savedBattler = gBattlerAttacker; - gBattlerAttacker = battler; - BattleScriptExecute(BattleScript_MentalHerbCureEnd2); - effect = ITEM_EFFECT_OTHER; - } - break; - case HOLD_EFFECT_MICLE_BERRY: - effect = TrySetMicleBerry(battler, gLastUsedItem, caseID); - break; - case HOLD_EFFECT_BERSERK_GENE: - effect = ConsumeBerserkGene(battler, caseID); - break; - default: - break; - } - - if (effect != 0) - { - gBattlerAttacker = gPotentialItemEffectBattler = gBattleScripting.battler = battler; - if (effect == ITEM_STATUS_CHANGE) - { - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - } - } - break; - case ITEMEFFECT_USE_LAST_ITEM: - effect = ItemEffectMoveEnd(battler, GetItemHoldEffect(gLastUsedItem)); - gBattleScripting.overrideBerryRequirements = 2; // to exit VARIOUS_CONSUME_BERRIES - if (effect) - { - gPotentialItemEffectBattler = gBattleScripting.battler = battler; - if (effect == ITEM_STATUS_CHANGE) - { - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - break; - } - break; - case ITEMEFFECT_MOVE_END: - for (battler = 0; battler < gBattlersCount; battler++) - { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); - if (ShouldWaitWithActivation(battler, holdEffect)) - continue; - - gLastUsedItem = gBattleMons[battler].item; - effect = ItemEffectMoveEnd(battler, holdEffect); - if (effect) - { - gPotentialItemEffectBattler = gBattleScripting.battler = battler; - if (effect == ITEM_STATUS_CHANGE) - { - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - break; - } - } - break; - case ITEMEFFECT_KINGSROCK: - // Occur on each hit of a multi-strike move - switch (atkHoldEffect) - { - case HOLD_EFFECT_FLINCH: - { - enum Ability ability = GetBattlerAbility(gBattlerAttacker); - if (B_SERENE_GRACE_BOOST >= GEN_5 && ability == ABILITY_SERENE_GRACE) - atkHoldEffectParam *= 2; - if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_RAINBOW && gCurrentMove != MOVE_SECRET_POWER) - atkHoldEffectParam *= 2; - if (IsBattlerTurnDamaged(gBattlerTarget) - && !MoveIgnoresKingsRock(gCurrentMove) - && gBattleMons[gBattlerTarget].hp - && RandomPercentage(RNG_HOLD_EFFECT_FLINCH, atkHoldEffectParam) - && ability != ABILITY_STENCH) - { - gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; - BattleScriptPushCursor(); - SetMoveEffect(gBattlerAttacker, gBattlerTarget, FALSE, FALSE); - BattleScriptPop(); - } - } - break; - case HOLD_EFFECT_BLUNDER_POLICY: - if (gBattleStruct->blunderPolicy - && IsBattlerAlive(gBattlerAttacker) - && CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) - { - gBattleStruct->blunderPolicy = FALSE; - gLastUsedItem = atkItem; - SET_STATCHANGER(STAT_SPEED, 2, FALSE); - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_AttackerItemStatRaise); - } - break; - default: - break; - } - break; - case ITEMEFFECT_LIFEORB_SHELLBELL: - // Occur after the final hit of a multi-strike move - switch (atkHoldEffect) - { - case HOLD_EFFECT_SHELL_BELL: - if (gBattleScripting.savedDmg > 0 - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) - && gBattlerAttacker != gBattlerTarget - && !IsBattlerAtMaxHp(gBattlerAttacker) - && IsBattlerAlive(gBattlerAttacker) - && GetMoveEffect(gCurrentMove) != EFFECT_FUTURE_SIGHT - && GetMoveEffect(gCurrentMove) != EFFECT_PAIN_SPLIT - && (B_HEAL_BLOCKING < GEN_5 || !gBattleMons[battler].volatiles.healBlock)) - { - gLastUsedItem = atkItem; - gPotentialItemEffectBattler = gBattlerAttacker; - gBattleScripting.battler = gBattlerAttacker; - gBattleStruct->moveDamage[gBattlerAttacker] = (gBattleScripting.savedDmg / atkHoldEffectParam) * -1; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = -1; - BattleScriptCall(BattleScript_ItemHealHP_Ret); - effect = ITEM_HP_CHANGE; - } - break; - case HOLD_EFFECT_LIFE_ORB: - if (IsBattlerAlive(gBattlerAttacker) - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleScripting.savedDmg > 0) - && !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD) - && !IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget, gCurrentMove)) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 10; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - effect = ITEM_HP_CHANGE; - BattleScriptCall(BattleScript_ItemHurtRet); - gLastUsedItem = atkItem; - } - break; - default: - break; - } - break; - case ITEMEFFECT_TARGET: - if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) - { - moveType = GetBattleMoveType(gCurrentMove); - switch (battlerHoldEffect) - { - case HOLD_EFFECT_AIR_BALLOON: - if (IsBattlerTurnDamaged(gBattlerTarget)) - { - effect = ITEM_EFFECT_OTHER; - BattleScriptCall(BattleScript_AirBaloonMsgPop); - } - break; - case HOLD_EFFECT_ROCKY_HELMET: - if (IsBattlerTurnDamaged(gBattlerTarget) - && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove) - && IsBattlerAlive(gBattlerAttacker) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 6; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - effect = ITEM_HP_CHANGE; - BattleScriptCall(BattleScript_RockyHelmetActivates); - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - RecordItemEffectBattle(battler, HOLD_EFFECT_ROCKY_HELMET); - } - break; - case HOLD_EFFECT_WEAKNESS_POLICY: - if (IsBattlerAlive(battler) - && IsBattlerTurnDamaged(gBattlerTarget) - && gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_SUPER_EFFECTIVE) - { - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_WeaknessPolicy); - } - break; - case HOLD_EFFECT_SNOWBALL: - if (IsBattlerAlive(battler) - && IsBattlerTurnDamaged(gBattlerTarget) - && moveType == TYPE_ICE) - { - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_TargetItemStatRaise); - SET_STATCHANGER(STAT_ATK, 1, FALSE); - } - break; - case HOLD_EFFECT_LUMINOUS_MOSS: - if (IsBattlerAlive(battler) - && IsBattlerTurnDamaged(gBattlerTarget) - && moveType == TYPE_WATER) - { - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_TargetItemStatRaise); - SET_STATCHANGER(STAT_SPDEF, 1, FALSE); - } - break; - case HOLD_EFFECT_CELL_BATTERY: - if (IsBattlerAlive(battler) - && IsBattlerTurnDamaged(gBattlerTarget) - && moveType == TYPE_ELECTRIC) - { - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_TargetItemStatRaise); - SET_STATCHANGER(STAT_ATK, 1, FALSE); - } - break; - case HOLD_EFFECT_ABSORB_BULB: - if (IsBattlerAlive(battler) - && IsBattlerTurnDamaged(gBattlerTarget) - && moveType == TYPE_WATER) - { - effect = ITEM_STATS_CHANGE; - BattleScriptCall(BattleScript_TargetItemStatRaise); - SET_STATCHANGER(STAT_SPATK, 1, FALSE); - } - break; - case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move - effect = TrySetEnigmaBerry(battler); - break; - case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move - if (IsBattlerAlive(gBattlerAttacker) - && IsBattlerTurnDamaged(gBattlerTarget) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && IsBattleMovePhysical(gCurrentMove) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 8; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - gBattleStruct->moveDamage[gBattlerAttacker] *= 2; - - effect = ITEM_HP_CHANGE; - BattleScriptCall(BattleScript_JabocaRowapBerryActivates); - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - RecordItemEffectBattle(battler, HOLD_EFFECT_JABOCA_BERRY); - } - break; - case HOLD_EFFECT_ROWAP_BERRY: // consume and damage attacker if used special move - if (IsBattlerAlive(gBattlerAttacker) - && IsBattlerTurnDamaged(gBattlerTarget) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && IsBattleMoveSpecial(gCurrentMove) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 8; - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) - gBattleStruct->moveDamage[gBattlerAttacker] *= 2; - - effect = ITEM_HP_CHANGE; - BattleScriptCall(BattleScript_JabocaRowapBerryActivates); - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - RecordItemEffectBattle(battler, HOLD_EFFECT_ROWAP_BERRY); - } - break; - case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move - effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); - break; - case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move - effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); - break; - case HOLD_EFFECT_CURE_STATUS: // only Toxic Chain's interaction with Knock Off - case HOLD_EFFECT_CURE_PSN: - if (gBattleMons[battler].status1 & STATUS1_PSN_ANY && !UnnerveOn(battler, gLastUsedItem) && GetBattlerAbility(gBattlerAttacker) == ABILITY_TOXIC_CHAIN && GetMoveEffect(gCurrentMove) == EFFECT_KNOCK_OFF) - { - gBattleScripting.battler = battler; - gBattleMons[battler].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); - BattleScriptExecute(BattleScript_BerryCurePsnEnd2); - BtlController_EmitSetMonData(battler, 0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - effect = ITEM_STATUS_CHANGE; - } - break; - case HOLD_EFFECT_STICKY_BARB: - if (IsBattlerTurnDamaged(gBattlerTarget) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) - && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && IsBattlerAlive(gBattlerAttacker) - && CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item) - && gBattleMons[gBattlerAttacker].item == ITEM_NONE) - { - // No sticky hold checks. - gEffectBattler = battler; // gEffectBattler = target - StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker takes target's barb - BattleScriptCall(BattleScript_StickyBarbTransfer); - effect = ITEM_EFFECT_OTHER; - } - break; - default: - break; - } - } - break; - case ITEMEFFECT_ORBS: - { - enum Ability battlerAbility = GetBattlerAbility(battler); - switch (battlerHoldEffect) - { - case HOLD_EFFECT_TOXIC_ORB: - if (CanBePoisoned(battler, battler, battlerAbility, battlerAbility)) // Can corrosion trigger toxic orb on itself? - { - effect = ITEM_STATUS_CHANGE; - gBattleMons[battler].status1 = STATUS1_TOXIC_POISON; - BattleScriptExecute(BattleScript_ToxicOrb); - RecordItemEffectBattle(battler, battlerHoldEffect); - } - break; - case HOLD_EFFECT_FLAME_ORB: - if (CanBeBurned(battler, battler, battlerAbility)) - { - effect = ITEM_STATUS_CHANGE; - gBattleMons[battler].status1 = STATUS1_BURN; - BattleScriptExecute(BattleScript_FlameOrb); - RecordItemEffectBattle(battler, battlerHoldEffect); - } - break; - case HOLD_EFFECT_STICKY_BARB: // Not an orb per se, but similar effect, and needs to NOT activate with pickpocket - if (battlerAbility != ABILITY_MAGIC_GUARD) - { - gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; - if (gBattleStruct->moveDamage[battler] == 0) - gBattleStruct->moveDamage[battler] = 1; - BattleScriptExecute(BattleScript_ItemHurtEnd2); - effect = ITEM_HP_CHANGE; - RecordItemEffectBattle(battler, battlerHoldEffect); - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); - } - break; - default: - break; - } - - if (effect == ITEM_STATUS_CHANGE) - { - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - } - break; - case ITEMEFFECT_WHITE_HERB: - case ITEMEFFECT_WHITE_HERB_ENDTURN: - case ITEMEFFECT_WHITE_HERB_FIRST_TURN: - if (battlerHoldEffect == HOLD_EFFECT_WHITE_HERB) - effect = RestoreWhiteHerbStats(battler, caseID); - break; - case ITEMEFFECT_MIRROR_HERB: - case ITEMEFFECT_MIRROR_HERB_FIRST_TURN: - if (battlerHoldEffect == HOLD_EFFECT_MIRROR_HERB) - effect = TryConsumeMirrorHerb(battler, caseID); - break; - } - - // Berry was successfully used on a Pokemon. - if (effect && (gLastUsedItem >= FIRST_BERRY_INDEX && gLastUsedItem <= LAST_BERRY_INDEX)) - GetBattlerPartyState(battler)->ateBerry = TRUE; - - return effect; -} - void ClearVariousBattlerFlags(u32 battler) { gDisableStructs[battler].furyCutterCounter = 0; @@ -7793,17 +6369,17 @@ u8 GetAttackerObedienceForAction() } } -enum ItemHoldEffect GetBattlerHoldEffect(u32 battler) +enum HoldEffect GetBattlerHoldEffect(u32 battler) { return GetBattlerHoldEffectInternal(battler, GetBattlerAbility(battler)); } -enum ItemHoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler) +enum HoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler) { return GetBattlerHoldEffectInternal(battler, ABILITY_NONE); } -enum ItemHoldEffect GetBattlerHoldEffectInternal(u32 battler, u32 ability) +enum HoldEffect GetBattlerHoldEffectInternal(u32 battler, u32 ability) { if (gBattleMons[battler].volatiles.embargo) return HOLD_EFFECT_NONE; @@ -7820,7 +6396,7 @@ enum ItemHoldEffect GetBattlerHoldEffectInternal(u32 battler, u32 ability) return GetItemHoldEffect(gBattleMons[battler].item); } -enum ItemHoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler) +enum HoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler) { gPotentialItemEffectBattler = battler; if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) @@ -7829,14 +6405,6 @@ enum ItemHoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler) return GetItemHoldEffect(gBattleMons[battler].item); } -static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item) -{ - if (item == ITEM_ENIGMA_BERRY_E_READER) - return gEnigmaBerries[battler].holdEffectParam; - else - return GetItemHoldEffectParam(item); -} - u32 GetBattlerHoldEffectParam(u32 battler) { if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) @@ -7845,7 +6413,7 @@ u32 GetBattlerHoldEffectParam(u32 battler) return GetItemHoldEffectParam(gBattleMons[battler].item); } -bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum ItemHoldEffect holdEffectAtk, u32 move) +bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 move) { if (holdEffectAtk == HOLD_EFFECT_PROTECTIVE_PADS) { @@ -7856,7 +6424,7 @@ bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Abilit return !IsMoveMakingContact(battlerAtk, battlerDef, abilityAtk, holdEffectAtk, move); } -bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum ItemHoldEffect holdEffectAtk, u32 move) +bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 move) { if (!(MoveMakesContact(move) || (GetMoveEffect(move) == EFFECT_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL))) @@ -7974,7 +6542,7 @@ enum IronBallCheck }; // Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics -bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect, enum InverseBattleCheck checkInverse) +bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum InverseBattleCheck checkInverse) { if (holdEffect == HOLD_EFFECT_IRON_BALL) return TRUE; @@ -7997,7 +6565,7 @@ bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum Ite return TRUE; } -bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum ItemHoldEffect holdEffect) +bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect) { return IsBattlerGroundedInverseCheck(battler, ability, holdEffect, NOT_INVERSE_BATTLE); } @@ -8019,7 +6587,7 @@ u32 GetBattlerWeight(u32 battler) u32 i; u32 weight = GetSpeciesWeight(gBattleMons[battler].species); enum Ability ability = GetBattlerAbility(battler); - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (ability == ABILITY_HEAVY_METAL) weight *= 2; @@ -9560,7 +8128,7 @@ static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef return UQ_4_12(1.0); } -static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, enum ItemHoldEffect holdEffectAtk) +static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, enum HoldEffect holdEffectAtk) { u32 metronomeTurns; uq4_12_t metronomeBoostBase; @@ -9591,7 +8159,7 @@ static inline uq4_12_t GetDefenderItemsModifier(struct DamageContext *ctx) switch (ctx->holdEffectDef) { case HOLD_EFFECT_RESIST_BERRY: - if (UnnerveOn(ctx->battlerDef, gBattleMons[ctx->battlerDef].item)) + if (IsUnnerveBlocked(ctx->battlerDef, gBattleMons[ctx->battlerDef].item)) return UQ_4_12(1.0); if (ctx->moveType == GetBattlerHoldEffectParam(ctx->battlerDef) && (ctx->moveType == TYPE_NORMAL || ctx->typeEffectivenessModifier >= UQ_4_12(2.0))) { @@ -10233,7 +8801,7 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) bool32 CanMegaEvolve(u32 battler) { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); // Check if Player has a Mega Ring. if (!TESTING @@ -10271,7 +8839,7 @@ bool32 CanMegaEvolve(u32 battler) bool32 CanUltraBurst(u32 battler) { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); // Check if Player has a Z-Ring if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT @@ -10559,7 +9127,7 @@ bool32 DoBattlersShareType(u32 battler1, u32 battler2) bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId) { u16 species = gBattleMons[battler].species; - enum ItemHoldEffect holdEffect = GetItemHoldEffect(itemId); + enum HoldEffect holdEffect = GetItemHoldEffect(itemId); if (ItemIsMail(itemId)) return FALSE; @@ -10887,31 +9455,6 @@ static bool32 TryRemoveScreens(u32 battler) return removed; } -static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) -{ - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) - { - if (battler == battlerDef || IsBattlerAlly(battler, battlerDef)) - continue; - - if (!IsBattlerAlive(battlerDef)) - continue; - - enum Ability ability = GetBattlerAbility(battlerDef); - switch (ability) - { - case ABILITY_UNNERVE: - case ABILITY_AS_ONE_ICE_RIDER: - case ABILITY_AS_ONE_SHADOW_RIDER: - return TRUE; - default: - break; - } - } - - return FALSE; -} - // Photon Geyser, Light That Burns the Sky, Tera Blast enum DamageCategory GetCategoryBasedOnStats(u32 battler) { @@ -11080,7 +9623,7 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem) bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) { bool32 ret = TRUE; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) { ret = FALSE; @@ -11151,23 +9694,6 @@ bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind) return ret; } -bool32 TryRoomService(u32 battler) -{ - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) - { - gEffectBattler = gBattleScripting.battler = battler; - SET_STATCHANGER(STAT_SPEED, 1, TRUE); - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_SPEED; - gBattleScripting.animArg2 = 0; - gLastUsedItem = gBattleMons[battler].item; - return TRUE; - } - else - { - return FALSE; - } -} - bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget) { if (GetGenConfig(GEN_CONFIG_PRANKSTER_DARK_TYPES) < GEN_7) @@ -11315,16 +9841,6 @@ void RemoveConfusionStatus(u32 battler) gBattleMons[battler].volatiles.infiniteConfusion = FALSE; } -static bool32 CanBeInfinitelyConfused(u32 battler) -{ - enum Ability ability = GetBattlerAbility(battler); - if (ability == ABILITY_OWN_TEMPO - || IsBattlerTerrainAffected(battler, ability, GetBattlerHoldEffect(battler), STATUS_FIELD_MISTY_TERRAIN) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) - return FALSE; - return TRUE; -} - u8 GetBattlerGender(u32 battler) { return GetGenderFromSpeciesAndPersonality(gBattleMons[battler].species, @@ -11760,19 +10276,6 @@ bool32 HasWeatherEffect(void) return TRUE; } -bool32 IsAnyTargetAffected(u32 battlerAtk) -{ - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) - { - if (battlerAtk == battlerDef) - continue; - - if (!(gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT)) - return TRUE; - } - return FALSE; -} - void UpdateStallMons(void) { if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS) @@ -11798,18 +10301,7 @@ void UpdateStallMons(void) // Handling for moves that target multiple opponents in doubles not handled currently } -bool32 TryRestoreHPBerries(u32 battler, enum ItemCaseId caseId) -{ - if (gItemsInfo[gBattleMons[battler].item].pocket == POCKET_BERRIES - || GetBattlerHoldEffect(battler) == HOLD_EFFECT_RESTORE_HP) // Edge case for Berry Juice - { - if (ItemBattleEffects(caseId, battler)) - return TRUE; - } - return FALSE; -} - -bool32 TrySwitchInEjectPack(enum ItemCaseId caseID) +bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) { // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. u32 ejectPackBattlers = 0; @@ -11846,9 +10338,9 @@ bool32 TrySwitchInEjectPack(enum ItemCaseId caseID) gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; - if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) + if (timing == FIRST_TURN) BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3); - else if (caseID == ITEMEFFECT_NORMAL) + else if (timing == END_TURN) BattleScriptExecute(BattleScript_EjectPackActivate_End2); else BattleScriptCall(BattleScript_EjectPackActivate_Ret); @@ -12189,6 +10681,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, enum Ability atkA if (gBattleStruct->battlerState[battlerAtk].usedMicleBerry) { + // TODO: Is this true? if (atkAbility == ABILITY_RIPEN) calc = (calc * 140) / 100; // ripen gives 40% acc boost else @@ -12437,6 +10930,18 @@ void RemoveAbilityFlags(u32 battler) } } +bool32 IsAnyTargetTurnDamaged(u32 battlerAtk) +{ + for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + { + if (battlerDef == battlerAtk) + continue; + if (IsBattlerTurnDamaged(battlerDef)) + return TRUE; + } + return FALSE; +} + bool32 IsAllowedToUseBag(void) { switch(VarGet(B_VAR_NO_BAG_USE)) diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 78d4ddc60f..196d462d62 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -36,7 +36,6 @@ #include "constants/songs.h" #include "constants/items.h" #include "constants/species.h" -#include "constants/hold_effects.h" #include "constants/battle_string_ids.h" #include "constants/battle_move_effects.h" #include "constants/abilities.h" @@ -113,7 +112,7 @@ bool32 IsZMove(u32 move) bool32 CanUseZMove(u32 battler) { - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); // Check if Player has Z-Power Ring. if (!TESTING && (battler == B_POSITION_PLAYER_LEFT @@ -144,7 +143,7 @@ bool32 CanUseZMove(u32 battler) u32 GetUsableZMove(u32 battler, u32 move) { u32 item = gBattleMons[battler].item; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) { @@ -167,7 +166,7 @@ void ActivateZMove(u32 battler) bool32 IsViableZMove(u32 battler, u32 move) { u32 item; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); int moveSlotIndex; item = gBattleMons[battler].item; diff --git a/src/data/hold_effects.h b/src/data/hold_effects.h new file mode 100644 index 0000000000..f56b6811f9 --- /dev/null +++ b/src/data/hold_effects.h @@ -0,0 +1,647 @@ +const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = +{ + [HOLD_EFFECT_NONE] = + { + }, + + [HOLD_EFFECT_RESTORE_HP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CURE_PAR] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CURE_SLP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CURE_PSN] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CURE_BRN] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CURE_FRZ] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_RESTORE_PP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onAttackerAfterHit = TRUE, + }, + + [HOLD_EFFECT_CURE_CONFUSION] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CURE_STATUS] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onStatusChange = TRUE, + }, + + [HOLD_EFFECT_CONFUSE_SPICY] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CONFUSE_DRY] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CONFUSE_SWEET] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CONFUSE_BITTER] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CONFUSE_SOUR] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_ATTACK_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_DEFENSE_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_SPEED_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_SP_ATTACK_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_SP_DEFENSE_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CRITICAL_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_RANDOM_STAT_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_EVASION_UP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_WHITE_HERB] = + { + .whiteHerb = TRUE, + .whiteHerbFirstTurn = TRUE, + .whiteHerbEndTurn = TRUE, + }, + + [HOLD_EFFECT_MACHO_BRACE] = + { + }, + + [HOLD_EFFECT_EXP_SHARE] = + { + }, + + [HOLD_EFFECT_QUICK_CLAW] = + { + }, + + [HOLD_EFFECT_FRIENDSHIP_UP] = + { + }, + + [HOLD_EFFECT_MENTAL_HERB] = + { + .onTargetAfterHit = TRUE, + .onAttackerAfterHit = TRUE, + }, + + [HOLD_EFFECT_CHOICE_BAND] = + { + }, + + [HOLD_EFFECT_FLINCH] = + { + .onAttackerAfterHit = TRUE, + }, + + [HOLD_EFFECT_DOUBLE_PRIZE] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + }, + + [HOLD_EFFECT_REPEL] = + { + }, + + [HOLD_EFFECT_SOUL_DEW] = + { + }, + + [HOLD_EFFECT_DEEP_SEA_TOOTH] = + { + }, + + [HOLD_EFFECT_DEEP_SEA_SCALE] = + { + }, + + [HOLD_EFFECT_CAN_ALWAYS_RUN] = + { + }, + + [HOLD_EFFECT_PREVENT_EVOLVE] = + { + }, + + [HOLD_EFFECT_FOCUS_BAND] = + { + }, + + [HOLD_EFFECT_LUCKY_EGG] = + { + }, + + [HOLD_EFFECT_SCOPE_LENS] = + { + }, + + [HOLD_EFFECT_LEFTOVERS] = + { + .leftovers = TRUE, + }, + + [HOLD_EFFECT_DRAGON_SCALE] = + { + }, + + [HOLD_EFFECT_LIGHT_BALL] = + { + }, + + [HOLD_EFFECT_TYPE_POWER] = + { + }, + + [HOLD_EFFECT_UPGRADE] = + { + }, + + [HOLD_EFFECT_SHELL_BELL] = + { + .lifeOrbShellBell = TRUE, + }, + + [HOLD_EFFECT_LUCKY_PUNCH] = + { + }, + + [HOLD_EFFECT_METAL_POWDER] = + { + }, + + [HOLD_EFFECT_THICK_CLUB] = + { + }, + + [HOLD_EFFECT_LEEK] = + { + }, + + [HOLD_EFFECT_CHOICE_SCARF] = + { + }, + + [HOLD_EFFECT_CHOICE_SPECS] = + { + }, + + [HOLD_EFFECT_DAMP_ROCK] = + { + }, + + [HOLD_EFFECT_GRIP_CLAW] = + { + }, + + [HOLD_EFFECT_HEAT_ROCK] = + { + }, + + [HOLD_EFFECT_ICY_ROCK] = + { + }, + + [HOLD_EFFECT_LIGHT_CLAY] = + { + }, + + [HOLD_EFFECT_SMOOTH_ROCK] = + { + }, + + [HOLD_EFFECT_POWER_HERB] = + { + }, + + [HOLD_EFFECT_BIG_ROOT] = + { + }, + + [HOLD_EFFECT_EXPERT_BELT] = + { + }, + + [HOLD_EFFECT_LIFE_ORB] = + { + .lifeOrbShellBell = TRUE, + }, + + [HOLD_EFFECT_METRONOME] = + { + }, + + [HOLD_EFFECT_MUSCLE_BAND] = + { + }, + + [HOLD_EFFECT_WIDE_LENS] = + { + }, + + [HOLD_EFFECT_WISE_GLASSES] = + { + }, + + [HOLD_EFFECT_ZOOM_LENS] = + { + }, + + [HOLD_EFFECT_LAGGING_TAIL] = + { + }, + + [HOLD_EFFECT_FOCUS_SASH] = + { + }, + + [HOLD_EFFECT_FLAME_ORB] = + { + .orbs = TRUE, + }, + + [HOLD_EFFECT_TOXIC_ORB] = + { + .orbs = TRUE, + }, + + [HOLD_EFFECT_STICKY_BARB] = + { + .onTargetAfterHit = TRUE, + .orbs = TRUE, + }, + + [HOLD_EFFECT_IRON_BALL] = + { + }, + + [HOLD_EFFECT_BLACK_SLUDGE] = + { + .leftovers = TRUE, + }, + + [HOLD_EFFECT_DESTINY_KNOT] = + { + }, + + [HOLD_EFFECT_SHED_SHELL] = + { + }, + + [HOLD_EFFECT_QUICK_POWDER] = + { + }, + + [HOLD_EFFECT_ADAMANT_ORB] = + { + }, + + [HOLD_EFFECT_LUSTROUS_ORB] = + { + }, + + [HOLD_EFFECT_GRISEOUS_ORB] = + { + }, + + [HOLD_EFFECT_ENIGMA_BERRY] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_RESIST_BERRY] = + { + }, + + [HOLD_EFFECT_POWER_ITEM] = + { + }, + + [HOLD_EFFECT_RESTORE_PCT_HP] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_MICLE_BERRY] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onHpThreshold = TRUE, + }, + + [HOLD_EFFECT_CUSTAP_BERRY] = + { + }, + + [HOLD_EFFECT_JABOCA_BERRY] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_ROWAP_BERRY] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_KEE_BERRY] = + { + .keeMarangaBerry = TRUE, + }, + + [HOLD_EFFECT_MARANGA_BERRY] = + { + .keeMarangaBerry = TRUE, + }, + + [HOLD_EFFECT_PLATE] = + { + }, + + [HOLD_EFFECT_FLOAT_STONE] = + { + }, + + [HOLD_EFFECT_EVIOLITE] = + { + }, + + [HOLD_EFFECT_ASSAULT_VEST] = + { + }, + + [HOLD_EFFECT_DRIVE] = + { + }, + + [HOLD_EFFECT_GEMS] = + { + }, + + [HOLD_EFFECT_ROCKY_HELMET] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_AIR_BALLOON] = + { + .onTargetAfterHit = TRUE, + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + }, + + [HOLD_EFFECT_RED_CARD] = + { + }, + + [HOLD_EFFECT_RING_TARGET] = + { + }, + + [HOLD_EFFECT_BINDING_BAND] = + { + }, + + [HOLD_EFFECT_EJECT_BUTTON] = + { + }, + + [HOLD_EFFECT_ABSORB_BULB] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_CELL_BATTERY] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_MEGA_STONE] = + { + }, + + [HOLD_EFFECT_SAFETY_GOGGLES] = + { + }, + + [HOLD_EFFECT_LUMINOUS_MOSS] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_SNOWBALL] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_WEAKNESS_POLICY] = + { + .onTargetAfterHit = TRUE, + }, + + [HOLD_EFFECT_PRIMAL_ORB] = + { + }, + + [HOLD_EFFECT_PROTECTIVE_PADS] = + { + }, + + [HOLD_EFFECT_TERRAIN_EXTENDER] = + { + }, + + [HOLD_EFFECT_TERRAIN_SEED] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onEffect = TRUE, + }, + + [HOLD_EFFECT_ADRENALINE_ORB] = + { + }, + + [HOLD_EFFECT_MEMORY] = + { + }, + + [HOLD_EFFECT_Z_CRYSTAL] = + { + }, + + [HOLD_EFFECT_UTILITY_UMBRELLA] = + { + }, + + [HOLD_EFFECT_EJECT_PACK] = + { + }, + + [HOLD_EFFECT_ROOM_SERVICE] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onEffect = TRUE, + }, + + [HOLD_EFFECT_BLUNDER_POLICY] = + { + .onAttackerAfterHit = TRUE, + }, + + [HOLD_EFFECT_HEAVY_DUTY_BOOTS] = + { + }, + + [HOLD_EFFECT_THROAT_SPRAY] = + { + .onAttackerAfterHit = TRUE, + }, + + [HOLD_EFFECT_ABILITY_SHIELD] = + { + }, + + [HOLD_EFFECT_CLEAR_AMULET] = + { + }, + + [HOLD_EFFECT_MIRROR_HERB] = + { + .mirrorHerb = TRUE, + .mirrorHerbFirstTurn = TRUE, + }, + + [HOLD_EFFECT_PUNCHING_GLOVE] = + { + }, + + [HOLD_EFFECT_COVERT_CLOAK] = + { + }, + + [HOLD_EFFECT_LOADED_DICE] = + { + }, + + [HOLD_EFFECT_BOOSTER_ENERGY] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + .onEffect = TRUE, + }, + + [HOLD_EFFECT_OGERPON_MASK] = + { + }, + + [HOLD_EFFECT_BERSERK_GENE] = + { + .onSwitchIn = TRUE, + .onSwitchInFirstTurn = TRUE, + }, +}; diff --git a/src/data/items.h b/src/data/items.h index 9927191d64..937e2b0d79 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -8489,7 +8489,7 @@ const struct Item gItemsInfo[] = { .name = ITEM_NAME("Electric Seed"), .price = (I_PRICE >= GEN_9) ? 20000 : 4000, - .holdEffect = HOLD_EFFECT_SEEDS, + .holdEffect = HOLD_EFFECT_TERRAIN_SEED, .holdEffectParam = HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN, .description = COMPOUND_STRING( "Boosts Defense on\n" @@ -8508,7 +8508,7 @@ const struct Item gItemsInfo[] = { .name = ITEM_NAME("Psychic Seed"), .price = (I_PRICE >= GEN_9) ? 20000 : 4000, - .holdEffect = HOLD_EFFECT_SEEDS, + .holdEffect = HOLD_EFFECT_TERRAIN_SEED, .holdEffectParam = HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN, .description = COMPOUND_STRING( "Boosts Sp. Def. on\n" @@ -8527,7 +8527,7 @@ const struct Item gItemsInfo[] = { .name = ITEM_NAME("Misty Seed"), .price = (I_PRICE >= GEN_9) ? 20000 : 4000, - .holdEffect = HOLD_EFFECT_SEEDS, + .holdEffect = HOLD_EFFECT_TERRAIN_SEED, .holdEffectParam = HOLD_EFFECT_PARAM_MISTY_TERRAIN, .description = COMPOUND_STRING( "Boosts Sp. Def. on\n" @@ -8546,7 +8546,7 @@ const struct Item gItemsInfo[] = { .name = ITEM_NAME("Grassy Seed"), .price = (I_PRICE >= GEN_9) ? 20000 : 4000, - .holdEffect = HOLD_EFFECT_SEEDS, + .holdEffect = HOLD_EFFECT_TERRAIN_SEED, .holdEffectParam = HOLD_EFFECT_PARAM_GRASSY_TERRAIN, .description = COMPOUND_STRING( "Boosts Defense on\n" diff --git a/src/daycare.c b/src/daycare.c index f645f3e5ab..d123331d4e 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -24,7 +24,6 @@ #include "regions.h" #include "constants/form_change_types.h" #include "constants/items.h" -#include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/region_map_sections.h" diff --git a/src/item.c b/src/item.c index 162798acde..a9522a723c 100644 --- a/src/item.c +++ b/src/item.c @@ -935,7 +935,7 @@ u32 GetItemSellPrice(u32 itemId) return GetItemPrice(itemId) / ITEM_SELL_FACTOR; } -bool32 IsHoldEffectChoice(enum ItemHoldEffect holdEffect) +bool32 IsHoldEffectChoice(enum HoldEffect holdEffect) { return holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF diff --git a/src/pokemon.c b/src/pokemon.c index 635624ba0e..08ca51ee65 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -57,7 +57,6 @@ #include "constants/cries.h" #include "constants/event_objects.h" #include "constants/form_change_types.h" -#include "constants/hold_effects.h" #include "constants/item_effects.h" #include "constants/items.h" #include "constants/layouts.h" @@ -4444,7 +4443,7 @@ bool32 DoesMonMeetAdditionalConditions(struct Pokemon *mon, const struct Evoluti u32 removeBagItemCount = 0; u32 evolutionTracker = GetMonData(mon, MON_DATA_EVOLUTION_TRACKER, 0); u32 partnerSpecies, partnerHeldItem; - enum ItemHoldEffect partnerHoldEffect; + enum HoldEffect partnerHoldEffect; if (tradePartner != NULL) { diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index a9aa85efc1..a0a00894ce 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -44,7 +44,6 @@ #include "tv.h" #include "window.h" #include "constants/battle_move_effects.h" -#include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/party_menu.h" diff --git a/test/battle/ability/unburden.c b/test/battle/ability/unburden.c index 0ddce0f227..4498bb4922 100644 --- a/test/battle/ability/unburden.c +++ b/test/battle/ability/unburden.c @@ -4,7 +4,7 @@ SINGLE_BATTLE_TEST("Unburden doubles speed once user uses item") { GIVEN { - ASSUME(GetItemHoldEffect(ITEM_GRASSY_SEED) == HOLD_EFFECT_SEEDS); + ASSUME(GetItemHoldEffect(ITEM_GRASSY_SEED) == HOLD_EFFECT_TERRAIN_SEED); ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_UNBURDEN); Item(ITEM_GRASSY_SEED); Speed(5); } OPPONENT(SPECIES_WOBBUFFET) { Speed(7); } diff --git a/test/battle/hold_effect/kee_berry.c b/test/battle/hold_effect/kee_berry.c index c63da86841..d37a132be4 100644 --- a/test/battle/hold_effect/kee_berry.c +++ b/test/battle/hold_effect/kee_berry.c @@ -89,3 +89,19 @@ DOUBLE_BATTLE_TEST("Kee Berry doesn't trigger if partner was hit") EXPECT(opponentRight->item == ITEM_KEE_BERRY); } } + +SINGLE_BATTLE_TEST("Kee Berry doesn't trigger if the move was boosted by Sheer Force") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_KEE_BERRY); } + OPPONENT(SPECIES_NIDOKING) { Ability(ABILITY_SHEER_FORCE); } + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + HP_BAR(player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} \ No newline at end of file diff --git a/test/battle/hold_effect/maranga_berry.c b/test/battle/hold_effect/maranga_berry.c index 22b72ba201..785e040ca4 100644 --- a/test/battle/hold_effect/maranga_berry.c +++ b/test/battle/hold_effect/maranga_berry.c @@ -89,3 +89,19 @@ DOUBLE_BATTLE_TEST("Maranga Berry doesn't trigger if partner was hit") EXPECT(opponentRight->item == ITEM_MARANGA_BERRY); } } + +SINGLE_BATTLE_TEST("Maranga Berry doesn't trigger if the move was boosted by Sheer Force") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MARANGA_BERRY); } + OPPONENT(SPECIES_NIDOKING) { Ability(ABILITY_SHEER_FORCE); } + } WHEN { + TURN { MOVE(opponent, MOVE_FIRE_PUNCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FIRE_PUNCH, opponent); + HP_BAR(player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/hold_effect/restore_hp.c b/test/battle/hold_effect/restore_hp.c index 9db149c023..ef96ead7e4 100644 --- a/test/battle/hold_effect/restore_hp.c +++ b/test/battle/hold_effect/restore_hp.c @@ -63,20 +63,3 @@ DOUBLE_BATTLE_TEST("Restore HP Item effects do not miss timing after a recoil mo ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); } } - -#if B_HP_BERRIES <= GEN_3 -SINGLE_BATTLE_TEST("Restore HP Berry triggers only during the end turn") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT) { MaxHP(100); HP(51); Item(ITEM_ORAN_BERRY); } - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - TURN {} - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); - } -} -#endif diff --git a/test/battle/hold_effect/seeds.c b/test/battle/hold_effect/seeds.c index eecb3846f7..b54dd54f5b 100644 --- a/test/battle/hold_effect/seeds.c +++ b/test/battle/hold_effect/seeds.c @@ -3,13 +3,13 @@ ASSUMPTIONS { - ASSUME(gItemsInfo[ITEM_ELECTRIC_SEED].holdEffect == HOLD_EFFECT_SEEDS); + ASSUME(gItemsInfo[ITEM_ELECTRIC_SEED].holdEffect == HOLD_EFFECT_TERRAIN_SEED); ASSUME(gItemsInfo[ITEM_ELECTRIC_SEED].holdEffectParam == HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN); - ASSUME(gItemsInfo[ITEM_GRASSY_SEED].holdEffect == HOLD_EFFECT_SEEDS); + ASSUME(gItemsInfo[ITEM_GRASSY_SEED].holdEffect == HOLD_EFFECT_TERRAIN_SEED); ASSUME(gItemsInfo[ITEM_GRASSY_SEED].holdEffectParam == HOLD_EFFECT_PARAM_GRASSY_TERRAIN); - ASSUME(gItemsInfo[ITEM_MISTY_SEED].holdEffect == HOLD_EFFECT_SEEDS); + ASSUME(gItemsInfo[ITEM_MISTY_SEED].holdEffect == HOLD_EFFECT_TERRAIN_SEED); ASSUME(gItemsInfo[ITEM_MISTY_SEED].holdEffectParam == HOLD_EFFECT_PARAM_MISTY_TERRAIN); - ASSUME(gItemsInfo[ITEM_PSYCHIC_SEED].holdEffect == HOLD_EFFECT_SEEDS); + ASSUME(gItemsInfo[ITEM_PSYCHIC_SEED].holdEffect == HOLD_EFFECT_TERRAIN_SEED); ASSUME(gItemsInfo[ITEM_PSYCHIC_SEED].holdEffectParam == HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN); } diff --git a/test/battle/move_effect/magic_room.c b/test/battle/move_effect/magic_room.c index cffd6034d7..02953f533d 100644 --- a/test/battle/move_effect/magic_room.c +++ b/test/battle/move_effect/magic_room.c @@ -36,4 +36,43 @@ DOUBLE_BATTLE_TEST("Magic Room prevents item hold effects") } } +SINGLE_BATTLE_TEST("Magic Room: An item that can activate will activate once Magic Room is over") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MAGIC_ROOM); MOVE(opponent, MOVE_GROWL); } + TURN {} + TURN {} + TURN {} + TURN {} + TURN {} + } SCENE { + // Turn 1 + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_ROOM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent); + // Turn 2 + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + // Turn 3 + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + // Turn 4 + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + // Turn 5 + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } + + MESSAGE("Magic Room wore off, and held items' effects returned to normal!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + } +} + TO_DO_BATTLE_TEST("TODO: Write Magic Room (Move Effect) test titles") diff --git a/test/battle/move_effect_secondary/steal_item.c b/test/battle/move_effect_secondary/steal_item.c index 79c293757f..e83d5c5cb5 100644 --- a/test/battle/move_effect_secondary/steal_item.c +++ b/test/battle/move_effect_secondary/steal_item.c @@ -148,11 +148,12 @@ SINGLE_BATTLE_TEST("Thief and Covet can't steal target's held item if user faint } } -SINGLE_BATTLE_TEST("Thief and Covet: Berry activation happens before the item can be stolen") +SINGLE_BATTLE_TEST("Thief and Covet: Berries that activate on HP thresholds are stolen before they can activate") { u32 move; PARAMETRIZE { move = MOVE_THIEF; } PARAMETRIZE { move = MOVE_COVET; } + GIVEN { PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET) { MaxHP(200); HP(101); Item(ITEM_ORAN_BERRY); } @@ -161,6 +162,25 @@ SINGLE_BATTLE_TEST("Thief and Covet: Berry activation happens before the item ca } SCENE { ANIMATION(ANIM_TYPE_MOVE, move, player); HP_BAR(opponent); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); + } +} + +SINGLE_BATTLE_TEST("Thief and Covet: Berries that activate on a Status activate before the item can be stolen") +{ + u32 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + + GIVEN { + PLAYER(SPECIES_TOXICROAK) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_STEAL, opponent); } diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index f57a15b2a5..9eba3e1901 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2181,7 +2181,7 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitGimmick && ctx->gimmick != GIMMICK_NONE) { u32 item = GetMonData(mon, MON_DATA_HELD_ITEM); - enum ItemHoldEffect holdEffect = GetItemHoldEffect(item); + enum HoldEffect holdEffect = GetItemHoldEffect(item); u32 species = GetMonData(mon, MON_DATA_SPECIES); u32 side = battlerId & BIT_SIDE;