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); bool32 IsBattlerTrapped(u32 battler, bool32 switching);
u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered); u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk); 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); bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
s32 AI_GetAbility(u32 battlerId); s32 AI_GetAbility(u32 battlerId);
u16 AI_GetHoldEffect(u32 battlerId); u32 AI_GetHoldEffect(u32 battlerId);
u32 AI_GetMoveAccuracy(u32 battlerAtk, u32 battlerDef, u32 move); u32 AI_GetMoveAccuracy(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move); bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move);
u32 AI_GetWeather(struct AiLogicData *aiData); u32 AI_GetWeather(struct AiLogicData *aiData);

View File

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

View File

@ -16,8 +16,9 @@ struct StatFractions
u8 divisor; 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 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); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
u8 GetBattlerTurnOrderNum(u8 battlerId); u8 GetBattlerTurnOrderNum(u8 battlerId);
bool32 NoAliveMonsForEitherParty(void); 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 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, 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); 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 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
uq4_12_t GetTypeModifier(u32 atkType, u32 defType); uq4_12_t GetTypeModifier(u32 atkType, u32 defType);
s32 GetStealthHazardDamage(u8 hazardType, u32 battler); 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; u32 weather;
u16 predictedMove = aiData->predictedMoves[battlerDef]; u16 predictedMove = aiData->predictedMoves[battlerDef];
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType);
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score; return score;
SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType); GET_MOVE_TYPE(move, moveType);
// check non-user target // 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 if (gLastMoves[battlerDef] != MOVE_NONE
&& gLastMoves[battlerDef] != 0xFFFF) && gLastMoves[battlerDef] != 0xFFFF)
{ {
/* TODO predicted moves
if (gLastMoves[battlerDef] == predictedMove) if (gLastMoves[battlerDef] == predictedMove)
score += 3; 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 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); SetTypeBeforeUsingMove(moves[i], attacker);
GET_MOVE_TYPE(moves[i], moveType); 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]; usable |= gBitTable[i];
} }
} }
@ -731,41 +731,6 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split)
return (usable == 0); 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. // 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) 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 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; uq4_12_t effectivenessMultiplier;
struct AiLogicData *aiData = AI_DATA;
SetBattlerData(battlerAtk); SetBattlerData(battlerAtk);
SetBattlerData(battlerDef); SetBattlerData(battlerDef);
@ -797,14 +763,12 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
SetTypeBeforeUsingMove(move, battlerAtk); SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType); 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) if (gBattleMoves[move].power)
{ {
s32 critChance; s32 critChanceIndex, normalDmg, fixedBasePower, n;
struct AiLogicData *aiData = AI_DATA;
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType); 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. // Certain moves like Rollout calculate damage based on values which change during the move execution, but before calling dmg calc.
switch (gBattleMoves[move].effect) switch (gBattleMoves[move].effect)
{ {
@ -823,15 +787,22 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
effectivenessMultiplier, weather, FALSE, effectivenessMultiplier, weather, FALSE,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[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, effectivenessMultiplier, weather, TRUE,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
u32 critChance = GetCritHitChance(critChanceIndex);
if (critChance == -1) // With critChance getting closer to 1, dmg gets closer to critDmg.
dmg = normalDmg;
else
dmg = (critDmg + normalDmg * (critChance - 1)) / (critChance); dmg = (critDmg + normalDmg * (critChance - 1)) / (critChance);
}
else
{
dmg = normalDmg;
}
if (!gBattleStruct->zmove.active) if (!gBattleStruct->zmove.active)
{ {
@ -1056,7 +1027,7 @@ uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
gBattleStruct->dynamicMoveType = 0; gBattleStruct->dynamicMoveType = 0;
SetTypeBeforeUsingMove(move, battlerAtk); SetTypeBeforeUsingMove(move, battlerAtk);
GET_MOVE_TYPE(move, moveType); 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(battlerAtk);
RestoreBattlerData(battlerDef); RestoreBattlerData(battlerDef);
@ -1191,18 +1162,14 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
return FALSE; 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; u32 indexSlot = GetMoveSlot(GetMovesArray(battlerDef), move);
u8 effectiveness; if (indexSlot < MAX_MON_MOVES)
u32 unusable = AI_DATA->moveLimitations[battlerDef]; {
if (GetNoOfHitsToKO(AI_DATA->simulatedDmg[battlerDef][battlerAtk][indexSlot], gBattleMons[battlerAtk].hp) <= nHits)
if (move != MOVE_NONE
&& move != 0xFFFF
&& !(unusable & gBitTable[i])
&& AI_CalcDamageSaveBattlers(move, battlerDef, battlerAtk, &effectiveness, FALSE) >= gBattleMons[battlerAtk].hp)
return TRUE; return TRUE;
}
return FALSE; return FALSE;
} }
@ -1282,7 +1249,7 @@ s32 AI_GetAbility(u32 battlerId)
return ABILITY_NONE; // Unknown. return ABILITY_NONE; // Unknown.
} }
u16 AI_GetHoldEffect(u32 battlerId) u32 AI_GetHoldEffect(u32 battlerId)
{ {
u32 holdEffect; u32 holdEffect;

View File

@ -751,7 +751,7 @@ static void SetAllPlayersBerryData(void)
{ {
s32 numPlayers; s32 numPlayers;
struct BattleEnigmaBerry *src; struct BattleEnigmaBerry *src;
u8 battler; u32 battler;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI) if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{ {
@ -2268,7 +2268,7 @@ static void EndLinkBattleInSteps(void)
case 2: case 2:
if (!gPaletteFade.active) if (!gPaletteFade.active)
{ {
u8 battlerCount; u32 battlerCount;
gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag(); gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag();
@ -4021,11 +4021,10 @@ u8 IsRunningFromBattleImpossible(u32 battler)
return BATTLE_RUN_SUCCESS; return BATTLE_RUN_SUCCESS;
} }
void SwitchPartyOrder(u8 battler) void SwitchPartyOrder(u32 battler)
{ {
s32 i; s32 i;
u8 partyId1; u32 partyId1, partyId2;
u8 partyId2;
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)); gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders));
@ -5589,7 +5588,7 @@ void RunBattleScriptCommands(void)
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
} }
void SetTypeBeforeUsingMove(u16 move, u32 battlerAtk) void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
{ {
u32 moveType, ateType, attackerAbility; u32 moveType, ateType, attackerAbility;
u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE); u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
@ -5728,8 +5727,8 @@ void SetTypeBeforeUsingMove(u16 move, u32 battlerAtk)
// var8001 - var8007: stat changes // var8001 - var8007: stat changes
void SetTotemBoost(void) void SetTotemBoost(void)
{ {
u8 battler = gSpecialVar_0x8000; u32 battler = gSpecialVar_0x8000;
u8 i; u32 i;
for (i = 0; i < (NUM_BATTLE_STATS - 1); 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); CMD_ARGS(const u8 *failInstr, u16 move);
u16 type, move = cmd->move; u32 type, move = cmd->move;
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u16 gBattlerAttackerAbility = GetBattlerAbility(gBattlerAttacker); u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u8 gBattlerAttackerHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
if (move == ACC_CURR_MOVE) if (move == ACC_CURR_MOVE)
move = gCurrentMove; move = gCurrentMove;
@ -1771,7 +1772,7 @@ static void Cmd_accuracycheck(void)
} }
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn || (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)))) || !(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 // 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, gBattlerAttacker,
gBattlerTarget, gBattlerTarget,
move, move,
gBattlerAttackerAbility, abilityAtk,
GetBattlerAbility(gBattlerTarget), abilityDef,
gBattlerAttackerHoldEffect, holdEffectAtk,
GetBattlerHoldEffect(gBattlerTarget, TRUE) GetBattlerHoldEffect(gBattlerTarget, TRUE)
); );
if (!RandomPercentage(RNG_ACCURACY, accuracy)) if (!RandomPercentage(RNG_ACCURACY, accuracy))
{ {
gMoveResultFlags |= MOVE_RESULT_MISSED; 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 gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
@ -1810,7 +1811,7 @@ static void Cmd_accuracycheck(void)
gBattleCommunication[MISS_TYPE] = B_MSG_MISSED; gBattleCommunication[MISS_TYPE] = B_MSG_MISSED;
if (gBattleMoves[move].power) if (gBattleMoves[move].power)
CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, TRUE); CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, abilityDef, TRUE);
} }
JumpIfMoveFailed(7, move); JumpIfMoveFailed(7, move);
} }
@ -1907,24 +1908,15 @@ static void Cmd_ppreduce(void)
#endif // B_CRIT_CHANCE #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)) #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; s32 critChance = 0;
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT
|| gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) || gStatuses3[battlerAtk] & STATUS3_CANT_SCORE_A_CRIT)
{ {
critChance = -1; 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 else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS
|| gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT || gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)
@ -1934,27 +1926,41 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
} }
else else
{ {
critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0)
+ (gBattleMoves[gCurrentMove].highCritRatio) + (gBattleMoves[gCurrentMove].highCritRatio)
+ (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS) + (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) + 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk)
#if B_AFFECTION_MECHANICS == TRUE #if B_AFFECTION_MECHANICS == TRUE
+ 2 * (GetBattlerFriendshipScore(gBattlerAttacker) >= FRIENDSHIP_200_TO_254) + 2 * (GetBattlerFriendshipScore(battlerAtk) >= FRIENDSHIP_200_TO_254)
#endif #endif
+ (abilityAtk == ABILITY_SUPER_LUCK); + (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)) if (critChance >= ARRAY_COUNT(sCriticalHitChance))
critChance = ARRAY_COUNT(sCriticalHitChance) - 1; critChance = ARRAY_COUNT(sCriticalHitChance) - 1;
} }
return critChance; 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 #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) if (critChanceIndex < 0)
return -1; return -1;
else else
@ -2005,7 +2011,7 @@ static void Cmd_typecalc(void)
u8 moveType; u8 moveType;
GET_MOVE_TYPE(gCurrentMove, moveType); GET_MOVE_TYPE(gCurrentMove, moveType);
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, TRUE); CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE);
gBattlescriptCurrInstr = cmd->nextInstr; 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]; move = gBattleMons[i].moves[j];
GET_MOVE_TYPE(move, moveType); 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++; effect++;
break; 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) 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, 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 // 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; 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); uq4_12_t modifier = UQ_4_12(1.0);
if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY)
{ {
u32 defAbility = GetBattlerAbility(defAbility);
modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);
if (gBattleMoves[move].effect == EFFECT_TWO_TYPED_MOVE) if (gBattleMoves[move].effect == EFFECT_TWO_TYPED_MOVE)
modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility);