Fixed AI not handling type effectiveness beyond x8 (#6127)

This commit is contained in:
Eduardo Quezada 2025-01-28 15:16:10 -03:00 committed by GitHub
parent a34a3cad36
commit 927bb8f833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 54 additions and 98 deletions

View File

@ -350,7 +350,7 @@ struct AiLogicData
u16 partnerMove;
u16 speedStats[MAX_BATTLERS_COUNT]; // Speed stats for all battles, calculated only once, same way as damages
struct SimulatedDamage simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
uq4_12_t effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 moveAccuracy[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 moveLimitations[MAX_BATTLERS_COUNT];
u8 monToSwitchInId[MAX_BATTLERS_COUNT]; // ID of the mon to switch in.

View File

@ -101,15 +101,14 @@ bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility);
bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect);
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category);
s32 AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType);
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType);
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType);
bool32 AI_IsDamagedByRecoil(u32 battler);
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef);
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
u16 *GetMovesArray(u32 battler);
bool32 IsConfusionMoveEffect(u32 moveEffect);
bool32 HasMove(u32 battlerId, u32 move);

View File

@ -14,16 +14,6 @@
#define AI_TYPE2_USER 3
#define AI_TYPE_MOVE 4
// type effectiveness
#define AI_EFFECTIVENESS_x8 7
#define AI_EFFECTIVENESS_x4 6
#define AI_EFFECTIVENESS_x2 5
#define AI_EFFECTIVENESS_x1 4
#define AI_EFFECTIVENESS_x0_5 3
#define AI_EFFECTIVENESS_x0_25 2
#define AI_EFFECTIVENESS_x0_125 1
#define AI_EFFECTIVENESS_x0 0
// AI Flags. Most run specific functions to update score, new flags are used for internal logic in other scripts
// See docs/ai_flags.md for more details.
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0) // AI will avoid using moves that are likely to fail or be ineffective in the current situation.

View File

