Refactor battler flee and watch options + clean up (#6724)

This commit is contained in:
Alex 2025-04-30 17:54:42 +02:00 committed by GitHub
parent 4ff2f3de42
commit 55f9f6adea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 169 additions and 166 deletions

View File

@ -816,8 +816,11 @@ struct BattleStruct
struct AiBattleData
{
s32 finalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier
u8 moveOrAction[MAX_BATTLERS_COUNT];
u8 chosenMoveIndex[MAX_BATTLERS_COUNT];
u8 chosenTarget[MAX_BATTLERS_COUNT];
u8 actionFlee:1;
u8 choiceWatch:1;
u8 padding:6;
};
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View File

@ -6,11 +6,6 @@ typedef s32 (*AiScoreFunc)(u32, u32, u32, s32);
#define UNKNOWN_NO_OF_HITS UINT32_MAX
// return vals for BattleAI_ChooseMoveOrAction
// 0 - 3 are move idx
#define AI_CHOICE_FLEE 4
#define AI_CHOICE_WATCH 5
// for AI_WhoStrikesFirst
#define AI_IS_FASTER 1
#define AI_IS_SLOWER -1
@ -114,7 +109,8 @@ enum AIScore
void BattleAI_SetupItems(void);
void BattleAI_SetupFlags(void);
void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler);
u32 BattleAI_ChooseMoveOrAction(u32 battler);
void ComputeBattlerDecisions(u32 battler);
u32 BattleAI_ChooseMoveIndex(u32 battler);
void Ai_InitPartyStruct(void);
void Ai_UpdateSwitchInData(u32 battler);
void Ai_UpdateFaintData(u32 battler);

View File

@ -265,14 +265,78 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
gAiBattleData->chosenTarget[battler] = gBattlerTarget;
}
u32 BattleAI_ChooseMoveOrAction(u32 battler)
bool32 BattlerChoseNonMoveAction(void)
{
u32 ret;
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_FLEE)
{
gAiBattleData->actionFlee = TRUE;
return TRUE;
}
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH)
{
gAiBattleData->choiceWatch = TRUE;
return TRUE;
}
return FALSE;
}
void SetupAISwitchingData(u32 battler, enum SwitchType switchType)
{
s32 opposingBattler = GetOppositeBattler(battler);
// AI's predicting data
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH))
{
AI_DATA->aiSwitchPredictionInProgress = TRUE;
AI_DATA->battlerDoingPrediction = battler;
AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType);
if (ShouldSwitch(opposingBattler))
AI_DATA->shouldSwitch |= (1u << opposingBattler);
AI_DATA->aiSwitchPredictionInProgress = FALSE;
gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species;
// Determine whether AI will use predictions this turn
AI_DATA->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, PREDICT_SWITCH_CHANCE);
}
// AI's data
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, switchType);
if (ShouldSwitch(battler))
AI_DATA->shouldSwitch |= (1u << battler);
gBattleStruct->prevTurnSpecies[battler] = gBattleMons[battler].species;
}
void ComputeBattlerDecisions(u32 battler)
{
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
&& (BattlerHasAi(battler)
&& !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
{
// If ai is about to flee or chosen to watch player, no need to calc anything
if (BattlerChoseNonMoveAction())
return;
// Risky AI switches aggressively even mid battle
enum SwitchType switchType = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE;
AI_DATA->aiCalcInProgress = TRUE;
BattleAI_SetupAIData(0xF, battler);
SetupAISwitchingData(battler, switchType);
gAiBattleData->chosenMoveIndex[battler] = BattleAI_ChooseMoveIndex(battler); // Calculate score and chose move index
AI_DATA->aiCalcInProgress = FALSE;
}
}
u32 BattleAI_ChooseMoveIndex(u32 battler)
{
u32 chosenMoveIndex;
if (!IsDoubleBattle())
ret = ChooseMoveOrAction_Singles(battler);
chosenMoveIndex = ChooseMoveOrAction_Singles(battler);
else
ret = ChooseMoveOrAction_Doubles(battler);
chosenMoveIndex = ChooseMoveOrAction_Doubles(battler);
// Clear protect structures, some flags may be set during AI calcs
// e.g. pranksterElevated from GetBattleMovePriority
@ -280,7 +344,8 @@ u32 BattleAI_ChooseMoveOrAction(u32 battler)
#if TESTING
TestRunner_Battle_CheckAiMoveScores(battler);
#endif // TESTING
return ret;
return chosenMoveIndex;
}
static void CopyBattlerDataToAIParty(u32 bPosition, u32 side)
@ -516,12 +581,6 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
gAiBattleData->finalScore[battlerAi][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i];
}
// Check special AI actions.
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_FLEE)
return AI_CHOICE_FLEE;
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH)
return AI_CHOICE_WATCH;
numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
consideredMoveArray[0] = 0;
@ -594,48 +653,37 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
AI_THINKING_STRUCT->aiLogicId++;
}
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_FLEE)
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[0];
mostViableMovesIndices[0] = 0;
mostViableMovesNo = 1;
for (j = 1; j < MAX_MON_MOVES; j++)
{
actionOrMoveIndex[i] = AI_CHOICE_FLEE;
}
else if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH)
{
actionOrMoveIndex[i] = AI_CHOICE_WATCH;
}
else
{
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[0];
mostViableMovesIndices[0] = 0;
mostViableMovesNo = 1;
for (j = 1; j < MAX_MON_MOVES; j++)
if (gBattleMons[battlerAi].moves[j] != 0)
{
if (gBattleMons[battlerAi].moves[j] != 0)
{
if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j]))
continue;
if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j]))
continue;
if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j])
{
mostViableMovesScores[mostViableMovesNo] = AI_THINKING_STRUCT->score[j];
mostViableMovesIndices[mostViableMovesNo] = j;
mostViableMovesNo++;
}
if (mostViableMovesScores[0] < AI_THINKING_STRUCT->score[j])
{
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[j];
mostViableMovesIndices[0] = j;
mostViableMovesNo = 1;
}
if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j])
{
mostViableMovesScores[mostViableMovesNo] = AI_THINKING_STRUCT->score[j];
mostViableMovesIndices[mostViableMovesNo] = j;
mostViableMovesNo++;
}
if (mostViableMovesScores[0] < AI_THINKING_STRUCT->score[j])
{
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[j];
mostViableMovesIndices[0] = j;
mostViableMovesNo = 1;
}
}
actionOrMoveIndex[i] = mostViableMovesIndices[Random() % mostViableMovesNo];
bestMovePointsForTarget[i] = mostViableMovesScores[0];
}
actionOrMoveIndex[i] = mostViableMovesIndices[Random() % mostViableMovesNo];
bestMovePointsForTarget[i] = mostViableMovesScores[0];
// Don't use a move against ally if it has less than 100 points.
if (i == BATTLE_PARTNER(battlerAi) && bestMovePointsForTarget[i] < AI_SCORE_DEFAULT)
{
bestMovePointsForTarget[i] = -1;
}
// Don't use a move against ally if it has less than 100 points.
if (i == BATTLE_PARTNER(battlerAi) && bestMovePointsForTarget[i] < AI_SCORE_DEFAULT)
{
bestMovePointsForTarget[i] = -1;
}
for (j = 0; j < MAX_MON_MOVES; j++)
@ -862,7 +910,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
RETURN_SCORE_MINUS(20);
// Don't setup into expected Focus Punch. Revisit alongside predictedMove with move prediction
if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS && moveEffect != EFFECT_SLEEP
if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS && moveEffect != EFFECT_SLEEP
&& GetMoveEffect(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING)) == EFFECT_FOCUS_PUNCH && RandomPercentage(RNG_AI_STATUS_FOCUS_PUNCH, STATUS_MOVE_FOCUS_PUNCH_CHANCE))
{
RETURN_SCORE_MINUS(20);

View File

@ -2232,11 +2232,11 @@ static bool32 AiExpectsToFaintPlayer(u32 battler)
{
u8 target = gAiBattleData->chosenTarget[battler];
if (gAiBattleData->moveOrAction[battler] > 3)
if (gAiBattleData->actionFlee || gAiBattleData->choiceWatch)
return FALSE; // AI not planning to use move
if (GetBattlerSide(target) != GetBattlerSide(battler)
&& CanIndexMoveFaintTarget(battler, target, gAiBattleData->moveOrAction[battler], AI_ATTACKING)
&& CanIndexMoveFaintTarget(battler, target, gAiBattleData->chosenMoveIndex[battler], AI_ATTACKING)
&& AI_IsFaster(battler, target, GetAIChosenMove(battler)))
{
// We expect to faint the target and move first -> dont use an item

View File

@ -87,7 +87,7 @@ bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move)
u32 GetAIChosenMove(u32 battlerId)
{
return (gBattleMons[battlerId].moves[gAiBattleData->moveOrAction[battlerId]]);
return (gBattleMons[battlerId].moves[gAiBattleData->chosenMoveIndex[battlerId]]);
}
bool32 AI_RandLessThan(u32 val)
@ -3184,8 +3184,8 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
else
{
// Battler can be frostbitten and has move/ability that synergizes with being frostbitten
if (CanBeFrozen(battlerAtk, battlerDef, abilityDef) &&
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef))
if (CanBeFrozen(battlerAtk, battlerDef, abilityDef)
&& DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;

View File

@ -532,7 +532,7 @@ static void OpponentHandleChooseAction(u32 battler)
static void OpponentHandleChooseMove(u32 battler)
{
u8 chosenMoveId;
u32 chosenMoveIndex;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
@ -542,61 +542,58 @@ static void OpponentHandleChooseMove(u32 battler)
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, ChooseMoveAndTargetInBattlePalace(battler));
}
else if (gAiBattleData->actionFlee)
{
gAiBattleData->actionFlee = FALSE;
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0);
}
else if (gAiBattleData->choiceWatch)
{
gAiBattleData->choiceWatch = FALSE;
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
}
else
{
chosenMoveId = gAiBattleData->moveOrAction[battler];
chosenMoveIndex = gAiBattleData->chosenMoveIndex[battler];
gBattlerTarget = gAiBattleData->chosenTarget[battler];
switch (chosenMoveId)
u32 chosenMove = moveInfo->moves[chosenMoveIndex];
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_USER)
gBattlerTarget = battler;
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH)
{
case AI_CHOICE_WATCH:
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
break;
case AI_CHOICE_FLEE:
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0);
break;
case 6:
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 15, gBattlerTarget);
break;
default:
{
u16 chosenMove = moveInfo->moves[chosenMoveId];
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_USER)
gBattlerTarget = battler;
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH)
{
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
if (gAbsentBattlerFlags & (1u << gBattlerTarget))
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
// If opponent can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
}
}
break;
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
if (gAbsentBattlerFlags & (1u << gBattlerTarget))
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
// If opponent can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveIndex])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (gBattlerTarget << 8));
}
}
OpponentBufferExecCompleted(battler);
}
else // Wild pokemon - use random move
{
u16 move;
u8 target;
u32 move;
u32 target;
do
{
chosenMoveId = Random() & 3;
move = moveInfo->moves[chosenMoveId];
chosenMoveIndex = Random() & (MAX_MON_MOVES - 1);
move = moveInfo->moves[chosenMoveIndex];
} while (move == MOVE_NONE);
if (GetBattlerMoveTargetType(battler, move) & MOVE_TARGET_USER)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (battler << 8));
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (battler << 8));
}
else if (IsDoubleBattle())
{
do {
@ -629,17 +626,17 @@ static void OpponentHandleChooseMove(u32 battler)
}
}
if (isPartnerEnemy && CanTargetBattler(battler, target, move))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (target << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (target << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (target << 8));
}
}
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
OpponentBufferExecCompleted(battler);
}

