mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 11:37:40 +01:00
move ai score calc to STATE_TURN_START_RECORD so AI can consider move selection for item use
This commit is contained in:
parent
c694e0cb90
commit
b33a8b0cb4
@ -584,6 +584,8 @@ struct BattleStruct
|
||||
bool8 spriteIgnore0Hp;
|
||||
struct Illusion illusion[MAX_BATTLERS_COUNT];
|
||||
s8 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier
|
||||
u8 aiMoveOrAction[MAX_BATTLERS_COUNT];
|
||||
u8 aiChosenTarget[MAX_BATTLERS_COUNT];
|
||||
u8 soulheartBattlerId;
|
||||
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
|
||||
bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once.
|
||||
|
@ -19,7 +19,7 @@
|
||||
return score; \
|
||||
}
|
||||
|
||||
|
||||
u8 ComputeBattleAiScores(u8 battler);
|
||||
void BattleAI_SetupItems(void);
|
||||
void BattleAI_SetupFlags(void);
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves);
|
||||
|
@ -20,6 +20,7 @@ void ClearBattlerItemEffectHistory(u8 battlerId);
|
||||
void SaveBattlerData(u8 battlerId);
|
||||
void SetBattlerData(u8 battlerId);
|
||||
void RestoreBattlerData(u8 battlerId);
|
||||
u16 GetAIChosenMove(u8 battlerId);
|
||||
|
||||
bool32 WillAIStrikeFirst(void);
|
||||
u32 GetTotalBaseStat(u32 species);
|
||||
@ -27,7 +28,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||
bool32 AtMaxHp(u8 battler);
|
||||
u32 GetHealthPercentage(u8 battler);
|
||||
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 consideredMove);
|
||||
bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk);
|
||||
bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits);
|
||||
bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod);
|
||||
|
@ -81,6 +81,8 @@
|
||||
|
||||
#define WILD_DOUBLE_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))))
|
||||
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gTrainerBattleOpponent_B == 0xFFFF))
|
||||
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
|
||||
|
||||
// Battle Outcome defines
|
||||
#define B_OUTCOME_WON 1
|
||||
|
@ -174,11 +174,12 @@ void BattleAI_SetupFlags(void)
|
||||
AI_THINKING_STRUCT->aiFlags |= AI_FLAG_DOUBLE_BATTLE; // Act smart in doubles and don't attack your partner.
|
||||
}
|
||||
|
||||
// sBattler_AI set in ComputeBattleAiScores
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves)
|
||||
{
|
||||
s32 i, move, dmg;
|
||||
u8 moveLimitations;
|
||||
|
||||
|
||||
// Clear AI data but preserve the flags.
|
||||
u32 flags = AI_THINKING_STRUCT->aiFlags;
|
||||
memset(AI_THINKING_STRUCT, 0, sizeof(struct AI_ThinkingStruct));
|
||||
@ -204,8 +205,9 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves)
|
||||
AI_THINKING_STRUCT->score[i] = 0;
|
||||
}
|
||||
|
||||
sBattler_AI = gActiveBattler;
|
||||
//sBattler_AI = gActiveBattler;
|
||||
gBattlerTarget = SetRandomTarget(sBattler_AI);
|
||||
gBattleStruct->aiChosenTarget[sBattler_AI] = gBattlerTarget;
|
||||
}
|
||||
|
||||
u8 BattleAI_ChooseMoveOrAction(void)
|
||||
@ -226,53 +228,73 @@ u8 BattleAI_ChooseMoveOrAction(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// damages/other info computed in GetAIDataAndCalcDmg
|
||||
u8 ComputeBattleAiScores(u8 battler)
|
||||
{
|
||||
sBattler_AI = battler;
|
||||
BattleAI_SetupAIData(0xF);
|
||||
return BattleAI_ChooseMoveOrAction();
|
||||
}
|
||||
|
||||
static void SetBattlerAiData(u8 battlerId)
|
||||
{
|
||||
AI_DATA->abilities[battlerId] = AI_GetAbility(battlerId);
|
||||
AI_DATA->items[battlerId] = gBattleMons[battlerId].item;
|
||||
AI_DATA->holdEffects[battlerId] = AI_GetHoldEffect(battlerId);
|
||||
AI_DATA->holdEffectParams[battlerId] = GetBattlerHoldEffectParam(battlerId);
|
||||
AI_DATA->predictedMoves[battlerId] = gLastMoves[battlerId];
|
||||
AI_DATA->hpPercents[battlerId] = GetHealthPercentage(battlerId);
|
||||
AI_DATA->moveLimitations[battlerId] = CheckMoveLimitations(battlerId, 0, 0xFF);
|
||||
}
|
||||
|
||||
void GetAiLogicData(void)
|
||||
{
|
||||
u32 battlerAtk, battlerDef, i, move;
|
||||
u8 effectiveness;
|
||||
s32 dmg;
|
||||
|
||||
memset(AI_DATA, 0, sizeof(struct AiLogicData));
|
||||
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
&& !IsWildMonSmart()))
|
||||
return;
|
||||
|
||||
memset(AI_DATA, 0, sizeof(struct AiLogicData));
|
||||
for (battlerAtk = 0; battlerAtk < MAX_BATTLERS_COUNT; battlerAtk++)
|
||||
// get/assume all battler data
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (IsBattlerAlive(battlerAtk)) {
|
||||
AI_DATA->abilities[battlerAtk] = AI_GetAbility(battlerAtk);
|
||||
AI_DATA->items[battlerAtk] = gBattleMons[battlerAtk].item;
|
||||
AI_DATA->holdEffects[battlerAtk] = AI_GetHoldEffect(battlerAtk);
|
||||
AI_DATA->holdEffectParams[battlerAtk] = GetBattlerHoldEffectParam(battlerAtk);
|
||||
AI_DATA->predictedMoves[battlerAtk] = gLastMoves[battlerAtk];
|
||||
AI_DATA->hpPercents[battlerAtk] = GetHealthPercentage(battlerAtk);
|
||||
AI_DATA->moveLimitations[battlerAtk] = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL);
|
||||
if (IsBattlerAlive(i)) {
|
||||
SetBattlerAiData(i);
|
||||
}
|
||||
}
|
||||
|
||||
// simulate AI damage
|
||||
for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++)
|
||||
{
|
||||
if (!IsBattlerAlive(battlerAtk)
|
||||
|| !IsBattlerAIControlled(battlerAtk)) {
|
||||
continue;
|
||||
}
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
continue;
|
||||
|
||||
// Simulate dmg for all AI moves against all other targets
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
RecordKnownMove(battlerDef, gLastMoves[battlerDef]);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
continue;
|
||||
dmg = 0;
|
||||
effectiveness = AI_EFFECTIVENESS_x0;
|
||||
move = gBattleMons[battlerAtk].moves[i];
|
||||
|
||||
RecordKnownMove(battlerDef, gLastMoves[battlerDef]);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
dmg = 0;
|
||||
effectiveness = AI_EFFECTIVENESS_x0;
|
||||
move = gBattleMons[battlerAtk].moves[i];
|
||||
if (move != 0
|
||||
&& move != 0xFFFF
|
||||
&& gBattleMoves[move].power != 0
|
||||
&& !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i]))
|
||||
{
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness);
|
||||
if (dmg == 0)
|
||||
dmg = 1;
|
||||
}
|
||||
|
||||
AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
|
||||
AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
|
||||
if (move != 0
|
||||
&& move != 0xFFFF
|
||||
//&& gBattleMoves[move].power != 0 /* we want to get effectiveness of status moves */
|
||||
&& !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i])) {
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness);
|
||||
}
|
||||
|
||||
AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
|
||||
AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -495,6 +517,7 @@ static u8 ChooseMoveOrAction_Doubles(void)
|
||||
}
|
||||
|
||||
gBattlerTarget = mostViableTargetsArray[Random() % mostViableTargetsNo];
|
||||
gBattleStruct->aiChosenTarget[sBattler_AI] = gBattlerTarget;
|
||||
return actionOrMoveIndex[gBattlerTarget];
|
||||
}
|
||||
|
||||
@ -556,7 +579,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
u16 moveEffect = gBattleMoves[move].effect;
|
||||
s32 moveType;
|
||||
u16 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move);
|
||||
u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, move);
|
||||
u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move);
|
||||
u8 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||
u32 i;
|
||||
@ -593,7 +616,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
|
||||
// check off screen
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
|
||||
|
||||
// check if negates type
|
||||
@ -1292,7 +1315,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to disable
|
||||
@ -1312,7 +1335,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to encore
|
||||
@ -1755,7 +1778,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
case EFFECT_MIMIC:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE
|
||||
|| gLastMoves[battlerDef] == 0xFFFF)
|
||||
@ -1912,7 +1935,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // partner is going to set up hazards
|
||||
&& AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk) == AI_IS_FASTER) // partner is going to set up before the potential Defog
|
||||
&& AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk, AI_DATA->partnerMove) == AI_IS_FASTER) // partner is going to set up before the potential Defog
|
||||
{
|
||||
score -= 10;
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
@ -1950,7 +1973,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SEMI_INVULNERABLE:
|
||||
if (predictedMove != MOVE_NONE
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|
||||
&& gBattleMoves[predictedMove].effect == EFFECT_SEMI_INVULNERABLE)
|
||||
score -= 10; // Don't Fly/dig/etc if opponent is going to fly/dig/etc after you
|
||||
|
||||
@ -2131,7 +2154,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_ME_FIRST:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
|
||||
score -= 10; // Target is predicted to go first, Me First will fail
|
||||
else
|
||||
return AI_CheckBadMove(battlerAtk, battlerDef, predictedMove, score);
|
||||
@ -2320,7 +2343,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_ELECTRIFY:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER
|
||||
//|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
@ -2349,7 +2372,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
|
||||
instructedMove = predictedMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerDef];
|
||||
@ -2389,21 +2412,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (!isDoubleBattle
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (!IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| !isDoubleBattle
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_SUCKER_PUNCH:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent going first
|
||||
if (IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent going first
|
||||
score -= 10;
|
||||
}
|
||||
break;
|
||||
@ -2455,7 +2478,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score--; // don't want to move last
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER // Opponent should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER // Opponent should go first
|
||||
|| AI_DATA->hpPercents[battlerAtk] > 50)
|
||||
score -= 4;
|
||||
break;
|
||||
@ -2604,7 +2627,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// Ally decided to use Frost Breath on us. we must have Anger Point as our ability
|
||||
if (AI_DATA->abilities[battlerAtk] == ABILITY_ANGER_POINT)
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER) // Partner moving first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER) // Partner moving first
|
||||
{
|
||||
// discourage raising our attack since it's about to be maxed out
|
||||
if (IsAttackBoostMoveEffect(effect))
|
||||
@ -2871,7 +2894,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_FASTER)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_FASTER)
|
||||
instructedMove = AI_DATA->partnerMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerAtkPartner];
|
||||
@ -2885,8 +2908,8 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner) == AI_IS_SLOWER) // Opponent mon 1 goes before partner
|
||||
|| AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)) == AI_IS_SLOWER)) // Opponent mon 2 goes before partner
|
||||
if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), AI_DATA->partnerMove) == AI_IS_SLOWER // Opponent mon 1 goes before partner
|
||||
|| AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), AI_DATA->partnerMove) == AI_IS_SLOWER) // Opponent mon 2 goes before partner
|
||||
{
|
||||
if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT)
|
||||
break; // These moves need to go last
|
||||
@ -2913,7 +2936,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_MAGNITUDE:
|
||||
if (!IsBattlerGrounded(battlerAtkPartner)
|
||||
|| (IsBattlerGrounded(battlerAtkPartner)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER
|
||||
&& IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
|
||||
score += 2;
|
||||
else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE)
|
||||
@ -2986,7 +3009,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// check already dead
|
||||
if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])
|
||||
&& CanTargetFaintAi(battlerAtk, battlerDef)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent should go first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent should go first
|
||||
{
|
||||
if (atkPriority > 0)
|
||||
score++;
|
||||
@ -3034,7 +3057,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case ABILITY_GRIM_NEIGH:
|
||||
case ABILITY_AS_ONE_ICE_RIDER:
|
||||
case ABILITY_AS_ONE_SHADOW_RIDER:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
score += 8; // prioritize killing target for stat boost
|
||||
@ -3481,7 +3504,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_MIMIC:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF)
|
||||
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score);
|
||||
@ -3540,7 +3563,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gDisableStructs[battlerDef].disableTimer == 0
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)) // mental herb
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // AI goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // AI goes first
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE
|
||||
&& gLastMoves[battlerDef] != 0xFFFF)
|
||||
@ -3594,7 +3617,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_DESTINY_BOND:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
@ -3703,7 +3726,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
}
|
||||
else if (isDoubleBattle && AI_GetBattlerMoveTargetType(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
|
||||
else if (isDoubleBattle && AI_GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
if (AI_DATA->abilities[battlerAtk] != ABILITY_TELEPATHY)
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
@ -3842,7 +3865,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY
|
||||
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
|
||||
score += 9;
|
||||
else
|
||||
score += 3;
|
||||
@ -3887,7 +3910,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
if (predictedMove != MOVE_NONE && !isDoubleBattle)
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].effect == EFFECT_EXPLOSION
|
||||
|| gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
|
||||
@ -3954,7 +3977,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef])
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Target goes first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Target goes first
|
||||
break; // Don't use if the attract won't have a change to activate
|
||||
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
|
||||
@ -3999,7 +4022,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // Partner is going to set up hazards
|
||||
&& AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk)) == AI_IS_SLOWER) // Partner going first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk), move) == AI_IS_SLOWER) // Partner going first
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
}
|
||||
|
||||
@ -4488,13 +4511,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_THROAT_CHOP:
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
score += 3; // Ai goes first and predicts the target will use a sound move
|
||||
else if (TestMoveFlagsInMoveset(battlerDef, FLAG_SOUND))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_HEAL_BLOCK:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
score += 3; // Try to cancel healing move
|
||||
else if (HasHealingEffect(battlerDef) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_LEFTOVERS
|
||||
|| (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON)))
|
||||
@ -4530,7 +4553,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (isDoubleBattle
|
||||
&& AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef) == AI_IS_SLOWER) // Attacker partner wouldn't go before target
|
||||
&& AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef, AI_DATA->partnerMove) == AI_IS_SLOWER) // Attacker partner wouldn't go before target
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_TAILWIND:
|
||||
@ -4552,7 +4575,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (IsBattlerGrounded(battlerAtk) && HasDamagingMoveOfType(battlerDef, TYPE_ELECTRIC)
|
||||
&& !(AI_GetTypeEffectiveness(MOVE_EARTHQUAKE, battlerDef, battlerAtk) == AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].type == TYPE_GROUND)
|
||||
score += 3; // Cause the enemy's move to fail
|
||||
@ -4567,7 +4590,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_CAMOUFLAGE:
|
||||
if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER // Attacker goes first
|
||||
if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER // Attacker goes first
|
||||
&& !IS_MOVE_STATUS(move) && AI_GetTypeEffectiveness(predictedMove, battlerDef, battlerAtk) != AI_EFFECTIVENESS_x0)
|
||||
score++;
|
||||
break;
|
||||
@ -4610,7 +4633,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Ai goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Ai goes first
|
||||
{
|
||||
if (AI_DATA->hpPercents[battlerAtk] < 20)
|
||||
score++;
|
||||
@ -4652,7 +4675,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 2;
|
||||
break;
|
||||
case EFFECT_ENDEAVOR:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent faster
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent faster
|
||||
{
|
||||
if (AI_DATA->hpPercents[battlerAtk] < 40)
|
||||
score++;
|
||||
@ -4683,7 +4706,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
return score;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|
||||
&& CanTargetFaintAi(battlerDef, battlerAtk)
|
||||
&& GetMovePriority(battlerAtk, move) == 0)
|
||||
{
|
||||
|
@ -18,6 +18,8 @@
|
||||
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
|
||||
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent);
|
||||
static bool8 ShouldUseItem(void);
|
||||
static bool32 AI_ShouldHeal(u32 healAmount);
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
||||
{
|
||||
@ -788,6 +790,25 @@ static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect)
|
||||
return AI_ITEM_NOT_RECOGNIZABLE;
|
||||
}
|
||||
|
||||
static bool32 AiExpectsToFaintPlayer(void)
|
||||
{
|
||||
bool32 canFaintPlayer;
|
||||
u32 i;
|
||||
u8 target = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
|
||||
if (gBattleStruct->aiMoveOrAction[gActiveBattler] > 3)
|
||||
return FALSE; // AI not planning to use move
|
||||
|
||||
if (GetBattlerSide(target) != GetBattlerSide(gActiveBattler)
|
||||
&& CanIndexMoveFaintTarget(gActiveBattler, target, gBattleStruct->aiMoveOrAction[gActiveBattler], 0)
|
||||
&& AI_WhoStrikesFirst(gActiveBattler, target, GetAIChosenMove(gActiveBattler)) == AI_IS_FASTER) {
|
||||
// We expect to faint the target and move first -> dont use an item
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool8 ShouldUseItem(void)
|
||||
{
|
||||
struct Pokemon *party;
|
||||
@ -802,6 +823,9 @@ static bool8 ShouldUseItem(void)
|
||||
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO)
|
||||
return FALSE;
|
||||
|
||||
if (AiExpectsToFaintPlayer())
|
||||
return FALSE;
|
||||
|
||||
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
|
||||
party = gPlayerParty;
|
||||
@ -843,21 +867,10 @@ static bool8 ShouldUseItem(void)
|
||||
switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2))
|
||||
{
|
||||
case AI_ITEM_FULL_RESTORE:
|
||||
if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp == 0)
|
||||
break;
|
||||
shouldUse = TRUE;
|
||||
shouldUse = AI_ShouldHeal(0);
|
||||
break;
|
||||
case AI_ITEM_HEAL_HP:
|
||||
paramOffset = GetItemEffectParamOffset(item, 4, 4);
|
||||
if (paramOffset == 0)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp == 0)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|
||||
|| gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
|
||||
shouldUse = TRUE;
|
||||
shouldUse = AI_ShouldHeal(itemEffects[GetItemEffectParamOffset(item, 4, 4)]);
|
||||
break;
|
||||
case AI_ITEM_CURE_CONDITION:
|
||||
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
|
||||
@ -948,3 +961,32 @@ static bool8 ShouldUseItem(void)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 AI_ShouldHeal(u32 healAmount)
|
||||
{
|
||||
bool32 shouldHeal = FALSE;
|
||||
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|
||||
|| gBattleMons[gActiveBattler].hp == 0
|
||||
|| (healAmount != 0 && gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > healAmount)) {
|
||||
// We have low enough HP to consider healing
|
||||
shouldHeal = !AI_OpponentCanFaintAiWithMod(healAmount); // if target can kill us even after we heal, why bother
|
||||
}
|
||||
|
||||
return shouldHeal;
|
||||
}
|
||||
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
|
||||
{
|
||||
u32 i;
|
||||
// Check special cases to NOT heal
|
||||
for (i = 0; i < gBattlersCount; i++) {
|
||||
if (GetBattlerSide(i) == B_SIDE_PLAYER) {
|
||||
if (CanTargetFaintAiWithMod(i, gActiveBattler, healAmount, 0)) {
|
||||
// Target is expected to faint us
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -440,9 +440,14 @@ static const u16 sOtherMoveCallingMoves[] =
|
||||
};
|
||||
|
||||
// Functions
|
||||
u16 GetAIChosenMove(u8 battlerId)
|
||||
{
|
||||
return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]);
|
||||
}
|
||||
|
||||
bool32 WillAIStrikeFirst(void)
|
||||
{
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget) == AI_IS_FASTER);
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget, AI_THINKING_STRUCT->moveConsidered) == AI_IS_FASTER);
|
||||
}
|
||||
|
||||
bool32 AI_RandLessThan(u8 val)
|
||||
@ -638,7 +643,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
|
||||
u32 move = gBattleResources->battleHistory->usedMoves[opposingBattler][i];
|
||||
if (gBattleMoves[move].effect == EFFECT_PROTECT && move != MOVE_ENDURE)
|
||||
return TRUE;
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler) == AI_IS_SLOWER)
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler, GetAIChosenMove(battlerAI)) == AI_IS_SLOWER)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -987,23 +992,25 @@ static u8 AI_GetEffectiveness(u16 multiplier)
|
||||
* AI_IS_FASTER: is user(ai) faster
|
||||
* AI_IS_SLOWER: is target faster
|
||||
*/
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered)
|
||||
{
|
||||
u32 fasterAI = 0, fasterPlayer = 0, i;
|
||||
s8 prioAI = 0;
|
||||
s8 prioPlayer = 0;
|
||||
s8 prioBattler2 = 0;
|
||||
u16 *battler2Moves = GetMovesArray(battler2);
|
||||
|
||||
// Check move priorities first.
|
||||
prioAI = GetMovePriority(battlerAI, AI_THINKING_STRUCT->moveConsidered);
|
||||
prioAI = GetMovePriority(battlerAI, moveConsidered);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBattleMons[battler2].moves[i] == 0 || gBattleMons[battler2].moves[i] == 0xFFFF)
|
||||
if (battler2Moves[i] == 0 || battler2Moves[i] == 0xFFFF)
|
||||
continue;
|
||||
|
||||
prioPlayer = GetMovePriority(battler2, gBattleMons[battler2].moves[i]);
|
||||
if (prioAI > prioPlayer)
|
||||
prioBattler2 = GetMovePriority(battler2, battler2Moves[i]);
|
||||
if (prioAI > prioBattler2)
|
||||
fasterAI++;
|
||||
else if (prioPlayer > prioAI)
|
||||
else if (prioBattler2 > prioAI)
|
||||
fasterPlayer++;
|
||||
}
|
||||
|
||||
@ -1017,6 +1024,8 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prioAI > prioBattler2)
|
||||
return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
|
||||
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
||||
if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0)
|
||||
return AI_IS_FASTER;
|
||||
@ -2422,7 +2431,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
||||
/*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost)
|
||||
return PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/
|
||||
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (!CanAIFaintTarget(battlerAtk, battlerDef, 0)) // Can't KO foe otherwise
|
||||
{
|
||||
@ -2782,7 +2791,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili
|
||||
{
|
||||
if (defAbility == ABILITY_INNER_FOCUS
|
||||
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent goes first
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent goes first
|
||||
{
|
||||
return 0; // don't try to flinch
|
||||
}
|
||||
@ -2906,7 +2915,7 @@ bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveI
|
||||
|
||||
bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
{
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user goes first
|
||||
u8 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument;
|
||||
@ -2933,7 +2942,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
|
||||
bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
||||
{
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user going first
|
||||
s32 damage = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
||||
|
@ -1535,7 +1535,7 @@ static void OpponentHandlePrintSelectionString(void)
|
||||
|
||||
static void OpponentHandleChooseAction(void)
|
||||
{
|
||||
AI_TrySwitchOrUseItem(); // TODO consider move choice first
|
||||
AI_TrySwitchOrUseItem();
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
@ -1559,9 +1559,8 @@ static void OpponentHandleChooseMove(void)
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
|| IsWildMonSmart())
|
||||
{
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
chosenMoveId = gBattleStruct->aiMoveOrAction[gActiveBattler];
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_WATCH:
|
||||
|
@ -1515,8 +1515,8 @@ static void PlayerPartnerHandleChooseMove(void)
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);
|
||||
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
chosenMoveId = gBattleStruct->aiMoveOrAction[gActiveBattler];
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
|
||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
|
@ -3918,6 +3918,11 @@ static void HandleTurnActionSelectionState(void)
|
||||
case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn.
|
||||
RecordedBattle_CopyBattlerMoves();
|
||||
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
|
||||
|
||||
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && IsBattlerAIControlled(gActiveBattler)) {
|
||||
gBattleStruct->aiMoveOrAction[gActiveBattler] = ComputeBattleAiScores(gActiveBattler);
|
||||
}
|
||||
break;
|
||||
case STATE_BEFORE_ACTION_CHOSEN: // Choose an action.
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
|
Loading…
Reference in New Issue
Block a user