@ -421,7 +421,7 @@ static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
struct SimulatedDamage dmg = {0};
u8 effectiveness = AI_EFFECTIVENESS_x0;
uq4_12_t effectiveness = Q_4_12(0.0);
move = moves[moveIndex];
if (move != MOVE_NONE
@ -722,14 +722,14 @@ static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u3
void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef)
{
struct BattlePokemon switchoutCandidate = gBattleMons[battlerDef];
struct SimulatedDamage simulatedDamageSwitchout[4];
u8 effectivenessSwitchout[4];
u8 moveAccuracySwitchout[4];
struct SimulatedDamage simulatedDamageSwitchout[MAX_MON_MOVES];
uq4_12_t effectivenessSwitchout[MAX_MON_MOVES];
u8 moveAccuracySwitchout[MAX_MON_MOVES];
struct BattlePokemon switchinCandidate;
struct SimulatedDamage simulatedDamageSwitchin[4];
u8 effectivenessSwitchin[4];
u8 moveAccuracySwitchin[4];
struct SimulatedDamage simulatedDamageSwitchin[MAX_MON_MOVES];
uq4_12_t effectivenessSwitchin[MAX_MON_MOVES];
u8 moveAccuracySwitchin[MAX_MON_MOVES];
struct Pokemon *party = GetBattlerParty(battlerDef);
struct BattlePokemon *savedBattleMons = AllocSaveBattleMons();
@ -839,7 +839,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
s32 moveType;
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
struct AiLogicData *aiData = AI_DATA;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
u32 i;
u32 weather;
@ -863,14 +863,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (gBattleStruct->battlerState[battlerDef].commandingDondozo)
RETURN_SCORE_MINUS(20);
// check if negates type
switch (effectiveness)
if (effectiveness == UQ_4_12(0.0))
{
case AI_EFFECTIVENESS_x0:
RETURN_SCORE_MINUS(20);
break;
case AI_EFFECTIVENESS_x0_125:
case AI_EFFECTIVENESS_x0_25:
}
else if (effectiveness < UQ_4_12(0.5))
{
switch (moveEffect)
{
case EFFECT_FIXED_DAMAGE_ARG:
@ -889,7 +887,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
default:
RETURN_SCORE_MINUS(10);
}
break;
}
// check non-user target
@ -922,7 +919,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case ABILITY_WONDER_GUARD:
if (effectiveness < AI_EFFECTIVENESS_x2)
if (effectiveness < UQ_4_12(2.0))
RETURN_SCORE_MINUS(20);
break;
case ABILITY_JUSTIFIED:
@ -1117,7 +1114,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE))
ADJUST_SCORE(-2);
if (effectiveness == AI_EFFECTIVENESS_x0)
if (effectiveness == UQ_4_12(0.0))
{
ADJUST_SCORE(-10);
}
@ -1452,7 +1449,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_FIXED_DAMAGE_ARG:
case EFFECT_FOCUS_PUNCH:
// AI_CBM_HighRiskForDamage
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2)
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
ADJUST_SCORE(-10);
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
|| IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
@ -2840,7 +2837,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_MAGNET_RISE:
if (IsBattlerGrounded(battlerAtk)
&& (HasMove(battlerAtkPartner, MOVE_EARTHQUAKE) || HasMove(battlerAtkPartner, MOVE_MAGNITUDE))
&& (AI_GetMoveEffectiveness(MOVE_EARTHQUAKE, battlerAtk, battlerAtkPartner) != AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
&& (AI_GetMoveEffectiveness(MOVE_EARTHQUAKE, battlerAtk, battlerAtkPartner) != UQ_4_12(0.0))) // Doesn't resist ground move
{
RETURN_SCORE_PLUS(DECENT_EFFECT); // partner has earthquake or magnitude -> good idea to use magnet rise
}
@ -3309,7 +3306,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
u32 moveEffect = GetMoveEffect(move);
struct AiLogicData *aiData = AI_DATA;
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex];
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex];
s32 score = 0;
u32 predictedMove = aiData->lastUsedMove[battlerDef];
@ -3323,7 +3320,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
moveEffect = EFFECT_PROTECT;
// check status move preference
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IsBattleMoveStatus(move) && effectiveness != AI_EFFECTIVENESS_x0)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IsBattleMoveStatus(move) && effectiveness != UQ_4_12(0.0))
ADJUST_SCORE(10);
// check thawing moves
@ -3346,7 +3343,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
IncreaseSleepScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_ABSORB:
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT && effectiveness >= AI_EFFECTIVENESS_x1)
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT && effectiveness >= UQ_4_12(1.0))
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_EXPLOSION:
@ -4488,7 +4485,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_MAGNET_RISE:
if (IsBattlerGrounded(battlerAtk) && HasDamagingMoveOfType(battlerDef, TYPE_ELECTRIC)
&& !(AI_GetMoveEffectiveness(MOVE_EARTHQUAKE, battlerDef, battlerAtk) == AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
&& !(effectiveness == UQ_4_12(0.0))) // Doesn't resist ground move
{
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker goes first
{
@ -4506,7 +4503,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_CAMOUFLAGE:
if (predictedMove != MOVE_NONE && AI_IsFaster(battlerAtk, battlerDef, move) // Attacker goes first
&& !IsBattleMoveStatus(move) && AI_GetMoveEffectiveness(predictedMove, battlerDef, battlerAtk) != AI_EFFECTIVENESS_x0)
&& !IsBattleMoveStatus(move) && effectiveness != UQ_4_12(0.0))
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_TOXIC_THREAD:
@ -5408,7 +5405,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
u32 aiHazardFlags = gSideStatuses[GetBattlerSide(battlerAtk)] & (SIDE_STATUS_HAZARDS_ANY);
u32 moveEffect = gMovesInfo[move].effect;
struct AiLogicData *aiData = AI_DATA;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
// Switch benefit
switch (moveEffect)
@ -5429,7 +5426,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(DECENT_EFFECT);
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CHECK_BAD_MOVE)
{
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2)
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
ADJUST_SCORE(10);
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
|| IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])

View File

@ -88,11 +88,12 @@ static inline bool32 SetSwitchinAndSwitch(u32 battler, u32 switchinId)
static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
{
//Variable initialization
u8 opposingPosition, atkType1, atkType2, defType1, defType2, effectiveness;
u8 opposingPosition, atkType1, atkType2, defType1, defType2;
s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0;
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = AI_DATA->abilities[battler], opposingBattler, weather = AI_GetWeather(AI_DATA);
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
u16 typeEffectiveness = UQ_4_12(1.0), aiMoveEffect; //baseline typing damage
uq4_12_t effectiveness;
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
@ -133,7 +134,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
if (!IsBattleMoveStatus(aiMove))
{
// Check if mon has a super effective move
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= UQ_4_12(2.0))
hasSuperEffectiveMove = TRUE;
// Get maximum damage mon can deal
@ -254,8 +255,8 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
aiMove = gBattleMons[battler].moves[moveIndex];
if ((AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > AI_EFFECTIVENESS_x0
|| AI_GetMoveEffectiveness(aiMove, battler, opposingPartner) > AI_EFFECTIVENESS_x0)
if ((AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > UQ_4_12(0.0)
|| AI_GetMoveEffectiveness(aiMove, battler, opposingPartner) > UQ_4_12(0.0))
&& aiMove != MOVE_NONE)
return FALSE;
}
@ -265,7 +266,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
aiMove = gBattleMons[battler].moves[moveIndex];
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > AI_EFFECTIVENESS_x0 && aiMove != MOVE_NONE)
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > UQ_4_12(0.0) && aiMove != MOVE_NONE)
return FALSE;
}
}
@ -294,7 +295,7 @@ static bool32 FindMonThatHitsWonderGuard(u32 battler)
move = gBattleMons[battler].moves[i];
if (move != MOVE_NONE)
{
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
return FALSE;
}
}
@ -319,7 +320,7 @@ static bool32 FindMonThatHitsWonderGuard(u32 battler)
if (move != MOVE_NONE)
{
// Found a mon
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
return SetSwitchinAndSwitch(battler, i);
}
}
@ -527,7 +528,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
|| monAbility == ABILITY_EARLY_BIRD)
|| holdEffect == (HOLD_EFFECT_CURE_SLP | HOLD_EFFECT_CURE_STATUS)
|| HasMove(battler, MOVE_SLEEP_TALK)
|| (HasMoveEffect(battler, MOVE_SNORE) && AI_GetMoveEffectiveness(MOVE_SNORE, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
|| (HasMoveEffect(battler, MOVE_SNORE) && AI_GetMoveEffectiveness(MOVE_SNORE, battler, opposingBattler) >= UQ_4_12(2.0))
|| (IsBattlerGrounded(battler)
&& (HasMove(battler, MOVE_MISTY_TERRAIN) || HasMove(battler, MOVE_ELECTRIC_TERRAIN)))
)
@ -643,7 +644,7 @@ static bool32 HasSuperEffectiveMoveAgainstOpponents(u32 battler, bool32 noRng)
if (move == MOVE_NONE)
continue;
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
{
if (noRng)
return TRUE;
@ -665,7 +666,7 @@ static bool32 HasSuperEffectiveMoveAgainstOpponents(u32 battler, bool32 noRng)
if (move == MOVE_NONE)
continue;
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
{
if (noRng)
return TRUE;
@ -764,7 +765,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
if (move == 0)
continue;
if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= AI_EFFECTIVENESS_x2 && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || AI_DATA->aiSwitchPredictionInProgress))
if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || AI_DATA->aiSwitchPredictionInProgress))
return SetSwitchinAndSwitch(battler, i);
}
}
@ -858,7 +859,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
// Stay in if effective move
else if (AI_GetMoveEffectiveness(encoredMove, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
else if (AI_GetMoveEffectiveness(encoredMove, battler, opposingBattler) >= UQ_4_12(2.0))
return FALSE;
// Switch out 50% of the time otherwise
@ -1210,7 +1211,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
for (i = 0; i < MAX_MON_MOVES; i++)
{
u32 move = GetMonData(&party[bestMonId], MON_DATA_MOVE1 + i);
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0))
break;
}
@ -1866,7 +1867,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
{
if (typeMatchup < bestResistEffective)
{
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) >= UQ_4_12(2.0))
{
if (canSwitchinWin1v1)
{

View File

@ -23,8 +23,6 @@
#include "constants/moves.h"
#include "constants/items.h"
static u32 AI_GetEffectiveness(uq4_12_t multiplier);
// Functions
u32 GetDmgRollType(u32 battlerAtk)
{
@ -387,7 +385,7 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
}
// To save computation time this function has 2 variants. One saves, sets and restores battlers, while the other doesn't.
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType)
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType)
{
struct SimulatedDamage dmg;
@ -395,7 +393,7 @@ struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 b
SaveBattlerData(battlerDef);
SetBattlerData(battlerAtk);
SetBattlerData(battlerDef);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather(AI_DATA), rollType);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather(AI_DATA), rollType);
RestoreBattlerData(battlerAtk);
RestoreBattlerData(battlerDef);
return dmg;
@ -631,7 +629,7 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal
*minimumDamage = minimum;
}
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType)
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType)
{
struct SimulatedDamage simDamage;
s32 moveType;
@ -755,7 +753,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
}
// convert multiper to AI_EFFECTIVENESS_xX
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
*typeEffectiveness = effectivenessMultiplier;
// Undo temporary settings
gBattleStruct->dynamicMoveType = 0;
@ -1040,7 +1038,7 @@ u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef)
return (bestDmg * 100) / gBattleMons[battlerDef].maxHP;
}
uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
{
uq4_12_t typeEffectiveness;
u32 moveType;
@ -1062,35 +1060,6 @@ uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
return typeEffectiveness;
}
u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef)
{
return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef));
}
static u32 AI_GetEffectiveness(uq4_12_t multiplier)
{
switch (multiplier)
{
case UQ_4_12(0.0):
return AI_EFFECTIVENESS_x0;
case UQ_4_12(0.125):
return AI_EFFECTIVENESS_x0_125;
case UQ_4_12(0.25):
return AI_EFFECTIVENESS_x0_25;
case UQ_4_12(0.5):
return AI_EFFECTIVENESS_x0_5;
case UQ_4_12(1.0):
default:
return AI_EFFECTIVENESS_x1;
case UQ_4_12(2.0):
return AI_EFFECTIVENESS_x2;
case UQ_4_12(4.0):
return AI_EFFECTIVENESS_x4;
case UQ_4_12(8.0):
return AI_EFFECTIVENESS_x8;
}
}
/* Checks to see if AI will move ahead of another battler
* The function uses a stripped down version of the checks from GetWhichBattlerFasterArgs
* Output:
@ -3012,7 +2981,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability)
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef))
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
return FALSE;
@ -3027,7 +2996,7 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBeParalyzed(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
@ -3089,7 +3058,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability)
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!CanBeBurned(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))
{
@ -3101,7 +3070,7 @@ bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtk
bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!AI_CanGetFrostbite(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))
{
@ -3113,7 +3082,7 @@ bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 b
bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if ((gBattleMons[battlerDef].status2 & STATUS2_INFATUATION)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| defAbility == ABILITY_OBLIVIOUS
|| !AreBattlersOfOppositeGender(battlerAtk, battlerDef)
|| AI_IsAbilityOnSide(battlerDef, ABILITY_AROMA_VEIL))
@ -3575,7 +3544,7 @@ void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons)
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool32 isPartyMonAttacker, enum DamageRollType rollType)
{
struct SimulatedDamage dmg;
u8 effectiveness;
uq4_12_t effectiveness;
struct BattlePokemon *savedBattleMons = AllocSaveBattleMons();
if (isPartyMonAttacker)
@ -4048,7 +4017,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
if (IsViableZMove(battlerAtk, chosenMove))
{
u8 effectiveness;
uq4_12_t effectiveness;
u32 zMove = GetUsableZMove(battlerAtk, chosenMove);
struct SimulatedDamage dmg;