diff --git a/include/battle_util.h b/include/battle_util.h index dd7a908e57..9eca2dacd7 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -23,17 +23,12 @@ #define MOVE_LIMITATION_PLACEHOLDER (1 << 15) #define MOVE_LIMITATIONS_ALL 0xFFFF -enum NonVolatileStatus +// Switches between simulated battle calc and actual battle combat +enum FunctionCallOption { - STATUS_CHECK_TRIGGER, - STATUS_RUN_SCRIPT, -}; - -enum AbilityEffectOptions -{ - ABILITY_CHECK_TRIGGER, - ABILITY_CHECK_TRIGGER_AI, - ABILITY_RUN_SCRIPT, + CHECK_TRIGGER, // Check the function without running scripts / setting any flags. + AI_CHECK, // Check the function without running scripts / setting any flags. Same as CHECK_TRIGGER but only used when additional data has to be fetched during ai calcs + RUN_SCRIPT, // Used during actual combat where a script has to be run / flags need to be set }; enum MoveAbsorbed @@ -245,8 +240,8 @@ enum MoveCanceller AtkCanceller_MoveSuccessOrder(void); void SetAtkCancellerForCalledMove(void); bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility); -bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum AbilityEffectOptions option); -bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum AbilityEffectOptions option); +bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option); +bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum FunctionCallOption option); u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg); bool32 TryPrimalReversion(u32 battler); bool32 IsNeutralizingGasOnField(void); @@ -360,7 +355,7 @@ bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 ability); bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, u32 abilityDef); bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, u32 abilityDef); bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef); -bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects secondaryMoveEffect, enum NonVolatileStatus option); +bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects secondaryMoveEffect, enum FunctionCallOption option); bool32 CanBeConfused(u32 battler); bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag); u32 GetBattlerAffectionHearts(u32 battler); @@ -406,5 +401,7 @@ bool32 AreAnyHazardsOnSide(u32 side); void RemoveAllHazardsFromField(u32 side); bool32 IsHazardOnSideAndClear(u32 side, enum Hazards hazardType); void RemoveHazardFromField(u32 side, enum Hazards hazardType); +bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option); +u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); #endif // GUARD_BATTLE_UTIL_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index e66c77ce87..f3976fae4a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -547,18 +547,27 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); } +#define BYPASSES_ACCURACY_CALC 101 // 101 indicates for ai that the move will always hit static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 move) { u32 accuracy; u32 abilityAtk = aiData->abilities[battlerAtk]; u32 abilityDef = aiData->abilities[battlerDef]; - if (abilityAtk == ABILITY_NO_GUARD || abilityDef == ABILITY_NO_GUARD || GetMoveAccuracy(move) == 0) // Moves with accuracy 0 or no guard ability always hit. - accuracy = 100; + if (CanMoveSkipAccuracyCalc(battlerAtk, battlerDef, abilityAtk, abilityDef, move, AI_CHECK)) + { + accuracy = BYPASSES_ACCURACY_CALC; + } else + { accuracy = GetTotalAccuracy(battlerAtk, battlerDef, move, abilityAtk, abilityDef, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); + // Cap normal accuracy at 100 for ai calcs. + // Done for comparison with moves that bypass accuracy checks (will be seen as 101 for ai calcs)) + accuracy = (accuracy > 100) ? 100 : accuracy; + } return accuracy; } +#undef BYPASSES_ACCURACY_CALC static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 weather) { @@ -672,8 +681,8 @@ static u32 PpStallReduction(u32 move, u32 battlerAtk) u32 abilityAtk = ABILITY_NONE; u32 abilityDef = GetPartyMonAbility(&gPlayerParty[partyIndex]); u32 moveType = GetBattleMoveType(move); // Probably doesn't handle dynamic types right now - if (CanAbilityAbsorbMove(battlerAtk, tempBattleMonIndex, abilityDef, move, moveType, ABILITY_CHECK_TRIGGER) - || CanAbilityBlockMove(battlerAtk, tempBattleMonIndex, abilityAtk, abilityDef, move, ABILITY_CHECK_TRIGGER) + if (CanAbilityAbsorbMove(battlerAtk, tempBattleMonIndex, abilityDef, move, moveType, CHECK_TRIGGER) + || CanAbilityBlockMove(battlerAtk, tempBattleMonIndex, abilityAtk, abilityDef, move, CHECK_TRIGGER) || (CalcPartyMonTypeEffectivenessMultiplier(move, species, abilityDef) == 0)) { totalStallValue += currentStallValue; @@ -1105,10 +1114,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) // check non-user target if (!(moveTarget & MOVE_TARGET_USER)) { - if (CanAbilityBlockMove(battlerAtk, battlerDef, abilityAtk, abilityDef, move, ABILITY_CHECK_TRIGGER_AI)) + if (CanAbilityBlockMove(battlerAtk, battlerDef, abilityAtk, abilityDef, move, AI_CHECK)) RETURN_SCORE_MINUS(20); - if (CanAbilityAbsorbMove(battlerAtk, battlerDef, abilityDef, move, moveType, ABILITY_CHECK_TRIGGER_AI)) + if (CanAbilityAbsorbMove(battlerAtk, battlerDef, abilityDef, move, moveType, AI_CHECK)) RETURN_SCORE_MINUS(20); switch (abilityDef) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index e7bcf00e7b..393eb054bf 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1010,8 +1010,8 @@ static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) bool32 moveAffectsTarget = TRUE; if (lastUsedMove != MOVE_NONE && (AI_GetMoveEffectiveness(lastUsedMove, battler, opposingBattler) == UQ_4_12(0.0) - || CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], lastUsedMove, GetMoveType(lastUsedMove), ABILITY_CHECK_TRIGGER) - || CanAbilityBlockMove(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], lastUsedMove, ABILITY_CHECK_TRIGGER))) + || CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], lastUsedMove, GetMoveType(lastUsedMove), AI_CHECK) + || CanAbilityBlockMove(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], lastUsedMove, AI_CHECK))) moveAffectsTarget = FALSE; if (HOLD_EFFECT_CHOICE(holdEffect) && IsBattlerItemEnabled(battler)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 149aada871..6d2fb0afce 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -514,16 +514,16 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx) partnerDefAbility = aiData->abilities[BATTLE_PARTNER(ctx->battlerDef)]; } - if (CanAbilityBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, battlerDefAbility, ctx->move, ABILITY_CHECK_TRIGGER)) + if (CanAbilityBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, battlerDefAbility, ctx->move, AI_CHECK)) return TRUE; - if (CanAbilityAbsorbMove(ctx->battlerAtk, ctx->battlerDef, battlerDefAbility, ctx->move, ctx->moveType, ABILITY_CHECK_TRIGGER)) + if (CanAbilityAbsorbMove(ctx->battlerAtk, ctx->battlerDef, battlerDefAbility, ctx->move, ctx->moveType, AI_CHECK)) return TRUE; // Limited to Lighning Rod and Storm Drain because otherwise the AI would consider Water Absorb, etc... if (partnerDefAbility == ABILITY_LIGHTNING_ROD || partnerDefAbility == ABILITY_STORM_DRAIN) { - if (CanAbilityAbsorbMove(ctx->battlerAtk, BATTLE_PARTNER(ctx->battlerDef), partnerDefAbility, ctx->move, ctx->moveType, ABILITY_CHECK_TRIGGER)) + if (CanAbilityAbsorbMove(ctx->battlerAtk, BATTLE_PARTNER(ctx->battlerDef), partnerDefAbility, ctx->move, ctx->moveType, AI_CHECK)) return TRUE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 567bdd85ee..f148129b2d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1248,7 +1248,7 @@ static void Cmd_attackcanceler(void) GetBattlerAbility(gBattlerAttacker), abilityDef, gCurrentMove, - ABILITY_RUN_SCRIPT)) + RUN_SCRIPT)) return; if (GetMoveNonVolatileStatus(gCurrentMove) == MOVE_EFFECT_PARALYSIS) @@ -1259,7 +1259,7 @@ static void Cmd_attackcanceler(void) abilityDef, gCurrentMove, GetBattleMoveType(gCurrentMove), - ABILITY_RUN_SCRIPT)) + RUN_SCRIPT)) return; } @@ -1432,7 +1432,7 @@ static void JumpIfMoveFailed(u32 adder, u32 move, u32 moveType, const u8 *failIn GetBattlerAbility(gBattlerTarget), move, moveType, - ABILITY_RUN_SCRIPT)) + RUN_SCRIPT)) return; } @@ -1464,211 +1464,6 @@ static bool32 JumpIfMoveAffectedByProtect(u32 move, u32 battler, u32 shouldJump, return affected; } -static bool32 AccuracyCalcHelper(u32 move, u32 battler) -{ - bool32 effect = FALSE; - u32 ability = ABILITY_NONE; - enum BattleMoveEffects moveEffect = GetMoveEffect(move); - u32 nonVolatileStatus = GetMoveNonVolatileStatus(move); - - if ((gStatuses3[battler] & STATUS3_ALWAYS_HITS && gDisableStructs[battler].battlerWithSureHit == gBattlerAttacker) - || (B_TOXIC_NEVER_MISS >= GEN_6 - && nonVolatileStatus == MOVE_EFFECT_TOXIC - && IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_POISON)) - || gStatuses4[battler] & STATUS4_GLAIVE_RUSH) - { - effect = TRUE; - } - // If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits. - else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD - && !(gStatuses3[battler] & STATUS3_COMMANDER) - && (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == SKY_DROP_NO_TARGET)) - { - effect = TRUE; - ability = ABILITY_NO_GUARD; - } - // If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits. - else if (GetBattlerAbility(battler) == ABILITY_NO_GUARD - && (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battler] == SKY_DROP_NO_TARGET)) - { - effect = TRUE; - ability = ABILITY_NO_GUARD; - } - // If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits. - else if (gStatuses3[battler] & STATUS3_TELEKINESIS - && !(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE) - && moveEffect != EFFECT_OHKO - && moveEffect != EFFECT_SHEER_COLD) - { - effect = TRUE; - } - else if (gBattleStruct->battlerState[battler].pursuitTarget) - { - effect = TRUE; - } - else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)) - { - effect = TRUE; - } - else if ((gStatuses3[battler] & STATUS3_COMMANDER) - || (gStatuses3[battler] & STATUS3_PHANTOM_FORCE) - || ((gStatuses3[battler] & STATUS3_ON_AIR) && !(MoveDamagesAirborne(move) || MoveDamagesAirborneDoubleDamage(move))) - || ((gStatuses3[battler] & STATUS3_UNDERGROUND) && !MoveDamagesUnderground(move)) - || ((gStatuses3[battler] & STATUS3_UNDERWATER) && !MoveDamagesUnderWater(move))) - { - gBattleStruct->moveResultFlags[battler] |= MOVE_RESULT_MISSED; - effect = TRUE; - } - else if (B_MINIMIZE_DMG_ACC >= GEN_6 - && (gStatuses3[battler] & STATUS3_MINIMIZED) - && MoveIncreasesPowerToMinimizedTargets(move)) - { - effect = TRUE; - } - else if (GetMoveAccuracy(move) == 0) - { - effect = TRUE; - } - - if (!effect && HasWeatherEffect()) - { - if (MoveAlwaysHitsInRain(move) && IsBattlerWeatherAffected(battler, B_WEATHER_RAIN)) - effect = TRUE; - else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && MoveAlwaysHitsInHailSnow(move)) - effect = TRUE; - - if (effect) - return effect; - } - - if (ability != ABILITY_NONE) - RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD); - - return effect; -} - -u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect) -{ - u32 calc, moveAcc; - s8 buff, accStage, evasionStage; - u32 atkParam = GetBattlerHoldEffectParam(battlerAtk); - u32 defParam = GetBattlerHoldEffectParam(battlerDef); - u32 atkAlly = BATTLE_PARTNER(battlerAtk); - u32 atkAllyAbility = GetBattlerAbility(atkAlly); - - gPotentialItemEffectBattler = battlerDef; - accStage = gBattleMons[battlerAtk].statStages[STAT_ACC]; - evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION]; - if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE || atkAbility == ABILITY_MINDS_EYE - || (B_ILLUMINATE_EFFECT >= GEN_9 && atkAbility == ABILITY_ILLUMINATE)) - evasionStage = DEFAULT_STAT_STAGE; - if (MoveIgnoresDefenseEvasionStages(move)) - evasionStage = DEFAULT_STAT_STAGE; - if (defAbility == ABILITY_UNAWARE) - accStage = DEFAULT_STAT_STAGE; - - if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED) - buff = accStage; - else - buff = accStage + DEFAULT_STAT_STAGE - evasionStage; - - if (buff < MIN_STAT_STAGE) - buff = MIN_STAT_STAGE; - if (buff > MAX_STAT_STAGE) - buff = MAX_STAT_STAGE; - - moveAcc = GetMoveAccuracy(move); - // Check Thunder and Hurricane on sunny weather. - if (IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && MoveHas50AccuracyInSun(move)) - moveAcc = 50; - // Check Wonder Skin. - if (defAbility == ABILITY_WONDER_SKIN && IsBattleMoveStatus(move) && moveAcc > 50) - moveAcc = 50; - - calc = gAccuracyStageRatios[buff].dividend * moveAcc; - calc /= gAccuracyStageRatios[buff].divisor; - - // Attacker's ability - switch (atkAbility) - { - case ABILITY_COMPOUND_EYES: - calc = (calc * 130) / 100; // 1.3 compound eyes boost - break; - case ABILITY_VICTORY_STAR: - calc = (calc * 110) / 100; // 1.1 victory star boost - break; - case ABILITY_HUSTLE: - if (IsBattleMovePhysical(move)) - calc = (calc * 80) / 100; // 1.2 hustle loss - break; - } - - // Target's ability - switch (defAbility) - { - case ABILITY_SAND_VEIL: - if (HasWeatherEffect() && gBattleWeather & B_WEATHER_SANDSTORM) - calc = (calc * 80) / 100; // 1.2 sand veil loss - break; - case ABILITY_SNOW_CLOAK: - if (HasWeatherEffect() && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) - calc = (calc * 80) / 100; // 1.2 snow cloak loss - break; - case ABILITY_TANGLED_FEET: - if (gBattleMons[battlerDef].volatiles.confusionTurns) - calc = (calc * 50) / 100; // 1.5 tangled feet loss - break; - } - - // Attacker's ally's ability - switch (atkAllyAbility) - { - case ABILITY_VICTORY_STAR: - if (IsBattlerAlive(atkAlly)) - calc = (calc * 110) / 100; // 1.1 ally's victory star boost - break; - } - - // Attacker's hold effect - switch (atkHoldEffect) - { - case HOLD_EFFECT_WIDE_LENS: - calc = (calc * (100 + atkParam)) / 100; - break; - case HOLD_EFFECT_ZOOM_LENS: - if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)) - calc = (calc * (100 + atkParam)) / 100; - break; - } - - // Target's hold effect - switch (defHoldEffect) - { - case HOLD_EFFECT_EVASION_UP: - calc = (calc * (100 - defParam)) / 100; - break; - } - - if (gBattleStruct->battlerState[battlerAtk].usedMicleBerry) - { - if (atkAbility == ABILITY_RIPEN) - calc = (calc * 140) / 100; // ripen gives 40% acc boost - else - calc = (calc * 120) / 100; // 20% acc boost - } - - if (gFieldStatuses & STATUS_FIELD_GRAVITY) - calc = (calc * 5) / 3; // 1.66 Gravity acc boost - - if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerDef) == AFFECTION_FIVE_HEARTS) - calc = (calc * 90) / 100; - - if (HasWeatherEffect() && gBattleWeather & B_WEATHER_FOG) - calc = (calc * 60) / 100; // modified by 3/5 - - return calc; -} - static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move) { if (move == ACC_CURR_MOVE) @@ -1696,7 +1491,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u GetBattlerAbility(gBattlerTarget), gCurrentMove, GetBattleMoveType(gCurrentMove), - ABILITY_RUN_SCRIPT); + RUN_SCRIPT); } } else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT @@ -1726,10 +1521,11 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u continue; numTargets++; - if (JumpIfMoveAffectedByProtect(move, battlerDef, FALSE, failInstr) || AccuracyCalcHelper(move, battlerDef)) + u32 abilityDef = GetBattlerAbility(battlerDef); + if (JumpIfMoveAffectedByProtect(move, battlerDef, FALSE, failInstr) + || CanMoveSkipAccuracyCalc(gBattlerAttacker, battlerDef, abilityAtk, abilityDef, move, RUN_SCRIPT)) continue; - u32 abilityDef = GetBattlerAbility(battlerDef); u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); u32 accuracy = GetTotalAccuracy(gBattlerAttacker, battlerDef, @@ -3337,7 +3133,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai GetBattlerAbility(gBattlerAttacker), battlerAbility, gBattleScripting.moveEffect, - STATUS_CHECK_TRIGGER)) + CHECK_TRIGGER)) SetNonVolatileStatusCondition(gEffectBattler, gBattleScripting.moveEffect, TRIGGER_ON_MOVE); break; case MOVE_EFFECT_CONFUSION: @@ -12548,7 +12344,7 @@ static void Cmd_trynonvolatilestatus(void) GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget), GetMoveNonVolatileStatus(gCurrentMove), - STATUS_RUN_SCRIPT)) + RUN_SCRIPT)) canInflictStatus = FALSE; if (canInflictStatus && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) @@ -13008,7 +12804,7 @@ static void Cmd_checknonvolatiletrigger(void) GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget), cmd->nonVolatile, - STATUS_CHECK_TRIGGER)) + CHECK_TRIGGER)) gBattlescriptCurrInstr = cmd->failInstr; else if (DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) gBattlescriptCurrInstr = cmd->failInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 8664fb417d..d882f28fa4 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -65,8 +65,8 @@ static void SetRandomMultiHitCounter(); static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static bool32 CanBeInfinitelyConfused(u32 battler); static bool32 IsAnyTargetAffected(u32 battlerAtk); -static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum NonVolatileStatus option); -static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum NonVolatileStatus option); +static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option); +static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent); @@ -2520,13 +2520,13 @@ static enum MoveCanceller CancellerMultiTargetMoves(void) gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; gBattleStruct->noResultString[battlerDef] = TRUE; } - else if (CanAbilityBlockMove(gBattlerAttacker, battlerDef, abilityAtk, abilityDef, gCurrentMove, ABILITY_CHECK_TRIGGER) + else if (CanAbilityBlockMove(gBattlerAttacker, battlerDef, abilityAtk, abilityDef, gCurrentMove, CHECK_TRIGGER) || (IsBattlerTerrainAffected(gBattlerAttacker, STATUS_FIELD_PSYCHIC_TERRAIN) && GetBattleMovePriority(gBattlerAttacker, abilityAtk, gCurrentMove) > 0)) { gBattleStruct->moveResultFlags[battlerDef] = 0; gBattleStruct->noResultString[battlerDef] = TRUE; } - else if (CanAbilityAbsorbMove(gBattlerAttacker, battlerDef, abilityDef, gCurrentMove, GetBattleMoveType(gCurrentMove), ABILITY_CHECK_TRIGGER)) + else if (CanAbilityAbsorbMove(gBattlerAttacker, battlerDef, abilityDef, gCurrentMove, GetBattleMoveType(gCurrentMove), CHECK_TRIGGER)) { gBattleStruct->moveResultFlags[battlerDef] = 0; gBattleStruct->noResultString[battlerDef] = DO_ACCURACY_CHECK; @@ -2962,13 +2962,13 @@ static void ChooseStatBoostAnimation(u32 battler) #undef ANIM_STAT_ACC #undef ANIM_STAT_EVASION -bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum AbilityEffectOptions option) +bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option) { const u8 *battleScriptBlocksMove = NULL; u32 battlerAbility = battlerDef; s32 atkPriority = 0; - if (option == ABILITY_CHECK_TRIGGER_AI) + if (option == AI_CHECK) atkPriority = GetBattleMovePriority(battlerAtk, abilityAtk, move); else atkPriority = GetChosenMovePriority(battlerAtk, abilityAtk); @@ -3018,7 +3018,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a && BlocksPrankster(move, battlerAtk, battlerDef, TRUE) && !(IsBattleMoveStatus(move) && (abilityDef == ABILITY_MAGIC_BOUNCE || gProtectStructs[battlerDef].bounceMove))) { - if (option == ABILITY_RUN_SCRIPT && !IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move))) + if (option == RUN_SCRIPT && !IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move))) CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster; @@ -3031,7 +3031,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a && IsBattlerAlive(partnerDef) && !IsBattlerAlly(battlerAtk, partnerDef)) { - if (option == ABILITY_CHECK_TRIGGER_AI) + if (option == AI_CHECK) abilityDef = gAiLogicData->abilities[partnerDef]; else abilityDef = GetBattlerAbility(partnerDef); @@ -3053,7 +3053,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a if (battleScriptBlocksMove == NULL) return FALSE; - if (option == ABILITY_RUN_SCRIPT) + if (option == RUN_SCRIPT) { gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. gLastUsedAbility = abilityDef; @@ -3065,7 +3065,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a return TRUE; } -bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum AbilityEffectOptions option) +bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum FunctionCallOption option) { enum MoveAbsorbed effect = MOVE_ABSORBED_BY_NO_ABILITY; const u8 *battleScript = NULL; @@ -3139,7 +3139,7 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 break; } - if (effect == MOVE_ABSORBED_BY_NO_ABILITY || option != ABILITY_RUN_SCRIPT) + if (effect == MOVE_ABSORBED_BY_NO_ABILITY || option != RUN_SCRIPT) return effect; switch (effect) @@ -5122,7 +5122,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gLastUsedAbility, GetBattlerAbility(gBattlerAttacker), gBattleStruct->synchronizeMoveEffect, - STATUS_CHECK_TRIGGER)) + CHECK_TRIGGER)) { if (B_SYNCHRONIZE_TOXIC < GEN_5 && gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; @@ -5152,7 +5152,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gLastUsedAbility, GetBattlerAbility(gBattlerAttacker), gBattleStruct->synchronizeMoveEffect, - STATUS_CHECK_TRIGGER)) + CHECK_TRIGGER)) { if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; @@ -5499,24 +5499,26 @@ bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, u32 abilityDef, enum SleepClau if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause) return FALSE; - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - ABILITY_NONE, // attacker ability does not matter - abilityDef, - MOVE_EFFECT_SLEEP, // also covers yawn - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + ABILITY_NONE, // attacker ability does not matter + abilityDef, + MOVE_EFFECT_SLEEP, // also covers yawn + CHECK_TRIGGER)) return TRUE; return FALSE; } bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef) { - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - abilityAtk, - abilityDef, - MOVE_EFFECT_TOXIC, // also covers poison - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + abilityAtk, + abilityDef, + MOVE_EFFECT_TOXIC, // also covers poison + CHECK_TRIGGER)) return TRUE; return FALSE; } @@ -5524,36 +5526,39 @@ bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 ability // TODO: check order of battlerAtk and battlerDef bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - ABILITY_NONE, // attacker ability does not matter - abilityDef, - MOVE_EFFECT_BURN, - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + ABILITY_NONE, // attacker ability does not matter + abilityDef, + MOVE_EFFECT_BURN, + CHECK_TRIGGER)) return TRUE; return FALSE; } bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - ABILITY_NONE, // attacker ability does not matter - abilityDef, - MOVE_EFFECT_PARALYSIS, - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + ABILITY_NONE, // attacker ability does not matter + abilityDef, + MOVE_EFFECT_PARALYSIS, + CHECK_TRIGGER)) return TRUE; return FALSE; } bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - ABILITY_NONE, // attacker ability does not matter - abilityDef, - MOVE_EFFECT_FREEZE, - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + ABILITY_NONE, // attacker ability does not matter + abilityDef, + MOVE_EFFECT_FREEZE, + CHECK_TRIGGER)) return TRUE; return FALSE; } @@ -5561,17 +5566,18 @@ bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, u32 abilityDef) // Unused, technically also redundant bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { - if (CanSetNonVolatileStatus(battlerAtk, - battlerDef, - ABILITY_NONE, // attacker ability does not matter - abilityDef, - MOVE_EFFECT_FREEZE_OR_FROSTBITE, // also covers frostbite - STATUS_CHECK_TRIGGER)) + if (CanSetNonVolatileStatus( + battlerAtk, + battlerDef, + ABILITY_NONE, // attacker ability does not matter + abilityDef, + MOVE_EFFECT_FREEZE_OR_FROSTBITE, // also covers frostbite + CHECK_TRIGGER)) return TRUE; return FALSE; } -bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects effect, enum NonVolatileStatus option) +bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects effect, enum FunctionCallOption option) { const u8 *battleScript = NULL; u32 sideBattler = ABILITY_NONE; @@ -5614,7 +5620,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u { battleScript = BattleScript_NotAffected; } - else if (option == STATUS_RUN_SCRIPT // Check only important during battle execution for moves + else if (option == RUN_SCRIPT // Check only important during battle execution for moves && CalcTypeEffectivenessMultiplierHelper(gCurrentMove, GetBattleMoveType(gCurrentMove), battlerAtk, battlerDef, abilityDef, TRUE) && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) { @@ -5741,11 +5747,11 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u return TRUE; } -static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum NonVolatileStatus option) +static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option) { if (battleScript != NULL) { - if (option == STATUS_RUN_SCRIPT) + if (option == RUN_SCRIPT) { if (battleScript != BattleScript_NotAffected) gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FAILED; @@ -5766,17 +5772,17 @@ static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abi return FALSE; } -static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum NonVolatileStatus option) +static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option) { // Can freely sleep own partner if (IsDoubleBattle() && IsSleepClauseEnabled() && IsBattlerAlly(battlerAtk, battlerDef)) { - if (option == STATUS_RUN_SCRIPT) + if (option == RUN_SCRIPT) gBattleStruct->battlerState[battlerDef].sleepClauseEffectExempt = TRUE; return FALSE; } - if (option == STATUS_RUN_SCRIPT) + if (option == RUN_SCRIPT) gBattleStruct->battlerState[battlerDef].sleepClauseEffectExempt = FALSE; // Can't sleep if clause is active otherwise if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef))) @@ -9767,7 +9773,7 @@ uq4_12_t GetOverworldTypeEffectiveness(struct Pokemon *mon, u8 moveType) MulByTypeEffectiveness(&ctx, &modifier, type2); if ((modifier <= UQ_4_12(1.0) && abilityDef == ABILITY_WONDER_GUARD) - || CanAbilityAbsorbMove(0, 0, abilityDef, MOVE_NONE, moveType, ABILITY_CHECK_TRIGGER)) + || CanAbilityAbsorbMove(0, 0, abilityDef, MOVE_NONE, moveType, CHECK_TRIGGER)) modifier = UQ_4_12(0.0); return modifier; @@ -11158,8 +11164,8 @@ static inline bool32 DoesBattlerHaveAbilityImmunity(u32 battlerAtk, u32 battlerD { u32 abilityDef = GetBattlerAbility(battlerDef); - return CanAbilityBlockMove(battlerAtk, battlerDef, GetBattlerAbility(battlerAtk), abilityDef, gCurrentMove, ABILITY_CHECK_TRIGGER) - || CanAbilityAbsorbMove(battlerAtk, battlerDef, abilityDef, gCurrentMove, moveType, ABILITY_CHECK_TRIGGER); + return CanAbilityBlockMove(battlerAtk, battlerDef, GetBattlerAbility(battlerAtk), abilityDef, gCurrentMove, CHECK_TRIGGER) + || CanAbilityAbsorbMove(battlerAtk, battlerDef, abilityDef, gCurrentMove, moveType, CHECK_TRIGGER); } bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef) @@ -11329,11 +11335,11 @@ void UpdateStallMons(void) u32 moveType = GetBattleMoveType(gCurrentMove); // Probably doesn't handle dynamic move types right now u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); u32 abilityDef = GetBattlerAbility(gBattlerTarget); - if (CanAbilityAbsorbMove(gBattlerAttacker, gBattlerTarget, abilityDef, gCurrentMove, moveType, ABILITY_CHECK_TRIGGER)) + if (CanAbilityAbsorbMove(gBattlerAttacker, gBattlerTarget, abilityDef, gCurrentMove, moveType, CHECK_TRIGGER)) { gAiBattleData->playerStallMons[gBattlerPartyIndexes[gBattlerTarget]]++; } - else if (CanAbilityBlockMove(gBattlerAttacker, gBattlerTarget, abilityAtk, abilityDef, gCurrentMove, ABILITY_CHECK_TRIGGER)) + else if (CanAbilityBlockMove(gBattlerAttacker, gBattlerTarget, abilityAtk, abilityDef, gCurrentMove, CHECK_TRIGGER)) { gAiBattleData->playerStallMons[gBattlerPartyIndexes[gBattlerTarget]]++; } @@ -11521,3 +11527,212 @@ void RemoveHazardFromField(u32 side, enum Hazards hazardType) i++; } } + +bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option) +{ + bool32 effect = FALSE; + u32 ability = ABILITY_NONE; + enum BattleMoveEffects moveEffect = GetMoveEffect(move); + u32 nonVolatileStatus = GetMoveNonVolatileStatus(move); + + if ((gStatuses3[battlerDef] & STATUS3_ALWAYS_HITS && gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk) + || (B_TOXIC_NEVER_MISS >= GEN_6 && nonVolatileStatus == MOVE_EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON)) + || gStatuses4[battlerDef] & STATUS4_GLAIVE_RUSH) + { + effect = TRUE; + } + // If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits. + else if (abilityAtk == ABILITY_NO_GUARD + && !(gStatuses3[battlerDef] & STATUS3_COMMANDER) + && (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET)) + { + effect = TRUE; + ability = ABILITY_NO_GUARD; + } + // If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits. + else if (abilityDef == ABILITY_NO_GUARD + && (moveEffect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[battlerDef] == SKY_DROP_NO_TARGET)) + { + effect = TRUE; + ability = ABILITY_NO_GUARD; + } + // If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits. + else if (gStatuses3[battlerDef] & STATUS3_TELEKINESIS + && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE) + && moveEffect != EFFECT_OHKO + && moveEffect != EFFECT_SHEER_COLD) + { + effect = TRUE; + } + else if (gBattleStruct->battlerState[battlerDef].pursuitTarget) + { + effect = TRUE; + } + else if (GetActiveGimmick(battlerAtk) == GIMMICK_Z_MOVE && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) + { + effect = TRUE; + } + else if ((gStatuses3[battlerDef] & STATUS3_COMMANDER) + || (gStatuses3[battlerDef] & STATUS3_PHANTOM_FORCE) + || ((gStatuses3[battlerDef] & STATUS3_ON_AIR) && !(MoveDamagesAirborne(move) || MoveDamagesAirborneDoubleDamage(move))) + || ((gStatuses3[battlerDef] & STATUS3_UNDERGROUND) && !MoveDamagesUnderground(move)) + || ((gStatuses3[battlerDef] & STATUS3_UNDERWATER) && !MoveDamagesUnderWater(move))) + { + if (option == RUN_SCRIPT) + { + gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_MISSED; + effect = TRUE; + } + else + { + effect = FALSE; + } + } + else if (B_MINIMIZE_DMG_ACC >= GEN_6 + && (gStatuses3[battlerDef] & STATUS3_MINIMIZED) + && MoveIncreasesPowerToMinimizedTargets(move)) + { + effect = TRUE; + } + else if (GetMoveAccuracy(move) == 0) + { + effect = TRUE; + } + + if (!effect && HasWeatherEffect()) + { + if (MoveAlwaysHitsInRain(move) && IsBattlerWeatherAffected(battlerDef, B_WEATHER_RAIN)) + effect = TRUE; + else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && MoveAlwaysHitsInHailSnow(move)) + effect = TRUE; + + if (effect) + return effect; + } + + if (ability != ABILITY_NONE && option == RUN_SCRIPT) + RecordAbilityBattle(battlerAtk, ABILITY_NO_GUARD); + + return effect; +} + +u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect) +{ + u32 calc, moveAcc; + s8 buff, accStage, evasionStage; + u32 atkParam = GetBattlerHoldEffectParam(battlerAtk); + u32 defParam = GetBattlerHoldEffectParam(battlerDef); + + gPotentialItemEffectBattler = battlerDef; + accStage = gBattleMons[battlerAtk].statStages[STAT_ACC]; + evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION]; + if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE || atkAbility == ABILITY_MINDS_EYE + || (B_ILLUMINATE_EFFECT >= GEN_9 && atkAbility == ABILITY_ILLUMINATE)) + evasionStage = DEFAULT_STAT_STAGE; + if (MoveIgnoresDefenseEvasionStages(move)) + evasionStage = DEFAULT_STAT_STAGE; + if (defAbility == ABILITY_UNAWARE) + accStage = DEFAULT_STAT_STAGE; + + if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED) + buff = accStage; + else + buff = accStage + DEFAULT_STAT_STAGE - evasionStage; + + if (buff < MIN_STAT_STAGE) + buff = MIN_STAT_STAGE; + if (buff > MAX_STAT_STAGE) + buff = MAX_STAT_STAGE; + + moveAcc = GetMoveAccuracy(move); + // Check Thunder and Hurricane on sunny weather. + if (IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && MoveHas50AccuracyInSun(move)) + moveAcc = 50; + // Check Wonder Skin. + if (defAbility == ABILITY_WONDER_SKIN && IsBattleMoveStatus(move) && moveAcc > 50) + moveAcc = 50; + + calc = gAccuracyStageRatios[buff].dividend * moveAcc; + calc /= gAccuracyStageRatios[buff].divisor; + + // Attacker's ability + switch (atkAbility) + { + case ABILITY_COMPOUND_EYES: + calc = (calc * 130) / 100; // 1.3 compound eyes boost + break; + case ABILITY_VICTORY_STAR: + calc = (calc * 110) / 100; // 1.1 victory star boost + break; + case ABILITY_HUSTLE: + if (IsBattleMovePhysical(move)) + calc = (calc * 80) / 100; // 1.2 hustle loss + break; + } + + // Target's ability + switch (defAbility) + { + case ABILITY_SAND_VEIL: + if (HasWeatherEffect() && gBattleWeather & B_WEATHER_SANDSTORM) + calc = (calc * 80) / 100; // 1.2 sand veil loss + break; + case ABILITY_SNOW_CLOAK: + if (HasWeatherEffect() && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) + calc = (calc * 80) / 100; // 1.2 snow cloak loss + break; + case ABILITY_TANGLED_FEET: + if (gBattleMons[battlerDef].volatiles.confusionTurns) + calc = (calc * 50) / 100; // 1.5 tangled feet loss + break; + } + + // Attacker's ally's ability + u32 atkAlly = BATTLE_PARTNER(battlerAtk); + switch (GetBattlerAbility(atkAlly)) + { + case ABILITY_VICTORY_STAR: + if (IsBattlerAlive(atkAlly)) + calc = (calc * 110) / 100; // 1.1 ally's victory star boost + break; + } + + // Attacker's hold effect + switch (atkHoldEffect) + { + case HOLD_EFFECT_WIDE_LENS: + calc = (calc * (100 + atkParam)) / 100; + break; + case HOLD_EFFECT_ZOOM_LENS: + if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)) + calc = (calc * (100 + atkParam)) / 100; + break; + } + + // Target's hold effect + switch (defHoldEffect) + { + case HOLD_EFFECT_EVASION_UP: + calc = (calc * (100 - defParam)) / 100; + break; + } + + if (gBattleStruct->battlerState[battlerAtk].usedMicleBerry) + { + if (atkAbility == ABILITY_RIPEN) + calc = (calc * 140) / 100; // ripen gives 40% acc boost + else + calc = (calc * 120) / 100; // 20% acc boost + } + + if (gFieldStatuses & STATUS_FIELD_GRAVITY) + calc = (calc * 5) / 3; // 1.66 Gravity acc boost + + if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerDef) == AFFECTION_FIVE_HEARTS) + calc = (calc * 90) / 100; + + if (HasWeatherEffect() && gBattleWeather & B_WEATHER_FOG) + calc = (calc * 60) / 100; // modified by 3/5 + + return calc; +}