From 32f8464fec640c8bb53596c3ca545908cad144f1 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:00:30 +0200 Subject: [PATCH] Attackcanceller fixes and improvements (#7698) --- asm/macros/battle_script.inc | 45 +- data/battle_scripts_1.s | 98 +-- docs/tutorials/how_to_new_move.md | 15 +- include/battle.h | 6 +- include/battle_ai_util.h | 3 +- include/battle_dynamax.h | 1 - include/battle_scripts.h | 10 +- include/battle_terastal.h | 2 +- include/battle_util.h | 18 +- include/constants/battle.h | 3 +- include/constants/battle_string_ids.h | 17 - src/battle_ai_main.c | 19 +- src/battle_ai_util.c | 31 +- src/battle_dynamax.c | 15 - src/battle_main.c | 2 + src/battle_message.c | 17 +- src/battle_script_commands.c | 336 +++----- src/battle_terastal.c | 21 +- src/battle_util.c | 738 +++++++++++------- src/data/battle_move_effects.h | 14 +- test/battle/ability/dazzling.c | 22 + test/battle/ability/teraform_zero.c | 1 + test/battle/gimmick/dynamax.c | 15 +- test/battle/gimmick/zmove.c | 2 +- test/battle/move_effect/aurora_veil.c | 29 + test/battle/move_effect/clangorous_soul.c | 55 +- .../battle/move_effect/fail_if_not_arg_type.c | 1 - test/battle/move_effect/no_retreat.c | 57 ++ test/battle/move_effect/psychic_terrain.c | 38 +- test/battle/move_effect/stockpile.c | 6 +- 30 files changed, 860 insertions(+), 777 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 0a50ce49d0..afbd257d21 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -724,14 +724,12 @@ .byte \mode .endm - .macro trysetrest failInstr:req + .macro trysetrest .byte 0x81 - .4byte \failInstr .endm - .macro jumpifnotfirstturn jumpInstr:req + .macro unused_0x82 .byte 0x82 - .4byte \jumpInstr .endm .macro unused_0x83 @@ -748,9 +746,8 @@ .byte \id .endm - .macro stockpiletobasedamage failInstr:req + .macro stockpiletobasedamage .byte 0x86 - .4byte \failInstr .endm .macro stockpiletohpheal failInstr:req @@ -1045,9 +1042,8 @@ .byte 0xc2 .endm - .macro trysetfutureattack failInstr:req + .macro setfutureattack .byte 0xc3 - .4byte \failInstr .endm .macro trydobeatup endInstr, failInstr @@ -1224,10 +1220,8 @@ .4byte \jumpInstr .endm - .macro jumpifnotcurrentmoveargtype battler:req, failInstr:req + .macro unused_0xE4 .byte 0xe4 - .byte \battler - .4byte \failInstr .endm .macro pickup @@ -1378,12 +1372,6 @@ .4byte \failInstr .endm - .macro jumpifcantfling battler:req, jumpInstr:req - callnative BS_JumpIfCantFling - .byte \battler - .4byte \jumpInstr - .endm - .macro itemstatchangeeffects battler:req callnative BS_RunStatChangeItems .byte \battler @@ -1438,10 +1426,6 @@ .4byte \failInstr .endm - .macro setglaiverush - callnative BS_SetGlaiveRush - .endm - .macro setpledge jumpInstr:req callnative BS_SetPledge .4byte \jumpInstr @@ -2179,11 +2163,6 @@ .4byte \failInstr .endm - .macro suckerpunchcheck failInstr:req - callnative BS_SuckerPunchCheck - .4byte \failInstr - .endm - .macro setsimplebeam failInstr:req callnative BS_SetSimpleBeam .4byte \failInstr @@ -2218,11 +2197,6 @@ .byte \case_ .endm - .macro trylastresort failInstr:req - callnative BS_TryLastResort - .4byte \failInstr - .endm - .macro tryautotomize failInstr:req callnative BS_TryAutotomize .4byte \failInstr @@ -2411,13 +2385,8 @@ .4byte \failInstr .endm - .macro checkpoltergeist failInstr:req - callnative BS_CheckPoltergeist - .4byte \failInstr - .endm - - .macro trynoretreat failInstr:req - callnative BS_TryNoRetreat + .macro setpoltergeistmessage failInstr:req + callnative BS_SetPoltergeistMessage .4byte \failInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 1a7e91193f..645481bedd 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -210,7 +210,7 @@ BattleScript_EffectDoodleMoveEnd: BattleScript_EffectGlaiveRush:: call BattleScript_EffectHit_Ret jumpifmoveresultflags MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_TryFaintMon - setglaiverush + setvolatile BS_ATTACKER, VOLATILE_GLAIVE_RUSH, 2 goto BattleScript_TryFaintMon BattleScript_SyrupBombActivates:: @@ -685,7 +685,6 @@ BattleScript_SkyDropFlyingAlreadyConfused: BattleScript_EffectFling:: attackcanceler - jumpifcantfling BS_ATTACKER, BattleScript_ButItFailed setlastuseditem BS_ATTACKER accuracycheck BattleScript_FlingMissed, ACC_CURR_MOVE pause B_WAIT_TIME_SHORT @@ -776,11 +775,6 @@ BattleScript_FlingMissed: removeitem BS_ATTACKER goto BattleScript_MoveMissedPause -BattleScript_EffectAuraWheel:: @ Aura Wheel can only be used by Morpeko - jumpifspecies SPECIES_MORPEKO_FULL_BELLY, BattleScript_EffectHit - jumpifspecies SPECIES_MORPEKO_HANGRY, BattleScript_EffectHit - goto BattleScript_PokemonCantUseTheMove - BattleScript_EffectClangorousSoul:: attackcanceler cutonethirdhpandraisestats BattleScript_ButItFailed @@ -819,7 +813,7 @@ BattleScript_OctlockTurnDmgEnd: BattleScript_EffectPoltergeist:: attackcanceler accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - checkpoltergeist BattleScript_ButItFailed + setpoltergeistmessage BattleScript_ButItFailed printstring STRINGID_ABOUTTOUSEPOLTERGEIST waitmessage B_WAIT_TIME_LONG goto BattleScript_HitFromCritCalc @@ -844,7 +838,8 @@ BattleScript_TryTarShot: BattleScript_EffectNoRetreat:: attackcanceler accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - trynoretreat BattleScript_ButItFailed + jumpifvolatile BS_TARGET, VOLATILE_NO_RETREAT, BattleScript_ButItFailed + setvolatile BS_TARGET, VOLATILE_NO_RETREAT attackanimation waitanimation call BattleScript_AllStatsUp @@ -901,7 +896,6 @@ BattleScript_MoveEffectLightScreen:: BattleScript_EffectStuffCheeks:: attackcanceler - jumpifnotberry BS_ATTACKER, BattleScript_ButItFailed attackanimation waitanimation setbyte sBERRY_OVERRIDE, 1 @@ -1049,12 +1043,6 @@ BattleScript_EffectFairyLock:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_FailIfNotArgType:: - attackcanceler - jumpifnotcurrentmoveargtype BS_ATTACKER, BattleScript_ButItFailed - accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - goto BattleScript_HitFromCritCalc - BattleScript_RemoveFireType:: printstring STRINGID_ATTACKERLOSTFIRETYPE waitmessage B_WAIT_TIME_LONG @@ -1747,12 +1735,6 @@ BattleScript_ShellSmashTrySpeed: BattleScript_ShellSmashEnd: goto BattleScript_MoveEnd -BattleScript_EffectLastResort:: - attackcanceler - trylastresort BattleScript_ButItFailed - accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - goto BattleScript_HitFromCritCalc - BattleScript_EffectGrowth:: attackcanceler jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_GrowthDoMoveAnim @@ -2064,12 +2046,6 @@ BattleScript_EffectSimpleBeam:: tryendneutralizinggas goto BattleScript_MoveEnd -BattleScript_EffectSuckerPunch:: - attackcanceler - suckerpunchcheck BattleScript_ButItFailed - accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE - goto BattleScript_HitFromAtkString - BattleScript_EffectLuckyChant:: attackcanceler setluckychant BattleScript_ButItFailed @@ -2577,7 +2553,7 @@ BattleScript_AbilityProtectsDoesntAffect:: setmoveresultflags MOVE_RESULT_FAILED goto BattleScript_MoveEnd -BattleScript_InsomniaProtects: +BattleScript_InsomniaProtects:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp printstring STRINGID_PKMNSTAYEDAWAKEUSING @@ -2866,16 +2842,10 @@ BattleScript_EffectLightScreen:: BattleScript_EffectRest:: attackcanceler - jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_RestIsAlreadyAsleep - jumpifability BS_ATTACKER, ABILITY_COMATOSE, BattleScript_RestIsAlreadyAsleep - jumpifuproarwakes BattleScript_RestCantSleep - jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects - jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects - jumpifability BS_ATTACKER, ABILITY_PURIFYING_SALT, BattleScript_InsomniaProtects .if B_LEAF_GUARD_PREVENTS_REST >= GEN_5 jumpifleafguardprotected BS_TARGET, BattleScript_LeafGuardPreventsRest .endif - trysetrest BattleScript_AlreadyAtFullHp + trysetrest pause B_WAIT_TIME_SHORT printfromtable gRestUsedStringIds waitmessage B_WAIT_TIME_LONG @@ -3288,10 +3258,6 @@ BattleScript_EffectPainSplit:: BattleScript_EffectSnore:: attackcanceler - jumpifability BS_ATTACKER, ABILITY_COMATOSE, BattleScript_SnoreIsAsleep - jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_SnoreIsAsleep - goto BattleScript_ButItFailed -BattleScript_SnoreIsAsleep:: jumpifhalfword CMP_EQUAL, gChosenMove, MOVE_SLEEP_TALK, BattleScript_DoSnore printstring STRINGID_PKMNFASTASLEEP waitmessage B_WAIT_TIME_LONG @@ -3331,7 +3297,7 @@ BattleScript_EffectSketch:: BattleScript_EffectDestinyBond:: attackcanceler - trysetdestinybond BattleScript_ButItFailed + setvolatile BS_ATTACKER, VOLATILE_DESTINY_BOND, 2 attackanimation waitanimation printstring STRINGID_PKMNTRYINGTOTAKEFOE @@ -3463,15 +3429,9 @@ BattleScript_DoGhostCurse:: tryfaintmon BS_ATTACKER goto BattleScript_MoveEnd -BattleScript_EffectMatBlock:: - attackcanceler - jumpifnotfirstturn BattleScript_ButItFailed - goto BattleScript_ProtectLikeAttack - BattleScript_EffectProtect:: BattleScript_EffectEndure:: attackcanceler -BattleScript_ProtectLikeAttack: setprotectlike attackanimation waitanimation @@ -3785,7 +3745,7 @@ BattleScript_EffectMirrorCoat:: BattleScript_EffectFutureSight:: attackcanceler - trysetfutureattack BattleScript_ButItFailed + setfutureattack attackanimation waitanimation printfromtable gFutureMoveUsedStringIds @@ -3880,11 +3840,6 @@ BattleScript_AlreadyAtFullHp:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectFirstTurnOnly:: - attackcanceler - jumpifnotfirstturn BattleScript_ButItFailed - goto BattleScript_EffectHit - BattleScript_FailedFromAtkCanceler:: attackcanceler BattleScript_ButItFailed:: @@ -3920,7 +3875,7 @@ BattleScript_EffectStockpile:: stockpile 0 attackanimation waitanimation - printfromtable gStockpileUsedStringIds + printstring STRINGID_PKMNSTOCKPILED waitmessage B_WAIT_TIME_LONG .if B_STOCKPILE_RAISES_DEFS < GEN_4 goto BattleScript_EffectStockpileEnd @@ -3965,29 +3920,22 @@ BattleScript_EffectSpitUp:: accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE damagecalc adjustdamage - stockpiletobasedamage BattleScript_SpitUpFail + stockpiletobasedamage call BattleScript_Hit_RetFromAtkAnimation tryfaintmon BS_TARGET removestockpilecounters - goto BattleScript_SpitUpEnd -BattleScript_SpitUpFail:: - checkparentalbondcounter 2, BattleScript_SpitUpEnd - pause B_WAIT_TIME_SHORT - printstring STRINGID_FAILEDTOSPITUP - waitmessage B_WAIT_TIME_LONG -BattleScript_SpitUpEnd: goto BattleScript_MoveEnd BattleScript_SpitUpFailProtect:: pause B_WAIT_TIME_LONG - stockpiletobasedamage BattleScript_SpitUpFail + stockpiletobasedamage resultmessage waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd BattleScript_EffectSwallow:: attackcanceler - stockpiletohpheal BattleScript_SwallowFail + stockpiletohpheal BattleScript_ButItFailed attackanimation waitanimation orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE @@ -3998,13 +3946,6 @@ BattleScript_EffectSwallow:: removestockpilecounters goto BattleScript_MoveEnd - -BattleScript_SwallowFail:: - pause B_WAIT_TIME_SHORT - printfromtable gSwallowFailStringIds - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - BattleScript_EffectHail:: attackcanceler call BattleScript_CheckPrimalWeather @@ -4112,9 +4053,6 @@ BattleScript_EffectFocusPunch:: BattleScript_EffectFollowMe:: attackcanceler - .if B_UPDATED_MOVE_DATA >= GEN_8 - jumpifnotbattletype BATTLE_TYPE_DOUBLE, BattleScript_ButItFailed - .endif setforcedtarget attackanimation waitanimation @@ -4324,7 +4262,8 @@ BattleScript_EffectRefresh:: BattleScript_EffectGrudge:: attackcanceler - trysetvolatile BS_ATTACKER, VOLATILE_GRUDGE, BattleScript_ButItFailed + jumpifvolatile BS_ATTACKER, VOLATILE_GRUDGE, BattleScript_ButItFailed + setvolatile BS_ATTACKER, VOLATILE_GRUDGE, 2 attackanimation waitanimation printstring STRINGID_PKMNWANTSGRUDGE @@ -7246,8 +7185,10 @@ BattleScript_DazzlingProtected:: goto BattleScript_MoveEnd BattleScript_MoveUsedPsychicTerrainPrevents:: - printstring STRINGID_POKEMONCANNOTUSEMOVE + pause B_WAIT_TIME_SHORT + printstring STRINGID_PSYCHICTERRAINPREVENTS waitmessage B_WAIT_TIME_LONG + setmoveresultflags MOVE_RESULT_NO_EFFECT goto BattleScript_MoveEnd BattleScript_GrassyTerrainHeals:: @@ -8396,11 +8337,6 @@ BattleScript_TryFaint: tryfaintmon BS_TARGET goto BattleScript_MoveEnd -BattleScript_EffectSteelRoller:: - attackcanceler - jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ButItFailed - goto BattleScript_HitFromAccCheck - BattleScript_RemoveTerrain:: removeterrain playanimation BS_ATTACKER, B_ANIM_RESTORE_BG diff --git a/docs/tutorials/how_to_new_move.md b/docs/tutorials/how_to_new_move.md index 0262ca3202..0f7ca1d15c 100644 --- a/docs/tutorials/how_to_new_move.md +++ b/docs/tutorials/how_to_new_move.md @@ -93,14 +93,21 @@ Contains more fundamental functions that control the flow of the battle. Functio ### data/battle_scripts_1.s Each move's effect is governed by a script defined here. For a simple example, let's look at the script for Fake Out/First Impression: +TODO: New Script ``` -BattleScript_EffectFirstTurnOnly:: +BattleScript_EffectTaunt:: attackcanceler - jumpifnotfirstturn BattleScript_ButItFailed - goto BattleScript_EffectHit + jumpifability BS_TARGET_SIDE, ABILITY_AROMA_VEIL, BattleScript_AromaVeilProtects + accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE + settaunt BattleScript_ButItFailed + attackanimation + waitanimation + printstring STRINGID_PKMNFELLFORTAUNT + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd ``` -`attackcanceler` is a command that covers all the cases that could cause a move to fail before it's even attempted (e.g. paralysis). And as we can tell from the commands, if it's not the first turn, we go to `BattleScript_ButItFailed` which evidently causes us to print the `attackstring` ("POKEMON used MOVE") then fail ("But it failed!"). Otherwise, we go to the generic "hit" effect which is the same script for moves that just deal damage and nothing else. +`attackcanceler` is a command that covers all cases that could cause a move to fail before it's even attempted (e.g. paralysis). The next command is a jump command. A jump command can check anything and usually comes with a jump instruction. Usually it jumps to a place from where the move should pick up because of certain conditions. The next one is an accuracy check. Accuracy checks happen after all prior move failure checks happened. The next set of commands are unique to a certain move, they are mostly the same for damaging moves but can widely differ for status moves. Lastly there is `BattleScript_MoveEnd` which the move after a succesful hit. An ability activation or specific move effect like Burn, Freeze, Absorb etc. This is the most advanced part of the ROM. There are dozens upon dozens of commands and hundreds of scripts so this guide would go on forever if I were to go into more detail. To learn how these scripts work, it's best to look at a few examples of moves you know. diff --git a/include/battle.h b/include/battle.h index bf3cb78c47..3af2efc504 100644 --- a/include/battle.h +++ b/include/battle.h @@ -113,7 +113,6 @@ struct DisableStruct u8 usedMoves:4; u8 truantCounter:1; u8 truantSwitchInHack:1; - u8 noRetreat:1; u8 tarShot:1; u8 octolock:1; u8 cudChew:1; @@ -123,6 +122,7 @@ struct DisableStruct u8 usedProteanLibero:1; u8 flashFireBoosted:1; u8 boosterEnergyActivated:1; + u8 padding1:1; u16 overwrittenAbility; // abilities overwritten during battle (keep separate from battle history in case of switching) u8 roostActive:1; u8 unburdenActive:1; @@ -130,9 +130,9 @@ struct DisableStruct 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 padding1:1; + u8 padding2:1; u8 octolockedBy:3; - u8 padding2:5; + u8 padding3:5; }; // Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 02e34961c8..b2e191c17f 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -155,6 +155,7 @@ u32 CountPositiveStatStages(u32 battlerId); u32 CountNegativeStatStages(u32 battlerId); // move checks +bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData); bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffect); bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category); enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo); @@ -312,7 +313,7 @@ bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget); #define AI_EFFECT_TERRAIN (1 << 1) #define AI_EFFECT_CLEAR_HAZARDS (1 << 2) #define AI_EFFECT_BREAK_SCREENS (1 << 3) -#define AI_EFFECT_RESET_STATS (1 << 4) +#define AI_EFFECT_RESET_STATS (1 << 4) #define AI_EFFECT_FORCE_SWITCH (1 << 5) #define AI_EFFECT_TORMENT (1 << 6) #define AI_EFFECT_LIGHT_SCREEN (1 << 7) diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index ed295bb1e6..03dfef96a3 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -11,7 +11,6 @@ u16 GetNonDynamaxHP(u32 battler); u16 GetNonDynamaxMaxHP(u32 battler); void UndoDynamax(u32 battler); bool32 IsMoveBlockedByMaxGuard(u32 move); -bool32 IsMoveBlockedByDynamax(u32 move); u16 GetMaxMove(u32 battler, u32 baseMove); u32 GetMaxMovePower(u32 move); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index dc64b88e07..18c30b6cc8 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -332,7 +332,6 @@ extern const u8 BattleScript_MistySurgeActivates[]; extern const u8 BattleScript_ElectricSurgeActivates[]; extern const u8 BattleScript_EffectSpectralThief[]; extern const u8 BattleScript_EffectLifeDew[]; -extern const u8 BattleScript_EffectSteelRoller[]; extern const u8 BattleScript_AbilityRaisesDefenderStat[]; extern const u8 BattleScript_PowderMoveNoEffect[]; extern const u8 BattleScript_GrassyTerrainHeals[]; @@ -597,6 +596,8 @@ extern const u8 BattleScript_EffectConversion[]; extern const u8 BattleScript_EffectRestoreHp[]; extern const u8 BattleScript_EffectLightScreen[]; extern const u8 BattleScript_EffectRest[]; +extern const u8 BattleScript_RestIsAlreadyAsleep[]; +extern const u8 BattleScript_InsomniaProtects[]; extern const u8 BattleScript_EffectOHKO[]; extern const u8 BattleScript_EffectHealBlock[]; extern const u8 BattleScript_RecoilIfMiss[]; @@ -677,7 +678,6 @@ extern const u8 BattleScript_EffectBeatUp[]; extern const u8 BattleScript_EffectSemiInvulnerable[]; extern const u8 BattleScript_EffectDefenseCurl[]; extern const u8 BattleScript_EffectSoftboiled[]; -extern const u8 BattleScript_EffectFirstTurnOnly[]; extern const u8 BattleScript_EffectStockpile[]; extern const u8 BattleScript_EffectSpitUp[]; extern const u8 BattleScript_EffectSwallow[]; @@ -742,7 +742,6 @@ extern const u8 BattleScript_EffectGuardSplit[]; extern const u8 BattleScript_EffectStickyWeb[]; extern const u8 BattleScript_EffectMetalBurst[]; extern const u8 BattleScript_EffectLuckyChant[]; -extern const u8 BattleScript_EffectSuckerPunch[]; extern const u8 BattleScript_EffectSimpleBeam[]; extern const u8 BattleScript_EffectEntrainment[]; extern const u8 BattleScript_EffectHealPulse[]; @@ -761,7 +760,6 @@ extern const u8 BattleScript_EffectElectrify[]; extern const u8 BattleScript_EffectReflectType[]; extern const u8 BattleScript_EffectSoak[]; extern const u8 BattleScript_EffectGrowth[]; -extern const u8 BattleScript_EffectLastResort[]; extern const u8 BattleScript_EffectShellSmash[]; extern const u8 BattleScript_EffectShiftGear[]; extern const u8 BattleScript_EffectDefenseUp3[]; @@ -790,14 +788,12 @@ extern const u8 BattleScript_EffectAcupressure[]; extern const u8 BattleScript_EffectAromaticMist[]; extern const u8 BattleScript_EffectPowder[]; extern const u8 BattleScript_EffectPartingShot[]; -extern const u8 BattleScript_EffectMatBlock[]; extern const u8 BattleScript_EffectInstruct[]; extern const u8 BattleScript_EffectLaserFocus[]; extern const u8 BattleScript_EffectMagneticFlux[]; extern const u8 BattleScript_EffectGearUp[]; extern const u8 BattleScript_EffectStrengthSap[]; extern const u8 BattleScript_EffectPurify[]; -extern const u8 BattleScript_FailIfNotArgType[]; extern const u8 BattleScript_EffectShoreUp[]; extern const u8 BattleScript_EffectGeomancy[]; extern const u8 BattleScript_EffectFairyLock[]; @@ -814,7 +810,6 @@ extern const u8 BattleScript_MoveEffectLeechSeed[]; extern const u8 BattleScript_MoveEffectHaze[]; extern const u8 BattleScript_MoveEffectIonDeluge[]; extern const u8 BattleScript_EffectHyperspaceFury[]; -extern const u8 BattleScript_EffectAuraWheel[]; extern const u8 BattleScript_EffectNoRetreat[]; extern const u8 BattleScript_EffectTarShot[]; extern const u8 BattleScript_EffectPoltergeist[]; @@ -855,5 +850,6 @@ extern const u8 BattleScript_SubmoveAttackstring[]; extern const u8 BattleScript_MetronomeAttackstring[]; extern const u8 BattleScript_SleepTalkAttackstring[]; extern const u8 BattleScript_NaturePowerAttackstring[]; +extern const u8 BattleScript_PokemonCantUseTheMove[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_terastal.h b/include/battle_terastal.h index 6b5c385463..9c55a49d70 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -7,7 +7,7 @@ bool32 CanTerastallize(u32 battler); u32 GetBattlerTeraType(u32 battler); void ExpendTypeStellarBoost(u32 battler, u32 type); bool32 IsTypeStellarBoosted(u32 battler, u32 type); -uq4_12_t GetTeraMultiplier(u32 battler, u32 type); +uq4_12_t GetTeraMultiplier(struct DamageContext *ctx); u16 GetTeraTypeRGB(u32 type); diff --git a/include/battle_util.h b/include/battle_util.h index a1eea3b770..b9652c1caf 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -105,7 +105,7 @@ struct TypePower enum MoveSuccessOrder { - CANCELLER_FLAGS, + CANCELLER_CLEAR_FLAGS, CANCELLER_STANCE_CHANGE_1, CANCELLER_SKY_DROP, CANCELLER_RECHARGE, @@ -130,10 +130,10 @@ enum MoveSuccessOrder CANCELLER_ATTACKSTRING, CANCELLER_PPDEDUCTION, CANCELLER_WEATHER_PRIMAL, - CANCELLER_DYNAMAX_BLOCKED, + CANCELLER_MOVE_FAILURE, CANCELLER_POWDER_STATUS, + CANCELLER_PRIORITY_BLOCK, CANCELLER_PROTEAN, - CANCELLER_PSYCHIC_TERRAIN, CANCELLER_EXPLODING_DAMP, CANCELLER_MULTIHIT_MOVES, CANCELLER_MULTI_TARGET_MOVES, @@ -180,6 +180,15 @@ struct DamageContext enum ItemHoldEffect holdEffectDef:16; }; +struct BattleContext +{ + u32 battlerAtk:3; + u32 battlerDef:3; + u32 currentMove:16; + enum BattleMoveEffects moveEffect:10; + u16 ability[MAX_BATTLERS_COUNT]; +}; + enum SleepClauseBlock { NOT_BLOCKED_BY_SLEEP_CLAUSE, @@ -242,7 +251,7 @@ bool32 IsAbilityAndRecord(u32 battler, u32 battlerAbility, u32 abilityToCheck); u32 DoEndTurnEffects(void); bool32 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); -enum MoveCanceller AtkCanceller_MoveSuccessOrder(void); +enum MoveCanceller AtkCanceller_MoveSuccessOrder(struct BattleContext *ctx); bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility); bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option); @@ -414,5 +423,6 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move); u32 GetNaturePowerMove(u32 battler); u32 GetNaturePowerMove(u32 battler); void RemoveAbilityFlags(u32 battler); +bool32 IsDazzlingAbility(u32 ability); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 47fff193a6..45a74be518 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -162,7 +162,7 @@ enum VolatileFlags F(VOLATILE_RECHARGE, recharge, (u32, 1)) \ F(VOLATILE_RAGE, rage, (u32, 1)) \ F(VOLATILE_SUBSTITUTE, substitute, (u32, 1), V_BATON_PASSABLE) \ - F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 1)) \ + F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 2)) \ F(VOLATILE_ESCAPE_PREVENTION, escapePrevention, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_NIGHTMARE, nightmare, (u32, 1)) \ F(VOLATILE_CURSED, cursed, (u32, 1), V_BATON_PASSABLE) \ @@ -196,6 +196,7 @@ enum VolatileFlags F(VOLATILE_AQUA_RING, aquaRing, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_LASER_FOCUS, laserFocus, (u32, 1)) \ F(VOLATILE_POWER_TRICK, powerTrick, (u32, 1), V_BATON_PASSABLE) \ + F(VOLATILE_NO_RETREAT, noRetreat, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_VESSEL_OF_RUIN, vesselOfRuin, (u32, 1)) \ F(VOLATILE_SWORD_OF_RUIN, swordOfRuin, (u32, 1)) \ F(VOLATILE_TABLETS_OF_RUIN, tabletsOfRuin, (u32, 1)) \ diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 83175f285f..e37da539f9 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -113,7 +113,6 @@ enum StringID STRINGID_PKMNCALMEDDOWN, STRINGID_PKMNCANTSLEEPINUPROAR, STRINGID_PKMNSTOCKPILED, - STRINGID_PKMNCANTSTOCKPILE, STRINGID_PKMNCANTSLEEPINUPROAR2, STRINGID_UPROARKEPTPKMNAWAKE, STRINGID_PKMNSTAYEDAWAKEUSING, @@ -239,8 +238,6 @@ enum StringID STRINGID_STARTEDHAIL, STRINGID_HAILCONTINUES, STRINGID_HAILSTOPPED, - STRINGID_FAILEDTOSPITUP, - STRINGID_FAILEDTOSWALLOW, STRINGID_STATCHANGESGONE, STRINGID_COINSSCATTERED, STRINGID_TOOWEAKFORSUBSTITUTE, @@ -858,20 +855,6 @@ enum UproarOverTurnStringID B_MSG_UPROAR_ENDS, }; -// gStockpileUsedStringIds -enum StockpileUsedStringID -{ - B_MSG_STOCKPILED, - B_MSG_CANT_STOCKPILE, -}; - -// gSwallowFailStringIds -enum SwallowFailStringID -{ - B_MSG_SWALLOW_FAILED, - B_MSG_SWALLOW_FULL_HP, -}; - // gKOFailedStringIds enum KOFailedStringID { diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index fb0e40bfd3..25971437bc 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1141,6 +1141,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) // check non-user target if (!(moveTarget & MOVE_TARGET_USER)) { + if (Ai_IsPriorityBlocked(battlerAtk, battlerDef, move, aiData)) + return TRUE; + if (CanAbilityBlockMove(battlerAtk, battlerDef, abilityAtk, abilityDef, move, AI_CHECK)) RETURN_SCORE_MINUS(20); @@ -2871,7 +2874,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_NO_RETREAT: - if (gDisableStructs[battlerAtk].noRetreat) + if (gBattleMons[battlerAtk].volatiles.noRetreat) ADJUST_SCORE(-10); break; case EFFECT_EXTREME_EVOBOOST: @@ -3106,7 +3109,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) switch (effect) { case EFFECT_HELPING_HAND: - if (!hasPartner + if (!hasPartner || !HasDamagingMove(battlerAtkPartner) || (aiData->partnerMove != MOVE_NONE && IsBattleMoveStatus(aiData->partnerMove))) { @@ -3122,7 +3125,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (hasTwoOpponents) { // Might be about to die - if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk) + if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk) && AI_IsSlower(battlerAtk, LEFT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY) && AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); @@ -3144,14 +3147,14 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) else if (IsBattlerAlive(RIGHT_FOE(battlerAtk))) { // Might be about to die - if (CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk) + if (CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk) && AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)) ADJUST_SCORE(GOOD_EFFECT); if (ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1) ADJUST_SCORE(GOOD_EFFECT); - } + } } break; case EFFECT_PERISH_SONG: @@ -3178,7 +3181,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(GOOD_EFFECT); break; case EFFECT_COACHING: - if (!hasPartner + if (!hasPartner || !HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)) { ADJUST_SCORE(WORST_EFFECT); @@ -5271,7 +5274,7 @@ case EFFECT_GUARD_SPLIT: ADJUST_SCORE(GOOD_EFFECT); // Set it for next pokemon in singles. else if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !hasPartner && (CountUsablePartyMons(battlerAtk) != 0)) - ADJUST_SCORE(DECENT_EFFECT); + ADJUST_SCORE(DECENT_EFFECT); // Don't unset it on last turn. else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter && ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM)) ADJUST_SCORE(GOOD_EFFECT); @@ -5406,7 +5409,7 @@ case EFFECT_GUARD_SPLIT: u32 partnerSpeed = aiData->speedStats[BATTLE_PARTNER(battlerAtk)]; u32 foe1Speed = aiData->speedStats[LEFT_FOE(battlerAtk)]; u32 foe2Speed = aiData->speedStats[RIGHT_FOE(battlerAtk)]; - + if (speed <= foe1Speed && (speed * 2) > foe1Speed) tailwindScore += 1; if (speed <= foe2Speed && (speed * 2) > foe2Speed) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index eac7e728b1..fc62eb907a 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -501,11 +501,11 @@ bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef) u32 GetTotalBaseStat(u32 species) { return GetSpeciesBaseHP(species) - + GetSpeciesBaseAttack(species) - + GetSpeciesBaseDefense(species) - + GetSpeciesBaseSpeed(species) - + GetSpeciesBaseSpAttack(species) - + GetSpeciesBaseSpDefense(species); + + GetSpeciesBaseAttack(species) + + GetSpeciesBaseDefense(species) + + GetSpeciesBaseSpeed(species) + + GetSpeciesBaseSpAttack(species) + + GetSpeciesBaseSpDefense(species); } bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) @@ -535,6 +535,24 @@ bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffe return TRUE; } +bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData) +{ + s32 atkPriority = GetBattleMovePriority(battlerAtk, aiData->abilities[battlerAtk], move); + + if (atkPriority <= 0 || IsBattlerAlly(battlerAtk, battlerDef)) + return FALSE; + + if (IsMoldBreakerTypeAbility(battlerAtk, aiData->abilities[battlerAtk]) || MoveIgnoresTargetAbility(move)) + return FALSE; + + if (IsDazzlingAbility(aiData->abilities[battlerDef])) + return TRUE; + + if (IsDoubleBattle() && IsDazzlingAbility(aiData->abilities[BATTLE_PARTNER(battlerDef)])) + return TRUE; + + return FALSE; +} // This function checks if all physical/special moves are either unusable or unreasonable to use. // Consider a pokemon boosting their attack against a ghost pokemon having only normal-type physical attacks. bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category) @@ -630,6 +648,9 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx) partnerDefAbility = aiData->abilities[BATTLE_PARTNER(ctx->battlerDef)]; } + if (Ai_IsPriorityBlocked(ctx->battlerAtk, ctx->battlerDef, ctx->move, aiData)) + return TRUE; + if (CanAbilityBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, battlerDefAbility, ctx->move, AI_CHECK)) return TRUE; diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index c6e91742e8..33a7d5ee28 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -234,21 +234,6 @@ bool32 IsMoveBlockedByMaxGuard(u32 move) return FALSE; } -// Weight-based moves (and some other moves in Raids) are blocked by Dynamax. -bool32 IsMoveBlockedByDynamax(u32 move) -{ - // TODO: Certain moves are banned in raids. - switch (GetMoveEffect(move)) - { - case EFFECT_HEAT_CRASH: - case EFFECT_LOW_KICK: - return TRUE; - default: - break; - } - return FALSE; -} - static u16 GetTypeBasedMaxMove(u32 battler, u32 type) { // Gigantamax check diff --git a/src/battle_main.c b/src/battle_main.c index 488a1e81f8..e7150694f1 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4840,7 +4840,9 @@ s32 GetBattleMovePriority(u32 battler, u32 ability, u32 move) priority++; } else if (ability == ABILITY_TRIAGE && IsHealingMove(move)) + { priority += 3; + } if (gProtectStructs[battler].quash) priority = -8; diff --git a/src/battle_message.c b/src/battle_message.c index f3d804dec5..04414d602d 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -272,7 +272,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNCALMEDDOWN] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} calmed down."), [STRINGID_PKMNCANTSLEEPINUPROAR] = COMPOUND_STRING("But {B_DEF_NAME_WITH_PREFIX2} can't sleep in an uproar!"), [STRINGID_PKMNSTOCKPILED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} stockpiled {B_BUFF1}!"), - [STRINGID_PKMNCANTSTOCKPILE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} can't stockpile any more!"), //I think this was replaced with just "But it failed!" [STRINGID_PKMNCANTSLEEPINUPROAR2] = COMPOUND_STRING("But {B_DEF_NAME_WITH_PREFIX2} can't sleep in an uproar!"), [STRINGID_UPROARKEPTPKMNAWAKE] = COMPOUND_STRING("But the uproar kept {B_DEF_NAME_WITH_PREFIX2} awake!"), [STRINGID_PKMNSTAYEDAWAKEUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} stayed awake using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup @@ -398,8 +397,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_STARTEDHAIL] = COMPOUND_STRING("It started to hail!"), [STRINGID_HAILCONTINUES] = COMPOUND_STRING("The hail is crashing down."), [STRINGID_HAILSTOPPED] = COMPOUND_STRING("The hail stopped."), - [STRINGID_FAILEDTOSPITUP] = COMPOUND_STRING("But it failed to spit up a thing!"), //not in gen 5+, uses "but it failed" - [STRINGID_FAILEDTOSWALLOW] = COMPOUND_STRING("But it failed to swallow a thing!"), //not in gen 5+, uses "but it failed" [STRINGID_STATCHANGESGONE] = COMPOUND_STRING("All stat changes were eliminated!"), [STRINGID_COINSSCATTERED] = COMPOUND_STRING("Coins were scattered everywhere!"), [STRINGID_TOOWEAKFORSUBSTITUTE] = COMPOUND_STRING("But it does not have enough HP left to make a substitute!"), @@ -663,7 +660,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_MISTYTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with a protective mist!"), [STRINGID_GRASSYTERRAINHEALS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is healed by the grassy terrain!"), [STRINGID_ELECTRICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with electrified terrain!"), - [STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with psychic terrain!"), + [STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is protected by the Psychic Terrain!"), [STRINGID_SAFETYGOGGLESPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is not affected thanks to its {B_LAST_ITEM}!"), [STRINGID_FLOWERVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounded itself with a veil of petals!"), [STRINGID_SWEETVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} can't fall asleep due to a veil of sweetness!"), @@ -1073,24 +1070,12 @@ const u16 gUproarOverTurnStringIds[] = [B_MSG_UPROAR_ENDS] = STRINGID_PKMNCALMEDDOWN }; -const u16 gStockpileUsedStringIds[] = -{ - [B_MSG_STOCKPILED] = STRINGID_PKMNSTOCKPILED, - [B_MSG_CANT_STOCKPILE] = STRINGID_PKMNCANTSTOCKPILE, -}; - const u16 gWokeUpStringIds[] = { [B_MSG_WOKE_UP] = STRINGID_PKMNWOKEUP, [B_MSG_WOKE_UP_UPROAR] = STRINGID_PKMNWOKEUPINUPROAR }; -const u16 gSwallowFailStringIds[] = -{ - [B_MSG_SWALLOW_FAILED] = STRINGID_FAILEDTOSWALLOW, - [B_MSG_SWALLOW_FULL_HP] = STRINGID_PKMNHPFULL -}; - const u16 gUproarAwakeStringIds[] = { [B_MSG_CANT_SLEEP_UPROAR] = STRINGID_PKMNCANTSLEEPINUPROAR2, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3b2eeeb942..41976e1149 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -467,7 +467,7 @@ static void Cmd_setreflect(void); static void Cmd_setseeded(void); static void Cmd_manipulatedamage(void); static void Cmd_trysetrest(void); -static void Cmd_jumpifnotfirstturn(void); +static void Cmd_unused_0x82(void); static void Cmd_unused_0x83(void); static void Cmd_jumpifuproarwakes(void); static void Cmd_stockpile(void); @@ -507,7 +507,7 @@ static void Cmd_settypetorandomresistance(void); static void Cmd_setalwayshitflag(void); static void Cmd_copymovepermanently(void); static void Cmd_unused_0xA9(void); -static void Cmd_trysetdestinybond(void); +static void Cmd_unused_AA(void); static void Cmd_trysetdestinybondtohappen(void); static void Cmd_settailwind(void); static void Cmd_tryspiteppreduce(void); @@ -532,7 +532,7 @@ static void Cmd_unused_0xBF(void); static void Cmd_recoverbasedonsunlight(void); static void Cmd_setstickyweb(void); static void Cmd_selectfirstvalidtarget(void); -static void Cmd_trysetfutureattack(void); +static void Cmd_setfutureattack(void); static void Cmd_trydobeatup(void); static void Cmd_setsemiinvulnerablebit(void); static void Cmd_tryfiretwoturnmovenowbyeffect(void); @@ -565,7 +565,7 @@ static void Cmd_trysetsnatch(void); static void Cmd_unused2(void); static void Cmd_switchoutabilities(void); static void Cmd_jumpifhasnohp(void); -static void Cmd_jumpifnotcurrentmoveargtype(void); +static void Cmd_unused_0xE4(void); static void Cmd_pickup(void); static void Cmd_unused_0xE6(void); static void Cmd_unused_0xE7(void); @@ -726,7 +726,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_setseeded, //0x7F Cmd_manipulatedamage, //0x80 Cmd_trysetrest, //0x81 - Cmd_jumpifnotfirstturn, //0x82 + Cmd_unused_0x82, //0x82 Cmd_unused_0x83, //0x83 Cmd_jumpifuproarwakes, //0x84 Cmd_stockpile, //0x85 @@ -766,7 +766,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_setalwayshitflag, //0xA7 Cmd_copymovepermanently, //0xA8 Cmd_unused_0xA9, //0xA9 - Cmd_trysetdestinybond, //0xAA + Cmd_unused_AA, //0xAA Cmd_trysetdestinybondtohappen, //0xAB Cmd_settailwind, //0xAC Cmd_tryspiteppreduce, //0xAD @@ -791,7 +791,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_recoverbasedonsunlight, //0xC0 Cmd_setstickyweb, //0xC1 Cmd_selectfirstvalidtarget, //0xC2 - Cmd_trysetfutureattack, //0xC3 + Cmd_setfutureattack, //0xC3 Cmd_trydobeatup, //0xC4 Cmd_setsemiinvulnerablebit, //0xC5 Cmd_tryfiretwoturnmovenowbyeffect, //0xC6 @@ -824,7 +824,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_unused2, //0xE1 Cmd_switchoutabilities, //0xE2 Cmd_jumpifhasnohp, //0xE3 - Cmd_jumpifnotcurrentmoveargtype, //0xE4 + Cmd_unused_0xE4, //0xE4 Cmd_pickup, //0xE5 Cmd_unused_0xE6, //0xE6 Cmd_unused_0xE7, //0xE7 @@ -1040,23 +1040,23 @@ u32 NumFaintedBattlersByAttacker(u32 battlerAtk) return numMonsFainted; } -bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move) +bool32 IsMovePowderBlocked(struct BattleContext *ctx) { bool32 effect = FALSE; - if (IsPowderMove(move) && (battlerAtk != battlerDef)) + if (IsPowderMove(ctx->currentMove) && (ctx->battlerAtk != ctx->battlerDef)) { if (B_POWDER_GRASS >= GEN_6 - && (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || GetBattlerAbility(battlerDef) == ABILITY_OVERCOAT)) + && (IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GRASS) || ctx->ability[ctx->battlerDef] == ABILITY_OVERCOAT)) { - gBattlerAbility = battlerDef; - RecordAbilityBattle(gBattlerTarget, ABILITY_OVERCOAT); + gBattlerAbility = ctx->battlerDef; + RecordAbilityBattle(ctx->battlerDef, ABILITY_OVERCOAT); effect = TRUE; } - else if (GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES) + else if (GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES) { - RecordItemEffectBattle(battlerDef, HOLD_EFFECT_SAFETY_GOGGLES); - gLastUsedItem = gBattleMons[battlerDef].item; + RecordItemEffectBattle(ctx->battlerDef, HOLD_EFFECT_SAFETY_GOGGLES); + gLastUsedItem = gBattleMons[ctx->battlerDef].item; effect = TRUE; } @@ -1103,22 +1103,30 @@ static void Cmd_attackcanceler(void) return; } - enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); + struct BattleContext ctx = {0}; + ctx.battlerAtk = gBattlerAttacker; + ctx.battlerDef = gBattlerTarget; + ctx.currentMove = gCurrentMove; + ctx.moveEffect = GetMoveEffect(ctx.currentMove); if (!IsBattlerAlive(gBattlerAttacker) - && effect != EFFECT_EXPLOSION - && effect != EFFECT_MISTY_EXPLOSION) + && ctx.moveEffect != EFFECT_EXPLOSION + && ctx.moveEffect != EFFECT_MISTY_EXPLOSION) { gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattlescriptCurrInstr = BattleScript_MoveEnd; return; } - if (AtkCanceller_MoveSuccessOrder() != MOVE_STEP_SUCCESS) + // With how attackcanceller works right now we only need attacker and target abilities. Might change in the future + ctx.ability[ctx.battlerAtk] = GetBattlerAbility(ctx.battlerAtk); + ctx.ability[ctx.battlerDef] = GetBattlerAbility(ctx.battlerDef); + + if (AtkCanceller_MoveSuccessOrder(&ctx) != MOVE_STEP_SUCCESS) return; if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF - && GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND + && ctx.ability[ctx.battlerAtk] == ABILITY_PARENTAL_BOND && IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker) && !(gAbsentBattlerFlags & (1u << gBattlerTarget)) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE) @@ -1129,44 +1137,43 @@ static void Cmd_attackcanceler(void) return; } - u32 abilityDef = GetBattlerAbility(gBattlerTarget); if (CanAbilityBlockMove( - gBattlerAttacker, - gBattlerTarget, - GetBattlerAbility(gBattlerAttacker), - abilityDef, - gCurrentMove, + ctx.battlerAtk, + ctx.battlerDef, + ctx.ability[ctx.battlerAtk], + ctx.ability[ctx.battlerDef], + ctx.currentMove, RUN_SCRIPT)) return; - if (GetMoveNonVolatileStatus(gCurrentMove) == MOVE_EFFECT_PARALYSIS) + if (GetMoveNonVolatileStatus(ctx.currentMove) == MOVE_EFFECT_PARALYSIS) { if (CanAbilityAbsorbMove( - gBattlerAttacker, - gBattlerTarget, - abilityDef, - gCurrentMove, - GetBattleMoveType(gCurrentMove), + ctx.battlerAtk, + ctx.battlerDef, + ctx.ability[ctx.battlerDef], + ctx.currentMove, + GetBattleMoveType(ctx.currentMove), RUN_SCRIPT)) return; } - if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove)) + if (IsMovePowderBlocked(&ctx)) return; // Check if no available target present on the field or if Sky Battles ban the move if ((NoTargetPresent(gBattlerAttacker, gCurrentMove) - && (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))) + && (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))) || (IsMoveNotAllowedInSkyBattles(gCurrentMove))) { gBattleStruct->noTargetPresent = TRUE; - if (effect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling. + if (ctx.moveEffect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling. gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem; else gBattlescriptCurrInstr = BattleScript_ButItFailed; - if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) + if (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); return; } @@ -1197,7 +1204,7 @@ static void Cmd_attackcanceler(void) { u32 battler = gBattlerTarget; - if (abilityDef == ABILITY_MAGIC_BOUNCE) + if (ctx.ability[ctx.battlerDef] == ABILITY_MAGIC_BOUNCE) { battler = gBattlerTarget; gBattleStruct->bouncedMoveIsUsed = TRUE; @@ -1257,11 +1264,11 @@ static void Cmd_attackcanceler(void) RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) - && (effect != EFFECT_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) - && (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) - && effect != EFFECT_SUCKER_PUNCH - && effect != EFFECT_COUNTER - && effect != EFFECT_UPPER_HAND) + && (ctx.moveEffect != EFFECT_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) + && (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)) + && ctx.moveEffect != EFFECT_SUCKER_PUNCH + && ctx.moveEffect != EFFECT_COUNTER + && ctx.moveEffect != EFFECT_UPPER_HAND) { if (!CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), gCurrentMove)) gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE; @@ -3238,7 +3245,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai gBattlescriptCurrInstr++; break; case MOVE_EFFECT_PREVENT_ESCAPE: - if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention) + if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention) // Do we need to check if the status is already set? { gBattleMons[gBattlerTarget].volatiles.escapePrevention = TRUE; gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker; @@ -6800,6 +6807,9 @@ static void Cmd_moveend(void) DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!"); // #endif } + + if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0) + gBattleMons[gBattlerAttacker].volatiles.destinyBond--; gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE; gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE; @@ -7572,7 +7582,7 @@ static void UpdateSentMonFlags(u32 battler) static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) { - gBattleMons[battler].volatiles.destinyBond = FALSE; + gBattleMons[battler].volatiles.destinyBond = 0; gHitMarker &= ~HITMARKER_DESTINYBOND; gBattleScripting.battler = battler; gBattleCommunication[MULTISTRING_CHOOSER] = multistringId; @@ -9659,17 +9669,12 @@ static void Cmd_manipulatedamage(void) static void Cmd_trysetrest(void) { - CMD_ARGS(const u8 *failInstr); + CMD_ARGS(); - const u8 *failInstr = cmd->failInstr; gBattlerTarget = gBattlerAttacker; gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].maxHP * (-1); - if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) - { - gBattlescriptCurrInstr = failInstr; - } - else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN)) + if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN)) { gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents; } @@ -9691,16 +9696,8 @@ static void Cmd_trysetrest(void) } } -static void Cmd_jumpifnotfirstturn(void) +static void Cmd_unused_0x82(void) { - CMD_ARGS(const u8 *jumpInstr); - - const u8 *jumpInstr = cmd->jumpInstr; - - if (gDisableStructs[gBattlerAttacker].isFirstTurn && !(gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)) - gBattlescriptCurrInstr = cmd->nextInstr; - else - gBattlescriptCurrInstr = jumpInstr; } static void Cmd_unused_0x83(void) @@ -9752,19 +9749,10 @@ static void Cmd_stockpile(void) switch (cmd->id) { case 0: - if (gDisableStructs[gBattlerAttacker].stockpileCounter >= 3) - { - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_STOCKPILE; - } - else - { - gDisableStructs[gBattlerAttacker].stockpileCounter++; - gDisableStructs[gBattlerAttacker].stockpileBeforeDef = gBattleMons[gBattlerAttacker].statStages[STAT_DEF]; - gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef = gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF]; - PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STOCKPILED; - } + gDisableStructs[gBattlerAttacker].stockpileCounter++; + gDisableStructs[gBattlerAttacker].stockpileBeforeDef = gBattleMons[gBattlerAttacker].statStages[STAT_DEF]; + gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef = gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF]; + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter); break; case 1: // Save def/sp def stats. if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) @@ -9780,20 +9768,12 @@ static void Cmd_stockpile(void) static void Cmd_stockpiletobasedamage(void) { - CMD_ARGS(const u8 *failInstr); + CMD_ARGS(); - const u8 *failInstr = cmd->failInstr; - if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0) - { - gBattlescriptCurrInstr = failInstr; - } - else - { - if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED) - gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; + if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED) + gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; - gBattlescriptCurrInstr = cmd->nextInstr; - } + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_stockpiletohpheal(void) @@ -9802,40 +9782,31 @@ static void Cmd_stockpiletohpheal(void) const u8 *failInstr = cmd->failInstr; - if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed) + if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp) { + gDisableStructs[gBattlerAttacker].stockpileCounter = 0; gBattlescriptCurrInstr = failInstr; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED; + gBattlerTarget = gBattlerAttacker; } else { - if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp) + if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0) { - gDisableStructs[gBattlerAttacker].stockpileCounter = 0; - gBattlescriptCurrInstr = failInstr; - gBattlerTarget = gBattlerAttacker; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FULL_HP; + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter)); + gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; } - else + else // Snatched move { - if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0) - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter)); - gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; - } - else // Snatched move - { - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; - gBattleScripting.animTurn = 1; - } - - if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) - gBattleStruct->moveDamage[gBattlerAttacker] = 1; - gBattleStruct->moveDamage[gBattlerAttacker] *= -1; - - gBattlescriptCurrInstr = cmd->nextInstr; - gBattlerTarget = gBattlerAttacker; + gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; + gBattleScripting.animTurn = 1; } + + if (gBattleStruct->moveDamage[gBattlerAttacker] == 0) + gBattleStruct->moveDamage[gBattlerAttacker] = 1; + gBattleStruct->moveDamage[gBattlerAttacker] *= -1; + + gBattlescriptCurrInstr = cmd->nextInstr; + gBattlerTarget = gBattlerAttacker; } } @@ -11488,23 +11459,8 @@ static inline bool32 IsDanamaxMonPresent(void) return FALSE; } -static void Cmd_trysetdestinybond(void) +static void Cmd_unused_AA(void) { - CMD_ARGS(const u8 *failInstr); - - if (IsDanamaxMonPresent()) - { - gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax; - } - else if (DoesDestinyBondFail(gBattlerAttacker)) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - gBattleMons[gBattlerAttacker].volatiles.destinyBond = TRUE; - gBattlescriptCurrInstr = cmd->nextInstr; - } } static void TrySetDestinyBondToHappen(void) @@ -12231,29 +12187,22 @@ static void Cmd_selectfirstvalidtarget(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_trysetfutureattack(void) +static void Cmd_setfutureattack(void) { - CMD_ARGS(const u8 *failInstr); + CMD_ARGS(); - if (gWishFutureKnock.futureSightCounter[gBattlerTarget] > gBattleTurnCounter) - { - gBattlescriptCurrInstr = cmd->failInstr; - } + gSideStatuses[GetBattlerSide(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK; + gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove; + gWishFutureKnock.futureSightBattlerIndex[gBattlerTarget] = gBattlerAttacker; + gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] = gBattlerPartyIndexes[gBattlerAttacker]; + gWishFutureKnock.futureSightCounter[gBattlerTarget] = gBattleTurnCounter + 3; + + if (gCurrentMove == MOVE_DOOM_DESIRE) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE; else - { - gSideStatuses[GetBattlerSide(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK; - gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove; - gWishFutureKnock.futureSightBattlerIndex[gBattlerTarget] = gBattlerAttacker; - gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] = gBattlerPartyIndexes[gBattlerAttacker]; - gWishFutureKnock.futureSightCounter[gBattlerTarget] = gBattleTurnCounter + 3; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT; - if (gCurrentMove == MOVE_DOOM_DESIRE) - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE; - else - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT; - - gBattlescriptCurrInstr = cmd->nextInstr; - } + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_trydobeatup(void) @@ -13054,18 +13003,8 @@ static void Cmd_jumpifhasnohp(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_jumpifnotcurrentmoveargtype(void) +static void Cmd_unused_0xE4(void) { - CMD_ARGS(u8 battler, const u8 *failInstr); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - const u8 *failInstr = cmd->failInstr; - u32 type = GetMoveArgType(gCurrentMove); - - if (!IS_BATTLER_OF_TYPE(battler, type)) - gBattlescriptCurrInstr = failInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_pickup(void) @@ -14396,17 +14335,6 @@ void BS_CalcMetalBurstDmg(void) } } -void BS_JumpIfCantFling(void) -{ - NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - - u32 battler = GetBattlerForBattleScript(cmd->battler); - if (!CanFling(battler)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_JumpIfMoreThanHalfHP(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); @@ -15070,13 +14998,6 @@ void BS_TrySetOctolock(void) } } -void BS_SetGlaiveRush(void) -{ - NATIVE_ARGS(); - gBattleMons[gBattlerAttacker].volatiles.glaiveRush = TRUE; - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_SetPledge(void) { NATIVE_ARGS(const u8 *jumpInstr); @@ -17039,19 +16960,6 @@ void BS_SetLuckyChant(void) } } -void BS_SuckerPunchCheck(void) -{ - NATIVE_ARGS(const u8 *failInstr); - if (gProtectStructs[gBattlerTarget].protected == PROTECT_OBSTRUCT) - gBattlescriptCurrInstr = cmd->failInstr; - else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) - gBattlescriptCurrInstr = cmd->failInstr; - else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]) && !gProtectStructs[gBattlerTarget].noValidMoves) - gBattlescriptCurrInstr = cmd->failInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_SetSimpleBeam(void) { NATIVE_ARGS(const u8 *failInstr); @@ -17183,15 +17091,6 @@ void BS_HandleFormChange(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_TryLastResort(void) -{ - NATIVE_ARGS(const u8 *failInstr); - if (CanUseLastResort(gBattlerAttacker)) - gBattlescriptCurrInstr = cmd->nextInstr; - else - gBattlescriptCurrInstr = cmd->failInstr; -} - void BS_TryAutotomize(void) { NATIVE_ARGS(const u8 *failInstr); @@ -17441,8 +17340,7 @@ void BS_SetAuroraVeil(void) { NATIVE_ARGS(); u32 side = GetBattlerSide(gBattlerAttacker); - if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL - || !(HasWeatherEffect() && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) + if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL) { gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -17877,9 +17775,7 @@ void BS_CutOneThirdHpAndRaiseStats(void) { NATIVE_ARGS(const u8 *failInstr); - bool8 atLeastOneStatBoosted = FALSE; - u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3); - + bool32 atLeastOneStatBoosted = FALSE; for (u32 stat = 1; stat < NUM_STATS; stat++) { if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -17888,9 +17784,9 @@ void BS_CutOneThirdHpAndRaiseStats(void) break; } } - if (atLeastOneStatBoosted && gBattleMons[gBattlerAttacker].hp > hpFraction) + if (atLeastOneStatBoosted) { - gBattleStruct->moveDamage[gBattlerAttacker] = hpFraction; + gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3); gBattlescriptCurrInstr = cmd->nextInstr; } else @@ -17899,36 +17795,12 @@ void BS_CutOneThirdHpAndRaiseStats(void) } } -void BS_CheckPoltergeist(void) +void BS_SetPoltergeistMessage(void) { NATIVE_ARGS(const u8 *failInstr); - if (gBattleMons[gBattlerTarget].item == ITEM_NONE - || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM - || GetBattlerAbility(gBattlerTarget) == ABILITY_KLUTZ) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].item); - gLastUsedItem = gBattleMons[gBattlerTarget].item; - gBattlescriptCurrInstr = cmd->nextInstr; - } -} - -void BS_TryNoRetreat(void) -{ - NATIVE_ARGS(const u8 *failInstr); - if (gDisableStructs[gBattlerTarget].noRetreat) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention) - gDisableStructs[gBattlerTarget].noRetreat = TRUE; - gBattlescriptCurrInstr = cmd->nextInstr; - } + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].item); + gLastUsedItem = gBattleMons[gBattlerTarget].item; + gBattlescriptCurrInstr = cmd->nextInstr; } void BS_CureCertainStatuses(void) diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 8799dc7ab2..c083ca0575 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -132,20 +132,19 @@ bool32 IsTypeStellarBoosted(u32 battler, u32 type) // Returns the STAB power multiplier to use when Terastallized. // Power multipliers from Smogon Research thread. -uq4_12_t GetTeraMultiplier(u32 battler, u32 type) +uq4_12_t GetTeraMultiplier(struct DamageContext *ctx) { - u32 teraType = GetBattlerTeraType(battler); - bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY); + u32 teraType = GetBattlerTeraType(ctx->battlerAtk); // Safety check. - if (GetActiveGimmick(battler) != GIMMICK_TERA) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_TERA) return UQ_4_12(1.0); // Stellar-type checks. if (teraType == TYPE_STELLAR) { - bool32 shouldBoost = IsTypeStellarBoosted(battler, type); - if (IS_BATTLER_OF_BASE_TYPE(battler, type)) + bool32 shouldBoost = IsTypeStellarBoosted(ctx->battlerAtk, ctx->moveType); + if (IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) { if (shouldBoost) return UQ_4_12(2.0); @@ -158,18 +157,18 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type) return UQ_4_12(1.0); } // Base and Tera type. - if (type == teraType && IS_BATTLER_OF_BASE_TYPE(battler, type)) + if (ctx->moveType == teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) { - if (hasAdaptability) + if (ctx->abilityAtk == ABILITY_ADAPTABILITY) return UQ_4_12(2.25); else return UQ_4_12(2.0); } // Base or Tera type only. - else if ((type == teraType && !IS_BATTLER_OF_BASE_TYPE(battler, type)) - || (type != teraType && IS_BATTLER_OF_BASE_TYPE(battler, type))) + else if ((ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) + || (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))) { - if (hasAdaptability) + if (ctx->abilityAtk == ABILITY_ADAPTABILITY) return UQ_4_12(2.0); else return UQ_4_12(1.5); diff --git a/src/battle_util.c b/src/battle_util.c index a75c8397dd..cc5543618e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1928,25 +1928,24 @@ static inline bool32 TryActivatePowderStatus(u32 move) return FALSE; } -static enum MoveCanceller CancellerFlags(void) +static enum MoveCanceller CancellerClearFlags(struct BattleContext *ctx) { - gBattleMons[gBattlerAttacker].volatiles.destinyBond = FALSE; - gBattleMons[gBattlerAttacker].volatiles.grudge = FALSE; - gBattleMons[gBattlerAttacker].volatiles.glaiveRush = FALSE; + gBattleMons[ctx->battlerAtk].volatiles.grudge = FALSE; + gBattleMons[ctx->battlerAtk].volatiles.glaiveRush = FALSE; return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerStanceChangeOne(void) +static enum MoveCanceller CancellerStanceChangeOne(struct BattleContext *ctx) { - if (B_STANCE_CHANGE_FAIL < GEN_7 && gChosenMove == gCurrentMove && TryFormChangeBeforeMove()) + if (B_STANCE_CHANGE_FAIL < GEN_7 && gChosenMove == ctx->currentMove && TryFormChangeBeforeMove()) return MOVE_STEP_BREAK; return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerSkyDrop(void) +static enum MoveCanceller CancellerSkyDrop(struct BattleContext *ctx) { // If Pokemon is being held in Sky Drop - if (gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable == STATE_SKY_DROP) + if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) { gBattlescriptCurrInstr = BattleScript_MoveEnd; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; @@ -1955,13 +1954,13 @@ static enum MoveCanceller CancellerSkyDrop(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerRecharge(void) +static enum MoveCanceller CancellerRecharge(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.recharge) + if (gBattleMons[ctx->battlerAtk].volatiles.recharge) { - gBattleMons[gBattlerAttacker].volatiles.recharge = TRUE; - gDisableStructs[gBattlerAttacker].rechargeTimer = 0; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gBattleMons[ctx->battlerAtk].volatiles.recharge = TRUE; + gDisableStructs[ctx->battlerAtk].rechargeTimer = 0; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; @@ -1969,15 +1968,15 @@ static enum MoveCanceller CancellerRecharge(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerAsleepOrFrozen(void) +static enum MoveCanceller CancellerAsleepOrFrozen(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) { - if (UproarWakeUpCheck(gBattlerAttacker)) + if (UproarWakeUpCheck(ctx->battlerAtk)) { - TryDeactivateSleepClause(GetBattlerSide(gBattlerAttacker), gBattlerPartyIndexes[gBattlerAttacker]); - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; - gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE; + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; BattleScriptCall(BattleScript_MoveUsedWokeUp); return MOVE_STEP_REMOVES_STATUS; @@ -1985,20 +1984,19 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void) else { u8 toSub; - if (IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_EARLY_BIRD)) + if (IsAbilityAndRecord(ctx->battlerAtk, ctx->ability[ctx->battlerAtk], ABILITY_EARLY_BIRD)) toSub = 2; else toSub = 1; - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) < toSub) - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; + if ((gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) < toSub) + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; else - gBattleMons[gBattlerAttacker].status1 -= toSub; - if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + gBattleMons[ctx->battlerAtk].status1 -= toSub; + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) { - enum BattleMoveEffects moveEffect = GetMoveEffect(gChosenMove); - if (moveEffect != EFFECT_SNORE && moveEffect != EFFECT_SLEEP_TALK) + if (ctx->moveEffect != EFFECT_SNORE && ctx->moveEffect != EFFECT_SLEEP_TALK) { - gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility = TRUE; + gProtectStructs[ctx->battlerAtk].nonVolatileStatusImmobility = TRUE; gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_REMOVES_STATUS; @@ -2006,25 +2004,25 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void) } else { - TryDeactivateSleepClause(GetBattlerSide(gBattlerAttacker), gBattlerPartyIndexes[gBattlerAttacker]); - gBattleMons[gBattlerAttacker].volatiles.nightmare = FALSE; + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; BattleScriptCall(BattleScript_MoveUsedWokeUp); return MOVE_STEP_REMOVES_STATUS; } } } - else if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE && !MoveThawsUser(gCurrentMove)) + else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE && !MoveThawsUser(ctx->currentMove)) { if (!RandomPercentage(RNG_FROZEN, 20)) { - gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility = TRUE; + gProtectStructs[ctx->battlerAtk].nonVolatileStatusImmobility = TRUE; gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; } else // unfreeze { - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; BattleScriptCall(BattleScript_MoveUsedUnfroze); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; } @@ -2033,9 +2031,9 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerObedience(void) +static enum MoveCanceller CancellerObedience(struct BattleContext *ctx) { - if (!gBattleMons[gBattlerAttacker].volatiles.multipleTurns) + if (!gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) { enum Obedience obedienceResult = GetAttackerObedienceForAction(); switch (obedienceResult) @@ -2048,36 +2046,36 @@ static enum MoveCanceller CancellerObedience(void) // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; return MOVE_STEP_FAILURE; case DISOBEYS_HITS_SELF: - gBattlerTarget = gBattlerAttacker; - struct DamageContext ctx; - ctx.battlerAtk = ctx.battlerDef = gBattlerAttacker; - ctx.move = MOVE_NONE; - ctx.moveType = TYPE_MYSTERY; - ctx.isCrit = FALSE; - ctx.randomFactor = FALSE; - ctx.updateFlags = TRUE; - ctx.fixedBasePower = 40; - gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx); + gBattlerTarget = ctx->battlerAtk; + struct DamageContext dmgCtx; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gHitMarker |= HITMARKER_OBEYS; return MOVE_STEP_FAILURE; // Move doesn't fail but mon hits itself case DISOBEYS_FALL_ASLEEP: if (IsSleepClauseEnabled()) - gBattleStruct->battlerState[gBattlerAttacker].sleepClauseEffectExempt = TRUE; + gBattleStruct->battlerState[ctx->battlerAtk].sleepClauseEffectExempt = TRUE; gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; return MOVE_STEP_FAILURE; break; case DISOBEYS_WHILE_ASLEEP: gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; return MOVE_STEP_FAILURE; case DISOBEYS_RANDOM_MOVE: - gCurrentMove = gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; + gCurrentMove = gCalledMove = gBattleMons[ctx->battlerAtk].moves[gCurrMovePos]; BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove); gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); gHitMarker |= HITMARKER_OBEYS; @@ -2088,42 +2086,42 @@ static enum MoveCanceller CancellerObedience(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerPowerPoints(void) +static enum MoveCanceller CancellerPowerPoints(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] == 0 - && gCurrentMove != MOVE_STRUGGLE - && !gSpecialStatuses[gBattlerAttacker].dancerUsedMove - && !gBattleMons[gBattlerAttacker].volatiles.multipleTurns) + if (gBattleMons[ctx->battlerAtk].pp[gCurrMovePos] == 0 + && ctx->currentMove != MOVE_STRUGGLE + && !gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + && !gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) { gBattlescriptCurrInstr = BattleScript_NoPPForMove; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; return MOVE_STEP_FAILURE; } return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerTruant(void) +static enum MoveCanceller CancellerTruant(struct BattleContext *ctx) { - if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter) + if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gDisableStructs[ctx->battlerAtk].truantCounter) { - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; - gBattlerAbility = gBattlerAttacker; + gBattlerAbility = ctx->battlerAtk; gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; return MOVE_STEP_FAILURE; } return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerFlinch(void) +static enum MoveCanceller CancellerFlinch(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.flinched) + if (gBattleMons[ctx->battlerAtk].volatiles.flinched) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; @@ -2131,13 +2129,15 @@ static enum MoveCanceller CancellerFlinch(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerDisabled(void) +static enum MoveCanceller CancellerDisabled(struct BattleContext *ctx) { - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gDisableStructs[ctx->battlerAtk].disabledMove == ctx->currentMove + && gDisableStructs[ctx->battlerAtk].disabledMove != MOVE_NONE) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - gBattleScripting.battler = gBattlerAttacker; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; @@ -2145,30 +2145,32 @@ static enum MoveCanceller CancellerDisabled(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerVolatileBlocked(void) +static enum MoveCanceller CancellerVolatileBlocked(struct BattleContext *ctx) { - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gBattleMons[gBattlerAttacker].volatiles.healBlock && IsHealBlockPreventingMove(gBattlerAttacker, gCurrentMove)) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gBattleMons[ctx->battlerAtk].volatiles.healBlock + && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->currentMove)) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - gBattleScripting.battler = gBattlerAttacker; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; } - else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(gCurrentMove)) + else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->currentMove)) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - gBattleScripting.battler = gBattlerAttacker; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; } - else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer > gBattleTurnCounter && IsSoundMove(gCurrentMove)) + else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gDisableStructs[ctx->battlerAtk].throatChopTimer > gBattleTurnCounter && IsSoundMove(ctx->currentMove)) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; @@ -2176,12 +2178,12 @@ static enum MoveCanceller CancellerVolatileBlocked(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerTaunted(void) +static enum MoveCanceller CancellerTaunted(struct BattleContext *ctx) { - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].tauntTimer && IsBattleMoveStatus(gCurrentMove)) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gDisableStructs[ctx->battlerAtk].tauntTimer && IsBattleMoveStatus(ctx->currentMove)) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_BREAK; @@ -2189,12 +2191,12 @@ static enum MoveCanceller CancellerTaunted(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerImprisoned(void) +static enum MoveCanceller CancellerImprisoned(struct BattleContext *ctx) { - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove)) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->currentMove)) { - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_FAILURE; @@ -2202,29 +2204,28 @@ static enum MoveCanceller CancellerImprisoned(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerConfused(void) +static enum MoveCanceller CancellerConfused(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.confusionTurns) + if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) { - if (!gBattleMons[gBattlerAttacker].volatiles.infiniteConfusion) - gBattleMons[gBattlerAttacker].volatiles.confusionTurns--; - if (gBattleMons[gBattlerAttacker].volatiles.confusionTurns) + if (!gBattleMons[ctx->battlerAtk].volatiles.infiniteConfusion) + gBattleMons[ctx->battlerAtk].volatiles.confusionTurns--; + if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) { // confusion dmg if (RandomPercentage(RNG_CONFUSION, (GetGenConfig(GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; - gBattlerTarget = gBattlerAttacker; - struct DamageContext ctx; - ctx.battlerAtk = ctx.battlerDef = gBattlerAttacker; - ctx.move = MOVE_NONE; - ctx.moveType = TYPE_MYSTERY; - ctx.isCrit = FALSE; - ctx.randomFactor = FALSE; - ctx.updateFlags = TRUE; - ctx.fixedBasePower = 40; - gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx); - gProtectStructs[gBattlerAttacker].confusionSelfDmg = TRUE; + struct DamageContext dmgCtx; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); + gProtectStructs[ctx->battlerAtk].confusionSelfDmg = TRUE; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; } @@ -2243,13 +2244,13 @@ static enum MoveCanceller CancellerConfused(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerParalysed(void) +static enum MoveCanceller CancellerParalysed(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS - && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS + && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->ability[ctx->battlerAtk], ABILITY_MAGIC_GUARD)) && !RandomPercentage(RNG_PARALYSIS, 75)) { - gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility = TRUE; + gProtectStructs[ctx->battlerAtk].nonVolatileStatusImmobility = TRUE; // This is removed in FRLG and Emerald for some reason //CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; @@ -2259,11 +2260,11 @@ static enum MoveCanceller CancellerParalysed(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerInfatuation(void) +static enum MoveCanceller CancellerInfatuation(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.infatuation) + if (gBattleMons[ctx->battlerAtk].volatiles.infatuation) { - gBattleScripting.battler = gBattleMons[gBattlerAttacker].volatiles.infatuation - 1; + gBattleScripting.battler = gBattleMons[ctx->battlerAtk].volatiles.infatuation - 1; if (!RandomPercentage(RNG_INFATUATION, 50)) { BattleScriptCall(BattleScript_MoveUsedIsInLove); @@ -2273,8 +2274,8 @@ static enum MoveCanceller CancellerInfatuation(void) { BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; - gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].unableToUseMove = TRUE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; return MOVE_STEP_FAILURE; } @@ -2282,11 +2283,11 @@ static enum MoveCanceller CancellerInfatuation(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerBide(void) +static enum MoveCanceller CancellerBide(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.bideTurns) + if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns) { - if (--gBattleMons[gBattlerAttacker].volatiles.bideTurns) + if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns) { gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; } @@ -2294,11 +2295,11 @@ static enum MoveCanceller CancellerBide(void) { // This is removed in FRLG and Emerald for some reason //gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE; - if (gBideDmg[gBattlerAttacker]) + if (gBideDmg[ctx->battlerAtk]) { gCurrentMove = MOVE_BIDE; - gBattlerTarget = gBideTarget[gBattlerAttacker]; - if (gAbsentBattlerFlags & (1u << gBattlerTarget)) + gBattlerTarget = gBideTarget[ctx->battlerAtk]; + if (gAbsentBattlerFlags & (1u << ctx->battlerDef)) gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1); gBattlescriptCurrInstr = BattleScript_BideAttack; return MOVE_STEP_BREAK; // Jumps to a different script but no failure @@ -2313,16 +2314,16 @@ static enum MoveCanceller CancellerBide(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerZMoves(void) +static enum MoveCanceller CancellerZMoves(struct BattleContext *ctx) { - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE) { // attacker has a queued z move - RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL); - SetGimmickAsActivated(gBattlerAttacker, GIMMICK_Z_MOVE); + RecordItemEffectBattle(ctx->battlerAtk, HOLD_EFFECT_Z_CRYSTAL); + SetGimmickAsActivated(ctx->battlerAtk, GIMMICK_Z_MOVE); - gBattleScripting.battler = gBattlerAttacker; - if (GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS) + gBattleScripting.battler = ctx->battlerAtk; + if (GetMoveCategory(ctx->currentMove) == DAMAGE_CATEGORY_STATUS) BattleScriptCall(BattleScript_ZMoveActivateStatus); else BattleScriptCall(BattleScript_ZMoveActivateDamaging); @@ -2332,20 +2333,20 @@ static enum MoveCanceller CancellerZMoves(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerChoiceLock(void) +static enum MoveCanceller CancellerChoiceLock(struct BattleContext *ctx) { - u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); + u16 *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; + enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk, TRUE); if (gChosenMove != MOVE_STRUGGLE && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) - && (IsHoldEffectChoice(holdEffect) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)) + && (IsHoldEffectChoice(holdEffect) || ctx->ability[ctx->battlerAtk] == ABILITY_GORILLA_TACTICS)) *choicedMoveAtk = gChosenMove; u32 moveIndex; for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { - if (gBattleMons[gBattlerAttacker].moves[moveIndex] == *choicedMoveAtk) + if (gBattleMons[ctx->battlerAtk].moves[moveIndex] == *choicedMoveAtk) break; } @@ -2355,15 +2356,14 @@ static enum MoveCanceller CancellerChoiceLock(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerCallSubmove(void) +static enum MoveCanceller CancellerCallSubmove(struct BattleContext *ctx) { u32 noEffect = FALSE; u32 calledMove = MOVE_NONE; - u32 effect = GetMoveEffect(gCurrentMove); const u8 *battleScript = NULL; battleScript = BattleScript_SubmoveAttackstring; - switch(effect) + switch(ctx->moveEffect) { case EFFECT_MIRROR_MOVE: calledMove = GetMirrorMoveMove(); @@ -2376,7 +2376,7 @@ static enum MoveCanceller CancellerCallSubmove(void) calledMove = GetAssistMove(); break; case EFFECT_NATURE_POWER: - calledMove = GetNaturePowerMove(gBattlerAttacker); + calledMove = GetNaturePowerMove(ctx->battlerAtk); battleScript = BattleScript_NaturePowerAttackstring; break; case EFFECT_SLEEP_TALK: @@ -2402,9 +2402,9 @@ static enum MoveCanceller CancellerCallSubmove(void) if (calledMove != MOVE_NONE) { - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) calledMove = GetTypeBasedZMove(calledMove); - if (effect == EFFECT_COPYCAT && IsMaxMove(calledMove)) + if (ctx->moveEffect == EFFECT_COPYCAT && IsMaxMove(calledMove)) calledMove = gBattleStruct->dynamax.lastUsedBaseMove; gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS; @@ -2417,23 +2417,23 @@ static enum MoveCanceller CancellerCallSubmove(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerThaw(void) +static enum MoveCanceller CancellerThaw(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE) { - if (!(IsMoveEffectRemoveSpeciesType(gCurrentMove, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_FIRE))) + if (!(IsMoveEffectRemoveSpeciesType(ctx->currentMove, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) { - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; BattleScriptCall(BattleScript_MoveUsedUnfroze); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; } return MOVE_STEP_REMOVES_STATUS; } - if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE && MoveThawsUser(gCurrentMove)) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE && MoveThawsUser(ctx->currentMove)) { - if (!(IsMoveEffectRemoveSpeciesType(gCurrentMove, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_FIRE))) + if (!(IsMoveEffectRemoveSpeciesType(ctx->currentMove, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) { - gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FROSTBITE; + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FROSTBITE; BattleScriptCall(BattleScript_MoveUsedUnfrostbite); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FROSTBITE_HEALED_BY_MOVE; } @@ -2442,29 +2442,29 @@ static enum MoveCanceller CancellerThaw(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerStanceChangeTwo(void) +static enum MoveCanceller CancellerStanceChangeTwo(struct BattleContext *ctx) { - if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == gCurrentMove && TryFormChangeBeforeMove()) + if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == ctx->currentMove && TryFormChangeBeforeMove()) return MOVE_STEP_BREAK; return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerAttackstring(void) +static enum MoveCanceller CancellerAttackstring(struct BattleContext *ctx) { gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED; BattleScriptCall(BattleScript_Attackstring); return MOVE_STEP_BREAK; } -static enum MoveCanceller CancellerPPDeduction(void) +static enum MoveCanceller CancellerPPDeduction(struct BattleContext *ctx) { - if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns - || gSpecialStatuses[gBattlerAttacker].dancerUsedMove - || gCurrentMove == MOVE_STRUGGLE) + if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns + || gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + || ctx->currentMove == MOVE_STRUGGLE) return MOVE_STEP_SUCCESS; s32 ppToDeduct = 1; - u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + u32 moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->currentMove); u32 movePosition = gCurrMovePos; if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS) @@ -2473,37 +2473,41 @@ static enum MoveCanceller CancellerPPDeduction(void) if (moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY || moveTarget == MOVE_TARGET_ALL_BATTLERS - || MoveForcesPressure(gCurrentMove)) + || MoveForcesPressure(ctx->currentMove)) { for (u32 i = 0; i < gBattlersCount; i++) { - if (!IsBattlerAlly(i, gBattlerAttacker) && IsBattlerAlive(i)) + if (!IsBattlerAlly(i, ctx->battlerAtk) && IsBattlerAlive(i)) ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE); } } else if (moveTarget != MOVE_TARGET_OPPONENTS_FIELD) { - if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE) + if (ctx->battlerAtk != ctx->battlerDef && ctx->ability[ctx->battlerDef] == ABILITY_PRESSURE) ppToDeduct++; } - gProtectStructs[gBattlerAttacker].notFirstStrike = TRUE; + gProtectStructs[ctx->battlerAtk].notFirstStrike = TRUE; // For item Metronome, echoed voice - if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker)) - gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0; + if (ctx->currentMove != gLastResultingMoves[ctx->battlerAtk] || WasUnableToUseMove(ctx->battlerAtk)) + gBattleStruct->sameMoveTurns[ctx->battlerAtk] = 0; - if (gBattleMons[gBattlerAttacker].pp[movePosition] > ppToDeduct) - gBattleMons[gBattlerAttacker].pp[movePosition] -= ppToDeduct; + if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct) + gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct; else - gBattleMons[gBattlerAttacker].pp[movePosition] = 0; + gBattleMons[ctx->battlerAtk].pp[movePosition] = 0; - if (MOVE_IS_PERMANENT(gBattlerAttacker, movePosition)) + if (MOVE_IS_PERMANENT(ctx->battlerAtk, movePosition)) { - BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_PPMOVE1_BATTLE + movePosition, 0, - sizeof(gBattleMons[gBattlerAttacker].pp[movePosition]), - &gBattleMons[gBattlerAttacker].pp[movePosition]); - MarkBattlerForControllerExec(gBattlerAttacker); + BtlController_EmitSetMonData( + ctx->battlerAtk, + B_COMM_TO_CONTROLLER, + REQUEST_PPMOVE1_BATTLE + movePosition, + 0, + sizeof(gBattleMons[ctx->battlerAtk].pp[movePosition]), + &gBattleMons[ctx->battlerAtk].pp[movePosition]); + MarkBattlerForControllerExec(ctx->battlerAtk); } if (gBattleStruct->submoveAnnouncement != SUBMOVE_NO_EFFECT) @@ -2514,7 +2518,7 @@ static enum MoveCanceller CancellerPPDeduction(void) gBattlescriptCurrInstr = BattleScript_ButItFailed; return MOVE_STEP_FAILURE; } - else if (CancellerVolatileBlocked() == MOVE_STEP_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove + else if (CancellerVolatileBlocked(ctx) == MOVE_STEP_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove { gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; return MOVE_STEP_FAILURE; @@ -2522,14 +2526,14 @@ static enum MoveCanceller CancellerPPDeduction(void) else { gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); + gBattlerTarget = GetBattleMoveTarget(ctx->currentMove, NO_TARGET_OVERRIDE); gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; // Possibly better to just move type setting and redirection to attackcanceller as a new case at this point - SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker); + SetTypeBeforeUsingMove(ctx->currentMove, ctx->battlerAtk); HandleMoveTargetRedirection(); - gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove); + gBattlescriptCurrInstr = GetMoveBattleScript(ctx->currentMove); return MOVE_STEP_BREAK; } } @@ -2537,13 +2541,13 @@ static enum MoveCanceller CancellerPPDeduction(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerWeatherPrimal(void) +static enum MoveCanceller CancellerWeatherPrimal(struct BattleContext *ctx) { enum MoveCanceller effect = MOVE_STEP_SUCCESS; - if (HasWeatherEffect() && GetMovePower(gCurrentMove) > 0) + if (HasWeatherEffect() && GetMovePower(ctx->currentMove) > 0) { - u32 moveType = GetBattleMoveType(gCurrentMove); - if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL) && (B_POWDER_RAIN >= GEN_7 || !TryActivatePowderStatus(gCurrentMove))) + u32 moveType = GetBattleMoveType(ctx->currentMove); + if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL) && (B_POWDER_RAIN >= GEN_7 || !TryActivatePowderStatus(ctx->currentMove))) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; effect = MOVE_STEP_FAILURE; @@ -2556,8 +2560,8 @@ static enum MoveCanceller CancellerWeatherPrimal(void) if (effect == MOVE_STEP_FAILURE) { gBattleScripting.moveEffect = MOVE_EFFECT_NONE; - gProtectStructs[gBattlerAttacker].chargingTurn = FALSE; - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; } @@ -2565,27 +2569,145 @@ static enum MoveCanceller CancellerWeatherPrimal(void) return effect; } -static enum MoveCanceller CancellerDynamaxBlocked(void) +static enum MoveCanceller CancellerMoveFailure(struct BattleContext *ctx) { - if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove)) + const u8 *battleScript = NULL; + + switch (ctx->moveEffect) + { + case EFFECT_FAIL_IF_NOT_ARG_TYPE: + if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->currentMove))) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_AURA_WHEEL: + if (gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_FULL_BELLY + && gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_HANGRY) + battleScript = BattleScript_PokemonCantUseTheMove; + break; + case EFFECT_AURORA_VEIL: + if (!(gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW) && HasWeatherEffect())) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_CLANGOROUS_SOUL: + if (gBattleMons[ctx->battlerAtk].hp <= max(1, GetNonDynamaxMaxHP(ctx->battlerAtk) / 3)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_COUNTER: + case EFFECT_MIRROR_COAT: + case EFFECT_METAL_BURST: + // TODO: Needs a refactor because the moves currently don't work according to new gens + break; + case EFFECT_DESTINY_BOND: + if (DoesDestinyBondFail(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FIRST_TURN_ONLY: + if (!gDisableStructs[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_MAT_BLOCK: + if (!gDisableStructs[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FLING: + if (!CanFling(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FOLLOW_ME: + if (B_UPDATED_MOVE_DATA >= GEN_8 && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FUTURE_SIGHT: + if (gWishFutureKnock.futureSightCounter[ctx->battlerDef] > gBattleTurnCounter) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_LAST_RESORT: + if (!CanUseLastResort(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_NO_RETREAT: + if (gBattleMons[ctx->battlerDef].volatiles.noRetreat) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_POLTERGEIST: + if (gBattleMons[ctx->battlerDef].item == ITEM_NONE + || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM + || ctx->ability[ctx->battlerDef] == ABILITY_KLUTZ) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_PROTECT: + // TODO + break; + case EFFECT_REST: + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP + || ctx->ability[ctx->battlerAtk] == ABILITY_COMATOSE) + battleScript = BattleScript_RestIsAlreadyAsleep; + else if (gBattleMons[ctx->battlerAtk].hp == gBattleMons[ctx->battlerAtk].maxHP) + battleScript = BattleScript_AlreadyAtFullHp; + else if (ctx->ability[ctx->battlerAtk] == ABILITY_INSOMNIA + || ctx->ability[ctx->battlerAtk] == ABILITY_VITAL_SPIRIT + || ctx->ability[ctx->battlerAtk] == ABILITY_PURIFYING_SALT) + battleScript = BattleScript_InsomniaProtects; + break; + case EFFECT_SUCKER_PUNCH: + if ((GetBattlerTurnOrderNum(ctx->battlerAtk) > GetBattlerTurnOrderNum(ctx->battlerDef)) + || (IsBattleMoveStatus(gBattleMons[ctx->battlerDef].moves[gBattleStruct->chosenMovePositions[ctx->battlerDef]]) && !gProtectStructs[ctx->battlerDef].noValidMoves)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_SNORE: + if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + && ctx->ability[ctx->battlerAtk] != ABILITY_COMATOSE) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STEEL_ROLLER: + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STOCKPILE: + if (gDisableStructs[ctx->battlerAtk].stockpileCounter >= 3) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STUFF_CHEEKS: + if (GetItemPocket(gBattleMons[ctx->battlerAtk].item) != POCKET_BERRIES) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_SWALLOW: + case EFFECT_SPIT_UP: + if (gDisableStructs[ctx->battlerAtk].stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_TELEPORT: + // TODO: follow up: Can't make sense of teleport logic + break; + case EFFECT_LOW_KICK: + case EFFECT_HEAT_CRASH: + if (GetActiveGimmick(ctx->battlerDef) == GIMMICK_DYNAMAX) + battleScript = BattleScript_MoveBlockedByDynamax; + break; + default: + break; + } + + if (battleScript != NULL) { gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; - gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax; + gBattlescriptCurrInstr = battleScript; return MOVE_STEP_FAILURE; } + return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerPowderStatus(void) +static enum MoveCanceller CancellerPowderStatus(struct BattleContext *ctx) { - if (TryActivatePowderStatus(gCurrentMove)) + if (TryActivatePowderStatus(ctx->currentMove)) { - if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) - gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; + if (!IsAbilityAndRecord(ctx->battlerAtk, ctx->ability[ctx->battlerAtk], ABILITY_MAGIC_GUARD)) + gBattleStruct->moveDamage[ctx->battlerAtk] = GetNonDynamaxMaxHP(ctx->battlerAtk) / 4; // This might be incorrect - if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE - || HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE)) + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + || HasTrainerUsedGimmick(ctx->battlerAtk, GIMMICK_Z_MOVE)) gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return MOVE_STEP_BREAK; @@ -2593,16 +2715,67 @@ static enum MoveCanceller CancellerPowderStatus(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerProtean(void) +bool32 IsDazzlingAbility(u32 ability) { - u32 moveType = GetBattleMoveType(gCurrentMove); - if (ProteanTryChangeType(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), gCurrentMove, moveType)) + switch (ability) + { + case ABILITY_DAZZLING: return TRUE; + case ABILITY_QUEENLY_MAJESTY: return TRUE; + case ABILITY_ARMOR_TAIL: return TRUE; + default: break; + } + return FALSE; +} + +static enum MoveCanceller CancellerPriorityBlock(struct BattleContext *ctx) +{ + bool32 effect = FALSE; + s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->ability[ctx->battlerAtk]); + u32 blockAbility = ABILITY_NONE; // ability of battler who is blocking + u32 blockedByBattler = ctx->battlerDef; + + if (priority <= 0 || IsBattlerAlly(ctx->battlerAtk, ctx->battlerDef)) + return MOVE_STEP_SUCCESS; + + if (IsDazzlingAbility(ctx->ability[ctx->battlerDef])) + { + blockAbility = ctx->ability[ctx->battlerDef]; + effect = TRUE; + } + else if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(ctx->battlerDef))) + { + blockAbility = GetBattlerAbility(BATTLE_PARTNER(ctx->battlerDef)); + if (IsDazzlingAbility(blockAbility)) + { + blockedByBattler = BATTLE_PARTNER(ctx->battlerDef); + effect = TRUE; + } + } + + if (effect) + { + gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. + gLastUsedAbility = blockAbility; + RecordAbilityBattle(blockedByBattler, blockAbility); + gBattleScripting.battler = gBattlerAbility = blockedByBattler; + gBattlescriptCurrInstr = BattleScript_DazzlingProtected; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + return MOVE_STEP_FAILURE; + } + + return MOVE_STEP_SUCCESS; +} + +static enum MoveCanceller CancellerProtean(struct BattleContext *ctx) +{ + u32 moveType = GetBattleMoveType(ctx->currentMove); + if (ProteanTryChangeType(ctx->battlerAtk, ctx->ability[ctx->battlerAtk], ctx->currentMove, moveType)) { if (GetGenConfig(GEN_PROTEAN_LIBERO) >= GEN_9) - gDisableStructs[gBattlerAttacker].usedProteanLibero = TRUE; + gDisableStructs[ctx->battlerAtk].usedProteanLibero = TRUE; PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); - gBattlerAbility = gBattlerAttacker; - PrepareStringBattle(STRINGID_EMPTYSTRING3, gBattlerAttacker); + gBattlerAbility = ctx->battlerAtk; + PrepareStringBattle(STRINGID_EMPTYSTRING3, ctx->battlerAtk); gBattleCommunication[MSG_DISPLAY] = 1; BattleScriptCall(BattleScript_ProteanActivates); return MOVE_STEP_BREAK; @@ -2610,27 +2783,10 @@ static enum MoveCanceller CancellerProtean(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerPsychicTerrain(void) -{ - if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN - && IsBattlerGrounded(gBattlerTarget) - && GetChosenMovePriority(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) > 0 - && GetMoveTarget(gCurrentMove) != MOVE_TARGET_ALL_BATTLERS - && GetMoveTarget(gCurrentMove) != MOVE_TARGET_OPPONENTS_FIELD - && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)) - { - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents; - gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceller CancellerExplodingDamp(void) +static enum MoveCanceller CancellerExplodingDamp(struct BattleContext *ctx) { u32 dampBattler = IsAbilityOnField(ABILITY_DAMP); - if (dampBattler && IsMoveDampBanned(gCurrentMove)) + if (dampBattler && IsMoveDampBanned(ctx->currentMove)) { gBattleScripting.battler = dampBattler - 1; gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; @@ -2640,19 +2796,19 @@ static enum MoveCanceller CancellerExplodingDamp(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerMultihitMoves(void) +static enum MoveCanceller CancellerMultihitMoves(struct BattleContext *ctx) { - if (GetMoveEffect(gCurrentMove) == EFFECT_MULTI_HIT) + if (GetMoveEffect(ctx->currentMove) == EFFECT_MULTI_HIT) { - u32 ability = GetBattlerAbility(gBattlerAttacker); + u32 ability = ctx->ability[ctx->battlerAtk]; if (ability == ABILITY_SKILL_LINK) { gMultiHitCounter = 5; } else if (ability == ABILITY_BATTLE_BOND - && gCurrentMove == MOVE_WATER_SHURIKEN - && gBattleMons[gBattlerAttacker].species == SPECIES_GRENINJA_ASH) + && ctx->currentMove == MOVE_WATER_SHURIKEN + && gBattleMons[ctx->battlerAtk].species == SPECIES_GRENINJA_ASH) { gMultiHitCounter = 3; } @@ -2663,28 +2819,28 @@ static enum MoveCanceller CancellerMultihitMoves(void) PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) } - else if (GetMoveStrikeCount(gCurrentMove) > 1) + else if (GetMoveStrikeCount(ctx->currentMove) > 1) { - if (GetMoveEffect(gCurrentMove) == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE) + if (ctx->moveEffect == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(ctx->battlerAtk, TRUE) == HOLD_EFFECT_LOADED_DICE) { gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); } else { - gMultiHitCounter = GetMoveStrikeCount(gCurrentMove); + gMultiHitCounter = GetMoveStrikeCount(ctx->currentMove); - if (GetMoveEffect(gCurrentMove) == EFFECT_DRAGON_DARTS - && !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove) - && CanTargetPartner(gBattlerAttacker, gBattlerTarget) - && TargetFullyImmuneToCurrMove(gBattlerAttacker, gBattlerTarget)) - gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); + if (ctx->moveEffect == EFFECT_DRAGON_DARTS + && !IsAffectedByFollowMe(ctx->battlerAtk, GetBattlerSide(ctx->battlerDef), ctx->currentMove) + && CanTargetPartner(ctx->battlerAtk, ctx->battlerDef) + && TargetFullyImmuneToCurrMove(ctx->battlerAtk, ctx->battlerDef)) + gBattlerTarget = BATTLE_PARTNER(ctx->battlerDef); } PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) } - else if (B_BEAT_UP >= GEN_5 && GetMoveEffect(gCurrentMove) == EFFECT_BEAT_UP) + else if (B_BEAT_UP >= GEN_5 && ctx->moveEffect == EFFECT_BEAT_UP) { - struct Pokemon* party = GetBattlerParty(gBattlerAttacker); + struct Pokemon* party = GetBattlerParty(ctx->battlerAtk); int i; for (i = 0; i < PARTY_SIZE; i++) @@ -2707,10 +2863,10 @@ static enum MoveCanceller CancellerMultihitMoves(void) return MOVE_STEP_SUCCESS; } -static enum MoveCanceller CancellerMultiTargetMoves(void) +static enum MoveCanceller CancellerMultiTargetMoves(struct BattleContext *ctx) { - u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); - u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); + u32 moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->currentMove); + u32 abilityAtk = ctx->ability[ctx->battlerAtk]; if (IsSpreadMove(moveTarget)) { @@ -2721,43 +2877,43 @@ static enum MoveCanceller CancellerMultiTargetMoves(void) u32 abilityDef = GetBattlerAbility(battlerDef); - if (gBattlerAttacker == battlerDef + if (ctx->battlerAtk == battlerDef || !IsBattlerAlive(battlerDef) - || (GetMoveEffect(gCurrentMove) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(gBattlerAttacker, battlerDef)) - || (moveTarget == MOVE_TARGET_BOTH && gBattlerAttacker == BATTLE_PARTNER(battlerDef)) - || IsBattlerProtected(gBattlerAttacker, battlerDef, gCurrentMove)) // Missing Invulnerable check + || (ctx->moveEffect == EFFECT_SYNCHRONOISE && !DoBattlersShareType(ctx->battlerAtk, battlerDef)) + || (moveTarget == MOVE_TARGET_BOTH && ctx->battlerAtk == BATTLE_PARTNER(battlerDef)) + || IsBattlerProtected(ctx->battlerAtk, battlerDef, ctx->currentMove)) // Missing Invulnerable check { gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; gBattleStruct->noResultString[battlerDef] = WILL_FAIL; } - else if (CanAbilityBlockMove(gBattlerAttacker, battlerDef, abilityAtk, abilityDef, gCurrentMove, CHECK_TRIGGER) - || (IsBattlerTerrainAffected(gBattlerAttacker, STATUS_FIELD_PSYCHIC_TERRAIN) && GetBattleMovePriority(gBattlerAttacker, abilityAtk, gCurrentMove) > 0)) + else if (CanAbilityBlockMove(ctx->battlerAtk, battlerDef, abilityAtk, abilityDef, ctx->currentMove, CHECK_TRIGGER) + || (IsBattlerTerrainAffected(ctx->battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && GetBattleMovePriority(ctx->battlerAtk, abilityAtk, gCurrentMove) > 0)) { gBattleStruct->moveResultFlags[battlerDef] = 0; gBattleStruct->noResultString[battlerDef] = WILL_FAIL; } - else if (CanAbilityAbsorbMove(gBattlerAttacker, battlerDef, abilityDef, gCurrentMove, GetBattleMoveType(gCurrentMove), CHECK_TRIGGER)) + else if (CanAbilityAbsorbMove(ctx->battlerAtk, battlerDef, abilityDef, ctx->currentMove, GetBattleMoveType(gCurrentMove), CHECK_TRIGGER)) { gBattleStruct->moveResultFlags[battlerDef] = 0; gBattleStruct->noResultString[battlerDef] = CHECK_ACCURACY; } else { - CalcTypeEffectivenessMultiplierHelper(gCurrentMove, GetBattleMoveType(gCurrentMove), gBattlerAttacker, battlerDef, abilityAtk, abilityDef, TRUE); // Sets moveResultFlags + CalcTypeEffectivenessMultiplierHelper(ctx->currentMove, GetBattleMoveType(ctx->currentMove), ctx->battlerAtk, battlerDef, abilityAtk, abilityDef, TRUE); // Sets moveResultFlags gBattleStruct->noResultString[battlerDef] = CAN_DAMAGE; } } if (moveTarget == MOVE_TARGET_BOTH) - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, gBattlerAttacker); + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, ctx->battlerAtk); else - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker); + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, ctx->battlerAtk); } return MOVE_STEP_SUCCESS; } -static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) = +static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(struct BattleContext *ctx) = { - [CANCELLER_FLAGS] = CancellerFlags, + [CANCELLER_CLEAR_FLAGS] = CancellerClearFlags, [CANCELLER_STANCE_CHANGE_1] = CancellerStanceChangeOne, [CANCELLER_SKY_DROP] = CancellerSkyDrop, [CANCELLER_RECHARGE] = CancellerRecharge, @@ -2782,29 +2938,35 @@ static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) = [CANCELLER_ATTACKSTRING] = CancellerAttackstring, [CANCELLER_PPDEDUCTION] = CancellerPPDeduction, [CANCELLER_WEATHER_PRIMAL] = CancellerWeatherPrimal, - [CANCELLER_DYNAMAX_BLOCKED] = CancellerDynamaxBlocked, + [CANCELLER_MOVE_FAILURE] = CancellerMoveFailure, [CANCELLER_POWDER_STATUS] = CancellerPowderStatus, + [CANCELLER_PRIORITY_BLOCK] = CancellerPriorityBlock, [CANCELLER_PROTEAN] = CancellerProtean, - [CANCELLER_PSYCHIC_TERRAIN] = CancellerPsychicTerrain, [CANCELLER_EXPLODING_DAMP] = CancellerExplodingDamp, [CANCELLER_MULTIHIT_MOVES] = CancellerMultihitMoves, [CANCELLER_MULTI_TARGET_MOVES] = CancellerMultiTargetMoves, }; -enum MoveCanceller AtkCanceller_MoveSuccessOrder(void) +enum MoveCanceller AtkCanceller_MoveSuccessOrder(struct BattleContext *ctx) { enum MoveCanceller effect = MOVE_STEP_SUCCESS; while (gBattleStruct->atkCancellerTracker < CANCELLER_END && effect == MOVE_STEP_SUCCESS) { - effect = sMoveSuccessOrderCancellers[gBattleStruct->atkCancellerTracker](); + effect = sMoveSuccessOrderCancellers[gBattleStruct->atkCancellerTracker](ctx); gBattleStruct->atkCancellerTracker++; } if (effect == MOVE_STEP_REMOVES_STATUS) { - BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerAttacker].status1); - MarkBattlerForControllerExec(gBattlerAttacker); + BtlController_EmitSetMonData( + ctx->battlerAtk, + B_COMM_TO_CONTROLLER, + REQUEST_STATUS_BATTLE, + 0, + 4, + &gBattleMons[gBattlerAttacker].status1); + MarkBattlerForControllerExec(ctx->battlerAtk); } return effect; @@ -3181,13 +3343,6 @@ static void ChooseStatBoostAnimation(u32 battler) bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option) { const u8 *battleScriptBlocksMove = NULL; - u32 battlerAbility = battlerDef; - s32 atkPriority = 0; - - if (option == AI_CHECK) - atkPriority = GetBattleMovePriority(battlerAtk, abilityAtk, move); - else - atkPriority = GetChosenMovePriority(battlerAtk, abilityAtk); switch (abilityDef) { @@ -3199,12 +3354,6 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a if (IsBallisticMove(move)) battleScriptBlocksMove = BattleScript_SoundproofProtected; break; - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - if (atkPriority > 0 && !IsBattlerAlly(battlerAtk, battlerDef)) - battleScriptBlocksMove = BattleScript_DazzlingProtected; - break; case ABILITY_GOOD_AS_GOLD: if (IsBattleMoveStatus(move)) { @@ -3214,40 +3363,39 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a break; } - if (atkPriority > 0) + if (battleScriptBlocksMove == NULL) { - // Prankster check - if (battleScriptBlocksMove == NULL - && IsBattleMoveStatus(move) - && BlocksPrankster(move, battlerAtk, battlerDef, TRUE) - && !(IsBattleMoveStatus(move) && (abilityDef == ABILITY_MAGIC_BOUNCE || gProtectStructs[battlerDef].bounceMove))) + s32 atkPriority = 0; + if (option == AI_CHECK) + atkPriority = GetBattleMovePriority(battlerAtk, abilityAtk, move); + else + atkPriority = GetChosenMovePriority(battlerAtk, abilityAtk); + + if (atkPriority <= 0) + { + // Not a priority move + } + else if (IsBattleMoveStatus(move) + && BlocksPrankster(move, battlerAtk, battlerDef, TRUE) + && !(IsBattleMoveStatus(move) && (abilityDef == ABILITY_MAGIC_BOUNCE || gProtectStructs[battlerDef].bounceMove))) { if (option == RUN_SCRIPT && !IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move))) CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected - battleScriptBlocksMove = BattleScript_DoesntAffectTargetAtkString; } - - // Check def partner ability - u32 partnerDef = BATTLE_PARTNER(battlerDef); - if (battleScriptBlocksMove == NULL - && IsDoubleBattle() - && IsBattlerAlive(partnerDef) - && !IsBattlerAlly(battlerAtk, partnerDef)) + else if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_PSYCHIC_TERRAIN) // Not an ability but similar conditions + && !IsBattlerAlly(battlerAtk, battlerDef) + && GetMoveTarget(move) != MOVE_TARGET_ALL_BATTLERS + && GetMoveTarget(move) != MOVE_TARGET_OPPONENTS_FIELD) { - if (option == AI_CHECK) - abilityDef = gAiLogicData->abilities[partnerDef]; - else - abilityDef = GetBattlerAbility(partnerDef); - - switch (abilityDef) + battleScriptBlocksMove = BattleScript_MoveUsedPsychicTerrainPrevents; + if (option == RUN_SCRIPT) { - case ABILITY_DAZZLING: - case ABILITY_QUEENLY_MAJESTY: - case ABILITY_ARMOR_TAIL: - battlerAbility = partnerDef; - battleScriptBlocksMove = BattleScript_DazzlingProtected; - break; + gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. + if (!IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move))) + CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected + gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents; + return TRUE; // Early return since we don't want to set remaining values } } } @@ -3257,10 +3405,10 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a if (option == RUN_SCRIPT) { - gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. + gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. gLastUsedAbility = abilityDef; RecordAbilityBattle(battlerDef, abilityDef); - gBattleScripting.battler = gBattlerAbility = battlerAbility; + gBattleScripting.battler = gBattlerAbility = battlerDef; gBattlescriptCurrInstr = battleScriptBlocksMove; } @@ -4698,7 +4846,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_ABILITY_SHIELD); break; } - + RemoveAbilityFlags(gBattlerAttacker); gLastUsedAbility = gBattleMons[gBattlerAttacker].ability; gBattleMons[gBattlerAttacker].ability = gDisableStructs[gBattlerAttacker].overwrittenAbility = gBattleMons[gBattlerTarget].ability; @@ -7448,7 +7596,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler) void ClearVariousBattlerFlags(u32 battler) { gDisableStructs[battler].furyCutterCounter = 0; - gBattleMons[battler].volatiles.destinyBond = FALSE; + gBattleMons[battler].volatiles.destinyBond = 0; gBattleMons[battler].volatiles.glaiveRush = FALSE; gBattleMons[battler].volatiles.grudge = FALSE; } @@ -9555,7 +9703,7 @@ static inline s32 DoMoveDamageCalcVars(struct DamageContext *ctx) s32 ApplyModifiersAfterDmgRoll(struct DamageContext *ctx, s32 dmg) { if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_TERA) - DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(ctx->battlerAtk, ctx->moveType)); + DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(ctx)); else DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(ctx)); DAMAGE_APPLY_MODIFIER(ctx->typeEffectivenessModifier); @@ -11446,11 +11594,7 @@ void ClearDamageCalcResults(void) bool32 DoesDestinyBondFail(u32 battler) { - if (B_DESTINY_BOND_FAIL >= GEN_7 - && GetMoveEffect(gLastLandedMoves[battler]) == EFFECT_DESTINY_BOND - && GetMoveEffect(gLastResultingMoves[battler]) == EFFECT_DESTINY_BOND) - return TRUE; - return FALSE; + return B_DESTINY_BOND_FAIL >= GEN_7 && gBattleMons[battler].volatiles.destinyBond; } // This check has always to be the last in a condtion statement because of the recording of AI data. @@ -11893,11 +12037,11 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u switch (defAbility) { case ABILITY_SAND_VEIL: - if (HasWeatherEffect() && gBattleWeather & B_WEATHER_SANDSTORM) + if (gBattleWeather & B_WEATHER_SANDSTORM && HasWeatherEffect()) calc = (calc * 80) / 100; // 1.2 sand veil loss break; case ABILITY_SNOW_CLOAK: - if (HasWeatherEffect() && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) + if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && HasWeatherEffect()) calc = (calc * 80) / 100; // 1.2 snow cloak loss break; case ABILITY_TANGLED_FEET: diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 3816548ba5..03e55ba9f7 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -819,7 +819,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_FIRST_TURN_ONLY] = { - .battleScript = BattleScript_EffectFirstTurnOnly, + .battleScript = BattleScript_EffectHit, .battleTvScore = 4, .encourageEncore = TRUE, }, @@ -1392,7 +1392,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SUCKER_PUNCH] = { - .battleScript = BattleScript_EffectSuckerPunch, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, @@ -1531,7 +1531,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_LAST_RESORT] = { - .battleScript = BattleScript_EffectLastResort, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, @@ -1721,7 +1721,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_MAT_BLOCK] = { - .battleScript = BattleScript_EffectMatBlock, + .battleScript = BattleScript_EffectProtect, .battleTvScore = 0, // TODO: Assign points .encourageEncore = TRUE, }, @@ -1774,7 +1774,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_FAIL_IF_NOT_ARG_TYPE] = { - .battleScript = BattleScript_FailIfNotArgType, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, @@ -1881,7 +1881,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_AURA_WHEEL] = { - .battleScript = BattleScript_EffectAuraWheel, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, @@ -2233,7 +2233,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_STEEL_ROLLER] = { - .battleScript = BattleScript_EffectSteelRoller, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, diff --git a/test/battle/ability/dazzling.c b/test/battle/ability/dazzling.c index 7541b8a566..19cd3db0e1 100644 --- a/test/battle/ability/dazzling.c +++ b/test/battle/ability/dazzling.c @@ -96,3 +96,25 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect from all mu } } } + +SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail prevent Protean activation") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; } + PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; } + PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; } + + GIVEN { + PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); } + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_WATER_SHURIKEN); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_SHURIKEN, player); + ABILITY_POPUP(player, ABILITY_PROTEAN); + } + ABILITY_POPUP(opponent, ability); + } +} diff --git a/test/battle/ability/teraform_zero.c b/test/battle/ability/teraform_zero.c index 96186f2047..9304041164 100644 --- a/test/battle/ability/teraform_zero.c +++ b/test/battle/ability/teraform_zero.c @@ -44,6 +44,7 @@ SINGLE_BATTLE_TEST("Teraform Zero can be replaced") PLAYER(SPECIES_TERAPAGOS); OPPONENT(SPECIES_WHIMSICOTT) { Ability(ABILITY_PRANKSTER); } } WHEN { + TURN { MOVE(opponent, MOVE_POUND); } TURN { MOVE(opponent, MOVE_WORRY_SEED); MOVE(player, MOVE_REST, gimmick: GIMMICK_TERA); } } SCENE { MESSAGE("The opposing Whimsicott used Worry Seed!"); diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 8220fee053..e0a13971be 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -903,7 +903,7 @@ SINGLE_BATTLE_TEST("Dynamax: Max Mindstorm sets up Psychic Terrain") } SCENE { MESSAGE("The opposing Wobbuffet used Extreme Speed!"); MESSAGE("Wobbuffet used Max Mindstorm!"); - MESSAGE("The opposing Wobbuffet cannot use Extreme Speed!"); + MESSAGE("Wobbuffet is protected by the Psychic Terrain!"); MESSAGE("Wobbuffet used Max Mindstorm!"); } } @@ -1648,18 +1648,5 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamax is reverted before switch out") } } -SINGLE_BATTLE_TEST("Dynamax: Destiny Bond fails if a dynamaxed battler is present on field") -{ - GIVEN { - ASSUME(GetMoveEffect(MOVE_DESTINY_BOND) == EFFECT_DESTINY_BOND); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_DESTINY_BOND); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX); } - } SCENE { - MESSAGE("The move was blocked by the power of Dynamax!"); - } -} - TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-lowering Max Moves, without showing a message") TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-increasing Max Moves, without showing a message") diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index 1cea196ae7..e1acb4e406 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -588,7 +588,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Genesis Supernova sets up psychic terrain") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_GENESIS_SUPERNOVA, player); NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); } - MESSAGE("Mew cannot use Quick Attack!"); + MESSAGE("The opposing Wobbuffet is protected by the Psychic Terrain!"); } } diff --git a/test/battle/move_effect/aurora_veil.c b/test/battle/move_effect/aurora_veil.c index b57c85df06..1b480ad1ed 100644 --- a/test/battle/move_effect/aurora_veil.c +++ b/test/battle/move_effect/aurora_veil.c @@ -25,6 +25,35 @@ SINGLE_BATTLE_TEST("Aurora Veil can only be used in Hail and Snow") } } +SINGLE_BATTLE_TEST("Aurora Veil will prevent Protean activation if it fails due to no Snow/Hail") +{ + GIVEN { + PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_AURORA_VEIL); } + } SCENE { + MESSAGE("But it failed!"); + NOT ABILITY_POPUP(player, ABILITY_PROTEAN); + } +} + +SINGLE_BATTLE_TEST("Aurora Veil wont prevent Protean activation when it fails due to being set up already") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_AURORA_VEIL); } + TURN { SWITCH(player, 1); } + TURN { MOVE(player, MOVE_AURORA_VEIL); } + } SCENE { + ABILITY_POPUP(player, ABILITY_PROTEAN); + MESSAGE("But it failed!"); + } +} + TO_DO_BATTLE_TEST("Aurora Veil reduces damage done to the user by half in singles") TO_DO_BATTLE_TEST("Aurora Veil reduces damage done to the user by roughly a third in doubles") TO_DO_BATTLE_TEST("Aurora Veil's damage reduction is ignored by Critical Hits") diff --git a/test/battle/move_effect/clangorous_soul.c b/test/battle/move_effect/clangorous_soul.c index 17e57d7dd5..a1a7ffa42a 100644 --- a/test/battle/move_effect/clangorous_soul.c +++ b/test/battle/move_effect/clangorous_soul.c @@ -1,11 +1,52 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Attack by 1 stage"); -TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Defense by 1 stage"); -TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Sp. Attack by 1 stage"); -TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Sp. Defense by 1 stage"); -TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Speed by 1 stage"); -TO_DO_BATTLE_TEST("Clangorous Soul cuts the user's HP by 1/3"); +SINGLE_BATTLE_TEST("Clangorous Soul cuts the user's HP by 1/3") +{ + s16 dmg; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player); + HP_BAR(player, captureDamage: &dmg); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(gBattleMons[0].maxHP / 3, dmg); + } +} + +SINGLE_BATTLE_TEST("Clangorous Soul raises the user's Atk, Def, Sp. Atk. Sp. Def and Speed by 1 stage") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Clangorous Soul fails if the user's HP is less or equal than 1/3") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(300); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player); + } +} + TO_DO_BATTLE_TEST("Clangorous Soul fails if Attack, Defense, Sp. Attack, Sp. Defense and Speed are all maxed out"); -TO_DO_BATTLE_TEST("Clangorous Soul fails if the user's HP is less or equal than 1/3"); diff --git a/test/battle/move_effect/fail_if_not_arg_type.c b/test/battle/move_effect/fail_if_not_arg_type.c index c1371d393d..4be65a5677 100644 --- a/test/battle/move_effect/fail_if_not_arg_type.c +++ b/test/battle/move_effect/fail_if_not_arg_type.c @@ -42,7 +42,6 @@ TO_DO_BATTLE_TEST("Burn Up doesn't thaw the user if it fails due to the user not SINGLE_BATTLE_TEST("Burn Up fails if the user has Protean/Libero and is not a Fire-type") { - KNOWN_FAILING; GIVEN { WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_6); PLAYER(SPECIES_REGIROCK); diff --git a/test/battle/move_effect/no_retreat.c b/test/battle/move_effect/no_retreat.c index bf2a15dfbf..fa944ab776 100644 --- a/test/battle/move_effect/no_retreat.c +++ b/test/battle/move_effect/no_retreat.c @@ -2,3 +2,60 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write No Retreat (Move Effect) test titles") + +SINGLE_BATTLE_TEST("No Retreat raises user's Atk/Def/Sp.Atk/Sp.Def/Speed unless No Retreat was already used by user") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_NO_RETREAT); } + TURN { MOVE(player, MOVE_NO_RETREAT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + bool32 escapePrevention = gBattleMons[0].volatiles.escapePrevention; + EXPECT_EQ(escapePrevention, TRUE); + } +} + +// Question: If No Retreat is used is the mon blocking the switch out changed? +SINGLE_BATTLE_TEST("No Retreat won't fail if user is prevented from escaping") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MEAN_LOOK); MOVE(player, MOVE_NO_RETREAT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEAN_LOOK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } +} + +SINGLE_BATTLE_TEST("No Retreat won't activate Protean if it fails due to already being used by the user") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); } + } WHEN { + TURN { MOVE(player, MOVE_NO_RETREAT); MOVE(opponent, MOVE_SKILL_SWAP); } + TURN { MOVE(player, MOVE_NO_RETREAT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player); + ABILITY_POPUP(player, ABILITY_PROTEAN); + } + } +} diff --git a/test/battle/move_effect/psychic_terrain.c b/test/battle/move_effect/psychic_terrain.c index b85653a0be..40df878a1f 100644 --- a/test/battle/move_effect/psychic_terrain.c +++ b/test/battle/move_effect/psychic_terrain.c @@ -11,7 +11,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority mov TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_QUICK_ATTACK); } } SCENE { MESSAGE("Claydol used Psychic Terrain!"); - MESSAGE("Claydol cannot use Quick Attack!"); + MESSAGE("The opposing Wobbuffet is protected by the Psychic Terrain!"); NOT { HP_BAR(opponent); } MESSAGE("The opposing Wobbuffet used Quick Attack!"); HP_BAR(player); @@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain increases power of Psychic-type moves by 30/ } } -SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user") +SINGLE_BATTLE_TEST("Psychic Terrain doesn't blocks priority moves that target the user") { GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); HP(1); } @@ -142,3 +142,37 @@ SINGLE_BATTLE_TEST("Psychic Terrain lasts for 5 turns") MESSAGE("The weirdness disappeared from the battlefield!"); } } + +DOUBLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves in doubles - Left") +{ + GIVEN { + PLAYER(SPECIES_CLAYDOL) { Ability(ABILITY_LEVITATE); } + PLAYER(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); } + OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_COTTON_SPORE); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_PSYCHIC_SURGE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_COTTON_SPORE, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves in doubles - Right") +{ + GIVEN { + PLAYER(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); } + PLAYER(SPECIES_CLAYDOL) { Ability(ABILITY_LEVITATE); } + OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_COTTON_SPORE); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_PSYCHIC_SURGE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_COTTON_SPORE, opponentLeft); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } +} diff --git a/test/battle/move_effect/stockpile.c b/test/battle/move_effect/stockpile.c index 907643a1bd..e18c97fc57 100644 --- a/test/battle/move_effect/stockpile.c +++ b/test/battle/move_effect/stockpile.c @@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Stockpile's count can go up only to 3") MESSAGE("Wobbuffet stockpiled 3!"); NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player); - MESSAGE("Wobbuffet can't stockpile any more!"); + MESSAGE("But it failed!"); } } @@ -49,9 +49,9 @@ SINGLE_BATTLE_TEST("Spit Up and Swallow don't work if used without Stockpile") } SCENE { NOT ANIMATION(ANIM_TYPE_MOVE, move, player); if (move == MOVE_SWALLOW) - MESSAGE("But it failed to swallow a thing!"); + MESSAGE("But it failed!"); else - MESSAGE("But it failed to spit up a thing!"); + MESSAGE("But it failed!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player); MESSAGE("Wobbuffet stockpiled 1!");