View File

@ -346,12 +346,12 @@ static void PlayerPartnerHandleChooseAction(u32 battler)
static void PlayerPartnerHandleChooseMove(u32 battler)
{
u8 chosenMoveId;
u32 chosenMoveIndex;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
chosenMoveId = gAiBattleData->moveOrAction[battler];
chosenMoveIndex = gAiBattleData->chosenMoveIndex[battler];
gBattlerTarget = gAiBattleData->chosenTarget[battler];
u32 moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]);
u32 moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveIndex]);
if (moveTarget & MOVE_TARGET_USER)
gBattlerTarget = battler;
@ -364,13 +364,13 @@ static void PlayerPartnerHandleChooseMove(u32 battler)
// If partner can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveIndex])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveIndex) | (gBattlerTarget << 8));
}
PlayerPartnerBufferExecCompleted(battler);

View File

@ -146,7 +146,7 @@ void FreeBattleSpritesData(void)
u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
{
s32 i, var1, var2;
s32 chosenMoveId = -1;
s32 chosenMoveIndex = -1;
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
u8 unusableMovesBits = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL);
s32 percent = Random() % 100;
@ -199,15 +199,15 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
gBattleStruct->palaceFlags &= (1 << MAX_BATTLERS_COUNT) - 1;
gBattleStruct->palaceFlags |= (selectedMoves << MAX_BATTLERS_COUNT);
BattleAI_SetupAIData(selectedMoves, battler);
chosenMoveId = BattleAI_ChooseMoveOrAction(battler);
chosenMoveIndex = BattleAI_ChooseMoveIndex(battler);
}
// If no moves matched the selected group, pick a new move from groups the Pokémon has
// In this case the AI is not checked again, so the choice may be worse
// If a move is chosen this way, there's a 50% chance that it will be unable to use it anyway
if (chosenMoveId == -1 || chosenMoveId >= MAX_MON_MOVES)
if (chosenMoveIndex == -1 || chosenMoveIndex >= MAX_MON_MOVES)
{
chosenMoveId = -1;
chosenMoveIndex = -1;
if (unusableMovesBits != ALL_MOVES_MASK)
{
numMovesPerGroup = 0, numMultipleMoveGroups = 0;
@ -254,8 +254,8 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
{
i = Random() % MAX_MON_MOVES;
if (!((1u << i) & unusableMovesBits))
chosenMoveId = i;
} while (chosenMoveId == -1);
chosenMoveIndex = i;
} while (chosenMoveIndex == -1);
}
else
{
@ -280,8 +280,8 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
{
i = Random() % MAX_MON_MOVES;
if (!((1u << i) & unusableMovesBits) && randSelectGroup == GetBattlePalaceMoveGroup(battler, moveInfo->moves[i]))
chosenMoveId = i;
} while (chosenMoveId == -1);
chosenMoveIndex = i;
} while (chosenMoveIndex == -1);
}
// Because the selected move was not from the Nature-chosen move group there's a 50% chance
@ -300,16 +300,16 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
}
}
moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]);
moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveIndex]);
if (moveTarget & MOVE_TARGET_USER)
chosenMoveId |= (battler << 8);
chosenMoveIndex |= (battler << 8);
else if (moveTarget == MOVE_TARGET_SELECTED)
chosenMoveId |= GetBattlePalaceTarget(battler);
chosenMoveIndex |= GetBattlePalaceTarget(battler);
else
chosenMoveId |= (GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(battler))) << 8);
chosenMoveIndex |= (GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(battler))) << 8);
return chosenMoveId;
return chosenMoveIndex;
}
#undef maxGroupNum

