Refactor battler flee and watch options + clean up (#6724)
This commit is contained in:
parent
4ff2f3de42
commit
55f9f6adea
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user