Changes AccuracyCalcHelper into CanMoveSkipAccuracyCalc (#7303)

This commit is contained in:
Alex 2025-07-09 23:33:48 +02:00 committed by GitHub
parent b5a13d5e7f
commit d15c490223
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 316 additions and 299 deletions

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}