View File

@ -4126,32 +4126,6 @@ enum
STATE_SELECTION_SCRIPT_MAY_RUN
};
void SetupAISwitchingData(u32 battler, enum SwitchType switchType)
{
s32 opposingBattler = GetOppositeBattler(battler);
// AI's predicting data
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH))
{
AI_DATA->aiSwitchPredictionInProgress = TRUE;
AI_DATA->battlerDoingPrediction = battler;
AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType);
if (ShouldSwitch(opposingBattler))
AI_DATA->shouldSwitch |= (1u << opposingBattler);
AI_DATA->aiSwitchPredictionInProgress = FALSE;
gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species;
// Determine whether AI will use predictions this turn
AI_DATA->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, PREDICT_SWITCH_CHANCE);
}
// AI's data
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, switchType);
if (ShouldSwitch(battler))
AI_DATA->shouldSwitch |= (1u << battler);
gBattleStruct->prevTurnSpecies[battler] = gBattleMons[battler].species;
}
static void HandleTurnActionSelectionState(void)
{
s32 i, battler;
@ -4165,22 +4139,7 @@ static void HandleTurnActionSelectionState(void)
case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn.
RecordedBattle_CopyBattlerMoves(battler);
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
enum SwitchType switchType = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE; // Risky AI switches aggressively even mid battle
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
{
AI_DATA->aiCalcInProgress = TRUE;
// Setup battler data
BattleAI_SetupAIData(0xF, battler);
SetupAISwitchingData(battler, switchType);
// Do scoring
gAiBattleData->moveOrAction[battler] = BattleAI_ChooseMoveOrAction(battler);
AI_DATA->aiCalcInProgress = FALSE;
}
ComputeBattlerDecisions(battler); // Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
// fallthrough
case STATE_BEFORE_ACTION_CHOSEN: // Choose an action.
gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE;