diff --git a/include/battle.h b/include/battle.h index 335d8b24b1..f44b23aa57 100644 --- a/include/battle.h +++ b/include/battle.h @@ -6,6 +6,7 @@ #include "constants/battle_switch_in.h" #include "constants/abilities.h" #include "constants/battle.h" +#include "constants/battle_move_resolution.h" #include "constants/form_change_types.h" #include "constants/hold_effects.h" #include "constants/moves.h" @@ -554,7 +555,7 @@ struct EventStates enum FirstTurnEventsStates beforeFirstTurn:8; enum FaintedActions faintedAction:8; enum BattlerId faintedActionBattler:4; - enum MoveSuccessOrder atkCanceler:8; + enum CancelerState atkCanceler:8; enum BattlerId atkCancelerBattler:4; enum BattleIntroStates battleIntro:8; enum SwitchInEvents switchIn:8; diff --git a/include/battle_environment.h b/include/battle_environment.h index 75f6515cda..450fc5d51c 100644 --- a/include/battle_environment.h +++ b/include/battle_environment.h @@ -3,6 +3,7 @@ #include "constants/battle.h" #include "battle_bg.h" +#include "task.h" struct BattleEnvironment { diff --git a/include/battle_move_resolution.h b/include/battle_move_resolution.h index 34c63f3c2b..01ce813140 100644 --- a/include/battle_move_resolution.h +++ b/include/battle_move_resolution.h @@ -3,7 +3,8 @@ #include "constants/battle_move_resolution.h" -bool32 DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState); +enum CancelerResult DoAttackCanceler(void); +enum MoveEndResult DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState); void MoveValuesCleanUp(void); #endif // GUARD_BATTLE_MOVE_RESOLUTION_H diff --git a/include/battle_util.h b/include/battle_util.h index 0c5212f783..267fadfbe5 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -86,75 +86,11 @@ enum ItemEffect // for Natural Gift and Fling struct TypePower { - enum Type type; + enum Type type:8; u8 power; u16 effect; }; -enum MoveSuccessOrder -{ - CANCELER_STANCE_CHANGE_1, - CANCELER_CLEAR_FLAGS, - CANCELER_SKY_DROP, - CANCELER_RECHARGE, - CANCELER_CHILLY_RECEPTION, - CANCELER_ASLEEP_OR_FROZEN, - CANCELER_POWER_POINTS, - CANCELER_OBEDIENCE, - CANCELER_TRUANT, - CANCELER_FOCUS_GEN5, - CANCELER_FLINCH, - CANCELER_DISABLED, - CANCELER_VOLATILE_BLOCKED, // Gravity / Heal Block / Throat Chop - CANCELER_TAUNTED, - CANCELER_IMPRISONED, - CANCELER_CONFUSED, - CANCELER_PARALYZED, - CANCELER_INFATUATION, - CANCELER_BIDE, - CANCELER_Z_MOVES, - CANCELER_CHOICE_LOCK, - CANCELER_CALLSUBMOVE, - CANCELER_THAW, - CANCELER_STANCE_CHANGE_2, - CANCELER_ATTACKSTRING, - CANCELER_PPDEDUCTION, - CANCELER_SKY_BATTLE, - CANCELER_WEATHER_PRIMAL, - CANCELER_FOCUS_PRE_GEN5, - CANCELER_MOVE_FAILURE, - CANCELER_POWDER_STATUS, - CANCELER_PRIORITY_BLOCK, - CANCELER_PROTEAN, - CANCELER_EXPLODING_DAMP, - CANCELER_EXPLOSION, - CANCELER_CHARGING, - CANCELER_NO_TARGET, - CANCELER_TOOK_ATTACK, - CANCELER_TARGET_FAILURE, - CANCELER_NOT_FULLY_PROTECTED, - CANCELER_MULTIHIT_MOVES, - CANCELER_END, -}; - -enum Obedience -{ - OBEYS, - DISOBEYS_LOAFS, - DISOBEYS_HITS_SELF, - DISOBEYS_FALL_ASLEEP, - DISOBEYS_WHILE_ASLEEP, - DISOBEYS_RANDOM_MOVE, -}; - -enum MoveCanceler -{ - MOVE_STEP_SUCCESS, - MOVE_STEP_BREAK, // Runs script. Increments state - MOVE_STEP_PAUSE, // Runs script. Does not increment state - MOVE_STEP_FAILURE, // Move failed, jump to script that handles the failure -}; - enum ImmunityHealStatusOutcome { IMMUNITY_NO_EFFECT, @@ -240,6 +176,7 @@ void HandleAction_ThrowBall(void); uq4_12_t CalcTypeEffectivenessMultiplierHelper(enum Move move, enum Type moveType, u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, bool32 recordAbilities); u32 GetCurrentBattleWeather(void); bool32 EndOrContinueWeather(void); +enum DamageCategory GetReflectDamageMoveDamageCategory(u32 battler, enum Move move); bool32 IsUnnerveBlocked(u32 battler, enum Item itemId); bool32 IsAffectedByFollowMe(u32 battlerAtk, enum BattleSide defSide, enum Move move); void DetermineTarget(enum MoveTarget moveTarget, bool32 overwriteTarget); @@ -280,7 +217,6 @@ s32 GetDrainedBigRootHp(u32 battler, s32 hp); bool32 IsAbilityAndRecord(u32 battler, enum Ability battlerAbility, enum Ability abilityToCheck); bool32 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); -enum MoveCanceler AtkCanceler_MoveSuccessOrder(void); bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, enum Ability ability); bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag); @@ -363,6 +299,7 @@ void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, enum Move move); bool32 CanFling(u32 battlerAtk, u32 battlerDef); bool32 IsTelekinesisBannedSpecies(u16 species); bool32 IsHealBlockPreventingMove(u32 battler, enum Move move); +bool32 IsGravityPreventingMove(enum Move move); bool32 IsBelchPreventingMove(u32 battler, enum Move move); bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, enum Item itemId); bool32 IsPartnerMonFromSameTrainer(u32 battler); diff --git a/include/constants/battle_move_resolution.h b/include/constants/battle_move_resolution.h index 6645ff1af6..653690c7dd 100644 --- a/include/constants/battle_move_resolution.h +++ b/include/constants/battle_move_resolution.h @@ -1,11 +1,75 @@ #ifndef GUARD_CONSTANTS_BATTLE_MOVE_RESOLUTION_H #define GUARD_CONSTANTS_BATTLE_MOVE_RESOLUTION_H +enum Obedience +{ + OBEYS, + DISOBEYS_LOAFS, + DISOBEYS_HITS_SELF, + DISOBEYS_FALL_ASLEEP, + DISOBEYS_WHILE_ASLEEP, + DISOBEYS_RANDOM_MOVE, +}; + +enum CancelerResult +{ + CANCELER_RESULT_SUCCESS, + CANCELER_RESULT_BREAK, // Runs script. Increments state + CANCELER_RESULT_PAUSE, // Runs script. Does not increment state + CANCELER_RESULT_FAILURE, // Move failed, jump to script that handles the failure +}; + +enum CancelerState +{ + CANCELER_STANCE_CHANGE_1, + CANCELER_CLEAR_FLAGS, + CANCELER_SKY_DROP, + CANCELER_RECHARGE, + CANCELER_CHILLY_RECEPTION, + CANCELER_ASLEEP_OR_FROZEN, + CANCELER_POWER_POINTS, + CANCELER_OBEDIENCE, + CANCELER_TRUANT, + CANCELER_FOCUS_GEN5, + CANCELER_FLINCH, + CANCELER_DISABLED, + CANCELER_VOLATILE_BLOCKED, // Gravity / Heal Block / Throat Chop + CANCELER_TAUNTED, + CANCELER_IMPRISONED, + CANCELER_CONFUSED, + CANCELER_PARALYZED, + CANCELER_INFATUATION, + CANCELER_BIDE, + CANCELER_Z_MOVES, + CANCELER_CHOICE_LOCK, + CANCELER_CALLSUBMOVE, + CANCELER_THAW, + CANCELER_STANCE_CHANGE_2, + CANCELER_ATTACKSTRING, + CANCELER_PPDEDUCTION, + CANCELER_SKY_BATTLE, + CANCELER_WEATHER_PRIMAL, + CANCELER_FOCUS_PRE_GEN5, + CANCELER_MOVE_FAILURE, + CANCELER_POWDER_STATUS, + CANCELER_PRIORITY_BLOCK, + CANCELER_PROTEAN, + CANCELER_EXPLODING_DAMP, + CANCELER_EXPLOSION, + CANCELER_CHARGING, + CANCELER_NO_TARGET, + CANCELER_TOOK_ATTACK, + CANCELER_TARGET_FAILURE, + CANCELER_NOT_FULLY_PROTECTED, + CANCELER_MULTIHIT_MOVES, + CANCELER_END, +}; + enum MoveEndResult { - MOVEEND_STEP_CONTINUE, - MOVEEND_STEP_RUN_SCRIPT, - MOVEEND_STEP_BREAK, + MOVEEND_RESULT_CONTINUE, + MOVEEND_RESULT_RUN_SCRIPT, + MOVEEND_RESULT_BREAK, }; // cases for Cmd_moveend - Order matters! diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 9c7a46989f..b6b2a83927 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle.h" +#include "battle_environment.h" #include "battle_hold_effects.h" #include "battle_ai_util.h" #include "battle_util.h" @@ -16,27 +17,1609 @@ static enum Move GetOriginallyUsedMove(enum Move chosenMove); static void SetSameMoveTurnValues(u32 moveEffect); static void TryClearChargeVolatile(enum Type moveType); static inline bool32 IsBattlerUsingBeakBlast(u32 battler); +static void RequestNonVolatileChangee(u32 battlerAtk); +static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx); +static bool32 TryMagicBounce(struct BattleContext *ctx); +static bool32 TryMagicCoat(struct BattleContext *ctx); +static bool32 TryActivatePowderStatus(enum Move move); +// Submoves +static enum Move GetMirrorMoveMove(void); +static enum Move GetMetronomeMove(void); +static enum Move GetAssistMove(void); +static enum Move GetSleepTalkMove(void); +static enum Move GetCopycatMove(void); +static enum Move GetMeFirstMove(void); + +// ============== // Attackcanceler +// ============== -// ---- -// TODO -// ---- +static enum CancelerResult CancelerClearFlags(struct BattleContext *ctx) +{ + gBattleMons[ctx->battlerAtk].volatiles.grudge = FALSE; + gBattleMons[ctx->battlerAtk].volatiles.glaiveRush = FALSE; + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} +static bool32 TryFormChangeBeforeMove(void) +{ + bool32 result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE); + if (!result) + result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY); + if (!result) + return FALSE; + + gBattleScripting.battler = gBattlerAttacker; + BattleScriptCall(BattleScript_BattlerFormChange); + return TRUE; +} + +static enum CancelerResult CancelerStanceChangeOne(struct BattleContext *ctx) +{ + if (B_STANCE_CHANGE_FAIL < GEN_7 && ctx->chosenMove == ctx->move && TryFormChangeBeforeMove()) + return CANCELER_RESULT_BREAK; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerSkyDrop(struct BattleContext *ctx) +{ + // If Pokemon is being held in Sky Drop + if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) + { + gBattlescriptCurrInstr = BattleScript_MoveEnd; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerRecharge(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerChillyReception(struct BattleContext *ctx) +{ + if (GetMoveEffect(ctx->move) == EFFECT_WEATHER_AND_SWITCH) + { + BattleScriptCall(BattleScript_ChillyReceptionMessage); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerAsleepOrFrozen(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_BREAK; + + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + { + if (UproarWakeUpCheck(ctx->battlerAtk)) + { + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; + gEffectBattler = ctx->battlerAtk; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + } + else + { + u32 toSub; + if (IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_EARLY_BIRD)) + toSub = 2; + else + toSub = 1; + + if ((gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) < toSub) + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; + else + gBattleMons[ctx->battlerAtk].status1 -= toSub; + + enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->move); + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + { + if (!IsUsableWhileAsleepEffect(moveEffect)) + { + result = CANCELER_RESULT_FAILURE; + gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; + } + else + { + result = CANCELER_RESULT_BREAK; + } + } + else + { + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + } + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE && !MoveThawsUser(ctx->move)) + { + if (!RandomPercentage(RNG_FROZEN, 20)) + { + result = CANCELER_RESULT_FAILURE; + gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; + } + else // unfreeze + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfroze); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + + return result; +} + +static enum CancelerResult CancelerObedience(struct BattleContext *ctx) +{ + if (!gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) + { + enum Obedience obedienceResult = GetAttackerObedienceForAction(); + switch (obedienceResult) + { + case OBEYS: + return CANCELER_RESULT_SUCCESS; + case DISOBEYS_LOAFS: + // Randomly select, then print a disobedient string + // 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[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + case DISOBEYS_HITS_SELF: + gBattlerTarget = ctx->battlerAtk; + struct BattleContext dmgCtx = {0}; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.isSelfInflicted = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); + gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; + return CANCELER_RESULT_FAILURE; // Move doesn't fail but mon hits itself + case DISOBEYS_FALL_ASLEEP: + if (IsSleepClauseEnabled()) + gBattleStruct->battlerState[ctx->battlerAtk].sleepClauseEffectExempt = TRUE; + gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + break; + case DISOBEYS_WHILE_ASLEEP: + gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + case DISOBEYS_RANDOM_MOVE: + gCurrentMove = gCalledMove = gBattleMons[ctx->battlerAtk].moves[gCurrMovePos]; + BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove); + gBattlerTarget = GetBattleMoveTarget(gCalledMove, TARGET_NONE); + return CANCELER_RESULT_BREAK; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerPowerPoints(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].pp[gCurrMovePos] == 0 + && ctx->move != MOVE_STRUGGLE + && !gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + && !gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) + { + gBattlescriptCurrInstr = BattleScript_NoPPForMove; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTruant(struct BattleContext *ctx) +{ + if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; + gBattlerAbility = ctx->battlerAtk; + gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocus(struct BattleContext *ctx) +{ + u32 focusPunchFailureConfig = GetConfig(CONFIG_FOCUS_PUNCH_FAILURE); + + // In Gens 3-4, only check if is using Focus Punch. + // In Gens 5-6, only check if the chosen move is Focus Punch. + // In Gens 7+, check if chose and is using Focus Punch. + if ((gProtectStructs[ctx->battlerAtk].physicalDmg || gProtectStructs[ctx->battlerAtk].specialDmg) + && (focusPunchFailureConfig < GEN_5 || GetMoveEffect(gChosenMoveByBattler[ctx->battlerAtk]) == EFFECT_FOCUS_PUNCH) + && (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH) + && !gProtectStructs[ctx->battlerAtk].survivedOHKO) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocusPreGen5(struct BattleContext *ctx) +{ + if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) < GEN_5) + return CancelerFocus(ctx); + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocusGen5(struct BattleContext *ctx) +{ + if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) >= GEN_5) + return CancelerFocus(ctx); + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFlinch(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.flinched) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerDisabled(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gBattleMons[ctx->battlerAtk].volatiles.disabledMove == ctx->move + && gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerVolatileBlocked(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gBattleMons[ctx->battlerAtk].volatiles.healBlock + && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move)) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; + return CANCELER_RESULT_FAILURE; + } + else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move)) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; + return CANCELER_RESULT_FAILURE; + } + else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTaunted(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.tauntTimer && IsBattleMoveStatus(ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerImprisoned(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerConfused(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].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, (GetConfig(CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) + { + gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; + gBattlerTarget = gBattlerAttacker; + struct BattleContext dmgCtx = {0}; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.isSelfInflicted = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; + return CANCELER_RESULT_FAILURE; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; + BattleScriptCall(BattleScript_MoveUsedIsConfused); + return CANCELER_RESULT_BREAK; + } + } + else // snapped out of confusion + { + BattleScriptCall(BattleScript_MoveUsedIsConfusedNoMore); + return CANCELER_RESULT_BREAK; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerParalyzed(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS + && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) + && !RandomPercentage(RNG_PARALYSIS, 75)) + { + CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerInfatuation(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.infatuation) + { + gBattleScripting.battler = gBattleMons[ctx->battlerAtk].volatiles.infatuation - 1; + if (!RandomPercentage(RNG_INFATUATION, 50)) + { + BattleScriptCall(BattleScript_MoveUsedIsInLove); + return CANCELER_RESULT_BREAK; + } + else + { + BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; + return CANCELER_RESULT_FAILURE; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerBide(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns) + { + if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns) + { + gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; + } + else + { + // This is removed in FRLG and Emerald for some reason + //gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE; + if (gBideDmg[ctx->battlerAtk]) + { + gCurrentMove = MOVE_BIDE; + gBattlerTarget = gBideTarget[ctx->battlerAtk]; + if (!IsBattlerAlive(ctx->battlerDef)) + gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED); + gBattlescriptCurrInstr = BattleScript_BideAttack; + return CANCELER_RESULT_BREAK; // Jumps to a different script but no failure + } + else + { + gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; + return CANCELER_RESULT_FAILURE; + } + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerZMoves(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE) + { + // attacker has a queued z move + RecordItemEffectBattle(ctx->battlerAtk, HOLD_EFFECT_Z_CRYSTAL); + SetGimmickAsActivated(ctx->battlerAtk, GIMMICK_Z_MOVE); + + gBattleScripting.battler = ctx->battlerAtk; + if (GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS) + BattleScriptCall(BattleScript_ZMoveActivateStatus); + else + BattleScriptCall(BattleScript_ZMoveActivateDamaging); + + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerChoiceLock(struct BattleContext *ctx) +{ + u16 *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; + enum HoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); + + if (gChosenMove != MOVE_STRUGGLE + && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) + && (IsHoldEffectChoice(holdEffect) || ctx->abilityAtk == ABILITY_GORILLA_TACTICS)) + *choicedMoveAtk = gChosenMove; + + u32 moveIndex; + for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + { + if (gBattleMons[ctx->battlerAtk].moves[moveIndex] == *choicedMoveAtk) + break; + } + + if (moveIndex == MAX_MON_MOVES) + *choicedMoveAtk = MOVE_NONE; + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerCallSubmove(struct BattleContext *ctx) +{ + bool32 noEffect = FALSE; + enum Move calledMove = MOVE_NONE; + const u8 *battleScript = NULL; + battleScript = BattleScript_SubmoveAttackstring; + + switch(GetMoveEffect(ctx->move)) + { + case EFFECT_MIRROR_MOVE: + calledMove = GetMirrorMoveMove(); + break; + case EFFECT_METRONOME: + calledMove = GetMetronomeMove(); + battleScript = BattleScript_MetronomeAttackstring; + break; + case EFFECT_ASSIST: + calledMove = GetAssistMove(); + break; + case EFFECT_NATURE_POWER: + calledMove = GetNaturePowerMove(ctx->battlerAtk); + battleScript = BattleScript_NaturePowerAttackstring; + break; + case EFFECT_SLEEP_TALK: + calledMove = GetSleepTalkMove(); + battleScript = BattleScript_SleepTalkAttackstring; + break; + case EFFECT_COPYCAT: + calledMove = GetCopycatMove(); + break; + case EFFECT_ME_FIRST: + calledMove = GetMeFirstMove(); + break; + default: + noEffect = TRUE; + break; + } + + if (noEffect) + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + return CANCELER_RESULT_SUCCESS; + } + + if (calledMove != MOVE_NONE) + { + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) + calledMove = GetTypeBasedZMove(calledMove); + if (GetMoveEffect(ctx->move) == EFFECT_COPYCAT && IsMaxMove(calledMove)) + calledMove = gBattleStruct->dynamax.lastUsedBaseMove; + + gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS; + gCalledMove = calledMove; + BattleScriptCall(battleScript); + return CANCELER_RESULT_BREAK; + } + + gBattleStruct->submoveAnnouncement = SUBMOVE_FAILURE; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerThaw(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_BREAK; + + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE) + { + if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfroze); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; + } + else + { + result = CANCELER_RESULT_FAILURE; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE && MoveThawsUser(ctx->move)) + { + if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FROSTBITE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfrostbite); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FROSTBITE_HEALED_BY_MOVE; + } + else + { + result = CANCELER_RESULT_FAILURE; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + return result; +} + +static enum CancelerResult CancelerStanceChangeTwo(struct BattleContext *ctx) +{ + if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == ctx->move && TryFormChangeBeforeMove()) + return CANCELER_RESULT_BREAK; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerAttackstring(struct BattleContext *ctx) +{ + BattleScriptCall(BattleScript_Attackstring); + if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) + gLastPrintedMoves[gBattlerAttacker] = gChosenMove; + return CANCELER_RESULT_BREAK; +} + +static enum CancelerResult CancelerPPDeduction(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns + || gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + || ctx->move == MOVE_STRUGGLE) + return CANCELER_RESULT_SUCCESS; + + s32 ppToDeduct = 1; + u32 moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + u32 movePosition = gCurrMovePos; + + if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS) + movePosition = gChosenMovePos; + + if (IsSpreadMove(moveTarget) + || moveTarget == TARGET_ALL_BATTLERS + || moveTarget == TARGET_FIELD + || MoveForcesPressure(ctx->move)) + { + for (u32 i = 0; i < gBattlersCount; i++) + { + if (!IsBattlerAlly(i, ctx->battlerAtk) && IsBattlerAlive(i)) + ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE); + } + } + else if (moveTarget != TARGET_OPPONENTS_FIELD) + { + if (ctx->battlerAtk != ctx->battlerDef && GetBattlerAbility(ctx->battlerDef) == ABILITY_PRESSURE) + ppToDeduct++; + } + + // For item Metronome, echoed voice + if (ctx->move != gLastResultingMoves[ctx->battlerAtk] || gBattleStruct->unableToUseMove) + gBattleMons[ctx->battlerAtk].volatiles.metronomeItemCounter = 0; + + if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct) + gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct; + else + gBattleMons[ctx->battlerAtk].pp[movePosition] = 0; + + if (MOVE_IS_PERMANENT(ctx->battlerAtk, movePosition)) + { + 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) + { + if (gBattleStruct->submoveAnnouncement == SUBMOVE_FAILURE) + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + else if (CancelerVolatileBlocked(ctx) == CANCELER_RESULT_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + return CANCELER_RESULT_FAILURE; + } + else + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + gBattlerTarget = GetBattleMoveTarget(ctx->move, TARGET_NONE); + gBattleScripting.animTurn = 0; + gBattleScripting.animTargetsHit = 0; + + // Possibly better to just move type setting and redirection to attackcanceler as a new case at this point + SetTypeBeforeUsingMove(ctx->move, ctx->battlerAtk); + DetermineTarget(moveTarget, FALSE); + ClearDamageCalcResults(); + gBattlescriptCurrInstr = GetMoveBattleScript(ctx->move); + return CANCELER_RESULT_BREAK; + } + } + + return CANCELER_RESULT_SUCCESS; +} + +// We don't have clear data on where this belongs to but I assume it should at least be checked before Protean +static enum CancelerResult CancelerSkyBattle(struct BattleContext *ctx) +{ + if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerWeatherPrimal(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + if (GetMovePower(ctx->move) > 0 && HasWeatherEffect()) + { + enum Type moveType = GetBattleMoveType(ctx->move); + if (moveType == TYPE_FIRE && gBattleWeather & B_WEATHER_RAIN_PRIMAL && (GetConfig(CONFIG_POWDER_STATUS_HEAVY_RAIN) >= GEN_7 || !TryActivatePowderStatus(ctx->move))) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; + result = CANCELER_RESULT_FAILURE; + } + else if (moveType == TYPE_WATER && gBattleWeather & B_WEATHER_SUN_PRIMAL) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; + result = CANCELER_RESULT_FAILURE; + } + if (result == CANCELER_RESULT_FAILURE) + { + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + } + } + + return result; +} + +static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx) +{ + const u8 *battleScript = NULL; + + switch (GetMoveEffect(ctx->move)) + { + case EFFECT_FAIL_IF_NOT_ARG_TYPE: + if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move))) + 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_ICY_ANY && 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_REFLECT_DAMAGE: + { + enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(ctx->battlerAtk, ctx->move); + if (IsBattlerAlly(ctx->battlerAtk, ctx->battlerDef)) + battleScript = BattleScript_ButItFailed; + // Counter / Metal Burst and took physical damage + else if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL + && gProtectStructs[ctx->battlerAtk].physicalDmg > 0 + && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].physicalBattlerId].hp)) + break; + // Mirror Coat / Metal Burst and took special damage + else if (reflectCategory == DAMAGE_CATEGORY_SPECIAL + && gProtectStructs[ctx->battlerAtk].specialDmg > 0 + && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].specialBattlerId].hp)) + break; + else + battleScript = BattleScript_ButItFailed; + break; + } + case EFFECT_DESTINY_BOND: + if (DoesDestinyBondFail(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FIRST_TURN_ONLY: + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_MAT_BLOCK: + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FLING: + if (!CanFling(ctx->battlerAtk, ctx->battlerDef)) + battleScript = BattleScript_ButItFailed; + else if (!IsBattlerAlive(ctx->battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling. + battleScript = BattleScript_FlingFailConsumeItem; + 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 (gBattleStruct->futureSight[ctx->battlerDef].counter > 0) + 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->abilityDef == ABILITY_KLUTZ) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_PROTECT: + case EFFECT_ENDURE: + TryResetConsecutiveUseCounter(gBattlerAttacker); + if (IsLastMonToMove(ctx->battlerAtk)) + { + battleScript = BattleScript_ButItFailed; + } + else + { + u32 protectMethod = GetMoveProtectMethod(ctx->move); + bool32 canUseProtectSecondTime = CanUseMoveConsecutively(ctx->battlerAtk); + bool32 canUseWideGuard = (GetConfig(CONFIG_WIDE_GUARD) >= GEN_6 && protectMethod == PROTECT_WIDE_GUARD); + bool32 canUseQuickGuard = (GetConfig(CONFIG_QUICK_GUARD) >= GEN_6 && protectMethod == PROTECT_QUICK_GUARD); + + if (!canUseProtectSecondTime && !canUseWideGuard && !canUseQuickGuard) + battleScript = BattleScript_ButItFailed; + } + if (battleScript != NULL) + { + gBattleMons[gBattlerAttacker].volatiles.consecutiveMoveUses = 0; + gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; + } + break; + case EFFECT_REST: + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP + || ctx->abilityAtk == ABILITY_COMATOSE) + battleScript = BattleScript_RestIsAlreadyAsleep; + else if (gBattleMons[ctx->battlerAtk].hp == gBattleMons[ctx->battlerAtk].maxHP) + battleScript = BattleScript_AlreadyAtFullHp; + else if (ctx->abilityAtk == ABILITY_INSOMNIA + || ctx->abilityAtk == ABILITY_VITAL_SPIRIT + || ctx->abilityAtk == ABILITY_PURIFYING_SALT) + battleScript = BattleScript_InsomniaProtects; + break; + case EFFECT_SUCKER_PUNCH: + if (HasBattlerActedThisTurn(ctx->battlerDef) + || (IsBattleMoveStatus(GetChosenMoveFromPosition(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_UPPER_HAND: + { + u32 prio = GetChosenMovePriority(ctx->battlerDef, GetBattlerAbility(ctx->battlerDef)); + if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status + || HasBattlerActedThisTurn(ctx->battlerDef) + || gChosenMoveByBattler[ctx->battlerDef] == MOVE_NONE + || IsBattleMoveStatus(gChosenMoveByBattler[ctx->battlerDef])) + battleScript = BattleScript_ButItFailed; + break; + } + case EFFECT_SNORE: + if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + && ctx->abilityAtk != ABILITY_COMATOSE) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STEEL_ROLLER: + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STOCKPILE: + if (gBattleMons[ctx->battlerAtk].volatiles.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 (gBattleMons[ctx->battlerAtk].volatiles.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) + { + gBattlescriptCurrInstr = battleScript; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerPowderStatus(struct BattleContext *ctx) +{ + if (TryActivatePowderStatus(ctx->move)) + { + if (!IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) + SetPassiveDamageAmount(ctx->battlerAtk, GetNonDynamaxMaxHP(ctx->battlerAtk) / 4); + + // This might be incorrect + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + || HasTrainerUsedGimmick(ctx->battlerAtk, GIMMICK_Z_MOVE)) + gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +bool32 IsDazzlingAbility(enum Ability ability) +{ + 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 CancelerResult CancelerPriorityBlock(struct BattleContext *ctx) +{ + bool32 effect = FALSE; + s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); + + if (priority <= 0) + return CANCELER_RESULT_SUCCESS; + + u32 battler; + u32 ability = ABILITY_NONE; // ability of battler who is blocking + bool32 isSpreadMove = IsSpreadMove(GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move)); + for (battler = 0; battler < gBattlersCount; battler++) + { + if (!IsBattlerAlive(battler) || IsBattlerAlly(ctx->battlerAtk, battler)) + continue; + if (!isSpreadMove && !IsBattlerAlly(battler, ctx->battlerDef)) + continue; + + ability = GetBattlerAbility(battler); + if (IsDazzlingAbility(ability)) + { + effect = TRUE; + break; + } + } + + if (effect) + { + gLastUsedAbility = ability; + RecordAbilityBattle(battler, ability); + gBattleScripting.battler = gBattlerAbility = battler; + gBattlescriptCurrInstr = BattleScript_DazzlingProtected; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerProtean(struct BattleContext *ctx) +{ + enum Type moveType = GetBattleMoveType(ctx->move); + if (ProteanTryChangeType(ctx->battlerAtk, ctx->abilityAtk, ctx->move, moveType)) + { + if (GetConfig(CONFIG_PROTEAN_LIBERO) >= GEN_9) + gBattleMons[ctx->battlerAtk].volatiles.usedProteanLibero = TRUE; + PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); + gBattlerAbility = ctx->battlerAtk; + PrepareStringBattle(STRINGID_EMPTYSTRING3, ctx->battlerAtk); + gBattleCommunication[MSG_DISPLAY] = 1; + BattleScriptCall(BattleScript_ProteanActivates); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerExplodingDamp(struct BattleContext *ctx) +{ + u32 dampBattler = IsAbilityOnField(ABILITY_DAMP); + if (dampBattler && IsMoveDampBanned(ctx->move)) + { + gBattleScripting.battler = dampBattler - 1; + gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerExplosion(struct BattleContext *ctx) +{ + // KO user of Explosion; for Final Gambit doesn't happen if target is immune or if it missed + if (IsExplosionMove(ctx->move) + && (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || !IsBattlerUnaffectedByMove(ctx->battlerDef))) + { + BattleScriptCall(BattleScript_Explosion); + return CANCELER_RESULT_BREAK; + } + + return CANCELER_RESULT_SUCCESS; +} + +static bool32 CanTwoTurnMoveFireThisTurn(struct BattleContext *ctx) +{ + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect + || GetMoveEffect(ctx->move) == EFFECT_GEOMANCY + || !IsBattlerWeatherAffected(ctx->battlerAtk, GetMoveTwoTurnAttackWeather(ctx->move))) + return FALSE; + return TRUE; +} + +static enum CancelerResult CancelerCharging(struct BattleContext *ctx) +{ + if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect + || GetMoveEffect(ctx->move) == EFFECT_SKY_DROP) + return CANCELER_RESULT_SUCCESS; + + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) // Second turn + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE; + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) + gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE; + result = CANCELER_RESULT_SUCCESS; + } + else if (!gProtectStructs[ctx->battlerAtk].chargingTurn) // First turn charge + { + gLockedMoves[ctx->battlerAtk] = ctx->move; + gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE; + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) + gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = GetMoveTwoTurnAttackStatus(ctx->move); + BattleScriptCall(BattleScript_TwoTurnMoveCharging); + result = CANCELER_RESULT_PAUSE; + } + else // Try move this turn. Otherwise use next turn + { + if (CanTwoTurnMoveFireThisTurn(ctx)) + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + result = CANCELER_RESULT_SUCCESS; + } + else if (ctx->holdEffectAtk == HOLD_EFFECT_POWER_HERB) + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + gLastUsedItem = gBattleMons[ctx->battlerAtk].item; + BattleScriptCall(BattleScript_PowerHerbActivation); + result = CANCELER_RESULT_BREAK; + } + else // Use move next turn + { + gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE; + gBattlescriptCurrInstr = BattleScript_MoveEnd; + result = CANCELER_RESULT_BREAK; + } + + } + + return result; +} + +static bool32 NoTargetPresent(u32 battler, u32 move, u32 moveTarget) +{ + switch (moveTarget) + { + case TARGET_USER_AND_ALLY: + return FALSE; // At least user is present + case TARGET_ALLY: + if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) // Seems like TARGET_ALLY is retargeting if no ally + return TRUE; + break; + case TARGET_SELECTED: + case TARGET_DEPENDS: + case TARGET_RANDOM: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(GetBattleMoveTarget(move, TARGET_NONE))) + return TRUE; + break; + case TARGET_BOTH: + case TARGET_SMART: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))) + return TRUE; + break; + case TARGET_FOES_AND_ALLY: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) + return TRUE; + break; + } + + return FALSE; +} + +static enum CancelerResult CancelerNoTarget(struct BattleContext *ctx) +{ + enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + + if (NoTargetPresent(gBattlerAttacker, gCurrentMove, moveTarget)) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + + if (ctx->battlerAtk == ctx->battlerDef + && moveTarget == TARGET_ALLY + && gProtectStructs[BATTLE_PARTNER(ctx->battlerAtk)].usedAllySwitch) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTookAttack(struct BattleContext *ctx) +{ + if (gSpecialStatuses[gBattlerTarget].abilityRedirected) + { + gSpecialStatuses[gBattlerTarget].abilityRedirected = FALSE; + BattleScriptCall(BattleScript_TookAttack); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +#define checkFailure FALSE +#define skipFailure TRUE +static bool32 IsSingleTarget(u32 battlerAtk, u32 battlerDef) +{ + if (battlerDef != gBattlerTarget) + return skipFailure; + return checkFailure; +} + +static bool32 IsSmartTarget(u32 battlerAtk, u32 battlerDef) +{ + if (battlerAtk == BATTLE_PARTNER(battlerDef)) + return skipFailure; + return checkFailure; +} + +static bool32 IsTargetingBothFoes(u32 battlerAtk, u32 battlerDef) +{ + if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef) + { + // Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT + if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS) + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; +} + +static bool32 IsTargetingSelf(u32 battlerAtk, u32 battlerDef) +{ + if (battlerAtk != battlerDef) + return skipFailure; + return skipFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists +} + +static bool32 IsTargetingAlly(u32 battlerAtk, u32 battlerDef) +{ + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; +} + +static bool32 IsTargetingSelfAndAlly(u32 battlerAtk, u32 battlerDef) +{ + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + if (battlerDef != battlerAtk) // Don't set result flags for user + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists +} + +static bool32 IsTargetingSelfOrAlly(u32 battlerAtk, u32 battlerDef) +{ + if (battlerDef == battlerAtk) + return skipFailure; + + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + + return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such move exists +} + +static bool32 IsTargetingFoesAndAlly(u32 battlerAtk, u32 battlerDef) +{ + if (battlerAtk == battlerDef) + return skipFailure; // Don't set result flags for user + return checkFailure; +} + +static bool32 IsTargetingField(u32 battlerAtk, u32 battlerDef) +{ + return skipFailure; +} + +static bool32 IsTargetingOpponentsField(u32 battlerAtk, u32 battlerDef) +{ + return checkFailure; // Bounce failure only +} + +static bool32 IsTargetingAllBattlers(u32 battlerAtk, u32 battlerDef) +{ + if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef) + return skipFailure; + return checkFailure; +} + +// ShouldCheckFailureOnTarget +static bool32 (*const sShouldCheckTargetMoveFailure[])(u32 battlerAtk, u32 battlerDef) = +{ + [TARGET_NONE] = IsTargetingField, + [TARGET_SELECTED] = IsSingleTarget, + [TARGET_DEPENDS] = IsSingleTarget, + [TARGET_OPPONENT] = IsSingleTarget, + [TARGET_RANDOM] = IsSingleTarget, + [TARGET_BOTH] = IsTargetingBothFoes, + [TARGET_USER] = IsTargetingSelf, + [TARGET_SMART] = IsSmartTarget, + [TARGET_ALLY] = IsTargetingAlly, + [TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly, + [TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly, + [TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly, + [TARGET_FIELD] = IsTargetingField, + [TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField, + [TARGET_ALL_BATTLERS] = IsTargetingAllBattlers, +}; + +static bool32 ShouldCheckTargetMoveFailure(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveTarget) +{ + // For Bounced moves + if (IsBattlerUnaffectedByMove(battlerDef)) + return skipFailure; + + return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef); +} +#undef checkFailure +#undef skipFailure + + +static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx) +{ + bool32 targetAvoidedAttack = FALSE; + enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + s32 movePriority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); + ctx->moveType = GetBattleMoveType(ctx->move); + ctx->updateFlags = TRUE; + ctx->runScript = TRUE; + + while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) + { + ctx->battlerDef = gBattleStruct->eventState.atkCancelerBattler++; + + if (ShouldCheckTargetMoveFailure(ctx->battlerAtk, ctx->battlerDef, ctx->move, moveTarget)) + continue; + + ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); + ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef); + + if (moveTarget == TARGET_OPPONENTS_FIELD) + { + if (CanBattlerBounceBackMove(ctx)) + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + continue; + } + + if (!IsBattlerAlive(ctx->battlerDef)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + continue; + } + else if (!BreaksThroughSemiInvulnerablity(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->abilityDef, ctx->move)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_AVOIDED_ATK; + if (GetMoveEffect(ctx->move) == EFFECT_FLING) + BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); + else + BattleScriptCall(BattleScript_TargetAvoidsAttack); + targetAvoidedAttack = TRUE; + } + else if (IsBattlerProtected(ctx)) + { + SetOrClearRageVolatile(); + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED; + if (GetMoveEffect(ctx->move) == EFFECT_FLING) + BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); + else + BattleScriptCall(BattleScript_TargetAvoidsAttack); + targetAvoidedAttack = TRUE; + } + else if (CanBattlerBounceBackMove(ctx)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + } + else if (CanMoveBeBlockedByTarget(ctx, movePriority)) + { + + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + targetAvoidedAttack = TRUE; + } + else if (GetMoveEffect(ctx->move) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(ctx->battlerAtk, ctx->battlerDef)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_NO_EFFECT; + BattleScriptCall(BattleScript_ItDoesntAffectFoe); + targetAvoidedAttack = TRUE; + } + else + { + CalcTypeEffectivenessMultiplier(ctx); + + if (ctx->abilityBlocked) + { + ctx->abilityBlocked = FALSE; + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; + gBattlerAbility = ctx->battlerDef; + RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef); + BattleScriptCall(BattleScript_AbilityPopUp); + targetAvoidedAttack = TRUE; + } + else if (ctx->airBalloonBlocked) + { + ctx->airBalloonBlocked = FALSE; + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; + BattleScriptCall(BattleScript_DoesntAffectScripting); + targetAvoidedAttack = TRUE; + } + } + + if (targetAvoidedAttack) + { + gLastLandedMoves[gBattlerTarget] = 0; // Might need investigation on what exactly clears is + gLastHitByType[gBattlerTarget] = 0; + gBattleScripting.battler = ctx->battlerDef; + gBattleStruct->pledgeMove = FALSE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + return CANCELER_RESULT_PAUSE; + } + } + + if (IsDoubleBattle()) + { + if (moveTarget == TARGET_BOTH) + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, gBattlerAttacker); + else if (moveTarget == TARGET_FOES_AND_ALLY) + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker); + } + + ctx->battlerDef = gBattlerTarget; + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} + +static bool32 CantFullyProtectFromMove(u32 battlerDef) +{ + if (MoveIgnoresProtect(gCurrentMove)) + return FALSE; + if (!IsZMove(gCurrentMove) && !IsMaxMove(gCurrentMove)) + return FALSE; + return GetProtectType(gProtectStructs[battlerDef].protected) == PROTECT_TYPE_SINGLE + && gProtectStructs[battlerDef].protected != PROTECT_MAX_GUARD; +} + +static enum CancelerResult CancelerNotFullyProtected(struct BattleContext *ctx) +{ + while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) + { + u32 battlerDef = gBattleStruct->eventState.atkCancelerBattler++; + + if (CantFullyProtectFromMove(battlerDef)) + { + BattleScriptCall(BattleScript_CouldntFullyProtect); + gBattleScripting.battler = battlerDef; + return CANCELER_RESULT_PAUSE; + } + } + + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} + +static bool32 IsMoveParentalBondAffected(struct BattleContext *ctx) +{ + if (ctx->abilityAtk != ABILITY_PARENTAL_BOND + || gBattleStruct->numSpreadTargets > 1 + || IsMoveParentalBondBanned(ctx->move) + || GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS + || GetMoveEffect(ctx->move) == EFFECT_SEMI_INVULNERABLE + || GetMoveEffect(ctx->move) == EFFECT_TWO_TURNS_ATTACK + || GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE + || ctx->move == MOVE_STRUGGLE) + return FALSE; + return TRUE; +} + +static void SetPossibleNewSmartTarget(u32 move) +{ + if (!IsBattlerUnaffectedByMove(gBattlerTarget) + || !CanTargetPartner(gBattlerAttacker, gBattlerTarget) + || IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), move) + || GetBattlerMoveTargetType(gBattlerAttacker, move) != TARGET_SMART) + return; + + u32 partner = BATTLE_PARTNER(gBattlerTarget); + if (!IsBattlerUnaffectedByMove(partner)) + gBattlerTarget = partner; +} + +static void SetRandomMultiHitCounter() +{ + if (GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_LOADED_DICE) + gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5); + else if (GetConfig(CONFIG_MULTI_HIT_CHANCE) >= GEN_5) + gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3); // 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits. + else + gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1); // 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits. +} + +static enum CancelerResult CancelerMultihitMoves(struct BattleContext *ctx) +{ + SetPossibleNewSmartTarget(ctx->move); + + if (IsBattlerUnaffectedByMove(gBattlerTarget)) // Dragon Darts can still hit partner + { + gMultiHitCounter = 0; + } + else if (IsMultiHitMove(ctx->move)) + { + enum Ability ability = ctx->abilityAtk; + + if (ability == ABILITY_SKILL_LINK) + { + gMultiHitCounter = 5; + } + else if (GetMoveEffect(ctx->move) == EFFECT_SPECIES_POWER_OVERRIDE + && gBattleMons[ctx->battlerAtk].species == GetMoveSpeciesPowerOverride_Species(ctx->move)) + { + gMultiHitCounter = GetMoveSpeciesPowerOverride_NumOfHits(ctx->move); + } + else + { + SetRandomMultiHitCounter(); + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else if (GetMoveStrikeCount(ctx->move) > 1) + { + if (GetMoveEffect(ctx->move) == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(ctx->battlerAtk) == HOLD_EFFECT_LOADED_DICE) + { + gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); + } + else + { + gMultiHitCounter = GetMoveStrikeCount(ctx->move); + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) + } + else if (GetMoveEffect(ctx->move) == EFFECT_BEAT_UP) + { + struct Pokemon* party = GetBattlerParty(ctx->battlerAtk); + int i; + gBattleStruct->beatUpSlot = 0; + gMultiHitCounter = 0; + memset(gBattleStruct->beatUpSpecies, 0xFF, sizeof(gBattleStruct->beatUpSpecies)); + + for (i = 0; i < PARTY_SIZE; i++) + { + u32 species = GetMonData(&party[i], MON_DATA_SPECIES); + if (species != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_HP) + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && !GetMonData(&party[i], MON_DATA_STATUS)) + { + if (GetConfig(CONFIG_BEAT_UP) >= GEN_5) + gBattleStruct->beatUpSpecies[gMultiHitCounter] = species; + else + gBattleStruct->beatUpSpecies[gMultiHitCounter] = i; + gMultiHitCounter++; + } + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else if (IsMoveParentalBondAffected(ctx)) + { + gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT; + gMultiHitCounter = 2; + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else + { + gMultiHitCounter = 0; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult (*const sMoveSuccessOrderCancelers[])(struct BattleContext *ctx) = +{ + [CANCELER_CLEAR_FLAGS] = CancelerClearFlags, + [CANCELER_STANCE_CHANGE_1] = CancelerStanceChangeOne, + [CANCELER_SKY_DROP] = CancelerSkyDrop, + [CANCELER_RECHARGE] = CancelerRecharge, + [CANCELER_CHILLY_RECEPTION] = CancelerChillyReception, + [CANCELER_ASLEEP_OR_FROZEN] = CancelerAsleepOrFrozen, + [CANCELER_OBEDIENCE] = CancelerObedience, + [CANCELER_POWER_POINTS] = CancelerPowerPoints, + [CANCELER_TRUANT] = CancelerTruant, + [CANCELER_FOCUS_GEN5] = CancelerFocusGen5, + [CANCELER_FLINCH] = CancelerFlinch, + [CANCELER_DISABLED] = CancelerDisabled, + [CANCELER_VOLATILE_BLOCKED] = CancelerVolatileBlocked, + [CANCELER_TAUNTED] = CancelerTaunted, + [CANCELER_IMPRISONED] = CancelerImprisoned, + [CANCELER_CONFUSED] = CancelerConfused, + [CANCELER_PARALYZED] = CancelerParalyzed, + [CANCELER_INFATUATION] = CancelerInfatuation, + [CANCELER_BIDE] = CancelerBide, + [CANCELER_Z_MOVES] = CancelerZMoves, + [CANCELER_CHOICE_LOCK] = CancelerChoiceLock, + [CANCELER_CALLSUBMOVE] = CancelerCallSubmove, + [CANCELER_THAW] = CancelerThaw, + [CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo, + [CANCELER_ATTACKSTRING] = CancelerAttackstring, + [CANCELER_PPDEDUCTION] = CancelerPPDeduction, + [CANCELER_SKY_BATTLE] = CancelerSkyBattle, + [CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal, + [CANCELER_FOCUS_PRE_GEN5] = CancelerFocusPreGen5, + [CANCELER_MOVE_FAILURE] = CancelerMoveFailure, + [CANCELER_POWDER_STATUS] = CancelerPowderStatus, + [CANCELER_PRIORITY_BLOCK] = CancelerPriorityBlock, + [CANCELER_PROTEAN] = CancelerProtean, + [CANCELER_EXPLODING_DAMP] = CancelerExplodingDamp, + [CANCELER_EXPLOSION] = CancelerExplosion, + [CANCELER_CHARGING] = CancelerCharging, + [CANCELER_NO_TARGET] = CancelerNoTarget, + [CANCELER_TOOK_ATTACK] = CancelerTookAttack, + [CANCELER_TARGET_FAILURE] = CancelerTargetFailure, + [CANCELER_NOT_FULLY_PROTECTED] = CancelerNotFullyProtected, + [CANCELER_MULTIHIT_MOVES] = CancelerMultihitMoves, +}; + +enum CancelerResult DoAttackCanceler(void) +{ + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + struct BattleContext ctx = {0}; + ctx.battlerAtk = gBattlerAttacker; + ctx.battlerDef = gBattlerTarget; + ctx.move = gCurrentMove; + ctx.chosenMove = gChosenMove; + ctx.abilityAtk = GetBattlerAbility(ctx.battlerAtk); + ctx.holdEffectAtk = GetBattlerHoldEffect(ctx.battlerAtk); + + while (gBattleStruct->eventState.atkCanceler < CANCELER_END && result == CANCELER_RESULT_SUCCESS) + { + result = sMoveSuccessOrderCancelers[gBattleStruct->eventState.atkCanceler](&ctx); + gBattleStruct->unableToUseMove = (result == CANCELER_RESULT_FAILURE); + if (result != CANCELER_RESULT_PAUSE) + gBattleStruct->eventState.atkCanceler++; + } + + return result; +} + +// ======== // Move End +// ======== -static enum MoveEndResult MoveEnd_SetValues(void) +static enum MoveEndResult MoveEndSetValues(void) { gBattleScripting.savedDmg += gBattleStruct->moveDamage[gBattlerTarget]; gBattleStruct->eventState.moveEndBattler = 0; gBattleStruct->eventState.moveEndBlock = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) +static enum MoveEndResult MoveEndProtectLikeEffect(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 temp = 0; if (CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove)) @@ -54,7 +1637,7 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 8); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SPIKY_SHIELD); BattleScriptCall(BattleScript_SpikyShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_KINGS_SHIELD: @@ -64,14 +1647,14 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) else gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_2; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; case PROTECT_BANEFUL_BUNKER: if (CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerTarget), GetBattlerAbility(gBattlerAttacker))) { gBattleScripting.moveEffect = MOVE_EFFECT_POISON; BattleScriptCall(BattleScript_BanefulBunkerEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_BURNING_BULWARK: @@ -79,20 +1662,20 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) { gBattleScripting.moveEffect = MOVE_EFFECT_BURN; BattleScriptCall(BattleScript_BanefulBunkerEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_OBSTRUCT: SWAP(gBattlerAttacker, gBattlerTarget, temp); // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; case PROTECT_SILK_TRAP: SWAP(gBattlerAttacker, gBattlerTarget, temp); // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; default: break; @@ -107,19 +1690,19 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].status1), &gBattleMons[gBattlerAttacker].status1); MarkBattlerForControllerExec(gBattlerAttacker); BattleScriptCall(BattleScript_BeakBlastBurn); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Absorb(void) +static enum MoveEndResult MoveEndAbsorb(void) { if (gBattleStruct->unableToUseMove) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } if (IsExplosionMove(gCurrentMove) @@ -130,10 +1713,10 @@ static enum MoveEndResult MoveEnd_Absorb(void) gBattleStruct->passiveHpUpdate[gBattlerAttacker] = 0; BattleScriptCall(BattleScript_FaintAttackerForExplosion); gBattleScripting.moveendState++; - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); switch (moveEffect) @@ -160,7 +1743,7 @@ static enum MoveEndResult MoveEnd_Absorb(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB_OOZE; BattleScriptCall(BattleScript_EffectAbsorbLiquidOoze); } - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_MAX_HP_50_RECOIL: @@ -176,7 +1759,7 @@ static enum MoveEndResult MoveEnd_Absorb(void) gSpecialStatuses[gBattlerAttacker].mindBlownRecoil = TRUE; TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MaxHp50Recoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; default: @@ -187,9 +1770,9 @@ static enum MoveEndResult MoveEnd_Absorb(void) return result; } -static enum MoveEndResult MoveEnd_Rage(void) +static enum MoveEndResult MoveEndRage(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleMons[gBattlerTarget].volatiles.rage && IsBattlerAlive(gBattlerTarget) @@ -201,92 +1784,92 @@ static enum MoveEndResult MoveEnd_Rage(void) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptCall(BattleScript_RageIsBuilding); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SynchronizeTarget(void) +static enum MoveEndResult MoveEndSynchronizeTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Abilities(void) +static enum MoveEndResult MoveEndAbilities(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum Ability targetAbility = GetBattlerAbility(gBattlerTarget); if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, targetAbility, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; else if (TryClearIllusion(gBattlerTarget, targetAbility)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AbilitiesAttacker(void) +static enum MoveEndResult MoveEndAbilitiesAttacker(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_ATTACKER, gBattlerAttacker, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_StatusImmunityAbilities(void) +static enum MoveEndResult MoveEndStatusImmunityAbilities(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; for (u32 battler = 0; battler < gBattlersCount; battler++) { if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } - if (result == MOVEEND_STEP_CONTINUE) + if (result == MOVEEND_RESULT_CONTINUE) gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SynchronizeAttacker(void) +static enum MoveEndResult MoveEndSynchronizeAttacker(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AttackerInvisible(void) +static enum MoveEndResult MoveEndAttackerInvisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) && gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)) { BtlController_EmitSpriteInvisibility(gBattlerAttacker, B_COMM_TO_CONTROLLER, TRUE); MarkBattlerForControllerExec(gBattlerAttacker); - result = MOVEEND_STEP_BREAK; + result = MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AttackerVisible(void) +static enum MoveEndResult MoveEndAttackerVisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsBattlerUnaffectedByMove(gBattlerTarget) || !IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) @@ -297,15 +1880,15 @@ static enum MoveEndResult MoveEnd_AttackerVisible(void) gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable = STATE_NONE; gSpecialStatuses[gBattlerAttacker].restoredBattlerSprite = TRUE; gBattleScripting.moveendState++; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_TargetVisible(void) +static enum MoveEndResult MoveEndTargetVisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (!gSpecialStatuses[gBattlerTarget].restoredBattlerSprite && gBattlerTarget < gBattlersCount && !IsSemiInvulnerable(gBattlerTarget, CHECK_ALL)) @@ -313,43 +1896,43 @@ static enum MoveEndResult MoveEnd_TargetVisible(void) BtlController_EmitSpriteInvisibility(gBattlerTarget, B_COMM_TO_CONTROLLER, FALSE); MarkBattlerForControllerExec(gBattlerTarget); gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE; - result = MOVEEND_STEP_BREAK; + result = MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_ItemEffectsTarget(void) +static enum MoveEndResult MoveEndItemEffectsTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); if (ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnTargetHitActivation) || ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnStatusChangeActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_ItemEffectsAttacker1(void) +static enum MoveEndResult MoveEndItemEffectsAttacker1(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnAttackerAfterHitActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Symbiosis(void) +static enum MoveEndResult MoveEndSymbiosis(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; for (u32 battler = 0; battler < gBattlersCount; battler++) { @@ -363,7 +1946,7 @@ static enum MoveEndResult MoveEnd_Symbiosis(void) gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(battler); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } } @@ -372,9 +1955,9 @@ static enum MoveEndResult MoveEnd_Symbiosis(void) return result; } -static enum MoveEndResult MoveEnd_Substitute(void) +static enum MoveEndResult MoveEndSubstitute(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; for (u32 i = 0; i < gBattlersCount; i++) { @@ -386,9 +1969,9 @@ static enum MoveEndResult MoveEnd_Substitute(void) return result; } -static enum MoveEndResult MoveEnd_FaintBlock(void) +static enum MoveEndResult MoveEndFaintBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; do { @@ -402,7 +1985,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) && !IsBattlerUnaffectedByMove(gBattlerTarget)) { BattleScriptCall(BattleScript_FinalGambit); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -411,7 +1994,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) || gBattleStruct->battlerState[gBattlerTarget].fainted) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } gBattleStruct->eventState.moveEndBlock++; @@ -423,7 +2006,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) if (!IsNeutralizingGasOnField()) { BattleScriptCall(BattleScript_NeutralizingGasExits); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } gBattleStruct->eventState.moveEndBlock++; @@ -474,7 +2057,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2; } BattleScriptCall(BattleScript_FaintBattler); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleStruct->eventState.moveEndBlock++; break; case FAINT_BLOCK_DO_DESTINY_BOND: @@ -482,7 +2065,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) { gBattleStruct->passiveHpUpdate[gBattlerAttacker] = gBattleMons[gBattlerAttacker].hp; BattleScriptCall(BattleScript_DestinyBondTakesLife); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -496,7 +2079,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) MarkBattlerForControllerExec(gBattlerAttacker); PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) BattleScriptCall(BattleScript_GrudgeTakesPp); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -507,12 +2090,12 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) break; } - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) break; } while (gBattleStruct->eventState.moveEndBlock != 0); - if (result == MOVEEND_STEP_CONTINUE) + if (result == MOVEEND_RESULT_CONTINUE) { gBattleStruct->eventState.moveEndBlock = 0; gBattleScripting.moveendState++; @@ -521,9 +2104,9 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) return result; } -static enum MoveEndResult MoveEnd_SkyDropConfuse(void) +static enum MoveEndResult MoveEndSkyDropConfuse(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; for (u32 battler = 0; battler < gBattlersCount; battler++) { @@ -543,7 +2126,7 @@ static enum MoveEndResult MoveEnd_SkyDropConfuse(void) // Clear skyDropTargets data gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET; gBattleStruct->skyDropTargets[targetId] = SKY_DROP_NO_TARGET; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } } @@ -552,7 +2135,7 @@ static enum MoveEndResult MoveEnd_SkyDropConfuse(void) return result; } -static enum MoveEndResult MoveEnd_UpdateLastMoves(void) +static enum MoveEndResult MoveEndUpdateLastMoves(void) { if (!IsOnPlayerSide(gBattlerAttacker)) UpdateStallMons(); @@ -627,10 +2210,10 @@ static enum MoveEndResult MoveEnd_UpdateLastMoves(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_MirrorMove(void) +static enum MoveEndResult MoveEndMirrorMove(void) { if (!gBattleStruct->unableToUseMove && gBattlerAttacker != gBattlerTarget @@ -644,12 +2227,12 @@ static enum MoveEndResult MoveEnd_MirrorMove(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Defrost(void) +static enum MoveEndResult MoveEndDefrost(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE && IsBattlerTurnDamaged(gBattlerTarget) @@ -662,7 +2245,7 @@ static enum MoveEndResult MoveEnd_Defrost(void) BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gBattlerTarget); BattleScriptCall(BattleScript_DefrostedViaFireMove); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE && IsBattlerTurnDamaged(gBattlerTarget) @@ -675,14 +2258,14 @@ static enum MoveEndResult MoveEnd_Defrost(void) BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gBattlerTarget); BattleScriptCall(BattleScript_FrostbiteHealedViaFireMove); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_NextTarget(void) +static enum MoveEndResult MoveEndNextTarget(void) { enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -702,7 +2285,7 @@ static enum MoveEndResult MoveEnd_NextTarget(void) gBattlescriptCurrInstr = BattleScript_FlushMessageBox; gBattleScripting.moveendState = 0; MoveValuesCleanUp(); - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } } else if (IsSpreadMove(moveTarget)) @@ -723,7 +2306,7 @@ static enum MoveEndResult MoveEnd_NextTarget(void) else BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } // Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon. else if (gBattleStruct->bouncedMoveIsUsed) @@ -745,33 +2328,33 @@ static enum MoveEndResult MoveEnd_NextTarget(void) MoveValuesCleanUp(); BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } } } RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove); gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_HpThresholdItemsTarget(void) +static enum MoveEndResult MoveEndHpThresholdItemsTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gMultiHitCounter && ItemBattleEffects(gBattlerTarget, gBattlerAttacker, GetBattlerHoldEffect(gBattlerTarget), IsOnHpThresholdActivation)) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_MultihitMove(void) +static enum MoveEndResult MoveEndMultihitMove(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (!IsBattlerUnaffectedByMove(gBattlerTarget) && !gBattleStruct->unableToUseMove @@ -789,7 +2372,7 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) BattleScriptCall(BattleScript_ScaleShot); else BattleScriptCall(BattleScript_MultiHitPrintStrings); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else { @@ -816,12 +2399,12 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) MoveValuesCleanUp(); BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } else { BattleScriptCall(BattleScript_MultiHitPrintStrings); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } } @@ -833,9 +2416,9 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) return result; } -static enum MoveEndResult MoveEnd_MoveBlock(void) +static enum MoveEndResult MoveEndMoveBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleSide side = GetBattlerSide(gBattlerTarget); enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); @@ -856,7 +2439,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) { gBattlerAbility = gBattlerTarget; BattleScriptCall(BattleScript_StickyHoldActivatesRet); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } @@ -880,7 +2463,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) } BattleScriptCall(BattleScript_KnockedOff); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_STEAL_ITEM: @@ -890,14 +2473,14 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) || !IsBattlerAlive(gBattlerAttacker) || !CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item)) { - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { BattleScriptCall(BattleScript_NoItemSteal); gLastUsedAbility = gBattleMons[gBattlerTarget].ability; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else { @@ -911,7 +2494,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) } gEffectBattler = gBattlerTarget; BattleScriptCall(BattleScript_ItemSteal); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_HIT_SWITCH_TARGET: @@ -941,7 +2524,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleScripting.switchCase = B_SWITCH_HIT; BattleScriptCall(BattleScript_TryHitSwitchTarget); } - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_SMACK_DOWN: @@ -955,7 +2538,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleMons[gBattlerTarget].volatiles.magnetRise = FALSE; gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE; BattleScriptCall(BattleScript_MoveEffectSmackDown); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RECOIL_IF_MISS: @@ -989,7 +2572,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) } SetPassiveDamageAmount(gBattlerAttacker, recoil); BattleScriptCall(BattleScript_RecoilIfMiss); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RECOIL: @@ -1003,7 +2586,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) SetPassiveDamageAmount(gBattlerAttacker, gBattleScripting.savedDmg * max(1, GetMoveRecoil(gCurrentMove)) / 100); TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_CHLOROBLAST: @@ -1018,14 +2601,14 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) SetPassiveDamageAmount(gBattlerAttacker, recoil); TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RAPID_SPIN: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { BattleScriptCall(BattleScript_RapidSpinAway); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_FELL_STINGER: @@ -1039,7 +2622,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_STONE_AXE: @@ -1050,7 +2633,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_StealthRockActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_CEASELESS_EDGE: @@ -1062,18 +2645,18 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) BattleScriptPush(gBattlescriptCurrInstr + 1); if (gBattleStruct->isSkyBattle) { - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SpikesActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } break; default: - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; break; } @@ -1081,41 +2664,41 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) return result; } -static enum MoveEndResult MoveEnd_ItemEffectsAttacker2(void) +static enum MoveEndResult MoveEndItemEffectsAttacker2(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AbilityEffectFoesFainted(void) +static enum MoveEndResult MoveEndAbilityEffectFoesFainted(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_FOES_FAINTED, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), gCurrentMove, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SheerForce(void) +static enum MoveEndResult MoveEndSheerForce(void) { if (IsSheerForceAffected(gCurrentMove, GetBattlerAbility(gBattlerAttacker))) gBattleScripting.moveendState = MOVEEND_EJECT_PACK; else gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ShellTrap(void) +static enum MoveEndResult MoveEndShellTrap(void) { for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { @@ -1135,10 +2718,10 @@ static enum MoveEndResult MoveEnd_ShellTrap(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ColorChange(void) +static enum MoveEndResult MoveEndColorChange(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1146,15 +2729,15 @@ static enum MoveEndResult MoveEnd_ColorChange(void) if (battler == gBattlerAttacker) continue; if (AbilityBattleEffects(ABILITYEFFECT_COLOR_CHANGE, battler, GetBattlerAbility(battler), 0, TRUE)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_KeeMarangaHpThresholdItemTarget(void) +static enum MoveEndResult MoveEndKeeMarangaHpThresholdItemTarget(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1164,11 +2747,11 @@ static enum MoveEndResult MoveEnd_KeeMarangaHpThresholdItemTarget(void) enum HoldEffect holdEffect = GetBattlerHoldEffect(battlerDef); if (ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsKeeMarangaBerryActivation) || ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsOnHpThresholdActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } static bool32 TryRedCard(u32 battlerAtk, u32 redCardBattler, u32 move) @@ -1209,9 +2792,9 @@ static bool32 TryEjectButton(u32 battlerAtk, u32 ejectButtonBattler) return TRUE; } -static enum MoveEndResult MoveEnd_CardButton(void) +static enum MoveEndResult MoveEndCardButton(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 redCardBattlers = 0; u32 ejectButtonBattlers = 0; @@ -1231,7 +2814,7 @@ static enum MoveEndResult MoveEnd_CardButton(void) if (!redCardBattlers && !ejectButtonBattlers) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } u8 battlers[4] = {0, 1, 2, 3}; @@ -1243,11 +2826,11 @@ static enum MoveEndResult MoveEnd_CardButton(void) // Only fastest red card or eject button activates if (redCardBattlers & 1u << battler && TryRedCard(gBattlerAttacker, battler, gCurrentMove)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; else if (ejectButtonBattlers & 1u << battler && TryEjectButton(gBattlerAttacker, battler)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) { for (u32 i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.tryEjectPack = FALSE; @@ -1260,24 +2843,24 @@ static enum MoveEndResult MoveEnd_CardButton(void) return result; } -static enum MoveEndResult MoveEnd_LifeOrbShellBell(void) +static enum MoveEndResult MoveEndLifeOrbShellBell(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (ItemBattleEffects(gBattlerAttacker, 0, GetBattlerHoldEffect(gBattlerAttacker), IsLifeOrbShellBellActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_FormChange(void) +static enum MoveEndResult MoveEndFormChange(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_AFTER_MOVE)) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; BattleScriptCall(BattleScript_AttackerFormChangeMoveEffect); } @@ -1285,9 +2868,9 @@ static enum MoveEndResult MoveEnd_FormChange(void) return result; } -static enum MoveEndResult MoveEnd_EmergencyExit(void) +static enum MoveEndResult MoveEndEmergencyExit(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 numEmergencyExitBattlers = 0; u32 emergencyExitBattlers = 0; @@ -1324,11 +2907,11 @@ static enum MoveEndResult MoveEnd_EmergencyExit(void) gBattleScripting.battler = battler; BattleScriptCall(BattleScript_EmergencyExit); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Only the fastest Emergency Exit / Wimp Out activates } - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; else gBattleScripting.moveendState++; @@ -1348,9 +2931,9 @@ static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, enum Ba return FALSE; } -static enum MoveEndResult MoveEnd_EjectPack(void) +static enum MoveEndResult MoveEndEjectPack(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 ejectPackBattlers = 0; u32 numEjectPackBattlers = 0; @@ -1389,7 +2972,7 @@ static enum MoveEndResult MoveEnd_EjectPack(void) gBattleStruct->battlerState[battler].usedEjectItem = TRUE; BattleScriptCall(BattleScript_EjectPackActivates); gAiLogicData->ejectPackSwitch = TRUE; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Only the fastest Eject item activates } @@ -1397,9 +2980,9 @@ static enum MoveEndResult MoveEnd_EjectPack(void) return result; } -static enum MoveEndResult MoveEnd_HitEscape(void) +static enum MoveEndResult MoveEndHitEscape(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && !gBattleStruct->unableToUseMove @@ -1407,7 +2990,7 @@ static enum MoveEndResult MoveEnd_HitEscape(void) && IsBattlerAlive(gBattlerAttacker) && !NoAliveMonsForBattlerSide(gBattlerTarget)) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; BattleScriptCall(BattleScript_EffectHitEscape); } @@ -1415,7 +2998,7 @@ static enum MoveEndResult MoveEnd_HitEscape(void) return result; } -static enum MoveEndResult MoveEnd_ItemsEffectsAll(void) +static enum MoveEndResult MoveEndItemsEffectsAll(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1423,15 +3006,15 @@ static enum MoveEndResult MoveEnd_ItemsEffectsAll(void) enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (ItemBattleEffects(battler, 0, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(battler, 0, holdEffect, IsOnHpThresholdActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_WhiteHerb(void) +static enum MoveEndResult MoveEndWhiteHerb(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1440,15 +3023,15 @@ static enum MoveEndResult MoveEnd_WhiteHerb(void) continue; if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsWhiteHerbActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Opportunist(void) +static enum MoveEndResult MoveEndOpportunist(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1456,15 +3039,15 @@ static enum MoveEndResult MoveEnd_Opportunist(void) if (!IsBattlerAlive(battler)) continue; if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, GetBattlerAbility(battler), 0, TRUE)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_MirrorHerb(void) +static enum MoveEndResult MoveEndMirrorHerb(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { @@ -1473,17 +3056,17 @@ static enum MoveEndResult MoveEnd_MirrorHerb(void) continue; if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Pickpocket(void) +static enum MoveEndResult MoveEndPickpocket(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item @@ -1512,7 +3095,7 @@ static enum MoveEndResult MoveEnd_Pickpocket(void) gEffectBattler = gBattlerAttacker; BattleScriptCall(BattleScript_Pickpocket); // Includes sticky hold check to print separate string - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Pickpocket activates on fastest mon, so exit loop. } } @@ -1522,9 +3105,9 @@ static enum MoveEndResult MoveEnd_Pickpocket(void) return result; } -static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) +static enum MoveEndResult MoveEndThirdMoveBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); switch (moveEffect) @@ -1533,7 +3116,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && IsBattlerTurnDamaged(gBattlerTarget)) { BattleScriptCall(BattleScript_RemoveTerrain); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_ICE_SPINNER: @@ -1543,7 +3126,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) && IsBattlerTurnDamaged(gBattlerTarget)) { BattleScriptCall(BattleScript_RemoveTerrain); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_NATURAL_GIFT: @@ -1565,7 +3148,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) ClearBattlerItemEffectHistory(gBattlerAttacker); if (!TrySymbiosis(gBattlerAttacker, item, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; default: @@ -1576,7 +3159,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) return result; } -static enum MoveEndResult MoveEnd_ChangedItems(void) +static enum MoveEndResult MoveEndChangedItems(void) { for (u32 battler = 0; battler < gBattlersCount; battler++) { @@ -1588,10 +3171,10 @@ static enum MoveEndResult MoveEnd_ChangedItems(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ClearBits(void) +static enum MoveEndResult MoveEndClearBits(void) { ValidateBattlers(); @@ -1658,12 +3241,12 @@ static enum MoveEndResult MoveEnd_ClearBits(void) gBattleMons[gBattlerAttacker].volatiles.unableToUseMove = gBattleStruct->unableToUseMove; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Dancer(void) +static enum MoveEndResult MoveEndDancer(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsDanceMove(gCurrentMove) && !gBattleStruct->snatchedMoveIsUsed) { @@ -1700,7 +3283,7 @@ static enum MoveEndResult MoveEnd_Dancer(void) } } if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, gCurrentMove, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } @@ -1708,9 +3291,9 @@ static enum MoveEndResult MoveEnd_Dancer(void) return result; } -static enum MoveEndResult MoveEnd_PursuitNextAction(void) +static enum MoveEndResult MoveEndPursuitNextAction(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleStruct->battlerState[gBattlerTarget].pursuitTarget) { @@ -1730,7 +3313,7 @@ static enum MoveEndResult MoveEnd_PursuitNextAction(void) gBattlescriptCurrInstr = BattleScript_DoSwitchOut; gBattleStruct->monToSwitchIntoId[gBattlerTarget] = gBattleStruct->pursuitStoredSwitch; ClearPursuitValues(); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } @@ -1740,69 +3323,69 @@ static enum MoveEndResult MoveEnd_PursuitNextAction(void) static enum MoveEndResult (*const sMoveEndHandlers[])(void) = { - [MOVEEND_SET_VALUES] = MoveEnd_SetValues, - [MOVEEND_PROTECT_LIKE_EFFECT] = MoveEnd_ProtectLikeEffect, - [MOVEEND_ABSORB] = MoveEnd_Absorb, - [MOVEEND_RAGE] = MoveEnd_Rage, - [MOVEEND_SYNCHRONIZE_TARGET] = MoveEnd_SynchronizeTarget, - [MOVEEND_ABILITIES] = MoveEnd_Abilities, - [MOVEEND_ABILITIES_ATTACKER] = MoveEnd_AbilitiesAttacker, - [MOVEEND_STATUS_IMMUNITY_ABILITIES] = MoveEnd_StatusImmunityAbilities, - [MOVEEND_SYNCHRONIZE_ATTACKER] = MoveEnd_SynchronizeAttacker, - [MOVEEND_ATTACKER_INVISIBLE] = MoveEnd_AttackerInvisible, - [MOVEEND_ATTACKER_VISIBLE] = MoveEnd_AttackerVisible, - [MOVEEND_TARGET_VISIBLE] = MoveEnd_TargetVisible, - [MOVEEND_ITEM_EFFECTS_TARGET] = MoveEnd_ItemEffectsTarget, - [MOVEEND_ITEM_EFFECTS_ATTACKER_1] = MoveEnd_ItemEffectsAttacker1, - [MOVEEND_SYMBIOSIS] = MoveEnd_Symbiosis, - [MOVEEND_SUBSTITUTE] = MoveEnd_Substitute, - [MOVEEND_FAINT_BLOCK] = MoveEnd_FaintBlock, - [MOVEEND_SKY_DROP_CONFUSE] = MoveEnd_SkyDropConfuse, - [MOVEEND_UPDATE_LAST_MOVES] = MoveEnd_UpdateLastMoves, - [MOVEEND_MIRROR_MOVE] = MoveEnd_MirrorMove, - [MOVEEND_DEFROST] = MoveEnd_Defrost, - [MOVEEND_NEXT_TARGET] = MoveEnd_NextTarget, - [MOVEEND_HP_THRESHOLD_ITEMS_TARGET] = MoveEnd_HpThresholdItemsTarget, - [MOVEEND_MULTIHIT_MOVE] = MoveEnd_MultihitMove, - [MOVEEND_MOVE_BLOCK] = MoveEnd_MoveBlock, - [MOVEEND_ITEM_EFFECTS_ATTACKER_2] = MoveEnd_ItemEffectsAttacker2, - [MOVEEND_ABILITY_EFFECT_FOES_FAINTED] = MoveEnd_AbilityEffectFoesFainted, - [MOVEEND_SHEER_FORCE] = MoveEnd_SheerForce, - [MOVEEND_SHELL_TRAP] = MoveEnd_ShellTrap, - [MOVEEND_COLOR_CHANGE] = MoveEnd_ColorChange, - [MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET] = MoveEnd_KeeMarangaHpThresholdItemTarget, - [MOVEEND_CARD_BUTTON] = MoveEnd_CardButton, - [MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEnd_LifeOrbShellBell, - [MOVEEND_FORM_CHANGE] = MoveEnd_FormChange, - [MOVEEND_EMERGENCY_EXIT] = MoveEnd_EmergencyExit, - [MOVEEND_EJECT_PACK] = MoveEnd_EjectPack, - [MOVEEND_HIT_ESCAPE] = MoveEnd_HitEscape, - [MOVEEND_ITEMS_EFFECTS_ALL] = MoveEnd_ItemsEffectsAll, - [MOVEEND_WHITE_HERB] = MoveEnd_WhiteHerb, - [MOVEEND_OPPORTUNIST] = MoveEnd_Opportunist, - [MOVEEND_MIRROR_HERB] = MoveEnd_MirrorHerb, - [MOVEEND_PICKPOCKET] = MoveEnd_Pickpocket, - [MOVEEND_THIRD_MOVE_BLOCK] = MoveEnd_ThirdMoveBlock, - [MOVEEND_CHANGED_ITEMS] = MoveEnd_ChangedItems, - [MOVEEND_CLEAR_BITS] = MoveEnd_ClearBits, - [MOVEEND_DANCER] = MoveEnd_Dancer, - [MOVEEND_PURSUIT_NEXT_ACTION] = MoveEnd_PursuitNextAction, + [MOVEEND_SET_VALUES] = MoveEndSetValues, + [MOVEEND_PROTECT_LIKE_EFFECT] = MoveEndProtectLikeEffect, + [MOVEEND_ABSORB] = MoveEndAbsorb, + [MOVEEND_RAGE] = MoveEndRage, + [MOVEEND_SYNCHRONIZE_TARGET] = MoveEndSynchronizeTarget, + [MOVEEND_ABILITIES] = MoveEndAbilities, + [MOVEEND_ABILITIES_ATTACKER] = MoveEndAbilitiesAttacker, + [MOVEEND_STATUS_IMMUNITY_ABILITIES] = MoveEndStatusImmunityAbilities, + [MOVEEND_SYNCHRONIZE_ATTACKER] = MoveEndSynchronizeAttacker, + [MOVEEND_ATTACKER_INVISIBLE] = MoveEndAttackerInvisible, + [MOVEEND_ATTACKER_VISIBLE] = MoveEndAttackerVisible, + [MOVEEND_TARGET_VISIBLE] = MoveEndTargetVisible, + [MOVEEND_ITEM_EFFECTS_TARGET] = MoveEndItemEffectsTarget, + [MOVEEND_ITEM_EFFECTS_ATTACKER_1] = MoveEndItemEffectsAttacker1, + [MOVEEND_SYMBIOSIS] = MoveEndSymbiosis, + [MOVEEND_SUBSTITUTE] = MoveEndSubstitute, + [MOVEEND_FAINT_BLOCK] = MoveEndFaintBlock, + [MOVEEND_SKY_DROP_CONFUSE] = MoveEndSkyDropConfuse, + [MOVEEND_UPDATE_LAST_MOVES] = MoveEndUpdateLastMoves, + [MOVEEND_MIRROR_MOVE] = MoveEndMirrorMove, + [MOVEEND_DEFROST] = MoveEndDefrost, + [MOVEEND_NEXT_TARGET] = MoveEndNextTarget, + [MOVEEND_HP_THRESHOLD_ITEMS_TARGET] = MoveEndHpThresholdItemsTarget, + [MOVEEND_MULTIHIT_MOVE] = MoveEndMultihitMove, + [MOVEEND_MOVE_BLOCK] = MoveEndMoveBlock, + [MOVEEND_ITEM_EFFECTS_ATTACKER_2] = MoveEndItemEffectsAttacker2, + [MOVEEND_ABILITY_EFFECT_FOES_FAINTED] = MoveEndAbilityEffectFoesFainted, + [MOVEEND_SHEER_FORCE] = MoveEndSheerForce, + [MOVEEND_SHELL_TRAP] = MoveEndShellTrap, + [MOVEEND_COLOR_CHANGE] = MoveEndColorChange, + [MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET] = MoveEndKeeMarangaHpThresholdItemTarget, + [MOVEEND_CARD_BUTTON] = MoveEndCardButton, + [MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEndLifeOrbShellBell, + [MOVEEND_FORM_CHANGE] = MoveEndFormChange, + [MOVEEND_EMERGENCY_EXIT] = MoveEndEmergencyExit, + [MOVEEND_EJECT_PACK] = MoveEndEjectPack, + [MOVEEND_HIT_ESCAPE] = MoveEndHitEscape, + [MOVEEND_ITEMS_EFFECTS_ALL] = MoveEndItemsEffectsAll, + [MOVEEND_WHITE_HERB] = MoveEndWhiteHerb, + [MOVEEND_OPPORTUNIST] = MoveEndOpportunist, + [MOVEEND_MIRROR_HERB] = MoveEndMirrorHerb, + [MOVEEND_PICKPOCKET] = MoveEndPickpocket, + [MOVEEND_THIRD_MOVE_BLOCK] = MoveEndThirdMoveBlock, + [MOVEEND_CHANGED_ITEMS] = MoveEndChangedItems, + [MOVEEND_CLEAR_BITS] = MoveEndClearBits, + [MOVEEND_DANCER] = MoveEndDancer, + [MOVEEND_PURSUIT_NEXT_ACTION] = MoveEndPursuitNextAction, }; -bool32 DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState) +enum MoveEndResult DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; do { result = sMoveEndHandlers[gBattleScripting.moveendState](); - if (endMode == 1 && result == MOVEEND_STEP_CONTINUE) + if (endMode == 1 && result == MOVEEND_RESULT_CONTINUE) gBattleScripting.moveendState = MOVEEND_COUNT; if (endMode == 2 && endState == gBattleScripting.moveendState) gBattleScripting.moveendState = MOVEEND_COUNT; - } while (gBattleScripting.moveendState != MOVEEND_COUNT && result == MOVEEND_STEP_CONTINUE); + } while (gBattleScripting.moveendState != MOVEEND_COUNT && result == MOVEEND_RESULT_CONTINUE); return result; } @@ -1899,3 +3482,241 @@ void MoveValuesCleanUp(void) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE; gBattleCommunication[MISS_TYPE] = 0; } + +static void RequestNonVolatileChangee(u32 battlerAtk) +{ + BtlController_EmitSetMonData( + battlerAtk, + B_COMM_TO_CONTROLLER, + REQUEST_STATUS_BATTLE, + 0, + 4, + &gBattleMons[battlerAtk].status1); + MarkBattlerForControllerExec(battlerAtk); +} + +static enum Move GetMirrorMoveMove(void) +{ + s32 i, validMovesCount; + enum Move move = MOVE_NONE; + u16 validMoves[MAX_BATTLERS_COUNT] = {0}; + + for (validMovesCount = 0, i = 0; i < gBattlersCount; i++) + { + if (i != gBattlerAttacker) + { + move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i]; + if (move != MOVE_NONE && move != MOVE_UNAVAILABLE) + { + validMoves[validMovesCount] = move; + validMovesCount++; + } + } + } + + move = gBattleStruct->lastTakenMove[gBattlerAttacker]; + if ((move == MOVE_NONE || move == MOVE_UNAVAILABLE) && validMovesCount != 0) + move = validMoves[Random() % validMovesCount]; + + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move)) + move = GetTypeBasedZMove(move); + + return move; +} + +static bool32 InvalidMetronomeMove(u32 move) +{ + return GetMoveEffect(move) == EFFECT_PLACEHOLDER + || IsMoveMetronomeBanned(move); +} + +static enum Move GetMetronomeMove(void) +{ + enum Move move = MOVE_NONE; + +#if B_METRONOME_MOVES >= GEN_9 + u32 moveCount = MOVES_COUNT_GEN9; +#elif B_METRONOME_MOVES >= GEN_8 + u32 moveCount = MOVES_COUNT_GEN8; +#elif B_METRONOME_MOVES >= GEN_7 + u32 moveCount = MOVES_COUNT_GEN7; +#elif B_METRONOME_MOVES >= GEN_6 + u32 moveCount = MOVES_COUNT_GEN6; +#elif B_METRONOME_MOVES >= GEN_5 + u32 moveCount = MOVES_COUNT_GEN5; +#elif B_METRONOME_MOVES >= GEN_4 + u32 moveCount = MOVES_COUNT_GEN4; +#elif B_METRONOME_MOVES >= GEN_3 + u32 moveCount = MOVES_COUNT_GEN3; +#elif B_METRONOME_MOVES >= GEN_2 + u32 moveCount = MOVES_COUNT_GEN2; +#else + u32 moveCount = MOVES_COUNT_GEN1; +#endif + + move = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove); + return move; +} + +static enum Move GetAssistMove(void) +{ + enum Move move = MOVE_NONE; + u32 chooseableMovesNo = 0; + struct Pokemon *party; + u16 validMoves[PARTY_SIZE * MAX_MON_MOVES] = {MOVE_NONE}; + + party = GetBattlerParty(gBattlerAttacker); + + for (u32 monId = 0; monId < PARTY_SIZE; monId++) + { + if (monId == gBattlerPartyIndexes[gBattlerAttacker]) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + continue; + + for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) + { + enum Move move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + + if (IsMoveAssistBanned(move)) + continue; + + validMoves[chooseableMovesNo++] = move; + } + } + + if (chooseableMovesNo) + move = validMoves[Random() % chooseableMovesNo]; + + return move; +} + +enum Move GetNaturePowerMove(u32 battler) +{ + enum Move move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower; + if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + move = MOVE_MOONBLAST; + else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + move = MOVE_THUNDERBOLT; + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + move = MOVE_ENERGY_BALL; + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + move = MOVE_PSYCHIC; + else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE) + move = B_NATURE_POWER_MOVES >= GEN_4 ? MOVE_TRI_ATTACK : MOVE_SWIFT; + + return move; +} + +static enum Move GetSleepTalkMove(void) +{ + enum Move move = MOVE_NONE; + + u32 i, unusableMovesBits = 0, movePosition; + + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_COMATOSE + && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)) + return move; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i]) + || gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect) + unusableMovesBits |= (1 << (i)); + } + + unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM)); + if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen + return move; + + // Set Sleep Talk as used move, so it works with Last Resort. + gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; + do + { + movePosition = MOD(Random(), MAX_MON_MOVES); + } while ((1u << movePosition) & unusableMovesBits); + + move = gBattleMons[gBattlerAttacker].moves[movePosition]; + gCurrMovePos = movePosition; + + return move; +} + +static enum Move GetCopycatMove(void) +{ + if (gLastUsedMove == MOVE_NONE + || gLastUsedMove == MOVE_UNAVAILABLE + || IsMoveCopycatBanned(gLastUsedMove) + || IsZMove(gLastUsedMove)) + return MOVE_NONE; + + return gLastUsedMove; +} + +static enum Move GetMeFirstMove(void) +{ + enum Move move = GetChosenMoveFromPosition(gBattlerTarget); + + if (IsBattleMoveStatus(move) + || IsMoveMeFirstBanned(move) + || HasBattlerActedThisTurn(gBattlerTarget)) + return MOVE_NONE; + + return move; +} + +static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx) +{ + return TryMagicBounce(ctx) || TryMagicCoat(ctx); +} + +static bool32 TryMagicBounce(struct BattleContext *ctx) +{ + if (!MoveCanBeBouncedBack(ctx->move)) + return FALSE; + + if (gBattleStruct->magicBounceActive || gBattleStruct->bouncedMoveIsUsed) + return FALSE; + + if (ctx->abilityDef != ABILITY_MAGIC_BOUNCE) + return FALSE; + + gBattleStruct->magicBounceActive = TRUE; + gBattleStruct->moveBouncer = ctx->battlerDef; + + return TRUE; +} + +static bool32 TryMagicCoat(struct BattleContext *ctx) +{ + if (!MoveCanBeBouncedBack(ctx->move) || gBattleStruct->magicBounceActive) // Magic Bounce has precedence over magic coat + return FALSE; + + if (gBattleStruct->magicCoatActive || gBattleStruct->bouncedMoveIsUsed) + return FALSE; + + if (!gProtectStructs[ctx->battlerDef].bounceMove) + return FALSE; + + gBattleStruct->magicCoatActive = TRUE; + gBattleStruct->moveBouncer = ctx->battlerDef; + + return TRUE; +} + +static bool32 TryActivatePowderStatus(enum Move move) +{ + u32 partnerMove = GetChosenMoveFromPosition(BATTLE_PARTNER(gBattlerAttacker)); + if (!gBattleMons[gBattlerAttacker].volatiles.powder) + return FALSE; + if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove) + return TRUE; + if (move == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE) + return TRUE; + if (move == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove) + return TRUE; + return FALSE; +} + diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 65ebdeb51f..8b13f5ed58 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1034,7 +1034,7 @@ static void Cmd_attackcanceler(void) return; } - if (AtkCanceler_MoveSuccessOrder() != MOVE_STEP_SUCCESS) + if (DoAttackCanceler() != CANCELER_RESULT_SUCCESS) return; if (gBattleStruct->magicBounceActive && !gBattleStruct->bouncedMoveIsUsed) @@ -5010,10 +5010,10 @@ static void Cmd_moveend(void) enum MoveEndResult result = DoMoveEnd(cmd->endMode, cmd->endState); - if (result == MOVEEND_STEP_BREAK) + if (result == MOVEEND_RESULT_BREAK) return; - if (gBattleScripting.moveendState == MOVEEND_COUNT && result == MOVEEND_STEP_CONTINUE) + if (gBattleScripting.moveendState == MOVEEND_COUNT && result == MOVEEND_RESULT_CONTINUE) gBattlescriptCurrInstr = cmd->nextInstr; } @@ -6249,7 +6249,7 @@ static void Cmd_futuresighttargetfailure(void) CMD_ARGS(const u8 *failInstr); // Just do CancelerTargetFailure - if (!DONE_TARGET_FAILURE && AtkCanceler_MoveSuccessOrder() != MOVE_STEP_SUCCESS) + if (!DONE_TARGET_FAILURE && DoAttackCanceler() != CANCELER_RESULT_SUCCESS) return; if (IsBattlerUnaffectedByMove(gBattlerTarget)) @@ -6257,6 +6257,7 @@ static void Cmd_futuresighttargetfailure(void) else gBattlescriptCurrInstr = cmd->nextInstr; } +#undef DONE_TARGET_FAILURE static u32 GetPossibleNextTarget(void) { diff --git a/src/battle_util.c b/src/battle_util.c index ad8f0f598d..4c982e2716 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -56,28 +56,16 @@ static bool32 TryRemoveScreens(u32 battler); static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler); static u32 GetFlingPowerFromItemId(enum Item itemId); -static void SetRandomMultiHitCounter(); static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, bool32 abilityAffected, const u8 *battleScript, enum ResultOption option); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum ResultOption option); static bool32 IsOpposingSideEmpty(u32 battler); static void ResetParadoxWeatherStat(u32 battler); static void ResetParadoxTerrainStat(u32 battler); static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method); -static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx); static bool32 IsPowderMoveBlocked(struct BattleContext *ctx); const u8 *AbsorbedByDrainHpAbility(u32 battlerDef); const u8 *AbsorbedByStatIncreaseAbility(u32 battlerDef, enum Ability abilityDef, enum Stat statId, u32 statAmount); const u8 *AbsorbedByFlashFire(u32 battlerDef); -static bool32 TryMagicBounce(struct BattleContext *ctx); -static bool32 TryMagicCoat(struct BattleContext *ctx); - -// Submoves -static enum Move GetMirrorMoveMove(void); -static enum Move GetMetronomeMove(void); -static enum Move GetAssistMove(void); -static enum Move GetSleepTalkMove(void); -static enum Move GetCopycatMove(void); -static enum Move GetMeFirstMove(void); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent); @@ -313,7 +301,7 @@ static s32 CalcBeatUpDamage(struct BattleContext *ctx) return dmg; } -static enum DamageCategory GetReflectDamageMoveDamageCategory(u32 battler, enum Move move) +enum DamageCategory GetReflectDamageMoveDamageCategory(u32 battler, enum Move move) { u32 damageCategories = GetMoveReflectDamage_DamageCategories(move); @@ -1435,7 +1423,7 @@ void BattleScriptPop(void) gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; } -static bool32 IsGravityPreventingMove(enum Move move) +bool32 IsGravityPreventingMove(enum Move move) { if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) return FALSE; @@ -2014,1589 +2002,6 @@ void TryClearRageAndFuryCutter(void) } } -static inline bool32 TryFormChangeBeforeMove(void) -{ - bool32 result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE); - if (!result) - result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY); - if (!result) - return FALSE; - - gBattleScripting.battler = gBattlerAttacker; - BattleScriptCall(BattleScript_BattlerFormChange); - return TRUE; -} - -static inline bool32 TryActivatePowderStatus(enum Move move) -{ - u32 partnerMove = GetChosenMoveFromPosition(BATTLE_PARTNER(gBattlerAttacker)); - if (!gBattleMons[gBattlerAttacker].volatiles.powder) - return FALSE; - if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove) - return TRUE; - if (move == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE) - return TRUE; - if (move == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove) - return TRUE; - return FALSE; -} - -static void RequestNonVolatileChangee(u32 battlerAtk) -{ - BtlController_EmitSetMonData( - battlerAtk, - B_COMM_TO_CONTROLLER, - REQUEST_STATUS_BATTLE, - 0, - 4, - &gBattleMons[battlerAtk].status1); - MarkBattlerForControllerExec(battlerAtk); -} - -static enum MoveCanceler CancelerClearFlags(struct BattleContext *ctx) -{ - gBattleMons[ctx->battlerAtk].volatiles.grudge = FALSE; - gBattleMons[ctx->battlerAtk].volatiles.glaiveRush = FALSE; - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerStanceChangeOne(struct BattleContext *ctx) -{ - if (B_STANCE_CHANGE_FAIL < GEN_7 && ctx->chosenMove == ctx->move && TryFormChangeBeforeMove()) - return MOVE_STEP_BREAK; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerSkyDrop(struct BattleContext *ctx) -{ - // If Pokemon is being held in Sky Drop - if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) - { - gBattlescriptCurrInstr = BattleScript_MoveEnd; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerRecharge(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerChillyReception(struct BattleContext *ctx) -{ - if (GetMoveEffect(ctx->move) == EFFECT_WEATHER_AND_SWITCH) - { - BattleScriptCall(BattleScript_ChillyReceptionMessage); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerAsleepOrFrozen(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_BREAK; - - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - { - if (UproarWakeUpCheck(ctx->battlerAtk)) - { - TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; - gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; - gEffectBattler = ctx->battlerAtk; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedWokeUp); - } - else - { - u32 toSub; - if (IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_EARLY_BIRD)) - toSub = 2; - else - toSub = 1; - - if ((gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) < toSub) - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; - else - gBattleMons[ctx->battlerAtk].status1 -= toSub; - - enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->move); - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - { - if (!IsUsableWhileAsleepEffect(moveEffect)) - { - effect = MOVE_STEP_FAILURE; - gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; - } - else - { - effect = MOVE_STEP_BREAK; - } - } - else - { - TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); - gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedWokeUp); - } - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE && !MoveThawsUser(ctx->move)) - { - if (!RandomPercentage(RNG_FROZEN, 20)) - { - effect = MOVE_STEP_FAILURE; - gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; - } - else // unfreeze - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfroze); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - - return effect; -} - -static enum MoveCanceler CancelerObedience(struct BattleContext *ctx) -{ - if (!gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) - { - enum Obedience obedienceResult = GetAttackerObedienceForAction(); - switch (obedienceResult) - { - case OBEYS: - return MOVE_STEP_SUCCESS; - case DISOBEYS_LOAFS: - // Randomly select, then print a disobedient string - // 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[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - case DISOBEYS_HITS_SELF: - gBattlerTarget = ctx->battlerAtk; - struct BattleContext dmgCtx = {0}; - dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; - dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; - dmgCtx.moveType = TYPE_MYSTERY; - dmgCtx.isCrit = FALSE; - dmgCtx.randomFactor = FALSE; - dmgCtx.updateFlags = TRUE; - dmgCtx.isSelfInflicted = TRUE; - dmgCtx.fixedBasePower = 40; - gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); - gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; - return MOVE_STEP_FAILURE; // Move doesn't fail but mon hits itself - case DISOBEYS_FALL_ASLEEP: - if (IsSleepClauseEnabled()) - gBattleStruct->battlerState[ctx->battlerAtk].sleepClauseEffectExempt = TRUE; - gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - break; - case DISOBEYS_WHILE_ASLEEP: - gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - case DISOBEYS_RANDOM_MOVE: - gCurrentMove = gCalledMove = gBattleMons[ctx->battlerAtk].moves[gCurrMovePos]; - BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove); - gBattlerTarget = GetBattleMoveTarget(gCalledMove, TARGET_NONE); - return MOVE_STEP_BREAK; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerPowerPoints(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].pp[gCurrMovePos] == 0 - && ctx->move != MOVE_STRUGGLE - && !gSpecialStatuses[ctx->battlerAtk].dancerUsedMove - && !gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) - { - gBattlescriptCurrInstr = BattleScript_NoPPForMove; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTruant(struct BattleContext *ctx) -{ - if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; - gBattlerAbility = ctx->battlerAtk; - gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocus(struct BattleContext *ctx) -{ - u32 focusPunchFailureConfig = GetConfig(CONFIG_FOCUS_PUNCH_FAILURE); - - // In Gens 3-4, only check if is using Focus Punch. - // In Gens 5-6, only check if the chosen move is Focus Punch. - // In Gens 7+, check if chose and is using Focus Punch. - if ((gProtectStructs[ctx->battlerAtk].physicalDmg || gProtectStructs[ctx->battlerAtk].specialDmg) - && (focusPunchFailureConfig < GEN_5 || GetMoveEffect(gChosenMoveByBattler[ctx->battlerAtk]) == EFFECT_FOCUS_PUNCH) - && (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH) - && !gProtectStructs[ctx->battlerAtk].survivedOHKO) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocusPreGen5(struct BattleContext *ctx) -{ - if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) < GEN_5) - return CancelerFocus(ctx); - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocusGen5(struct BattleContext *ctx) -{ - if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) >= GEN_5) - return CancelerFocus(ctx); - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFlinch(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.flinched) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerDisabled(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - && gBattleMons[ctx->battlerAtk].volatiles.disabledMove == ctx->move - && gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerVolatileBlocked(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - && gBattleMons[ctx->battlerAtk].volatiles.healBlock - && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move)) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; - return MOVE_STEP_FAILURE; - } - else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move)) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; - return MOVE_STEP_FAILURE; - } - else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTaunted(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.tauntTimer && IsBattleMoveStatus(ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerImprisoned(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerConfused(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].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, (GetConfig(CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) - { - gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; - struct BattleContext dmgCtx = {0}; - dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; - dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; - dmgCtx.moveType = TYPE_MYSTERY; - dmgCtx.isCrit = FALSE; - dmgCtx.randomFactor = FALSE; - dmgCtx.updateFlags = TRUE; - dmgCtx.isSelfInflicted = TRUE; - dmgCtx.fixedBasePower = 40; - gBattlerTarget = gBattlerAttacker; - gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; - return MOVE_STEP_FAILURE; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; - BattleScriptCall(BattleScript_MoveUsedIsConfused); - return MOVE_STEP_BREAK; - } - } - else // snapped out of confusion - { - BattleScriptCall(BattleScript_MoveUsedIsConfusedNoMore); - return MOVE_STEP_BREAK; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerParalyzed(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS - && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) - && !RandomPercentage(RNG_PARALYSIS, 75)) - { - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerInfatuation(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.infatuation) - { - gBattleScripting.battler = gBattleMons[ctx->battlerAtk].volatiles.infatuation - 1; - if (!RandomPercentage(RNG_INFATUATION, 50)) - { - BattleScriptCall(BattleScript_MoveUsedIsInLove); - return MOVE_STEP_BREAK; - } - else - { - BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; - return MOVE_STEP_FAILURE; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerBide(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns) - { - if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns) - { - gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; - } - else - { - // This is removed in FRLG and Emerald for some reason - //gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE; - if (gBideDmg[ctx->battlerAtk]) - { - gCurrentMove = MOVE_BIDE; - gBattlerTarget = gBideTarget[ctx->battlerAtk]; - if (!IsBattlerAlive(ctx->battlerDef)) - gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED); - gBattlescriptCurrInstr = BattleScript_BideAttack; - return MOVE_STEP_BREAK; // Jumps to a different script but no failure - } - else - { - gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; - return MOVE_STEP_FAILURE; - } - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerZMoves(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE) - { - // attacker has a queued z move - RecordItemEffectBattle(ctx->battlerAtk, HOLD_EFFECT_Z_CRYSTAL); - SetGimmickAsActivated(ctx->battlerAtk, GIMMICK_Z_MOVE); - - gBattleScripting.battler = ctx->battlerAtk; - if (GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS) - BattleScriptCall(BattleScript_ZMoveActivateStatus); - else - BattleScriptCall(BattleScript_ZMoveActivateDamaging); - - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerChoiceLock(struct BattleContext *ctx) -{ - u16 *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; - enum HoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); - - if (gChosenMove != MOVE_STRUGGLE - && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) - && (IsHoldEffectChoice(holdEffect) || ctx->abilityAtk == ABILITY_GORILLA_TACTICS)) - *choicedMoveAtk = gChosenMove; - - u32 moveIndex; - for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) - { - if (gBattleMons[ctx->battlerAtk].moves[moveIndex] == *choicedMoveAtk) - break; - } - - if (moveIndex == MAX_MON_MOVES) - *choicedMoveAtk = MOVE_NONE; - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerCallSubmove(struct BattleContext *ctx) -{ - bool32 noEffect = FALSE; - enum Move calledMove = MOVE_NONE; - const u8 *battleScript = NULL; - battleScript = BattleScript_SubmoveAttackstring; - - switch(GetMoveEffect(ctx->move)) - { - case EFFECT_MIRROR_MOVE: - calledMove = GetMirrorMoveMove(); - break; - case EFFECT_METRONOME: - calledMove = GetMetronomeMove(); - battleScript = BattleScript_MetronomeAttackstring; - break; - case EFFECT_ASSIST: - calledMove = GetAssistMove(); - break; - case EFFECT_NATURE_POWER: - calledMove = GetNaturePowerMove(ctx->battlerAtk); - battleScript = BattleScript_NaturePowerAttackstring; - break; - case EFFECT_SLEEP_TALK: - calledMove = GetSleepTalkMove(); - battleScript = BattleScript_SleepTalkAttackstring; - break; - case EFFECT_COPYCAT: - calledMove = GetCopycatMove(); - break; - case EFFECT_ME_FIRST: - calledMove = GetMeFirstMove(); - break; - default: - noEffect = TRUE; - break; - } - - if (noEffect) - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - return MOVE_STEP_SUCCESS; - } - - if (calledMove != MOVE_NONE) - { - if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) - calledMove = GetTypeBasedZMove(calledMove); - if (GetMoveEffect(ctx->move) == EFFECT_COPYCAT && IsMaxMove(calledMove)) - calledMove = gBattleStruct->dynamax.lastUsedBaseMove; - - gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS; - gCalledMove = calledMove; - BattleScriptCall(battleScript); - return MOVE_STEP_BREAK; - } - - gBattleStruct->submoveAnnouncement = SUBMOVE_FAILURE; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerThaw(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_BREAK; - - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE) - { - if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfroze); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; - } - else - { - effect = MOVE_STEP_FAILURE; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE && MoveThawsUser(ctx->move)) - { - if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FROSTBITE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfrostbite); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FROSTBITE_HEALED_BY_MOVE; - } - else - { - effect = MOVE_STEP_FAILURE; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - return effect; -} - -static enum MoveCanceler CancelerStanceChangeTwo(struct BattleContext *ctx) -{ - if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == ctx->move && TryFormChangeBeforeMove()) - return MOVE_STEP_BREAK; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerAttackstring(struct BattleContext *ctx) -{ - BattleScriptCall(BattleScript_Attackstring); - if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) - gLastPrintedMoves[gBattlerAttacker] = gChosenMove; - return MOVE_STEP_BREAK; -} - -static enum MoveCanceler CancelerPPDeduction(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns - || gSpecialStatuses[ctx->battlerAtk].dancerUsedMove - || ctx->move == MOVE_STRUGGLE) - return MOVE_STEP_SUCCESS; - - s32 ppToDeduct = 1; - u32 moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - u32 movePosition = gCurrMovePos; - - if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS) - movePosition = gChosenMovePos; - - if (IsSpreadMove(moveTarget) - || moveTarget == TARGET_ALL_BATTLERS - || moveTarget == TARGET_FIELD - || MoveForcesPressure(ctx->move)) - { - for (u32 i = 0; i < gBattlersCount; i++) - { - if (!IsBattlerAlly(i, ctx->battlerAtk) && IsBattlerAlive(i)) - ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE); - } - } - else if (moveTarget != TARGET_OPPONENTS_FIELD) - { - if (ctx->battlerAtk != ctx->battlerDef && GetBattlerAbility(ctx->battlerDef) == ABILITY_PRESSURE) - ppToDeduct++; - } - - // For item Metronome, echoed voice - if (ctx->move != gLastResultingMoves[ctx->battlerAtk] || gBattleStruct->unableToUseMove) - gBattleMons[ctx->battlerAtk].volatiles.metronomeItemCounter = 0; - - if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct) - gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct; - else - gBattleMons[ctx->battlerAtk].pp[movePosition] = 0; - - if (MOVE_IS_PERMANENT(ctx->battlerAtk, movePosition)) - { - 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) - { - if (gBattleStruct->submoveAnnouncement == SUBMOVE_FAILURE) - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - else if (CancelerVolatileBlocked(ctx) == MOVE_STEP_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - return MOVE_STEP_FAILURE; - } - else - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - gBattlerTarget = GetBattleMoveTarget(ctx->move, TARGET_NONE); - gBattleScripting.animTurn = 0; - gBattleScripting.animTargetsHit = 0; - - // Possibly better to just move type setting and redirection to attackcanceler as a new case at this point - SetTypeBeforeUsingMove(ctx->move, ctx->battlerAtk); - DetermineTarget(moveTarget, FALSE); - ClearDamageCalcResults(); - gBattlescriptCurrInstr = GetMoveBattleScript(ctx->move); - return MOVE_STEP_BREAK; - } - } - - return MOVE_STEP_SUCCESS; -} - -// We don't have clear data on where this belongs to but I assume it should at least be checked before Protean -static enum MoveCanceler CancelerSkyBattle(struct BattleContext *ctx) -{ - if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerWeatherPrimal(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_SUCCESS; - if (HasWeatherEffect() && GetMovePower(ctx->move) > 0) - { - enum Type moveType = GetBattleMoveType(ctx->move); - if (moveType == TYPE_FIRE && gBattleWeather & B_WEATHER_RAIN_PRIMAL && (GetConfig(CONFIG_POWDER_STATUS_HEAVY_RAIN) >= GEN_7 || !TryActivatePowderStatus(ctx->move))) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; - effect = MOVE_STEP_FAILURE; - } - else if (moveType == TYPE_WATER && gBattleWeather & B_WEATHER_SUN_PRIMAL) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; - effect = MOVE_STEP_FAILURE; - } - if (effect == MOVE_STEP_FAILURE) - { - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; - } - } - return effect; -} - -static enum MoveCanceler CancelerMoveFailure(struct BattleContext *ctx) -{ - const u8 *battleScript = NULL; - - switch (GetMoveEffect(ctx->move)) - { - case EFFECT_FAIL_IF_NOT_ARG_TYPE: - if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move))) - 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_ICY_ANY && 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_REFLECT_DAMAGE: - { - enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(ctx->battlerAtk, ctx->move); - if (IsBattlerAlly(ctx->battlerAtk, ctx->battlerDef)) - battleScript = BattleScript_ButItFailed; - // Counter / Metal Burst and took physical damage - else if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL - && gProtectStructs[ctx->battlerAtk].physicalDmg > 0 - && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].physicalBattlerId].hp)) - break; - // Mirror Coat / Metal Burst and took special damage - else if (reflectCategory == DAMAGE_CATEGORY_SPECIAL - && gProtectStructs[ctx->battlerAtk].specialDmg > 0 - && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].specialBattlerId].hp)) - break; - else - battleScript = BattleScript_ButItFailed; - break; - } - case EFFECT_DESTINY_BOND: - if (DoesDestinyBondFail(ctx->battlerAtk)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_FIRST_TURN_ONLY: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_MAT_BLOCK: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_FLING: - if (!CanFling(ctx->battlerAtk, ctx->battlerDef)) - battleScript = BattleScript_ButItFailed; - else if (!IsBattlerAlive(ctx->battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling. - battleScript = BattleScript_FlingFailConsumeItem; - 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 (gBattleStruct->futureSight[ctx->battlerDef].counter > 0) - 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->abilityDef == ABILITY_KLUTZ) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_PROTECT: - case EFFECT_ENDURE: - TryResetConsecutiveUseCounter(gBattlerAttacker); - if (IsLastMonToMove(ctx->battlerAtk)) - { - battleScript = BattleScript_ButItFailed; - } - else - { - u32 protectMethod = GetMoveProtectMethod(ctx->move); - bool32 canUseProtectSecondTime = CanUseMoveConsecutively(ctx->battlerAtk); - bool32 canUseWideGuard = (GetConfig(CONFIG_WIDE_GUARD) >= GEN_6 && protectMethod == PROTECT_WIDE_GUARD); - bool32 canUseQuickGuard = (GetConfig(CONFIG_QUICK_GUARD) >= GEN_6 && protectMethod == PROTECT_QUICK_GUARD); - - if (!canUseProtectSecondTime && !canUseWideGuard && !canUseQuickGuard) - battleScript = BattleScript_ButItFailed; - } - if (battleScript != NULL) - { - gBattleMons[gBattlerAttacker].volatiles.consecutiveMoveUses = 0; - gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; - } - break; - case EFFECT_REST: - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP - || ctx->abilityAtk == ABILITY_COMATOSE) - battleScript = BattleScript_RestIsAlreadyAsleep; - else if (gBattleMons[ctx->battlerAtk].hp == gBattleMons[ctx->battlerAtk].maxHP) - battleScript = BattleScript_AlreadyAtFullHp; - else if (ctx->abilityAtk == ABILITY_INSOMNIA - || ctx->abilityAtk == ABILITY_VITAL_SPIRIT - || ctx->abilityAtk == ABILITY_PURIFYING_SALT) - battleScript = BattleScript_InsomniaProtects; - break; - case EFFECT_SUCKER_PUNCH: - if (HasBattlerActedThisTurn(ctx->battlerDef) - || (IsBattleMoveStatus(GetChosenMoveFromPosition(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_UPPER_HAND: - { - u32 prio = GetChosenMovePriority(ctx->battlerDef, GetBattlerAbility(ctx->battlerDef)); - if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status - || HasBattlerActedThisTurn(ctx->battlerDef) - || gChosenMoveByBattler[ctx->battlerDef] == MOVE_NONE - || IsBattleMoveStatus(gChosenMoveByBattler[ctx->battlerDef])) - battleScript = BattleScript_ButItFailed; - break; - } - case EFFECT_SNORE: - if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - && ctx->abilityAtk != ABILITY_COMATOSE) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_STEEL_ROLLER: - if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_STOCKPILE: - if (gBattleMons[ctx->battlerAtk].volatiles.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 (gBattleMons[ctx->battlerAtk].volatiles.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) - { - gBattlescriptCurrInstr = battleScript; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerPowderStatus(struct BattleContext *ctx) -{ - if (TryActivatePowderStatus(ctx->move)) - { - if (!IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) - SetPassiveDamageAmount(ctx->battlerAtk, GetNonDynamaxMaxHP(ctx->battlerAtk) / 4); - - // This might be incorrect - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - || HasTrainerUsedGimmick(ctx->battlerAtk, GIMMICK_Z_MOVE)) - gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -bool32 IsDazzlingAbility(enum Ability ability) -{ - 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 MoveCanceler CancelerPriorityBlock(struct BattleContext *ctx) -{ - bool32 effect = FALSE; - s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); - - if (priority <= 0) - return MOVE_STEP_SUCCESS; - - u32 battler; - u32 ability = ABILITY_NONE; // ability of battler who is blocking - bool32 isSpreadMove = IsSpreadMove(GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move)); - for (battler = 0; battler < gBattlersCount; battler++) - { - if (!IsBattlerAlive(battler) || IsBattlerAlly(ctx->battlerAtk, battler)) - continue; - if (!isSpreadMove && !IsBattlerAlly(battler, ctx->battlerDef)) - continue; - - ability = GetBattlerAbility(battler); - if (IsDazzlingAbility(ability)) - { - effect = TRUE; - break; - } - } - - if (effect) - { - gLastUsedAbility = ability; - RecordAbilityBattle(battler, ability); - gBattleScripting.battler = gBattlerAbility = battler; - gBattlescriptCurrInstr = BattleScript_DazzlingProtected; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerProtean(struct BattleContext *ctx) -{ - enum Type moveType = GetBattleMoveType(ctx->move); - if (ProteanTryChangeType(ctx->battlerAtk, ctx->abilityAtk, ctx->move, moveType)) - { - if (GetConfig(CONFIG_PROTEAN_LIBERO) >= GEN_9) - gBattleMons[ctx->battlerAtk].volatiles.usedProteanLibero = TRUE; - PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); - gBattlerAbility = ctx->battlerAtk; - PrepareStringBattle(STRINGID_EMPTYSTRING3, ctx->battlerAtk); - gBattleCommunication[MSG_DISPLAY] = 1; - BattleScriptCall(BattleScript_ProteanActivates); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerExplodingDamp(struct BattleContext *ctx) -{ - u32 dampBattler = IsAbilityOnField(ABILITY_DAMP); - if (dampBattler && IsMoveDampBanned(ctx->move)) - { - gBattleScripting.battler = dampBattler - 1; - gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerExplosion(struct BattleContext *ctx) -{ - // KO user of Explosion; for Final Gambit doesn't happen if target is immune or if it missed - if (IsExplosionMove(ctx->move) - && (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || !IsBattlerUnaffectedByMove(ctx->battlerDef))) - { - BattleScriptCall(BattleScript_Explosion); - return MOVE_STEP_BREAK; - } - - return MOVE_STEP_SUCCESS; -} - -static bool32 CanTwoTurnMoveFireThisTurn(struct BattleContext *ctx) -{ - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect - || GetMoveEffect(ctx->move) == EFFECT_GEOMANCY - || !IsBattlerWeatherAffected(ctx->battlerAtk, GetMoveTwoTurnAttackWeather(ctx->move))) - return FALSE; - return TRUE; -} - -static enum MoveCanceler CancelerCharging(struct BattleContext *ctx) -{ - if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect - || GetMoveEffect(ctx->move) == EFFECT_SKY_DROP) - return MOVE_STEP_SUCCESS; - - enum MoveCanceler step = MOVE_STEP_SUCCESS; - - if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) // Second turn - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE; - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) - gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE; - step = MOVE_STEP_SUCCESS; - } - else if (!gProtectStructs[ctx->battlerAtk].chargingTurn) // First turn charge - { - gLockedMoves[ctx->battlerAtk] = ctx->move; - gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE; - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) - gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = GetMoveTwoTurnAttackStatus(ctx->move); - BattleScriptCall(BattleScript_TwoTurnMoveCharging); - step = MOVE_STEP_PAUSE; - } - else // Try move this turn. Otherwise use next turn - { - if (CanTwoTurnMoveFireThisTurn(ctx)) - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - step = MOVE_STEP_SUCCESS; - } - else if (ctx->holdEffectAtk == HOLD_EFFECT_POWER_HERB) - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - gLastUsedItem = gBattleMons[ctx->battlerAtk].item; - BattleScriptCall(BattleScript_PowerHerbActivation); - step = MOVE_STEP_BREAK; - } - else // Use move next turn - { - gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE; - gBattlescriptCurrInstr = BattleScript_MoveEnd; - step = MOVE_STEP_BREAK; - } - - } - - return step; -} - -static bool32 NoTargetPresent(u32 battler, u32 move, u32 moveTarget) -{ - switch (moveTarget) - { - case TARGET_USER_AND_ALLY: - return FALSE; // At least user is present - case TARGET_ALLY: - if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) // Seems like TARGET_ALLY is retargeting if no ally - return TRUE; - break; - case TARGET_SELECTED: - case TARGET_DEPENDS: - case TARGET_RANDOM: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(GetBattleMoveTarget(move, TARGET_NONE))) - return TRUE; - break; - case TARGET_BOTH: - case TARGET_SMART: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))) - return TRUE; - break; - case TARGET_FOES_AND_ALLY: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) - return TRUE; - break; - } - - return FALSE; -} - -static enum MoveCanceler CancelerNoTarget(struct BattleContext *ctx) -{ - enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - - if (NoTargetPresent(gBattlerAttacker, gCurrentMove, moveTarget)) - { - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - - if (ctx->battlerAtk == ctx->battlerDef - && moveTarget == TARGET_ALLY - && gProtectStructs[BATTLE_PARTNER(ctx->battlerAtk)].usedAllySwitch) - { - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTookAttack(struct BattleContext *ctx) -{ - if (gSpecialStatuses[gBattlerTarget].abilityRedirected) - { - gSpecialStatuses[gBattlerTarget].abilityRedirected = FALSE; - BattleScriptCall(BattleScript_TookAttack); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -#define checkFailure FALSE -#define skipFailure TRUE -static bool32 IsSingleTarget(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != gBattlerTarget) - return skipFailure; - return checkFailure; -} - -static bool32 IsSmartTarget(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk == BATTLE_PARTNER(battlerDef)) - return skipFailure; - return checkFailure; -} - -static bool32 IsTargetingBothFoes(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef) - { - // Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT - if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS) - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; -} - -static bool32 IsTargetingSelf(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk != battlerDef) - return skipFailure; - return skipFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists -} - -static bool32 IsTargetingAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; -} - -static bool32 IsTargetingSelfAndAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - if (battlerDef != battlerAtk) // Don't set result flags for user - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists -} - -static bool32 IsTargetingSelfOrAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef == battlerAtk) - return skipFailure; - - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - - return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such move exists -} - -static bool32 IsTargetingFoesAndAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk == battlerDef) - return skipFailure; // Don't set result flags for user - return checkFailure; -} - -static bool32 IsTargetingField(u32 battlerAtk, u32 battlerDef) -{ - return skipFailure; -} - -static bool32 IsTargetingOpponentsField(u32 battlerAtk, u32 battlerDef) -{ - return checkFailure; // Bounce failure only -} - -static bool32 IsTargetingAllBattlers(u32 battlerAtk, u32 battlerDef) -{ - if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef) - return skipFailure; - return checkFailure; -} - -// ShouldCheckFailureOnTarget -static bool32 (*const sShouldCheckTargetMoveFailure[])(u32 battlerAtk, u32 battlerDef) = -{ - [TARGET_NONE] = IsTargetingField, - [TARGET_SELECTED] = IsSingleTarget, - [TARGET_DEPENDS] = IsSingleTarget, - [TARGET_OPPONENT] = IsSingleTarget, - [TARGET_RANDOM] = IsSingleTarget, - [TARGET_BOTH] = IsTargetingBothFoes, - [TARGET_USER] = IsTargetingSelf, - [TARGET_SMART] = IsSmartTarget, - [TARGET_ALLY] = IsTargetingAlly, - [TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly, - [TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly, - [TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly, - [TARGET_FIELD] = IsTargetingField, - [TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField, - [TARGET_ALL_BATTLERS] = IsTargetingAllBattlers, -}; - -static bool32 ShouldCheckTargetMoveFailure(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveTarget) -{ - // For Bounced moves - if (IsBattlerUnaffectedByMove(battlerDef)) - return skipFailure; - - return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef); -} -#undef checkFailure -#undef skipFailure - - -static enum MoveCanceler CancelerTargetFailure(struct BattleContext *ctx) -{ - bool32 targetAvoidedAttack = FALSE; - enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - s32 movePriority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); - ctx->moveType = GetBattleMoveType(ctx->move); - ctx->updateFlags = TRUE; - ctx->runScript = TRUE; - - while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) - { - ctx->battlerDef = gBattleStruct->eventState.atkCancelerBattler++; - - if (ShouldCheckTargetMoveFailure(ctx->battlerAtk, ctx->battlerDef, ctx->move, moveTarget)) - continue; - - ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); - ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef); - - if (moveTarget == TARGET_OPPONENTS_FIELD) - { - if (CanBattlerBounceBackMove(ctx)) - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - continue; - } - - if (!IsBattlerAlive(ctx->battlerDef)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - continue; - } - else if (!BreaksThroughSemiInvulnerablity(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->abilityDef, ctx->move)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_AVOIDED_ATK; - if (GetMoveEffect(ctx->move) == EFFECT_FLING) - BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); - else - BattleScriptCall(BattleScript_TargetAvoidsAttack); - targetAvoidedAttack = TRUE; - } - else if (IsBattlerProtected(ctx)) - { - SetOrClearRageVolatile(); - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED; - if (GetMoveEffect(ctx->move) == EFFECT_FLING) - BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); - else - BattleScriptCall(BattleScript_TargetAvoidsAttack); - targetAvoidedAttack = TRUE; - } - else if (CanBattlerBounceBackMove(ctx)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - } - else if (CanMoveBeBlockedByTarget(ctx, movePriority)) - { - - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - targetAvoidedAttack = TRUE; - } - else if (GetMoveEffect(ctx->move) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(ctx->battlerAtk, ctx->battlerDef)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_NO_EFFECT; - BattleScriptCall(BattleScript_ItDoesntAffectFoe); - targetAvoidedAttack = TRUE; - } - else - { - CalcTypeEffectivenessMultiplier(ctx); - - if (ctx->abilityBlocked) - { - ctx->abilityBlocked = FALSE; - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; - gBattlerAbility = ctx->battlerDef; - RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef); - BattleScriptCall(BattleScript_AbilityPopUp); - targetAvoidedAttack = TRUE; - } - else if (ctx->airBalloonBlocked) - { - ctx->airBalloonBlocked = FALSE; - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; - BattleScriptCall(BattleScript_DoesntAffectScripting); - targetAvoidedAttack = TRUE; - } - } - - if (targetAvoidedAttack) - { - gLastLandedMoves[gBattlerTarget] = 0; // Might need investigation on what exactly clears is - gLastHitByType[gBattlerTarget] = 0; - gBattleScripting.battler = ctx->battlerDef; - gBattleStruct->pledgeMove = FALSE; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - return MOVE_STEP_PAUSE; - } - } - - if (IsDoubleBattle()) - { - if (moveTarget == TARGET_BOTH) - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, gBattlerAttacker); - else if (moveTarget == TARGET_FOES_AND_ALLY) - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker); - } - - ctx->battlerDef = gBattlerTarget; - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static bool32 CantFullyProtectFromMove(u32 battlerDef) -{ - if (MoveIgnoresProtect(gCurrentMove)) - return FALSE; - if (!IsZMove(gCurrentMove) && !IsMaxMove(gCurrentMove)) - return FALSE; - return GetProtectType(gProtectStructs[battlerDef].protected) == PROTECT_TYPE_SINGLE - && gProtectStructs[battlerDef].protected != PROTECT_MAX_GUARD; -} - -static enum MoveCanceler CancelerNotFullyProtected(struct BattleContext *ctx) -{ - while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) - { - u32 battlerDef = gBattleStruct->eventState.atkCancelerBattler++; - - if (CantFullyProtectFromMove(battlerDef)) - { - BattleScriptCall(BattleScript_CouldntFullyProtect); - gBattleScripting.battler = battlerDef; - return MOVE_STEP_PAUSE; - } - } - - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static bool32 IsMoveParentalBondAffected(struct BattleContext *ctx) -{ - if (ctx->abilityAtk != ABILITY_PARENTAL_BOND - || gBattleStruct->numSpreadTargets > 1 - || IsMoveParentalBondBanned(ctx->move) - || GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS - || GetMoveEffect(ctx->move) == EFFECT_SEMI_INVULNERABLE - || GetMoveEffect(ctx->move) == EFFECT_TWO_TURNS_ATTACK - || GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE - || ctx->move == MOVE_STRUGGLE) - return FALSE; - return TRUE; -} - -static void SetPossibleNewSmartTarget(u32 move) -{ - if (!IsBattlerUnaffectedByMove(gBattlerTarget) - || !CanTargetPartner(gBattlerAttacker, gBattlerTarget) - || IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), move) - || GetBattlerMoveTargetType(gBattlerAttacker, move) != TARGET_SMART) - return; - - u32 partner = BATTLE_PARTNER(gBattlerTarget); - if (!IsBattlerUnaffectedByMove(partner)) - gBattlerTarget = partner; -} - -static enum MoveCanceler CancelerMultihitMoves(struct BattleContext *ctx) -{ - SetPossibleNewSmartTarget(ctx->move); - - if (IsBattlerUnaffectedByMove(gBattlerTarget)) // Dragon Darts can still hit partner - { - gMultiHitCounter = 0; - } - else if (IsMultiHitMove(ctx->move)) - { - enum Ability ability = ctx->abilityAtk; - - if (ability == ABILITY_SKILL_LINK) - { - gMultiHitCounter = 5; - } - else if (GetMoveEffect(ctx->move) == EFFECT_SPECIES_POWER_OVERRIDE - && gBattleMons[ctx->battlerAtk].species == GetMoveSpeciesPowerOverride_Species(ctx->move)) - { - gMultiHitCounter = GetMoveSpeciesPowerOverride_NumOfHits(ctx->move); - } - else - { - SetRandomMultiHitCounter(); - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else if (GetMoveStrikeCount(ctx->move) > 1) - { - if (GetMoveEffect(ctx->move) == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(ctx->battlerAtk) == HOLD_EFFECT_LOADED_DICE) - { - gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); - } - else - { - gMultiHitCounter = GetMoveStrikeCount(ctx->move); - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) - } - else if (GetMoveEffect(ctx->move) == EFFECT_BEAT_UP) - { - struct Pokemon* party = GetBattlerParty(ctx->battlerAtk); - int i; - gBattleStruct->beatUpSlot = 0; - gMultiHitCounter = 0; - memset(gBattleStruct->beatUpSpecies, 0xFF, sizeof(gBattleStruct->beatUpSpecies)); - - for (i = 0; i < PARTY_SIZE; i++) - { - u32 species = GetMonData(&party[i], MON_DATA_SPECIES); - if (species != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_HP) - && !GetMonData(&party[i], MON_DATA_IS_EGG) - && !GetMonData(&party[i], MON_DATA_STATUS)) - { - if (GetConfig(CONFIG_BEAT_UP) >= GEN_5) - gBattleStruct->beatUpSpecies[gMultiHitCounter] = species; - else - gBattleStruct->beatUpSpecies[gMultiHitCounter] = i; - gMultiHitCounter++; - } - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else if (IsMoveParentalBondAffected(ctx)) - { - gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT; - gMultiHitCounter = 2; - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else - { - gMultiHitCounter = 0; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler (*const sMoveSuccessOrderCancelers[])(struct BattleContext *ctx) = -{ - [CANCELER_CLEAR_FLAGS] = CancelerClearFlags, - [CANCELER_STANCE_CHANGE_1] = CancelerStanceChangeOne, - [CANCELER_SKY_DROP] = CancelerSkyDrop, - [CANCELER_RECHARGE] = CancelerRecharge, - [CANCELER_CHILLY_RECEPTION] = CancelerChillyReception, - [CANCELER_ASLEEP_OR_FROZEN] = CancelerAsleepOrFrozen, - [CANCELER_OBEDIENCE] = CancelerObedience, - [CANCELER_POWER_POINTS] = CancelerPowerPoints, - [CANCELER_TRUANT] = CancelerTruant, - [CANCELER_FOCUS_GEN5] = CancelerFocusGen5, - [CANCELER_FLINCH] = CancelerFlinch, - [CANCELER_DISABLED] = CancelerDisabled, - [CANCELER_VOLATILE_BLOCKED] = CancelerVolatileBlocked, - [CANCELER_TAUNTED] = CancelerTaunted, - [CANCELER_IMPRISONED] = CancelerImprisoned, - [CANCELER_CONFUSED] = CancelerConfused, - [CANCELER_PARALYZED] = CancelerParalyzed, - [CANCELER_INFATUATION] = CancelerInfatuation, - [CANCELER_BIDE] = CancelerBide, - [CANCELER_Z_MOVES] = CancelerZMoves, - [CANCELER_CHOICE_LOCK] = CancelerChoiceLock, - [CANCELER_CALLSUBMOVE] = CancelerCallSubmove, - [CANCELER_THAW] = CancelerThaw, - [CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo, - [CANCELER_ATTACKSTRING] = CancelerAttackstring, - [CANCELER_PPDEDUCTION] = CancelerPPDeduction, - [CANCELER_SKY_BATTLE] = CancelerSkyBattle, - [CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal, - [CANCELER_FOCUS_PRE_GEN5] = CancelerFocusPreGen5, - [CANCELER_MOVE_FAILURE] = CancelerMoveFailure, - [CANCELER_POWDER_STATUS] = CancelerPowderStatus, - [CANCELER_PRIORITY_BLOCK] = CancelerPriorityBlock, - [CANCELER_PROTEAN] = CancelerProtean, - [CANCELER_EXPLODING_DAMP] = CancelerExplodingDamp, - [CANCELER_EXPLOSION] = CancelerExplosion, - [CANCELER_CHARGING] = CancelerCharging, - [CANCELER_NO_TARGET] = CancelerNoTarget, - [CANCELER_TOOK_ATTACK] = CancelerTookAttack, - [CANCELER_TARGET_FAILURE] = CancelerTargetFailure, - [CANCELER_NOT_FULLY_PROTECTED] = CancelerNotFullyProtected, - [CANCELER_MULTIHIT_MOVES] = CancelerMultihitMoves, -}; - -enum MoveCanceler AtkCanceler_MoveSuccessOrder(void) -{ - enum MoveCanceler effect = MOVE_STEP_SUCCESS; - - struct BattleContext ctx = {0}; - ctx.battlerAtk = gBattlerAttacker; - ctx.battlerDef = gBattlerTarget; - ctx.move = gCurrentMove; - ctx.chosenMove = gChosenMove; - ctx.abilityAtk = GetBattlerAbility(ctx.battlerAtk); - ctx.holdEffectAtk = GetBattlerHoldEffect(ctx.battlerAtk); - - while (gBattleStruct->eventState.atkCanceler < CANCELER_END && effect == MOVE_STEP_SUCCESS) - { - effect = sMoveSuccessOrderCancelers[gBattleStruct->eventState.atkCanceler](&ctx); - gBattleStruct->unableToUseMove = (effect == MOVE_STEP_FAILURE); - if (effect != MOVE_STEP_PAUSE) - gBattleStruct->eventState.atkCanceler++; - } - - return effect; -} - bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { u32 i, playerId, flankId; @@ -4170,45 +2575,6 @@ const u8 *AbsorbedByFlashFire(u32 battlerDef) } } -static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx) -{ - return TryMagicBounce(ctx) || TryMagicCoat(ctx); -} - -static bool32 TryMagicBounce(struct BattleContext *ctx) -{ - if (!MoveCanBeBouncedBack(ctx->move)) - return FALSE; - - if (gBattleStruct->magicBounceActive || gBattleStruct->bouncedMoveIsUsed) - return FALSE; - - if (ctx->abilityDef != ABILITY_MAGIC_BOUNCE) - return FALSE; - - gBattleStruct->magicBounceActive = TRUE; - gBattleStruct->moveBouncer = ctx->battlerDef; - - return TRUE; -} - -static bool32 TryMagicCoat(struct BattleContext *ctx) -{ - if (!MoveCanBeBouncedBack(ctx->move) || gBattleStruct->magicBounceActive) // Magic Bounce has precedence over magic coat - return FALSE; - - if (gBattleStruct->magicCoatActive || gBattleStruct->bouncedMoveIsUsed) - return FALSE; - - if (!gProtectStructs[ctx->battlerDef].bounceMove) - return FALSE; - - gBattleStruct->magicCoatActive = TRUE; - gBattleStruct->moveBouncer = ctx->battlerDef; - - return TRUE; -} - static u32 GetFirstBattlerOnSide(enum BattleSide side) { for (u32 battler = 0; battler < gBattlersCount; battler++) @@ -11279,16 +9645,6 @@ u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) return battler; } -static void SetRandomMultiHitCounter() -{ - if (GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_LOADED_DICE) - gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5); - else if (GetConfig(CONFIG_MULTI_HIT_CHANCE) >= GEN_5) - gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3); // 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits. - else - gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1); // 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits. -} - void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon) { gBattleMons[battler].level = GetMonData(mon, MON_DATA_LEVEL); @@ -12424,183 +10780,6 @@ bool32 IsAffectedByPowderMove(u32 battler, enum Ability ability, enum HoldEffect return TRUE; } -static enum Move GetMirrorMoveMove(void) -{ - s32 i, validMovesCount; - enum Move move = MOVE_NONE; - u16 validMoves[MAX_BATTLERS_COUNT] = {0}; - - for (validMovesCount = 0, i = 0; i < gBattlersCount; i++) - { - if (i != gBattlerAttacker) - { - move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i]; - if (move != MOVE_NONE && move != MOVE_UNAVAILABLE) - { - validMoves[validMovesCount] = move; - validMovesCount++; - } - } - } - - move = gBattleStruct->lastTakenMove[gBattlerAttacker]; - if ((move == MOVE_NONE || move == MOVE_UNAVAILABLE) && validMovesCount != 0) - move = validMoves[Random() % validMovesCount]; - - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move)) - move = GetTypeBasedZMove(move); - - return move; -} - -static bool32 InvalidMetronomeMove(u32 move) -{ - return GetMoveEffect(move) == EFFECT_PLACEHOLDER - || IsMoveMetronomeBanned(move); -} - -static enum Move GetMetronomeMove(void) -{ - enum Move move = MOVE_NONE; - -#if B_METRONOME_MOVES >= GEN_9 - u32 moveCount = MOVES_COUNT_GEN9; -#elif B_METRONOME_MOVES >= GEN_8 - u32 moveCount = MOVES_COUNT_GEN8; -#elif B_METRONOME_MOVES >= GEN_7 - u32 moveCount = MOVES_COUNT_GEN7; -#elif B_METRONOME_MOVES >= GEN_6 - u32 moveCount = MOVES_COUNT_GEN6; -#elif B_METRONOME_MOVES >= GEN_5 - u32 moveCount = MOVES_COUNT_GEN5; -#elif B_METRONOME_MOVES >= GEN_4 - u32 moveCount = MOVES_COUNT_GEN4; -#elif B_METRONOME_MOVES >= GEN_3 - u32 moveCount = MOVES_COUNT_GEN3; -#elif B_METRONOME_MOVES >= GEN_2 - u32 moveCount = MOVES_COUNT_GEN2; -#else - u32 moveCount = MOVES_COUNT_GEN1; -#endif - - move = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove); - return move; -} - -static enum Move GetAssistMove(void) -{ - enum Move move = MOVE_NONE; - s32 chooseableMovesNo = 0; - struct Pokemon *party; - enum Move *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES); - - if (validMoves != NULL) - { - party = GetBattlerParty(gBattlerAttacker); - - for (u32 monId = 0; monId < PARTY_SIZE; monId++) - { - if (monId == gBattlerPartyIndexes[gBattlerAttacker]) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) - continue; - - for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) - { - enum Move move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); - - if (IsMoveAssistBanned(move)) - continue; - - validMoves[chooseableMovesNo++] = move; - } - } - } - - if (chooseableMovesNo) - move = validMoves[Random() % chooseableMovesNo]; - - TRY_FREE_AND_SET_NULL(validMoves); - - return move; -} - -enum Move GetNaturePowerMove(u32 battler) -{ - enum Move move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower; - if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) - move = MOVE_MOONBLAST; - else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) - move = MOVE_THUNDERBOLT; - else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) - move = MOVE_ENERGY_BALL; - else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) - move = MOVE_PSYCHIC; - else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE) - move = B_NATURE_POWER_MOVES >= GEN_4 ? MOVE_TRI_ATTACK : MOVE_SWIFT; - - return move; -} - -static enum Move GetSleepTalkMove(void) -{ - enum Move move = MOVE_NONE; - - u32 i, unusableMovesBits = 0, movePosition; - - if (GetBattlerAbility(gBattlerAttacker) != ABILITY_COMATOSE - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)) - return move; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i]) - || gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect) - unusableMovesBits |= (1 << (i)); - } - - unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM)); - if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen - return move; - - // Set Sleep Talk as used move, so it works with Last Resort. - gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; - do - { - movePosition = MOD(Random(), MAX_MON_MOVES); - } while ((1u << movePosition) & unusableMovesBits); - - move = gBattleMons[gBattlerAttacker].moves[movePosition]; - gCurrMovePos = movePosition; - - return move; -} - -static enum Move GetCopycatMove(void) -{ - if (gLastUsedMove == MOVE_NONE - || gLastUsedMove == MOVE_UNAVAILABLE - || IsMoveCopycatBanned(gLastUsedMove) - || IsZMove(gLastUsedMove)) - return MOVE_NONE; - - return gLastUsedMove; -} - -static enum Move GetMeFirstMove(void) -{ - enum Move move = GetChosenMoveFromPosition(gBattlerTarget); - - if (IsBattleMoveStatus(move) - || IsMoveMeFirstBanned(move) - || HasBattlerActedThisTurn(gBattlerTarget)) - return MOVE_NONE; - - return move; -} - void RemoveAbilityFlags(u32 battler) { gBattleMons[battler].volatiles.unburdenActive = FALSE;