Optimization / Clean up of AI move usability (#6332)

This commit is contained in:
Alex 2025-03-19 04:42:07 +01:00 committed by GitHub
parent 6031bb2a78
commit 98a5fab898
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 90 additions and 88 deletions

View File

@ -28,6 +28,13 @@ enum AIPivot
SHOULD_PIVOT,
};
static inline bool32 IsMoveUnusable(u32 moveIndex, u32 move, u32 moveLimitations)
{
return move == MOVE_NONE
|| move == MOVE_UNAVAILABLE
|| moveLimitations & 1u << moveIndex;
}
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_RandLessThan(u32 val);

View File

@ -238,8 +238,7 @@ void BattleAI_SetupFlags(void)
void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
{
s32 i;
u8 moveLimitations;
u32 moveLimitations;
u32 flags[MAX_BATTLERS_COUNT];
// Clear AI data but preserve the flags.
@ -247,24 +246,19 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
memset(AI_THINKING_STRUCT, 0, sizeof(struct AI_ThinkingStruct));
memcpy(&AI_THINKING_STRUCT->aiFlags[0], &flags[0], sizeof(u32) * MAX_BATTLERS_COUNT);
// Conditional score reset, unlike Ruby.
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (defaultScoreMoves & 1)
SET_SCORE(battler, i, AI_SCORE_DEFAULT);
else
SET_SCORE(battler, i, 0);
defaultScoreMoves >>= 1;
}
moveLimitations = AI_DATA->moveLimitations[battler];
// Ignore moves that aren't possible to use.
for (i = 0; i < MAX_MON_MOVES; i++)
// Conditional score reset, unlike Ruby.
for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if ((1u << i) & moveLimitations)
SET_SCORE(battler, i, 0);
if (moveLimitations & (1u << moveIndex))
SET_SCORE(battler, moveIndex, 0);
if (defaultScoreMoves & 1)
SET_SCORE(battler, moveIndex, AI_SCORE_DEFAULT);
else
SET_SCORE(battler, moveIndex, 0);
defaultScoreMoves >>= 1;
}
gBattlerTarget = SetRandomTarget(battler);
@ -414,6 +408,7 @@ static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u
{
u32 moveIndex, move;
u16 *moves = GetMovesArray(battlerAtk);
u32 moveLimitations = aiData->moveLimitations[battlerAtk];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
@ -421,14 +416,13 @@ static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u
uq4_12_t effectiveness = Q_4_12(0.0);
move = moves[moveIndex];
if (move != MOVE_NONE
&& move != MOVE_UNAVAILABLE
//&& !IsBattleMoveStatus(move) /* we want to get effectiveness and accuracy of status moves */
&& !(aiData->moveLimitations[battlerAtk] & (1u << moveIndex)))
{
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather);
aiData->moveAccuracy[battlerAtk][battlerDef][moveIndex] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
}
if (IsMoveUnusable(moveIndex, move, moveLimitations))
continue;
// Also get effectiveness of status moves
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather);
aiData->moveAccuracy[battlerAtk][battlerDef][moveIndex] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex] = dmg;
aiData->effectiveness[battlerAtk][battlerDef][moveIndex] = effectiveness;
}

View File

