mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 03:34:15 +01:00
improve ai switching with bad moves (#3213)
This commit is contained in:
commit
766a1a27a7
@ -292,6 +292,8 @@ struct AiLogicData
|
|||||||
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
s32 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
|
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||||
u8 moveLimitations[MAX_BATTLERS_COUNT];
|
u8 moveLimitations[MAX_BATTLERS_COUNT];
|
||||||
|
bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler.
|
||||||
|
u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch.
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AI_ThinkingStruct
|
struct AI_ThinkingStruct
|
||||||
@ -304,8 +306,7 @@ struct AI_ThinkingStruct
|
|||||||
u32 aiFlags;
|
u32 aiFlags;
|
||||||
u8 aiAction;
|
u8 aiAction;
|
||||||
u8 aiLogicId;
|
u8 aiLogicId;
|
||||||
struct AI_SavedBattleMon saved[4];
|
struct AI_SavedBattleMon saved[MAX_BATTLERS_COUNT];
|
||||||
bool8 switchMon; // Because all available moves have no/little effect.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AI_MOVE_HISTORY_COUNT 3
|
#define AI_MOVE_HISTORY_COUNT 3
|
||||||
|
@ -403,6 +403,78 @@ void GetAiLogicData(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool32 AI_SwitchMonIfSuitable(u32 battlerId)
|
||||||
|
{
|
||||||
|
u32 monToSwitchId = GetMostSuitableMonToSwitchInto();
|
||||||
|
if (monToSwitchId != PARTY_SIZE)
|
||||||
|
{
|
||||||
|
AI_DATA->shouldSwitchMon |= gBitTable[battlerId];
|
||||||
|
AI_DATA->monToSwitchId[battlerId] = monToSwitchId;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool32 AI_ShouldSwitchIfBadMoves(u32 battlerId, bool32 doubleBattle)
|
||||||
|
{
|
||||||
|
u32 i, j;
|
||||||
|
// If can switch.
|
||||||
|
if (CountUsablePartyMons(battlerId) > 0
|
||||||
|
&& !IsBattlerTrapped(battlerId, TRUE)
|
||||||
|
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
|
||||||
|
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
|
||||||
|
{
|
||||||
|
// Consider switching if all moves are worthless to use.
|
||||||
|
if (GetTotalBaseStat(gBattleMons[battlerId].species) >= 310 // Mon is not weak.
|
||||||
|
&& gBattleMons[battlerId].hp >= gBattleMons[battlerId].maxHP / 2) // Mon has more than 50% of its HP
|
||||||
|
{
|
||||||
|
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
|
||||||
|
if (doubleBattle)
|
||||||
|
{
|
||||||
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (i != battlerId && IsBattlerAlive(i))
|
||||||
|
{
|
||||||
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||||
|
{
|
||||||
|
if (gBattleStruct->aiFinalScore[battlerId][i][j] > cap)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (j != MAX_MON_MOVES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == MAX_BATTLERS_COUNT && AI_SwitchMonIfSuitable(battlerId))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||||
|
{
|
||||||
|
if (AI_THINKING_STRUCT->score[i] > cap)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == MAX_MON_MOVES && AI_SwitchMonIfSuitable(battlerId))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consider switching if your mon with truant is bodied by Protect spam.
|
||||||
|
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
|
||||||
|
if (GetBattlerAbility(battlerId) == ABILITY_TRUANT
|
||||||
|
&& IsTruantMonVulnerable(battlerId, gBattlerTarget)
|
||||||
|
&& gDisableStructs[battlerId].truantCounter
|
||||||
|
&& gBattleMons[battlerId].hp >= gBattleMons[battlerId].maxHP / 2
|
||||||
|
&& AI_SwitchMonIfSuitable(battlerId))
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static u8 ChooseMoveOrAction_Singles(void)
|
static u8 ChooseMoveOrAction_Singles(void)
|
||||||
{
|
{
|
||||||
u8 currentMoveArray[MAX_MON_MOVES];
|
u8 currentMoveArray[MAX_MON_MOVES];
|
||||||
@ -436,46 +508,9 @@ static u8 ChooseMoveOrAction_Singles(void)
|
|||||||
|
|
||||||
gActiveBattler = sBattler_AI;
|
gActiveBattler = sBattler_AI;
|
||||||
|
|
||||||
// If can switch.
|
// Switch mon if there are no good moves to use.
|
||||||
if (CountUsablePartyMons(sBattler_AI) > 0
|
if (AI_ShouldSwitchIfBadMoves(sBattler_AI, FALSE))
|
||||||
&& !IsAbilityPreventingEscape(sBattler_AI)
|
return AI_CHOICE_SWITCH;
|
||||||
&& !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
|
|
||||||
&& !(gStatuses3[gActiveBattler] & STATUS3_ROOTED)
|
|
||||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
|
|
||||||
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
|
|
||||||
{
|
|
||||||
// Consider switching if all moves are worthless to use.
|
|
||||||
if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak.
|
|
||||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
|
|
||||||
{
|
|
||||||
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
|
|
||||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
||||||
{
|
|
||||||
if (AI_THINKING_STRUCT->score[i] > cap)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
|
||||||
{
|
|
||||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
|
||||||
return AI_CHOICE_SWITCH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consider switching if your mon with truant is bodied by Protect spam.
|
|
||||||
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
|
|
||||||
if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT
|
|
||||||
&& IsTruantMonVulnerable(sBattler_AI, gBattlerTarget)
|
|
||||||
&& gDisableStructs[sBattler_AI].truantCounter
|
|
||||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
|
|
||||||
{
|
|
||||||
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
|
||||||
{
|
|
||||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
|
||||||
return AI_CHOICE_SWITCH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
numOfBestMoves = 1;
|
numOfBestMoves = 1;
|
||||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||||
@ -590,7 +625,6 @@ static u8 ChooseMoveOrAction_Doubles(void)
|
|||||||
if (i == BATTLE_PARTNER(sBattler_AI) && bestMovePointsForTarget[i] < 100)
|
if (i == BATTLE_PARTNER(sBattler_AI) && bestMovePointsForTarget[i] < 100)
|
||||||
{
|
{
|
||||||
bestMovePointsForTarget[i] = -1;
|
bestMovePointsForTarget[i] = -1;
|
||||||
mostViableMovesScores[0] = mostViableMovesScores[0]; // Needed to match.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,6 +634,10 @@ static u8 ChooseMoveOrAction_Doubles(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch mon if all of the moves are bad to use against any of the target.
|
||||||
|
if (AI_ShouldSwitchIfBadMoves(sBattler_AI, TRUE))
|
||||||
|
return AI_CHOICE_SWITCH;
|
||||||
|
|
||||||
mostMovePoints = bestMovePointsForTarget[0];
|
mostMovePoints = bestMovePointsForTarget[0];
|
||||||
mostViableTargetsArray[0] = 0;
|
mostViableTargetsArray[0] = 0;
|
||||||
mostViableTargetsNo = 1;
|
mostViableTargetsNo = 1;
|
||||||
|
@ -60,10 +60,10 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
|||||||
|
|
||||||
static bool8 ShouldSwitchIfAllBadMoves(void)
|
static bool8 ShouldSwitchIfAllBadMoves(void)
|
||||||
{
|
{
|
||||||
if (gBattleResources->ai->switchMon)
|
if (AI_DATA->shouldSwitchMon & gBitTable[gActiveBattler])
|
||||||
{
|
{
|
||||||
gBattleResources->ai->switchMon = 0;
|
AI_DATA->shouldSwitchMon &= ~(gBitTable[gActiveBattler]);
|
||||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
gBattleStruct->AI_monToSwitchIntoId[gActiveBattler] = AI_DATA->monToSwitchId[gActiveBattler];
|
||||||
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -352,23 +352,30 @@ static void PlayerPartnerHandleChooseMove(u32 battler)
|
|||||||
chosenMoveId = gBattleStruct->aiMoveOrAction[battler];
|
chosenMoveId = gBattleStruct->aiMoveOrAction[battler];
|
||||||
gBattlerTarget = gBattleStruct->aiChosenTarget[battler];
|
gBattlerTarget = gBattleStruct->aiChosenTarget[battler];
|
||||||
|
|
||||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
if (chosenMoveId == AI_CHOICE_SWITCH)
|
||||||
gBattlerTarget = battler;
|
|
||||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
|
|
||||||
{
|
{
|
||||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
BtlController_EmitTwoReturnValues(BUFFER_B, 10, 0xFFFF);
|
||||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
|
||||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId]))
|
|
||||||
QueueZMove(battler, moveInfo->moves[chosenMoveId]);
|
|
||||||
|
|
||||||
// If partner can mega evolve, do it.
|
|
||||||
if (CanMegaEvolve(battler))
|
|
||||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
|
||||||
else
|
else
|
||||||
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
{
|
||||||
|
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||||
|
gBattlerTarget = battler;
|
||||||
|
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
|
||||||
|
{
|
||||||
|
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||||
|
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||||
|
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId]))
|
||||||
|
QueueZMove(battler, moveInfo->moves[chosenMoveId]);
|
||||||
|
|
||||||
|
// If partner can mega evolve, do it.
|
||||||
|
if (CanMegaEvolve(battler))
|
||||||
|
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||||
|
else
|
||||||
|
BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||||
|
}
|
||||||
|
|
||||||
PlayerPartnerBufferExecCompleted(battler);
|
PlayerPartnerBufferExecCompleted(battler);
|
||||||
}
|
}
|
||||||
@ -382,7 +389,7 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler)
|
|||||||
chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler);
|
chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(battler);
|
||||||
}
|
}
|
||||||
// Switching out
|
// Switching out
|
||||||
else
|
else if (gBattleStruct->monToSwitchIntoId[battler] == PARTY_SIZE)
|
||||||
{
|
{
|
||||||
chosenMonId = GetMostSuitableMonToSwitchInto();
|
chosenMonId = GetMostSuitableMonToSwitchInto();
|
||||||
if (chosenMonId == PARTY_SIZE) // just switch to the next mon
|
if (chosenMonId == PARTY_SIZE) // just switch to the next mon
|
||||||
@ -402,6 +409,12 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler)
|
|||||||
}
|
}
|
||||||
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
||||||
}
|
}
|
||||||
|
else // Mon to switch out has been already chosen.
|
||||||
|
{
|
||||||
|
chosenMonId = gBattleStruct->monToSwitchIntoId[battler];
|
||||||
|
*(gBattleStruct->AI_monToSwitchIntoId + battler) = PARTY_SIZE;
|
||||||
|
*(gBattleStruct->monToSwitchIntoId + battler) = chosenMonId;
|
||||||
|
}
|
||||||
BtlController_EmitChosenMonReturnValue(BUFFER_B, chosenMonId, NULL);
|
BtlController_EmitChosenMonReturnValue(BUFFER_B, chosenMonId, NULL);
|
||||||
PlayerPartnerBufferExecCompleted(battler);
|
PlayerPartnerBufferExecCompleted(battler);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user