Move out ai struct out of BattleResources

This commit is contained in:
AlexOn1ine 2025-05-01 11:22:57 +02:00
parent 1a2cd5645a
commit 182d42642d
16 changed files with 558 additions and 555 deletions

View File

@ -143,7 +143,7 @@
* Prevent certain status moves when item is known + Fake Out changes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3219 * Prevent certain status moves when item is known + Fake Out changes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3219
* Improve AI switching with bad moves by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3213 * Improve AI switching with bad moves by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3213
* Fixed `CanTargetFaintAi` index issue by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3306 * Fixed `CanTargetFaintAi` index issue by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3306
* Transform updates `AI_PARTY` data by @ghoulslash in https://github.com/rh-hideout/pokeemerald-expansion/pull/3295 * Transform updates `gAiPartyData` data by @ghoulslash in https://github.com/rh-hideout/pokeemerald-expansion/pull/3295
* Greatly reduce AI lag by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3308 * Greatly reduce AI lag by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3308
### Cleanup ### Cleanup

View File

@ -510,7 +510,7 @@
* `AI_STRIKES_FIRST` is also replaced by `AI_IsFaster`. * `AI_STRIKES_FIRST` is also replaced by `AI_IsFaster`.
* Switch AI uses trapping abilities aggressively by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4669 * Switch AI uses trapping abilities aggressively by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4669
* Use 9th roll instead of average in AI calcs by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4679 * Use 9th roll instead of average in AI calcs by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4679
* Use `AI_DATA->abilities` in more places by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4729 * Use `gAiLogicData->abilities` in more places by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4729
* Minor switch AI cleanup by @Pawkkie, @DizzyEggg, @AlexOn1ine and @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4849 * Minor switch AI cleanup by @Pawkkie, @DizzyEggg, @AlexOn1ine and @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4849
* Removed `SetBattlerData` from `AI_CalcDamage` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4881 * Removed `SetBattlerData` from `AI_CalcDamage` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4881
* Removed unused `AI_FLAG_HELP_PARTNER` by @pkmnsnfrn and @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4918 * Removed unused `AI_FLAG_HELP_PARTNER` by @pkmnsnfrn and @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/4918

View File

@ -289,16 +289,17 @@ struct AiPartyMon
u16 item; u16 item;
u16 heldEffect; u16 heldEffect;
u16 ability; u16 ability;
u16 gender;
u16 level; u16 level;
u16 moves[MAX_MON_MOVES]; u16 moves[MAX_MON_MOVES];
u32 status; u32 status;
bool8 isFainted;
bool8 wasSentInBattle;
u8 switchInCount; // Counts how many times this Pokemon has been sent out or switched into in a battle. u8 switchInCount; // Counts how many times this Pokemon has been sent out or switched into in a battle.
u8 gender:2;
u8 isFainted:1;
u8 wasSentInBattle:1;
u8 padding:4;
}; };
struct AIPartyData // Opposing battlers - party mons. struct AiPartyData // Opposing battlers - party mons.
{ {
struct AiPartyMon mons[NUM_BATTLE_SIDES][PARTY_SIZE]; // 2 parties(player, opponent). Used to save information on opposing party. struct AiPartyMon mons[NUM_BATTLE_SIDES][PARTY_SIZE]; // 2 parties(player, opponent). Used to save information on opposing party.
u8 count[NUM_BATTLE_SIDES]; u8 count[NUM_BATTLE_SIDES];
@ -346,7 +347,7 @@ struct AiLogicData
u8 battlerDoingPrediction; // Stores which battler is currently running its prediction calcs u8 battlerDoingPrediction; // Stores which battler is currently running its prediction calcs
}; };
struct AI_ThinkingStruct struct AiThinkingStruct
{ {
u8 aiState; u8 aiState;
u8 movesetIndex; u8 movesetIndex;
@ -397,20 +398,11 @@ struct BattleResources
struct BattleScriptsStack* battleScriptsStack; struct BattleScriptsStack* battleScriptsStack;
struct BattleCallbacksStack* battleCallbackStack; struct BattleCallbacksStack* battleCallbackStack;
struct StatsArray* beforeLvlUp; struct StatsArray* beforeLvlUp;
struct AI_ThinkingStruct *ai;
struct AiLogicData *aiData;
struct AIPartyData *aiParty;
struct BattleHistory *battleHistory;
u8 bufferA[MAX_BATTLERS_COUNT][0x200]; u8 bufferA[MAX_BATTLERS_COUNT][0x200];
u8 bufferB[MAX_BATTLERS_COUNT][0x200]; u8 bufferB[MAX_BATTLERS_COUNT][0x200];
u8 transferBuffer[0x100]; u8 transferBuffer[0x100];
}; };
#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai))
#define AI_DATA ((struct AiLogicData *)(gBattleResources->aiData))
#define AI_PARTY ((struct AIPartyData *)(gBattleResources->aiParty))
#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory))
struct BattleResults struct BattleResults
{ {
u8 playerFaintCounter; // 0x0 u8 playerFaintCounter; // 0x0
@ -1127,6 +1119,10 @@ extern struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT];
extern struct BattleScripting gBattleScripting; extern struct BattleScripting gBattleScripting;
extern struct BattleStruct *gBattleStruct; extern struct BattleStruct *gBattleStruct;
extern struct AiBattleData *gAiBattleData; extern struct AiBattleData *gAiBattleData;
extern struct AiThinkingStruct *gAiThinkingStruct;
extern struct AiLogicData *gAiLogicData;
extern struct AiPartyData *gAiPartyData;
extern struct BattleHistory *gBattleHistory;
extern u8 *gLinkBattleSendBuffer; extern u8 *gLinkBattleSendBuffer;
extern u8 *gLinkBattleRecvBuffer; extern u8 *gLinkBattleRecvBuffer;
extern struct BattleResources *gBattleResources; extern struct BattleResources *gBattleResources;

View File

@ -60,7 +60,7 @@ enum AIScore
{ \ { \
TestRunner_Battle_AISetScore(__FILE__, __LINE__, battler, movesetIndex, val); \ TestRunner_Battle_AISetScore(__FILE__, __LINE__, battler, movesetIndex, val); \
} \ } \
AI_THINKING_STRUCT->score[movesetIndex] = val; \ gAiThinkingStruct->score[movesetIndex] = val; \
} while (0) \ } while (0) \
#define ADJUST_SCORE(val) \ #define ADJUST_SCORE(val) \
@ -68,7 +68,7 @@ enum AIScore
{ \ { \
if (TESTING) \ if (TESTING) \
{ \ { \
TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, AI_THINKING_STRUCT->movesetIndex, val); \ TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, gAiThinkingStruct->movesetIndex, val); \
} \ } \
score += val; \ score += val; \
} while (0) \ } while (0) \
@ -78,7 +78,7 @@ enum AIScore
{ \ { \
if (TESTING) \ if (TESTING) \
{ \ { \
TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, AI_THINKING_STRUCT->movesetIndex, val); \ TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, gAiThinkingStruct->movesetIndex, val); \
} \ } \
score += val; \ score += val; \
return score; \ return score; \
@ -89,7 +89,7 @@ enum AIScore
{ \ { \
if (TESTING) \ if (TESTING) \
{ \ { \
TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, AI_THINKING_STRUCT->movesetIndex, val); \ TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, battlerAtk, gAiThinkingStruct->movesetIndex, val); \
} \ } \
(*score) += val; \ (*score) += val; \
} while (0) \ } while (0) \

View File

@ -9,7 +9,7 @@
// Battle Debug Menu // Battle Debug Menu
#define DEBUG_BATTLE_MENU TRUE // If set to TRUE, enables a debug menu to use in battles by pressing the Select button. #define DEBUG_BATTLE_MENU TRUE // If set to TRUE, enables a debug menu to use in battles by pressing the Select button.
#define DEBUG_AI_DELAY_TIMER FALSE // If set to TRUE, displays the number of frames it takes for the AI to choose a move. Replaces the "What will PKMN do" text. Useful for devs or anyone who modifies the AI code and wants to see if it doesn't take too long to run. #define DEBUG_AI_DELAY_TIMER TRUE // If set to TRUE, displays the number of frames it takes for the AI to choose a move. Replaces the "What will PKMN do" text. Useful for devs or anyone who modifies the AI code and wants to see if it doesn't take too long to run.
// Pokémon Debug // Pokémon Debug
#define DEBUG_POKEMON_SPRITE_VISUALIZER TRUE // Enables a debug menu for Pokémon sprites and icons, accessed by pressing Select in the summary screen. #define DEBUG_POKEMON_SPRITE_VISUALIZER TRUE // Enables a debug menu for Pokémon sprites and icons, accessed by pressing Select in the summary screen.

View File

