final ai optimizations

This commit is contained in:
DizzyEggg 2023-09-14 11:05:00 +02:00
parent 5ea66f3c0c
commit bb9501449c
9 changed files with 79 additions and 109 deletions

View File

@ -32,10 +32,10 @@ u32 GetHealthPercentage(u32 battler);
bool32 IsBattlerTrapped(u32 battler, bool32 switching);
u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
bool32 CanMoveFaintBattler(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits);
bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits);
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
s32 AI_GetAbility(u32 battlerId);
u16 AI_GetHoldEffect(u32 battlerId);
u32 AI_GetHoldEffect(u32 battlerId);
u32 AI_GetMoveAccuracy(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move);
u32 AI_GetWeather(struct AiLogicData *aiData);

View File

@ -57,7 +57,7 @@ void SwitchInClearSetData(u32 battler);
void FaintClearSetData(u32 battler);
void BattleTurnPassed(void);
u8 IsRunningFromBattleImpossible(u32 battler);
void SwitchPartyOrder(u8 battlerId);
void SwitchPartyOrder(u32 battlerId);
void SwapTurnOrder(u8 id1, u8 id2);
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect);
u32 GetBattlerTotalSpeedStat(u32 battler);
@ -69,7 +69,7 @@ u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves);
void RunBattleScriptCommands_PopCallbacksStack(void);
void RunBattleScriptCommands(void);
void SpecialStatusesClear(void);
void SetTypeBeforeUsingMove(u16 move, u32 battlerAtk);
void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk);
bool32 IsWildMonSmart(void);
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
void ModifyPersonalityForNature(u32 *personality, u32 newNature);

View File

@ -16,8 +16,9 @@ struct StatFractions
u8 divisor;
};
s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk);
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility);
s32 GetInverseCritChance(u32 battlerAtk, u32 battlerDef, u32 move);
s32 GetCritHitChance(s32 critChanceIndex);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
u8 GetBattlerTurnOrderNum(u8 battlerId);
bool32 NoAliveMonsForEitherParty(void);

View File

@ -174,7 +174,7 @@ u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef);
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 recordAbilities);
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities);
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
uq4_12_t GetTypeModifier(u32 atkType, u32 defType);
s32 GetStealthHazardDamage(u8 hazardType, u32 battler);

View File

@ -698,12 +698,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
u32 weather;
u16 predictedMove = aiData->predictedMoves[battlerDef];
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType);
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score;
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType);
// check non-user target
@ -3757,10 +3755,9 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
if (gLastMoves[battlerDef] != MOVE_NONE
&& gLastMoves[battlerDef] != 0xFFFF)
{
/* TODO predicted moves
if (gLastMoves[battlerDef] == predictedMove)
score += 3;
else */if (CanMoveFaintBattler(gLastMoves[battlerDef], battlerDef, battlerAtk, 1))
else if (CanTargetMoveFaintAi(gLastMoves[battlerDef], battlerDef, battlerAtk, 1))
score += 2; //Disable move that can kill attacker
}
}

View File

