From f222bcaff530f85346c781c9829a007d291b5ca9 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 30 Nov 2025 17:25:20 +0100 Subject: [PATCH] Switch-in Events Refactor (#8128) --- asm/macros/battle_script.inc | 15 +- data/battle_scripts_1.s | 380 ++++----------- data/battle_scripts_2.s | 1 + include/battle.h | 38 +- include/battle_end_turn.h | 1 - include/battle_hold_effects.h | 10 +- include/battle_main.h | 9 +- include/battle_script_commands.h | 2 - include/battle_scripts.h | 40 +- include/battle_switch_in.h | 8 + include/battle_util.h | 22 +- include/constants/battle.h | 2 +- include/constants/battle_script_commands.h | 19 +- include/constants/battle_switch_in.h | 37 ++ src/battle_ai_switch_items.c | 8 +- src/battle_dynamax.c | 2 +- src/battle_end_turn.c | 20 +- src/battle_hold_effects.c | 232 +++------ src/battle_main.c | 110 +---- src/battle_message.c | 8 +- src/battle_script_commands.c | 448 ++++-------------- src/battle_switch_in.c | 406 ++++++++++++++++ src/battle_terastal.c | 6 +- src/battle_util.c | 517 ++++++++++----------- src/battle_z_move.c | 2 +- src/data/hold_effects.h | 34 +- src/pokemon.c | 2 +- test/battle/ability/commander.c | 2 +- test/battle/ability/costar.c | 29 ++ test/battle/ability/hospitality.c | 36 ++ test/battle/ability/tera_shift.c | 20 +- test/battle/ability/unnerve.c | 18 + test/battle/ai/ai_switching.c | 158 +++---- test/battle/end_turn_effects.c | 31 ++ test/battle/form_change/primal_reversion.c | 16 +- test/battle/hazards.c | 16 +- test/battle/move_effect/court_change.c | 4 +- 37 files changed, 1272 insertions(+), 1437 deletions(-) create mode 100644 include/battle_switch_in.h create mode 100644 include/constants/battle_switch_in.h create mode 100644 src/battle_switch_in.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 7eb3b9d064..8f587a1112 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -425,8 +425,9 @@ .4byte \jumpInstr .endm - .macro unused_0x48 + .macro trainerslidein position:req .byte 0x48 + .byte \position .endm .macro moveend endMode:req, endState:req @@ -516,9 +517,8 @@ .byte \battler .endm - .macro trainerslidein position:req + .macro switchinevents .byte 0x53 - .byte \position .endm .macro playse song:req @@ -1297,7 +1297,7 @@ .byte 0xf3 .endm - .macro unused_0xf4 + .macro sortbattlers .byte 0xf4 .endm @@ -1873,10 +1873,6 @@ manipulatedamage DMG_1_8_TARGET_HP .endm - .macro dmgtomaxattackerhp - manipulatedamage DMG_FULL_ATTACKER_HP - .endm - .macro jumpifflowerveil jumpInstr:req jumpifnottype BS_TARGET, TYPE_GRASS, 1f jumpifability BS_TARGET_SIDE, ABILITY_FLOWER_VEIL, \jumpInstr @@ -2119,8 +2115,9 @@ callnative BS_InstantHpDrop .endm - .macro clearstatus + .macro clearstatus battler:req callnative BS_ClearStatus + .byte \battler .endm .macro restoremovepp diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 8754ed053c..2bb48eb289 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -143,6 +143,7 @@ BattleScript_EffectShedTail:: switchinanim BS_ATTACKER, FALSE, TRUE waitstate switchineffects BS_ATTACKER + switchinevents end BattleScript_EffectPsychicNoise:: @@ -246,15 +247,15 @@ BattleScript_EffectChillyReceptionPlayAnimation: return BattleScript_EffectChillyReceptionBlockedByPrimalSun: call BattleScript_EffectChillyReceptionTrySwitchWeatherFailed - call BattleScript_ExtremelyHarshSunlightWasNotLessenedRet + call BattleScript_ExtremelyHarshSunlightWasNotLessened goto BattleScript_MoveSwitch BattleScript_EffectChillyReceptionBlockedByPrimalRain: call BattleScript_EffectChillyReceptionTrySwitchWeatherFailed - call BattleScript_NoReliefFromHeavyRainRet + call BattleScript_NoReliefFromHeavyRain goto BattleScript_MoveSwitch BattleScript_EffectChillyReceptionBlockedByStrongWinds: call BattleScript_EffectChillyReceptionTrySwitchWeatherFailed - call BattleScript_MysteriousAirCurrentBlowsOnRet + call BattleScript_MysteriousAirCurrentBlowsOn goto BattleScript_MoveSwitch BattleScript_EffectChillyReceptionTrySwitchWeatherFailed: jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed @@ -290,6 +291,7 @@ BattleScript_MoveSwitchOpenPartyScreen:: switchinanim BS_ATTACKER, FALSE, FALSE waitstate switchineffects BS_ATTACKER + switchinevents BattleScript_MoveSwitchEnd: end @@ -440,6 +442,7 @@ BattleScript_EffectRevivalBlessingSendOut: switchinanim BS_SCRIPTING, FALSE, FALSE waitstate switchineffects BS_SCRIPTING + switchinevents goto BattleScript_MoveEnd BattleScript_StealthRockActivates:: @@ -589,7 +592,7 @@ BattleScript_AffectionBasedStatus_HealFrostbiteString: printstring STRINGID_ATTACKERHEALEDITSFROSTBITE BattleScript_AffectionBasedStatusHeal_Continue: waitmessage B_WAIT_TIME_LONG - clearstatus + clearstatus BS_ATTACKER waitstate updatestatusicon BS_ATTACKER waitstate @@ -2041,6 +2044,7 @@ BattleScript_EffectHealingWishGen4: switchinanim BS_ATTACKER, FALSE, TRUE waitstate switchineffects BS_ATTACKER + switchinevents goto BattleScript_EffectHealingWishEnd BattleScript_HealingWishActivates:: @@ -2052,15 +2056,13 @@ BattleScript_LunarDanceActivates:: BattleScript_EffectHealingWishRestore: printfromtable gHealingWishStringIds waitmessage B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_WISH_HEAL + playanimation BS_SCRIPTING, B_ANIM_WISH_HEAL waitanimation - dmgtomaxattackerhp - manipulatedamage DMG_CHANGE_SIGN - healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE - datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE - clearstatus + healthbarupdate BS_SCRIPTING, PASSIVE_HP_UPDATE + datahpupdate BS_SCRIPTING, PASSIVE_HP_UPDATE + clearstatus BS_SCRIPTING, waitstate - updatestatusicon BS_ATTACKER + updatestatusicon BS_SCRIPTING waitstate printstring STRINGID_HEALINGWISHHEALED waitmessage B_WAIT_TIME_LONG @@ -3584,6 +3586,7 @@ BattleScript_EffectBatonPass:: switchinanim BS_ATTACKER, FALSE, TRUE waitstate switchineffects BS_ATTACKER + switchinevents goto BattleScript_MoveEnd BattleScript_EffectMorningSun:: @@ -3617,71 +3620,28 @@ BattleScript_EffectSunnyDay:: goto BattleScript_MoveWeatherChange BattleScript_ExtremelyHarshSunlightWasNotLessened: - pause B_WAIT_TIME_SHORT - printstring STRINGID_EXTREMELYHARSHSUNLIGHTWASNOTLESSENED - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - -BattleScript_ExtremelyHarshSunlightWasNotLessenedEnd3: - pause B_WAIT_TIME_SHORT - printstring STRINGID_EXTREMELYHARSHSUNLIGHTWASNOTLESSENED - waitmessage B_WAIT_TIME_LONG - end3 - -BattleScript_ExtremelyHarshSunlightWasNotLessenedRet: pause B_WAIT_TIME_SHORT printstring STRINGID_EXTREMELYHARSHSUNLIGHTWASNOTLESSENED waitmessage B_WAIT_TIME_LONG return BattleScript_NoReliefFromHeavyRain: - pause B_WAIT_TIME_SHORT - printstring STRINGID_NORELIEFROMHEAVYRAIN - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - -BattleScript_NoReliefFromHeavyRainEnd3: - pause B_WAIT_TIME_SHORT - printstring STRINGID_NORELIEFROMHEAVYRAIN - waitmessage B_WAIT_TIME_LONG - end3 - -BattleScript_NoReliefFromHeavyRainRet: pause B_WAIT_TIME_SHORT printstring STRINGID_NORELIEFROMHEAVYRAIN waitmessage B_WAIT_TIME_LONG return BattleScript_MysteriousAirCurrentBlowsOn: - pause B_WAIT_TIME_SHORT - printstring STRINGID_MYSTERIOUSAIRCURRENTBLOWSON - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - -BattleScript_MysteriousAirCurrentBlowsOnEnd3: - pause B_WAIT_TIME_SHORT - printstring STRINGID_MYSTERIOUSAIRCURRENTBLOWSON - waitmessage B_WAIT_TIME_LONG - end3 - -BattleScript_MysteriousAirCurrentBlowsOnRet: pause B_WAIT_TIME_SHORT printstring STRINGID_MYSTERIOUSAIRCURRENTBLOWSON waitmessage B_WAIT_TIME_LONG return -BattleScript_BlockedByPrimalWeatherEnd3:: +BattleScript_BlockedByPrimalWeather:: call BattleScript_AbilityPopUp - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessenedEnd3 - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRainEnd3 - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOnEnd3 - end3 - -BattleScript_BlockedByPrimalWeatherRet:: - call BattleScript_AbilityPopUp - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessenedRet - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRainRet - jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOnRet + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_SUN_PRIMAL, BattleScript_ExtremelyHarshSunlightWasNotLessened + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_RAIN_PRIMAL, BattleScript_NoReliefFromHeavyRain + jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_STRONG_WINDS, BattleScript_MysteriousAirCurrentBlowsOn return BattleScript_EffectBellyDrum:: @@ -4474,6 +4434,7 @@ BattleScript_FaintedMonSendOutNew: jumpifbytenotequal sSHIFT_SWITCHED, sZero, BattleScript_FaintedMonShiftSwitched BattleScript_FaintedMonSendOutNewEnd: switchineffects BS_FAINTED + switchinevents jumpifbattletype BATTLE_TYPE_DOUBLE, BattleScript_FaintedMonEnd cancelallactions BattleScript_FaintedMonEnd:: @@ -4481,6 +4442,7 @@ BattleScript_FaintedMonEnd:: BattleScript_FaintedMonShiftSwitched: copybyte sSAVED_BATTLER, gBattlerTarget switchineffects BS_ATTACKER + switchinevents resetsentmonsvalue copybyte gBattlerTarget, sSAVED_BATTLER goto BattleScript_FaintedMonSendOutNewEnd @@ -4506,9 +4468,13 @@ BattleScript_HandleFaintedMonLoop:: switchineffects BS_FAINTED_MULTIPLE_1 jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_HandleFaintedMonLoop BattleScript_HandleFaintedMonMultipleEnd:: - switchineffects BS_FAINTED_MULTIPLE_2 + switchinevents end2 +BattleScript_FirstTurnSwitchInEvents:: + switchinevents + end3 + BattleScript_LocalTrainerBattleWon:: jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTwoTrainersDefeated printstring STRINGID_PLAYERDEFEATEDTRAINER1 @@ -4720,6 +4686,7 @@ BattleScript_DoSwitchOut:: switchinanim BS_ATTACKER, FALSE, FALSE waitstate switchineffects BS_ATTACKER + switchinevents moveendcase MOVEEND_STATUS_IMMUNITY_ABILITIES moveendcase MOVEEND_MIRROR_MOVE end2 @@ -4863,8 +4830,9 @@ BattleScript_MagicRoomEnds:: printstring STRINGID_MAGICROOMENDS waitmessage B_WAIT_TIME_LONG setbyte gBattlerTarget, 0 + sortbattlers BattleScript_MagicRoomHealingItemsLoop: - copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattlerTarget, 1 + copyarraywithindex gBattlerAttacker, gBattlersBySpeed, gBattlerTarget, 1 tryactivateitem BS_ATTACKER, ACTIVATION_ON_USABLE_AGAIN addbyte gBattlerTarget, 1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_MagicRoomHealingItemsLoop @@ -4986,6 +4954,7 @@ BattleScript_RoarSuccessSwitch:: waitstate printstring STRINGID_PKMNWASDRAGGEDOUT switchineffects BS_TARGET + switchinevents jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_RED_CARD, BattleScript_RoarSuccessSwitch_Ret setbyte sSWITCH_CASE, B_SWITCH_NORMAL goto BattleScript_MoveEnd @@ -5116,62 +5085,18 @@ BattleScript_DestinyBondTakesLife:: tryfaintmon BS_ATTACKER return -BattleScript_DmgHazardsOnAttacker:: - healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE - datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE - call BattleScript_PrintHurtByDmgHazards - tryfaintmon BS_ATTACKER - tryfaintmon_spikes BS_ATTACKER, BattleScript_DmgHazardsOnAttackerFainted - return - -BattleScript_DmgHazardsOnAttackerFainted:: - setbyte sGIVEEXP_STATE, 0 - getexp BS_ATTACKER - moveendall - goto BattleScript_HandleFaintedMon - -BattleScript_DmgHazardsOnTarget:: - healthbarupdate BS_TARGET, PASSIVE_HP_UPDATE - datahpupdate BS_TARGET, PASSIVE_HP_UPDATE - call BattleScript_PrintHurtByDmgHazards - tryfaintmon BS_TARGET - tryfaintmon_spikes BS_TARGET, BattleScript_DmgHazardsOnTargetFainted - return - -BattleScript_DmgHazardsOnTargetFainted:: - setbyte sGIVEEXP_STATE, 0 - getexp BS_TARGET - moveendall - goto BattleScript_HandleFaintedMon - -BattleScript_DmgHazardsOnBattlerScripting:: +BattleScript_DmgHazardsOnBattler:: healthbarupdate BS_SCRIPTING, PASSIVE_HP_UPDATE datahpupdate BS_SCRIPTING, PASSIVE_HP_UPDATE call BattleScript_PrintHurtByDmgHazards tryfaintmon BS_SCRIPTING - tryfaintmon_spikes BS_SCRIPTING, BattleScript_DmgHazardsOnBattlerScriptingFainted return BattleScript_DmgHazardsOnBattlerScriptingFainted:: setbyte sGIVEEXP_STATE, 0 getexp BS_SCRIPTING - moveendall - goto BattleScript_HandleFaintedMon - -BattleScript_DmgHazardsOnFaintedBattler:: - healthbarupdate BS_FAINTED, PASSIVE_HP_UPDATE - datahpupdate BS_FAINTED, PASSIVE_HP_UPDATE - call BattleScript_PrintHurtByDmgHazards - tryfaintmon BS_FAINTED - tryfaintmon_spikes BS_FAINTED, BattleScript_DmgHazardsOnFaintedBattlerFainted return -BattleScript_DmgHazardsOnFaintedBattlerFainted:: - setbyte sGIVEEXP_STATE, 0 - getexp BS_FAINTED - moveendall - goto BattleScript_HandleFaintedMon - BattleScript_PrintHurtByDmgHazards:: printfromtable gDmgHazardsStringIds waitmessage B_WAIT_TIME_LONG @@ -5802,7 +5727,7 @@ BattleScript_PrimalReversion:: printstring STRINGID_PKMNREVERTEDTOPRIMAL waitmessage B_WAIT_TIME_LONG switchinabilities BS_SCRIPTING - end3 + return BattleScript_PowerConstruct:: flushtextbox @@ -5848,10 +5773,6 @@ BattleScript_BattlerFormChangeNoPopup: handleformchange BS_SCRIPTING, 2 return -BattleScript_BattlerFormChangeEnd3:: - call BattleScript_BattlerFormChange - end3 - BattleScript_BattlerFormChangeEnd3NoPopup:: call BattleScript_BattlerFormChangeNoPopup end2 @@ -5860,7 +5781,7 @@ BattleScript_BattlerFormChangeEnd2:: call BattleScript_BattlerFormChange end2 -BattleScript_BattlerFormChangeWithStringEnd3:: +BattleScript_BattlerFormChangeWithString:: pause 5 call BattleScript_AbilityPopUpScripting flushtextbox @@ -5871,7 +5792,7 @@ BattleScript_BattlerFormChangeWithStringEnd3:: handleformchange BS_SCRIPTING, 2 printstring STRINGID_PKMNTRANSFORMED waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_AttackerFormChangeMoveEffect:: waitmessage 1 @@ -5940,10 +5861,6 @@ BattleScript_IllusionOffAndTerastallization:: call BattleScript_IllusionOff goto BattleScript_Terastallization -BattleScript_IllusionOffEnd3:: - call BattleScript_IllusionOff - end3 - BattleScript_IllusionOff:: setspriteignore0hp TRUE playanimation BS_SCRIPTING, B_ANIM_ILLUSION_OFF @@ -6381,7 +6298,7 @@ BattleScript_DrizzleActivates:: waitstate playanimation BS_BATTLER_0, B_ANIM_RAIN_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_AbilityRaisesDefenderStat:: pause B_WAIT_TIME_SHORT @@ -6463,6 +6380,11 @@ BattleScript_EmergencyExit:: pause B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN waitanimation + jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EmergencyExitTrainer + setteleportoutcome BS_SCRIPTING + finishaction + return +BattleScript_EmergencyExitTrainer: openpartyscreen BS_SCRIPTING, BattleScript_EmergencyExitRet waitstate returntoball BS_SCRIPTING, FALSE @@ -6475,48 +6397,12 @@ BattleScript_EmergencyExit:: switchinanim BS_SCRIPTING, FALSE, TRUE waitstate switchineffects BS_SCRIPTING + switchinevents BattleScript_EmergencyExitRet: return -BattleScript_EmergencyExitWild:: - pause 5 - call BattleScript_AbilityPopUpScripting - pause B_WAIT_TIME_LONG - playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN - waitanimation - setteleportoutcome BS_SCRIPTING - finishaction - return - BattleScript_EmergencyExitEnd2:: - pause 5 - call BattleScript_AbilityPopUp - pause B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN - waitanimation - openpartyscreen BS_ATTACKER, BattleScript_EmergencyExitRetEnd2 - waitstate - returntoball BS_ATTACKER, FALSE - switchoutabilities BS_ATTACKER - switchhandleorder BS_ATTACKER, 2 - getswitchedmondata BS_ATTACKER - switchindataupdate BS_ATTACKER - hpthresholds BS_ATTACKER - printstring STRINGID_SWITCHINMON - switchinanim BS_ATTACKER, FALSE, TRUE - waitstate - switchineffects BS_ATTACKER -BattleScript_EmergencyExitRetEnd2: - end2 - -BattleScript_EmergencyExitWildEnd2:: - pause 5 - call BattleScript_AbilityPopUp - pause B_WAIT_TIME_LONG - playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN - waitanimation - setteleportoutcome BS_ATTACKER - finishaction + call BattleScript_EmergencyExit end2 BattleScript_TraceActivates:: @@ -6526,7 +6412,7 @@ BattleScript_TraceActivates:: waitmessage B_WAIT_TIME_LONG settracedability BS_SCRIPTING switchinabilities BS_SCRIPTING - end3 + return BattleScript_ReceiverActivates:: call BattleScript_AbilityPopUp @@ -6599,7 +6485,7 @@ BattleScript_SandstreamActivates:: waitstate playanimation BS_BATTLER_0, B_ANIM_SANDSTORM_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_SandSpitActivates:: pause B_WAIT_TIME_SHORT @@ -6622,8 +6508,9 @@ BattleScript_ActivateWeatherAbilities: savetarget tryboosterenergy ON_WEATHER setbyte gBattlerAttacker, 0 + sortbattlers BattleScript_ActivateWeatherAbilities_Loop: - copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + copyarraywithindex gBattlerTarget, gBattlersBySpeed, gBattlerAttacker, 1 activateweatherchangeabilities BS_TARGET addbyte gBattlerAttacker, 1 jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_ActivateWeatherAbilities_Loop @@ -6676,7 +6563,7 @@ BattleScript_IntimidateLoopIncrement: restoretarget restoreattacker pause B_WAIT_TIME_MED - end3 + return BattleScript_IntimidatePrevented:: copybyte sBATTLER, gBattlerTarget @@ -6728,7 +6615,7 @@ BattleScript_SupersweetSyrupLoopIncrement: restoretarget restoreattacker pause B_WAIT_TIME_MED - end3 + return BattleScript_SupersweetSyrupWontDecrease: printstring STRINGID_STATSWONTDECREASE @@ -6741,7 +6628,7 @@ BattleScript_DroughtActivates:: waitstate playanimation BS_BATTLER_0, B_ANIM_SUN_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_DesolateLandActivates:: pause B_WAIT_TIME_SHORT @@ -6750,7 +6637,7 @@ BattleScript_DesolateLandActivates:: waitstate playanimation BS_BATTLER_0, B_ANIM_SUN_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_PrimalWeatherBlocksMove:: pause B_WAIT_TIME_SHORT @@ -6765,7 +6652,7 @@ BattleScript_PrimordialSeaActivates:: waitstate playanimation BS_BATTLER_0, B_ANIM_RAIN_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_DeltaStreamActivates:: pause B_WAIT_TIME_SHORT @@ -6773,7 +6660,7 @@ BattleScript_DeltaStreamActivates:: printstring STRINGID_MYSTERIOUSAIRCURRENT waitstate playanimation BS_ATTACKER, B_ANIM_STRONG_WINDS - end3 + return BattleScript_ProtosynthesisActivates:: call BattleScript_AbilityPopUpScripting @@ -6781,7 +6668,7 @@ BattleScript_ProtosynthesisActivates:: waitmessage B_WAIT_TIME_MED printstring STRINGID_STATWASHEIGHTENED waitmessage B_WAIT_TIME_MED - end3 + return BattleScript_QuarkDriveActivates:: call BattleScript_AbilityPopUp @@ -6789,34 +6676,34 @@ BattleScript_QuarkDriveActivates:: waitmessage B_WAIT_TIME_MED printstring STRINGID_STATWASHEIGHTENED waitmessage B_WAIT_TIME_MED - end3 + return BattleScript_RuinAbilityActivates:: call BattleScript_AbilityPopUp printstring STRINGID_ABILITYWEAKENEDSURROUNDINGMONSSTAT waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_SupremeOverlordActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_CostarActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_PKMNCOPIEDSTATCHANGES waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_ZeroToHeroActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUpScripting printstring STRINGID_ZEROTOHEROTRANSFORMATION waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_CommanderActivates:: pause B_WAIT_TIME_SHORT @@ -6855,7 +6742,7 @@ BattleScript_CommanderSpeedIncrease: waitmessage B_WAIT_TIME_LONG BattleScript_CommanderEnd: restoreattacker - end3 + return BattleScript_HospitalityActivates:: pause B_WAIT_TIME_SHORT @@ -6865,7 +6752,7 @@ BattleScript_HospitalityActivates:: playanimation BS_EFFECT_BATTLER, B_ANIM_SIMPLE_HEAL healthbarupdate BS_EFFECT_BATTLER, PASSIVE_HP_UPDATE datahpupdate BS_EFFECT_BATTLER, PASSIVE_HP_UPDATE - end3 + return BattleScript_AttackWeakenedByStrongWinds:: pause B_WAIT_TIME_SHORT @@ -6878,7 +6765,7 @@ BattleScript_MimicryActivates:: call BattleScript_AbilityPopUp printstring STRINGID_BATTLERTYPECHANGEDTO waitmessage B_WAIT_TIME_SHORT - end3 + return BattleScript_SnowWarningActivatesHail:: pause B_WAIT_TIME_SHORT @@ -6887,7 +6774,7 @@ BattleScript_SnowWarningActivatesHail:: waitstate playanimation BS_BATTLER_0, B_ANIM_HAIL_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_SnowWarningActivatesSnow:: pause B_WAIT_TIME_SHORT @@ -6896,7 +6783,7 @@ BattleScript_SnowWarningActivatesSnow:: waitstate playanimation BS_BATTLER_0, B_ANIM_SNOW_CONTINUES call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_ActivateTerrainEffects: saveattacker @@ -6904,8 +6791,9 @@ BattleScript_ActivateTerrainEffects: tryboosterenergy ON_TERRAIN resetterrainabilityflags setbyte gBattlerAttacker, 0 + sortbattlers BattleScript_ActivateTerrainSeed: - copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + copyarraywithindex gBattlerTarget, gBattlersBySpeed, gBattlerAttacker, 1 tryterrainseed BS_TARGET, BattleScript_ActivateTerrainAbility removeitem BS_TARGET BattleScript_ActivateTerrainAbility: @@ -6923,7 +6811,7 @@ BattleScript_ElectricSurgeActivates:: waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG call BattleScript_ActivateTerrainEffects - end3 + return BattleScript_MistySurgeActivates:: pause B_WAIT_TIME_SHORT @@ -6932,7 +6820,7 @@ BattleScript_MistySurgeActivates:: waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG call BattleScript_ActivateTerrainEffects - end3 + return BattleScript_GrassySurgeActivates:: pause B_WAIT_TIME_SHORT @@ -6941,7 +6829,7 @@ BattleScript_GrassySurgeActivates:: waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG call BattleScript_ActivateTerrainEffects - end3 + return BattleScript_PsychicSurgeActivates:: pause B_WAIT_TIME_SHORT @@ -6950,7 +6838,7 @@ BattleScript_PsychicSurgeActivates:: waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG call BattleScript_ActivateTerrainEffects - end3 + return BattleScript_BadDreamsActivates:: setbyte gBattlerTarget, 0 @@ -7231,7 +7119,7 @@ BattleScript_BattlerAbilityStatRaiseOnSwitchIn:: printstring STRINGID_SCRIPTINGABILITYSTATRAISE waitmessage B_WAIT_TIME_LONG BattleScript_BattlerAbilityStatRaiseOnSwitchInRet: - end3 + return BattleScript_ScriptingAbilityStatRaise:: copybyte gBattlerAbility, sBATTLER @@ -7310,7 +7198,7 @@ BattleScript_FellStingerRaisesAtkEnd: BattleScript_AttackerAbilityStatRaiseEnd3:: call BattleScript_AttackerAbilityStatRaise restoreattacker - end3 + return BattleScript_AttackerAbilityStatRaiseEnd2:: call BattleScript_AttackerAbilityStatRaise @@ -7318,12 +7206,6 @@ BattleScript_AttackerAbilityStatRaiseEnd2:: end2 BattleScript_SwitchInAbilityMsg:: - call BattleScript_AbilityPopUp - printfromtable gSwitchInAbilityStringIds - waitmessage B_WAIT_TIME_LONG - end3 - -BattleScript_SwitchInAbilityMsgRet:: call BattleScript_AbilityPopUp printfromtable gSwitchInAbilityStringIds waitmessage B_WAIT_TIME_LONG @@ -7339,7 +7221,7 @@ BattleScript_ActivateAsOne:: call BattleScript_AbilityPopUp printfromtable gSwitchInAbilityStringIds waitmessage B_WAIT_TIME_LONG - end3 + return BattleScript_FriskMsgWithPopup:: copybyte gBattlerAbility, gBattlerAttacker @@ -7356,7 +7238,7 @@ BattleScript_FriskActivates:: tryfriskmessage restoreattacker restoretarget - end3 + return BattleScript_ImposterActivates:: call BattleScript_AbilityPopUp @@ -7367,7 +7249,7 @@ BattleScript_ImposterActivates:: waitmessage B_WAIT_TIME_LONG restoreattacker restoretarget - end3 + return BattleScript_HurtAttacker: healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE @@ -7501,13 +7383,6 @@ BattleScript_AbilityCuredStatus:: updatestatusicon BS_SCRIPTING return -BattleScript_AbilityCuredStatusEnd3:: - call BattleScript_AbilityPopUp - printstring STRINGID_PKMNSXCUREDITSYPROBLEM - waitmessage B_WAIT_TIME_LONG - updatestatusicon BS_SCRIPTING - end3 - BattleScript_BattlerShookOffTaunt:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSHOOKOFFTHETAUNT @@ -7565,10 +7440,6 @@ BattleScript_SubstituteFade:: printstring STRINGID_PKMNSUBSTITUTEFADED return -BattleScript_BerryCureStatusEnd2:: - call BattleScript_BerryCureStatusRet - end2 - BattleScript_BerryCureStatusRet:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printfromtable CureStatusBerryEffectStringID @@ -7595,10 +7466,6 @@ BattleScript_BerryReduceDmg:: removeitem BS_SCRIPTING return -BattleScript_BerryCureConfusionEnd2:: - call BattleScript_BerryCureConfusionRet - end2 - BattleScript_BerryCureConfusionRet:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_PKMNSITEMSNAPPEDOUT @@ -7629,7 +7496,7 @@ BattleScript_WhiteHerbRet:: removeitem BS_SCRIPTING return -BattleScript_ItemHealHP_RemoveItemRet:: +BattleScript_ItemHealHP_RemoveItem:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_ItemHealHP_RemoveItemRet_AbilityPopUp goto BattleScript_ItemHealHP_RemoveItemRet_Anim BattleScript_ItemHealHP_RemoveItemRet_AbilityPopUp: @@ -7643,21 +7510,7 @@ BattleScript_ItemHealHP_RemoveItemRet_Anim: removeitem BS_SCRIPTING return -BattleScript_ItemHealHP_RemoveItemEnd2:: - jumpifability BS_ATTACKER, ABILITY_RIPEN, BattleScript_ItemHealHP_RemoveItemEnd2_AbilityPopUp - goto BattleScript_ItemHealHP_RemoveItemEnd2_Anim -BattleScript_ItemHealHP_RemoveItemEnd2_AbilityPopUp: - call BattleScript_AbilityPopUpScripting -BattleScript_ItemHealHP_RemoveItemEnd2_Anim: - playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMRESTOREDHEALTH - waitmessage B_WAIT_TIME_LONG - healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE - datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE - removeitem BS_ATTACKER - end2 - -BattleScript_BerryPPHealRet:: +BattleScript_BerryPPHeal:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_BerryPPHeal_AbilityPopup goto BattleScript_BerryPPHeal_Anim BattleScript_BerryPPHeal_AbilityPopup: @@ -7669,19 +7522,10 @@ BattleScript_BerryPPHeal_Anim: removeitem BS_SCRIPTING return -BattleScript_BerryPPHealEnd2:: - call BattleScript_BerryPPHealRet - end2 - BattleScript_ItemHealHP_End2:: call BattleScript_ItemHealHP_Ret end2 -BattleScript_AirBalloonMsgIn:: - printstring STRINGID_AIRBALLOONFLOAT - waitmessage B_WAIT_TIME_LONG - end3 - BattleScript_AirBalloonMsgInRet:: printstring STRINGID_AIRBALLOONFLOAT waitmessage B_WAIT_TIME_LONG @@ -7756,22 +7600,7 @@ BattleScript_HangedOnMsg:: BattleScript_HangedOnMsgRet: return -BattleScript_BerryConfuseHealEnd2:: - jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_BerryConfuseHealEnd2_AbilityPopup - goto BattleScript_BerryConfuseHealEnd2_Anim -BattleScript_BerryConfuseHealEnd2_AbilityPopup: - call BattleScript_AbilityPopUp -BattleScript_BerryConfuseHealEnd2_Anim: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_PKMNSITEMRESTOREDHEALTH - waitmessage B_WAIT_TIME_LONG - healthbarupdate BS_SCRIPTING, PASSIVE_HP_UPDATE - datahpupdate BS_SCRIPTING, PASSIVE_HP_UPDATE - seteffectprimary BS_SCRIPTING, BS_SCRIPTING, MOVE_EFFECT_CONFUSION - removeitem BS_SCRIPTING - end2 - -BattleScript_BerryConfuseHealRet:: +BattleScript_BerryConfuseHeal:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_BerryConfuseHealRet_AbilityPopup goto BattleScript_BerryConfuseHealRet_Anim BattleScript_BerryConfuseHealRet_AbilityPopup: @@ -7786,10 +7615,6 @@ BattleScript_BerryConfuseHealRet_Anim: removeitem BS_SCRIPTING return -BattleScript_ConsumableStatRaiseEnd2:: - call BattleScript_ConsumableStatRaiseRet - end2 - BattleScript_ConsumableStatRaiseRet:: jumpifnotberry BS_SCRIPTING, BattleScript_ConsumableStatRaiseRet_Anim jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_ConsumableStatRaiseRet_AbilityPopup @@ -7809,17 +7634,13 @@ BattleScript_ConsumableStatRaiseRet_Anim: BattleScript_ConsumableStatRaiseRet_End: return -BattleScript_BerryFocusEnergyRet:: +BattleScript_BerryFocusEnergy:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_PKMNUSEDXTOGETPUMPED waitmessage B_WAIT_TIME_LONG removeitem BS_SCRIPTING return -BattleScript_BerryFocusEnergyEnd2:: - call BattleScript_BerryFocusEnergyRet - end2 - BattleScript_ActionSelectionItemsCantBeUsed:: printselectionstring STRINGID_ITEMSCANTBEUSEDNOW endselectionscript @@ -7990,10 +7811,6 @@ BattleScript_OpportunistCopyStatChangeEnd: setbyte sSTAT_ANIM_PLAYED, FALSE return -BattleScript_OpportunistCopyStatChangeEnd3:: - call BattleScript_OpportunistCopyStatChange - end3 - BattleScript_TotemVar:: call BattleScript_TotemVar_Ret end2 @@ -8014,14 +7831,14 @@ BattleScript_AnnounceAirLockCloudNine:: printstring STRINGID_AIRLOCKACTIVATES waitmessage B_WAIT_TIME_LONG call BattleScript_ActivateWeatherAbilities - end3 + return BattleScript_ActivateTeraformZero:: call BattleScript_AbilityPopUp waitmessage B_WAIT_TIME_LONG jumpifhalfword CMP_COMMON_BITS, gBattleWeather, B_WEATHER_ANY, BattleScript_ActivateTeraformZero_RemoveWeather jumpifhalfword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZero_RemoveTerrain - goto BattleScript_ActivateTeraformZero_End + goto BattleScript_ActivateTeraformZero_Ret BattleScript_ActivateTeraformZero_RemoveWeather: removeweather printfromtable gWeatherEndsStringIds @@ -8039,16 +7856,17 @@ BattleScript_ActivateTeraformZeroEffects: tryboosterenergy ON_ANY resetterrainabilityflags setbyte gBattlerAttacker, 0 + sortbattlers BattleScript_ActivateTeraformZeroLoop: - copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + copyarraywithindex gBattlerTarget, gBattlersBySpeed, gBattlerAttacker, 1 activateterrainchangeabilities BS_TARGET activateweatherchangeabilities BS_TARGET addbyte gBattlerAttacker, 1 jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_ActivateTeraformZeroLoop restoreattacker restoretarget -BattleScript_ActivateTeraformZero_End: - end3 +BattleScript_ActivateTeraformZero_Ret: + return BattleScript_QuickClawActivation:: flushtextbox @@ -8074,19 +7892,7 @@ BattleScript_CustapBerryActivation:: removeitem BS_ATTACKER end2 -BattleScript_MicleBerryActivateEnd2:: - jumpifability BS_ATTACKER, ABILITY_RIPEN, BattleScript_MicleBerryActivateEnd2_Ripen - goto BattleScript_MicleBerryActivateEnd2_Anim -BattleScript_MicleBerryActivateEnd2_Ripen: - call BattleScript_AbilityPopUp -BattleScript_MicleBerryActivateEnd2_Anim: - playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT - printstring STRINGID_MICLEBERRYACTIVATES - waitmessage B_WAIT_TIME_LONG - removeitem BS_ATTACKER - end2 - -BattleScript_MicleBerryActivateRet:: +BattleScript_MicleBerryActivate:: jumpifability BS_SCRIPTING, ABILITY_RIPEN, BattleScript_MicleBerryActivateRet_Ripen goto BattleScript_MicleBerryActivateRet_Anim BattleScript_MicleBerryActivateRet_Ripen: @@ -8295,6 +8101,7 @@ BattleScript_EjectButtonActivates:: switchinanim BS_SCRIPTING, FALSE, TRUE waitstate switchineffects BS_SCRIPTING + switchinevents BattleScript_EjectButtonEnd: return @@ -8305,10 +8112,6 @@ BattleScript_EjectPackActivate_End2:: call BattleScript_EjectPackActivate_Ret end2 -BattleScript_EjectPackActivate_End3:: - call BattleScript_EjectPackActivate_Ret - end3 - BattleScript_EjectPackActivates:: jumpifcantswitch BS_SCRIPTING, BattleScript_EjectButtonEnd goto BattleScript_EjectPackActivate_Ret @@ -8366,7 +8169,7 @@ BattleScript_PastelVeilLoopIncrement: goto BattleScript_PastelVeilEnd BattleScript_PastelVeilEnd: restoretarget - end3 + return BattleScript_NeutralizingGasExits:: saveattacker @@ -8375,8 +8178,9 @@ BattleScript_NeutralizingGasExits:: printstring STRINGID_NEUTRALIZINGGASOVER waitmessage B_WAIT_TIME_LONG setbyte gBattlerAttacker, 0 + sortbattlers BattleScript_NeutralizingGasExitsLoop: - copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + copyarraywithindex gBattlerTarget, gBattlersBySpeed, gBattlerAttacker, 1 jumpifabilitycantbereactivated BS_TARGET, BattleScript_NeutralizingGasExitsLoopIncrement saveattacker switchinabilities BS_TARGET @@ -8759,14 +8563,6 @@ BattleScript_BerserkGeneRet_End: removeitem BS_SCRIPTING return -BattleScript_BerserkGeneRetEnd2:: - call BattleScript_BerserkGeneRet - end2 - -BattleScript_BoosterEnergyEnd2:: - call BattleScript_BoosterEnergyRet - end2 - BattleScript_BoosterEnergyRet:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 call BattleScript_AbilityPopUpScripting diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 5f7cc23a8c..13a2f02a03 100755 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -81,6 +81,7 @@ BattleScript_ItemRestoreHP_SendOutRevivedBattler: switchinanim BS_SCRIPTING, FALSE, FALSE waitstate switchineffects BS_SCRIPTING + switchinevents end BattleScript_ItemCureStatus:: diff --git a/include/battle.h b/include/battle.h index 3cd40cb16b..815d8614d9 100755 --- a/include/battle.h +++ b/include/battle.h @@ -3,6 +3,7 @@ // should they be included here or included individually by every file? #include "constants/battle_end_turn.h" +#include "constants/battle_switch_in.h" #include "constants/abilities.h" #include "constants/battle.h" #include "constants/form_change_types.h" @@ -133,12 +134,11 @@ struct DisableStruct u8 neutralizingGas:1; u8 iceFaceActivationPrevention:1; // fixes hit escape move edge case u8 unnerveActivated:1; // Unnerve and As One (Unnerve part) activate only once per switch in - u8 hazardsDone:1; u8 endured:1; u8 tryEjectPack:1; u8 octolockedBy:3; u8 paradoxBoostedStat:4; - u8 padding2:1; + u8 padding2:2; }; // Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects @@ -171,7 +171,8 @@ struct ProtectStruct u16 helpingHand:3; u16 assuranceDoubled:1; u16 myceliumMight:1; - u16 padding:11; + u16 forcedSwitch:1; + u16 padding:10; // End of 16-bit bitfield u16 physicalDmg; u16 specialDmg; @@ -192,27 +193,23 @@ struct SpecialStatus u8 afterYou:1; u8 enduredDamage:1; u8 dancerUsedMove:1; - u8 padding1:1; + u8 rototillerAffected:1; // to be affected by rototiller // End of byte - u8 switchInAbilityDone:1; + u8 criticalHit:1; u8 switchInItemDone:1; u8 instructedChosenTarget:3; u8 berryReduced:1; u8 neutralizingGasRemoved:1; // See VARIOUS_TRY_END_NEUTRALIZING_GAS u8 padding2:1; // End of byte - u8 gemParam; - // End of byte + u8 gemParam:7; u8 gemBoost:1; - u8 rototillerAffected:1; // to be affected by rototiller + // End of byte u8 parentalBondState:2; u8 multiHitOn:1; u8 distortedTypeMatchups:1; u8 teraShellAbilityDone:1; - u8 criticalHit:1; - // End of byte u8 dancerOriginalTarget:3; - u8 padding3:5; // End of byte }; @@ -588,8 +585,10 @@ struct BattlerState u32 wasAboveHalfHp:1; // For Berserk, Emergency Exit, Wimp Out and Anger Shell. u32 commanderSpecies:11; u32 selectionScriptFinished:1; + u32 switchIn:1; u32 padding:3; // End of Word + u16 hpOnSwitchout; }; struct PartyState @@ -620,7 +619,9 @@ struct EventStates enum BattlerId faintedActionBattler:4; enum MoveSuccessOrder atkCanceler:8; enum BattleIntroStates battleIntro:8; - u32 padding:24; + enum SwitchInEvents switchIn:8; + u32 battlerSwitchIn:8; // SwitchInFirstEventBlock, SwitchInSecondEventBlock + u32 padding:8; }; // Cleared at the beginning of the battle. Fields need to be cleared when needed manually otherwise. @@ -669,17 +670,15 @@ struct BattleStruct u8 wallyWaitFrames; u8 wallyMoveFrames; u16 lastTakenMove[MAX_BATTLERS_COUNT]; // Last move that a battler was hit with. - u16 hpOnSwitchout[NUM_BATTLE_SIDES]; u32 savedBattleTypeFlags; u16 abilityPreventingSwitchout; u8 hpScale; u16 synchronizeMoveEffect; u8 anyMonHasTransformed:1; // Only used in battle_tv.c - u8 multipleSwitchInState:2; - u8 multipleSwitchInCursor:3; u8 sleepClauseNotBlocked:1; u8 isSkyBattle:1; - u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT]; + u8 unused:5; + u8 sortedBattlers[MAX_BATTLERS_COUNT]; void (*savedCallback)(void); u16 chosenItem[MAX_BATTLERS_COUNT]; u16 choicedMove[MAX_BATTLERS_COUNT]; @@ -696,7 +695,7 @@ struct BattleStruct u8 fickleBeamBoosted:1; u8 poisonPuppeteerConfusion:1; u8 toxicChainPriority:1; // If Toxic Chain will trigger on target, all other non volatiles will be blocked - u8 moldBreakerActive:1; + u8 battlersSorted:1; // To avoid unnessasery computation u16 startingStatusTimer; struct BattleTvMovePoints tvMovePoints; struct BattleTv tv; @@ -775,7 +774,7 @@ struct BattleStruct u8 printedStrongWindsWeakenedAttack:1; u8 numSpreadTargets:2; u8 noTargetPresent:1; - u8 padding2:1; + u8 moldBreakerActive:1; struct MessageStatus slideMessageStatus; u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; u8 hazardsQueue[NUM_BATTLE_SIDES][HAZARDS_MAX_COUNT]; @@ -787,7 +786,7 @@ struct BattleStruct u16 flingItem; u8 incrementEchoedVoice:1; u8 echoedVoiceCounter:3; - u8 padding3:4; + u8 padding4:4; }; struct AiBattleData @@ -1045,6 +1044,7 @@ extern u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT]; extern u8 gBattlerPositions[MAX_BATTLERS_COUNT]; extern u8 gActionsByTurnOrder[MAX_BATTLERS_COUNT]; extern u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT]; +extern u8 gBattlersBySpeed[MAX_BATTLERS_COUNT]; extern u8 gCurrentTurnActionNumber; extern u8 gCurrentActionFuncId; extern struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT]; diff --git a/include/battle_end_turn.h b/include/battle_end_turn.h index 468544cf2b..fb300f9abc 100644 --- a/include/battle_end_turn.h +++ b/include/battle_end_turn.h @@ -4,4 +4,3 @@ u32 DoEndTurnEffects(void); #endif // GUARD_BATTLE_END_TURN - diff --git a/include/battle_hold_effects.h b/include/battle_hold_effects.h index 51e63ea20a..17c0cb5f48 100644 --- a/include/battle_hold_effects.h +++ b/include/battle_hold_effects.h @@ -4,11 +4,8 @@ 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; @@ -21,7 +18,8 @@ struct HoldEffectInfo u32 orbs:1; u32 onEffect:1; u32 onFling:1; - u32 padding:14; + u32 boosterEnergy:1; + u32 padding:16; }; extern const struct HoldEffectInfo gHoldEffectsInfo[]; @@ -30,11 +28,8 @@ typedef bool32 (*ActivationTiming)(enum HoldEffect holdEffect); enum ItemEffect ItemBattleEffects(u32 primaryBattler, u32 secondaryBattler, enum HoldEffect holdEffect, 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); @@ -48,5 +43,6 @@ bool32 IsOnEffectActivation(enum HoldEffect holdEffect); bool32 IsForceTriggerItemActivation(enum HoldEffect holdEffect); bool32 IsOnBerryActivation(enum HoldEffect holdEffect); bool32 IsOnFlingActivation(enum HoldEffect holdEffect); +bool32 IsBoosterEnergyActivation(enum HoldEffect holdEffect); #endif // GUARD_BATTLE_HOLD_EFFECTS diff --git a/include/battle_main.h b/include/battle_main.h index c08da1d9ca..8fcbbb1c96 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -56,13 +56,7 @@ enum FirstTurnEventsStates FIRST_TURN_EVENTS_TERRAIN, FIRST_TURN_EVENTS_STARTING_STATUS, FIRST_TURN_EVENTS_TOTEM_BOOST, - FIRST_TURN_EVENTS_NEUTRALIZING_GAS, - FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES, - FIRST_TURN_EVENTS_ITEM_EFFECTS, - FIRST_TURN_EVENTS_WHITE_HERB, - FIRST_TURN_EVENTS_OPPORTUNIST, - FIRST_TURN_EVENTS_MIRROR_HERB, - FIRST_TURN_EVENTS_EJECT_PACK, + FIRST_TURN_SWITCH_IN_EVENTS, FIRST_TURN_EVENTS_END, }; @@ -116,6 +110,7 @@ void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon bool32 CanPlayerForfeitNormalTrainerBattle(void); bool32 DidPlayerForfeitNormalTrainerBattle(void); void BattleDebug_WonBattle(void); +s32 Factorial(s32 n); extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE]; diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index d011b50947..4705bb8de6 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -68,13 +68,11 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem); u8 GetCatchingBattler(void); bool32 ProteanTryChangeType(u32 battler, enum Ability ability, u32 move, enum Type moveType); bool32 IsMoveNotAllowedInSkyBattles(u32 move); -bool32 DoSwitchInAbilities(u32 battlerId); u8 GetFirstFaintedPartyIndex(u8 battlerId); bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler); void SaveBattlerTarget(u32 battler); void SaveBattlerAttacker(u32 battler); bool32 CanBurnHitThaw(u16 move); -bool32 EmergencyExitCanBeTriggered(u32 battler); extern void (*const gBattleScriptingCommandsTable[])(void); extern const struct StatFractions gAccuracyStageRatios[]; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index e2f9f1636c..5b6fefa8b2 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -3,7 +3,6 @@ extern const u8 BattleScript_SupersweetSyrupActivates[]; extern const u8 BattleScript_OpportunistCopyStatChange[]; -extern const u8 BattleScript_OpportunistCopyStatChangeEnd3[]; extern const u8 BattleScript_MirrorHerbCopyStatChange[]; extern const u8 BattleScript_MirrorHerbCopyStatChangeEnd2[]; extern const u8 BattleScript_NotAffected[]; @@ -81,10 +80,7 @@ extern const u8 BattleScript_SelectingDisabledMoveInPalace[]; extern const u8 BattleScript_SelectingUnusableMoveInPalace[]; extern const u8 BattleScript_EncoredNoMore[]; extern const u8 BattleScript_DestinyBondTakesLife[]; -extern const u8 BattleScript_DmgHazardsOnAttacker[]; -extern const u8 BattleScript_DmgHazardsOnTarget[]; -extern const u8 BattleScript_DmgHazardsOnBattlerScripting[]; -extern const u8 BattleScript_DmgHazardsOnFaintedBattler[]; +extern const u8 BattleScript_DmgHazardsOnBattler[]; extern const u8 BattleScript_PerishSongTakesLife[]; extern const u8 BattleScript_PerishSongCountGoesDown[]; extern const u8 BattleScript_AllStatsUpZMove[]; @@ -199,7 +195,6 @@ extern const u8 BattleScript_AbilityStatusEffect[]; extern const u8 BattleScript_SynchronizeActivates[]; extern const u8 BattleScript_NoItemSteal[]; extern const u8 BattleScript_AbilityCuredStatus[]; -extern const u8 BattleScript_AbilityCuredStatusEnd3[]; extern const u8 BattleScript_IgnoresWhileAsleep[]; extern const u8 BattleScript_IgnoresAndUsesRandomMove[]; extern const u8 BattleScript_MoveUsedLoafingAround[]; @@ -207,27 +202,20 @@ extern const u8 BattleScript_TruantLoafingAround[]; extern const u8 BattleScript_IgnoresAndFallsAsleep[]; extern const u8 BattleScript_IgnoresAndHitsItself[]; extern const u8 BattleScript_SubstituteFade[]; -extern const u8 BattleScript_BerryCureStatusEnd2[]; extern const u8 BattleScript_BerryCureStatusRet[]; -extern const u8 BattleScript_BerryCureConfusionEnd2[]; extern const u8 BattleScript_BerryCureConfusionRet[]; extern const u8 BattleScript_WhiteHerbEnd2[]; extern const u8 BattleScript_WhiteHerbRet[]; -extern const u8 BattleScript_ItemHealHP_RemoveItemRet[]; -extern const u8 BattleScript_ItemHealHP_RemoveItemEnd2[]; -extern const u8 BattleScript_BerryPPHealEnd2[]; -extern const u8 BattleScript_BerryPPHealRet[]; +extern const u8 BattleScript_ItemHealHP_RemoveItem[]; +extern const u8 BattleScript_BerryPPHeal[]; extern const u8 BattleScript_ItemHealHP_End2[]; extern const u8 BattleScript_ItemHealHP_Ret[]; extern const u8 BattleScript_SelectingNotAllowedMoveChoiceItem[]; extern const u8 BattleScript_SelectingNotAllowedMoveChoiceItemInPalace[]; extern const u8 BattleScript_HangedOnMsg[]; -extern const u8 BattleScript_BerryConfuseHealEnd2[]; -extern const u8 BattleScript_BerryConfuseHealRet[]; -extern const u8 BattleScript_ConsumableStatRaiseEnd2[]; +extern const u8 BattleScript_BerryConfuseHeal[]; extern const u8 BattleScript_ConsumableStatRaiseRet[]; -extern const u8 BattleScript_BerryFocusEnergyRet[]; -extern const u8 BattleScript_BerryFocusEnergyEnd2[]; +extern const u8 BattleScript_BerryFocusEnergy[]; extern const u8 BattleScript_ActionSelectionItemsCantBeUsed[]; extern const u8 BattleScript_AbilityAvoidsDamage[]; extern const u8 BattleScript_AbilityShieldProtects[]; @@ -272,7 +260,6 @@ extern const u8 BattleScript_AttackerAbilityStatRaiseEnd2[]; extern const u8 BattleScript_PoisonHealActivates[]; extern const u8 BattleScript_BadDreamsActivates[]; extern const u8 BattleScript_SwitchInAbilityMsg[]; -extern const u8 BattleScript_SwitchInAbilityMsgRet[]; extern const u8 BattleScript_ToxicSpikesPoisoned[]; extern const u8 BattleScript_ToxicSpikesBadlyPoisoned[]; extern const u8 BattleScript_ToxicSpikesAbsorbed[]; @@ -344,7 +331,6 @@ extern const u8 BattleScript_WeaknessPolicy[]; extern const u8 BattleScript_TargetItemStatRaise[]; extern const u8 BattleScript_RockyHelmetActivates[]; extern const u8 BattleScript_ItemHurtEnd2[]; -extern const u8 BattleScript_AirBalloonMsgIn[]; extern const u8 BattleScript_AirBalloonMsgInRet[]; extern const u8 BattleScript_AirBalloonMsgPop[]; extern const u8 BattleScript_ItemHurtRet[]; @@ -353,15 +339,12 @@ extern const u8 BattleScript_FlameOrb[]; extern const u8 BattleScript_MoveEffectIncinerate[]; extern const u8 BattleScript_MoveEffectBugBite[]; extern const u8 BattleScript_IllusionOff[]; -extern const u8 BattleScript_IllusionOffEnd3[]; extern const u8 BattleScript_IllusionOffAndTerastallization[]; extern const u8 BattleScript_DancerActivates[]; extern const u8 BattleScript_AftermathDmg[]; extern const u8 BattleScript_BattlerFormChange[]; extern const u8 BattleScript_BattlerFormChangeEnd2[]; -extern const u8 BattleScript_BattlerFormChangeEnd3[]; extern const u8 BattleScript_AttackerFormChangeWithString[]; -extern const u8 BattleScript_BattlerFormChangeWithStringEnd3[]; extern const u8 BattleScript_TargetFormChange[]; extern const u8 BattleScript_AnticipationActivates[]; extern const u8 BattleScript_SlowStartEnds[]; @@ -373,9 +356,7 @@ extern const u8 BattleScript_FriskMsg[]; extern const u8 BattleScript_FriskMsgWithPopup[]; extern const u8 BattleScript_MoodyActivates[]; extern const u8 BattleScript_EmergencyExit[]; -extern const u8 BattleScript_EmergencyExitWild[]; extern const u8 BattleScript_EmergencyExitEnd2[]; -extern const u8 BattleScript_EmergencyExitWildEnd2[]; extern const u8 BattleScript_CheekPouchActivates[]; extern const u8 BattleScript_TotemVar[]; extern const u8 BattleScript_TotemFlaredToLife[]; @@ -392,7 +373,7 @@ extern const u8 BattleScript_QuickClawActivation[]; extern const u8 BattleScript_QuickDrawActivation[]; extern const u8 BattleScript_CustapBerryActivation[]; extern const u8 BattleScript_MicleBerryActivateEnd2[]; -extern const u8 BattleScript_MicleBerryActivateRet[]; +extern const u8 BattleScript_MicleBerryActivate[]; extern const u8 BattleScript_JabocaRowapBerryActivates[]; extern const u8 BattleScript_NotAffectedAbilityPopUp[]; extern const u8 BattleScript_BattlerShookOffTaunt[]; @@ -405,7 +386,6 @@ extern const u8 BattleScript_RedCardActivates[]; extern const u8 BattleScript_EjectButtonActivates[]; extern const u8 BattleScript_EjectPackActivate_Ret[]; extern const u8 BattleScript_EjectPackActivate_End2[]; -extern const u8 BattleScript_EjectPackActivate_End3[]; extern const u8 BattleScript_EjectPackActivates[]; extern const u8 BattleScript_MentalHerbCureRet[]; extern const u8 BattleScript_MentalHerbCureEnd2[]; @@ -425,8 +405,7 @@ extern const u8 BattleScript_PrimalWeatherBlocksMove[]; extern const u8 BattleScript_DeltaStreamActivates[]; extern const u8 BattleScript_MysteriousAirCurrentBlowsOn[]; extern const u8 BattleScript_AttackWeakenedByStrongWinds[]; -extern const u8 BattleScript_BlockedByPrimalWeatherEnd3[]; -extern const u8 BattleScript_BlockedByPrimalWeatherRet[]; +extern const u8 BattleScript_BlockedByPrimalWeather[]; extern const u8 BattleScript_PrimalReversion[]; extern const u8 BattleScript_HyperspaceFuryRemoveProtect[]; extern const u8 BattleScript_SelectingNotAllowedMoveGorillaTactics[]; @@ -472,7 +451,7 @@ extern const u8 BattleScript_ToxicDebrisActivates[]; extern const u8 BattleScript_EarthEaterActivates[]; extern const u8 BattleScript_MimicryActivates[]; extern const u8 BattleScript_IceFaceNullsDamage[]; -extern const u8 BattleScript_BattlerFormChangeWithStringEnd3[]; +extern const u8 BattleScript_BattlerFormChangeWithString[]; extern const u8 BattleScript_DampPreventsAftermath[]; extern const u8 BattleScript_HealingWishActivates[]; extern const u8 BattleScript_LunarDanceActivates[]; @@ -482,7 +461,6 @@ extern const u8 BattleScript_CouldntFullyProtect[]; extern const u8 BattleScript_MoveEffectStockpileWoreOff[]; extern const u8 BattleScript_SpikesActivates[]; extern const u8 BattleScript_BerserkGeneRet[]; -extern const u8 BattleScript_BerserkGeneRetEnd2[]; extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[]; extern const u8 BattleScript_DefDown[]; extern const u8 BattleScript_UltraBurst[]; @@ -502,7 +480,6 @@ extern const u8 BattleScript_EffectPsychicNoise[]; extern const u8 BattleScript_AromaVeilProtectsRet[]; extern const u8 BattleScript_LowerAtkSpAtk[]; extern const u8 BattleScript_Terastallization[]; -extern const u8 BattleScript_BoosterEnergyEnd2[]; extern const u8 BattleScript_BoosterEnergyRet[]; extern const u8 BattleScript_TeraShellDistortingTypeMatchups[]; extern const u8 BattleScript_TeraFormChange[]; @@ -520,6 +497,7 @@ extern const u8 BattleScript_AlreadyParalyzed[]; extern const u8 BattleScript_AlreadyBurned[]; extern const u8 BattleScript_PrintAbilityMadeIneffective[]; extern const u8 BattleScript_ItDoesntAffectFoe[]; +extern const u8 BattleScript_FirstTurnSwitchInEvents[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/battle_switch_in.h b/include/battle_switch_in.h new file mode 100644 index 0000000000..fcbd20c72f --- /dev/null +++ b/include/battle_switch_in.h @@ -0,0 +1,8 @@ +#ifndef GUARD_BATTLE_SWITCH_IN +#define GUARD_BATTLE_SWITCH_IN + +#include "constants/battle_switch_in.h" + +bool32 DoSwitchInEvents(void); + +#endif // GUARD_BATTLE_SWITCH_IN diff --git a/include/battle_util.h b/include/battle_util.h index 36ca129206..be044b4ac3 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -52,19 +52,21 @@ enum AbilityEffect ABILITYEFFECT_ON_SWITCHIN, ABILITYEFFECT_ENDTURN, ABILITYEFFECT_MOVE_END_ATTACKER, - ABILITYEFFECT_COLOR_CHANGE, // Color Change, Berserk, Anger Shell + ABILITYEFFECT_COLOR_CHANGE, // Color Change / Berserk / Anger Shell ABILITYEFFECT_MOVE_END, ABILITYEFFECT_IMMUNITY, ABILITYEFFECT_SYNCHRONIZE, ABILITYEFFECT_ATK_SYNCHRONIZE, ABILITYEFFECT_MOVE_END_OTHER, + + // On Switch in + ABILITYEFFECT_TERA_SHIFT, ABILITYEFFECT_NEUTRALIZINGGAS, - ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, + ABILITYEFFECT_UNNERVE, + ABILITYEFFECT_COMMANDER, // Commander / Hospitality / Costar ABILITYEFFECT_ON_WEATHER, ABILITYEFFECT_ON_TERRAIN, ABILITYEFFECT_OPPORTUNIST, - ABILITYEFFECT_OPPORTUNIST_FIRST_TURN, - ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, }; enum ItemEffect @@ -207,7 +209,7 @@ enum SkyDropState enum EjectPackTiming { - FIRST_TURN, + START_OF_TURN, END_TURN, OTHER, }; @@ -263,7 +265,7 @@ bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag); bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, u32 move, enum FunctionCallOption option); bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, u32 move, enum Type moveType, enum FunctionCallOption option); bool32 TryFieldEffects(enum FieldEffectCases caseId); -u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, u32 special, u32 moveArg); +u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, u32 move, bool32 shouldAbilityTrigger); bool32 TryPrimalReversion(u32 battler); bool32 IsNeutralizingGasOnField(void); bool32 IsMoldBreakerTypeAbility(u32 battler, enum Ability ability); @@ -322,13 +324,13 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); u32 GetBattlerVisualSpecies(u32 battler); -bool32 TryClearIllusion(u32 battler, enum AbilityEffect caseID); +bool32 TryClearIllusion(u32 battler, enum Ability ability); u32 GetIllusionMonSpecies(u32 battler); struct Pokemon *GetIllusionMonPtr(u32 battler); void ClearIllusionMon(u32 battler); u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler); bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); -u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID); +u32 TryImmunityAbilityHealStatus(u32 battler); bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); uq4_12_t GetBadgeBoostModifier(void); enum DamageCategory GetBattleMoveCategory(u32 move); @@ -347,7 +349,7 @@ void TryRestoreHeldItems(void); bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item); void TrySaveExchangedItem(u32 battler, u16 stolenItem); bool32 IsPartnerMonFromSameTrainer(u32 battler); -bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes); +bool32 IsBattlerAffectedByHazards(u32 battler, enum HoldEffect holdEffect, bool32 toxicSpikes); void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); bool32 CompareStat(u32 battler, enum Stat statId, u8 cmpTo, u8 cmpKind, enum Ability ability); bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); @@ -415,6 +417,8 @@ bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); void ChooseStatBoostAnimation(u32 battler); void UpdateStallMons(void); bool32 TrySwitchInEjectPack(enum EjectPackTiming timing); +bool32 TryEmergencyExit(void); +bool32 EmergencyExitCanBeTriggered(u32 battler); u32 GetBattlerVolatile(u32 battler, enum Volatile _volatile); void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue); bool32 ItemHealMonVolatile(u32 battler, u16 itemId); diff --git a/include/constants/battle.h b/include/constants/battle.h index 8dacb7a60a..2437ce8df8 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -218,7 +218,7 @@ enum VolatileFlags F(VOLATILE_VESSEL_OF_RUIN, vesselOfRuin, (u32, 1)) \ F(VOLATILE_SWORD_OF_RUIN, swordOfRuin, (u32, 1)) \ F(VOLATILE_TABLETS_OF_RUIN, tabletsOfRuin, (u32, 1)) \ - F(VOLATILE_BEADS_OF_RUIN, beadsOfRuin, (u32, 1)) + F(VOLATILE_BEADS_OF_RUIN, beadsOfRuin, (u32, 1)) /* Use within a macro to get the maximum allowed value for a volatile. Requires _typeMaxValue as input. */ diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 234ebad0b2..953f6533b7 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -88,17 +88,10 @@ #define CMP_COMMON_BITS 4 #define CMP_NO_COMMON_BITS 5 -// Veriouses have been deprecated but the enum and function will be supported for one more release cycle -enum CmdVarious -{ - VARIOUS_NONE, -}; - // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 1 #define DMG_1_8_TARGET_HP 2 -#define DMG_FULL_ATTACKER_HP 3 -#define DMG_BIG_ROOT 4 +#define DMG_BIG_ROOT 3 // Cmd_jumpifcantswitch #define SWITCH_IGNORE_ESCAPE_PREVENTION (1 << 7) @@ -189,10 +182,12 @@ enum MoveEndEffects MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE = (MOVEEND_HIT_ESCAPE + 1), }; -// switch cases -#define B_SWITCH_NORMAL 0 -#define B_SWITCH_HIT 1 // dragon tail, circle throw -#define B_SWITCH_RED_CARD 2 +enum SwitchInCases +{ + B_SWITCH_NORMAL, + B_SWITCH_HIT, // dragon tail, circle throw + B_SWITCH_RED_CARD, +}; enum StatusTrigger { diff --git a/include/constants/battle_switch_in.h b/include/constants/battle_switch_in.h new file mode 100644 index 0000000000..3a3084472b --- /dev/null +++ b/include/constants/battle_switch_in.h @@ -0,0 +1,37 @@ +#ifndef GUARD_CONSTANTS_BATTLE_SWITCH_IN_H +#define GUARD_CONSTANTS_BATTLE_SWITCH_IN_H + +enum SwitchInEvents +{ + SWITCH_IN_EVENTS_ORDER_BY_SPEED, + SWITCH_IN_EVENTS_TERA_SHIFT, + SWITCH_IN_EVENTS_NEUTRALIZING_GAS, + SWITCH_IN_EVENTS_UNNERVE, + SWITCH_IN_EVENTS_FIRST_BLOCK, + SWITCH_IN_EVENTS_SECOND_BLOCK, + SWITCH_IN_EVENTS_WHITE_HERB, + SWITCH_IN_EVENTS_OPPORTUNIST, + SWITCH_IN_EVENTS_MIRROR_HERB, + SWITCH_IN_EVENTS_CLEAR_SET_VALUES, + SWITCH_IN_EVENTS_EJECT_PACK, + SWITCH_IN_EVENTS_COUNT, +}; + +enum SwitchInFirstEventBlock +{ + FIRST_EVENT_BLOCK_HEALING_WISH, + FIRST_EVENT_BLOCK_HAZARDS, + FIRST_EVENT_BLOCK_GENERAL_ABILITIES, + FIRST_EVENT_BLOCK_IMMUNITY_ABILITIES, + FIRST_EVENT_BLOCK_ITEMS, + FIRST_EVENT_BLOCK_COUNT, +}; + +enum SwitchInSecondEventBlock +{ + SECOND_EVENT_ABILITIES, + SECOND_EVENT_BOOSTER_ENERGY, + SECOND_EVENT_BLOCK_COUNT, +}; + +#endif // GUARD_CONSTANTS_BATTLE_SWITCH_IN_H diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index b1e61e3493..53c7b25276 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -111,12 +111,12 @@ u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario) static bool32 IsAceMon(u32 battler, u32 monPartyId) { if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ACE_POKEMON - && !gBattleStruct->battlerState[battler].forcedSwitch - && monPartyId == CalculateEnemyPartyCountInSide(battler)-1) + && !gProtectStructs[battler].forcedSwitch + && monPartyId == CalculateEnemyPartyCountInSide(battler)-1) return TRUE; if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON - && !gBattleStruct->battlerState[battler].forcedSwitch - && (monPartyId == CalculateEnemyPartyCount()-1 || monPartyId == CalculateEnemyPartyCount()-2)) + && !gProtectStructs[battler].forcedSwitch + && (monPartyId == CalculateEnemyPartyCount()-1 || monPartyId == CalculateEnemyPartyCount()-2)) return TRUE; return FALSE; } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index d5edbc0e61..4a0da2d8a0 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -190,7 +190,7 @@ void ActivateDynamax(u32 battler) if (!gBattleMons[battler].volatiles.transformed) // Ditto cannot Gigantamax. TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX); - BattleScriptExecute(BattleScript_DynamaxBegins); + BattleScriptPushCursorAndCallback(BattleScript_DynamaxBegins); } // Unsets the flags used for Dynamaxing and reverts max HP if needed. diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index b4d71c2cf4..4932baf275 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -123,7 +123,7 @@ static bool32 HandleEndTurnWeatherDamage(u32 battler) case BATTLE_WEATHER_RAIN_DOWNPOUR: if (ability == ABILITY_DRY_SKIN || ability == ABILITY_RAIN_DISH) { - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; } break; @@ -131,7 +131,7 @@ static bool32 HandleEndTurnWeatherDamage(u32 battler) case BATTLE_WEATHER_SUN_PRIMAL: if (ability == ABILITY_DRY_SKIN || ability == ABILITY_SOLAR_POWER) { - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; } break; @@ -156,7 +156,7 @@ static bool32 HandleEndTurnWeatherDamage(u32 battler) case BATTLE_WEATHER_SNOW: if (ability == ABILITY_ICE_BODY) { - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; } else if (currBattleWeather == BATTLE_WEATHER_HAIL) @@ -192,12 +192,8 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler) { gBattlerAbility = battler; gLastUsedAbility = ability; - - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) - BattleScriptExecute(BattleScript_EmergencyExitEnd2); - else - BattleScriptExecute(BattleScript_EmergencyExitWildEnd2); - + gBattleScripting.battler = battler; + BattleScriptExecute(BattleScript_EmergencyExitEnd2); effect = TRUE; } @@ -380,7 +376,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler) case ABILITY_HEALER: case ABILITY_HYDRATION: case ABILITY_SHED_SKIN: - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; break; default: @@ -1281,7 +1277,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler) case ABILITY_MOODY: case ABILITY_PICKUP: case ABILITY_SPEED_BOOST: - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; break; default: @@ -1333,7 +1329,7 @@ static bool32 HandleEndTurnFormChangeAbilities(u32 battler) case ABILITY_SHIELDS_DOWN: case ABILITY_ZEN_MODE: case ABILITY_HUNGER_SWITCH: - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE)) + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) effect = TRUE; default: break; diff --git a/src/battle_hold_effects.c b/src/battle_hold_effects.c index 9f363ea120..5216b2652e 100644 --- a/src/battle_hold_effects.c +++ b/src/battle_hold_effects.c @@ -12,11 +12,8 @@ #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; } @@ -29,6 +26,7 @@ bool32 IsOrbsActivation(enum HoldEffect holdEffect) { return gHol bool32 IsOnEffectActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onEffect; } bool32 IsOnBerryActivation(enum HoldEffect holdEffect) { return GetItemPocket(gLastUsedItem) == POCKET_BERRIES; } bool32 IsOnFlingActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].onFling; } +bool32 IsBoosterEnergyActivation(enum HoldEffect holdEffect) { return gHoldEffectsInfo[holdEffect].boosterEnergy; } bool32 IsForceTriggerItemActivation(enum HoldEffect holdEffect) { @@ -51,7 +49,7 @@ static enum ItemEffect TryDoublePrize(u32 battler) return effect; } -enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTiming timing) +enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -66,17 +64,14 @@ enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTi gBattlerAbility = gBattleScripting.battler = battler; gDisableStructs[battler].boosterEnergyActivated = TRUE; RecordAbilityBattle(battler, ability); - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BoosterEnergyEnd2); - else - BattleScriptCall(BattleScript_BoosterEnergyRet); + BattleScriptCall(BattleScript_BoosterEnergyRet); effect = ITEM_EFFECT_OTHER; } return effect; } -static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing) +static enum ItemEffect TryRoomService(u32 battler) { if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, GetBattlerAbility(battler))) { @@ -84,19 +79,15 @@ static enum ItemEffect TryRoomService(u32 battler, ActivationTiming timing) SET_STATCHANGER(STAT_SPEED, 1, TRUE); gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_SPEED; gBattleScripting.animArg2 = 0; - - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - else - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); - + gLastUsedItem = gBattleMons[battler].item; + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); return ITEM_STATS_CHANGE; } return ITEM_NO_EFFECT; } -enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, enum Stat statId, ActivationTiming timing) +enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, enum Stat statId) { if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battler))) { @@ -104,32 +95,29 @@ enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, enum Stat statId, Ac SET_STATCHANGER(statId, 1, FALSE); gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId; gBattleScripting.animArg2 = 0; - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_ConsumableStatRaiseEnd2); - else - BattleScriptCall(BattleScript_ConsumableStatRaiseRet); + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); return ITEM_STATS_CHANGE; } return ITEM_NO_EFFECT; } -static enum ItemEffect TryTerrainSeeds(u32 battler, u32 item, ActivationTiming timing) +static enum ItemEffect TryTerrainSeeds(u32 battler, u32 item) { enum ItemEffect effect = ITEM_NO_EFFECT; switch (GetItemHoldEffectParam(item)) { case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, timing); + effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF); break; case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, timing); + effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF); break; case HOLD_EFFECT_PARAM_MISTY_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, timing); + effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF); break; case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: - effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, timing); + effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF); break; } @@ -146,7 +134,7 @@ static bool32 CanBeInfinitelyConfused(u32 battler) return TRUE; } -static enum ItemEffect TryBerserkGene(u32 battler, ActivationTiming timing) +static enum ItemEffect TryBerserkGene(u32 battler) { if (CanBeInfinitelyConfused(battler)) gBattleMons[battler].volatiles.infiniteConfusion = TRUE; @@ -154,11 +142,7 @@ static enum ItemEffect TryBerserkGene(u32 battler, ActivationTiming timing) 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); - + BattleScriptCall(BattleScript_BerserkGeneRet); return ITEM_STATS_CHANGE; } @@ -185,7 +169,7 @@ static enum ItemEffect RestoreWhiteHerbStats(u32 battler, ActivationTiming timin return effect; } -static enum ItemEffect TryConsumeMirrorHerb(u32 battler, ActivationTiming timing) +static enum ItemEffect TryConsumeMirrorHerb(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -193,10 +177,7 @@ static enum ItemEffect TryConsumeMirrorHerb(u32 battler, ActivationTiming timing { gProtectStructs[battler].eatMirrorHerb = 0; ChooseStatBoostAnimation(battler); - if (timing == IsMirrorHerbFirstTurnActivation) - BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2); - else - BattleScriptCall(BattleScript_MirrorHerbCopyStatChange); + BattleScriptCall(BattleScript_MirrorHerbCopyStatChange); effect = ITEM_STATS_CHANGE; } @@ -244,10 +225,7 @@ static enum ItemEffect TryAirBalloon(u32 battler, ActivationTiming timing) else if (!gSpecialStatuses[battler].switchInItemDone) { gSpecialStatuses[battler].switchInItemDone = TRUE; - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptPushCursorAndCallback(BattleScript_AirBalloonMsgIn); - else - BattleScriptCall(BattleScript_AirBalloonMsgInRet); + BattleScriptCall(BattleScript_AirBalloonMsgInRet); RecordItemEffectBattle(battler, HOLD_EFFECT_AIR_BALLOON); effect = ITEM_EFFECT_OTHER; } @@ -411,7 +389,7 @@ static enum ItemEffect TrySetEnigmaBerry(u32 battlerDef, u32 battlerAtk) if (GetBattlerAbility(battlerDef) == ABILITY_RIPEN) healAmount *= 2; SetHealAmount(battlerDef, healAmount); - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); + BattleScriptCall(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } @@ -677,7 +655,7 @@ static enum ItemEffect TryBlackSludgeDamage(u32 battler, enum HoldEffect holdEff return effect; } -static enum ItemEffect TryCureParalysis(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureParalysis(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -685,17 +663,14 @@ static enum ItemEffect TryCureParalysis(u32 battler, ActivationTiming timing) { gBattleMons[battler].status1 &= ~STATUS1_PARALYSIS; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PARALYSIS; - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryCureStatusEnd2); - else - BattleScriptCall(BattleScript_BerryCureStatusRet); + BattleScriptCall(BattleScript_BerryCureStatusRet); effect = ITEM_STATUS_CHANGE; } return effect; } -static enum ItemEffect TryCurePoison(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCurePoison(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -703,17 +678,14 @@ static enum ItemEffect TryCurePoison(u32 battler, ActivationTiming timing) { 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); + BattleScriptCall(BattleScript_BerryCureStatusRet); effect = ITEM_STATUS_CHANGE; } return effect; } -static enum ItemEffect TryCureBurn(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureBurn(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -721,17 +693,14 @@ static enum ItemEffect TryCureBurn(u32 battler, ActivationTiming timing) { gBattleMons[battler].status1 &= ~STATUS1_BURN; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_BURN; - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryCureStatusEnd2); - else - BattleScriptCall(BattleScript_BerryCureStatusRet); + BattleScriptCall(BattleScript_BerryCureStatusRet); effect = ITEM_STATUS_CHANGE; } return effect; } -static enum ItemEffect TryCureFreezeOrFrostbite(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureFreezeOrFrostbite(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -749,17 +718,12 @@ static enum ItemEffect TryCureFreezeOrFrostbite(u32 battler, ActivationTiming ti } if (effect == ITEM_STATUS_CHANGE) - { - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryCureStatusEnd2); - else - BattleScriptCall(BattleScript_BerryCureStatusRet); - } + BattleScriptCall(BattleScript_BerryCureStatusRet); return effect; } -static enum ItemEffect TryCureSleep(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureSleep(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -769,34 +733,28 @@ static enum ItemEffect TryCureSleep(u32 battler, ActivationTiming timing) 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); + BattleScriptCall(BattleScript_BerryCureStatusRet); effect = ITEM_STATUS_CHANGE; } return effect; } -static enum ItemEffect TryCureConfusion(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureConfusion(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; if (gBattleMons[battler].volatiles.confusionTurns > 0) { RemoveConfusionStatus(battler); - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); - else - BattleScriptCall(BattleScript_BerryCureConfusionRet); + BattleScriptCall(BattleScript_BerryCureConfusionRet); effect = ITEM_EFFECT_OTHER; } return effect; } -static enum ItemEffect TryCureAnyStatus(u32 battler, ActivationTiming timing) +static enum ItemEffect TryCureAnyStatus(u32 battler) { enum ItemEffect effect = ITEM_NO_EFFECT; u32 string = 0; @@ -841,10 +799,7 @@ static enum ItemEffect TryCureAnyStatus(u32 battler, ActivationTiming timing) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_NORMALIZED_STATUS; gBattleMons[battler].status1 = 0; RemoveConfusionStatus(battler); - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryCureStatusEnd2); - else - BattleScriptCall(BattleScript_BerryCureStatusRet); + BattleScriptCall(BattleScript_BerryCureStatusRet); effect = ITEM_STATUS_CHANGE; } @@ -857,7 +812,7 @@ enum HealAmount PERCENT_HEAL_AMOUNT, }; -static u32 ItemHealHp(u32 battler, u32 itemId, enum HealAmount percentHeal, ActivationTiming timing) +static u32 ItemHealHp(u32 battler, u32 itemId, enum HealAmount percentHeal) { enum ItemEffect effect = ITEM_NO_EFFECT; enum Ability ability = GetBattlerAbility(battler); @@ -876,18 +831,14 @@ static u32 ItemHealHp(u32 battler, u32 itemId, enum HealAmount percentHeal, Acti healAmount *= 2; SetHealAmount(battler, healAmount); - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); - else - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); - + BattleScriptCall(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } return effect; } -static u32 ItemRestorePp(u32 battler, u32 itemId, ActivationTiming timing) +static u32 ItemRestorePp(u32 battler, u32 itemId) { enum ItemEffect effect = ITEM_NO_EFFECT; struct Pokemon *mon = GetBattlerMon(battler); @@ -915,12 +866,7 @@ static u32 ItemRestorePp(u32 battler, u32 itemId, ActivationTiming timing) changedPP = currentPP + ppRestored; PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); - - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryPPHealEnd2); - else - BattleScriptCall(BattleScript_BerryPPHealRet); - + BattleScriptCall(BattleScript_BerryPPHeal); gBattleScripting.battler = battler; BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); MarkBattlerForControllerExec(battler); @@ -932,7 +878,7 @@ static u32 ItemRestorePp(u32 battler, u32 itemId, ActivationTiming timing) return effect; } -static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, ActivationTiming timing) +static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId) { enum ItemEffect effect = ITEM_NO_EFFECT; u32 hpFraction = B_CONFUSE_BERRIES_HEAL >= GEN_7 ? 4 : 2; @@ -945,28 +891,17 @@ static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, A if (ability == ABILITY_RIPEN) healAmount *= 2; SetHealAmount(battler, healAmount); - if (timing == IsOnSwitchInFirstTurnActivation) - { - if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) - BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); - else - BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); - } + if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) + BattleScriptCall(BattleScript_BerryConfuseHeal); else - { - if (GetFlavorRelationByPersonality(gBattleMons[battler].personality, flavorId) < 0) - BattleScriptCall(BattleScript_BerryConfuseHealRet); - else - BattleScriptCall(BattleScript_ItemHealHP_RemoveItemRet); - } - PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, flavorId); + BattleScriptCall(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } return effect; } -static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, enum Stat statId, ActivationTiming timing) +static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, enum Stat statId) { enum ItemEffect effect = ITEM_NO_EFFECT; enum Ability ability = GetBattlerAbility(battler); @@ -978,18 +913,14 @@ static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, enum Stat statId, 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); + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); effect = ITEM_STATS_CHANGE; } return effect; } -static enum ItemEffect CriticalHitRatioUp(u32 battler, u32 itemId, ActivationTiming timing) +static enum ItemEffect CriticalHitRatioUp(u32 battler, u32 itemId) { enum ItemEffect effect = ITEM_NO_EFFECT; @@ -998,17 +929,14 @@ static enum ItemEffect CriticalHitRatioUp(u32 battler, u32 itemId, ActivationTim && HasEnoughHpToEatBerry(battler, GetBattlerAbility(battler), GetItemHoldEffectParam(itemId), itemId)) { gBattleMons[battler].volatiles.focusEnergy = TRUE; - if (timing == IsOnSwitchInFirstTurnActivation) - BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); - else - BattleScriptCall(BattleScript_BerryFocusEnergyRet); + BattleScriptCall(BattleScript_BerryFocusEnergy); effect = ITEM_EFFECT_OTHER; } return effect; } -static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, ActivationTiming timing) +static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId) { enum ItemEffect effect = ITEM_NO_EFFECT; enum Stat stat; @@ -1039,27 +967,21 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, ActivationT 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); + BattleScriptCall(BattleScript_ConsumableStatRaiseRet); effect = ITEM_STATS_CHANGE; } return effect; } -static enum ItemEffect TrySetMicleBerry(u32 battler, u32 itemId, ActivationTiming timing) +static enum ItemEffect TrySetMicleBerry(u32 battler, u32 itemId) { 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); + BattleScriptCall(BattleScript_MicleBerryActivate); effect = ITEM_EFFECT_OTHER; } return effect; @@ -1092,22 +1014,22 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect effect = TryDoublePrize(itemBattler); break; case HOLD_EFFECT_ROOM_SERVICE: - effect = TryRoomService(itemBattler, timing); + effect = TryRoomService(itemBattler); break; case HOLD_EFFECT_TERRAIN_SEED: - effect = TryTerrainSeeds(itemBattler, item, timing); + effect = TryTerrainSeeds(itemBattler, item); break; case HOLD_EFFECT_BERSERK_GENE: - effect = TryBerserkGene(itemBattler, timing); + effect = TryBerserkGene(itemBattler); break; case HOLD_EFFECT_BOOSTER_ENERGY: - effect = TryBoosterEnergy(itemBattler, GetBattlerAbility(itemBattler), timing); + effect = TryBoosterEnergy(itemBattler, GetBattlerAbility(itemBattler)); break; case HOLD_EFFECT_WHITE_HERB: effect = RestoreWhiteHerbStats(itemBattler, timing); break; case HOLD_EFFECT_MIRROR_HERB: - effect = TryConsumeMirrorHerb(itemBattler, timing); + effect = TryConsumeMirrorHerb(itemBattler); break; case HOLD_EFFECT_FLINCH: // Kings Rock effect = TryKingsRock(itemBattler, battler, item); @@ -1185,73 +1107,73 @@ enum ItemEffect ItemBattleEffects(u32 itemBattler, u32 battler, enum HoldEffect effect = TryBlackSludgeDamage(itemBattler, holdEffect); break; case HOLD_EFFECT_CURE_PAR: // Cheri Berry - effect = TryCureParalysis(itemBattler, timing); + effect = TryCureParalysis(itemBattler); break; case HOLD_EFFECT_CURE_PSN: // Pecha Berry - effect = TryCurePoison(itemBattler, timing); + effect = TryCurePoison(itemBattler); break; case HOLD_EFFECT_CURE_BRN: // Rawst Berry - effect = TryCureBurn(itemBattler, timing); + effect = TryCureBurn(itemBattler); break; case HOLD_EFFECT_CURE_FRZ: // Aspear Berry - effect = TryCureFreezeOrFrostbite(itemBattler, timing); + effect = TryCureFreezeOrFrostbite(itemBattler); break; case HOLD_EFFECT_CURE_SLP: // Chesto Berry - effect = TryCureSleep(itemBattler, timing); + effect = TryCureSleep(itemBattler); break; case HOLD_EFFECT_CURE_CONFUSION: // Persim Berry - effect = TryCureConfusion(itemBattler, timing); + effect = TryCureConfusion(itemBattler); break; case HOLD_EFFECT_CURE_STATUS: // Lum Berry - effect = TryCureAnyStatus(itemBattler, timing); + effect = TryCureAnyStatus(itemBattler); break; case HOLD_EFFECT_RESTORE_HP: // Oran / Sitrus Berry / Berry Juice - effect = ItemHealHp(itemBattler, item, FIXED_HEAL_AMOUNT, timing); + effect = ItemHealHp(itemBattler, item, FIXED_HEAL_AMOUNT); break; case HOLD_EFFECT_RESTORE_PCT_HP: // Sitrus Berry - effect = ItemHealHp(itemBattler, item, PERCENT_HEAL_AMOUNT, timing); + effect = ItemHealHp(itemBattler, item, PERCENT_HEAL_AMOUNT); break; case HOLD_EFFECT_RESTORE_PP: // Leppa Berry - effect = ItemRestorePp(itemBattler, item, timing); + effect = ItemRestorePp(itemBattler, item); break; case HOLD_EFFECT_CONFUSE_SPICY: // Figy Berry - effect = HealConfuseBerry(itemBattler, item, FLAVOR_SPICY, timing); + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SPICY); break; case HOLD_EFFECT_CONFUSE_DRY: // Wiki Berry - effect = HealConfuseBerry(itemBattler, item, FLAVOR_DRY, timing); + effect = HealConfuseBerry(itemBattler, item, FLAVOR_DRY); break; case HOLD_EFFECT_CONFUSE_SWEET: // Mago Berry - effect = HealConfuseBerry(itemBattler, item, FLAVOR_SWEET, timing); + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SWEET); break; case HOLD_EFFECT_CONFUSE_BITTER: // Aguav Berry - effect = HealConfuseBerry(itemBattler, item, FLAVOR_BITTER, timing); + effect = HealConfuseBerry(itemBattler, item, FLAVOR_BITTER); break; case HOLD_EFFECT_CONFUSE_SOUR: // Iapapa Berry - effect = HealConfuseBerry(itemBattler, item, FLAVOR_SOUR, timing); + effect = HealConfuseBerry(itemBattler, item, FLAVOR_SOUR); break; case HOLD_EFFECT_ATTACK_UP: // Liechi Berry - effect = StatRaiseBerry(itemBattler, item, STAT_ATK, timing); + effect = StatRaiseBerry(itemBattler, item, STAT_ATK); break; case HOLD_EFFECT_DEFENSE_UP: // Ganlon Berry - effect = StatRaiseBerry(itemBattler, item, STAT_DEF, timing); + effect = StatRaiseBerry(itemBattler, item, STAT_DEF); break; case HOLD_EFFECT_SPEED_UP: // Salac Berry - effect = StatRaiseBerry(itemBattler, item, STAT_SPEED, timing); + effect = StatRaiseBerry(itemBattler, item, STAT_SPEED); break; case HOLD_EFFECT_SP_ATTACK_UP: // Petaya Berry - effect = StatRaiseBerry(itemBattler, item, STAT_SPATK, timing); + effect = StatRaiseBerry(itemBattler, item, STAT_SPATK); break; case HOLD_EFFECT_SP_DEFENSE_UP: // Apicot Berry - effect = StatRaiseBerry(itemBattler, item, STAT_SPDEF, timing); + effect = StatRaiseBerry(itemBattler, item, STAT_SPDEF); break; case HOLD_EFFECT_CRITICAL_UP: // Lansat Berry - effect = CriticalHitRatioUp(itemBattler, item, timing); + effect = CriticalHitRatioUp(itemBattler, item); break; case HOLD_EFFECT_RANDOM_STAT_UP: // Starf Berry - effect = RandomStatRaiseBerry(itemBattler, item, timing); + effect = RandomStatRaiseBerry(itemBattler, item); break; case HOLD_EFFECT_MICLE_BERRY: - effect = TrySetMicleBerry(itemBattler, item, timing); + effect = TrySetMicleBerry(itemBattler, item); break; default: break; diff --git a/src/battle_main.c b/src/battle_main.c index a0d578bf33..1d14ae29f2 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -127,7 +127,6 @@ static void HandleEndTurn_MonFled(void); static void HandleEndTurn_FinishBattle(void); static u32 Crc32B (const u8 *data, u32 size); static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i); -static s32 Factorial(s32); EWRAM_DATA u16 gBattle_BG0_X = 0; EWRAM_DATA u16 gBattle_BG0_Y = 0; @@ -157,6 +156,7 @@ EWRAM_DATA u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gBattlerPositions[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gActionsByTurnOrder[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA u8 gBattlersBySpeed[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gCurrentTurnActionNumber = 0; EWRAM_DATA u8 gCurrentActionFuncId = 0; EWRAM_DATA struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT] = {0}; @@ -208,12 +208,10 @@ EWRAM_DATA u8 gSentPokesToOpponent[2] = {0}; EWRAM_DATA struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA struct BattleScripting gBattleScripting = {0}; EWRAM_DATA struct BattleStruct *gBattleStruct = NULL; - EWRAM_DATA struct AiThinkingStruct *gAiThinkingStruct = NULL; EWRAM_DATA struct AiLogicData *gAiLogicData = NULL; EWRAM_DATA struct AiPartyData *gAiPartyData = NULL; EWRAM_DATA struct BattleHistory *gBattleHistory = NULL; - EWRAM_DATA struct AiBattleData *gAiBattleData = NULL; EWRAM_DATA u8 *gLinkBattleSendBuffer = NULL; @@ -3242,7 +3240,6 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gBattleStruct->battlerState[battler].canPickupItem = FALSE; gBattleStruct->battlerState[battler].wasAboveHalfHp = gBattleMons[battler].hp > gBattleMons[battler].maxHP / 2; gBattleStruct->hazardsCounter = 0; - gDisableStructs[battler].hazardsDone = FALSE; gSpecialStatuses[battler].switchInItemDone = FALSE; ClearPursuitValuesIfSet(battler); @@ -3265,9 +3262,6 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gBattleStruct->choicedMove[battler] = MOVE_NONE; gBattleStruct->eventState.arenaTurn = 0xFF; - // Restore struct member so replacement does not miss timing - gSpecialStatuses[battler].switchInAbilityDone = FALSE; - // Reset damage to prevent things like red card activating if the switched-in mon is holding it gSpecialStatuses[battler].physicalDmg = 0; gSpecialStatuses[battler].specialDmg = 0; @@ -3491,7 +3485,7 @@ static void DoBattleIntro(void) gBattleMons[battler].types[1] = GetSpeciesType(gBattleMons[battler].species, 1); gBattleMons[battler].types[2] = TYPE_MYSTERY; gBattleMons[battler].ability = GetAbilityBySpecies(gBattleMons[battler].species, gBattleMons[battler].abilityNum); - gBattleStruct->hpOnSwitchout[GetBattlerSide(battler)] = gBattleMons[battler].hp; + gBattleStruct->battlerState[battler].hpOnSwitchout = gBattleMons[battler].hp; memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles)); for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; @@ -3771,7 +3765,7 @@ static void DoBattleIntro(void) static void TryDoEventsBeforeFirstTurn(void) { - s32 i, j; + s32 i; if (gBattleControllerExecFlags) return; @@ -3807,25 +3801,6 @@ static void TryDoEventsBeforeFirstTurn(void) gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1); gBattleTurnCounter = 0; - - struct BattleContext ctx = {0}; - for (i = 0; i < gBattlersCount; i++) - { - gBattlerByTurnOrder[i] = i; - ctx.abilities[i] = GetBattlerAbility(i); - ctx.holdEffects[i] = GetBattlerHoldEffect(i); - } - for (i = 0; i < gBattlersCount - 1; i++) - { - for (j = i + 1; j < gBattlersCount; j++) - { - ctx.battlerAtk = gBattlerByTurnOrder[i]; - ctx.battlerDef = gBattlerByTurnOrder[j]; - - if (GetWhichBattlerFaster(&ctx, TRUE) == -1) - SwapTurnOrder(i, j); - } - } gBattleStruct->eventState.beforeFristTurn++; break; case FIRST_TURN_EVENTS_OVERWORLD_WEATHER: @@ -3856,78 +3831,13 @@ static void TryDoEventsBeforeFirstTurn(void) memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts for Mirror Herb and Opportunist gBattleStruct->eventState.beforeFristTurn++; break; - case FIRST_TURN_EVENTS_NEUTRALIZING_GAS: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, 0, 0, 0) != 0) - return; - } - gBattleStruct->switchInBattlerCounter = 0; + case FIRST_TURN_SWITCH_IN_EVENTS: + gBattleStruct->eventState.switchIn = 0; + for (u32 battler = 0; battler < gBattlersCount; battler++) + gBattleStruct->battlerState[battler].switchIn = TRUE; + BattleScriptPushCursorAndCallback(BattleScript_FirstTurnSwitchInEvents); gBattleStruct->eventState.beforeFristTurn++; break; - case FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - - if (TryPrimalReversion(battler)) - return; - if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0)) - return; - if (TryClearIllusion(battler, ABILITYEFFECT_ON_SWITCHIN)) - return; - if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, battler, 0, 0, 0) != 0) - return; - } - gBattleStruct->switchInBattlerCounter = 0; - gBattleStruct->eventState.beforeFristTurn++; - break; - case FIRST_TURN_EVENTS_ITEM_EFFECTS: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnSwitchInFirstTurnActivation)) - return; - } - gBattleStruct->switchInBattlerCounter = 0; - gBattleStruct->eventState.beforeFristTurn++; - break; - case FIRST_TURN_EVENTS_WHITE_HERB: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsWhiteHerbFirstTurnActivation)) - return; - } - gBattleStruct->switchInBattlerCounter = 0; - gBattleStruct->eventState.beforeFristTurn++; - break; - case FIRST_TURN_EVENTS_OPPORTUNIST: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST_FIRST_TURN, battler, GetBattlerAbility(battler), 0, 0)) - return; - } - gBattleStruct->switchInBattlerCounter = 0; - gBattleStruct->eventState.beforeFristTurn++; - break; - case FIRST_TURN_EVENTS_MIRROR_HERB: - while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest - { - u32 battler = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++]; - if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbFirstTurnActivation)) - return; - } - gBattleStruct->switchInBattlerCounter = 0; - gBattleStruct->eventState.beforeFristTurn++; - break; - case FIRST_TURN_EVENTS_EJECT_PACK: - gBattleStruct->eventState.beforeFristTurn++; - if (TrySwitchInEjectPack(FIRST_TURN)) - return; - break; case FIRST_TURN_EVENTS_END: for (i = 0; i < MAX_BATTLERS_COUNT; i++) { @@ -5403,7 +5313,7 @@ static void RunTurnActionsFunctions(void) } } - *(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber; + gBattleStruct->savedTurnActionNumber = gCurrentTurnActionNumber; sTurnActionsFuncsTable[gCurrentActionFuncId](); if (gCurrentTurnActionNumber >= gBattlersCount) // everyone did their actions, turn finished @@ -6168,7 +6078,7 @@ bool32 IsWildMonSmart(void) #endif } -static s32 Factorial(s32 n) +s32 Factorial(s32 n) { s32 f = 1, i; for (i = 2; i <= n; i++) diff --git a/src/battle_message.c b/src/battle_message.c index 0ca29c0a46..6fb4f79a03 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -616,10 +616,10 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_TOXICSPIKESPOISONED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was poisoned!"), [STRINGID_TOXICSPIKESBADLYPOISONED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was badly poisoned!"), [STRINGID_STICKYWEBSWITCHIN] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} was caught in a sticky web!"), - [STRINGID_HEALINGWISHCAMETRUE] = COMPOUND_STRING("The healing wish came true for {B_ATK_NAME_WITH_PREFIX2}!"), - [STRINGID_HEALINGWISHHEALED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} regained health!"), - [STRINGID_LUNARDANCECAMETRUE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} became cloaked in mystical moonlight!"), - [STRINGID_CURSEDBODYDISABLED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled by {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY}!"), + [STRINGID_HEALINGWISHCAMETRUE] = COMPOUND_STRING("The healing wish came true for {B_SCR_NAME_WITH_PREFIX2}!"), + [STRINGID_HEALINGWISHHEALED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} regained health!"), + [STRINGID_LUNARDANCECAMETRUE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} became cloaked in mystical moonlight!"), + [STRINGID_CURSEDBODYDISABLED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled by {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY}!"), [STRINGID_ATTACKERACQUIREDABILITY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} acquired {B_ATK_ABILITY}!"), [STRINGID_TARGETABILITYSTATLOWER] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} {B_BUFF2}lowered its {B_BUFF1}!"), [STRINGID_TARGETSTATWONTGOHIGHER] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1} won't go any higher!"), diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 99847ba3f3..b32b690c4d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6,6 +6,7 @@ #include "battle_ai_main.h" #include "battle_ai_util.h" #include "battle_scripts.h" +#include "battle_switch_in.h" #include "battle_environment.h" #include "battle_z_move.h" #include "item.h" @@ -411,7 +412,7 @@ static void Cmd_endselectionscript(void); static void Cmd_playanimation(void); static void Cmd_playanimation_var(void); static void Cmd_jumpfifsemiinvulnerable(void); -static void Cmd_unused_0x48(void); +static void Cmd_trainerslidein(void); static void Cmd_moveend(void); static void Cmd_sethealblock(void); static void Cmd_returnatktoball(void); @@ -422,7 +423,7 @@ static void Cmd_jumpifcantswitch(void); static void Cmd_openpartyscreen(void); static void Cmd_switchhandleorder(void); static void Cmd_switchineffects(void); -static void Cmd_trainerslidein(void); +static void Cmd_switchinevents(void); static void Cmd_playse(void); static void Cmd_fanfare(void); static void Cmd_playfaintcry(void); @@ -583,7 +584,7 @@ static void Cmd_givecaughtmon(void); static void Cmd_trysetcaughtmondexflags(void); static void Cmd_displaydexinfo(void); static void Cmd_trygivecaughtmonnick(void); -static void Cmd_unused_0xf4(void); +static void Cmd_sortbattlers(void); static void Cmd_removeattackerstatus1(void); static void Cmd_finishaction(void); static void Cmd_finishturn(void); @@ -670,7 +671,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_playanimation, //0x45 Cmd_playanimation_var, //0x46 Cmd_jumpfifsemiinvulnerable, //0x47 - Cmd_unused_0x48, //0x48 + Cmd_trainerslidein, //0x48 Cmd_moveend, //0x49 Cmd_sethealblock, //0x4A Cmd_returnatktoball, //0x4B @@ -681,7 +682,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_openpartyscreen, //0x50 Cmd_switchhandleorder, //0x51 Cmd_switchineffects, //0x52 - Cmd_trainerslidein, //0x53 + Cmd_switchinevents, //0x53 Cmd_playse, //0x54 Cmd_fanfare, //0x55 Cmd_playfaintcry, //0x56 @@ -842,7 +843,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_trysetcaughtmondexflags, //0xF1 Cmd_displaydexinfo, //0xF2 Cmd_trygivecaughtmonnick, //0xF3 - Cmd_unused_0xf4, //0xF4 + Cmd_sortbattlers, //0xF4 Cmd_removeattackerstatus1, //0xF5 Cmd_finishaction, //0xF6 Cmd_finishturn, //0xF7 @@ -960,7 +961,9 @@ static void ValidateSavedBattlerCounts(void) if (gBattleStruct->savedAttackerCount > 0) { if (TESTING) + { Test_ExitWithResult(TEST_RESULT_ERROR, 0, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!", __FILE__, __LINE__); + } else DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!"); } @@ -1089,23 +1092,6 @@ bool32 IsPowderMoveBlocked(struct BattleContext *ctx) return TRUE; } -bool32 EmergencyExitCanBeTriggered(u32 battler) -{ - enum Ability ability = GetBattlerAbility(battler); - - if (ability != ABILITY_EMERGENCY_EXIT && ability != ABILITY_WIMP_OUT) - return FALSE; - - if (IsBattlerAlive(battler) - && HadMoreThanHalfHpNowDoesnt(battler) - && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) - && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) - return TRUE; - - return FALSE; -} - static inline bool32 IsBattlerUsingBeakBlast(u32 battler) { if (gChosenActionByBattler[battler] != B_ACTION_USE_MOVE) @@ -2464,6 +2450,7 @@ static void Cmd_datahpupdate(void) if (gBattleControllerExecFlags) return; + switch (cmd->updateState) { case PASSIVE_HP_UPDATE: @@ -4285,6 +4272,7 @@ static void Cmd_tryfaintmon(void) && !IsBattlerAlive(battler)) { gHitMarker |= HITMARKER_FAINTED(battler); + gBattleStruct->eventState.faintedAction = 0; BattleScriptPush(cmd->nextInstr); gBattlescriptCurrInstr = faintScript; if (IsOnPlayerSide(battler)) @@ -5001,8 +4989,10 @@ static void Cmd_checkteamslost(void) u32 occupiedOpponentSpots = (gBattlersCount / 2) - emptyOpponentSpots; u32 alivePlayerPartyMons = CountAliveMonsForBattlerSide(B_POSITION_PLAYER_LEFT) - occupiedPlayerSpots; u32 aliveOpponentPartyMons = CountAliveMonsForBattlerSide(B_POSITION_OPPONENT_LEFT) - occupiedOpponentSpots; + u32 emptySlotsTotal = emptyPlayerSpots + emptyOpponentSpots; + u32 alivePartyMonsTotal = alivePlayerPartyMons + aliveOpponentPartyMons; - if (emptyPlayerSpots > 0 && alivePlayerPartyMons > 0 && emptyOpponentSpots > 0 && aliveOpponentPartyMons > 0) + if (emptySlotsTotal >= 2 && alivePartyMonsTotal >= 2) gBattlescriptCurrInstr = cmd->jumpInstr; else gBattlescriptCurrInstr = cmd->nextInstr; @@ -5547,8 +5537,15 @@ static void Cmd_jumpfifsemiinvulnerable(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0x48(void) +static void Cmd_trainerslidein(void) { + CMD_ARGS(u8 position); + + u32 battler = GetBattlerForBattleScript(cmd->position); + BtlController_EmitTrainerSlide(battler, B_COMM_TO_CONTROLLER); + MarkBattlerForControllerExec(battler); + + gBattlescriptCurrInstr = cmd->nextInstr; } static inline bool32 TryTriggerSymbiosis(u32 battler, u32 ally) @@ -6235,33 +6232,34 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_SYNCHRONIZE_TARGET: // target synchronize - if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, TRUE)) effect = TRUE; gBattleScripting.moveendState++; break; case MOVEEND_ABILITIES: // Such as abilities activating on contact(Poison Spore, Rough Skin, etc.). - if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, 0, 0, 0)) + i = GetBattlerAbility(gBattlerTarget); + if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, i, 0, TRUE)) effect = TRUE; - else if (TryClearIllusion(gBattlerTarget, ABILITYEFFECT_MOVE_END)) + else if (TryClearIllusion(gBattlerTarget, i)) effect = TRUE; gBattleScripting.moveendState++; break; case MOVEEND_ABILITIES_ATTACKER: // Poison Touch, possibly other in the future - if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_ATTACKER, gBattlerAttacker, 0, 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_ATTACKER, gBattlerAttacker, 0, 0, TRUE)) effect = TRUE; gBattleScripting.moveendState++; break; case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities for (u16 battler = 0; battler < gBattlersCount; battler++) { - if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, TRUE)) effect = TRUE; } if (!effect) gBattleScripting.moveendState++; break; case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize - if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, TRUE)) effect = TRUE; gBattleScripting.moveendState++; break; @@ -6661,7 +6659,7 @@ static void Cmd_moveend(void) u32 battler = gBattleStruct->eventState.moveEndBattler++; if (battler == gBattlerAttacker) continue; - if (AbilityBattleEffects(ABILITYEFFECT_COLOR_CHANGE, battler, GetBattlerAbility(battler), 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_COLOR_CHANGE, battler, GetBattlerAbility(battler), 0, TRUE)) return; } gBattleStruct->eventState.moveEndBattler = 0; @@ -6831,12 +6829,7 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.battler = battler; - - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) - BattleScriptCall(BattleScript_EmergencyExit); - else - BattleScriptCall(BattleScript_EmergencyExitWild); - + BattleScriptCall(BattleScript_EmergencyExit); break; // Only the fastest Emergency Exit / Wimp Out activates } } @@ -6935,7 +6928,7 @@ static void Cmd_moveend(void) u32 battler = gBattleStruct->eventState.moveEndBattler++; if (!IsBattlerAlive(battler)) continue; - if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, GetBattlerAbility(battler), 0, 0)) + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, GetBattlerAbility(battler), 0, TRUE)) return; } gBattleStruct->eventState.moveEndBattler = 0; @@ -7151,7 +7144,7 @@ static void Cmd_moveend(void) nextDancer = battler | 0x4; } } - if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, gCurrentMove)) + if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, gCurrentMove, TRUE)) effect = TRUE; } } @@ -7814,264 +7807,39 @@ static void Cmd_switchhandleorder(void) gBattlescriptCurrInstr = cmd->nextInstr; } -bool32 DoSwitchInAbilities(u32 battler) -{ - return (TryPrimalReversion(battler) - || AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0) - || (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect() && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0)) - || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0))); -} - static void UpdateSentMonFlags(u32 battler) { UpdateSentPokesToOpponentValue(battler); - gHitMarker &= ~HITMARKER_FAINTED(battler); gSpecialStatuses[battler].faintedHasReplacement = FALSE; -} + gBattleStruct->battlerState[battler].switchIn = TRUE; + gProtectStructs[battler].forcedSwitch = FALSE; -static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) -{ - gBattleScripting.battler = battler; - gBattleCommunication[MULTISTRING_CHOOSER] = multistringId; + // There is a hack here to ensure the truant counter will be 0 when the battler's next turn starts. + // The truant counter is not updated in the case where a mon switches in after a lost judgment in the battle arena. + if (GetBattlerAbility(battler) == ABILITY_TRUANT + && gCurrentActionFuncId != B_ACTION_USE_MOVE + && !gDisableStructs[battler].truantSwitchInHack) + gDisableStructs[battler].truantCounter = 1; + gDisableStructs[battler].truantSwitchInHack = 0; - if (gBattlescriptCurrInstr[1] == BS_TARGET) - BattleScriptCall(BattleScript_DmgHazardsOnTarget); - else if (gBattlescriptCurrInstr[1] == BS_ATTACKER) - BattleScriptCall(BattleScript_DmgHazardsOnAttacker); - else if (gBattlescriptCurrInstr[1] == BS_SCRIPTING) - BattleScriptCall(BattleScript_DmgHazardsOnBattlerScripting); - else - BattleScriptCall(BattleScript_DmgHazardsOnFaintedBattler); -} - -void TryHazardsOnSwitchIn(u32 battler, u32 side, enum Hazards hazardType) -{ - switch (hazardType) + for (u32 i = 0; i < gBattlersCount; i++) { - case HAZARDS_NONE: - break; - case HAZARDS_SPIKES: - { - enum Ability ability = GetBattlerAbility(battler); - if (ability != ABILITY_MAGIC_GUARD - && IsBattlerAffectedByHazards(battler, FALSE) - && IsBattlerGrounded(battler, ability, GetBattlerHoldEffect(battler))) - { - s32 spikesDmg = GetNonDynamaxMaxHP(battler) / ((5 - gSideTimers[side].spikesAmount) * 2); - SetPassiveDamageAmount(battler, spikesDmg); - SetDmgHazardsBattlescript(battler, B_MSG_PKMNHURTBYSPIKES); - } - break; + if (gBattlerByTurnOrder[i] == battler) + gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; } - case HAZARDS_STICKY_WEB: - if (IsBattlerAffectedByHazards(battler, FALSE) && IsBattlerGrounded(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler))) - { - gBattleScripting.battler = battler; - SET_STATCHANGER(STAT_SPEED, 1, TRUE); - BattleScriptCall(BattleScript_StickyWebOnSwitchIn); - } - break; - case HAZARDS_TOXIC_SPIKES: - if (!IsBattlerGrounded(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler))) - break; - - if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) // Absorb the toxic spikes. - { - gBattleStruct->hazardsCounter--; // reduce counter so the next hazard can be applied - gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount = 0; - RemoveHazardFromField(side, HAZARDS_TOXIC_SPIKES); - gEffectBattler = battler; - BattleScriptCall(BattleScript_ToxicSpikesAbsorbed); - } - else if (IsBattlerAffectedByHazards(battler, TRUE) - && CanBePoisoned(battler, battler, GetBattlerAbility(battler), GetBattlerAbility(battler))) - { - gBattleScripting.battler = battler; - BattleScriptPushCursor(); - if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) - { - gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned; - gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON; - } - else - { - gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned; - gBattleMons[battler].status1 |= STATUS1_POISON; - } - - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1); - MarkBattlerForControllerExec(battler); - } - break; - case HAZARDS_STEALTH_ROCK: - if (IsBattlerAffectedByHazards(battler, FALSE) && GetBattlerAbility(battler) != ABILITY_MAGIC_GUARD) - { - gBattleStruct->passiveHpUpdate[battler] = GetStealthHazardDamage(TYPE_SIDE_HAZARD_POINTED_STONES, battler); - if (gBattleStruct->passiveHpUpdate[battler] != 0) - SetDmgHazardsBattlescript(battler, B_MSG_STEALTHROCKDMG); - } - break; - case HAZARDS_STEELSURGE: - if (IsBattlerAffectedByHazards(battler, FALSE) && GetBattlerAbility(battler) != ABILITY_MAGIC_GUARD) - { - gBattleStruct->passiveHpUpdate[battler] = GetStealthHazardDamage(TYPE_SIDE_HAZARD_SHARP_STEEL, battler); - if (gBattleStruct->passiveHpUpdate[battler] != 0) - SetDmgHazardsBattlescript(battler, B_MSG_SHARPSTEELDMG); - } - break; - case HAZARDS_MAX_COUNT: - break; - } -} - -static bool32 DoSwitchInEffectsForBattler(u32 battler) -{ - u32 i = 0; - u32 side = GetBattlerSide(battler); - // Neutralizing Gas announces itself before hazards - if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0)) - { - return TRUE; - } - // Healing Wish activates before hazards. - // Starting from Gen8 - it heals only pokemon which can be healed. In gens 5,6,7 the effect activates anyways. - else if ((gBattleStruct->battlerState[battler].storedHealingWish || gBattleStruct->battlerState[battler].storedLunarDance) - && (gBattleMons[battler].hp != gBattleMons[battler].maxHP || gBattleMons[battler].status1 != 0 || GetGenConfig(GEN_CONFIG_HEALING_WISH_SWITCH) < GEN_8)) - { - gBattlerAttacker = battler; - if (gBattleStruct->battlerState[battler].storedHealingWish) - { - BattleScriptCall(BattleScript_HealingWishActivates); - gBattleStruct->battlerState[battler].storedHealingWish = FALSE; - } - else // Lunar Dance - { - BattleScriptCall(BattleScript_LunarDanceActivates); - gBattleStruct->battlerState[battler].storedLunarDance = FALSE; - } - } - else if (EmergencyExitCanBeTriggered(battler)) - { - gBattleScripting.battler = gBattlerAbility = battler; - gSpecialStatuses[battler].switchInItemDone = FALSE; - gBattleStruct->battlerState[battler].forcedSwitch = FALSE; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) - BattleScriptCall(BattleScript_EmergencyExit); - else - BattleScriptCall(BattleScript_EmergencyExitWild); - } - else if (!gDisableStructs[battler].hazardsDone) - { - TryHazardsOnSwitchIn(battler, side, gBattleStruct->hazardsQueue[side][gBattleStruct->hazardsCounter]); - gBattleStruct->hazardsCounter++; - // Done once we reach the first element without any hazard type or the array is full - if (gBattleStruct->hazardsQueue[side][gBattleStruct->hazardsCounter] == HAZARDS_NONE - || gBattleStruct->hazardsCounter == HAZARDS_MAX_COUNT) - { - gDisableStructs[battler].hazardsDone = TRUE; - gBattleStruct->hazardsCounter = 0; - } - } - else if (gBattleMons[battler].hp != gBattleMons[battler].maxHP && gBattleStruct->zmove.healReplacement) - { - gBattleStruct->zmove.healReplacement = FALSE; - SetHealAmount(battler, gBattleMons[battler].maxHP); - gBattleScripting.battler = battler; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; - BattleScriptCall(BattleScript_HealReplacementZMove); - } - else - { - enum Ability battlerAbility = GetBattlerAbility(battler); - // There is a hack here to ensure the truant counter will be 0 when the battler's next turn starts. - // The truant counter is not updated in the case where a mon switches in after a lost judgment in the battle arena. - if (battlerAbility == ABILITY_TRUANT - && gCurrentActionFuncId != B_ACTION_USE_MOVE - && !gDisableStructs[battler].truantSwitchInHack) - gDisableStructs[battler].truantCounter = 1; - - gDisableStructs[battler].truantSwitchInHack = 0; - - if (DoSwitchInAbilities(battler)) - return TRUE; - - if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnSwitchInActivation)) - return TRUE; - - for (i = 0; i < gBattlersCount; i++) - { - if (i == battler) - continue; - - enum Ability ability = GetBattlerAbility(i); - switch (ability) - { - case ABILITY_TRACE: - case ABILITY_COMMANDER: - if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, ability, 0, 0)) - return TRUE; - break; - case ABILITY_FORECAST: - case ABILITY_FLOWER_GIFT: - case ABILITY_PROTOSYNTHESIS: - if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, i, ability, 0, 0)) - return TRUE; - break; - default: - break; - } - if (TryClearIllusion(i, ABILITYEFFECT_ON_SWITCHIN)) - return TRUE; - } - - for (i = 0; i < gBattlersCount; i++) - { - if (ItemBattleEffects(i, 0, GetBattlerHoldEffect(i), IsWhiteHerbActivation)) - return TRUE; - } - for (i = 0; i < gBattlersCount; i++) - { - if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, i, GetBattlerAbility(i), 0, 0)) - return TRUE; - } - for (i = 0; i < gBattlersCount; i++) - { - if (ItemBattleEffects(i, 0, GetBattlerHoldEffect(i), IsMirrorHerbActivation)) - return TRUE; - } - - for (i = 0; i < gBattlersCount; i++) - { - if (gBattlerByTurnOrder[i] == battler) - gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; - - gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; - } - - gSpecialStatuses[battler].switchInItemDone = FALSE; - gBattleStruct->battlerState[battler].forcedSwitch = FALSE; - gBattleStruct->battlerState[battler].wasAboveHalfHp = FALSE; - return FALSE; - } - - return TRUE; // Effect's script plays. } static void Cmd_switchineffects(void) { CMD_ARGS(u8 battler); - u32 i, battler = GetBattlerForBattleScript(cmd->battler); + u32 battler = GetBattlerForBattleScript(cmd->battler); - switch (cmd->battler) + UpdateSentMonFlags(battler); + + if (cmd->battler == BS_FAINTED_MULTIPLE_1) { - // Multiple mons fainted and are being switched-in. Their abilities/hazards will play according to speed ties. - case BS_FAINTED_MULTIPLE_1: // Saves the battlers. - gBattleStruct->battlerState[battler].multipleSwitchInBattlers = TRUE; - UpdateSentMonFlags(battler); - - // Increment fainted battler. - do + do // Increment fainted battler { gBattlerFainted++; if (gBattlerFainted >= gBattlersCount) @@ -8079,55 +7847,20 @@ static void Cmd_switchineffects(void) if (gHitMarker & HITMARKER_FAINTED(gBattlerFainted) && !(gAbsentBattlerFlags & (1u << gBattlerFainted))) break; } while (1); - - gBattlescriptCurrInstr = cmd->nextInstr; - return; - case BS_FAINTED_MULTIPLE_2: // Plays hazards/abilities. - switch (gBattleStruct->multipleSwitchInState) - { - case 0: // Sort battlers by speed - for (i = 0; i < gBattlersCount; i++) - gBattleStruct->multipleSwitchInSortedBattlers[i] = i; - SortBattlersBySpeed(gBattleStruct->multipleSwitchInSortedBattlers, FALSE); - gBattleStruct->multipleSwitchInState++; - gBattleStruct->multipleSwitchInCursor = 0; - // Loop through all available battlers - case 1: - for (; gBattleStruct->multipleSwitchInCursor < gBattlersCount; gBattleStruct->multipleSwitchInCursor++) - { - gBattlerFainted = gBattleStruct->multipleSwitchInSortedBattlers[gBattleStruct->multipleSwitchInCursor]; - if (gBattleStruct->battlerState[gBattlerFainted].multipleSwitchInBattlers) - { - if (DoSwitchInEffectsForBattler(gBattlerFainted)) - return; - } - } - if (TrySwitchInEjectPack(OTHER)) - return; - // All battlers done, end - for (i = 0; i < gBattlersCount; i++) - gBattleStruct->battlerState[i].multipleSwitchInBattlers = FALSE; - - gBattleStruct->multipleSwitchInState = 0; - gBattlescriptCurrInstr = cmd->nextInstr; - } - break; - default: - UpdateSentMonFlags(battler); - if (!DoSwitchInEffectsForBattler(battler) && !TrySwitchInEjectPack(OTHER)) - gBattlescriptCurrInstr = cmd->nextInstr; - break; } + + gBattleStruct->eventState.switchIn = 0; + gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_trainerslidein(void) +static void Cmd_switchinevents(void) { - CMD_ARGS(u8 position); - - u32 battler = GetBattlerForBattleScript(cmd->position); - BtlController_EmitTrainerSlide(battler, B_COMM_TO_CONTROLLER); - MarkBattlerForControllerExec(battler); - + CMD_ARGS(); + while (gBattleStruct->eventState.switchIn < SWITCH_IN_EVENTS_COUNT) + { + if (DoSwitchInEvents()) + return; + } gBattlescriptCurrInstr = cmd->nextInstr; } @@ -9235,7 +8968,7 @@ static void Cmd_hpthresholds2(void) { u32 battler = GetBattlerForBattleScript(cmd->battler); u32 opposingBattler = BATTLE_OPPOSITE(battler); - u8 hpSwitchout = gBattleStruct->hpOnSwitchout[GetBattlerSide(opposingBattler)]; + u32 hpSwitchout = gBattleStruct->battlerState[opposingBattler].hpOnSwitchout; s32 result = (hpSwitchout - gBattleMons[opposingBattler].hp) * 100 / hpSwitchout; if (gBattleMons[opposingBattler].hp >= hpSwitchout) @@ -9884,9 +9617,6 @@ static void Cmd_manipulatedamage(void) case DMG_1_8_TARGET_HP: SetPassiveDamageAmount(gBattlerTarget, GetNonDynamaxMaxHP(gBattlerTarget) / 8); break; - case DMG_FULL_ATTACKER_HP: - gBattleStruct->passiveHpUpdate[gBattlerTarget] = GetNonDynamaxMaxHP(gBattlerAttacker); - break; case DMG_BIG_ROOT: gBattleStruct->passiveHpUpdate[gBattlerAttacker] = -1 * GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->passiveHpUpdate[gBattlerAttacker]); break; @@ -10746,9 +10476,8 @@ static void Cmd_forcerandomswitch(void) { gBattleStruct->battlerPartyIndexes[gBattlerTarget] = gBattlerPartyIndexes[gBattlerTarget]; gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch; - gBattleStruct->battlerState[gBattlerTarget].forcedSwitch = TRUE; + gProtectStructs[gBattlerTarget].forcedSwitch = TRUE; gBattleStruct->monToSwitchIntoId[gBattlerTarget] = validMons[RandomUniform(RNG_FORCE_RANDOM_SWITCH, 0, validMonsCount - 1)]; - if (!IsMultiBattle()) SwitchPartyOrder(gBattlerTarget); @@ -14233,8 +13962,17 @@ static void Cmd_trygivecaughtmonnick(void) } } -static void Cmd_unused_0xf4(void) +static void Cmd_sortbattlers(void) { + CMD_ARGS(); + if (!gBattleStruct->battlersSorted) + { + for (u32 i = 0; i < gBattlersCount; i++) + gBattlersBySpeed[i] = i; + + SortBattlersBySpeed(gBattlersBySpeed, FALSE); + } + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_removeattackerstatus1(void) @@ -15826,7 +15564,7 @@ void BS_ActivateWeatherChangeAbilities(void) u32 battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; - AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0); + AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, GetBattlerAbility(battler), MOVE_NONE, TRUE); } void BS_ActivateTerrainChangeAbilities(void) @@ -15835,7 +15573,7 @@ void BS_ActivateTerrainChangeAbilities(void) u32 battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; - AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0); + AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, GetBattlerAbility(battler), MOVE_NONE, TRUE); } void BS_ResetTerrainAbilityFlags(void) @@ -16673,7 +16411,8 @@ void BS_SetTracedAbility(void) void BS_TryIllusionOff(void) { NATIVE_ARGS(u8 battler); - if (TryClearIllusion(GetBattlerForBattleScript(cmd->battler), ABILITYEFFECT_MOVE_END)) + u32 battler = GetBattlerForBattleScript(cmd->battler); + if (TryClearIllusion(battler, GetBattlerAbility(battler))) return; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -16775,10 +16514,10 @@ void BS_GetBattlerFainted(void) gBattlescriptCurrInstr = cmd->nextInstr; } +// TODO: What is this fixing??? void BS_ResetSwitchInAbilityBits(void) { NATIVE_ARGS(); - gSpecialStatuses[gBattlerAttacker].switchInAbilityDone = FALSE; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -17038,17 +16777,17 @@ void BS_SwitchinAbilities(void) { NATIVE_ARGS(u8 battler); u32 battler = GetBattlerForBattleScript(cmd->battler); + u32 ability = GetBattlerAbility(battler); gBattlescriptCurrInstr = cmd->nextInstr; - AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); - AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); - AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); - AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, 0); - - if (gBattleWeather & B_WEATHER_ANY && HasWeatherEffect()) - AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0); - - if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) - AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0); + AbilityBattleEffects(ABILITYEFFECT_TERA_SHIFT, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_UNNERVE, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_COMMANDER, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, ability, MOVE_NONE, TRUE); + AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, ability, MOVE_NONE, TRUE); } void BS_InstantHpDrop(void) @@ -17061,16 +16800,17 @@ void BS_InstantHpDrop(void) void BS_ClearStatus(void) { - NATIVE_ARGS(); - gBattleMons[gBattlerAttacker].status1 = 0; + NATIVE_ARGS(u8 battler); + u32 battler = GetBattlerForBattleScript(cmd->battler); + gBattleMons[battler].status1 = 0; BtlController_EmitSetMonData( - gBattlerAttacker, + battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, - sizeof(gBattleMons[gBattlerAttacker].status1), - &gBattleMons[gBattlerAttacker].status1); - MarkBattlerForControllerExec(gBattlerAttacker); + sizeof(gBattleMons[battler].status1), + &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_switch_in.c b/src/battle_switch_in.c new file mode 100644 index 0000000000..197afaa3b1 --- /dev/null +++ b/src/battle_switch_in.c @@ -0,0 +1,406 @@ +#include "global.h" +#include "battle.h" +#include "battle_hold_effects.h" +#include "battle_util.h" +#include "battle_scripts.h" +#include "battle_switch_in.h" +#include "battle_controllers.h" +#include "generational_changes.h" +#include "constants/battle.h" +#include "constants/moves.h" + +static bool32 FirstEventBlockEvents(struct BattleContext *ctx); +static bool32 TryHazardsOnSwitchIn(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType); +static bool32 SecondEventBlockEvents(struct BattleContext *ctx); + +bool32 DoSwitchInEvents(void) +{ + u32 battler; + + struct BattleContext ctx = {0}; + for (battler = 0; battler < gBattlersCount; battler++) + { + if (!IsBattlerAlive(battler)) + continue; + ctx.abilities[battler] = GetBattlerAbility(battler); + ctx.holdEffects[battler] = GetBattlerHoldEffect(battler); + } + + switch (gBattleStruct->eventState.switchIn) + { + case SWITCH_IN_EVENTS_ORDER_BY_SPEED: + for (u32 i = 0; i < gBattlersCount; i++) + gBattlersBySpeed[i] = i; + SortBattlersBySpeed(gBattlersBySpeed, FALSE); + gBattleStruct->battlersSorted = TRUE; + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.battlerSwitchIn = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_TERA_SHIFT: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (AbilityBattleEffects(ABILITYEFFECT_TERA_SHIFT, battler, ctx.abilities[battler], 0, gBattleStruct->battlerState[battler].switchIn)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_NEUTRALIZING_GAS: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, ctx.abilities[battler], 0, gBattleStruct->battlerState[battler].switchIn)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_UNNERVE: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (AbilityBattleEffects(ABILITYEFFECT_UNNERVE, battler, ctx.abilities[battler], 0, gBattleStruct->battlerState[battler].switchIn)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_FIRST_BLOCK: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter]; + + if (!IsBattlerAlive(battler) && gBattleStruct->eventState.battlerSwitchIn != FIRST_EVENT_BLOCK_HAZARDS) + { + gBattleStruct->switchInBattlerCounter++; + gBattleStruct->eventState.battlerSwitchIn = 0; + continue; + } + + ctx.battlerAtk = battler; + while (gBattleStruct->eventState.battlerSwitchIn < FIRST_EVENT_BLOCK_COUNT) + { + if (FirstEventBlockEvents(&ctx)) + return TRUE; + } + + gBattleStruct->switchInBattlerCounter++; + gBattleStruct->eventState.battlerSwitchIn = 0; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_SECOND_BLOCK: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter]; + + if (!IsBattlerAlive(battler)) + { + gBattleStruct->switchInBattlerCounter++; + gBattleStruct->eventState.battlerSwitchIn = 0; + continue; + } + + ctx.battlerAtk = battler; + while (gBattleStruct->eventState.battlerSwitchIn < SECOND_EVENT_BLOCK_COUNT) + { + if (SecondEventBlockEvents(&ctx)) + return TRUE; + } + + gBattleStruct->switchInBattlerCounter++; + gBattleStruct->eventState.battlerSwitchIn = 0; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_WHITE_HERB: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (ItemBattleEffects(battler, 0, ctx.holdEffects[battler], IsWhiteHerbActivation)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_OPPORTUNIST: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, ctx.abilities[battler], 0, TRUE)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_MIRROR_HERB: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbActivation)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_CLEAR_SET_VALUES: + for (battler = 0; battler < gBattlersCount; battler++) + { + if (gBattleStruct->battlerState[battler].switchIn) + { + gBattleStruct->battlerState[battler].hpOnSwitchout = gBattleMons[battler].hp; + gBattleStruct->battlerState[battler].switchIn = FALSE; + } + } + gBattleStruct->battlersSorted = FALSE; + gBattleStruct->hazardsCounter = 0; + gBattleStruct->eventState.switchIn++; + break; + case SWITCH_IN_EVENTS_EJECT_PACK: + gBattleStruct->eventState.switchIn++; + if (TrySwitchInEjectPack(START_OF_TURN)) + return TRUE; + break; + case SWITCH_IN_EVENTS_COUNT: + break; + } + return FALSE; +} + +static bool32 CanBattlerBeHealed(u32 battler) +{ + if (GetGenConfig(GEN_CONFIG_HEALING_WISH_SWITCH) < GEN_8) + return TRUE; + + if (gBattleMons[battler].hp != gBattleMons[battler].maxHP || gBattleMons[battler].status1) + return TRUE; + + return FALSE; +} + +static bool32 FirstEventBlockEvents(struct BattleContext *ctx) +{ + bool32 effect = FALSE; + u32 battler = ctx->battlerAtk; + + switch (gBattleStruct->eventState.battlerSwitchIn) + { + case FIRST_EVENT_BLOCK_HEALING_WISH: + if (!gBattleStruct->battlerState[battler].switchIn || !CanBattlerBeHealed(battler)) + { + effect = FALSE; + } + else if (gBattleStruct->battlerState[battler].storedHealingWish) + { + gBattleStruct->battlerState[battler].storedHealingWish = FALSE; + SetHealAmount(battler, GetNonDynamaxMaxHP(battler)); + gBattleScripting.battler = battler; + BattleScriptCall(BattleScript_HealingWishActivates); + effect = TRUE; + } + else if (gBattleStruct->battlerState[battler].storedLunarDance) + { + gBattleStruct->battlerState[battler].storedLunarDance = FALSE; + SetHealAmount(battler, GetNonDynamaxMaxHP(battler)); + gBattleScripting.battler = battler; + BattleScriptCall(BattleScript_LunarDanceActivates); + effect = TRUE; + } + else if (gBattleStruct->zmove.healReplacement & 1u << battler) + { + gBattleStruct->zmove.healReplacement &= ~(1u << battler); + SetHealAmount(battler, GetNonDynamaxMaxHP(battler)); + gBattleScripting.battler = battler; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; + BattleScriptCall(BattleScript_HealReplacementZMove); + effect = TRUE; + } + gBattleStruct->eventState.battlerSwitchIn++; + break; + case FIRST_EVENT_BLOCK_HAZARDS: + if (!gBattleStruct->battlerState[battler].switchIn) + { + gBattleStruct->eventState.battlerSwitchIn++; + } + else if (EmergencyExitCanBeTriggered(battler)) + { + gBattleScripting.battler = gBattlerAbility = battler; + gSpecialStatuses[battler].switchInItemDone = FALSE; + gBattleStruct->battlerState[battler].forcedSwitch = FALSE; + gBattleStruct->eventState.switchIn = 0; + BattleScriptCall(BattleScript_EmergencyExit); + effect = TRUE; + } + else + { + enum Hazards hazard = gBattleStruct->hazardsQueue[GetBattlerSide(battler)][gBattleStruct->hazardsCounter]; + if (hazard == HAZARDS_NONE || gBattleStruct->hazardsCounter >= HAZARDS_MAX_COUNT) + { + gBattleStruct->hazardsCounter = 0; + gBattleStruct->eventState.battlerSwitchIn++; + } + else + { + effect = TryHazardsOnSwitchIn(battler, ctx->abilities[battler], ctx->holdEffects[battler], hazard); + } + } + break; + case FIRST_EVENT_BLOCK_GENERAL_ABILITIES: + if (TryPrimalReversion(battler) + || AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, ctx->abilities[battler], MOVE_NONE, gBattleStruct->battlerState[battler].switchIn) + || TryClearIllusion(battler, ctx->abilities[battler])) + effect = TRUE; + gBattleStruct->eventState.battlerSwitchIn++; + break; + case FIRST_EVENT_BLOCK_IMMUNITY_ABILITIES: + if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, ctx->abilities[battler], MOVE_NONE, TRUE)) + effect = TRUE; + gBattleStruct->eventState.battlerSwitchIn++; + break; + case FIRST_EVENT_BLOCK_ITEMS: + if (ItemBattleEffects(battler, 0, ctx->holdEffects[battler], IsOnSwitchInActivation)) + effect = TRUE; + gBattleStruct->eventState.battlerSwitchIn++; + break; + case FIRST_EVENT_BLOCK_COUNT: + gBattleStruct->eventState.battlerSwitchIn++; + break; + } + + return effect; +} + +static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) +{ + gBattleScripting.battler = battler; + gBattleCommunication[MULTISTRING_CHOOSER] = multistringId; + BattleScriptCall(BattleScript_DmgHazardsOnBattler); +} + +static bool32 TryHazardsOnSwitchIn(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType) +{ + bool32 effect = FALSE; + u32 side = GetBattlerSide(battler); + bool32 clearedToxicSpikes = FALSE; + + switch (hazardType) + { + case HAZARDS_NONE: + break; + case HAZARDS_SPIKES: + if (!IsAbilityAndRecord(battler, ability, ABILITY_MAGIC_GUARD) + && IsBattlerAffectedByHazards(battler, holdEffect, FALSE) + && IsBattlerGrounded(battler, ability, holdEffect)) + { + s32 spikesDmg = GetNonDynamaxMaxHP(battler) / ((5 - gSideTimers[side].spikesAmount) * 2); + SetPassiveDamageAmount(battler, spikesDmg); + SetDmgHazardsBattlescript(battler, B_MSG_PKMNHURTBYSPIKES); + effect = TRUE; + } + break; + case HAZARDS_STICKY_WEB: + if (IsBattlerAffectedByHazards(battler, holdEffect, FALSE) && IsBattlerGrounded(battler, ability, holdEffect)) + { + gBattleScripting.battler = battler; + SET_STATCHANGER(STAT_SPEED, 1, TRUE); + BattleScriptCall(BattleScript_StickyWebOnSwitchIn); + effect = TRUE; + } + break; + case HAZARDS_TOXIC_SPIKES: + if (!IsBattlerGrounded(battler, ability, holdEffect)) + { + effect = FALSE; + } + else if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) // Absorb the toxic spikes. + { + gSideTimers[side].toxicSpikesAmount = 0; + RemoveHazardFromField(side, HAZARDS_TOXIC_SPIKES); + gEffectBattler = battler; + BattleScriptCall(BattleScript_ToxicSpikesAbsorbed); + clearedToxicSpikes = TRUE; + effect = TRUE; + } + else if (IsBattlerAffectedByHazards(battler, holdEffect, TRUE) + && CanBePoisoned(battler, battler, ability, ability)) + { + gBattleScripting.battler = battler; + BattleScriptPushCursor(); + if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) + { + gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned; + gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON; + } + else + { + gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned; + gBattleMons[battler].status1 |= STATUS1_POISON; + } + + BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); + effect = TRUE; + } + break; + case HAZARDS_STEALTH_ROCK: + if (IsBattlerAffectedByHazards(battler, holdEffect, FALSE) && ability != ABILITY_MAGIC_GUARD) + { + gBattleStruct->passiveHpUpdate[battler] = GetStealthHazardDamage(TYPE_SIDE_HAZARD_POINTED_STONES, battler); + if (gBattleStruct->passiveHpUpdate[battler] != 0) + { + SetDmgHazardsBattlescript(battler, B_MSG_STEALTHROCKDMG); + effect = TRUE; + } + } + break; + case HAZARDS_STEELSURGE: + if (IsBattlerAffectedByHazards(battler, holdEffect, FALSE) && ability != ABILITY_MAGIC_GUARD) + { + gBattleStruct->passiveHpUpdate[battler] = GetStealthHazardDamage(TYPE_SIDE_HAZARD_SHARP_STEEL, battler); + if (gBattleStruct->passiveHpUpdate[battler] != 0) + { + SetDmgHazardsBattlescript(battler, B_MSG_SHARPSTEELDMG); + effect = TRUE; + } + } + break; + case HAZARDS_MAX_COUNT: + break; + } + + if (!clearedToxicSpikes) + gBattleStruct->hazardsCounter++; + + return effect; +} + +static bool32 SecondEventBlockEvents(struct BattleContext *ctx) +{ + bool32 effect = FALSE; + u32 battler = ctx->battlerAtk; + + switch (gBattleStruct->eventState.battlerSwitchIn) + { + case SECOND_EVENT_ABILITIES: + if (AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, ctx->abilities[battler], MOVE_NONE, TRUE) + || AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, ctx->abilities[battler], MOVE_NONE, TRUE) + || AbilityBattleEffects(ABILITYEFFECT_COMMANDER, battler, ctx->abilities[battler], MOVE_NONE, gBattleStruct->battlerState[battler].switchIn)) + effect = TRUE; + gBattleStruct->eventState.battlerSwitchIn++; + break; + case SECOND_EVENT_BOOSTER_ENERGY: + if (ItemBattleEffects(battler, 0, ctx->holdEffects[battler], IsBoosterEnergyActivation)) + effect = TRUE; + gBattleStruct->eventState.battlerSwitchIn++; + break; + case SECOND_EVENT_BLOCK_COUNT: + gBattleStruct->eventState.battlerSwitchIn++; + break; + } + + return effect; +} diff --git a/src/battle_terastal.c b/src/battle_terastal.c index f0e720c93e..6b71f9055e 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -35,12 +35,12 @@ void ActivateTera(u32 battler) // Execute battle script. PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler)); if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION)) - BattleScriptExecute(BattleScript_TeraFormChange); + BattleScriptPushCursorAndCallback(BattleScript_TeraFormChange); else if (gBattleStruct->illusion[gBattlerAttacker].state == ILLUSION_ON && DoesSpeciesHaveFormChangeMethod(GetIllusionMonSpecies(gBattlerAttacker), FORM_CHANGE_BATTLE_TERASTALLIZATION)) - BattleScriptExecute(BattleScript_IllusionOffAndTerastallization); + BattleScriptPushCursorAndCallback(BattleScript_IllusionOffAndTerastallization); else - BattleScriptExecute(BattleScript_Terastallization); + BattleScriptPushCursorAndCallback(BattleScript_Terastallization); } // Applies palette blend and enables UI indicator after animation has played diff --git a/src/battle_util.c b/src/battle_util.c index b5b10ebcd7..5dfa6049b7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1856,6 +1856,7 @@ bool32 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return FALSE; + do { s32 i; @@ -3907,11 +3908,10 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId) return effect; } -u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, u32 special, u32 moveArg) +u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, u32 move, bool32 shouldAbilityTrigger) { u32 effect = 0; enum Type moveType = 0; - u32 move = 0; u32 side = 0; u32 i = 0, j = 0; u32 partner = 0; @@ -3922,16 +3922,12 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (gBattlerAttacker >= gBattlersCount) gBattlerAttacker = battler; - if (special) - gLastUsedAbility = special; - else if (ability) + if (ability) gLastUsedAbility = ability; else gLastUsedAbility = GetBattlerAbility(battler); - if (moveArg) - move = moveArg; - else + if (move == MOVE_NONE) move = gCurrentMove; moveType = GetBattleMoveType(move); @@ -3948,8 +3944,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab u32 target1; u32 target2; - if (gSpecialStatuses[battler].switchInAbilityDone) - break; if (GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD) break; @@ -3974,7 +3968,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (effect != 0) { - BattleScriptPushCursorAndCallback(BattleScript_TraceActivates); + BattleScriptCall(BattleScript_TraceActivates); gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability; RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability PREPARE_MON_NICK_WITH_PREFIX_LOWER_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget]) @@ -3983,14 +3977,14 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITY_IMPOSTER: + if (gBattleStruct->battlerState[battler].switchIn) { u32 diagonalBattler = BATTLE_OPPOSITE(battler); if (IsDoubleBattle()) diagonalBattler = BATTLE_PARTNER(diagonalBattler); // Imposter only activates when the battler first switches in - if (gDisableStructs[battler].isFirstTurn == 2 - && !gDisableStructs[battler].overwrittenAbility + if (!gDisableStructs[battler].overwrittenAbility && IsBattlerAlive(diagonalBattler) && !gBattleMons[diagonalBattler].volatiles.substitute && !gBattleMons[diagonalBattler].volatiles.transformed @@ -4002,95 +3996,68 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab SaveBattlerTarget(gBattlerTarget); gBattlerAttacker = battler; gBattlerTarget = diagonalBattler; - BattleScriptPushCursorAndCallback(BattleScript_ImposterActivates); + BattleScriptCall(BattleScript_ImposterActivates); effect++; } } break; case ABILITY_MOLD_BREAKER: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_MOLDBREAKER; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_TERAVOLT: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_TERAVOLT; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_TURBOBLAZE: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_TURBOBLAZE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_SLOW_START: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gDisableStructs[battler].slowStartTimer = 5; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_SLOWSTART; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); - effect++; - } - break; - case ABILITY_UNNERVE: - if (!gSpecialStatuses[battler].switchInAbilityDone && !gDisableStructs[battler].unnerveActivated) - { - gEffectBattler = GetOppositeBattler(battler); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_UNNERVE; - gDisableStructs[battler].unnerveActivated = TRUE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); - effect++; - } - break; - case ABILITY_AS_ONE_ICE_RIDER: - case ABILITY_AS_ONE_SHADOW_RIDER: - if (!gSpecialStatuses[battler].switchInAbilityDone && !gDisableStructs[battler].unnerveActivated) - { - gEffectBattler = GetOppositeBattler(battler); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ASONE; - gDisableStructs[battler].unnerveActivated = TRUE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_ActivateAsOne); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_CURIOUS_MEDICINE: - if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() - && IsBattlerAlive(BATTLE_PARTNER(battler)) && TryResetBattlerStatChanges(BATTLE_PARTNER(battler))) + if (shouldAbilityTrigger + && IsDoubleBattle() + && IsBattlerAlive(BATTLE_PARTNER(battler)) + && TryResetBattlerStatChanges(BATTLE_PARTNER(battler))) { gEffectBattler = BATTLE_PARTNER(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_CURIOUS_MEDICINE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_PASTEL_VEIL: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { SaveBattlerTarget(gBattlerTarget); gBattlerTarget = battler; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_PASTEL_VEIL; - BattleScriptPushCursorAndCallback(BattleScript_PastelVeilActivates); + BattleScriptCall(BattleScript_PastelVeilActivates); effect++; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; } break; case ABILITY_ANTICIPATION: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { struct DamageContext ctx = {0}; uq4_12_t modifier = UQ_4_12(1.0); @@ -4125,31 +4092,28 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (effect != 0) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ANTICIPATION; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); } } break; case ABILITY_FRISK: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_FriskActivates); // Try activate + BattleScriptCall(BattleScript_FriskActivates); effect++; } return effect; // Note: It returns effect as to not record the ability if Frisk does not activate. case ABILITY_FOREWARN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { ForewarnChooseMove(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FOREWARN; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DOWNLOAD: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { enum Stat statId; u32 opposingBattler; @@ -4174,96 +4138,86 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab else statId = STAT_SPATK; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(statId, 1, FALSE); SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); - BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3); + BattleScriptCall(BattleScript_AttackerAbilityStatRaiseEnd3); effect++; } } break; case ABILITY_PRESSURE: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_PRESSURE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DARK_AURA: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_DARKAURA; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_FAIRY_AURA: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FAIRYAURA; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_AURA_BREAK: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_AURABREAK; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_COMATOSE: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_COMATOSE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_SCREEN_CLEANER: - if (!gSpecialStatuses[battler].switchInAbilityDone && TryRemoveScreens(battler)) + if (shouldAbilityTrigger && TryRemoveScreens(battler)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_SCREENCLEANER; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DRIZZLE: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); + BattleScriptCall(BattleScript_DrizzleActivates); effect++; } - else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect() && !gSpecialStatuses[battler].switchInAbilityDone) + else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect()) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); + BattleScriptCall(BattleScript_BlockedByPrimalWeather); effect++; } break; case ABILITY_SAND_STREAM: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); + BattleScriptCall(BattleScript_SandstreamActivates); effect++; } - else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect() && !gSpecialStatuses[battler].switchInAbilityDone) + else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect()) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); + BattleScriptCall(BattleScript_BlockedByPrimalWeather); effect++; } break; @@ -4271,31 +4225,29 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_DROUGHT: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); + BattleScriptCall(BattleScript_DroughtActivates); effect++; } - else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect() && !gSpecialStatuses[battler].switchInAbilityDone) + else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect()) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); + BattleScriptCall(BattleScript_BlockedByPrimalWeather); effect++; } break; case ABILITY_SNOW_WARNING: if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow); + BattleScriptCall(BattleScript_SnowWarningActivatesSnow); effect++; } else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail); + BattleScriptCall(BattleScript_SnowWarningActivatesHail); effect++; } - else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect() && !gSpecialStatuses[battler].switchInAbilityDone) + else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect()) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); + BattleScriptCall(BattleScript_BlockedByPrimalWeather); effect++; } break; @@ -4303,70 +4255,65 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_HADRON_ENGINE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) { - BattleScriptPushCursorAndCallback(BattleScript_ElectricSurgeActivates); + BattleScriptCall(BattleScript_ElectricSurgeActivates); effect++; } break; case ABILITY_GRASSY_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN)) { - BattleScriptPushCursorAndCallback(BattleScript_GrassySurgeActivates); + BattleScriptCall(BattleScript_GrassySurgeActivates); effect++; } break; case ABILITY_MISTY_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN)) { - BattleScriptPushCursorAndCallback(BattleScript_MistySurgeActivates); + BattleScriptCall(BattleScript_MistySurgeActivates); effect++; } break; case ABILITY_PSYCHIC_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN)) { - BattleScriptPushCursorAndCallback(BattleScript_PsychicSurgeActivates); + BattleScriptCall(BattleScript_PsychicSurgeActivates); effect++; } break; case ABILITY_INTIMIDATE: - if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler)) + if (shouldAbilityTrigger && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_ATK, 1, TRUE); - BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivates); + BattleScriptCall(BattleScript_IntimidateActivates); effect++; } break; case ABILITY_SUPERSWEET_SYRUP: - if (!gSpecialStatuses[battler].switchInAbilityDone + if (shouldAbilityTrigger && !GetBattlerPartyState(battler)->supersweetSyrup && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; GetBattlerPartyState(battler)->supersweetSyrup = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_SupersweetSyrupActivates); + BattleScriptCall(BattleScript_SupersweetSyrupActivates); effect++; } break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_AnnounceAirLockCloudNine); + BattleScriptCall(BattleScript_AnnounceAirLockCloudNine); effect++; } break; case ABILITY_TERAFORM_ZERO: - if (!gSpecialStatuses[battler].switchInAbilityDone - && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) + if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_ActivateTeraformZero); + BattleScriptCall(BattleScript_ActivateTeraformZero); effect++; } break; @@ -4378,162 +4325,122 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_SHIELDS_DOWN: if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) { - BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeEnd3); + BattleScriptCall(BattleScript_BattlerFormChange); effect++; } break; case ABILITY_INTREPID_SWORD: - if (!gSpecialStatuses[battler].switchInAbilityDone - && !GetBattlerPartyState(battler)->intrepidSwordBoost) + if (shouldAbilityTrigger && !GetBattlerPartyState(battler)->intrepidSwordBoost) { if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9) GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; + if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_ATK, 1, FALSE); - BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); + BattleScriptCall(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } } break; case ABILITY_DAUNTLESS_SHIELD: - if (!gSpecialStatuses[battler].switchInAbilityDone - && !GetBattlerPartyState(battler)->dauntlessShieldBoost) + if (shouldAbilityTrigger && !GetBattlerPartyState(battler)->dauntlessShieldBoost) { if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9) GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; + if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_DEF, 1, FALSE); - BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); + BattleScriptCall(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } } break; case ABILITY_WIND_RIDER: - if (!gSpecialStatuses[battler].switchInAbilityDone + if (shouldAbilityTrigger && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_ATK, 1, FALSE); - BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); + BattleScriptCall(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } break; case ABILITY_DESOLATE_LAND: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN_PRIMAL, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_DesolateLandActivates); + BattleScriptCall(BattleScript_DesolateLandActivates); effect++; } break; case ABILITY_PRIMORDIAL_SEA: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN_PRIMAL, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_PrimordialSeaActivates); + BattleScriptCall(BattleScript_PrimordialSeaActivates); effect++; } break; case ABILITY_DELTA_STREAM: if (TryChangeBattleWeather(battler, BATTLE_WEATHER_STRONG_WINDS, gLastUsedAbility)) { - BattleScriptPushCursorAndCallback(BattleScript_DeltaStreamActivates); + BattleScriptCall(BattleScript_DeltaStreamActivates); effect++; } break; case ABILITY_VESSEL_OF_RUIN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleMons[battler].volatiles.vesselOfRuin = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); + BattleScriptCall(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_SWORD_OF_RUIN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleMons[battler].volatiles.swordOfRuin = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_DEF); - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); + BattleScriptCall(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_TABLETS_OF_RUIN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleMons[battler].volatiles.tabletsOfRuin = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); + BattleScriptCall(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_BEADS_OF_RUIN: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { gBattleMons[battler].volatiles.beadsOfRuin = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPDEF); - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); + BattleScriptCall(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_SUPREME_OVERLORD: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; gBattleStruct->supremeOverlordCounter[battler] = min(5, GetBattlerSideFaintCounter(battler)); if (gBattleStruct->supremeOverlordCounter[battler] > 0) { - BattleScriptPushCursorAndCallback(BattleScript_SupremeOverlordActivates); + BattleScriptCall(BattleScript_SupremeOverlordActivates); effect++; } } break; - case ABILITY_COSTAR: - if (!gSpecialStatuses[battler].switchInAbilityDone - && IsDoubleBattle() - && IsBattlerAlive(BATTLE_PARTNER(battler)) - && CountBattlerStatIncreases(BATTLE_PARTNER(battler), FALSE)) - { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - for (i = 0; i < NUM_BATTLE_STATS; i++) - gBattleMons[battler].statStages[i] = gBattleMons[BATTLE_PARTNER(battler)].statStages[i]; - gEffectBattler = BATTLE_PARTNER(battler); - BattleScriptPushCursorAndCallback(BattleScript_CostarActivates); - effect++; - } - break; case ABILITY_ZERO_TO_HERO: - if (!gSpecialStatuses[battler].switchInAbilityDone - && GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES) == SPECIES_PALAFIN_HERO + if (GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES) == SPECIES_PALAFIN_HERO && !GetBattlerPartyState(battler)->transformZeroToHero) { - gSpecialStatuses[battler].switchInAbilityDone = TRUE; GetBattlerPartyState(battler)->transformZeroToHero = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates); - effect++; - } - break; - case ABILITY_HOSPITALITY: - partner = BATTLE_PARTNER(battler); - - if (!gSpecialStatuses[battler].switchInAbilityDone - && IsDoubleBattle() - && !gBattleMons[partner].volatiles.healBlock - && gBattleMons[partner].hp < gBattleMons[partner].maxHP - && IsBattlerAlive(partner)) - { - gEffectBattler = partner; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - SetHealAmount(partner, GetNonDynamaxMaxHP(partner) / 4); - BattleScriptPushCursorAndCallback(BattleScript_HospitalityActivates); + BattleScriptCall(BattleScript_ZeroToHeroActivates); effect++; } break; @@ -4541,7 +4448,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK: case ABILITY_EMBODY_ASPECT_WELLSPRING_MASK: case ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (shouldAbilityTrigger) { enum Stat stat; @@ -4557,43 +4464,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL, gLastUsedAbility)) break; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(stat, 1, FALSE); - BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); - effect++; - } - break; - case ABILITY_TERA_SHIFT: - if (!gSpecialStatuses[battler].switchInAbilityDone - && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL - && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) - { - gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3); - effect++; - } - break; - case ABILITY_COMMANDER: - partner = BATTLE_PARTNER(battler); - if (!gSpecialStatuses[battler].switchInAbilityDone - && IsBattlerAlive(partner) - && IsBattlerAlive(battler) - && gBattleStruct->battlerState[partner].commanderSpecies == SPECIES_NONE - && gBattleMons[partner].species == SPECIES_DONDOZO - && GET_BASE_SPECIES_ID(GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) - { - SaveBattlerAttacker(gBattlerAttacker); - gSpecialStatuses[battler].switchInAbilityDone = TRUE; - gBattlerAttacker = partner; - gBattleStruct->battlerState[battler].commandingDondozo = TRUE; - gBattleStruct->battlerState[partner].commanderSpecies = gBattleMons[battler].species; - gBattleMons[battler].volatiles.semiInvulnerable = STATE_COMMANDER; - if (gBattleMons[battler].volatiles.confusionTurns > 0 && !gBattleMons[battler].volatiles.infiniteConfusion) - gBattleMons[battler].volatiles.confusionTurns--; - BtlController_EmitSpriteInvisibility(battler, B_COMM_TO_CONTROLLER, TRUE); - MarkBattlerForControllerExec(battler); - BattleScriptPushCursorAndCallback(BattleScript_CommanderActivates); + BattleScriptCall(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } break; @@ -5239,7 +5111,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab { if (gBattleWeather & B_WEATHER_PRIMAL_ANY && HasWeatherEffect()) { - BattleScriptCall(BattleScript_BlockedByPrimalWeatherRet); + BattleScriptCall(BattleScript_BlockedByPrimalWeather); effect++; } else if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility)) @@ -5444,7 +5316,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITYEFFECT_OPPORTUNIST: - case ABILITYEFFECT_OPPORTUNIST_FIRST_TURN: switch (ability) { case ABILITY_OPPORTUNIST: @@ -5453,10 +5324,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gBattleScripting.battler = battler; gProtectStructs[battler].activateOpportunist--; ChooseStatBoostAnimation(battler); - if (caseID == ABILITYEFFECT_OPPORTUNIST_FIRST_TURN) - BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChangeEnd3); - else - BattleScriptCall(BattleScript_OpportunistCopyStatChange); + BattleScriptCall(BattleScript_OpportunistCopyStatChange); effect = 1; } break; @@ -5465,13 +5333,10 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITYEFFECT_IMMUNITY: - effect = TryImmunityAbilityHealStatus(battler, caseID); + effect = TryImmunityAbilityHealStatus(battler); if (effect) return effect; break; - case ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES: - effect = TryImmunityAbilityHealStatus(battler, caseID); - break; case ABILITYEFFECT_SYNCHRONIZE: if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE) { @@ -5535,22 +5400,112 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE; } break; - + case ABILITYEFFECT_TERA_SHIFT: + if (ability == ABILITY_TERA_SHIFT + && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL + && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) + { + gBattleScripting.battler = battler; + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; + BattleScriptCall(BattleScript_BattlerFormChangeWithString); + effect++; + } + break; case ABILITYEFFECT_NEUTRALIZINGGAS: - case ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN: - // Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards - if (gLastUsedAbility == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas) + if (ability == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas) { gDisableStructs[battler].neutralizingGas = TRUE; gBattlerAbility = battler; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS; - if (caseID == ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN) - BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); - else - BattleScriptCall(BattleScript_SwitchInAbilityMsgRet); + BattleScriptCall(BattleScript_SwitchInAbilityMsg); effect++; } break; + case ABILITYEFFECT_UNNERVE: + switch (ability) + { + case ABILITY_UNNERVE: + if (shouldAbilityTrigger && !gDisableStructs[battler].unnerveActivated) + { + gBattleScripting.battler = battler; + gEffectBattler = GetOppositeBattler(battler); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_UNNERVE; + gDisableStructs[battler].unnerveActivated = TRUE; + BattleScriptCall(BattleScript_SwitchInAbilityMsg); + effect++; + } + break; + case ABILITY_AS_ONE_ICE_RIDER: + case ABILITY_AS_ONE_SHADOW_RIDER: + if (shouldAbilityTrigger && !gDisableStructs[battler].unnerveActivated) + { + gBattleScripting.battler = battler; + gEffectBattler = GetOppositeBattler(battler); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ASONE; + gDisableStructs[battler].unnerveActivated = TRUE; + BattleScriptCall(BattleScript_ActivateAsOne); + effect++; + } + break; + default: + break; + } + break; + case ABILITYEFFECT_COMMANDER: + gBattleScripting.battler = battler; + partner = BATTLE_PARTNER(battler); + switch (ability) + { + case ABILITY_COMMANDER: + if (IsBattlerAlive(partner) + && IsBattlerAlive(battler) + && gBattleStruct->battlerState[partner].commanderSpecies == SPECIES_NONE + && gBattleMons[partner].species == SPECIES_DONDOZO + && GET_BASE_SPECIES_ID(GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) + { + SaveBattlerAttacker(gBattlerAttacker); + gBattlerAttacker = partner; + gBattleStruct->battlerState[battler].commandingDondozo = TRUE; + gBattleStruct->battlerState[partner].commanderSpecies = gBattleMons[battler].species; + gBattleMons[battler].volatiles.semiInvulnerable = STATE_COMMANDER; + if (gBattleMons[battler].volatiles.confusionTurns > 0 && !gBattleMons[battler].volatiles.infiniteConfusion) + gBattleMons[battler].volatiles.confusionTurns--; + BtlController_EmitSpriteInvisibility(battler, B_COMM_TO_CONTROLLER, TRUE); + MarkBattlerForControllerExec(battler); + BattleScriptCall(BattleScript_CommanderActivates); + effect++; + } + break; + case ABILITY_HOSPITALITY: + if (shouldAbilityTrigger + && IsDoubleBattle() + && !gBattleMons[partner].volatiles.healBlock + && gBattleMons[partner].hp < gBattleMons[partner].maxHP + && IsBattlerAlive(partner)) + { + gEffectBattler = partner; + SetHealAmount(partner, GetNonDynamaxMaxHP(partner) / 4); + BattleScriptCall(BattleScript_HospitalityActivates); + effect++; + } + break; + case ABILITY_COSTAR: + if (shouldAbilityTrigger + && IsDoubleBattle() + && IsBattlerAlive(partner) + && CountBattlerStatIncreases(partner, FALSE)) + { + for (i = 0; i < NUM_BATTLE_STATS; i++) + gBattleMons[battler].statStages[i] = gBattleMons[partner].statStages[i]; + gEffectBattler = partner; + BattleScriptCall(BattleScript_CostarActivates); + effect++; + } + break; + default: + break; + } + break; case ABILITYEFFECT_ON_WEATHER: // For ability effects that activate when the battle weather changes. if (!IsBattlerAlive(battler)) return effect; @@ -5575,7 +5530,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab { gBattleScripting.battler = battler; gDisableStructs[battler].weatherAbilityDone = TRUE; - BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3); + BattleScriptCall(BattleScript_BattlerFormChangeWithString); effect++; } break; @@ -5590,7 +5545,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler); PREPARE_STAT_BUFFER(gBattleTextBuff1, gDisableStructs[battler].paradoxBoostedStat); gBattleScripting.battler = battler; - BattleScriptPushCursorAndCallback(BattleScript_ProtosynthesisActivates); + BattleScriptCall(BattleScript_ProtosynthesisActivates); effect++; } break; @@ -5610,7 +5565,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gDisableStructs[battler].terrainAbilityDone = TRUE; ChangeTypeBasedOnTerrain(battler); gBattlerAbility = gBattleScripting.battler = battler; - BattleScriptPushCursorAndCallback(BattleScript_MimicryActivates); + BattleScriptCall(BattleScript_MimicryActivates); effect++; } break; @@ -5624,7 +5579,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler); PREPARE_STAT_BUFFER(gBattleTextBuff1, gDisableStructs[battler].paradoxBoostedStat); gBattlerAbility = gBattleScripting.battler = battler; - BattleScriptPushCursorAndCallback(BattleScript_QuarkDriveActivates); + BattleScriptCall(BattleScript_QuarkDriveActivates); effect++; } break; @@ -5634,10 +5589,11 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; } - if (effect && gLastUsedAbility != 0xFFFF) + if (effect) + { RecordAbilityBattle(battler, gLastUsedAbility); - if (effect && caseID <= ABILITYEFFECT_MOVE_END) gBattlerAbility = battler; + } return effect; } @@ -5648,7 +5604,7 @@ bool32 TryPrimalReversion(u32 battler) && GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) != gBattleMons[battler].species) { gBattleScripting.battler = battler; - BattleScriptPushCursorAndCallback(BattleScript_PrimalReversion); + BattleScriptCall(BattleScript_PrimalReversion); return TRUE; } return FALSE; @@ -9040,16 +8996,16 @@ void ActivateMegaEvolution(u32 battler) gLastUsedItem = gBattleMons[battler].item; SetActiveGimmick(battler, GIMMICK_MEGA); if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != gBattleMons[battler].species) - BattleScriptExecute(BattleScript_WishMegaEvolution); + BattleScriptPushCursorAndCallback(BattleScript_WishMegaEvolution); else - BattleScriptExecute(BattleScript_MegaEvolution); + BattleScriptPushCursorAndCallback(BattleScript_MegaEvolution); } void ActivateUltraBurst(u32 battler) { gLastUsedItem = gBattleMons[battler].item; SetActiveGimmick(battler, GIMMICK_ULTRA_BURST); - BattleScriptExecute(BattleScript_UltraBurst); + BattleScriptPushCursorAndCallback(BattleScript_UltraBurst); } bool32 IsBattlerMegaEvolved(u32 battler) @@ -9354,18 +9310,15 @@ u32 GetBattlerVisualSpecies(u32 battler) return gBattleMons[battler].species; } -bool32 TryClearIllusion(u32 battler, enum AbilityEffect caseID) +bool32 TryClearIllusion(u32 battler, enum Ability ability) { if (gBattleStruct->illusion[battler].state != ILLUSION_ON) return FALSE; - if (GetBattlerAbility(battler) == ABILITY_ILLUSION && IsBattlerAlive(battler)) + if (ability == ABILITY_ILLUSION && IsBattlerAlive(battler)) return FALSE; gBattleScripting.battler = battler; - if (caseID == ABILITYEFFECT_ON_SWITCHIN) - BattleScriptPushCursorAndCallback(BattleScript_IllusionOffEnd3); - else - BattleScriptCall(BattleScript_IllusionOff); + BattleScriptCall(BattleScript_IllusionOff); return TRUE; } @@ -9459,7 +9412,7 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler) return FALSE; } -u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID) +u32 TryImmunityAbilityHealStatus(u32 battler) { u32 effect = 0; switch (GetBattlerAbilityIgnoreMoldBreaker(battler)) @@ -9526,31 +9479,19 @@ u32 TryImmunityAbilityHealStatus(u32 battler, enum AbilityEffect caseID) { case 1: // status cleared gBattleMons[battler].status1 = 0; - if (caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) - BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); - else - BattleScriptCall(BattleScript_AbilityCuredStatus); + BattleScriptCall(BattleScript_AbilityCuredStatus); break; case 2: // get rid of confusion RemoveConfusionStatus(battler); - if (caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) - BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); - else - BattleScriptCall(BattleScript_AbilityCuredStatus); + BattleScriptCall(BattleScript_AbilityCuredStatus); break; case 3: // get rid of infatuation gBattleMons[battler].volatiles.infatuation = 0; - if (caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) - BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); - else - BattleScriptCall(BattleScript_AbilityCuredStatus); + BattleScriptCall(BattleScript_AbilityCuredStatus); break; case 4: // get rid of taunt gDisableStructs[battler].tauntTimer = 0; - if (caseID == ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES) - BattleScriptExecute(BattleScript_AbilityCuredStatusEnd3); - else - BattleScriptCall(BattleScript_AbilityCuredStatus); + BattleScriptCall(BattleScript_AbilityCuredStatus); break; } @@ -9833,11 +9774,14 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem) gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen = TRUE; } -bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) +bool32 IsBattlerAffectedByHazards(u32 battler, enum HoldEffect holdEffect, bool32 toxicSpikes) { bool32 ret = TRUE; - enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); - if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) + if (!IsBattlerAlive(battler)) + { + ret = FALSE; + } + else if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) { ret = FALSE; RecordItemEffectBattle(battler, holdEffect); @@ -10549,9 +10493,7 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) gBattleScripting.battler = battler; gLastUsedItem = gBattleMons[battler].item; - if (timing == FIRST_TURN) - BattleScriptPushCursorAndCallback(BattleScript_EjectPackActivate_End3); - else if (timing == END_TURN) + if (timing == END_TURN) BattleScriptExecute(BattleScript_EjectPackActivate_End2); else BattleScriptCall(BattleScript_EjectPackActivate_Ret); @@ -10562,6 +10504,23 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) return FALSE; } +bool32 EmergencyExitCanBeTriggered(u32 battler) +{ + enum Ability ability = GetBattlerAbility(battler); + + if (ability != ABILITY_EMERGENCY_EXIT && ability != ABILITY_WIMP_OUT) + return FALSE; + + if (IsBattlerAlive(battler) + && HadMoreThanHalfHpNowDoesnt(battler) + && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) + && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) + && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) + return TRUE; + + return FALSE; +} + #define UNPACK_VOLATILE_GETTERS(_enum, _fieldName, _typeMaxValue, ...) case _enum: return gBattleMons[battler].volatiles._fieldName; // Gets the value of a volatile status flag for a certain battler diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 4b517ccc8f..15a34a1e06 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -517,7 +517,7 @@ void SetZEffect(void) } break; case Z_EFFECT_RESTORE_REPLACEMENT_HP: - gBattleStruct->zmove.healReplacement = TRUE; + gBattleStruct->zmove.healReplacement |= 1u << gBattlerAttacker; BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; gBattlescriptCurrInstr = BattleScript_ZEffectPrintString; diff --git a/src/data/hold_effects.h b/src/data/hold_effects.h index 221c9e166f..cf4fb5f568 100644 --- a/src/data/hold_effects.h +++ b/src/data/hold_effects.h @@ -7,161 +7,138 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [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, .onFling = TRUE, }, @@ -201,7 +178,6 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_DOUBLE_PRIZE] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, }, [HOLD_EFFECT_REPEL] = @@ -424,14 +400,12 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_RESTORE_PCT_HP] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, .onHpThreshold = TRUE, }, [HOLD_EFFECT_MICLE_BERRY] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, .onHpThreshold = TRUE, }, @@ -492,7 +466,6 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = { .onTargetAfterHit = TRUE, .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, }, [HOLD_EFFECT_RED_CARD] = @@ -559,7 +532,6 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_TERRAIN_SEED] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, .onEffect = TRUE, }, @@ -586,7 +558,6 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_ROOM_SERVICE] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, .onEffect = TRUE, }, @@ -615,7 +586,6 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_MIRROR_HERB] = { .mirrorHerb = TRUE, - .mirrorHerbFirstTurn = TRUE, }, [HOLD_EFFECT_PUNCHING_GLOVE] = @@ -632,9 +602,8 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_BOOSTER_ENERGY] = { - .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, .onEffect = TRUE, + .boosterEnergy = TRUE, }, [HOLD_EFFECT_OGERPON_MASK] = @@ -644,6 +613,5 @@ const struct HoldEffectInfo gHoldEffectsInfo[HOLD_EFFECT_COUNT] = [HOLD_EFFECT_BERSERK_GENE] = { .onSwitchIn = TRUE, - .onSwitchInFirstTurn = TRUE, }, }; diff --git a/src/pokemon.c b/src/pokemon.c index 16c3844527..9e8ee13a42 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3728,7 +3728,7 @@ void CopyPartyMonToBattleData(u32 battler, u32 partyIndex) u32 side = GetBattlerSide(battler); struct Pokemon *party = GetSideParty(side); PokemonToBattleMon(&party[partyIndex], &gBattleMons[battler]); - gBattleStruct->hpOnSwitchout[side] = gBattleMons[battler].hp; + gBattleStruct->battlerState[battler].hpOnSwitchout = gBattleMons[battler].hp; UpdateSentPokesToOpponentValue(battler); ClearTemporarySpeciesSpriteData(battler, FALSE, FALSE); } diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c index c64590a3a3..b80887bd5f 100644 --- a/test/battle/ability/commander.c +++ b/test/battle/ability/commander.c @@ -66,9 +66,9 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri will still take residual damage from a f } WHEN { TURN { } } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM); ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); - ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM); MESSAGE("Dondozo is buffeted by the sandstorm!"); MESSAGE("Tatsugiri is buffeted by the sandstorm!"); MESSAGE("The opposing Wobbuffet is buffeted by the sandstorm!"); diff --git a/test/battle/ability/costar.c b/test/battle/ability/costar.c index 3d041a8d53..5876cc0156 100644 --- a/test/battle/ability/costar.c +++ b/test/battle/ability/costar.c @@ -25,6 +25,35 @@ DOUBLE_BATTLE_TEST("Costar copies an ally's stat stages upon entering battle") } } +DOUBLE_BATTLE_TEST("Costar copies an ally's stat stages after their ability activates upon entering battle") +{ + u32 speedLeft, speedRight = 0; + + PARAMETRIZE { speedLeft = 200; speedRight = 150; } + PARAMETRIZE { speedLeft = 150; speedRight = 200; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(100); } + PLAYER(SPECIES_WOBBUFFET) { Speed(110); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); HP(1); }; + OPPONENT(SPECIES_WYNAUT) { Speed(10); HP(1); }; + OPPONENT(SPECIES_FLAMIGO) { Speed(speedLeft); Ability(ABILITY_COSTAR); } + OPPONENT(SPECIES_ZACIAN) { Speed(speedRight); Ability(ABILITY_INTREPID_SWORD); } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_HYPER_VOICE); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + } + } SCENE { + ABILITY_POPUP(opponentRight, ABILITY_INTREPID_SWORD); + ABILITY_POPUP(opponentLeft, ABILITY_COSTAR); + } THEN { + EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } +} + // Copy from Ruin ability tests TO_DO_BATTLE_TEST("Costar's message displays correctly after all battlers fainted - Player"); TO_DO_BATTLE_TEST("Costar's message displays correctly after all battlers fainted - Opponent"); diff --git a/test/battle/ability/hospitality.c b/test/battle/ability/hospitality.c index 2c89c3eb97..b00e78db90 100644 --- a/test/battle/ability/hospitality.c +++ b/test/battle/ability/hospitality.c @@ -111,3 +111,39 @@ DOUBLE_BATTLE_TEST("Hospitality is blocked by Heal Block") } } } + +DOUBLE_BATTLE_TEST("Hospitality user restores 25% of ally's max HP after taking hazard damage") +{ + u32 speedLeft, speedRight = 0; + + PARAMETRIZE { speedLeft = 200; speedRight = 150; } + PARAMETRIZE { speedLeft = 150; speedRight = 200; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); HP(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(1); HP(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(speedLeft); }; + PLAYER(SPECIES_POLTCHAGEIST) { Speed(speedRight); Ability(ABILITY_HOSPITALITY); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(110); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(100); }; + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_SPIKES); + MOVE(opponentRight, MOVE_HYPER_VOICE); + SEND_OUT(playerLeft, 2); + SEND_OUT(playerRight, 3); + } + } SCENE { + HP_BAR(playerLeft); + HP_BAR(playerRight); + if (speedLeft == 200) { + HP_BAR(playerLeft); // Spikes dmg + HP_BAR(playerRight); // Spikes dmg + } else { + HP_BAR(playerRight); // Spikes dmg + HP_BAR(playerLeft); // Spikes dmg + } + ABILITY_POPUP(playerRight, ABILITY_HOSPITALITY); + HP_BAR(playerLeft); // Hospitality Heal + } +} diff --git a/test/battle/ability/tera_shift.c b/test/battle/ability/tera_shift.c index 9149160cd1..b6c55111f3 100644 --- a/test/battle/ability/tera_shift.c +++ b/test/battle/ability/tera_shift.c @@ -25,11 +25,29 @@ SINGLE_BATTLE_TEST("Tera Shift can't be suppressed by Neutralizing Gas") } WHEN { TURN { ; } } SCENE { - ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); ABILITY_POPUP(player, ABILITY_TERA_SHIFT); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); MESSAGE("Terapagos transformed!"); + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); } THEN { EXPECT_EQ(player->species, SPECIES_TERAPAGOS_TERASTAL); } } + +SINGLE_BATTLE_TEST("Tera Shift activates before Neutralizing Gas regardless of Speed") +{ + u32 speed = 0; + + PARAMETRIZE { speed = 50; } + PARAMETRIZE { speed = 150; } + + GIVEN { + PLAYER(SPECIES_TERAPAGOS_NORMAL) { Speed(speed); Ability(ABILITY_TERA_SHIFT); } + OPPONENT(SPECIES_KOFFING) { Speed(100); Ability(ABILITY_NEUTRALIZING_GAS); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_TERA_SHIFT); + ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS); + } +} diff --git a/test/battle/ability/unnerve.c b/test/battle/ability/unnerve.c index a1eabbcf5a..b88f77656d 100644 --- a/test/battle/ability/unnerve.c +++ b/test/battle/ability/unnerve.c @@ -124,3 +124,21 @@ DOUBLE_BATTLE_TEST("Unnerve stops applying on death but applies on revive") } } + +SINGLE_BATTLE_TEST("Unnerve activates before other switch in abilities regardless of Speed") +{ + u32 speed = 0; + + PARAMETRIZE { speed = 50; } + PARAMETRIZE { speed = 150; } + + GIVEN { + PLAYER(SPECIES_PINSIR) { Speed(100); Ability(ABILITY_MOLD_BREAKER); } + OPPONENT(SPECIES_JOLTIK) { Speed(speed); Ability(ABILITY_UNNERVE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(opponent, ABILITY_UNNERVE); + ABILITY_POPUP(player, ABILITY_MOLD_BREAKER); + } +} diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 40a1d16778..ba61df328e 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -66,12 +66,12 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same Pokémon for 2 spo { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); PLAYER(SPECIES_RATTATA); PLAYER(SPECIES_RATTATA); // No moves to damage player. @@ -96,12 +96,12 @@ AI_MULTI_BATTLE_TEST("AI partner will not switch mid-turn into a player Pokémon { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_HAUNTER); MULTI_PLAYER(SPECIES_RATTATA); // No moves to damage opponents. @@ -110,7 +110,7 @@ AI_MULTI_BATTLE_TEST("AI partner will not switch mid-turn into a player Pokémon MULTI_PARTNER(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); } MULTI_OPPONENT_A(SPECIES_RATTATA) { Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_B(SPECIES_KANGASKHAN) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { EXPECT_SWITCH(playerRight, 5); }; } SCENE { @@ -128,12 +128,12 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch mid-turn into a player Pok { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_HAUNTER); MULTI_PLAYER(SPECIES_RATTATA); // No moves to damage opponents. @@ -142,7 +142,7 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch mid-turn into a player Pok MULTI_PARTNER(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); } MULTI_OPPONENT_A(SPECIES_RATTATA) { Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_A(SPECIES_KANGASKHAN) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { EXPECT_SWITCH(playerRight, 5); }; } SCENE { @@ -160,12 +160,12 @@ AI_MULTI_BATTLE_TEST("AI partner will not switch into a player Pokémon after fa { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE {flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE {flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_GENGAR); MULTI_PLAYER(SPECIES_RATTATA); // No moves to damage opponents. @@ -174,7 +174,7 @@ AI_MULTI_BATTLE_TEST("AI partner will not switch into a player Pokémon after fa MULTI_PARTNER(SPECIES_HAUNTER); MULTI_OPPONENT_A(SPECIES_TRAPINCH) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_B(SPECIES_VIBRAVA) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { EXPECT_MOVE(playerRight, MOVE_CELEBRATE); EXPECT_SEND_OUT(playerRight, 5); }; } SCENE { @@ -190,12 +190,12 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch into a player Pokémon aft { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_GENGAR); MULTI_PLAYER(SPECIES_RATTATA); // No moves to damage opponents. @@ -204,7 +204,7 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch into a player Pokémon aft MULTI_PARTNER(SPECIES_HAUNTER); MULTI_OPPONENT_A(SPECIES_TRAPINCH) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_A(SPECIES_VIBRAVA) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { EXPECT_MOVE(playerRight, MOVE_CELEBRATE); EXPECT_SEND_OUT(playerRight, 5); }; } SCENE { @@ -233,7 +233,7 @@ AI_MULTI_BATTLE_TEST("AI partner will not switch into a player Pokémon (multi)" MULTI_PARTNER(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); HP(1); } MULTI_OPPONENT_A(SPECIES_RATTATA) { Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_B(SPECIES_KANGASKHAN) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { MOVE(playerLeft, MOVE_AURA_SPHERE, target:playerRight); EXPECT_SWITCH(playerRight, 4); EXPECT_SEND_OUT(playerRight, 3); }; TURN { EXPECT_MOVE(playerRight, MOVE_SHADOW_BALL, target:opponentLeft); }; @@ -250,12 +250,12 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch into a player Pokémon (2v { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_HAUNTER); MULTI_PLAYER(SPECIES_RATTATA); // No moves to damage opponents. @@ -263,7 +263,7 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI partner will not switch into a player Pokémon (2v MULTI_PARTNER(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); HP(1); } MULTI_OPPONENT_A(SPECIES_RATTATA) { Moves(MOVE_CELEBRATE); } MULTI_OPPONENT_A(SPECIES_KANGASKHAN) { Moves(MOVE_CELEBRATE); } - + } WHEN { TURN { MOVE(playerLeft, MOVE_AURA_SPHERE, target:playerRight); EXPECT_SWITCH(playerRight, 4); EXPECT_SEND_OUT(playerRight, 3); }; TURN { EXPECT_MOVE(playerRight, MOVE_SHADOW_BALL, target:opponentLeft); }; @@ -279,12 +279,12 @@ AI_TWO_VS_ONE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE {flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE {flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_RATTATA); MULTI_PLAYER(SPECIES_RATTATA); MULTI_PARTNER(SPECIES_KANGASKHAN); @@ -309,12 +309,12 @@ AI_ONE_VS_TWO_BATTLE_TEST("AI will not switch into a partner Pokémon in a 1v2 b { u32 flags; - PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } - PARAMETRIZE {flags = 0; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); + AI_FLAGS(flags); MULTI_PLAYER(SPECIES_RATTATA); MULTI_PLAYER(SPECIES_KANGASKHAN); // No moves to damage player. @@ -322,7 +322,7 @@ AI_ONE_VS_TWO_BATTLE_TEST("AI will not switch into a partner Pokémon in a 1v2 b MULTI_OPPONENT_B(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); } MULTI_OPPONENT_B(SPECIES_GASTLY) { Moves(MOVE_LICK); } MULTI_OPPONENT_B(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); } - + } WHEN { TURN { EXPECT_SWITCH(opponentRight, 5); }; } SCENE { @@ -417,11 +417,11 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Switch effect moves will send AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Eject Button will send out Ace Mon if it's the only one remaining") { - u32 aiSmartMonChoicesFlag; - PARAMETRIZE { aiSmartMonChoicesFlag = 0; } - PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } + u32 flags; + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ACE_POKEMON; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_ACE_POKEMON; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag | AI_FLAG_ACE_POKEMON); + AI_FLAGS(flags); PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); } OPPONENT(SPECIES_ZIGZAGOON) { Item(ITEM_EJECT_BUTTON); }; OPPONENT(SPECIES_LINOONE); @@ -432,11 +432,11 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Eject Button will send out Ace AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Eject Pack will send out Ace Mon if it's the only one remaining") { - u32 aiSmartMonChoicesFlag; - PARAMETRIZE { aiSmartMonChoicesFlag = 0; } - PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } + u32 flags; + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ACE_POKEMON; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_ACE_POKEMON; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag | AI_FLAG_ACE_POKEMON); + AI_FLAGS(flags); PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); } PLAYER(SPECIES_ARCANINE) { Ability(ABILITY_INTIMIDATE); Moves(MOVE_SCRATCH); } OPPONENT(SPECIES_ZIGZAGOON) { Item(ITEM_EJECT_PACK); Moves(MOVE_SCRATCH); } @@ -536,13 +536,13 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemo AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage when choosing which Pokemon to switch in") { u32 aiIsSmart = 0; - u32 aiSmartSwitchFlags = 0; + u32 flags = 0; - PARAMETRIZE { aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd - PARAMETRIZE { aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage + PARAMETRIZE { aiIsSmart = 0; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd + PARAMETRIZE { aiIsSmart = 1; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); + AI_FLAGS(flags); PLAYER(SPECIES_MEGANIUM) { Speed(100); SpDefense(328); SpAttack(265); Moves(MOVE_STEALTH_ROCK, MOVE_SURF); } // Meganium does ~56% minimum ~66% maximum, enough to KO Charizard after rocks and never KO Typhlosion after rocks OPPONENT(SPECIES_PONYTA) { Level(5); Speed(5); Moves(MOVE_SCRATCH); } OPPONENT(SPECIES_CHARIZARD) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium @@ -555,18 +555,18 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize type matchup + SE move, then type matchup") { - u32 aiSmartSwitchFlags = 0; + u32 flags = 0; u32 move1; u32 move2; u32 expectedIndex; - PARAMETRIZE { expectedIndex = 3; move1 = MOVE_SCRATCH; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage - PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = 0; } - PARAMETRIZE { expectedIndex = 2; move1 = MOVE_SCRATCH; move2 = MOVE_WATER_PULSE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move - PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } + PARAMETRIZE { expectedIndex = 3; move1 = MOVE_SCRATCH; move2 = MOVE_SCRATCH; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } + PARAMETRIZE { expectedIndex = 2; move1 = MOVE_SCRATCH; move2 = MOVE_WATER_PULSE; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_SCRATCH; flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); + AI_FLAGS(flags); PLAYER(SPECIES_MARSHTOMP) { Level(30); Moves(MOVE_MUD_BOMB, MOVE_WATER_GUN, MOVE_GROWL, MOVE_MUD_SHOT); Speed(5); } OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(6); } // Forces switchout OPPONENT(SPECIES_TANGELA) { Level(30); Moves(move1); Speed(4); } @@ -739,21 +739,21 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo // Trapping behaviour AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch in trapping mon mid battle") { - u32 aiSmartSwitchingFlag = 0; - PARAMETRIZE { aiSmartSwitchingFlag = 0; } - PARAMETRIZE { aiSmartSwitchingFlag = AI_FLAG_SMART_SWITCHING; } + u32 flags = 0; + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING; } PASSES_RANDOMLY(SHOULD_SWITCH_TRAPPER_PERCENTAGE, 100, RNG_AI_SWITCH_TRAPPER); GIVEN { ASSUME(GetSpeciesType(SPECIES_GOLURK, 0) == TYPE_GROUND); ASSUME(GetSpeciesType(SPECIES_GOLURK, 1) == TYPE_GHOST); - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchingFlag); + AI_FLAGS(flags); PLAYER(SPECIES_ELECTRODE) { Speed(4); Moves(MOVE_THUNDERBOLT, MOVE_AURA_SPHERE, MOVE_PROTECT); } PLAYER(SPECIES_WOBBUFFET) { Speed(1); }; OPPONENT(SPECIES_SNORLAX) { Speed(1); Moves(MOVE_HEADBUTT); } OPPONENT(SPECIES_DUGTRIO) { Speed(3); Ability(ABILITY_ARENA_TRAP); Moves(MOVE_EARTHQUAKE); } OPPONENT(SPECIES_GOLURK) { Speed(5); Moves(MOVE_EARTHQUAKE); } } WHEN { - if (aiSmartSwitchingFlag == AI_FLAG_SMART_SWITCHING) + if (flags & AI_FLAG_SMART_SWITCHING) TURN { MOVE(player, MOVE_AURA_SPHERE) ; EXPECT_SWITCH(opponent, 1); } else TURN { MOVE(player, MOVE_AURA_SPHERE) ; EXPECT_MOVE(opponent, MOVE_HEADBUTT); } @@ -762,19 +762,19 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch in trapping mon m AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will switch in trapping mon after KO") { - u32 aiSmartMonChoicesFlag = 0; // Enables trapping behaviour after KOs - PARAMETRIZE { aiSmartMonChoicesFlag = 0; } // No trapping behaviour - PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches + u32 flags = 0; // Enables trapping behaviour after KOs + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } // No trapping behaviour + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches GIVEN { ASSUME(GetSpeciesType(SPECIES_MAWILE, 0) == TYPE_STEEL); - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag); + AI_FLAGS(flags); PLAYER(SPECIES_MAWILE) { Speed(2); Moves(MOVE_PROTECT, MOVE_SCRATCH); } PLAYER(SPECIES_WOBBUFFET) { Speed(1); } OPPONENT(SPECIES_SNORLAX) { Speed(3); Moves(MOVE_SELF_DESTRUCT); } OPPONENT(SPECIES_MAGNEZONE) { Speed(1); Ability(ABILITY_MAGNET_PULL); Moves(MOVE_SHOCK_WAVE); } OPPONENT(SPECIES_MEGANIUM) { Speed(3); Moves(MOVE_EARTH_POWER); } } WHEN { - if (aiSmartMonChoicesFlag == AI_FLAG_SMART_MON_CHOICES) + if (flags & AI_FLAG_SMART_MON_CHOICES) TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 1); } else TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 2); } @@ -783,12 +783,12 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will switch in trapping mon AI_SINGLE_BATTLE_TEST("AI won't use trapping behaviour if player only has 1 mon left") { - u32 aiSmartMonChoicesFlag = 0; // Enables trapping behaviour after KOs - PARAMETRIZE { aiSmartMonChoicesFlag = 0; } // No trapping behaviour - PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches + u32 flags = 0; // Enables trapping behaviour after KOs + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; } // No trapping behaviour + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches GIVEN { ASSUME(GetSpeciesType(SPECIES_MAWILE, 0) == TYPE_STEEL); - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag); + AI_FLAGS(flags); PLAYER(SPECIES_MAWILE) { Speed(2); Moves(MOVE_PROTECT, MOVE_SCRATCH); } OPPONENT(SPECIES_SNORLAX) { Speed(3); Moves(MOVE_SELF_DESTRUCT); } OPPONENT(SPECIES_MAGNEZONE) { Speed(1); Ability(ABILITY_MAGNET_PULL); Moves(MOVE_SHOCK_WAVE); } @@ -1242,6 +1242,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and oppon } } +#if 0 AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and opponent has invulnerability move and is faster") { PASSES_RANDOMLY(SHOULD_SWITCH_TRUANT_PERCENTAGE, 100, RNG_AI_SWITCH_TRUANT); @@ -1255,6 +1256,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and oppon TURN { SKIP_TURN(player); EXPECT_SWITCH(opponent, 1); } } } +#endif AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if main attacking stat lowered by 2 stages with good switchin candidate 50% of the time") { @@ -1332,18 +1334,18 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI correctly handles abilities AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't switch out if Yawn'd with only Ace mon remaining") { - u32 aceFlag; - PARAMETRIZE{ aceFlag = 0; } - PARAMETRIZE{ aceFlag = AI_FLAG_ACE_POKEMON; } + u32 flags; + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING | AI_FLAG_ACE_POKEMON; } GIVEN { ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | aceFlag | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING); + AI_FLAGS(flags); PLAYER(SPECIES_SLAKOTH) { Moves(MOVE_SCRATCH, MOVE_YAWN); } OPPONENT(SPECIES_SLAKOTH) { Moves(MOVE_SCRATCH); } OPPONENT(SPECIES_SLAKOTH) { Moves(MOVE_HEADBUTT); } } WHEN { TURN { MOVE(player, MOVE_YAWN); EXPECT_MOVE(opponent, MOVE_SCRATCH); } - if (aceFlag) + if (flags & AI_FLAG_ACE_POKEMON) TURN { MOVE(player, MOVE_SCRATCH); EXPECT_MOVE(opponent, MOVE_SCRATCH); } else TURN { MOVE(player, MOVE_SCRATCH); EXPECT_SWITCH(opponent, 1); } @@ -1352,17 +1354,17 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't switch out if Yawn'd wi AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't switch in ace mon after U-Turn if other options available") { - u32 aceFlag; - PARAMETRIZE{ aceFlag = 0; } - PARAMETRIZE{ aceFlag = AI_FLAG_ACE_POKEMON; } + u32 flags; + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING; } + PARAMETRIZE { flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ACE_POKEMON | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | aceFlag | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_SMART_SWITCHING); + AI_FLAGS(flags); PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SURF); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_U_TURN); } OPPONENT(SPECIES_NUMEL) { Level(5); Moves(MOVE_SPLASH); } OPPONENT(SPECIES_SCIZOR) { Moves(MOVE_BUG_BITE); } } WHEN { - if (aceFlag) + if (flags & AI_FLAG_ACE_POKEMON) TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 1); MOVE(player, MOVE_SURF); } else TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 2); MOVE(player, MOVE_SURF); } @@ -1371,17 +1373,17 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI won't switch in ace mon after AI_SINGLE_BATTLE_TEST("Switch AI: AI won't switch in ace mon after U-Turn if other options available") { - u32 aceFlag; - PARAMETRIZE{ aceFlag = 0; } - PARAMETRIZE{ aceFlag = AI_FLAG_ACE_POKEMON; } + u32 flag; + PARAMETRIZE{ flag = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT; } + PARAMETRIZE{ flag = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ACE_POKEMON | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | aceFlag | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT); + AI_FLAGS(flag); PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SURF); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_U_TURN); } OPPONENT(SPECIES_NUMEL) { Level(5); Moves(MOVE_SPLASH); } OPPONENT(SPECIES_SCIZOR) { Moves(MOVE_BUG_BITE); } } WHEN { - if (aceFlag) + if (flag & AI_FLAG_ACE_POKEMON) TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 1); MOVE(player, MOVE_SURF); } else TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 2); MOVE(player, MOVE_SURF); } diff --git a/test/battle/end_turn_effects.c b/test/battle/end_turn_effects.c index 931f792efe..2fe68e5cef 100644 --- a/test/battle/end_turn_effects.c +++ b/test/battle/end_turn_effects.c @@ -139,3 +139,34 @@ ONE_VS_TWO_BATTLE_TEST("End Turn Effects: First Event Block is executed correctl EXPECT_GT(damage, 0); } } + +DOUBLE_BATTLE_TEST("End Turn Effects: New mons will switch in after if previous mons died due to hazards") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { HP(1); }; + OPPONENT(SPECIES_WOBBUFFET) { HP(1); }; + OPPONENT(SPECIES_WYNAUT) { HP(1); }; + OPPONENT(SPECIES_WOBBUFFET) { HP(1); }; + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(playerLeft, MOVE_SPIKES); + MOVE(playerRight, MOVE_HYPER_VOICE); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + SEND_OUT(opponentLeft, 4); + SEND_OUT(opponentRight, 5); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, playerRight); + HP_BAR(opponentLeft); + HP_BAR(opponentRight); + HP_BAR(opponentLeft); + HP_BAR(opponentRight); + HP_BAR(opponentLeft); + HP_BAR(opponentRight); + } +} diff --git a/test/battle/form_change/primal_reversion.c b/test/battle/form_change/primal_reversion.c index 7ae591f27b..4c458b7cdd 100644 --- a/test/battle/form_change/primal_reversion.c +++ b/test/battle/form_change/primal_reversion.c @@ -295,14 +295,14 @@ DOUBLE_BATTLE_TEST("Primal reversion and other switch-in effects trigger for all ASSUME(GetMoveEffect(MOVE_STICKY_WEB) == EFFECT_STICKY_WEB); ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES); ASSUME(GetMoveEffect(MOVE_TOXIC_SPIKES) == EFFECT_TOXIC_SPIKES); - PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_CATERPIE) { HP(1); } - PLAYER(SPECIES_SCRAFTY) { Ability(ABILITY_INTIMIDATE); } - PLAYER(SPECIES_RESHIRAM); - OPPONENT(SPECIES_CATERPIE) { HP(1); } - OPPONENT(SPECIES_CATERPIE) { HP(1); } - OPPONENT(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); } - OPPONENT(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + PLAYER(SPECIES_CATERPIE) { Speed(8); HP(1); } + PLAYER(SPECIES_SCRAFTY) { Speed(100); Ability(ABILITY_INTIMIDATE); } + PLAYER(SPECIES_RESHIRAM) { Speed(90); } + OPPONENT(SPECIES_CATERPIE) { Speed(9); HP(1); } + OPPONENT(SPECIES_CATERPIE) { Speed(7); HP(1); } + OPPONENT(SPECIES_KYOGRE) { Speed(80); Item(ITEM_BLUE_ORB); } + OPPONENT(SPECIES_GROUDON) { Speed(70); Item(ITEM_RED_ORB); } } WHEN { TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(opponentLeft, MOVE_SPIKES); diff --git a/test/battle/hazards.c b/test/battle/hazards.c index ea0aef70e5..aa67c983f9 100644 --- a/test/battle/hazards.c +++ b/test/battle/hazards.c @@ -64,8 +64,6 @@ SINGLE_BATTLE_TEST("Hazards are applied correctly after a battler faints") SINGLE_BATTLE_TEST("Toxic Spikes can be removed after fainting to other hazards") { - KNOWN_FAILING; // tryfaintmon changes something that doesn't allow other switch-in effects on the battler - GIVEN { PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_GRIMER) { HP(1); } @@ -134,13 +132,13 @@ DOUBLE_BATTLE_TEST("Hazards can trigger Emergency Exit and hazards still activat { GIVEN { ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT); - PLAYER(SPECIES_WOBBUFFET) { HP(1); } - PLAYER(SPECIES_WOBBUFFET) { HP(1); } - PLAYER(SPECIES_GOLISOPOD) { HP(105); MaxHP(200); Ability(ABILITY_EMERGENCY_EXIT); } - PLAYER(SPECIES_WYNAUT); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET) { Speed(1); HP(1); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); HP(1); } + PLAYER(SPECIES_GOLISOPOD) { Speed(10); HP(105); MaxHP(200); Ability(ABILITY_EMERGENCY_EXIT); } + PLAYER(SPECIES_WYNAUT) { Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } } WHEN { TURN { MOVE(opponentLeft, MOVE_STEALTH_ROCK); MOVE(opponentRight, MOVE_TOXIC_SPIKES); } TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_SPIKES); } diff --git a/test/battle/move_effect/court_change.c b/test/battle/move_effect/court_change.c index 40a1fe4e86..c5a44635eb 100644 --- a/test/battle/move_effect/court_change.c +++ b/test/battle/move_effect/court_change.c @@ -171,9 +171,7 @@ DOUBLE_BATTLE_TEST("Court Change used by the player swaps G-Max Steelsurge") MESSAGE("Copperajah used G-Max Steelsurge!"); SEND_IN_MESSAGE("Wobbuffet"); MESSAGE("The sharp steel bit into Wobbuffet!"); - NONE_OF { - MESSAGE("The sharp steel bit into the opposing Wynaut!"); - } + NOT MESSAGE("The sharp steel bit into the opposing Wynaut!"); } }