From a9d6832908cc209899a8f189d3945d4720f6d0e8 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Wed, 27 Dec 2023 07:54:37 -0500 Subject: [PATCH] Separate AI flags by battler position (#3003) * ai flags by battlerId * fix recoded battle saved ai flags * update aiFlags check in OpponentHandleChoosePokemon * add header for TRAINER_CUSTOM_PARTNER define * initialize flags in BattleAI_SetupAIData * fix usage of TRAINER_CUSTOM_PARTNER * remove whitespace --------- Co-authored-by: ghoulslash --- include/battle.h | 2 +- src/battle_ai_main.c | 126 ++++++++++++++++++++----------- src/battle_ai_switch_items.c | 20 ++--- src/battle_ai_util.c | 66 +++++++++------- src/battle_controller_opponent.c | 2 +- src/battle_debug.c | 4 +- src/recorded_battle.c | 2 +- 7 files changed, 139 insertions(+), 83 deletions(-) diff --git a/include/battle.h b/include/battle.h index c41698fd5f..3fe22a4377 100644 --- a/include/battle.h +++ b/include/battle.h @@ -336,7 +336,7 @@ struct AI_ThinkingStruct u16 moveConsidered; s32 score[MAX_MON_MOVES]; u32 funcResult; - u32 aiFlags; + u32 aiFlags[MAX_BATTLERS_COUNT]; u8 aiAction; u8 aiLogicId; struct AI_SavedBattleMon saved[MAX_BATTLERS_COUNT]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8980691356..abe780db68 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -22,6 +22,7 @@ #include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/items.h" +#include "constants/trainers.h" #define AI_ACTION_DONE (1 << 0) #define AI_ACTION_FLEE (1 << 1) @@ -139,36 +140,76 @@ static u32 GetWildAiFlags(void) return flags; } +static u32 GetAiFlags(u16 trainerId) +{ + u32 flags = 0; + + if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart()) + return 0; + if (trainerId == 0xFFFF) + { + flags = GetWildAiFlags(); + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) + flags = GetAiScriptsInRecordedBattle(); + else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + flags = AI_FLAG_SAFARI; + else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) + flags = AI_FLAG_ROAMING; + else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) + flags = AI_FLAG_FIRST_BATTLE; + else if (gBattleTypeFlags & BATTLE_TYPE_FACTORY) + flags = GetAiScriptsInBattleFactory(); + else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE)) + flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; + else + flags = gTrainers[trainerId].aiFlags; + } + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + flags |= AI_FLAG_DOUBLE_BATTLE; + + return flags; +} + void BattleAI_SetupFlags(void) { + AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI + #if DEBUG_OVERWORLD_MENU == TRUE if (gIsDebugBattle) - AI_THINKING_STRUCT->aiFlags = gDebugAIFlags; - else + { + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags; + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags; + return; + } #endif - if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - AI_THINKING_STRUCT->aiFlags = GetAiScriptsInRecordedBattle(); - else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) - AI_THINKING_STRUCT->aiFlags = AI_FLAG_SAFARI; - else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) - AI_THINKING_STRUCT->aiFlags = AI_FLAG_ROAMING; - else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - AI_THINKING_STRUCT->aiFlags = AI_FLAG_FIRST_BATTLE; - else if (gBattleTypeFlags & BATTLE_TYPE_FACTORY) - AI_THINKING_STRUCT->aiFlags = GetAiScriptsInBattleFactory(); - else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE)) - AI_THINKING_STRUCT->aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT; - else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags | gTrainers[gTrainerBattleOpponent_B].aiFlags; + + if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))) + { + // smart wild AI + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF); + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF); + } else - AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags; - - // check smart wild AI - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)) && IsWildMonSmart()) - AI_THINKING_STRUCT->aiFlags |= GetWildAiFlags(); - - if (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS) || gTrainers[gTrainerBattleOpponent_A].doubleBattle) - AI_THINKING_STRUCT->aiFlags |= AI_FLAG_DOUBLE_BATTLE; // Act smart in doubles and don't attack your partner. + { + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(gTrainerBattleOpponent_A); + if (gTrainerBattleOpponent_B != 0) + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(gTrainerBattleOpponent_B); + else + AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT]; + } + + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + { + AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)); + } + else + { + AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = 0; // player + } } // sBattler_AI set in ComputeBattleAiScores @@ -176,11 +217,12 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler) { s32 i; u8 moveLimitations; + u32 flags[MAX_BATTLERS_COUNT]; // Clear AI data but preserve the flags. - u32 flags = AI_THINKING_STRUCT->aiFlags; + memcpy(&flags[0], &AI_THINKING_STRUCT->aiFlags[0], sizeof(u32) * MAX_BATTLERS_COUNT); memset(AI_THINKING_STRUCT, 0, sizeof(struct AI_ThinkingStruct)); - AI_THINKING_STRUCT->aiFlags = flags; + memcpy(&AI_THINKING_STRUCT->aiFlags[0], &flags[0], sizeof(u32) * MAX_BATTLERS_COUNT); // Conditional score reset, unlike Ruby. for (i = 0; i < MAX_MON_MOVES; i++) @@ -251,7 +293,7 @@ static void CopyBattlerDataToAIParty(u32 bPosition, u32 side) void Ai_InitPartyStruct(void) { u32 i; - bool32 isOmniscient = (AI_THINKING_STRUCT->aiFlags & AI_FLAG_OMNISCIENT); + bool32 isOmniscient = (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_OMNISCIENT) || (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_OMNISCIENT); struct Pokemon *mon; AI_PARTY->count[B_SIDE_PLAYER] = gPlayerPartyCount; @@ -433,13 +475,13 @@ static bool32 AI_ShouldSwitchIfBadMoves(u32 battler, bool32 doubleBattle) if (CountUsablePartyMons(battler) > 0 && !IsBattlerTrapped(battler, 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)) + && AI_THINKING_STRUCT->aiFlags[battler] & (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[battler].species) >= 310 // Mon is not weak. && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2) // Mon has more than 50% of its HP { - s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93; + s32 cap = AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93; if (doubleBattle) { for (i = 0; i < MAX_BATTLERS_COUNT; i++) @@ -492,7 +534,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi) u8 consideredMoveArray[MAX_MON_MOVES]; u32 numOfBestMoves; s32 i; - u32 flags = AI_THINKING_STRUCT->aiFlags; + u32 flags = AI_THINKING_STRUCT->aiFlags[battlerAi]; AI_DATA->partnerMove = 0; // no ally while (flags != 0) @@ -577,7 +619,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) AI_DATA->partnerMove = GetAllyChosenMove(battlerAi); AI_THINKING_STRUCT->aiLogicId = 0; AI_THINKING_STRUCT->movesetIndex = 0; - flags = AI_THINKING_STRUCT->aiFlags; + flags = AI_THINKING_STRUCT->aiFlags[sBattler_AI]; while (flags != 0) { @@ -1017,7 +1059,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_EXPLOSION: - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE)) + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE)) ADJUST_SCORE(-2); if (effectiveness == AI_EFFECTIVENESS_x0) @@ -1996,7 +2038,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } } - /*if (AI_THINKING_STRUCT->aiFlags == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI + /*if (AI_THINKING_STRUCT->aiFlags[battlerAtk] == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI && IS_DOUBLE_BATTLE) //Make the regular AI know how to use Protect minimally in Doubles { u8 shouldProtect = ShouldProtect(battlerAtk, battlerDef, move); @@ -2803,7 +2845,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) switch (atkPartnerAbility) { case ABILITY_VOLT_ABSORB: - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE)) + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE)) { RETURN_SCORE_MINUS(10); } @@ -2824,7 +2866,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case ABILITY_WATER_ABSORB: case ABILITY_DRY_SKIN: - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE)) + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE)) { RETURN_SCORE_MINUS(10); } @@ -3233,7 +3275,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score } // check status move preference - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_PREFER_STATUS_MOVES && IS_MOVE_STATUS(move) && effectiveness != AI_EFFECTIVENESS_x0) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IS_MOVE_STATUS(move) && effectiveness != AI_EFFECTIVENESS_x0) ADJUST_SCORE(1); // check thawing moves @@ -3241,7 +3283,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score ADJUST_SCORE(10); // check burn / frostbite - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING && AI_DATA->abilities[battlerAtk] == ABILITY_NATURAL_CURE) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && AI_DATA->abilities[battlerAtk] == ABILITY_NATURAL_CURE) { if ((gBattleMons[battlerAtk].status1 & STATUS1_BURN && HasOnlyMovesWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL, TRUE)) || (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && HasOnlyMovesWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL, TRUE))) @@ -3263,7 +3305,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score ADJUST_SCORE(2); case EFFECT_EXPLOSION: case EFFECT_MEMENTO: - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7) { if (aiData->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128)) ADJUST_SCORE(1); @@ -3569,7 +3611,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score ADJUST_SCORE(5); if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) ADJUST_SCORE(2); - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SCREENER) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) ADJUST_SCORE(2); } break; @@ -3611,7 +3653,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score ADJUST_SCORE(5); break; case EFFECT_MIST: - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SCREENER) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) ADJUST_SCORE(2); break; case EFFECT_FOCUS_ENERGY: @@ -4070,7 +4112,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score break; case STAT_DEF: case STAT_SPDEF: - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) ADJUST_SCORE(1); break; } @@ -4868,7 +4910,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score || gBattleResults.battleTurnCounter != 0) return score; - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER && CanTargetFaintAi(battlerDef, battlerAtk) && GetMovePriority(battlerAtk, move) == 0) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 7436f7af6d..5f96ef1e34 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -38,10 +38,10 @@ static void InitializeSwitchinCandidate(struct Pokemon *mon) static bool32 IsAceMon(u32 battler, u32 monPartyId) { - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON - && !(gBattleStruct->forcedSwitch & gBitTable[battler]) - && monPartyId == CalculateEnemyPartyCount()-1) - return TRUE; + if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON + && !(gBattleStruct->forcedSwitch & gBitTable[battler]) + && monPartyId == CalculateEnemyPartyCount()-1) + return TRUE; return FALSE; } @@ -77,7 +77,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) u16 typeEffectiveness = UQ_4_12(1.0), aiMoveEffect; //baseline typing damage // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)) + if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)) return FALSE; // Won't bother configuring this for double battles @@ -433,7 +433,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) && monAbility != ABILITY_SOUNDPROOF) switchMon = TRUE; - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING) + if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING) { //Yawn if (gStatuses3[battler] & STATUS3_YAWN @@ -851,7 +851,7 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler) static bool32 ShouldSwitchIfEncored(u32 battler, bool32 emitResult) { // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)) + if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)) return FALSE; // If not Encored or if no good switchin, don't switch @@ -877,7 +877,7 @@ static bool32 AreAttackingStatsLowered(u32 battler, bool32 emitResult) s8 spAttackingStage = gBattleMons[battler].statStages[STAT_SPATK]; // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer - if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)) + if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)) return FALSE; // Physical attacker @@ -1015,7 +1015,7 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult) return TRUE; //These Functions can prompt switch to generic pary members - if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE)) + if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE)) return FALSE; if (ShouldSwitchIfAllBadMoves(battler, emitResult)) return TRUE; @@ -1939,7 +1939,7 @@ u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd) // Split ideal mon decision between after previous mon KO'd (prioritize offensive options) and after switching active mon out (prioritize defensive options), and expand the scope of both. // Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer. - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_MON_CHOICES) + if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_MON_CHOICES) { bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchAfterMonKOd); return bestMonId; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 1ad4343442..2cb7878d42 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -404,7 +404,7 @@ bool32 BattlerHasAi(u32 battlerId) bool32 IsAiBattlerAware(u32 battlerId) { - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_OMNISCIENT) + if (AI_THINKING_STRUCT->aiFlags[battlerId] & AI_FLAG_OMNISCIENT) return TRUE; return BattlerHasAi(battlerId); @@ -1290,7 +1290,7 @@ u32 AI_DecideHoldEffectForTurn(u32 battlerId) else holdEffect = GetBattlerHoldEffect(battlerId, FALSE); - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) + if (AI_THINKING_STRUCT->aiFlags[battlerId] & AI_FLAG_NEGATE_UNAWARE) return holdEffect; if (gStatuses3[battlerId] & STATUS3_EMBARGO) @@ -1341,7 +1341,7 @@ bool32 AI_IsBattlerGrounded(u32 battlerId) bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move) { - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) + if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE) return FALSE; // AI handicap flag: doesn't understand ability suppression concept if (IsMoldBreakerTypeAbility(atkAbility) || gBattleMoves[move].ignoresTargetAbility) @@ -1352,7 +1352,7 @@ bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move) static inline bool32 AI_WeatherHasEffect(struct AiLogicData *aiData) { - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) + if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE) return TRUE; // AI doesn't understand weather supression (handicap) return aiData->weatherHasEffect; // weather damping abilities are announced @@ -1437,7 +1437,7 @@ bool32 IsHazardMoveEffect(u32 moveEffect) bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility) { - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) + if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE) return FALSE; if (move == MOVE_SKY_DROP @@ -1766,7 +1766,9 @@ u32 CountNegativeStatStages(u32 battlerId) bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4 @@ -1783,7 +1785,9 @@ bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4 @@ -1800,7 +1804,9 @@ bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) @@ -1815,7 +1821,9 @@ bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4 @@ -1831,7 +1839,9 @@ bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4 @@ -1847,7 +1857,9 @@ bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (defAbility != ABILITY_CONTRARY @@ -1864,7 +1876,9 @@ bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility) bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) + && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) + && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE @@ -2969,7 +2983,7 @@ bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move) if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef])) return TRUE; // battler is taking secondary damage with low HP - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) { if (!CanTargetFaintAi(battlerDef, battlerAtk)) return TRUE; // attacker goes first and opponent can't kill us @@ -3553,7 +3567,7 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128)) return; - if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here switch (statId) @@ -3627,8 +3641,8 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) + if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove) && AI_DATA->hpPercents[battlerDef] > 20) @@ -3636,7 +3650,7 @@ void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) if (!HasDamagingMove(battlerDef)) ADJUST_SCORE_PTR(2); - if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL && HasMoveEffect(battlerAtk, EFFECT_PROTECT)) + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL && HasMoveEffect(battlerAtk, EFFECT_PROTECT)) ADJUST_SCORE_PTR(1); // stall tactic if (HasMoveEffect(battlerAtk, EFFECT_VENOSHOCK) @@ -3651,8 +3665,8 @@ void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) + if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) @@ -3671,8 +3685,8 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) + if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) @@ -3693,8 +3707,8 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) + if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) @@ -3712,8 +3726,8 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) + if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) @@ -3731,7 +3745,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { - if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; if (AI_CanGiveFrostbite(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 649240ce34..7ffcd5ae2c 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -679,7 +679,7 @@ static void OpponentHandleChoosePokemon(u32 battler) if (IsValidForBattle(&gEnemyParty[chosenMonId]) && chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler2] - && (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON) + && (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON) || chosenMonId != CalculateEnemyPartyCount() - 1 || CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle)) { diff --git a/src/battle_debug.c b/src/battle_debug.c index 3fca57e38f..1b172046ba 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -1901,8 +1901,8 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data) data->modifyArrows.typeOfVal = VAL_BITFIELD_32; goto CASE_ITEM_STATUS; case LIST_ITEM_AI: - data->modifyArrows.modifiedValPtr = &gBattleResources->ai->aiFlags; - data->modifyArrows.currValue = GetBitfieldValue(gBattleResources->ai->aiFlags, data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount); + data->modifyArrows.modifiedValPtr = &gBattleResources->ai->aiFlags[data->battlerId]; + data->modifyArrows.currValue = GetBitfieldValue(gBattleResources->ai->aiFlags[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount); data->modifyArrows.typeOfVal = VAL_BITFIELD_32; goto CASE_ITEM_STATUS; CASE_ITEM_STATUS: diff --git a/src/recorded_battle.c b/src/recorded_battle.c index 1ba62b2837..2ac33a08a0 100644 --- a/src/recorded_battle.c +++ b/src/recorded_battle.c @@ -87,7 +87,7 @@ void RecordedBattle_Init(u8 mode) for (j = 0; j < BATTLER_RECORD_SIZE; j++) sBattleRecords[i][j] = 0xFF; sBattleFlags = gBattleTypeFlags; - sAI_Scripts = gBattleResources->ai->aiFlags; + sAI_Scripts = gBattleResources->ai->aiFlags[B_POSITION_OPPONENT_LEFT]; } } }