@ -723,7 +723,7 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split)
{
SetTypeBeforeUsingMove(moves[i], attacker);
GET_MOVE_TYPE(moves[i], moveType);
if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, FALSE) != 0)
if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, AI_DATA->abilities[target], FALSE) != 0)
usable |= gBitTable[i];
}
}
@ -731,41 +731,6 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split)
return (usable == 0);
}
static bool32 AI_GetIfCrit(u32 move, u32 battlerAtk, u32 battlerDef)
{
bool32 isCrit;
switch (CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE))
{
case -1:
case 0:
default:
isCrit = FALSE;
break;
case 1:
if (gBattleMoves[move].highCritRatio && (Random() % 5 == 0))
isCrit = TRUE;
else
isCrit = FALSE;
break;
case 2:
if (gBattleMoves[move].highCritRatio && (Random() % 2 == 0))
isCrit = TRUE;
else if (!(gBattleMoves[move].highCritRatio) && (Random() % 4) == 0)
isCrit = TRUE;
else
isCrit = FALSE;
break;
case -2:
case 3:
case 4:
isCrit = TRUE;
break;
}
return isCrit;
}
// To save computation time this function has 2 variants. One saves, sets and restores battlers, while the other doesn't.
s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower)
{
@ -776,8 +741,9 @@ s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *type
s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather)
{
s32 dmg, moveType, critDmg, normalDmg, fixedBasePower, n;
s32 dmg, moveType;
uq4_12_t effectivenessMultiplier;
struct AiLogicData *aiData = AI_DATA;
SetBattlerData(battlerAtk);
SetBattlerData(battlerDef);
@ -797,14 +763,12 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType);
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE);
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
if (gBattleMoves[move].power)
{
s32 critChance;
struct AiLogicData *aiData = AI_DATA;
s32 critChanceIndex, normalDmg, fixedBasePower, n;
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType);
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
// Certain moves like Rollout calculate damage based on values which change during the move execution, but before calling dmg calc.
switch (gBattleMoves[move].effect)
{
@ -823,15 +787,22 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
effectivenessMultiplier, weather, FALSE,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
critChanceIndex = CalcCritChanceStageArgs(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
if (critChanceIndex > 1) // Consider crit damage only if a move has at least +1 crit chance
{
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
effectivenessMultiplier, weather, TRUE,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
if (critChance == -1)
dmg = normalDmg;
else
u32 critChance = GetCritHitChance(critChanceIndex);
// With critChance getting closer to 1, dmg gets closer to critDmg.
dmg = (critDmg + normalDmg * (critChance - 1)) / (critChance);
}
else
{
dmg = normalDmg;
}
if (!gBattleStruct->zmove.active)
{
@ -1056,7 +1027,7 @@ uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
gBattleStruct->dynamicMoveType = 0;
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType);
typeEffectiveness = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE);
typeEffectiveness = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], FALSE);
RestoreBattlerData(battlerAtk);
RestoreBattlerData(battlerDef);
@ -1191,18 +1162,14 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
return FALSE;
}
bool32 CanMoveFaintBattler(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits)
bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits)
{
s32 i, dmg;
u8 effectiveness;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
if (move != MOVE_NONE
&& move != 0xFFFF
&& !(unusable & gBitTable[i])
&& AI_CalcDamageSaveBattlers(move, battlerDef, battlerAtk, &effectiveness, FALSE) >= gBattleMons[battlerAtk].hp)
return TRUE;
u32 indexSlot = GetMoveSlot(GetMovesArray(battlerDef), move);
if (indexSlot < MAX_MON_MOVES)
{
if (GetNoOfHitsToKO(AI_DATA->simulatedDmg[battlerDef][battlerAtk][indexSlot], gBattleMons[battlerAtk].hp) <= nHits)
return TRUE;
}
return FALSE;
}
@ -1282,7 +1249,7 @@ s32 AI_GetAbility(u32 battlerId)
return ABILITY_NONE; // Unknown.
}
u16 AI_GetHoldEffect(u32 battlerId)
u32 AI_GetHoldEffect(u32 battlerId)
{
u32 holdEffect;

View File

@ -751,7 +751,7 @@ static void SetAllPlayersBerryData(void)
{
s32 numPlayers;
struct BattleEnigmaBerry *src;
u8 battler;
u32 battler;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
@ -2268,7 +2268,7 @@ static void EndLinkBattleInSteps(void)
case 2:
if (!gPaletteFade.active)
{
u8 battlerCount;
u32 battlerCount;
gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag();
@ -4021,11 +4021,10 @@ u8 IsRunningFromBattleImpossible(u32 battler)
return BATTLE_RUN_SUCCESS;
}
void SwitchPartyOrder(u8 battler)
void SwitchPartyOrder(u32 battler)
{
s32 i;
u8 partyId1;
u8 partyId2;
u32 partyId1, partyId2;
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders));
@ -5589,7 +5588,7 @@ void RunBattleScriptCommands(void)
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
void SetTypeBeforeUsingMove(u16 move, u32 battlerAtk)
void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
{
u32 moveType, ateType, attackerAbility;
u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
@ -5728,8 +5727,8 @@ void SetTypeBeforeUsingMove(u16 move, u32 battlerAtk)
// var8001 - var8007: stat changes
void SetTotemBoost(void)
{
u8 battler = gSpecialVar_0x8000;
u8 i;
u32 battler = gSpecialVar_0x8000;
u32 i;
for (i = 0; i < (NUM_BATTLE_STATS - 1); i++)
{

View File

@ -1752,10 +1752,11 @@ static void Cmd_accuracycheck(void)
{
CMD_ARGS(const u8 *failInstr, u16 move);
u16 type, move = cmd->move;
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u16 gBattlerAttackerAbility = GetBattlerAbility(gBattlerAttacker);
u8 gBattlerAttackerHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
u32 type, move = cmd->move;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
if (move == ACC_CURR_MOVE)
move = gCurrentMove;
@ -1771,7 +1772,7 @@ static void Cmd_accuracycheck(void)
}
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn
&& (gBattlerAttackerAbility == ABILITY_SKILL_LINK || gBattlerAttackerHoldEffect == HOLD_EFFECT_LOADED_DICE
&& (abilityAtk == ABILITY_SKILL_LINK || holdEffectAtk == HOLD_EFFECT_LOADED_DICE
|| !(gBattleMoves[move].effect == EFFECT_TRIPLE_KICK || gBattleMoves[move].effect == EFFECT_POPULATION_BOMB))))
{
// No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb
@ -1791,16 +1792,16 @@ static void Cmd_accuracycheck(void)
gBattlerAttacker,
gBattlerTarget,
move,
gBattlerAttackerAbility,
GetBattlerAbility(gBattlerTarget),
gBattlerAttackerHoldEffect,
abilityAtk,
abilityDef,
holdEffectAtk,
GetBattlerHoldEffect(gBattlerTarget, TRUE)
);
if (!RandomPercentage(RNG_ACCURACY, accuracy))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
if (gBattlerAttackerHoldEffect == HOLD_EFFECT_BLUNDER_POLICY)
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
@ -1810,7 +1811,7 @@ static void Cmd_accuracycheck(void)
gBattleCommunication[MISS_TYPE] = B_MSG_MISSED;
if (gBattleMoves[move].power)
CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, TRUE);
CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, abilityDef, TRUE);
}
JumpIfMoveFailed(7, move);
}
@ -1907,24 +1908,15 @@ static void Cmd_ppreduce(void)
#endif // B_CRIT_CHANCE
#define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD))
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility)
s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk)
{
s32 critChance = 0;
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT
|| gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT)
|| gStatuses3[battlerAtk] & STATUS3_CANT_SCORE_A_CRIT)
{
critChance = -1;
}
else if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
{
if (recordAbility)
RecordAbilityBattle(battlerDef, abilityDef);
critChance = -1;
}
else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS
|| gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)
@ -1934,27 +1926,41 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
}
else
{
critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0)
critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0)
+ (gBattleMoves[gCurrentMove].highCritRatio)
+ (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS)
+ 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY)
+ 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[battlerAtk].species == SPECIES_CHANSEY)
+ 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk)
#if B_AFFECTION_MECHANICS == TRUE
+ 2 * (GetBattlerFriendshipScore(gBattlerAttacker) >= FRIENDSHIP_200_TO_254)
+ 2 * (GetBattlerFriendshipScore(battlerAtk) >= FRIENDSHIP_200_TO_254)
#endif
+ (abilityAtk == ABILITY_SUPER_LUCK);
if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
{
if (recordAbility && critChance >= 2) // Record ability only if move had at least +1 chance to get a crit.
RecordAbilityBattle(battlerDef, abilityDef);
critChance = -1;
}
if (critChance >= ARRAY_COUNT(sCriticalHitChance))
critChance = ARRAY_COUNT(sCriticalHitChance) - 1;
}
return critChance;
}
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility)
{
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
return CalcCritChanceStageArgs(battlerAtk, battlerDef, move, recordAbility, abilityAtk, abilityDef, holdEffectAtk);
}
#undef BENEFITS_FROM_LEEK
s32 GetInverseCritChance(u32 battlerAtk, u32 battlerDef, u32 move)
s32 GetCritHitChance(s32 critChanceIndex)
{
s32 critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE);
if (critChanceIndex < 0)
return -1;
else
@ -2005,7 +2011,7 @@ static void Cmd_typecalc(void)
u8 moveType;
GET_MOVE_TYPE(gCurrentMove, moveType);
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, TRUE);
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE);
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -4368,7 +4368,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
{
move = gBattleMons[i].moves[j];
GET_MOVE_TYPE(move, moveType);
if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, FALSE) >= UQ_4_12(2.0))
if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, ABILITY_ANTICIPATION, FALSE) >= UQ_4_12(2.0))
{
effect++;
break;
@ -9827,7 +9827,8 @@ static u32 GetWeather(void)
s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags)
{
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor,
updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags), GetWeather());
updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), updateFlags),
GetWeather());
}
// for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once
@ -9985,13 +9986,12 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
return modifier;
}
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 recordAbilities)
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities)
{
uq4_12_t modifier = UQ_4_12(1.0);
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
{
u32 defAbility = GetBattlerAbility(defAbility);
modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
if (gBattleMoves[move].effect == EFFECT_TWO_TYPED_MOVE)
modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);