@ -34,8 +34,8 @@
static u32 ChooseMoveOrAction_Singles(u32 battlerAi); static u32 ChooseMoveOrAction_Singles(u32 battlerAi);
static u32 ChooseMoveOrAction_Doubles(u32 battlerAi); static u32 ChooseMoveOrAction_Doubles(u32 battlerAi);
static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef); static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef);
static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAi, u32 battlerDef); static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAi, u32 battlerDef);
static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect); static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect);
// ewram // ewram
@ -99,7 +99,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
void BattleAI_SetupItems(void) void BattleAI_SetupItems(void)
{ {
s32 i; s32 i;
u8 *data = (u8 *)BATTLE_HISTORY; u8 *data = (u8 *)gBattleHistory;
const u16 *items = GetTrainerItemsFromId(TRAINER_BATTLE_PARAM.opponentA); const u16 *items = GetTrainerItemsFromId(TRAINER_BATTLE_PARAM.opponentA);
for (i = 0; i < sizeof(struct BattleHistory); i++) for (i = 0; i < sizeof(struct BattleHistory); i++)
@ -117,8 +117,8 @@ void BattleAI_SetupItems(void)
{ {
if (items[i] != ITEM_NONE) if (items[i] != ITEM_NONE)
{ {
BATTLE_HISTORY->trainerItems[BATTLE_HISTORY->itemsNo] = items[i]; gBattleHistory->trainerItems[gBattleHistory->itemsNo] = items[i];
BATTLE_HISTORY->itemsNo++; gBattleHistory->itemsNo++;
} }
} }
} }
@ -196,43 +196,43 @@ static u32 GetAiFlags(u16 trainerId)
void BattleAI_SetupFlags(void) void BattleAI_SetupFlags(void)
{ {
if (IsAiVsAiBattle()) if (IsAiVsAiBattle())
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT] = GetAiFlags(gPartnerTrainerId); gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = GetAiFlags(gPartnerTrainerId);
else else
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI
if (DEBUG_OVERWORLD_MENU && gIsDebugBattle) if (DEBUG_OVERWORLD_MENU && gIsDebugBattle)
{ {
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags; gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags;
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags; gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags;
return; return;
} }
if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))) if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)))
{ {
// smart wild AI // smart wild AI
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF); gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF);
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF); gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF);
} }
else else
{ {
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA); gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA);
if (TRAINER_BATTLE_PARAM.opponentB != 0) if (TRAINER_BATTLE_PARAM.opponentB != 0)
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentB); gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentB);
else else
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT]; gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT];
} }
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{ {
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)); gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE));
} }
else if (IsDoubleBattle() && IsAiVsAiBattle()) else if (IsDoubleBattle() && IsAiVsAiBattle())
{ {
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT]; gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT];
} }
else else
{ {
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = 0; // player gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = 0; // player
} }
} }
@ -242,11 +242,11 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
u32 flags[MAX_BATTLERS_COUNT]; u32 flags[MAX_BATTLERS_COUNT];
// Clear AI data but preserve the flags. // Clear AI data but preserve the flags.
memcpy(&flags[0], &AI_THINKING_STRUCT->aiFlags[0], sizeof(u32) * MAX_BATTLERS_COUNT); memcpy(&flags[0], &gAiThinkingStruct->aiFlags[0], sizeof(u32) * MAX_BATTLERS_COUNT);
memset(AI_THINKING_STRUCT, 0, sizeof(struct AI_ThinkingStruct)); memset(gAiThinkingStruct, 0, sizeof(struct AiThinkingStruct));
memcpy(&AI_THINKING_STRUCT->aiFlags[0], &flags[0], sizeof(u32) * MAX_BATTLERS_COUNT); memcpy(&gAiThinkingStruct->aiFlags[0], &flags[0], sizeof(u32) * MAX_BATTLERS_COUNT);
moveLimitations = AI_DATA->moveLimitations[battler]; moveLimitations = gAiLogicData->moveLimitations[battler];
// Conditional score reset, unlike Ruby. // Conditional score reset, unlike Ruby.
for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
@ -267,13 +267,13 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
bool32 BattlerChoseNonMoveAction(void) bool32 BattlerChoseNonMoveAction(void)
{ {
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_FLEE) if (gAiThinkingStruct->aiAction & AI_ACTION_FLEE)
{ {
gAiBattleData->actionFlee = TRUE; gAiBattleData->actionFlee = TRUE;
return TRUE; return TRUE;
} }
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH) if (gAiThinkingStruct->aiAction & AI_ACTION_WATCH)
{ {
gAiBattleData->choiceWatch = TRUE; gAiBattleData->choiceWatch = TRUE;
return TRUE; return TRUE;
@ -287,24 +287,24 @@ void SetupAISwitchingData(u32 battler, enum SwitchType switchType)
s32 opposingBattler = GetOppositeBattler(battler); s32 opposingBattler = GetOppositeBattler(battler);
// AI's predicting data // AI's predicting data
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH)) if ((gAiThinkingStruct->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH))
{ {
AI_DATA->aiSwitchPredictionInProgress = TRUE; gAiLogicData->aiSwitchPredictionInProgress = TRUE;
AI_DATA->battlerDoingPrediction = battler; gAiLogicData->battlerDoingPrediction = battler;
AI_DATA->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType); gAiLogicData->mostSuitableMonId[opposingBattler] = GetMostSuitableMonToSwitchInto(opposingBattler, switchType);
if (ShouldSwitch(opposingBattler)) if (ShouldSwitch(opposingBattler))
AI_DATA->shouldSwitch |= (1u << opposingBattler); gAiLogicData->shouldSwitch |= (1u << opposingBattler);
AI_DATA->aiSwitchPredictionInProgress = FALSE; gAiLogicData->aiSwitchPredictionInProgress = FALSE;
gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species; gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species;
// Determine whether AI will use predictions this turn // Determine whether AI will use predictions this turn
AI_DATA->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, PREDICT_SWITCH_CHANCE); gAiLogicData->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, PREDICT_SWITCH_CHANCE);
} }
// AI's data // AI's data
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, switchType); gAiLogicData->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, switchType);
if (ShouldSwitch(battler)) if (ShouldSwitch(battler))
AI_DATA->shouldSwitch |= (1u << battler); gAiLogicData->shouldSwitch |= (1u << battler);
gBattleStruct->prevTurnSpecies[battler] = gBattleMons[battler].species; gBattleStruct->prevTurnSpecies[battler] = gBattleMons[battler].species;
} }
@ -319,13 +319,13 @@ void ComputeBattlerDecisions(u32 battler)
return; return;
// Risky AI switches aggressively even mid battle // Risky AI switches aggressively even mid battle
enum SwitchType switchType = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE; enum SwitchType switchType = (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE;
AI_DATA->aiCalcInProgress = TRUE; gAiLogicData->aiCalcInProgress = TRUE;
BattleAI_SetupAIData(0xF, battler); BattleAI_SetupAIData(0xF, battler);
SetupAISwitchingData(battler, switchType); SetupAISwitchingData(battler, switchType);
gAiBattleData->chosenMoveIndex[battler] = BattleAI_ChooseMoveIndex(battler); // Calculate score and chose move index gAiBattleData->chosenMoveIndex[battler] = BattleAI_ChooseMoveIndex(battler); // Calculate score and chose move index
AI_DATA->aiCalcInProgress = FALSE; gAiLogicData->aiCalcInProgress = FALSE;
} }
} }
@ -351,7 +351,7 @@ u32 BattleAI_ChooseMoveIndex(u32 battler)
static void CopyBattlerDataToAIParty(u32 bPosition, u32 side) static void CopyBattlerDataToAIParty(u32 bPosition, u32 side)
{ {
u32 battler = GetBattlerAtPosition(bPosition); u32 battler = GetBattlerAtPosition(bPosition);
struct AiPartyMon *aiMon = &AI_PARTY->mons[side][gBattlerPartyIndexes[battler]]; struct AiPartyMon *aiMon = &gAiPartyData->mons[side][gBattlerPartyIndexes[battler]];
struct BattlePokemon *bMon = &gBattleMons[battler]; struct BattlePokemon *bMon = &gBattleMons[battler];
aiMon->species = bMon->species; aiMon->species = bMon->species;
@ -366,11 +366,11 @@ static void CopyBattlerDataToAIParty(u32 bPosition, u32 side)
void Ai_InitPartyStruct(void) void Ai_InitPartyStruct(void)
{ {
u32 i; u32 i;
bool32 isOmniscient = (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_OMNISCIENT) || (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_OMNISCIENT); bool32 isOmniscient = (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_OMNISCIENT) || (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_OMNISCIENT);
struct Pokemon *mon; struct Pokemon *mon;
AI_PARTY->count[B_SIDE_PLAYER] = CalculatePlayerPartyCount(); gAiPartyData->count[B_SIDE_PLAYER] = CalculatePlayerPartyCount();
AI_PARTY->count[B_SIDE_OPPONENT] = CalculateEnemyPartyCount(); gAiPartyData->count[B_SIDE_OPPONENT] = CalculateEnemyPartyCount();
// Save first 2 or 4(in doubles) mons // Save first 2 or 4(in doubles) mons
CopyBattlerDataToAIParty(B_POSITION_PLAYER_LEFT, B_SIDE_PLAYER); CopyBattlerDataToAIParty(B_POSITION_PLAYER_LEFT, B_SIDE_PLAYER);
@ -385,20 +385,20 @@ void Ai_InitPartyStruct(void)
} }
// Find fainted mons // Find fainted mons
for (i = 0; i < AI_PARTY->count[B_SIDE_PLAYER]; i++) for (i = 0; i < gAiPartyData->count[B_SIDE_PLAYER]; i++)
{ {
if (GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0) if (GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0)
AI_PARTY->mons[B_SIDE_PLAYER][i].isFainted = TRUE; gAiPartyData->mons[B_SIDE_PLAYER][i].isFainted = TRUE;
if (isOmniscient) if (isOmniscient)
{ {
u32 j; u32 j;
mon = &gPlayerParty[i]; mon = &gPlayerParty[i];
AI_PARTY->mons[B_SIDE_PLAYER][i].item = GetMonData(mon, MON_DATA_HELD_ITEM); gAiPartyData->mons[B_SIDE_PLAYER][i].item = GetMonData(mon, MON_DATA_HELD_ITEM);
AI_PARTY->mons[B_SIDE_PLAYER][i].heldEffect = ItemId_GetHoldEffect(AI_PARTY->mons[B_SIDE_PLAYER][i].item); gAiPartyData->mons[B_SIDE_PLAYER][i].heldEffect = ItemId_GetHoldEffect(gAiPartyData->mons[B_SIDE_PLAYER][i].item);
AI_PARTY->mons[B_SIDE_PLAYER][i].ability = GetMonAbility(mon); gAiPartyData->mons[B_SIDE_PLAYER][i].ability = GetMonAbility(mon);
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
AI_PARTY->mons[B_SIDE_PLAYER][i].moves[j] = GetMonData(mon, MON_DATA_MOVE1 + j); gAiPartyData->mons[B_SIDE_PLAYER][i].moves[j] = GetMonData(mon, MON_DATA_MOVE1 + j);
} }
} }
} }
@ -407,19 +407,19 @@ void Ai_UpdateSwitchInData(u32 battler)
{ {
u32 i; u32 i;
u32 side = GetBattlerSide(battler); u32 side = GetBattlerSide(battler);
struct AiPartyMon *aiMon = &AI_PARTY->mons[side][gBattlerPartyIndexes[battler]]; struct AiPartyMon *aiMon = &gAiPartyData->mons[side][gBattlerPartyIndexes[battler]];
// See if the switched-in mon has been already in battle // See if the switched-in mon has been already in battle
if (aiMon->wasSentInBattle) if (aiMon->wasSentInBattle)
{ {
if (aiMon->ability) if (aiMon->ability)
BATTLE_HISTORY->abilities[battler] = aiMon->ability; gBattleHistory->abilities[battler] = aiMon->ability;
if (aiMon->heldEffect) if (aiMon->heldEffect)
BATTLE_HISTORY->itemEffects[battler] = aiMon->heldEffect; gBattleHistory->itemEffects[battler] = aiMon->heldEffect;
for (i = 0; i < MAX_MON_MOVES; i++) for (i = 0; i < MAX_MON_MOVES; i++)
{ {
if (aiMon->moves[i]) if (aiMon->moves[i])
BATTLE_HISTORY->usedMoves[battler][i] = aiMon->moves[i]; gBattleHistory->usedMoves[battler][i] = aiMon->moves[i];
} }
aiMon->switchInCount++; aiMon->switchInCount++;
aiMon->status = gBattleMons[battler].status1; // Copy status, because it could've been changed in battle. aiMon->status = gBattleMons[battler].status1; // Copy status, because it could've been changed in battle.
@ -435,7 +435,7 @@ void Ai_UpdateSwitchInData(u32 battler)
void Ai_UpdateFaintData(u32 battler) void Ai_UpdateFaintData(u32 battler)
{ {
struct AiPartyMon *aiMon = &AI_PARTY->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]]; struct AiPartyMon *aiMon = &gAiPartyData->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]];
ClearBattlerMoveHistory(battler); ClearBattlerMoveHistory(battler);
ClearBattlerAbilityHistory(battler); ClearBattlerAbilityHistory(battler);
ClearBattlerItemEffectHistory(battler); ClearBattlerItemEffectHistory(battler);
@ -530,7 +530,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
// get/assume all battler data and simulate AI damage // get/assume all battler data and simulate AI damage
battlersCount = gBattlersCount; battlersCount = gBattlersCount;
AI_DATA->aiCalcInProgress = TRUE; gAiLogicData->aiCalcInProgress = TRUE;
if (DEBUG_AI_DELAY_TIMER) if (DEBUG_AI_DELAY_TIMER)
CycleCountStart(); CycleCountStart();
for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++)
@ -551,7 +551,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
if (DEBUG_AI_DELAY_TIMER) if (DEBUG_AI_DELAY_TIMER)
// We add to existing to compound multiple calls // We add to existing to compound multiple calls
gBattleStruct->aiDelayCycles += CycleCountEnd(); gBattleStruct->aiDelayCycles += CycleCountEnd();
AI_DATA->aiCalcInProgress = FALSE; gAiLogicData->aiCalcInProgress = FALSE;
} }
static u32 ChooseMoveOrAction_Singles(u32 battlerAi) static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
@ -560,29 +560,29 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
u8 consideredMoveArray[MAX_MON_MOVES]; u8 consideredMoveArray[MAX_MON_MOVES];
u32 numOfBestMoves; u32 numOfBestMoves;
s32 i; s32 i;
u32 flags = AI_THINKING_STRUCT->aiFlags[battlerAi]; u32 flags = gAiThinkingStruct->aiFlags[battlerAi];
AI_DATA->partnerMove = 0; // no ally gAiLogicData->partnerMove = 0; // no ally
while (flags != 0) while (flags != 0)
{ {
if (flags & 1) if (flags & 1)
{ {
if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON)) if (IsBattlerPredictedToSwitch(gBattlerTarget) && (gAiThinkingStruct->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON))
BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battlerAi, gBattlerTarget); BattleAI_DoAIProcessing_PredictedSwitchin(gAiThinkingStruct, gAiLogicData, battlerAi, gBattlerTarget);
else else
BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi, gBattlerTarget); BattleAI_DoAIProcessing(gAiThinkingStruct, battlerAi, gBattlerTarget);
} }
flags >>= 1; flags >>= 1;
AI_THINKING_STRUCT->aiLogicId++; gAiThinkingStruct->aiLogicId++;
} }
for (i = 0; i < MAX_MON_MOVES; i++) for (i = 0; i < MAX_MON_MOVES; i++)
{ {
gAiBattleData->finalScore[battlerAi][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; gAiBattleData->finalScore[battlerAi][gBattlerTarget][i] = gAiThinkingStruct->score[i];
} }
numOfBestMoves = 1; numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; currentMoveArray[0] = gAiThinkingStruct->score[0];
consideredMoveArray[0] = 0; consideredMoveArray[0] = 0;
for (i = 1; i < MAX_MON_MOVES; i++) for (i = 1; i < MAX_MON_MOVES; i++)
@ -590,15 +590,15 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
if (gBattleMons[battlerAi].moves[i] != MOVE_NONE) if (gBattleMons[battlerAi].moves[i] != MOVE_NONE)
{ {
// In ruby, the order of these if statements is reversed. // In ruby, the order of these if statements is reversed.
if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) if (currentMoveArray[0] == gAiThinkingStruct->score[i])
{ {
currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->score[i]; currentMoveArray[numOfBestMoves] = gAiThinkingStruct->score[i];
consideredMoveArray[numOfBestMoves++] = i; consideredMoveArray[numOfBestMoves++] = i;
} }
if (currentMoveArray[0] < AI_THINKING_STRUCT->score[i]) if (currentMoveArray[0] < gAiThinkingStruct->score[i])
{ {
numOfBestMoves = 1; numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->score[i]; currentMoveArray[0] = gAiThinkingStruct->score[i];
consideredMoveArray[0] = i; consideredMoveArray[0] = i;
} }
} }
@ -635,25 +635,25 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
gBattlerTarget = i; gBattlerTarget = i;
AI_DATA->partnerMove = GetAllyChosenMove(battlerAi); gAiLogicData->partnerMove = GetAllyChosenMove(battlerAi);
AI_THINKING_STRUCT->aiLogicId = 0; gAiThinkingStruct->aiLogicId = 0;
AI_THINKING_STRUCT->movesetIndex = 0; gAiThinkingStruct->movesetIndex = 0;
flags = AI_THINKING_STRUCT->aiFlags[battlerAi]; flags = gAiThinkingStruct->aiFlags[battlerAi];
while (flags != 0) while (flags != 0)
{ {
if (flags & 1) if (flags & 1)
{ {
if (IsBattlerPredictedToSwitch(gBattlerTarget) && (AI_THINKING_STRUCT->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON)) if (IsBattlerPredictedToSwitch(gBattlerTarget) && (gAiThinkingStruct->aiFlags[battlerAi] & AI_FLAG_PREDICT_INCOMING_MON))
BattleAI_DoAIProcessing_PredictedSwitchin(AI_THINKING_STRUCT, AI_DATA, battlerAi, gBattlerTarget); BattleAI_DoAIProcessing_PredictedSwitchin(gAiThinkingStruct, gAiLogicData, battlerAi, gBattlerTarget);
else else
BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi, gBattlerTarget); BattleAI_DoAIProcessing(gAiThinkingStruct, battlerAi, gBattlerTarget);
} }
flags >>= 1; flags >>= 1;
AI_THINKING_STRUCT->aiLogicId++; gAiThinkingStruct->aiLogicId++;
} }
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[0]; mostViableMovesScores[0] = gAiThinkingStruct->score[0];
mostViableMovesIndices[0] = 0; mostViableMovesIndices[0] = 0;
mostViableMovesNo = 1; mostViableMovesNo = 1;
for (j = 1; j < MAX_MON_MOVES; j++) for (j = 1; j < MAX_MON_MOVES; j++)
@ -663,15 +663,15 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j])) if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j]))
continue; continue;
if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j]) if (mostViableMovesScores[0] == gAiThinkingStruct->score[j])
{ {
mostViableMovesScores[mostViableMovesNo] = AI_THINKING_STRUCT->score[j]; mostViableMovesScores[mostViableMovesNo] = gAiThinkingStruct->score[j];
mostViableMovesIndices[mostViableMovesNo] = j; mostViableMovesIndices[mostViableMovesNo] = j;
mostViableMovesNo++; mostViableMovesNo++;
} }
if (mostViableMovesScores[0] < AI_THINKING_STRUCT->score[j]) if (mostViableMovesScores[0] < gAiThinkingStruct->score[j])
{ {
mostViableMovesScores[0] = AI_THINKING_STRUCT->score[j]; mostViableMovesScores[0] = gAiThinkingStruct->score[j];
mostViableMovesIndices[0] = j; mostViableMovesIndices[0] = j;
mostViableMovesNo = 1; mostViableMovesNo = 1;
} }
@ -688,7 +688,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
{ {
gAiBattleData->finalScore[battlerAi][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; gAiBattleData->finalScore[battlerAi][gBattlerTarget][j] = gAiThinkingStruct->score[j];
} }
} }
} }
@ -728,7 +728,7 @@ static inline bool32 ShouldConsiderMoveForBattler(u32 battlerAi, u32 battlerDef,
return TRUE; return TRUE;
} }
static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef) static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef)
{ {
do do
{ {
@ -763,7 +763,7 @@ static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u3
aiThink->movesetIndex = 0; aiThink->movesetIndex = 0;
} }
void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef) void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef)
{ {
struct BattlePokemon switchoutCandidate = gBattleMons[battlerDef]; struct BattlePokemon switchoutCandidate = gBattleMons[battlerDef];
struct SimulatedDamage simulatedDamageSwitchout[MAX_MON_MOVES]; struct SimulatedDamage simulatedDamageSwitchout[MAX_MON_MOVES];
@ -881,8 +881,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
enum BattleMoveEffects moveEffect = GetMoveEffect(move); enum BattleMoveEffects moveEffect = GetMoveEffect(move);
s32 moveType; s32 moveType;
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex];
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
u32 i; u32 i;
u32 weather; u32 weather;
@ -1151,7 +1151,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-20); ADJUST_SCORE(-20);
break; break;
case EFFECT_EXPLOSION: case EFFECT_EXPLOSION:
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE)) if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE))
ADJUST_SCORE(-2); ADJUST_SCORE(-2);
if (effectiveness == UQ_4_12(0.0)) if (effectiveness == UQ_4_12(0.0))
@ -1501,7 +1501,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-1); ADJUST_SCORE(-1);
if ((predictedMove == MOVE_NONE || GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_STATUS if ((predictedMove == MOVE_NONE || GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_STATUS
|| DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove)) || DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove))
&& !(predictedMove == MOVE_NONE && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY))) // Let Risky AI predict blindly based on stats && !(predictedMove == MOVE_NONE && (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY))) // Let Risky AI predict blindly based on stats
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
break; break;
@ -2006,8 +2006,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better
break; break;
case EFFECT_RECOIL_IF_MISS: case EFFECT_RECOIL_IF_MISS:
if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] < 75 if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && gAiLogicData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] < 75
&& !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)) && !(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY))
ADJUST_SCORE(-6); ADJUST_SCORE(-6);
break; break;
case EFFECT_TRANSFORM: case EFFECT_TRANSFORM:
@ -2122,7 +2122,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
} }
} }
/*if (AI_THINKING_STRUCT->aiFlags[battlerAtk] == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI /*if (gAiThinkingStruct->aiFlags[battlerAtk] == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI
&& IsDoubleBattle()) //Make the regular AI know how to use Protect minimally in Doubles && IsDoubleBattle()) //Make the regular AI know how to use Protect minimally in Doubles
{ {
u8 shouldProtect = ShouldProtect(battlerAtk, battlerDef, move); u8 shouldProtect = ShouldProtect(battlerAtk, battlerDef, move);
@ -2436,7 +2436,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
} }
else if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) else if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS))
{ {
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up
{ {
@ -2759,7 +2759,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// Don't use a status move if the mon is the last one in the party, has no good switchin, or is trapped // Don't use a status move if the mon is the last one in the party, has no good switchin, or is trapped
else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS
&& (CountUsablePartyMons(battlerAtk) < 1 && (CountUsablePartyMons(battlerAtk) < 1
|| AI_DATA->mostSuitableMonId[battlerAtk] == PARTY_SIZE || gAiLogicData->mostSuitableMonId[battlerAtk] == PARTY_SIZE
|| (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk)))) || (!AI_CanBattlerEscape(battlerAtk) && IsBattlerTrapped(battlerDef, battlerAtk))))
ADJUST_SCORE(-30); ADJUST_SCORE(-30);
} }
@ -2772,7 +2772,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex; u32 movesetIndex = gAiThinkingStruct->movesetIndex;
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score; return score;
@ -2791,7 +2791,7 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
} }
else if (CanTargetFaintAi(battlerDef, battlerAtk) else if (CanTargetFaintAi(battlerDef, battlerAtk)
&& GetWhichBattlerFasterOrTies(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER && GetWhichBattlerFasterOrTies(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
&& GetBattleMovePriority(battlerAtk, AI_DATA->abilities[battlerAtk], move) > 0) && GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], move) > 0)
{ {
ADJUST_SCORE(LAST_CHANCE); ADJUST_SCORE(LAST_CHANCE);
} }
@ -2808,7 +2808,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
// ally data // ally data
u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk); u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk);
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
u32 atkPartnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)]; u32 atkPartnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)];
u32 atkPartnerHoldEffect = aiData->holdEffects[BATTLE_PARTNER(battlerAtk)]; u32 atkPartnerHoldEffect = aiData->holdEffects[BATTLE_PARTNER(battlerAtk)];
enum BattleMoveEffects partnerEffect = GetMoveEffect(aiData->partnerMove); enum BattleMoveEffects partnerEffect = GetMoveEffect(aiData->partnerMove);
@ -2949,13 +2949,13 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (MoveAlwaysCrits(move) if (MoveAlwaysCrits(move)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& AI_IsFaster(battlerAtk, battlerAtkPartner, move) && AI_IsFaster(battlerAtk, battlerAtkPartner, move)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING)) && !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING))
{ {
RETURN_SCORE_PLUS(GOOD_EFFECT); RETURN_SCORE_PLUS(GOOD_EFFECT);
} }
break; break;
case ABILITY_VOLT_ABSORB: case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE)) if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE))
{ {
RETURN_SCORE_MINUS(10); RETURN_SCORE_MINUS(10);
} }
@ -2978,7 +2978,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case ABILITY_WATER_ABSORB: case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN: case ABILITY_DRY_SKIN:
case ABILITY_EARTH_EATER: case ABILITY_EARTH_EATER:
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE)) if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE))
{ {
RETURN_SCORE_MINUS(10); RETURN_SCORE_MINUS(10);
} }
@ -2993,7 +2993,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
} }
break; break;
case ABILITY_WATER_COMPACTION: case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) >= 4) if (moveType == TYPE_WATER && GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING) >= 4)
{ {
RETURN_SCORE_PLUS(WEAK_EFFECT); // only mon with this ability is weak to water so only make it okay if we do very little damage RETURN_SCORE_PLUS(WEAK_EFFECT); // only mon with this ability is weak to water so only make it okay if we do very little damage
} }
@ -3020,7 +3020,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& !IsBattleMoveStatus(move) && !IsBattleMoveStatus(move)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL) && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING)) && !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING))
{ {
RETURN_SCORE_PLUS(WEAK_EFFECT); RETURN_SCORE_PLUS(WEAK_EFFECT);
} }
@ -3029,7 +3029,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (!IsBattleMoveStatus(move) if (!IsBattleMoveStatus(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG) && (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING)) && !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING))
{ {
RETURN_SCORE_PLUS(WEAK_EFFECT); RETURN_SCORE_PLUS(WEAK_EFFECT);
} }
@ -3086,7 +3086,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& !IsBattleMoveStatus(move) && !IsBattleMoveStatus(move)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL) && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK) && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING)) && !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING))
{ {
RETURN_SCORE_PLUS(WEAK_EFFECT); RETURN_SCORE_PLUS(WEAK_EFFECT);
} }
@ -3225,8 +3225,8 @@ static bool32 IsPinchBerryItemEffect(enum ItemHoldEffect holdEffect)
static s32 CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1, u32 moveSlot2) static s32 CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1, u32 moveSlot2)
{ {
u32 acc1 = AI_DATA->moveAccuracy[battlerAtk][battlerDef][moveSlot1]; u32 acc1 = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][moveSlot1];
u32 acc2 = AI_DATA->moveAccuracy[battlerAtk][battlerDef][moveSlot2]; u32 acc2 = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][moveSlot2];
if (acc1 > acc2) if (acc1 > acc2)
return 1; return 1;
@ -3349,7 +3349,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
{ {
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
enum ItemHoldEffect holdEffect = aiData->holdEffects[battlerAtk]; enum ItemHoldEffect holdEffect = aiData->holdEffects[battlerAtk];
s32 score = 0; s32 score = 0;
@ -3358,7 +3358,7 @@ static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
{ {
case HOLD_EFFECT_BLUNDER_POLICY: case HOLD_EFFECT_BLUNDER_POLICY:
{ {
u32 moveAcc = aiData->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; u32 moveAcc = aiData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex];
if (moveAcc <= LOW_ACCURACY_THRESHOLD) if (moveAcc <= LOW_ACCURACY_THRESHOLD)
{ {
@ -3381,8 +3381,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
{ {
// move data // move data
enum BattleMoveEffects moveEffect = GetMoveEffect(move); enum BattleMoveEffects moveEffect = GetMoveEffect(move);
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex; u32 movesetIndex = gAiThinkingStruct->movesetIndex;
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex]; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex];
s32 score = 0; s32 score = 0;
@ -3397,7 +3397,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
moveEffect = EFFECT_PROTECT; moveEffect = EFFECT_PROTECT;
// check status move preference // check status move preference
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IsBattleMoveStatus(move) && effectiveness != UQ_4_12(0.0)) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IsBattleMoveStatus(move) && effectiveness != UQ_4_12(0.0))
ADJUST_SCORE(10); ADJUST_SCORE(10);
// check thawing moves // check thawing moves
@ -3405,7 +3405,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(10); ADJUST_SCORE(10);
// check burn / frostbite // check burn / frostbite
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && AI_DATA->abilities[battlerAtk] == ABILITY_NATURAL_CURE) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && gAiLogicData->abilities[battlerAtk] == ABILITY_NATURAL_CURE)
{ {
if ((gBattleMons[battlerAtk].status1 & STATUS1_BURN && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL, TRUE)) if ((gBattleMons[battlerAtk].status1 & STATUS1_BURN && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL, TRUE))
|| (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL, TRUE))) || (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && HasOnlyMovesWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL, TRUE)))
@ -3431,14 +3431,14 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break; break;
case EFFECT_EXPLOSION: case EFFECT_EXPLOSION:
case EFFECT_MEMENTO: case EFFECT_MEMENTO:
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7)
{ {
if (aiData->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128)) if (aiData->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128))
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
} }
break; break;
case EFFECT_FINAL_GAMBIT: case EFFECT_FINAL_GAMBIT:
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE)
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
break; break;
case EFFECT_MIRROR_MOVE: case EFFECT_MIRROR_MOVE:
@ -3767,7 +3767,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
} }
break; break;
case EFFECT_BATON_PASS: case EFFECT_BATON_PASS:
if ((AI_DATA->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE if ((gAiLogicData->shouldSwitch & (1u << battlerAtk)) && (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE
|| (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING | STATUS3_MAGNET_RISE | STATUS3_POWER_TRICK)) || (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING | STATUS3_MAGNET_RISE | STATUS3_POWER_TRICK))
|| AnyStatIsRaised(battlerAtk))) || AnyStatIsRaised(battlerAtk)))
ADJUST_SCORE(BEST_EFFECT); ADJUST_SCORE(BEST_EFFECT);
@ -4451,7 +4451,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move
break; break;
case EFFECT_TRICK_ROOM: case EFFECT_TRICK_ROOM:
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) if (!(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS))
{ {
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef))
ADJUST_SCORE(GOOD_EFFECT); ADJUST_SCORE(GOOD_EFFECT);
@ -4620,9 +4620,9 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (GetFirstFaintedPartyIndex(battlerAtk) != PARTY_SIZE) if (GetFirstFaintedPartyIndex(battlerAtk) != PARTY_SIZE)
{ {
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
if (AI_DATA->shouldSwitch & (1u << battlerAtk)) // Bad matchup if (gAiLogicData->shouldSwitch & (1u << battlerAtk)) // Bad matchup
ADJUST_SCORE(WEAK_EFFECT); ADJUST_SCORE(WEAK_EFFECT);
if (AI_DATA->mostSuitableMonId[battlerAtk] != PARTY_SIZE) // Good mon to send in after if (gAiLogicData->mostSuitableMonId[battlerAtk] != PARTY_SIZE) // Good mon to send in after
ADJUST_SCORE(WEAK_EFFECT); ADJUST_SCORE(WEAK_EFFECT);
} }
break; break;
@ -4909,15 +4909,15 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
if (GetMovePower(move) != 0) if (GetMovePower(move) != 0)
{ {
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 0) if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING) == 0)
ADJUST_AND_RETURN_SCORE(NO_DAMAGE_OR_FAILS); // No point in checking the move further so return early ADJUST_AND_RETURN_SCORE(NO_DAMAGE_OR_FAILS); // No point in checking the move further so return early
else else
{ {
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & (AI_FLAG_RISKY | AI_FLAG_PREFER_HIGHEST_DAMAGE_MOVE) if (gAiThinkingStruct->aiFlags[battlerAtk] & (AI_FLAG_RISKY | AI_FLAG_PREFER_HIGHEST_DAMAGE_MOVE)
&& GetBestDmgMoveFromBattler(battlerAtk, battlerDef, AI_ATTACKING) == move) && GetBestDmgMoveFromBattler(battlerAtk, battlerDef, AI_ATTACKING) == move)
ADJUST_SCORE(BEST_DAMAGE_MOVE); ADJUST_SCORE(BEST_DAMAGE_MOVE);
else else
ADJUST_SCORE(AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex)); ADJUST_SCORE(AI_CompareDamagingMoves(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex));
} }
} }
@ -4935,10 +4935,10 @@ static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32
|| gBattleResults.battleTurnCounter != 0) || gBattleResults.battleTurnCounter != 0)
return score; return score;
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING
&& AI_IsSlower(battlerAtk, battlerDef, move) && AI_IsSlower(battlerAtk, battlerDef, move)
&& CanTargetFaintAi(battlerDef, battlerAtk) && CanTargetFaintAi(battlerDef, battlerAtk)
&& GetBattleMovePriority(battlerAtk, AI_DATA->abilities[battlerAtk], move) == 0) && GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], move) == 0)
{ {
RETURN_SCORE_MINUS(20); // No point in setting up if you will faint. Should just switch if possible.. RETURN_SCORE_MINUS(20); // No point in setting up if you will faint. Should just switch if possible..
} }
@ -5062,7 +5062,7 @@ static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32
static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
u8 i; u8 i;
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score; return score;
@ -5138,9 +5138,9 @@ static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score; return score;
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 1) if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING) == 1)
ADJUST_SCORE(BEST_EFFECT); ADJUST_SCORE(BEST_EFFECT);
else if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 2) else if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING) == 2)
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
return score; return score;
@ -5162,7 +5162,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
{ {
if (gBattleResults.battleTurnCounter == 0) if (gBattleResults.battleTurnCounter == 0)
ADJUST_SCORE(GOOD_EFFECT); ADJUST_SCORE(GOOD_EFFECT);
else if (AI_DATA->hpPercents[battlerAtk] < 60) else if (gAiLogicData->hpPercents[battlerAtk] < 60)
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
else else
ADJUST_SCORE(WEAK_EFFECT); ADJUST_SCORE(WEAK_EFFECT);
@ -5210,9 +5210,9 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
{ {
if ((effect == EFFECT_HEAL_PULSE || effect == EFFECT_HIT_ENEMY_HEAL_ALLY) if ((effect == EFFECT_HEAL_PULSE || effect == EFFECT_HIT_ENEMY_HEAL_ALLY)
|| (moveType == TYPE_ELECTRIC && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_VOLT_ABSORB) || (moveType == TYPE_ELECTRIC && gAiLogicData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_VOLT_ABSORB)
|| (moveType == TYPE_GROUND && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_EARTH_EATER) || (moveType == TYPE_GROUND && gAiLogicData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_EARTH_EATER)
|| (moveType == TYPE_WATER && (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_DRY_SKIN || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_WATER_ABSORB))) || (moveType == TYPE_WATER && (gAiLogicData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_DRY_SKIN || gAiLogicData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_WATER_ABSORB)))
{ {
if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK) if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK)
return 0; return 0;
@ -5221,14 +5221,14 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), BATTLE_PARTNER(battlerAtk)))) || (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), BATTLE_PARTNER(battlerAtk))))
ADJUST_SCORE(-1); ADJUST_SCORE(-1);
if (AI_DATA->hpPercents[battlerDef] <= 50) if (gAiLogicData->hpPercents[battlerDef] <= 50)
ADJUST_SCORE(WEAK_EFFECT); ADJUST_SCORE(WEAK_EFFECT);
} }
} }
else else
{ {
// Consider AI HP // Consider AI HP
if (AI_DATA->hpPercents[battlerAtk] > 70) if (gAiLogicData->hpPercents[battlerAtk] > 70)
{ {
// high hp // high hp
switch (effect) switch (effect)
@ -5253,7 +5253,7 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break; break;
} }
} }
else if (AI_DATA->hpPercents[battlerAtk] > 30) else if (gAiLogicData->hpPercents[battlerAtk] > 30)
{ {
// med hp // med hp
if (IsStatRaisingEffect(effect) || IsStatLoweringEffect(effect)) if (IsStatRaisingEffect(effect) || IsStatLoweringEffect(effect))
@ -5316,18 +5316,18 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
} }
// consider target HP // consider target HP
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING)) if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING))
{ {
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
} }
else else
{ {
if (AI_DATA->hpPercents[battlerDef] > 70) if (gAiLogicData->hpPercents[battlerDef] > 70)
{ {
// high HP // high HP
; // nothing yet ; // nothing yet
} }
else if (AI_DATA->hpPercents[battlerDef] > 30) else if (gAiLogicData->hpPercents[battlerDef] > 30)
{ {
// med HP - check discouraged effects // med HP - check discouraged effects
switch (effect) switch (effect)
@ -5395,7 +5395,7 @@ static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
{ {
enum BattleMoveEffects moveEffect = GetMoveEffect(move); enum BattleMoveEffects moveEffect = GetMoveEffect(move);
if (GetMoveCategory(move) != DAMAGE_CATEGORY_STATUS || GetMoveEffect(AI_DATA->partnerMove) == moveEffect) if (GetMoveCategory(move) != DAMAGE_CATEGORY_STATUS || GetMoveEffect(gAiLogicData->partnerMove) == moveEffect)
return score; return score;
switch (moveEffect) switch (moveEffect)
@ -5438,7 +5438,7 @@ static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
case EFFECT_STEALTH_ROCK: case EFFECT_STEALTH_ROCK:
case EFFECT_STICKY_WEB: case EFFECT_STICKY_WEB:
case EFFECT_TOXIC_SPIKES: case EFFECT_TOXIC_SPIKES:
if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, AI_DATA)) if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, gAiLogicData))
ADJUST_SCORE(POWERFUL_STATUS_MOVE); ADJUST_SCORE(POWERFUL_STATUS_MOVE);
break; break;
case EFFECT_GRASSY_TERRAIN: case EFFECT_GRASSY_TERRAIN:
@ -5492,15 +5492,15 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
u32 opposingHazardFlags = gSideStatuses[GetBattlerSide(battlerDef)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_TOXIC_SPIKES); u32 opposingHazardFlags = gSideStatuses[GetBattlerSide(battlerDef)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_TOXIC_SPIKES);
u32 aiHazardFlags = gSideStatuses[GetBattlerSide(battlerAtk)] & (SIDE_STATUS_HAZARDS_ANY); u32 aiHazardFlags = gSideStatuses[GetBattlerSide(battlerAtk)] & (SIDE_STATUS_HAZARDS_ANY);
enum BattleMoveEffects moveEffect = GetMoveEffect(move); enum BattleMoveEffects moveEffect = GetMoveEffect(move);
struct AiLogicData *aiData = AI_DATA; struct AiLogicData *aiData = gAiLogicData;
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex];
// Switch benefit // Switch benefit
switch (moveEffect) switch (moveEffect)
{ {
case EFFECT_PURSUIT: case EFFECT_PURSUIT:
{ {
u32 hitsToKO = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING); u32 hitsToKO = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING);
if (hitsToKO == 2) if (hitsToKO == 2)
ADJUST_SCORE(GOOD_EFFECT); ADJUST_SCORE(GOOD_EFFECT);
else if (hitsToKO == 1) else if (hitsToKO == 1)
@ -5512,7 +5512,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_FOCUS_PUNCH: case EFFECT_FOCUS_PUNCH:
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CHECK_BAD_MOVE) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_CHECK_BAD_MOVE)
{ {
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0)) if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
ADJUST_SCORE(10); ADJUST_SCORE(10);
@ -5573,7 +5573,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
break; break;
case EFFECT_RESTORE_HP: case EFFECT_RESTORE_HP:
if (AI_DATA->hpPercents[battlerAtk] < 60) if (gAiLogicData->hpPercents[battlerAtk] < 60)
ADJUST_SCORE(GOOD_EFFECT); ADJUST_SCORE(GOOD_EFFECT);
break; break;
@ -5639,12 +5639,12 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
static void AI_Flee(void) static void AI_Flee(void)
{ {
AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK); gAiThinkingStruct->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK);
} }
static void AI_Watch(void) static void AI_Watch(void)
{ {
AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_WATCH | AI_ACTION_DO_NOT_ATTACK); gAiThinkingStruct->aiAction |= (AI_ACTION_DONE | AI_ACTION_WATCH | AI_ACTION_DO_NOT_ATTACK);
} }
// Roaming pokemon logic // Roaming pokemon logic
@ -5654,9 +5654,9 @@ static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (AI_CanBattlerEscape(battlerAtk)) if (AI_CanBattlerEscape(battlerAtk))
roamerCanFlee = TRUE; roamerCanFlee = TRUE;
else if (AI_DATA->abilities[battlerAtk] == ABILITY_RUN_AWAY) else if (gAiLogicData->abilities[battlerAtk] == ABILITY_RUN_AWAY)
roamerCanFlee = TRUE; roamerCanFlee = TRUE;
else if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CAN_ALWAYS_RUN) else if (gAiLogicData->holdEffects[battlerAtk] == HOLD_EFFECT_CAN_ALWAYS_RUN)
roamerCanFlee = TRUE; roamerCanFlee = TRUE;
if (!roamerCanFlee && IsBattlerTrapped(battlerDef, battlerAtk)) if (!roamerCanFlee && IsBattlerTrapped(battlerDef, battlerAtk))
@ -5682,7 +5682,7 @@ static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// First battle logic // First battle logic
static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
if (AI_DATA->hpPercents[battlerDef] <= 20) if (gAiLogicData->hpPercents[battlerDef] <= 20)
AI_Flee(); AI_Flee();
return score; return score;

View File

@ -34,8 +34,8 @@ static u32 GetHPHealAmount(u8 itemEffectParam, struct Pokemon *mon);
static void InitializeSwitchinCandidate(struct Pokemon *mon) static void InitializeSwitchinCandidate(struct Pokemon *mon)
{ {
PokemonToBattleMon(mon, &AI_DATA->switchinCandidate.battleMon); PokemonToBattleMon(mon, &gAiLogicData->switchinCandidate.battleMon);
AI_DATA->switchinCandidate.hypotheticalStatus = FALSE; gAiLogicData->switchinCandidate.hypotheticalStatus = FALSE;
} }
u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario) u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario)
@ -108,18 +108,18 @@ u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario)
u32 GetThinkingBattler(u32 battler) u32 GetThinkingBattler(u32 battler)
{ {
if (AI_DATA->aiSwitchPredictionInProgress) if (gAiLogicData->aiSwitchPredictionInProgress)
return AI_DATA->battlerDoingPrediction; return gAiLogicData->battlerDoingPrediction;
return battler; return battler;
} }
static bool32 IsAceMon(u32 battler, u32 monPartyId) static bool32 IsAceMon(u32 battler, u32 monPartyId)
{ {
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_ACE_POKEMON if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_ACE_POKEMON
&& !gBattleStruct->battlerState[battler].forcedSwitch && !gBattleStruct->battlerState[battler].forcedSwitch
&& monPartyId == CalculateEnemyPartyCountInSide(battler)-1) && monPartyId == CalculateEnemyPartyCountInSide(battler)-1)
return TRUE; return TRUE;
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_DOUBLE_ACE_POKEMON if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_DOUBLE_ACE_POKEMON
&& !gBattleStruct->battlerState[battler].forcedSwitch && !gBattleStruct->battlerState[battler].forcedSwitch
&& (monPartyId == CalculateEnemyPartyCount()-1 || monPartyId == CalculateEnemyPartyCount()-2)) && (monPartyId == CalculateEnemyPartyCount()-1 || monPartyId == CalculateEnemyPartyCount()-2))
return TRUE; return TRUE;
@ -168,7 +168,7 @@ static inline bool32 SetSwitchinAndSwitch(u32 battler, u32 switchinId)
static bool32 AI_DoesChoiceItemBlockMove(u32 battler, u32 move) static bool32 AI_DoesChoiceItemBlockMove(u32 battler, u32 move)
{ {
// Choice locked into something else // Choice locked into something else
if (AI_DATA->lastUsedMove[battler] != MOVE_NONE && AI_DATA->lastUsedMove[battler] != move && HOLD_EFFECT_CHOICE(GetBattlerHoldEffect(battler, FALSE)) && IsBattlerItemEnabled(battler)) if (gAiLogicData->lastUsedMove[battler] != MOVE_NONE && gAiLogicData->lastUsedMove[battler] != move && HOLD_EFFECT_CHOICE(GetBattlerHoldEffect(battler, FALSE)) && IsBattlerItemEnabled(battler))
return TRUE; return TRUE;
return FALSE; return FALSE;
} }
@ -181,13 +181,13 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
//Variable initialization //Variable initialization
u8 opposingPosition, atkType1, atkType2, defType1, defType2; u8 opposingPosition, atkType1, atkType2, defType1, defType2;
s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0; s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0;
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = AI_DATA->abilities[battler], opposingBattler; u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = gAiLogicData->abilities[battler], opposingBattler;
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE; bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
u16 typeEffectiveness = UQ_4_12(1.0); //baseline typing damage u16 typeEffectiveness = UQ_4_12(1.0); //baseline typing damage
enum BattleMoveEffects aiMoveEffect; enum BattleMoveEffects aiMoveEffect;
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
// Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
@ -228,7 +228,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
hasSuperEffectiveMove = TRUE; hasSuperEffectiveMove = TRUE;
// Get maximum damage mon can deal // Get maximum damage mon can deal
damageDealt = AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, AI_DATA); damageDealt = AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, gAiLogicData);
if(damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove)) if(damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove))
{ {
maxDamageDealt = damageDealt; maxDamageDealt = damageDealt;
@ -255,7 +255,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
playerMove = gBattleMons[opposingBattler].moves[i]; playerMove = gBattleMons[opposingBattler].moves[i];
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH) if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH)
{ {
damageTaken = AI_GetDamage(opposingBattler, battler, i, AI_DEFENDING, AI_DATA); damageTaken = AI_GetDamage(opposingBattler, battler, i, AI_DEFENDING, gAiLogicData);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
{ {
return maxDamageTaken = damageTaken; return maxDamageTaken = damageTaken;
@ -281,7 +281,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
} }
// If we don't have any other viable options, don't switch out // If we don't have any other viable options, don't switch out
if (AI_DATA->mostSuitableMonId[battler] == PARTY_SIZE) if (gAiLogicData->mostSuitableMonId[battler] == PARTY_SIZE)
return FALSE; return FALSE;
// Start assessing whether or not mon has bad odds // Start assessing whether or not mon has bad odds
@ -293,7 +293,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4)))
{ {
// 50% chance to stay in regardless // 50% chance to stay in regardless
if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !gAiLogicData->aiSwitchPredictionInProgress)
return FALSE; return FALSE;
// Switch mon out // Switch mon out
@ -313,7 +313,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
return FALSE; return FALSE;
// 50% chance to stay in regardless // 50% chance to stay in regardless
if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !gAiLogicData->aiSwitchPredictionInProgress)
return FALSE; return FALSE;
// Switch mon out // Switch mon out
@ -326,11 +326,11 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
static bool32 ShouldSwitchIfTruant(u32 battler) static bool32 ShouldSwitchIfTruant(u32 battler)
{ {
// Switch if mon with truant is bodied by Protect or invulnerability spam // Switch if mon with truant is bodied by Protect or invulnerability spam
if (AI_DATA->abilities[battler] == ABILITY_TRUANT if (gAiLogicData->abilities[battler] == ABILITY_TRUANT
&& IsTruantMonVulnerable(battler, gBattlerTarget) && IsTruantMonVulnerable(battler, gBattlerTarget)
&& gDisableStructs[battler].truantCounter && gDisableStructs[battler].truantCounter
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE)
{ {
if (RandomPercentage(RNG_AI_SWITCH_TRUANT, GetSwitchChance(SHOULD_SWITCH_TRUANT))) if (RandomPercentage(RNG_AI_SWITCH_TRUANT, GetSwitchChance(SHOULD_SWITCH_TRUANT)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
@ -402,7 +402,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
if (RandomPercentage(RNG_AI_SWITCH_ALL_MOVES_BAD, GetSwitchChance(SHOULD_SWITCH_ALL_MOVES_BAD))) if (RandomPercentage(RNG_AI_SWITCH_ALL_MOVES_BAD, GetSwitchChance(SHOULD_SWITCH_ALL_MOVES_BAD)))
{ {
if (AI_DATA->mostSuitableMonId[battler] == PARTY_SIZE) // No good candidate mons, find any one that can deal damage if (gAiLogicData->mostSuitableMonId[battler] == PARTY_SIZE) // No good candidate mons, find any one that can deal damage
return FindMonWithMoveOfEffectiveness(battler, opposingBattler, UQ_4_12(1.0)); return FindMonWithMoveOfEffectiveness(battler, opposingBattler, UQ_4_12(1.0));
else // Good candidate mon, send that in else // Good candidate mon, send that in
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
@ -419,7 +419,7 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler)
if (IsDoubleBattle()) if (IsDoubleBattle())
return FALSE; return FALSE;
if (AI_DATA->abilities[opposingBattler] != ABILITY_WONDER_GUARD) if (gAiLogicData->abilities[opposingBattler] != ABILITY_WONDER_GUARD)
return FALSE; return FALSE;
// Check if Pokémon has a super effective move. // Check if Pokémon has a super effective move.
@ -435,7 +435,7 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler)
if (RandomPercentage(RNG_AI_SWITCH_WONDER_GUARD, GetSwitchChance(SHOULD_SWITCH_WONDER_GUARD))) if (RandomPercentage(RNG_AI_SWITCH_WONDER_GUARD, GetSwitchChance(SHOULD_SWITCH_WONDER_GUARD)))
{ {
if (AI_DATA->mostSuitableMonId[battler] == PARTY_SIZE) // No good candidate mons, find any one that can deal damage if (gAiLogicData->mostSuitableMonId[battler] == PARTY_SIZE) // No good candidate mons, find any one that can deal damage
return FindMonWithMoveOfEffectiveness(battler, opposingBattler, UQ_4_12(2.0)); return FindMonWithMoveOfEffectiveness(battler, opposingBattler, UQ_4_12(2.0));
else // Good candidate mon, send that in else // Good candidate mon, send that in
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
@ -454,18 +454,18 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
struct Pokemon *party; struct Pokemon *party;
u16 monAbility, aiMove; u16 monAbility, aiMove;
u32 opposingBattler = GetOppositeBattler(battler); u32 opposingBattler = GetOppositeBattler(battler);
u32 incomingMove = AI_DATA->lastUsedMove[opposingBattler]; u32 incomingMove = gAiLogicData->lastUsedMove[opposingBattler];
u32 incomingType = GetMoveType(incomingMove); u32 incomingType = GetMoveType(incomingMove);
u32 predictedMove = incomingMove; // Update for move prediction u32 predictedMove = incomingMove; // Update for move prediction
u32 predictedType = GetMoveType(predictedMove); u32 predictedType = GetMoveType(predictedMove);
bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove)); bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove));
s32 i, j; s32 i, j;
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
if (gBattleStruct->prevTurnSpecies[battler] != gBattleMons[battler].species) // AI mon has changed, player's behaviour no longer reliable; note to override this if using AI_FLAG_PREDICT_MOVE if (gBattleStruct->prevTurnSpecies[battler] != gBattleMons[battler].species) // AI mon has changed, player's behaviour no longer reliable; note to override this if using AI_FLAG_PREDICT_MOVE
return FALSE; return FALSE;
if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING_STAY_IN, STAY_IN_ABSORBING_PERCENTAGE) || AI_DATA->aiSwitchPredictionInProgress)) if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING_STAY_IN, STAY_IN_ABSORBING_PERCENTAGE) || gAiLogicData->aiSwitchPredictionInProgress))
return FALSE; return FALSE;
if (AreStatsRaised(battler)) if (AreStatsRaised(battler))
return FALSE; return FALSE;
@ -479,7 +479,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
// Only check damage if it's a damaging move // Only check damage if it's a damaging move
if (!IsBattleMoveStatus(aiMove)) if (!IsBattleMoveStatus(aiMove))
{ {
if (AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, AI_DATA) > gBattleMons[opposingBattler].hp) if (AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, gAiLogicData) > gBattleMons[opposingBattler].hp)
return FALSE; return FALSE;
} }
} }
@ -581,13 +581,13 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler) static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler)
{ {
u32 opposingBattler = GetOppositeBattler(battler); u32 opposingBattler = GetOppositeBattler(battler);
u32 incomingMove = AI_DATA->lastUsedMove[opposingBattler]; u32 incomingMove = gAiLogicData->lastUsedMove[opposingBattler];
bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove)); bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove));
if (IsDoubleBattle() || !(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (IsDoubleBattle() || !(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
if (isOpposingBattlerChargingOrInvulnerable && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN))) if (isOpposingBattlerChargingOrInvulnerable && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
return FALSE; return FALSE;
@ -603,7 +603,7 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler)
s32 opposingBattler = GetOppositeBattler(battler); s32 opposingBattler = GetOppositeBattler(battler);
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
// Check if current mon has an ability that traps opponent // Check if current mon has an ability that traps opponent
@ -624,7 +624,7 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler)
if (CanAbilityTrapOpponent(monAbility, opposingBattler) || (CanAbilityTrapOpponent(AI_GetBattlerAbility(opposingBattler), opposingBattler) && monAbility == ABILITY_TRACE)) if (CanAbilityTrapOpponent(monAbility, opposingBattler) || (CanAbilityTrapOpponent(AI_GetBattlerAbility(opposingBattler), opposingBattler) && monAbility == ABILITY_TRACE))
{ {
// If mon in slot i is the most suitable switchin candidate, then it's a trapper than wins 1v1 // If mon in slot i is the most suitable switchin candidate, then it's a trapper than wins 1v1
if (i == AI_DATA->mostSuitableMonId[battler] && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN))) if (i == gAiLogicData->mostSuitableMonId[battler] && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
} }
} }
@ -634,8 +634,8 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler)
static bool32 ShouldSwitchIfBadlyStatused(u32 battler) static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
{ {
bool32 switchMon = FALSE; bool32 switchMon = FALSE;
u16 monAbility = AI_DATA->abilities[battler]; u16 monAbility = gAiLogicData->abilities[battler];
enum ItemHoldEffect holdEffect = AI_DATA->holdEffects[battler]; enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[battler];
u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
u8 opposingBattler = GetBattlerAtPosition(opposingPosition); u8 opposingBattler = GetBattlerAtPosition(opposingPosition);
bool32 hasStatRaised = AnyStatIsRaised(battler); bool32 hasStatRaised = AnyStatIsRaised(battler);
@ -647,7 +647,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
&& RandomPercentage(RNG_AI_SWITCH_PERISH_SONG, GetSwitchChance(SHOULD_SWITCH_PERISH_SONG))) && RandomPercentage(RNG_AI_SWITCH_PERISH_SONG, GetSwitchChance(SHOULD_SWITCH_PERISH_SONG)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)
{ {
//Yawn //Yawn
if (gStatuses3[battler] & STATUS3_YAWN if (gStatuses3[battler] & STATUS3_YAWN
@ -658,7 +658,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
switchMon = TRUE; switchMon = TRUE;
// If we don't have a good switchin, not worth switching // If we don't have a good switchin, not worth switching
if (AI_DATA->mostSuitableMonId[battler] == PARTY_SIZE) if (gAiLogicData->mostSuitableMonId[battler] == PARTY_SIZE)
switchMon = FALSE; switchMon = FALSE;
// Check if Active Pokemon can KO opponent instead of switching // Check if Active Pokemon can KO opponent instead of switching
@ -680,10 +680,10 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
// Check if Active Pokemon evasion boosted and might be able to dodge until awake // Check if Active Pokemon evasion boosted and might be able to dodge until awake
if (gBattleMons[battler].statStages[STAT_EVASION] > (DEFAULT_STAT_STAGE + 3) if (gBattleMons[battler].statStages[STAT_EVASION] > (DEFAULT_STAT_STAGE + 3)
&& AI_DATA->abilities[opposingBattler] != ABILITY_UNAWARE && gAiLogicData->abilities[opposingBattler] != ABILITY_UNAWARE
&& AI_DATA->abilities[opposingBattler] != ABILITY_KEEN_EYE && gAiLogicData->abilities[opposingBattler] != ABILITY_KEEN_EYE
&& AI_DATA->abilities[opposingBattler] != ABILITY_MINDS_EYE && gAiLogicData->abilities[opposingBattler] != ABILITY_MINDS_EYE
&& (B_ILLUMINATE_EFFECT >= GEN_9 && AI_DATA->abilities[opposingBattler] != ABILITY_ILLUMINATE) && (B_ILLUMINATE_EFFECT >= GEN_9 && gAiLogicData->abilities[opposingBattler] != ABILITY_ILLUMINATE)
&& !(gBattleMons[battler].status2 & STATUS2_FORESIGHT) && !(gBattleMons[battler].status2 & STATUS2_FORESIGHT)
&& !(gStatuses3[battler] & STATUS3_MIRACLE_EYED)) && !(gStatuses3[battler] & STATUS3_MIRACLE_EYED))
switchMon = FALSE; switchMon = FALSE;
@ -695,12 +695,12 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
// Secondary Damage // Secondary Damage
if (monAbility != ABILITY_MAGIC_GUARD if (monAbility != ABILITY_MAGIC_GUARD
&& !AiExpectsToFaintPlayer(battler) && !AiExpectsToFaintPlayer(battler)
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE)
{ {
//Toxic //Toxic
if (((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2)) if (((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2))
&& gBattleMons[battler].hp >= (gBattleMons[battler].maxHP / 3) && gBattleMons[battler].hp >= (gBattleMons[battler].maxHP / 3)
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED)))) && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED))))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
@ -723,7 +723,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
// Infatuation // Infatuation
if (gBattleMons[battler].status2 & STATUS2_INFATUATION if (gBattleMons[battler].status2 & STATUS2_INFATUATION
&& !AiExpectsToFaintPlayer(battler) && !AiExpectsToFaintPlayer(battler)
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& RandomPercentage(RNG_AI_SWITCH_INFATUATION, GetSwitchChance(SHOULD_SWITCH_INFATUATION))) && RandomPercentage(RNG_AI_SWITCH_INFATUATION, GetSwitchChance(SHOULD_SWITCH_INFATUATION)))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
} }
@ -740,18 +740,18 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler)
|| IsNeutralizingGasOnField()) || IsNeutralizingGasOnField())
return FALSE; return FALSE;
switch(AI_DATA->abilities[battler]) switch(gAiLogicData->abilities[battler])
{ {
case ABILITY_NATURAL_CURE: case ABILITY_NATURAL_CURE:
//Attempt to cure bad ailment //Attempt to cure bad ailment
if (gBattleMons[battler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON) if (gBattleMons[battler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON)
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG)))) && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG))))
break; break;
//Attempt to cure lesser ailment //Attempt to cure lesser ailment
if ((gBattleMons[battler].status1 & STATUS1_ANY) if ((gBattleMons[battler].status1 & STATUS1_ANY)
&& (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2) && (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2)
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK)))) && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK))))
break; break;
@ -762,7 +762,7 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler)
if (gBattleMons[battler].status1 & STATUS1_ANY) if (gBattleMons[battler].status1 & STATUS1_ANY)
return FALSE; return FALSE;
if ((gBattleMons[battler].hp <= ((gBattleMons[battler].maxHP * 2) / 3)) if ((gBattleMons[battler].hp <= ((gBattleMons[battler].maxHP * 2) / 3))
&& AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE
&& (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR)))) && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR))))
break; break;
@ -836,7 +836,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
u16 move; u16 move;
// Similar functionality handled more thoroughly by ShouldSwitchIfHasBadOdds // Similar functionality handled more thoroughly by ShouldSwitchIfHasBadOdds
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)
return FALSE; return FALSE;
if (gLastLandedMoves[battler] == MOVE_NONE) if (gLastLandedMoves[battler] == MOVE_NONE)
@ -898,7 +898,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
if (move == 0) if (move == 0)
continue; continue;
if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || AI_DATA->aiSwitchPredictionInProgress)) if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || gAiLogicData->aiSwitchPredictionInProgress))
return SetSwitchinAndSwitch(battler, i); return SetSwitchinAndSwitch(battler, i);
} }
} }
@ -911,7 +911,7 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
{ {
u32 battlerIn1, battlerIn2; u32 battlerIn1, battlerIn2;
u32 hazardDamage = 0, battlerHp = gBattleMons[battler].hp; u32 hazardDamage = 0, battlerHp = gBattleMons[battler].hp;
u32 ability = AI_DATA->abilities[battler], aiMove; u32 ability = gAiLogicData->abilities[battler], aiMove;
s32 firstId, lastId, i, j; s32 firstId, lastId, i, j;
struct Pokemon *party; struct Pokemon *party;
@ -974,7 +974,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
u32 opposingBattler = GetOppositeBattler(battler); u32 opposingBattler = GetOppositeBattler(battler);
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
// If not Encore'd don't switch // If not Encore'd don't switch
@ -990,7 +990,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
return FALSE; return FALSE;
// Switch out 50% of the time otherwise // Switch out 50% of the time otherwise
else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_DAMAGE)) || AI_DATA->aiSwitchPredictionInProgress) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_DAMAGE)) || gAiLogicData->aiSwitchPredictionInProgress) && gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE)
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
return FALSE; return FALSE;
@ -999,13 +999,13 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) static bool32 ShouldSwitchIfBadChoiceLock(u32 battler)
{ {
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, FALSE); enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, FALSE);
u32 lastUsedMove = AI_DATA->lastUsedMove[battler]; u32 lastUsedMove = gAiLogicData->lastUsedMove[battler];
u32 opposingBattler = GetOppositeBattler(battler); u32 opposingBattler = GetOppositeBattler(battler);
bool32 moveAffectsTarget = TRUE; bool32 moveAffectsTarget = TRUE;
if (lastUsedMove != MOVE_NONE && (AI_GetMoveEffectiveness(lastUsedMove, battler, opposingBattler) == UQ_4_12(0.0) if (lastUsedMove != MOVE_NONE && (AI_GetMoveEffectiveness(lastUsedMove, battler, opposingBattler) == UQ_4_12(0.0)
|| CanAbilityAbsorbMove(battler, opposingBattler, AI_DATA->abilities[opposingBattler], lastUsedMove, GetMoveType(lastUsedMove), ABILITY_CHECK_TRIGGER) || CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], lastUsedMove, GetMoveType(lastUsedMove), ABILITY_CHECK_TRIGGER)
|| CanAbilityBlockMove(battler, opposingBattler, AI_DATA->abilities[battler], AI_DATA->abilities[opposingBattler], lastUsedMove, ABILITY_CHECK_TRIGGER))) || CanAbilityBlockMove(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], lastUsedMove, ABILITY_CHECK_TRIGGER)))
moveAffectsTarget = FALSE; moveAffectsTarget = FALSE;
if (HOLD_EFFECT_CHOICE(holdEffect) && IsBattlerItemEnabled(battler)) if (HOLD_EFFECT_CHOICE(holdEffect) && IsBattlerItemEnabled(battler))
@ -1024,7 +1024,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler)
s8 spAttackingStage = gBattleMons[battler].statStages[STAT_SPATK]; s8 spAttackingStage = gBattleMons[battler].statStages[STAT_SPATK];
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) if (!(gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
return FALSE; return FALSE;
// Physical attacker // Physical attacker
@ -1036,7 +1036,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler)
// 50% chance if attack at -2 and have a good candidate mon // 50% chance if attack at -2 and have a good candidate mon
else if (attackingStage == DEFAULT_STAT_STAGE - 2) else if (attackingStage == DEFAULT_STAT_STAGE - 2)
{ {
if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) if (gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || gAiLogicData->aiSwitchPredictionInProgress))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
} }
// If at -3 or worse, switch out regardless // If at -3 or worse, switch out regardless
@ -1053,7 +1053,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler)
// 50% chance if attack at -2 and have a good candidate mon // 50% chance if attack at -2 and have a good candidate mon
else if (spAttackingStage == DEFAULT_STAT_STAGE - 2) else if (spAttackingStage == DEFAULT_STAT_STAGE - 2)
{ {
if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) if (gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || gAiLogicData->aiSwitchPredictionInProgress))
return SetSwitchinAndSwitch(battler, PARTY_SIZE); return SetSwitchinAndSwitch(battler, PARTY_SIZE);
} }
// If at -3 or worse, switch out regardless // If at -3 or worse, switch out regardless
@ -1100,7 +1100,7 @@ bool32 ShouldSwitch(u32 battler)
return FALSE; return FALSE;
// Sequence Switching AI never switches mid-battle // Sequence Switching AI never switches mid-battle
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SEQUENCE_SWITCHING) if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SEQUENCE_SWITCHING)
return FALSE; return FALSE;
availableToSwitch = 0; availableToSwitch = 0;
@ -1152,7 +1152,7 @@ bool32 ShouldSwitch(u32 battler)
if (ShouldSwitchIfWonderGuard(battler)) if (ShouldSwitchIfWonderGuard(battler))
return TRUE; return TRUE;
if ((AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE)) if ((gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE))
return FALSE; return FALSE;
if (HasGoodSubstituteMove(battler)) if (HasGoodSubstituteMove(battler))
return FALSE; return FALSE;
@ -1182,7 +1182,7 @@ bool32 ShouldSwitch(u32 battler)
// Removing switch capabilites under specific conditions // Removing switch capabilites under specific conditions
// These Functions prevent the "FindMonWithFlagsAndSuperEffective" from getting out of hand. // These Functions prevent the "FindMonWithFlagsAndSuperEffective" from getting out of hand.
// We don't use FindMonWithFlagsAndSuperEffective with AI_FLAG_SMART_SWITCHING, so we can bail early. // We don't use FindMonWithFlagsAndSuperEffective with AI_FLAG_SMART_SWITCHING, so we can bail early.
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)
return FALSE; return FALSE;
if (HasSuperEffectiveMoveAgainstOpponents(battler, FALSE)) if (HasSuperEffectiveMoveAgainstOpponents(battler, FALSE))
return FALSE; return FALSE;
@ -1206,14 +1206,14 @@ bool32 IsSwitchinValid(u32 battler)
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch
{ {
if ((AI_DATA->shouldSwitch & (1u << partner)) && AI_DATA->monToSwitchInId[partner] == AI_DATA->mostSuitableMonId[battler]) if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler])
{ {
return FALSE; return FALSE;
} }
} }
else // Override switch else // Override switch
{ {
if ((AI_DATA->shouldSwitch & (1u << partner)) && AI_DATA->monToSwitchInId[partner] == gBattleStruct->AI_monToSwitchIntoId[battler]) if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gBattleStruct->AI_monToSwitchIntoId[battler])
{ {
return FALSE; return FALSE;
} }
@ -1233,12 +1233,12 @@ void AI_TrySwitchOrUseItem(u32 battler)
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{ {
if (AI_DATA->shouldSwitch & (1u << battler) && IsSwitchinValid(battler)) if (gAiLogicData->shouldSwitch & (1u << battler) && IsSwitchinValid(battler))
{ {
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0); BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE)
{ {
s32 monToSwitchId = AI_DATA->mostSuitableMonId[battler]; s32 monToSwitchId = gAiLogicData->mostSuitableMonId[battler];
if (monToSwitchId == PARTY_SIZE) if (monToSwitchId == PARTY_SIZE)
{ {
if (!IsDoubleBattle()) if (!IsDoubleBattle())
@ -1277,7 +1277,7 @@ void AI_TrySwitchOrUseItem(u32 battler)
} }
gBattleStruct->monToSwitchIntoId[battler] = gBattleStruct->AI_monToSwitchIntoId[battler]; gBattleStruct->monToSwitchIntoId[battler] = gBattleStruct->AI_monToSwitchIntoId[battler];
AI_DATA->monToSwitchInId[battler] = gBattleStruct->AI_monToSwitchIntoId[battler]; gAiLogicData->monToSwitchInId[battler] = gBattleStruct->AI_monToSwitchIntoId[battler];
return; return;
} }
else if (ShouldUseItem(battler)) else if (ShouldUseItem(battler))
@ -1400,11 +1400,11 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
InitializeSwitchinCandidate(&party[i]); InitializeSwitchinCandidate(&party[i]);
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
{ {
aiMove = AI_DATA->switchinCandidate.battleMon.moves[j]; aiMove = gAiLogicData->switchinCandidate.battleMon.moves[j];
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove)) if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{ {
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j); aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, AI_ATTACKING); dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
if (bestDmg < dmg) if (bestDmg < dmg)
{ {
bestDmg = dmg; bestDmg = dmg;
@ -1494,8 +1494,8 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon
// Gets damage / healing from weather // Gets damage / healing from weather
static s32 GetSwitchinWeatherImpact(void) static s32 GetSwitchinWeatherImpact(void)
{ {
s32 weatherImpact = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; s32 weatherImpact = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP, ability = gAiLogicData->switchinCandidate.battleMon.ability;
enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item); enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(gAiLogicData->switchinCandidate.battleMon.item);
if (HasWeatherEffect()) if (HasWeatherEffect())
{ {
@ -1503,7 +1503,7 @@ static s32 GetSwitchinWeatherImpact(void)
if (holdEffect != HOLD_EFFECT_SAFETY_GOGGLES && ability != ABILITY_MAGIC_GUARD && ability != ABILITY_OVERCOAT) if (holdEffect != HOLD_EFFECT_SAFETY_GOGGLES && ability != ABILITY_MAGIC_GUARD && ability != ABILITY_OVERCOAT)
{ {
if ((gBattleWeather & B_WEATHER_HAIL) if ((gBattleWeather & B_WEATHER_HAIL)
&& (AI_DATA->switchinCandidate.battleMon.types[0] != TYPE_ICE || AI_DATA->switchinCandidate.battleMon.types[1] != TYPE_ICE) && (gAiLogicData->switchinCandidate.battleMon.types[0] != TYPE_ICE || gAiLogicData->switchinCandidate.battleMon.types[1] != TYPE_ICE)
&& ability != ABILITY_SNOW_CLOAK && ability != ABILITY_ICE_BODY) && ability != ABILITY_SNOW_CLOAK && ability != ABILITY_ICE_BODY)
{ {
weatherImpact = maxHP / 16; weatherImpact = maxHP / 16;
@ -1511,9 +1511,9 @@ static s32 GetSwitchinWeatherImpact(void)
weatherImpact = 1; weatherImpact = 1;
} }
else if ((gBattleWeather & B_WEATHER_SANDSTORM) else if ((gBattleWeather & B_WEATHER_SANDSTORM)
&& (AI_DATA->switchinCandidate.battleMon.types[0] != TYPE_GROUND && AI_DATA->switchinCandidate.battleMon.types[1] != TYPE_GROUND && (gAiLogicData->switchinCandidate.battleMon.types[0] != TYPE_GROUND && gAiLogicData->switchinCandidate.battleMon.types[1] != TYPE_GROUND
&& AI_DATA->switchinCandidate.battleMon.types[0] != TYPE_ROCK && AI_DATA->switchinCandidate.battleMon.types[1] != TYPE_ROCK && gAiLogicData->switchinCandidate.battleMon.types[0] != TYPE_ROCK && gAiLogicData->switchinCandidate.battleMon.types[1] != TYPE_ROCK
&& AI_DATA->switchinCandidate.battleMon.types[0] != TYPE_STEEL && AI_DATA->switchinCandidate.battleMon.types[1] != TYPE_STEEL && gAiLogicData->switchinCandidate.battleMon.types[0] != TYPE_STEEL && gAiLogicData->switchinCandidate.battleMon.types[1] != TYPE_STEEL
&& ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_RUSH && ability != ABILITY_SAND_FORCE)) && ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_RUSH && ability != ABILITY_SAND_FORCE))
{ {
weatherImpact = maxHP / 16; weatherImpact = maxHP / 16;
@ -1558,13 +1558,13 @@ static s32 GetSwitchinWeatherImpact(void)
// Gets one turn of recurring healing // Gets one turn of recurring healing
static u32 GetSwitchinRecurringHealing(void) static u32 GetSwitchinRecurringHealing(void)
{ {
u32 recurringHealing = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; u32 recurringHealing = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP, ability = gAiLogicData->switchinCandidate.battleMon.ability;
enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item); enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(gAiLogicData->switchinCandidate.battleMon.item);
// Items // Items
if (ability != ABILITY_KLUTZ) if (ability != ABILITY_KLUTZ)
{ {
if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && (AI_DATA->switchinCandidate.battleMon.types[0] == TYPE_POISON || AI_DATA->switchinCandidate.battleMon.types[1] == TYPE_POISON)) if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && (gAiLogicData->switchinCandidate.battleMon.types[0] == TYPE_POISON || gAiLogicData->switchinCandidate.battleMon.types[1] == TYPE_POISON))
{ {
recurringHealing = maxHP / 16; recurringHealing = maxHP / 16;
if (recurringHealing == 0) if (recurringHealing == 0)
@ -1579,7 +1579,7 @@ static u32 GetSwitchinRecurringHealing(void)
} // Intentionally omitting Shell Bell for its inconsistency } // Intentionally omitting Shell Bell for its inconsistency
// Abilities // Abilities
if (ability == ABILITY_POISON_HEAL && (AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_POISON)) if (ability == ABILITY_POISON_HEAL && (gAiLogicData->switchinCandidate.battleMon.status1 & STATUS1_POISON))
{ {
u32 healing = maxHP / 8; u32 healing = maxHP / 8;
if (healing == 0) if (healing == 0)
@ -1592,13 +1592,13 @@ static u32 GetSwitchinRecurringHealing(void)
// Gets one turn of recurring damage // Gets one turn of recurring damage
static u32 GetSwitchinRecurringDamage(void) static u32 GetSwitchinRecurringDamage(void)
{ {
u32 passiveDamage = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability; u32 passiveDamage = 0, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP, ability = gAiLogicData->switchinCandidate.battleMon.ability;
enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item); enum ItemHoldEffect holdEffect = ItemId_GetHoldEffect(gAiLogicData->switchinCandidate.battleMon.item);
// Items // Items
if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ) if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ)
{ {
if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && AI_DATA->switchinCandidate.battleMon.types[0] != TYPE_POISON && AI_DATA->switchinCandidate.battleMon.types[1] != TYPE_POISON) if (holdEffect == HOLD_EFFECT_BLACK_SLUDGE && gAiLogicData->switchinCandidate.battleMon.types[0] != TYPE_POISON && gAiLogicData->switchinCandidate.battleMon.types[1] != TYPE_POISON)
{ {
passiveDamage = maxHP / 8; passiveDamage = maxHP / 8;
if (passiveDamage == 0) if (passiveDamage == 0)
@ -1623,14 +1623,14 @@ static u32 GetSwitchinRecurringDamage(void)
// Gets one turn of status damage // Gets one turn of status damage
static u32 GetSwitchinStatusDamage(u32 battler) static u32 GetSwitchinStatusDamage(u32 battler)
{ {
u8 defType1 = AI_DATA->switchinCandidate.battleMon.types[0], defType2 = AI_DATA->switchinCandidate.battleMon.types[1]; u8 defType1 = gAiLogicData->switchinCandidate.battleMon.types[0], defType2 = gAiLogicData->switchinCandidate.battleMon.types[1];
u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount; u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount;
u16 heldItemEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item); u16 heldItemEffect = ItemId_GetHoldEffect(gAiLogicData->switchinCandidate.battleMon.item);
u32 status = AI_DATA->switchinCandidate.battleMon.status1, ability = AI_DATA->switchinCandidate.battleMon.ability, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP; u32 status = gAiLogicData->switchinCandidate.battleMon.status1, ability = gAiLogicData->switchinCandidate.battleMon.ability, maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP;
u32 statusDamage = 0; u32 statusDamage = 0;
// Status condition damage // Status condition damage
if ((status != 0) && AI_DATA->switchinCandidate.battleMon.ability != ABILITY_MAGIC_GUARD) if ((status != 0) && gAiLogicData->switchinCandidate.battleMon.ability != ABILITY_MAGIC_GUARD)
{ {
if (status & STATUS1_BURN) if (status & STATUS1_BURN)
{ {
@ -1661,11 +1661,11 @@ static u32 GetSwitchinStatusDamage(u32 battler)
else if ((status & STATUS1_TOXIC_POISON) && ability != ABILITY_POISON_HEAL) else if ((status & STATUS1_TOXIC_POISON) && ability != ABILITY_POISON_HEAL)
{ {
if ((status & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns if ((status & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns
AI_DATA->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1); gAiLogicData->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1);
statusDamage = maxHP / 16; statusDamage = maxHP / 16;
if (statusDamage == 0) if (statusDamage == 0)
statusDamage = 1; statusDamage = 1;
statusDamage *= AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8; statusDamage *= gAiLogicData->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8;
} }
} }
@ -1681,14 +1681,14 @@ static u32 GetSwitchinStatusDamage(u32 battler)
{ {
if (tSpikesLayers == 1) if (tSpikesLayers == 1)
{ {
AI_DATA->switchinCandidate.battleMon.status1 = STATUS1_POISON; // Assign "hypothetical" status to the switchin candidate so we can get the damage it would take from TSpikes gAiLogicData->switchinCandidate.battleMon.status1 = STATUS1_POISON; // Assign "hypothetical" status to the switchin candidate so we can get the damage it would take from TSpikes
AI_DATA->switchinCandidate.hypotheticalStatus = TRUE; gAiLogicData->switchinCandidate.hypotheticalStatus = TRUE;
} }
if (tSpikesLayers == 2) if (tSpikesLayers == 2)
{ {
AI_DATA->switchinCandidate.battleMon.status1 = STATUS1_TOXIC_POISON; // Assign "hypothetical" status to the switchin candidate so we can get the damage it would take from TSpikes gAiLogicData->switchinCandidate.battleMon.status1 = STATUS1_TOXIC_POISON; // Assign "hypothetical" status to the switchin candidate so we can get the damage it would take from TSpikes
AI_DATA->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1); gAiLogicData->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1);
AI_DATA->switchinCandidate.hypotheticalStatus = TRUE; gAiLogicData->switchinCandidate.hypotheticalStatus = TRUE;
} }
} }
return statusDamage; return statusDamage;
@ -1697,16 +1697,16 @@ static u32 GetSwitchinStatusDamage(u32 battler)
// Gets number of hits to KO factoring in hazards, healing held items, status, and weather // Gets number of hits to KO factoring in hazards, healing held items, status, and weather
static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler) static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
{ {
u32 startingHP = AI_DATA->switchinCandidate.battleMon.hp - GetSwitchinHazardsDamage(battler, &AI_DATA->switchinCandidate.battleMon); u32 startingHP = gAiLogicData->switchinCandidate.battleMon.hp - GetSwitchinHazardsDamage(battler, &gAiLogicData->switchinCandidate.battleMon);
s32 weatherImpact = GetSwitchinWeatherImpact(); // Signed to handle both damage and healing in the same value s32 weatherImpact = GetSwitchinWeatherImpact(); // Signed to handle both damage and healing in the same value
u32 recurringDamage = GetSwitchinRecurringDamage(); u32 recurringDamage = GetSwitchinRecurringDamage();
u32 recurringHealing = GetSwitchinRecurringHealing(); u32 recurringHealing = GetSwitchinRecurringHealing();
u32 statusDamage = GetSwitchinStatusDamage(battler); u32 statusDamage = GetSwitchinStatusDamage(battler);
u32 hitsToKO = 0, singleUseItemHeal = 0; u32 hitsToKO = 0, singleUseItemHeal = 0;
u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = ItemId_GetHoldEffect(item); u16 maxHP = gAiLogicData->switchinCandidate.battleMon.maxHP, item = gAiLogicData->switchinCandidate.battleMon.item, heldItemEffect = ItemId_GetHoldEffect(item);
u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = ItemId_GetHoldEffectParam(item); u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = ItemId_GetHoldEffectParam(item);
u32 opposingBattler = GetOppositeBattler(battler); u32 opposingBattler = GetOppositeBattler(battler);
u32 opposingAbility = gBattleMons[opposingBattler].ability, ability = AI_DATA->switchinCandidate.battleMon.ability; u32 opposingAbility = gBattleMons[opposingBattler].ability, ability = gAiLogicData->switchinCandidate.battleMon.ability;
bool32 usedSingleUseHealingItem = FALSE, opponentCanBreakMold = IsMoldBreakerTypeAbility(opposingBattler, opposingAbility); bool32 usedSingleUseHealingItem = FALSE, opponentCanBreakMold = IsMoldBreakerTypeAbility(opposingBattler, opposingAbility);
s32 currentHP = startingHP; s32 currentHP = startingHP;
@ -1737,7 +1737,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
currentHP = currentHP - weatherImpact; currentHP = currentHP - weatherImpact;
// Check if we're at a single use healing item threshold // Check if we're at a single use healing item threshold
if (AI_DATA->switchinCandidate.battleMon.ability != ABILITY_KLUTZ && usedSingleUseHealingItem == FALSE if (gAiLogicData->switchinCandidate.battleMon.ability != ABILITY_KLUTZ && usedSingleUseHealingItem == FALSE
&& !(opposingAbility == ABILITY_UNNERVE && GetPocketByItemId(item) == POCKET_BERRIES)) && !(opposingAbility == ABILITY_UNNERVE && GetPocketByItemId(item) == POCKET_BERRIES))
{ {
switch (heldItemEffect) switch (heldItemEffect)
@ -1784,7 +1784,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
currentHP = currentHP + recurringHealing - recurringDamage - statusDamage; currentHP = currentHP + recurringHealing - recurringDamage - statusDamage;
// Recalculate toxic damage if needed // Recalculate toxic damage if needed
if (AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_POISON) if (gAiLogicData->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_POISON)
statusDamage = GetSwitchinStatusDamage(battler); statusDamage = GetSwitchinStatusDamage(battler);
// Reduce weather duration // Reduce weather duration
@ -1795,14 +1795,14 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
} }
// Disguise will always add an extra hit to KO // Disguise will always add an extra hit to KO
if (opponentCanBreakMold && AI_DATA->switchinCandidate.battleMon.species == SPECIES_MIMIKYU_DISGUISED) if (opponentCanBreakMold && gAiLogicData->switchinCandidate.battleMon.species == SPECIES_MIMIKYU_DISGUISED)
hitsToKO++; hitsToKO++;
// If mon had a hypothetical status from TSpikes, clear it // If mon had a hypothetical status from TSpikes, clear it
if (AI_DATA->switchinCandidate.hypotheticalStatus == TRUE) if (gAiLogicData->switchinCandidate.hypotheticalStatus == TRUE)
{ {
AI_DATA->switchinCandidate.battleMon.status1 = 0; gAiLogicData->switchinCandidate.battleMon.status1 = 0;
AI_DATA->switchinCandidate.hypotheticalStatus = FALSE; gAiLogicData->switchinCandidate.hypotheticalStatus = FALSE;
} }
return hitsToKO; return hitsToKO;
} }
@ -1875,7 +1875,7 @@ static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent)
return FALSE; return FALSE;
else if (ability == ABILITY_SHADOW_TAG) else if (ability == ABILITY_SHADOW_TAG)
{ {
if (B_SHADOW_TAG_ESCAPE >= GEN_4 && AI_DATA->abilities[opponent] == ABILITY_SHADOW_TAG) // Check if ability exists in species if (B_SHADOW_TAG_ESCAPE >= GEN_4 && gAiLogicData->abilities[opponent] == ABILITY_SHADOW_TAG) // Check if ability exists in species
return FALSE; return FALSE;
else else
return TRUE; return TRUE;
@ -1897,9 +1897,9 @@ static inline bool32 IsFreeSwitch(enum SwitchType switchType, u32 battlerSwitchi
{ {
if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) && movedSecond) if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) && movedSecond)
return TRUE; return TRUE;
if (AI_DATA->ejectButtonSwitch) if (gAiLogicData->ejectButtonSwitch)
return TRUE; return TRUE;
if (AI_DATA->ejectPackSwitch) if (gAiLogicData->ejectPackSwitch)
{ {
u32 opposingAbility = AI_GetBattlerAbility(opposingBattler); u32 opposingAbility = AI_GetBattlerAbility(opposingBattler);
// If faster, not a free switch; likely lowered own stats // If faster, not a free switch; likely lowered own stats
@ -1969,12 +1969,12 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
InitializeSwitchinCandidate(&party[i]); InitializeSwitchinCandidate(&party[i]);
// While not really invalid per se, not really wise to switch into this mon // While not really invalid per se, not really wise to switch into this mon
if (AI_DATA->switchinCandidate.battleMon.ability == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler)) if (gAiLogicData->switchinCandidate.battleMon.ability == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))
continue; continue;
// Get max number of hits for player to KO AI mon and type matchup for defensive switching // Get max number of hits for player to KO AI mon and type matchup for defensive switching
hitsToKOAI = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, AI_DATA->switchinCandidate.battleMon), battler); hitsToKOAI = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon), battler);
typeMatchup = GetSwitchinTypeMatchup(opposingBattler, AI_DATA->switchinCandidate.battleMon); typeMatchup = GetSwitchinTypeMatchup(opposingBattler, gAiLogicData->switchinCandidate.battleMon);
// Track max hits to KO and set defensive mon // Track max hits to KO and set defensive mon
if(hitsToKOAI > maxHitsToKO) if(hitsToKOAI > maxHitsToKO)
@ -1987,13 +1987,13 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Check through current mon's moves // Check through current mon's moves
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
{ {
aiMove = AI_DATA->switchinCandidate.battleMon.moves[j]; aiMove = gAiLogicData->switchinCandidate.battleMon.moves[j];
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove)) if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, AI_ATTACKING); damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, AI_ATTACKING);
// Offensive switchin decisions are based on which whether switchin moves first and whether it can win a 1v1 // Offensive switchin decisions are based on which whether switchin moves first and whether it can win a 1v1
isSwitchinFirst = AI_WhoStrikesFirstPartyMon(battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, aiMove); isSwitchinFirst = AI_WhoStrikesFirstPartyMon(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, aiMove);
canSwitchinWin1v1 = CanSwitchinWin1v1(hitsToKOAI, GetNoOfHitsToKOBattlerDmg(damageDealt, opposingBattler), isSwitchinFirst, isFreeSwitch); canSwitchinWin1v1 = CanSwitchinWin1v1(hitsToKOAI, GetNoOfHitsToKOBattlerDmg(damageDealt, opposingBattler), isSwitchinFirst, isFreeSwitch);
// Check for Baton Pass; hitsToKO requirements mean mon can boost and BP without dying whether it's slower or not // Check for Baton Pass; hitsToKO requirements mean mon can boost and BP without dying whether it's slower or not
@ -2068,8 +2068,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
} }
// If mon can trap // If mon can trap
if ((CanAbilityTrapOpponent(AI_DATA->switchinCandidate.battleMon.ability, opposingBattler) if ((CanAbilityTrapOpponent(gAiLogicData->switchinCandidate.battleMon.ability, opposingBattler)
|| (CanAbilityTrapOpponent(AI_GetBattlerAbility(opposingBattler), opposingBattler) && AI_DATA->switchinCandidate.battleMon.ability == ABILITY_TRACE)) || (CanAbilityTrapOpponent(AI_GetBattlerAbility(opposingBattler), opposingBattler) && gAiLogicData->switchinCandidate.battleMon.ability == ABILITY_TRACE))
&& CountUsablePartyMons(opposingBattler) > 0 && CountUsablePartyMons(opposingBattler) > 0
&& canSwitchinWin1v1) && canSwitchinWin1v1)
trapperId = i; trapperId = i;
@ -2104,7 +2104,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
} }
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon. // If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || AI_DATA->ejectButtonSwitch || AI_DATA->ejectPackSwitch)) && (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
return aceMonId; return aceMonId;
return PARTY_SIZE; return PARTY_SIZE;
@ -2166,14 +2166,14 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
GetAIPartyIndexes(battler, &firstId, &lastId); GetAIPartyIndexes(battler, &firstId, &lastId);
party = GetBattlerParty(battler); party = GetBattlerParty(battler);
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SEQUENCE_SWITCHING) if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SEQUENCE_SWITCHING)
{ {
bestMonId = GetNextMonInParty(party, firstId, lastId, battlerIn1, battlerIn2); bestMonId = GetNextMonInParty(party, firstId, lastId, battlerIn1, battlerIn2);
return bestMonId; return bestMonId;
} }
// Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer. // Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer.
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_MON_CHOICES && !IsDoubleBattle()) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic if (gAiThinkingStruct->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_MON_CHOICES && !IsDoubleBattle()) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
{ {
bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchType); bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchType);
return bestMonId; return bestMonId;
@ -2221,7 +2221,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon. // If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || AI_DATA->ejectButtonSwitch || AI_DATA->ejectPackSwitch)) && (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
return aceMonId; return aceMonId;
return PARTY_SIZE; return PARTY_SIZE;
@ -2284,7 +2284,7 @@ static bool32 ShouldUseItem(u32 battler)
const u8 *itemEffects; const u8 *itemEffects;
u8 battlerSide; u8 battlerSide;
item = gBattleResources->battleHistory->trainerItems[i]; item = gBattleHistory->trainerItems[i];
if (item == ITEM_NONE) if (item == ITEM_NONE)
continue; continue;
itemEffects = ItemId_GetEffect(item); itemEffects = ItemId_GetEffect(item);
@ -2354,7 +2354,7 @@ static bool32 ShouldUseItem(u32 battler)
gBattleStruct->itemPartyIndex[battler] = gBattlerPartyIndexes[battler]; gBattleStruct->itemPartyIndex[battler] = gBattlerPartyIndexes[battler];
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_ITEM, 0); BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_ITEM, 0);
gBattleStruct->chosenItem[battler] = item; gBattleStruct->chosenItem[battler] = item;
gBattleResources->battleHistory->trainerItems[i] = 0; gBattleHistory->trainerItems[i] = 0;
return shouldUse; return shouldUse;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -650,14 +650,14 @@ static void OpponentHandleChooseItem(u32 battler)
static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler) static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
{ {
return AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ACE_POKEMON
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1) && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1)
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle; && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle;
} }
static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler) static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
{ {
return AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1) && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1)
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2) && (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2)
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle && CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle

View File

@ -972,7 +972,7 @@ static void PutMovesPointsText(struct BattleDebugMenu *data)
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 83 + count * 54, i * 15, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 83 + count * 54, i * 15, 0, NULL);
ConvertIntToDecimalStringN(text, ConvertIntToDecimalStringN(text,
AI_GetDamage(data->aiBattlerId, battlerDef, i, AI_ATTACKING, AI_DATA), AI_GetDamage(data->aiBattlerId, battlerDef, i, AI_ATTACKING, gAiLogicData),
STR_CONV_MODE_RIGHT_ALIGN, 3); STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 110 + count * 54, i * 15, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 110 + count * 54, i * 15, 0, NULL);
@ -980,9 +980,9 @@ static void PutMovesPointsText(struct BattleDebugMenu *data)
} }
} }
if (AI_DATA->shouldSwitch & (1u << data->aiBattlerId)) if (gAiLogicData->shouldSwitch & (1u << data->aiBattlerId))
{ {
u32 switchMon = GetMonData(&gEnemyParty[AI_DATA->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES); u32 switchMon = GetMonData(&gEnemyParty[gAiLogicData->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, sText_IsSwitching, 74, 64, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, sText_IsSwitching, 74, 64, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, gSpeciesInfo[switchMon].speciesName, 74 + 68, 64, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, gSpeciesInfo[switchMon].speciesName, 74 + 68, 64, 0, NULL);
@ -1129,9 +1129,9 @@ static void PutAiInfoText(struct BattleDebugMenu *data)
{ {
if (GetBattlerSide(i) == B_SIDE_PLAYER && IsBattlerAlive(i)) if (GetBattlerSide(i) == B_SIDE_PLAYER && IsBattlerAlive(i))
{ {
u16 ability = AI_DATA->abilities[i]; u16 ability = gAiLogicData->abilities[i];
enum ItemHoldEffect holdEffect = AI_DATA->holdEffects[i]; enum ItemHoldEffect holdEffect = gAiLogicData->holdEffects[i];
u16 item = AI_DATA->items[i]; u16 item = gAiLogicData->items[i];
u8 x = (i == B_POSITION_PLAYER_LEFT) ? 83 + (i) * 75 : 83 + (i-1) * 75; u8 x = (i == B_POSITION_PLAYER_LEFT) ? 83 + (i) * 75 : 83 + (i-1) * 75;
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, gAbilitiesInfo[ability].name, x, 0, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, gAbilitiesInfo[ability].name, x, 0, 0, NULL);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, ItemId_GetName(item), x, 15, 0, NULL); AddTextPrinterParameterized(data->aiMovesWindowId, FONT_SMALL, ItemId_GetName(item), x, 15, 0, NULL);
@ -1147,10 +1147,10 @@ static void PutAiPartyText(struct BattleDebugMenu *data)
{ {
u32 i, j, count; u32 i, j, count;
u8 *text = Alloc(0x50), *txtPtr; u8 *text = Alloc(0x50), *txtPtr;
struct AiPartyMon *aiMons = AI_PARTY->mons[GetBattlerSide(data->aiBattlerId)]; struct AiPartyMon *aiMons = gAiPartyData->mons[GetBattlerSide(data->aiBattlerId)];
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11); FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
count = AI_PARTY->count[GetBattlerSide(data->aiBattlerId)]; count = gAiPartyData->count[GetBattlerSide(data->aiBattlerId)];
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
if (aiMons[i].wasSentInBattle) if (aiMons[i].wasSentInBattle)
@ -1276,8 +1276,8 @@ static void Task_ShowAiParty(u8 taskId)
LoadMonIconPalettes(); LoadMonIconPalettes();
LoadPartyMenuAilmentGfx(); LoadPartyMenuAilmentGfx();
data->aiBattlerId = data->battlerId; data->aiBattlerId = data->battlerId;
aiMons = AI_PARTY->mons[GetBattlerSide(data->aiBattlerId)]; aiMons = gAiPartyData->mons[GetBattlerSide(data->aiBattlerId)];
for (i = 0; i < AI_PARTY->count[GetBattlerSide(data->aiBattlerId)]; i++) for (i = 0; i < gAiPartyData->count[GetBattlerSide(data->aiBattlerId)]; i++)
{ {
u16 species = SPECIES_NONE; // Question mark u16 species = SPECIES_NONE; // Question mark
if (aiMons[i].wasSentInBattle && aiMons[i].species) if (aiMons[i].wasSentInBattle && aiMons[i].species)
@ -2199,8 +2199,8 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
data->modifyArrows.typeOfVal = VAL_BITFIELD_32; data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS; goto CASE_ITEM_STATUS;
case LIST_ITEM_AI: case LIST_ITEM_AI:
data->modifyArrows.modifiedValPtr = &gBattleResources->ai->aiFlags[data->battlerId]; data->modifyArrows.modifiedValPtr = &gAiThinkingStruct->aiFlags[data->battlerId];
data->modifyArrows.currValue = GetBitfieldValue(gBattleResources->ai->aiFlags[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount); data->modifyArrows.currValue = GetBitfieldValue(gAiThinkingStruct->aiFlags[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32; data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS; goto CASE_ITEM_STATUS;
CASE_ITEM_STATUS: CASE_ITEM_STATUS:

View File

@ -209,7 +209,14 @@ EWRAM_DATA u8 gSentPokesToOpponent[2] = {0};
EWRAM_DATA struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct BattleScripting gBattleScripting = {0}; EWRAM_DATA struct BattleScripting gBattleScripting = {0};
EWRAM_DATA struct BattleStruct *gBattleStruct = NULL; EWRAM_DATA struct BattleStruct *gBattleStruct = NULL;
EWRAM_DATA struct AiThinkingStruct *gAiThinkingStruct = NULL;
EWRAM_DATA struct AiLogicData *gAiLogicData = NULL;
EWRAM_DATA struct AiPartyData *gAiPartyData = NULL;
EWRAM_DATA struct BattleHistory *gBattleHistory = NULL;
EWRAM_DATA struct AiBattleData *gAiBattleData = NULL; EWRAM_DATA struct AiBattleData *gAiBattleData = NULL;
EWRAM_DATA u8 *gLinkBattleSendBuffer = NULL; EWRAM_DATA u8 *gLinkBattleSendBuffer = NULL;
EWRAM_DATA u8 *gLinkBattleRecvBuffer = NULL; EWRAM_DATA u8 *gLinkBattleRecvBuffer = NULL;
EWRAM_DATA struct BattleResources *gBattleResources = NULL; EWRAM_DATA struct BattleResources *gBattleResources = NULL;
@ -3230,8 +3237,8 @@ void SwitchInClearSetData(u32 battler)
gSpecialStatuses[battler].enduredDamage = FALSE; gSpecialStatuses[battler].enduredDamage = FALSE;
// Reset Eject Button / Eject Pack switch detection // Reset Eject Button / Eject Pack switch detection
AI_DATA->ejectButtonSwitch = FALSE; gAiLogicData->ejectButtonSwitch = FALSE;
AI_DATA->ejectPackSwitch = FALSE; gAiLogicData->ejectPackSwitch = FALSE;
// Reset G-Max Chi Strike boosts. // Reset G-Max Chi Strike boosts.
gBattleStruct->bonusCritStages[battler] = 0; gBattleStruct->bonusCritStages[battler] = 0;
@ -3887,7 +3894,7 @@ static void TryDoEventsBeforeFirstTurn(void)
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
SetShellSideArmCategory(); SetShellSideArmCategory();
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers SetAiLogicDataForTurn(gAiLogicData); // get assumed abilities, hold effects, etc of all battlers
if (gBattleTypeFlags & BATTLE_TYPE_ARENA) if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
{ {
@ -3987,7 +3994,7 @@ void BattleTurnPassed(void)
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks(); AssignUsableGimmicks();
SetShellSideArmCategory(); SetShellSideArmCategory();
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers SetAiLogicDataForTurn(gAiLogicData); // get assumed abilities, hold effects, etc of all battlers
gBattleMainFunc = HandleTurnActionSelectionState; gBattleMainFunc = HandleTurnActionSelectionState;
if (gBattleTypeFlags & BATTLE_TYPE_PALACE) if (gBattleTypeFlags & BATTLE_TYPE_PALACE)

View File

@ -7109,7 +7109,7 @@ static void Cmd_moveend(void)
gBattleStruct->battlerState[battler].usedEjectItem = TRUE; gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
BattleScriptPushCursor(); BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates; gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
AI_DATA->ejectButtonSwitch = TRUE; gAiLogicData->ejectButtonSwitch = TRUE;
break; // Only the fastest Eject Button activates break; // Only the fastest Eject Button activates
} }
} }
@ -7159,7 +7159,7 @@ static void Cmd_moveend(void)
gBattleStruct->battlerState[battler].usedEjectItem = TRUE; gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
BattleScriptPushCursor(); BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivates; gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
AI_DATA->ejectPackSwitch = TRUE; gAiLogicData->ejectPackSwitch = TRUE;
break; // Only the fastest Eject item activates break; // Only the fastest Eject item activates
} }
} }
@ -11643,13 +11643,13 @@ static void Cmd_various(void)
case VARIOUS_SAVE_BATTLER_ITEM: case VARIOUS_SAVE_BATTLER_ITEM:
{ {
VARIOUS_ARGS(); VARIOUS_ARGS();
gBattleResources->battleHistory->heldItems[battler] = gBattleMons[battler].item; gBattleHistory->heldItems[battler] = gBattleMons[battler].item;
break; break;
} }
case VARIOUS_RESTORE_BATTLER_ITEM: case VARIOUS_RESTORE_BATTLER_ITEM:
{ {
VARIOUS_ARGS(); VARIOUS_ARGS();
gBattleMons[battler].item = gBattleResources->battleHistory->heldItems[battler]; gBattleMons[battler].item = gBattleHistory->heldItems[battler];
break; break;
} }
case VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM: case VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM:
@ -18704,7 +18704,7 @@ void BS_TryIntimidatEjectpack(void)
{ {
gProtectStructs[battler].statFell = FALSE; gProtectStructs[battler].statFell = FALSE;
gProtectStructs[partnerBattler].statFell = FALSE; gProtectStructs[partnerBattler].statFell = FALSE;
AI_DATA->ejectPackSwitch = TRUE; gAiLogicData->ejectPackSwitch = TRUE;
gBattleScripting.battler = affectedBattler; gBattleScripting.battler = affectedBattler;
gLastUsedItem = gBattleMons[affectedBattler].item; gLastUsedItem = gBattleMons[affectedBattler].item;
RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK); RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK);

View File

@ -2984,7 +2984,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
&& !IsBattlerAlly(battlerAtk, partnerDef)) && !IsBattlerAlly(battlerAtk, partnerDef))
{ {
if (option == ABILITY_CHECK_TRIGGER_AI) if (option == ABILITY_CHECK_TRIGGER_AI)
abilityDef = AI_DATA->abilities[partnerDef]; abilityDef = gAiLogicData->abilities[partnerDef];
else else
abilityDef = GetBattlerAbility(partnerDef); abilityDef = GetBattlerAbility(partnerDef);
@ -6080,7 +6080,7 @@ static enum ItemEffect TryEjectPack(u32 battler, enum ItemCaseId caseID)
{ {
gProtectStructs[battler].statFell = FALSE; gProtectStructs[battler].statFell = FALSE;
gBattleScripting.battler = battler; gBattleScripting.battler = battler;
AI_DATA->ejectPackSwitch = TRUE; gAiLogicData->ejectPackSwitch = TRUE;
if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN) if (caseID == ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN)
{ {
BattleScriptExecute(BattleScript_EjectPackActivate_End2); BattleScriptExecute(BattleScript_EjectPackActivate_End2);
@ -7822,7 +7822,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, bool32 considerInverse)
return FALSE; return FALSE;
if (holdEffect == HOLD_EFFECT_AIR_BALLOON) if (holdEffect == HOLD_EFFECT_AIR_BALLOON)
return FALSE; return FALSE;
if ((AI_DATA->aiCalcInProgress ? AI_DATA->abilities[battler] : GetBattlerAbility(battler)) == ABILITY_LEVITATE) if ((gAiLogicData->aiCalcInProgress ? gAiLogicData->abilities[battler] : GetBattlerAbility(battler)) == ABILITY_LEVITATE)
return FALSE; return FALSE;
if (IS_BATTLER_OF_TYPE(battler, TYPE_FLYING) && (!considerInverse || !FlagGet(B_FLAG_INVERSE_BATTLE))) if (IS_BATTLER_OF_TYPE(battler, TYPE_FLYING) && (!considerInverse || !FlagGet(B_FLAG_INVERSE_BATTLE)))
return FALSE; return FALSE;

View File

@ -19,6 +19,10 @@ void AllocateBattleResources(void)
gBattleStruct = AllocZeroed(sizeof(*gBattleStruct)); gBattleStruct = AllocZeroed(sizeof(*gBattleStruct));
gAiBattleData = AllocZeroed(sizeof(*gAiBattleData)); gAiBattleData = AllocZeroed(sizeof(*gAiBattleData));
gAiThinkingStruct = AllocZeroed(sizeof(*gAiThinkingStruct));
gAiLogicData = AllocZeroed(sizeof(*gAiLogicData));
gAiPartyData = AllocZeroed(sizeof(*gAiPartyData));
gBattleHistory = AllocZeroed(sizeof(*gBattleHistory));
#if B_FLAG_SKY_BATTLE #if B_FLAG_SKY_BATTLE
gBattleStruct->isSkyBattle = FlagGet(B_FLAG_SKY_BATTLE); gBattleStruct->isSkyBattle = FlagGet(B_FLAG_SKY_BATTLE);
@ -29,10 +33,6 @@ void AllocateBattleResources(void)
gBattleResources->battleScriptsStack = AllocZeroed(sizeof(*gBattleResources->battleScriptsStack)); gBattleResources->battleScriptsStack = AllocZeroed(sizeof(*gBattleResources->battleScriptsStack));
gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack)); gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack));
gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp)); gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp));
gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai));
gBattleResources->aiData = AllocZeroed(sizeof(*gBattleResources->aiData));
gBattleResources->aiParty = AllocZeroed(sizeof(*gBattleResources->aiParty));
gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory));
gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE); gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
gLinkBattleRecvBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE); gLinkBattleRecvBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
@ -57,15 +57,15 @@ void FreeBattleResources(void)
{ {
FREE_AND_SET_NULL(gBattleStruct); FREE_AND_SET_NULL(gBattleStruct);
FREE_AND_SET_NULL(gAiBattleData); FREE_AND_SET_NULL(gAiBattleData);
FREE_AND_SET_NULL(gAiThinkingStruct);
FREE_AND_SET_NULL(gAiLogicData);
FREE_AND_SET_NULL(gAiPartyData);
FREE_AND_SET_NULL(gBattleHistory);
FREE_AND_SET_NULL(gBattleResources->secretBase); FREE_AND_SET_NULL(gBattleResources->secretBase);
FREE_AND_SET_NULL(gBattleResources->battleScriptsStack); FREE_AND_SET_NULL(gBattleResources->battleScriptsStack);
FREE_AND_SET_NULL(gBattleResources->battleCallbackStack); FREE_AND_SET_NULL(gBattleResources->battleCallbackStack);
FREE_AND_SET_NULL(gBattleResources->beforeLvlUp); FREE_AND_SET_NULL(gBattleResources->beforeLvlUp);
FREE_AND_SET_NULL(gBattleResources->ai);
FREE_AND_SET_NULL(gBattleResources->aiData);
FREE_AND_SET_NULL(gBattleResources->aiParty);
FREE_AND_SET_NULL(gBattleResources->battleHistory);
FREE_AND_SET_NULL(gBattleResources); FREE_AND_SET_NULL(gBattleResources);
FREE_AND_SET_NULL(gLinkBattleSendBuffer); FREE_AND_SET_NULL(gLinkBattleSendBuffer);

View File

@ -87,7 +87,7 @@ void RecordedBattle_Init(u8 mode)
for (j = 0; j < BATTLER_RECORD_SIZE; j++) for (j = 0; j < BATTLER_RECORD_SIZE; j++)
sBattleRecords[i][j] = 0xFF; sBattleRecords[i][j] = 0xFF;
sBattleFlags = gBattleTypeFlags; sBattleFlags = gBattleTypeFlags;
sAI_Scripts = gBattleResources->ai->aiFlags[B_POSITION_OPPONENT_LEFT]; sAI_Scripts = gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT];
} }
} }
} }

View File

@ -45,7 +45,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has
} SCENE { } SCENE {
MESSAGE("Shuckle's Defense fell!"); // Contrary activates MESSAGE("Shuckle's Defense fell!"); // Contrary activates
} THEN { } THEN {
EXPECT(gBattleResources->aiData->abilities[B_POSITION_PLAYER_LEFT] == ABILITY_CONTRARY); EXPECT(gAiThinkingStruct->abilities[B_POSITION_PLAYER_LEFT] == ABILITY_CONTRARY);
} }
} }
@ -749,7 +749,7 @@ AI_DOUBLE_BATTLE_TEST("AI recognizes Volt Absorb received from Trace")
} WHEN { } WHEN {
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDERSHOCK); NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDER_WAVE); NOT_EXPECT_MOVE(opponentRight, MOVE_THUNDER_WAVE); } TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDERSHOCK); NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDER_WAVE); NOT_EXPECT_MOVE(opponentRight, MOVE_THUNDER_WAVE); }
} THEN { } THEN {
EXPECT(gBattleResources->aiData->abilities[B_POSITION_PLAYER_RIGHT] == ABILITY_VOLT_ABSORB); EXPECT(gAiThinkingStruct->abilities[B_POSITION_PLAYER_RIGHT] == ABILITY_VOLT_ABSORB);
} }
} }