@ -415,15 +415,15 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
{
s32 i, moveType;
u32 usable = 0;
u32 unusable = AI_DATA->moveLimitations[attacker];
u16 *moves = GetMovesArray(attacker);
u32 moveLimitations = AI_DATA->moveLimitations[attacker];
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE
&& moves[i] != MOVE_UNAVAILABLE
&& GetBattleMoveCategory(moves[i]) == category
&& !(unusable & (1u << i)))
if (IsMoveUnusable(i, moves[i], moveLimitations))
continue;
if (GetBattleMoveCategory(moves[i]) == category)
{
SetTypeBeforeUsingMove(moves[i], attacker);
moveType = GetBattleMoveType(moves[i]);
@ -1231,18 +1231,19 @@ static bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
// Check if target has means to faint ai mon.
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
{
struct AiLogicData *aiData = AI_DATA;
s32 moveIndex;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
u16 *moves = GetMovesArray(battlerDef);
u32 moveLimitations = aiData->moveLimitations[battlerDef];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex))
&& AI_GetDamage(battlerDef, battlerAtk, moveIndex, AI_DEFENDING, AI_DATA) >= gBattleMons[battlerAtk].hp
if (IsMoveUnusable(moveIndex, moves[moveIndex], moveLimitations))
continue;
if (AI_GetDamage(battlerDef, battlerAtk, moveIndex, AI_DEFENDING, aiData) >= gBattleMons[battlerAtk].hp
&& !CanEndureHit(battlerDef, battlerAtk, moves[moveIndex]))
{
return TRUE;
}
}
return FALSE;
@ -1268,18 +1269,21 @@ u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk)
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext)
{
struct AiLogicData *aiData = AI_DATA;
u32 moveIndex;
u32 move = 0;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battlerAtk];
u16 *moves = GetMovesArray(battlerAtk);
u32 moveLimitations = aiData->moveLimitations[battlerAtk];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex))
&& bestDmg < AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA))
if (IsMoveUnusable(moveIndex, moves[moveIndex], moveLimitations))
continue;
if (bestDmg < AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, aiData))
{
bestDmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA);
bestDmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, aiData);
move = moves[moveIndex];
}
}
@ -1288,20 +1292,20 @@ u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcCon
u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext calcContext)
{
struct AiLogicData *aiData = AI_DATA;
u32 moveIndex;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battler];
u16 *moves = GetMovesArray(battler);
u32 moveLimitations = aiData->moveLimitations[battler];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[moveIndex] != MOVE_NONE
&& moves[moveIndex] != MOVE_UNAVAILABLE
&& !(unusable & (1u << moveIndex))
&& bestDmg < AI_GetDamage(battler, battlerTarget, moveIndex, calcContext, AI_DATA))
{
bestDmg = AI_GetDamage(battler, battlerTarget, moveIndex, calcContext, AI_DATA);
}
if (IsMoveUnusable(moveIndex, moves[moveIndex], moveLimitations))
continue;
u32 damage = AI_GetDamage(battler, battlerTarget, moveIndex, calcContext, aiData);
if (bestDmg < damage)
bestDmg = damage;
}
return bestDmg;
@ -1311,28 +1315,28 @@ u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext
// If numHits > 1, check if the target will be KO'ed by that number of hits (ignoring healing effects)
bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
{
struct AiLogicData *aiData = AI_DATA;
s32 moveIndex, dmg;
u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
u16 *moves = gBattleMons[battlerAtk].moves;
u32 moveLimitations = aiData->moveLimitations[battlerAtk];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(moveLimitations & (1u << moveIndex)))
if (IsMoveUnusable(moveIndex, moves[moveIndex], moveLimitations))
continue;
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_ATTACKING, aiData);
if (numHits)
dmg *= numHits;
if (gBattleMons[battlerDef].hp <= dmg)
{
// Use the pre-calculated value in simulatedDmg instead of re-calculating it
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_ATTACKING, AI_DATA);
if (numHits > 1)
return TRUE;
if (numHits)
dmg *= numHits;
if (gBattleMons[battlerDef].hp <= dmg)
{
if (numHits > 1)
return TRUE;
if (!CanEndureHit(battlerAtk, battlerDef, moves[moveIndex]))
return TRUE;
}
if (!CanEndureHit(battlerAtk, battlerDef, moves[moveIndex]))
return TRUE;
}
}
@ -1353,25 +1357,28 @@ bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits)
// Check if target has means to faint ai mon after modding hp/dmg
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod)
{
struct AiLogicData *aiData = AI_DATA;
u32 moveIndex;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
s32 dmg;
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
u16 *moves = GetMovesArray(battlerDef);
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
u32 moveLimitations = aiData->moveLimitations[battlerAtk];
if (hpCheck > gBattleMons[battlerAtk].maxHP)
hpCheck = gBattleMons[battlerAtk].maxHP;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_DEFENDING, AI_DATA);
if (IsMoveUnusable(moveIndex, moves[moveIndex], moveLimitations))
continue;
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_DEFENDING, aiData);
if (dmgMod)
dmg *= dmgMod;
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex)) && dmg >= hpCheck)
{
if (dmg >= hpCheck)
return TRUE;
}
}
return FALSE;
@ -2271,20 +2278,17 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] == MOVE_NONE || moves[i] == MOVE_UNAVAILABLE)
if (IsMoveUnusable(i, moves[i], moveLimitations))
continue;
if (!((1u << i) & moveLimitations))
{
if (ignoreStatus && IsBattleMoveStatus(moves[i]))
continue;
else if ((!IsBattleMoveStatus(moves[i]) && GetMoveAccuracy(moves[i]) == 0)
|| GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
continue;
if (ignoreStatus && IsBattleMoveStatus(moves[i]))
continue;
else if ((!IsBattleMoveStatus(moves[i]) && GetMoveAccuracy(moves[i]) == 0)
|| GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
continue;
if (AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] <= accCheck)
return TRUE;
}
if (AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] <= accCheck)
return TRUE;
}
return FALSE;
@ -2292,20 +2296,17 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool
bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef)
{
u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
u32 i;
u16 *moves = GetMovesArray(battlerAtk);
u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] == MOVE_NONE)
break;
if (!((1u << i) & moveLimitations))
{
if (GetMoveEffect(moves[i]) == EFFECT_SLEEP
&& AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] < 85)
return TRUE;
}
if (IsMoveUnusable(i, moves[i], moveLimitations))
continue;
if (GetMoveEffect(moves[i]) == EFFECT_SLEEP && AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] < 85)
return TRUE;
}
return FALSE;
}