From 035b2332af37c2357534b8d9c35aa492ddbc4203 Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Thu, 13 Jan 2022 11:28:27 -0500 Subject: [PATCH] cache ai damage, effectiveness calcs --- include/battle.h | 36 +- include/battle_ai_main.h | 1 + include/battle_ai_util.h | 8 +- include/battle_script_commands.h | 2 +- include/battle_util.h | 1 + src/battle_ai_main.c | 1061 +++++++++++++++--------------- src/battle_ai_util.c | 290 ++++---- src/battle_debug.c | 7 +- src/battle_main.c | 3 + src/battle_script_commands.c | 17 +- src/battle_util.c | 21 +- src/battle_util2.c | 2 + 12 files changed, 690 insertions(+), 759 deletions(-) diff --git a/include/battle.h b/include/battle.h index feb22eaa58..e1981f0a63 100644 --- a/include/battle.h +++ b/include/battle.h @@ -243,33 +243,20 @@ struct AI_SavedBattleMon struct AiLogicData { - //attacker data - u16 atkAbility; - u16 atkItem; - u16 atkHoldEffect; - u8 atkParam; - u16 atkSpecies; - // target data - u16 defAbility; - u16 defItem; - u16 defHoldEffect; - u8 defParam; - u16 defSpecies; - // attacker partner data - u8 battlerAtkPartner; + u16 abilities[MAX_BATTLERS_COUNT]; + u16 items[MAX_BATTLERS_COUNT]; + u16 holdEffects[MAX_BATTLERS_COUNT]; + u8 holdEffectParams[MAX_BATTLERS_COUNT]; + u16 predictedMoves[MAX_BATTLERS_COUNT]; + u8 hpPercents[MAX_BATTLERS_COUNT]; u16 partnerMove; - u16 atkPartnerAbility; - u16 atkPartnerHoldEffect; - bool32 targetSameSide; - // target partner data - u8 battlerDefPartner; - u16 defPartnerAbility; - u16 defPartnerHoldEffect; + s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex + u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex + u8 moveLimitations[MAX_BATTLERS_COUNT]; }; struct AI_ThinkingStruct { - struct AiLogicData data; u8 aiState; u8 movesetIndex; u16 moveConsidered; @@ -278,7 +265,6 @@ struct AI_ThinkingStruct u32 aiFlags; u8 aiAction; u8 aiLogicId; - s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, move struct AI_SavedBattleMon saved[4]; bool8 switchMon; // Because all available moves have no/little effect. }; @@ -321,13 +307,14 @@ struct BattleResources struct BattleCallbacksStack* battleCallbackStack; struct StatsArray* beforeLvlUp; struct AI_ThinkingStruct *ai; + struct AiLogicData *aiData; struct BattleHistory *battleHistory; u8 bufferA[MAX_BATTLERS_COUNT][0x200]; u8 bufferB[MAX_BATTLERS_COUNT][0x200]; }; #define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai)) -#define AI_DATA ((struct AiLogicData *)(&gBattleResources->ai->data)) +#define AI_DATA ((struct AiLogicData *)(gBattleResources->aiData)) #define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory)) struct BattleResults @@ -593,7 +580,6 @@ struct BattleStruct bool8 spriteIgnore0Hp; struct Illusion illusion[MAX_BATTLERS_COUNT]; s8 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier - s32 aiSimulatedDamage[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, move to make debugging easier u8 soulheartBattlerId; u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles. bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once. diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index 66c614f78c..1852817491 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -24,6 +24,7 @@ void BattleAI_SetupItems(void); void BattleAI_SetupFlags(void); void BattleAI_SetupAIData(u8 defaultScoreMoves); u8 BattleAI_ChooseMoveOrAction(void); +void GetAiLogicData(void); extern u8 sBattler_AI; diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 5ccf68c58e..42374c1af3 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -33,7 +33,7 @@ bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits); bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod); s32 AI_GetAbility(u32 battlerId); u16 AI_GetHoldEffect(u32 battlerId); -u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u8 atkHoldEffect, u8 defHoldEffect, u16 move); +u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 move); bool32 DoesBattlerIgnoreAbilityChecks(u16 atkAbility, u16 move); bool32 AI_WeatherHasEffect(void); bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits); @@ -45,7 +45,7 @@ bool32 HasDamagingMoveOfType(u8 battlerId, u8 type); u32 GetBattlerSecondaryDamage(u8 battlerId); bool32 BattlerWillFaintFromWeather(u8 battler, u16 ability); bool32 BattlerWillFaintFromSecondaryDamage(u8 battler, u16 ability); -bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u32 accuracy, u16 move); +bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move); bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveIndex); u16 GetBattlerSideSpeedAverage(u8 battler); bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage); @@ -80,7 +80,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility); // move checks bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect); bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split); -s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef); +s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness); u8 GetMoveDamageResult(u16 move); u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef); u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef); @@ -143,7 +143,7 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move); bool32 IsWakeupTurn(u8 battler); // partner logic -u16 GetAllyChosenMove(void); +u16 GetAllyChosenMove(u8 battlerId); bool32 IsValidDoubleBattle(u8 battlerAtk); bool32 IsTargetingPartner(u8 battlerAtk, u8 battlerDef); bool32 DoesPartnerHaveSameMoveEffect(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove); diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index dfa81e7676..f5215fdc8b 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -16,7 +16,7 @@ struct StatFractions s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility); s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move); -u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move); +u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); u8 GetBattlerTurnOrderNum(u8 battlerId); bool32 NoAliveMonsForEitherParty(void); void SetMoveEffect(bool32 primary, u32 certain); diff --git a/include/battle_util.h b/include/battle_util.h index 1c5216c73b..53d899c9dd 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -129,6 +129,7 @@ bool32 IsBattlerAlive(u8 battlerId); u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move); u32 GetBattlerWeight(u8 battlerId); s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags); +s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier); u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities); u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef); u16 GetTypeModifier(u8 atkType, u8 defType); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d5815ad701..7ee638ddb1 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -194,7 +194,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves) defaultScoreMoves >>= 1; } - moveLimitations = CheckMoveLimitations(gActiveBattler, 0, MOVE_LIMITATIONS_ALL); + moveLimitations = AI_DATA->moveLimitations[gActiveBattler]; // Ignore moves that aren't possible to use. for (i = 0; i < MAX_MON_MOVES; i++) @@ -204,26 +204,6 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves) } sBattler_AI = gActiveBattler; - // Simulate dmg for all AI moves against all other targets - for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) - { - if (sBattler_AI == gBattlerTarget) - continue; - for (i = 0; i < MAX_MON_MOVES; i++) - { - dmg = 0; - move = gBattleMons[sBattler_AI].moves[i]; - if (gBattleMoves[move].power != 0 && !(moveLimitations & gBitTable[i])) - { - dmg = AI_CalcDamage(move, sBattler_AI, gBattlerTarget); - if (dmg == 0) - dmg = 1; - } - - AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][i] = dmg; - } - } - gBattlerTarget = SetRandomTarget(sBattler_AI); } @@ -245,29 +225,56 @@ u8 BattleAI_ChooseMoveOrAction(void) return ret; } -static void GetAiLogicData(u8 battlerAtk, u8 battlerDef) +void GetAiLogicData(void) { - // attacker data - AI_DATA->atkAbility = AI_GetAbility(battlerAtk); - AI_DATA->atkItem = gBattleMons[battlerAtk].item; - AI_DATA->atkHoldEffect = AI_GetHoldEffect(battlerAtk); - AI_DATA->atkParam = GetBattlerHoldEffectParam(battlerAtk); - AI_DATA->atkSpecies = gBattleMons[battlerAtk].species; - // target data - AI_DATA->defAbility = AI_GetAbility(battlerDef); - AI_DATA->defItem = (AI_GetHoldEffect(battlerDef) == HOLD_EFFECT_NONE) ? ITEM_NONE : gBattleMons[battlerDef].item; - AI_DATA->defHoldEffect = AI_GetHoldEffect(battlerDef); - AI_DATA->defParam = GetBattlerHoldEffectParam(battlerDef); - AI_DATA->defSpecies = gBattleMons[battlerDef].species; - // attacker partner data - AI_DATA->battlerAtkPartner = BATTLE_PARTNER(battlerAtk); - AI_DATA->partnerMove = GetAllyChosenMove(); - AI_DATA->atkPartnerAbility = AI_GetAbility(AI_DATA->battlerAtkPartner); - AI_DATA->atkPartnerHoldEffect = AI_GetHoldEffect(AI_DATA->battlerAtkPartner); - // target partner data - AI_DATA->battlerDefPartner = BATTLE_PARTNER(battlerDef); - AI_DATA->defPartnerAbility = AI_GetAbility(AI_DATA->battlerDefPartner); - AI_DATA->defPartnerHoldEffect = AI_GetHoldEffect(AI_DATA->battlerDefPartner); + u32 battlerAtk, battlerDef, i, move; + u8 effectiveness; + s32 dmg; + + if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER) + && !IsWildMonSmart())) + return; + + memset(AI_DATA, 0, sizeof(struct AiLogicData)); + for (battlerAtk = 0; battlerAtk < MAX_BATTLERS_COUNT; battlerAtk++) + { + if (IsBattlerAlive(battlerAtk)) { + AI_DATA->abilities[battlerAtk] = AI_GetAbility(battlerAtk); + AI_DATA->items[battlerAtk] = gBattleMons[battlerAtk].item; + AI_DATA->holdEffects[battlerAtk] = AI_GetHoldEffect(battlerAtk); + AI_DATA->holdEffectParams[battlerAtk] = GetBattlerHoldEffectParam(battlerAtk); + AI_DATA->predictedMoves[battlerAtk] = gLastMoves[battlerAtk]; + AI_DATA->hpPercents[battlerAtk] = GetHealthPercentage(battlerAtk); + AI_DATA->moveLimitations[battlerAtk] = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL); + + // Simulate dmg for all AI moves against all other targets + for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + { + if (battlerAtk == battlerDef) + continue; + + RecordKnownMove(battlerDef, gLastMoves[battlerDef]); + for (i = 0; i < MAX_MON_MOVES; i++) + { + dmg = 0; + effectiveness = AI_EFFECTIVENESS_x0; + move = gBattleMons[battlerAtk].moves[i]; + if (move != 0 + && move != 0xFFFF + && gBattleMoves[move].power != 0 + && !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i])) + { + dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness); + if (dmg == 0) + dmg = 1; + } + + AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg; + AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness; + } + } + } + } } static u8 ChooseMoveOrAction_Singles(void) @@ -278,9 +285,7 @@ static u8 ChooseMoveOrAction_Singles(void) s32 i, id; u32 flags = AI_THINKING_STRUCT->aiFlags; - RecordLastUsedMoveByTarget(); - GetAiLogicData(sBattler_AI, gBattlerTarget); - + AI_DATA->partnerMove = 0; // no ally while (flags != 0) { if (flags & 1) @@ -295,7 +300,6 @@ static u8 ChooseMoveOrAction_Singles(void) for (i = 0; i < MAX_MON_MOVES; i++) { gBattleStruct->aiFinalScore[sBattler_AI][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; - gBattleStruct->aiSimulatedDamage[sBattler_AI][gBattlerTarget][i] = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][i]; } // Check special AI actions. @@ -400,11 +404,10 @@ static u8 ChooseMoveOrAction_Doubles(void) BattleAI_SetupAIData(0xF); gBattlerTarget = i; - GetAiLogicData(sBattler_AI, gBattlerTarget); - if ((i & BIT_SIDE) != (sBattler_AI & BIT_SIDE)) RecordLastUsedMoveByTarget(); + AI_DATA->partnerMove = GetAllyChosenMove(i); AI_THINKING_STRUCT->aiLogicId = 0; AI_THINKING_STRUCT->movesetIndex = 0; flags = AI_THINKING_STRUCT->aiFlags; @@ -464,7 +467,6 @@ static u8 ChooseMoveOrAction_Doubles(void) for (j = 0; j < MAX_MON_MOVES; j++) { gBattleStruct->aiFinalScore[sBattler_AI][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; - gBattleStruct->aiSimulatedDamage[sBattler_AI][gBattlerTarget][j] = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][j]; } } } @@ -550,11 +552,10 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) u16 moveEffect = gBattleMoves[move].effect; s32 moveType; u16 moveTarget = gBattleMoves[move].target; - u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, move); - u8 effectiveness = AI_GetMoveEffectiveness(move, battlerAtk, battlerDef); + u8 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); u32 i; - u16 predictedMove = gLastMoves[battlerDef]; // TODO better move prediction + u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); @@ -569,7 +570,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { // handle negative checks on non-user target // check powder moves - if (TestMoveFlags(move, FLAG_POWDER) && !IsAffectedByPowder(battlerDef, AI_DATA->defAbility, AI_DATA->defHoldEffect)) + if (TestMoveFlags(move, FLAG_POWDER) && !IsAffectedByPowder(battlerDef, AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerDef])) { RETURN_SCORE_MINUS(20); } @@ -577,9 +578,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // check ground immunities if (moveType == TYPE_GROUND && !IsBattlerGrounded(battlerDef) - && ((AI_DATA->defAbility == ABILITY_LEVITATE - && DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) - || AI_DATA->defHoldEffect == HOLD_EFFECT_AIR_BALLOON + && ((AI_DATA->abilities[battlerDef] == ABILITY_LEVITATE + && DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_AIR_BALLOON || (gStatuses3[battlerDef] & (STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS))) && move != MOVE_THOUSAND_ARROWS) { @@ -602,9 +603,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // target ability checks - if (!DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) + if (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) { - switch (AI_DATA->defAbility) + switch (AI_DATA->abilities[battlerDef]) { case ABILITY_MAGIC_GUARD: switch (moveEffect) @@ -719,13 +720,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (IsShieldsDownProtected(battlerAtk) && IsNonVolatileStatusMoveEffect(moveEffect)) RETURN_SCORE_MINUS(10); break; - case ABILITY_WONDER_SKIN: - if (IS_MOVE_STATUS(move)) - accuracy = 50; - break; case ABILITY_LEAF_GUARD: if (AI_WeatherHasEffect() && (gBattleWeather & B_WEATHER_SUN) - && AI_DATA->defHoldEffect != HOLD_EFFECT_UTILITY_UMBRELLA + && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_UTILITY_UMBRELLA && IsNonVolatileStatusMoveEffect(moveEffect)) RETURN_SCORE_MINUS(10); break; @@ -734,14 +731,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // target partner ability checks & not attacking partner if (isDoubleBattle) { - switch (AI_DATA->defPartnerAbility) + switch (AI_DATA->abilities[BATTLE_PARTNER(battlerDef)]) { case ABILITY_LIGHTNING_ROD: - if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(move, AI_DATA->atkAbility)) + if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(move, AI_DATA->abilities[battlerAtk])) RETURN_SCORE_MINUS(20); break; case ABILITY_STORM_DRAIN: - if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(move, AI_DATA->atkAbility)) + if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(move, AI_DATA->abilities[battlerAtk])) RETURN_SCORE_MINUS(20); break; case ABILITY_MAGIC_BOUNCE: @@ -771,7 +768,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // gen7+ dark type mons immune to priority->elevated moves from prankster #if B_PRANKSTER >= GEN_7 - if (AI_DATA->atkAbility == ABILITY_PRANKSTER && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK) && IS_MOVE_STATUS(move) + if (AI_DATA->abilities[battlerAtk] == ABILITY_PRANKSTER && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK) && IS_MOVE_STATUS(move) && !(moveTarget & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_USER))) RETURN_SCORE_MINUS(10); #endif @@ -840,7 +837,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) default: break; // check move damage case EFFECT_SLEEP: - if (!AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_EXPLOSION: @@ -851,7 +848,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { score -= 10; } - else if (IsAbilityOnField(ABILITY_DAMP) && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) + else if (IsAbilityOnField(ABILITY_DAMP) && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) { score -= 10; } @@ -864,7 +861,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_DREAM_EATER: - if (!(gBattleMons[battlerDef].status1 & STATUS1_SLEEP) || AI_DATA->defAbility == ABILITY_COMATOSE) + if (!(gBattleMons[battlerDef].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerDef] == ABILITY_COMATOSE) score -= 8; else if (effectiveness == AI_EFFECTIVENESS_x0) score -= 10; @@ -872,7 +869,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // stat raising effects case EFFECT_ATTACK_UP: case EFFECT_ATTACK_UP_2: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; break; case EFFECT_STUFF_CHEEKS: @@ -883,65 +880,65 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_DEFENSE_UP_2: case EFFECT_DEFENSE_UP_3: case EFFECT_DEFENSE_CURL: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 10; break; case EFFECT_SPECIAL_ATTACK_UP: case EFFECT_SPECIAL_ATTACK_UP_2: case EFFECT_SPECIAL_ATTACK_UP_3: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 10; break; case EFFECT_SPECIAL_DEFENSE_UP: case EFFECT_SPECIAL_DEFENSE_UP_2: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 10; break; case EFFECT_ACCURACY_UP: case EFFECT_ACCURACY_UP_2: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ACC)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ACC)) score -= 10; break; case EFFECT_EVASION_UP: case EFFECT_EVASION_UP_2: case EFFECT_MINIMIZE: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_EVASION)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_EVASION)) score -= 10; break; case EFFECT_COSMIC_POWER: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 8; break; case EFFECT_BULK_UP: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 8; break; case EFFECT_CALM_MIND: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 8; break; case EFFECT_DRAGON_DANCE: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) score -= 8; break; case EFFECT_COIL: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ACC)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ACC)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 6; break; case EFFECT_ATTACK_ACCURACY_UP: //hone claws - if (AI_DATA->atkAbility != ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY) { if (gBattleMons[battlerAtk].statStages[STAT_ATK] >= MAX_STAT_STAGE && (gBattleMons[battlerAtk].statStages[STAT_ACC] >= MAX_STAT_STAGE || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL))) @@ -958,47 +955,47 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 20; else if (!HasMoveWithType(battlerAtk, TYPE_ELECTRIC)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 5; break; case EFFECT_QUIVER_DANCE: case EFFECT_GEOMANCY: if (gBattleMons[battlerAtk].statStages[STAT_SPATK] >= MAX_STAT_STAGE || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 6; break; case EFFECT_SHIFT_GEAR: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) score -= 8; break; case EFFECT_SHELL_SMASH: - if (AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 8; } else { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) score -= 6; } break; case EFFECT_GROWTH: case EFFECT_ATTACK_SPATK_UP: // work up - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 8; break; case EFFECT_ROTOTILLER: @@ -1006,30 +1003,30 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) && AI_IsBattlerGrounded(battlerAtk) - && (BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK))) - && !(IS_BATTLER_OF_TYPE(AI_DATA->battlerAtkPartner, TYPE_GRASS) - && AI_IsBattlerGrounded(AI_DATA->battlerAtkPartner) - && AI_DATA->atkPartnerAbility != ABILITY_CONTRARY - && (BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_ATK) - || BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_SPATK)))) + && (BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK))) + && !(IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS) + && AI_IsBattlerGrounded(BATTLE_PARTNER(battlerAtk)) + && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_CONTRARY + && (BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_ATK) + || BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPATK)))) { score -= 10; } } else if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) && AI_IsBattlerGrounded(battlerAtk) - && (BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK)))) + && (BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK)))) { score -= 10; } break; case EFFECT_GEAR_UP: - if (AI_DATA->atkAbility == ABILITY_PLUS || AI_DATA->atkAbility == ABILITY_MINUS) + if (AI_DATA->abilities[battlerAtk] == ABILITY_PLUS || AI_DATA->abilities[battlerAtk] == ABILITY_MINUS) { // same as growth, work up - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 8; break; } @@ -1040,13 +1037,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { - if (AI_DATA->atkPartnerAbility == ABILITY_PLUS || AI_DATA->atkPartnerAbility == ABILITY_MINUS) + if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { - if ((!BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) - && (!BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL))) + if ((!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + && (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL))) score -= 10; } - else if (AI_DATA->atkAbility != ABILITY_PLUS && AI_DATA->atkAbility != ABILITY_MINUS) + else if (AI_DATA->abilities[battlerAtk] != ABILITY_PLUS && AI_DATA->abilities[battlerAtk] != ABILITY_MINUS) { score -= 10; // nor our or our partner's ability is plus/minus } @@ -1057,11 +1054,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_MAGNETIC_FLUX: - if (AI_DATA->atkAbility == ABILITY_PLUS || AI_DATA->atkAbility == ABILITY_MINUS) + if (AI_DATA->abilities[battlerAtk] == ABILITY_PLUS || AI_DATA->abilities[battlerAtk] == ABILITY_MINUS) { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) score -= 8; } else if (!isDoubleBattle) @@ -1071,14 +1068,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { - if (AI_DATA->atkPartnerAbility == ABILITY_PLUS || AI_DATA->atkPartnerAbility == ABILITY_MINUS) + if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { - if (!BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_DEF)) + if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_SPDEF)) + else if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) score -= 8; } - else if (AI_DATA->atkAbility != ABILITY_PLUS && AI_DATA->atkAbility != ABILITY_MINUS) + else if (AI_DATA->abilities[battlerAtk] != ABILITY_PLUS && AI_DATA->abilities[battlerAtk] != ABILITY_MINUS) { score -= 10; // nor our or our partner's ability is plus/minus } @@ -1087,49 +1084,49 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // stat lowering effects case EFFECT_ATTACK_DOWN: case EFFECT_ATTACK_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) score -= 10; - else if (AI_DATA->defAbility == ABILITY_HYPER_CUTTER) + else if (AI_DATA->abilities[battlerDef] == ABILITY_HYPER_CUTTER) score -= 10; break; case EFFECT_DEFENSE_DOWN: case EFFECT_DEFENSE_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_DEF)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_DEF)) score -= 10; break; case EFFECT_SPEED_DOWN: case EFFECT_SPEED_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) score -= 10; - else if (AI_DATA->defAbility == ABILITY_SPEED_BOOST) + else if (AI_DATA->abilities[battlerDef] == ABILITY_SPEED_BOOST) score -= 10; break; case EFFECT_SPECIAL_ATTACK_DOWN: case EFFECT_SPECIAL_ATTACK_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_SPECIAL)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_SPECIAL)) score -= 10; break; case EFFECT_SPECIAL_DEFENSE_DOWN: case EFFECT_SPECIAL_DEFENSE_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPDEF)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPDEF)) score -= 10; break; case EFFECT_ACCURACY_DOWN: case EFFECT_ACCURACY_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ACC)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ACC)) score -= 10; - else if (AI_DATA->defAbility == ABILITY_KEEN_EYE) + else if (AI_DATA->abilities[battlerDef] == ABILITY_KEEN_EYE) score -= 8; break; case EFFECT_EVASION_DOWN: case EFFECT_EVASION_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_EVASION)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_EVASION)) score -= 10; break; case EFFECT_TICKLE: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ATK)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_DEF)) + else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_DEF)) score -= 8; break; case EFFECT_VENOM_DRENCH: @@ -1139,18 +1136,18 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPATK)) + else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) score -= 8; - else if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) score -= 6; } break; case EFFECT_NOBLE_ROAR: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPATK)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) score -= 8; break; case EFFECT_CAPTIVATE: @@ -1163,7 +1160,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; // other case EFFECT_HAZE: - if (PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) { score -= 10; // partner already using haze } @@ -1171,12 +1168,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) { - if (gBattleMons[battlerAtk].statStages[i] > DEFAULT_STAT_STAGE || gBattleMons[AI_DATA->battlerAtkPartner].statStages[i] > DEFAULT_STAT_STAGE) + if (gBattleMons[battlerAtk].statStages[i] > DEFAULT_STAT_STAGE || gBattleMons[BATTLE_PARTNER(battlerAtk)].statStages[i] > DEFAULT_STAT_STAGE) score -= 10; // Don't want to reset our boosted stats } for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) { - if (gBattleMons[battlerDef].statStages[i] < DEFAULT_STAT_STAGE || gBattleMons[AI_DATA->battlerDefPartner].statStages[i] < DEFAULT_STAT_STAGE) + if (gBattleMons[battlerDef].statStages[i] < DEFAULT_STAT_STAGE || gBattleMons[BATTLE_PARTNER(battlerDef)].statStages[i] < DEFAULT_STAT_STAGE) score -= 10; //Don't want to reset enemy lowered stats } } @@ -1199,46 +1196,46 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) //case EFFECT_ENDEAVOR: case EFFECT_LOW_KICK: // AI_CBM_HighRiskForDamage - if (AI_DATA->defAbility == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) + if (AI_DATA->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) score -= 10; break; case EFFECT_COUNTER: case EFFECT_MIRROR_COAT: - if (IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) + if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) score--; if (predictedMove == MOVE_NONE || GetBattleMoveSplit(predictedMove) == SPLIT_STATUS - || DoesSubstituteBlockMove(battlerAtk, AI_DATA->battlerDefPartner, predictedMove)) + || DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove)) score -= 10; break; case EFFECT_ROAR: if (CountUsablePartyMons(battlerDef) == 0) score -= 10; - else if (AI_DATA->defAbility == ABILITY_SUCTION_CUPS) + else if (AI_DATA->abilities[battlerDef] == ABILITY_SUCTION_CUPS) score -= 10; break; case EFFECT_TOXIC_THREAD: - if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) score--; // may still want to just poison //fallthrough case EFFECT_POISON: case EFFECT_TOXIC: - if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_LIGHT_SCREEN: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_LIGHTSCREEN - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_REFLECT: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_REFLECT - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_AURORA_VEIL: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) || !(gBattleWeather & B_WEATHER_HAIL)) score -= 10; break; @@ -1247,12 +1244,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) && move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE)) return 0; - if (!ShouldTryOHKO(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, accuracy, move)) + if (!ShouldTryOHKO(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move)) score -= 10; break; case EFFECT_MIST: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_MIST - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_FOCUS_ENERGY: @@ -1262,17 +1259,17 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_CONFUSE: case EFFECT_SWAGGER: case EFFECT_FLATTER: - if (!AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (!AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_PARALYZE: - if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_SUBSTITUTE: - if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || AI_DATA->defAbility == ABILITY_INFILTRATOR) + if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || AI_DATA->abilities[battlerDef] == ABILITY_INFILTRATOR) score -= 8; - else if (GetHealthPercentage(battlerAtk) <= 25) + else if (AI_DATA->hpPercents[battlerAtk] <= 25) score -= 10; else if (B_SOUND_SUBSTITUTE >= GEN_6 && TestMoveFlagsInMoveset(battlerDef, FLAG_SOUND)) score -= 8; @@ -1280,15 +1277,15 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_LEECH_SEED: if (gStatuses3[battlerDef] & STATUS3_LEECHSEED || IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; - else if (AI_DATA->defAbility == ABILITY_LIQUID_OOZE) + else if (AI_DATA->abilities[battlerDef] == ABILITY_LIQUID_OOZE) score -= 3; break; case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB) - && !PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB) + && !PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first { @@ -1307,8 +1304,8 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB) - && !DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB) + && !DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first { @@ -1327,60 +1324,60 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_SNORE: case EFFECT_SLEEP_TALK: - if (IsWakeupTurn(battlerAtk) || (!(gBattleMons[battlerAtk].status1 & STATUS1_SLEEP) || AI_DATA->atkAbility != ABILITY_COMATOSE)) + if (IsWakeupTurn(battlerAtk) || (!(gBattleMons[battlerAtk].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerAtk] != ABILITY_COMATOSE)) score -= 10; // if mon will wake up, is not asleep, or is not comatose break; case EFFECT_MEAN_LOOK: - if (IsBattlerTrapped(battlerDef, TRUE) || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (IsBattlerTrapped(battlerDef, TRUE) || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_NIGHTMARE: if (gBattleMons[battlerDef].status2 & STATUS2_NIGHTMARE) score -= 10; - else if (!(gBattleMons[battlerDef].status1 & STATUS1_SLEEP) || AI_DATA->defAbility == ABILITY_COMATOSE) + else if (!(gBattleMons[battlerDef].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerDef] == ABILITY_COMATOSE) score -= 8; - else if (DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + else if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_CURSE: if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) { if (gBattleMons[battlerDef].status2 & STATUS2_CURSED - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; - else if (GetHealthPercentage(battlerAtk) <= 50) + else if (AI_DATA->hpPercents[battlerAtk] <= 50) score -= 6; } else // regular curse { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->atkAbility, STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) score -= 8; } break; case EFFECT_SPIKES: if (gSideTimers[GetBattlerSide(battlerDef)].spikesAmount >= 3) score -= 10; - else if (PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].spikesAmount == 2) score -= 10; // only one mon needs to set up the last layer of Spikes break; case EFFECT_STEALTH_ROCK: if (gSideTimers[GetBattlerSide(battlerDef)].stealthRockAmount > 0 - || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) //Only one mon needs to set up Stealth Rocks + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) //Only one mon needs to set up Stealth Rocks score -= 10; break; case EFFECT_TOXIC_SPIKES: if (gSideTimers[GetBattlerSide(battlerDef)].toxicSpikesAmount >= 2) score -= 10; - else if (PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].toxicSpikesAmount == 1) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].toxicSpikesAmount == 1) score -= 10; // only one mon needs to set up the last layer of Toxic Spikes break; case EFFECT_STICKY_WEB: if (gSideTimers[GetBattlerSide(battlerDef)].stickyWebAmount) score -= 10; - else if (PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].stickyWebAmount) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].stickyWebAmount) score -= 10; // only one mon needs to set up Sticky Web break; case EFFECT_FORESIGHT: @@ -1388,74 +1385,74 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; else if (gBattleMons[battlerDef].statStages[STAT_EVASION] <= 4 || !(IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST)) - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 9; break; case EFFECT_PERISH_SONG: if (isDoubleBattle) { if (CountUsablePartyMons(battlerAtk) == 0 - && AI_DATA->atkAbility != ABILITY_SOUNDPROOF - && AI_DATA->atkPartnerAbility != ABILITY_SOUNDPROOF + && AI_DATA->abilities[battlerAtk] != ABILITY_SOUNDPROOF + && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_SOUNDPROOF && CountUsablePartyMons(FOE(battlerAtk)) >= 1) { score -= 10; //Don't wipe your team if you're going to lose } - else if ((!IsBattlerAlive(FOE(battlerAtk)) || AI_GetAbility(FOE(battlerAtk)) == ABILITY_SOUNDPROOF + else if ((!IsBattlerAlive(FOE(battlerAtk)) || AI_DATA->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF || gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG) - && (!IsBattlerAlive(BATTLE_PARTNER(FOE(battlerAtk))) || AI_GetAbility(BATTLE_PARTNER(FOE(battlerAtk))) == ABILITY_SOUNDPROOF + && (!IsBattlerAlive(BATTLE_PARTNER(FOE(battlerAtk))) || AI_DATA->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_SOUNDPROOF || gStatuses3[BATTLE_PARTNER(FOE(battlerAtk))] & STATUS3_PERISH_SONG)) { score -= 10; //Both enemies are perish songed } - else if (DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + else if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) { score -= 10; } } else { - if (CountUsablePartyMons(battlerAtk) == 0 && AI_DATA->atkAbility != ABILITY_SOUNDPROOF + if (CountUsablePartyMons(battlerAtk) == 0 && AI_DATA->abilities[battlerAtk] != ABILITY_SOUNDPROOF && CountUsablePartyMons(battlerDef) >= 1) score -= 10; - if (gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG || AI_GetAbility(FOE(battlerAtk)) == ABILITY_SOUNDPROOF) + if (gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG || AI_DATA->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF) score -= 10; } break; case EFFECT_SANDSTORM: if (gBattleWeather & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove)) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; case EFFECT_SUNNY_DAY: if (gBattleWeather & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove)) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; case EFFECT_RAIN_DANCE: if (gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove)) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; case EFFECT_HAIL: if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove)) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) score -= 8; break; case EFFECT_ATTRACT: - if (!AI_CanBeInfatuated(battlerAtk, battlerDef, AI_DATA->defAbility, + if (!AI_CanBeInfatuated(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], GetGenderFromSpeciesAndPersonality(gBattleMons[battlerAtk].species, gBattleMons[battlerAtk].personality), GetGenderFromSpeciesAndPersonality(gBattleMons[battlerDef].species, gBattleMons[battlerDef].personality))) score -= 10; break; case EFFECT_SAFEGUARD: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_SAFEGUARD - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_MAGNITUDE: - if (AI_DATA->defAbility == ABILITY_LEVITATE) + if (AI_DATA->abilities[battlerDef] == ABILITY_LEVITATE) score -= 10; break; case EFFECT_PARTING_SHOT: @@ -1482,9 +1479,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 6; break; case EFFECT_BELLY_DRUM: - if (AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) score -= 10; - else if (GetHealthPercentage(battlerAtk) <= 60) + else if (AI_DATA->hpPercents[battlerAtk] <= 60) score -= 10; break; case EFFECT_FUTURE_SIGHT: @@ -1504,7 +1501,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (move == MOVE_FAKE_OUT) // filter out first impression { - if ((AI_DATA->atkHoldEffect == HOLD_EFFECT_CHOICE_BAND || AI_DATA->atkAbility == ABILITY_GORILLA_TACTICS) + if ((AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND || AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS) && (CountUsablePartyMons(battlerDef) > 0 || !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))) { if (CountUsablePartyMons(battlerAtk) == 0) @@ -1529,27 +1526,27 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { if (AtMaxHp(battlerAtk)) score -= 10; - else if (GetHealthPercentage(battlerAtk) >= 80) + else if (AI_DATA->hpPercents[battlerAtk] >= 80) score -= 5; // do it if nothing better } break; case EFFECT_TORMENT: if (gBattleMons[battlerDef].status2 & STATUS2_TORMENT - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) { score -= 10; break; } - if (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect == HOLD_EFFECT_MENTAL_HERB) + if (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_MENTAL_HERB) score -= 6; break; case EFFECT_WILL_O_WISP: - if (!AI_CanBurn(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (!AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_MEMENTO: - if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; else if (gBattleMons[battlerDef].statStages[STAT_ATK] == MIN_STAT_STAGE && gBattleMons[battlerDef].statStages[STAT_SPATK] == MIN_STAT_STAGE) score -= 10; @@ -1557,15 +1554,15 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FOLLOW_ME: case EFFECT_HELPING_HAND: if (!isDoubleBattle - || !IsBattlerAlive(AI_DATA->battlerAtkPartner) - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) + || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) || (AI_DATA->partnerMove != MOVE_NONE && IS_MOVE_STATUS(AI_DATA->partnerMove)) - || *(gBattleStruct->monToSwitchIntoId + AI_DATA->battlerAtkPartner) != PARTY_SIZE) //Partner is switching out. + || *(gBattleStruct->monToSwitchIntoId + BATTLE_PARTNER(battlerAtk)) != PARTY_SIZE) //Partner is switching out. score -= 10; break; case EFFECT_TRICK: case EFFECT_KNOCK_OFF: - if (AI_DATA->defAbility == ABILITY_STICKY_HOLD) + if (AI_DATA->abilities[battlerDef] == ABILITY_STICKY_HOLD) score -= 10; break; case EFFECT_INGRAIN: @@ -1589,14 +1586,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_PSYCHO_SHIFT: - if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY && !AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY && !AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; else if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && !AI_CanBurn(battlerAtk, battlerDef, - AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; - else if (gBattleMons[battlerAtk].status1 & STATUS1_PARALYSIS && !AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + else if (gBattleMons[battlerAtk].status1 & STATUS1_PARALYSIS && !AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; - else if (gBattleMons[battlerAtk].status1 & STATUS1_SLEEP && !AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + else if (gBattleMons[battlerAtk].status1 & STATUS1_SLEEP && !AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; else score -= 10; // attacker has no status to transmit @@ -1604,23 +1601,23 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_MUD_SPORT: if (gFieldStatuses & STATUS_FIELD_MUDSPORT || gStatuses4[battlerAtk] & STATUS4_MUD_SPORT - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_WATER_SPORT: if (gFieldStatuses & STATUS_FIELD_WATERSPORT || gStatuses4[battlerAtk] & STATUS4_WATER_SPORT - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_ABSORB: - if (AI_DATA->defAbility == ABILITY_LIQUID_OOZE) + if (AI_DATA->abilities[battlerDef] == ABILITY_LIQUID_OOZE) score -= 6; break; case EFFECT_STRENGTH_SAP: - if (AI_DATA->defAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->defAbility, STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) score -= 10; break; case EFFECT_COPYCAT: @@ -1628,23 +1625,23 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) return AI_CheckBadMove(battlerAtk, battlerDef, predictedMove, score); case EFFECT_FLOWER_SHIELD: if (!IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) - && !(isDoubleBattle && IS_BATTLER_OF_TYPE(AI_DATA->battlerAtkPartner, TYPE_GRASS))) + && !(isDoubleBattle && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS))) score -= 10; break; case EFFECT_AROMATIC_MIST: - if (!isDoubleBattle || gBattleMons[AI_DATA->battlerAtkPartner].hp == 0 || !BattlerStatCanRise(AI_DATA->battlerAtkPartner, AI_DATA->atkPartnerAbility, STAT_SPDEF)) + if (!isDoubleBattle || gBattleMons[BATTLE_PARTNER(battlerAtk)].hp == 0 || !BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) score -= 10; break; case EFFECT_BIDE: if (!HasDamagingMove(battlerDef) - || GetHealthPercentage(battlerAtk) < 30 //Close to death + || AI_DATA->hpPercents[battlerAtk] < 30 //Close to death || gBattleMons[battlerDef].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) //No point in biding if can't take damage score -= 10; break; case EFFECT_HIT_SWITCH_TARGET: - if (DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; // don't scare away pokemon twice - else if (GetHealthPercentage(battlerDef) < 10 && GetBattlerSecondaryDamage(battlerDef)) + else if (AI_DATA->hpPercents[battlerDef] < 10 && GetBattlerSecondaryDamage(battlerDef)) score -= 10; // don't blow away mon that will faint soon else if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG) score -= 10; @@ -1655,7 +1652,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_REST: - if (!AI_CanSleep(battlerAtk, AI_DATA->atkAbility)) + if (!AI_CanSleep(battlerAtk, AI_DATA->abilities[battlerAtk])) score -= 10; //fallthrough case EFFECT_RESTORE_HP: @@ -1663,7 +1660,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ROOST: if (AtMaxHp(battlerAtk)) score -= 10; - else if (GetHealthPercentage(battlerAtk) >= 90) + else if (AI_DATA->hpPercents[battlerAtk] >= 90) score -= 9; //No point in healing, but should at least do it if nothing better break; case EFFECT_MORNING_SUN: @@ -1673,31 +1670,31 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 3; else if (AtMaxHp(battlerAtk)) score -= 10; - else if (GetHealthPercentage(battlerAtk) >= 90) + else if (AI_DATA->hpPercents[battlerAtk] >= 90) score -= 9; //No point in healing, but should at least do it if nothing better break; case EFFECT_PURIFY: if (!(gBattleMons[battlerDef].status1 & STATUS1_ANY)) score -= 10; - else if (battlerDef == AI_DATA->battlerAtkPartner) + else if (battlerDef == BATTLE_PARTNER(battlerAtk)) break; //Always heal your ally else if (AtMaxHp(battlerAtk)) score -= 10; - else if (GetHealthPercentage(battlerAtk) >= 90) + else if (AI_DATA->hpPercents[battlerAtk] >= 90) score -= 8; //No point in healing, but should at least do it if nothing better break; case EFFECT_SUPER_FANG: - if (GetHealthPercentage(battlerDef) < 50) + if (AI_DATA->hpPercents[battlerDef] < 50) score -= 4; break; case EFFECT_RECOIL_IF_MISS: - if (AI_DATA->atkAbility != ABILITY_MAGIC_GUARD && accuracy < 75) + if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_GetMoveAccuracy(battlerAtk, battlerDef, move) < 75) score -= 6; break; case EFFECT_RECOIL_25: - if (AI_DATA->atkAbility != ABILITY_MAGIC_GUARD && AI_DATA->atkAbility != ABILITY_ROCK_HEAD) + if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 4); + u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 4); if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex)) score -= 10; break; @@ -1705,18 +1702,18 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_RECOIL_33: case EFFECT_RECOIL_33_STATUS: - if (AI_DATA->atkAbility != ABILITY_MAGIC_GUARD && AI_DATA->atkAbility != ABILITY_ROCK_HEAD) + if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 3); + u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 3); if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex)) score -= 10; break; } break; case EFFECT_RECOIL_50: - if (AI_DATA->atkAbility != ABILITY_MAGIC_GUARD && AI_DATA->atkAbility != ABILITY_ROCK_HEAD) + if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 2); + u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 2); if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex)) score -= 10; break; @@ -1724,13 +1721,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TEETER_DANCE: if (((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move) && AI_DATA->defAbility == ABILITY_OWN_TEMPO) + || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move) && AI_DATA->abilities[battlerDef] == ABILITY_OWN_TEMPO) || (IsBattlerGrounded(battlerDef) && AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) || (DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) - && ((gBattleMons[AI_DATA->battlerDefPartner].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move) && AI_DATA->defPartnerAbility == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(AI_DATA->battlerDefPartner) && AI_IsTerrainAffected(AI_DATA->battlerDefPartner, STATUS_FIELD_MISTY_TERRAIN)) - || (DoesSubstituteBlockMove(battlerAtk, AI_DATA->battlerDefPartner, move)))) + && ((gBattleMons[BATTLE_PARTNER(battlerDef)].status2 & STATUS2_CONFUSION) + || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_OWN_TEMPO) + || (IsBattlerGrounded(BATTLE_PARTNER(battlerDef)) && AI_IsTerrainAffected(BATTLE_PARTNER(battlerDef), STATUS_FIELD_MISTY_TERRAIN)) + || (DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), move)))) { score -= 10; } @@ -1741,13 +1738,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_TWO_TURNS_ATTACK: - if (AI_DATA->atkHoldEffect != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) + if (AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) score -= 6; break; case EFFECT_RECHARGE: - if (AI_DATA->defAbility == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) + if (AI_DATA->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) score -= 10; - else if (AI_DATA->atkAbility != ABILITY_TRUANT + else if (AI_DATA->abilities[battlerAtk] != ABILITY_TRUANT && !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) score -= 2; break; @@ -1778,15 +1775,15 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_LOCK_ON: if (gStatuses3[battlerDef] & STATUS3_ALWAYS_HITS - || AI_DATA->atkAbility == ABILITY_NO_GUARD - || AI_DATA->defAbility == ABILITY_NO_GUARD - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || AI_DATA->abilities[battlerAtk] == ABILITY_NO_GUARD + || AI_DATA->abilities[battlerDef] == ABILITY_NO_GUARD + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_LASER_FOCUS: if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS) score -= 10; - else if (AI_DATA->defAbility == ABILITY_SHELL_ARMOR || AI_DATA->defAbility == ABILITY_BATTLE_ARMOR) + else if (AI_DATA->abilities[battlerDef] == ABILITY_SHELL_ARMOR || AI_DATA->abilities[battlerDef] == ABILITY_BATTLE_ARMOR) score -= 8; break; case EFFECT_SKETCH: @@ -1801,7 +1798,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // TODO break; case EFFECT_HEAL_BELL: - if (!AnyPartyMemberStatused(battlerAtk, TestMoveFlags(move, FLAG_SOUND)) || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (!AnyPartyMemberStatused(battlerAtk, TestMoveFlags(move, FLAG_SOUND)) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_HIT_PREVENT_ESCAPE: @@ -1835,7 +1832,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (decreased) break; - if (IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility)) + if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])) { score -= 10; break; @@ -1846,8 +1843,8 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) && move != MOVE_CRAFTY_SHIELD) //These moves have infinite usage { if (GetBattlerSecondaryDamage(battlerAtk) >= gBattleMons[battlerAtk].hp - && AI_DATA->defAbility != ABILITY_MOXIE - && AI_DATA->defAbility != ABILITY_BEAST_BOOST) + && AI_DATA->abilities[battlerDef] != ABILITY_MOXIE + && AI_DATA->abilities[battlerDef] != ABILITY_BEAST_BOOST) { score -= 10; //Don't protect if you're going to faint after protecting } @@ -1881,7 +1878,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gBattleMons[battlerDef].statStages[STAT_EVASION] <= 4 || !(IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK)) - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 9; break; case EFFECT_BURN_UP: @@ -1894,7 +1891,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || gSideTimers[GetBattlerSide(battlerDef)].auroraVeilTimer != 0 || gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY) { - if (PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) { score -= 10; //Only need one hazards removal break; @@ -1910,7 +1907,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // partner is going to set up hazards - && AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerAtk) == AI_IS_FASTER) // partner is going to set up before the potential Defog + && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk) == AI_IS_FASTER) // partner is going to set up before the potential Defog { score -= 10; break; // Don't use Defog if partner is going to set up hazards @@ -1919,7 +1916,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // evasion check if (gBattleMons[battlerDef].statStages[STAT_EVASION] == MIN_STAT_STAGE - || ((AI_DATA->defAbility == ABILITY_CONTRARY) && !IsTargetingPartner(battlerAtk, battlerDef))) // don't want to raise target stats unless its your partner + || ((AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) && !IsTargetingPartner(battlerAtk, battlerDef))) // don't want to raise target stats unless its your partner score -= 10; break; @@ -1927,12 +1924,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) { - if (gBattleMons[battlerAtk].statStages[i] > DEFAULT_STAT_STAGE || gBattleMons[AI_DATA->battlerAtkPartner].statStages[i] > DEFAULT_STAT_STAGE) + if (gBattleMons[battlerAtk].statStages[i] > DEFAULT_STAT_STAGE || gBattleMons[BATTLE_PARTNER(battlerAtk)].statStages[i] > DEFAULT_STAT_STAGE) score -= 10; // Don't want to reset our boosted stats } for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) { - if (gBattleMons[battlerDef].statStages[i] < DEFAULT_STAT_STAGE || gBattleMons[AI_DATA->battlerDefPartner].statStages[i] < DEFAULT_STAT_STAGE) + if (gBattleMons[battlerDef].statStages[i] < DEFAULT_STAT_STAGE || gBattleMons[BATTLE_PARTNER(battlerDef)].statStages[i] < DEFAULT_STAT_STAGE) score -= 10; //Don't want to copy enemy lowered stats } } @@ -1940,8 +1937,8 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SPECTRAL_THIEF: break; case EFFECT_SOLAR_BEAM: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_POWER_HERB - || (AI_WeatherHasEffect() && gBattleWeather & B_WEATHER_SUN && AI_DATA->atkHoldEffect != HOLD_EFFECT_UTILITY_UMBRELLA)) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB + || (AI_WeatherHasEffect() && gBattleWeather & B_WEATHER_SUN && AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) break; if (CanTargetFaintAi(battlerDef, battlerAtk)) //Attacker can be knocked out score -= 4; @@ -1952,39 +1949,39 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) && gBattleMoves[predictedMove].effect == EFFECT_SEMI_INVULNERABLE) score -= 10; // Don't Fly/dig/etc if opponent is going to fly/dig/etc after you - if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->atkAbility) + if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk]) && (move == MOVE_FLY || move == MOVE_BOUNCE)) score -= 10; // Attacker will faint while in the air break; case EFFECT_HEALING_WISH: //healing wish, lunar dance - if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; else if (IsPartyFullyHealedExceptBattler(battlerAtk)) score -= 10; break; case EFFECT_FINAL_GAMBIT: - if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_NATURE_POWER: return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(), score); case EFFECT_TAUNT: if (gDisableStructs[battlerDef].tauntTimer > 0 - || DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score--; break; case EFFECT_BESTOW: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_NONE + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_NONE || !CanBattlerGetOrLoseItem(battlerAtk, gBattleMons[battlerAtk].item)) // AI knows its own item score -= 10; break; case EFFECT_ROLE_PLAY: - if (AI_DATA->atkAbility == AI_DATA->defAbility - || AI_DATA->defAbility == ABILITY_NONE - || IsRolePlayBannedAbilityAtk(AI_DATA->atkAbility) - || IsRolePlayBannedAbility(AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerAtk] == AI_DATA->abilities[battlerDef] + || AI_DATA->abilities[battlerDef] == ABILITY_NONE + || IsRolePlayBannedAbilityAtk(AI_DATA->abilities[battlerAtk]) + || IsRolePlayBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; - else if (IsAbilityOfRating(AI_DATA->atkAbility, 5)) + else if (IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 5)) score -= 4; break; case EFFECT_WISH: @@ -2006,40 +2003,40 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_YAWN: if (gStatuses3[battlerDef] & STATUS3_YAWN) score -= 10; - else if (!AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + else if (!AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_SKILL_SWAP: - if (AI_DATA->atkAbility == ABILITY_NONE || AI_DATA->defAbility == ABILITY_NONE - || IsSkillSwapBannedAbility(AI_DATA->atkAbility) || IsSkillSwapBannedAbility(AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerAtk] == ABILITY_NONE || AI_DATA->abilities[battlerDef] == ABILITY_NONE + || IsSkillSwapBannedAbility(AI_DATA->abilities[battlerAtk]) || IsSkillSwapBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; break; case EFFECT_WORRY_SEED: - if (AI_DATA->defAbility == ABILITY_INSOMNIA - || IsWorrySeedBannedAbility(AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerDef] == ABILITY_INSOMNIA + || IsWorrySeedBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; break; case EFFECT_GASTRO_ACID: if (gStatuses3[battlerDef] & STATUS3_GASTRO_ACID - || IsGastroAcidBannedAbility(AI_DATA->defAbility)) + || IsGastroAcidBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; break; case EFFECT_ENTRAINMENT: - if (AI_DATA->atkAbility == ABILITY_NONE - || IsEntrainmentBannedAbilityAttacker(AI_DATA->atkAbility) - || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerAtk] == ABILITY_NONE + || IsEntrainmentBannedAbilityAttacker(AI_DATA->abilities[battlerAtk]) + || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; break; case EFFECT_CORE_ENFORCER: break; case EFFECT_SIMPLE_BEAM: - if (AI_DATA->defAbility == ABILITY_SIMPLE - || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerDef] == ABILITY_SIMPLE + || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->abilities[battlerDef])) score -= 10; break; case EFFECT_SNATCH: if (!TestMoveFlagsInMoveset(battlerDef, FLAG_SNATCH_AFFECTED) - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_POWER_TRICK: @@ -2140,42 +2137,42 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_NATURAL_GIFT: - if (AI_DATA->atkAbility == ABILITY_KLUTZ + if (AI_DATA->abilities[battlerAtk] == ABILITY_KLUTZ || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || GetPocketByItemId(gBattleMons[battlerAtk].item) != POCKET_BERRIES) score -= 10; break; case EFFECT_GRASSY_TERRAIN: - if (PartnerMoveEffectIsTerrain(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) score -= 10; break; case EFFECT_ELECTRIC_TERRAIN: - if (PartnerMoveEffectIsTerrain(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) score -= 10; break; case EFFECT_PSYCHIC_TERRAIN: - if (PartnerMoveEffectIsTerrain(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) score -= 10; break; case EFFECT_MISTY_TERRAIN: - if (PartnerMoveEffectIsTerrain(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) score -= 10; break; case EFFECT_PLEDGE: - if (isDoubleBattle && gBattleMons[AI_DATA->battlerAtkPartner].hp > 0) + if (isDoubleBattle && gBattleMons[BATTLE_PARTNER(battlerAtk)].hp > 0) { if (AI_DATA->partnerMove != MOVE_NONE && gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_PLEDGE && move != AI_DATA->partnerMove) // Different pledge moves { - if (gBattleMons[AI_DATA->battlerAtkPartner].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) - // && gBattleMons[AI_DATA->battlerAtkPartner].status1 != 1) // Will wake up this turn - how would AI know + if (gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) + // && gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 != 1) // Will wake up this turn - how would AI know score -= 10; // Don't use combo move if your partner will cause failure } } break; case EFFECT_TRICK_ROOM: - if (PartnerMoveIs(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove, MOVE_TRICK_ROOM)) + if (PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_TRICK_ROOM)) { score -= 10; } @@ -2191,23 +2188,23 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_MAGIC_ROOM: - if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_WONDER_ROOM: - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_GRAVITY: if ((gFieldStatuses & STATUS_FIELD_GRAVITY && !IS_BATTLER_OF_TYPE(battlerAtk, TYPE_FLYING) - && AI_DATA->atkHoldEffect != HOLD_EFFECT_AIR_BALLOON) // Should revert Gravity in this case - || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + && AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_AIR_BALLOON) // Should revert Gravity in this case + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_ION_DELUGE: if (gFieldStatuses & STATUS_FIELD_ION_DELUGE - || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_FLING: @@ -2222,19 +2219,19 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) switch (effect) { case MOVE_EFFECT_BURN: - if (!AI_CanBurn(battlerAtk, battlerDef, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (!AI_CanBurn(battlerAtk, battlerDef, BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case MOVE_EFFECT_PARALYSIS: - if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case MOVE_EFFECT_POISON: - if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case MOVE_EFFECT_TOXIC: - if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) score -= 10; break; case MOVE_EFFECT_FREEZE: @@ -2246,34 +2243,34 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_EMBARGO: - if (AI_DATA->defAbility == ABILITY_KLUTZ + if (AI_DATA->abilities[battlerDef] == ABILITY_KLUTZ || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || gDisableStructs[battlerDef].embargoTimer != 0 - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_POWDER: if (!HasMoveWithType(battlerDef, TYPE_FIRE) - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_TELEKINESIS: if (gStatuses3[battlerDef] & (STATUS3_TELEKINESIS | STATUS3_ROOTED | STATUS3_SMACKED_DOWN) || gFieldStatuses & STATUS_FIELD_GRAVITY - || AI_DATA->defHoldEffect == HOLD_EFFECT_IRON_BALL + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_IRON_BALL || IsTelekinesisBannedSpecies(gBattleMons[battlerDef].species) - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_THROAT_CHOP: break; case EFFECT_HEAL_BLOCK: if (gDisableStructs[battlerDef].healBlockTimer != 0 - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_SOAK: - if (PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove) + if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove) || (gBattleMons[battlerDef].type1 == TYPE_WATER && gBattleMons[battlerDef].type2 == TYPE_WATER && gBattleMons[battlerDef].type3 == TYPE_MYSTERY)) @@ -2283,11 +2280,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) switch (move) { case MOVE_TRICK_OR_TREAT: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case MOVE_FORESTS_CURSE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; } @@ -2318,7 +2315,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ELECTRIFY: if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER //|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_TOPSY_TURVY: @@ -2328,7 +2325,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) u8 targetNegativeStages = CountNegativeStatStages(battlerDef); if (targetPositiveStages == 0 //No good stat changes to make bad - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; else if (targetNegativeStages < targetPositiveStages) @@ -2336,7 +2333,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_FAIRY_LOCK: - if ((gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if ((gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_DO_NOTHING: @@ -2359,7 +2356,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) #endif || (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF) || gBattleMons[battlerDef].status2 & STATUS2_MULTIPLETURNS - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) { score -= 10; } @@ -2386,14 +2383,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_QUASH: if (!isDoubleBattle || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_AFTER_YOU: if (!IsTargetingPartner(battlerAtk, battlerDef) || !isDoubleBattle || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER - || PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_SUCKER_PUNCH: @@ -2405,19 +2402,19 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TAILWIND: if (gSideTimers[GetBattlerSide(battlerAtk)].tailwindTimer != 0 - || PartnerMoveIs(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove, MOVE_TAILWIND) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_TAILWIND) || (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 1)) // Trick Room active and not ending this turn score -= 10; break; case EFFECT_LUCKY_CHANT: if (gSideTimers[GET_BATTLER_SIDE(battlerAtk)].luckyChantTimer != 0 - || PartnerMoveIsSameNoTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 10; break; case EFFECT_MAGNET_RISE: if (gFieldStatuses & STATUS_FIELD_GRAVITY || gDisableStructs[battlerAtk].magnetRiseTimer != 0 - || AI_DATA->atkHoldEffect == HOLD_EFFECT_IRON_BALL + || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_IRON_BALL || gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_MAGNET_RISE | STATUS3_SMACKED_DOWN) || !IsBattlerGrounded(battlerAtk)) score -= 10; @@ -2432,7 +2429,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_SYNCHRONOISE: //Check holding ring target or is of same type - if (AI_DATA->defHoldEffect == HOLD_EFFECT_RING_TARGET + if (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET || IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type1) || IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type2) || IS_BATTLER_OF_TYPE(battlerDef, gBattleMons[battlerAtk].type3)) @@ -2443,16 +2440,16 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ERUPTION: if (effectiveness <= AI_EFFECTIVENESS_x0_5) score--; - if (GetHealthPercentage(battlerDef) < 50) + if (AI_DATA->hpPercents[battlerDef] < 50) score--; break; case EFFECT_VITAL_THROW: - if (WillAIStrikeFirst() && GetHealthPercentage(battlerAtk) < 40) + if (WillAIStrikeFirst() && AI_DATA->hpPercents[battlerAtk] < 40) score--; // don't want to move last break; case EFFECT_FLAIL: if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER // Opponent should go first - || GetHealthPercentage(battlerAtk) > 50) + || AI_DATA->hpPercents[battlerAtk] > 50) score -= 4; break; //TODO @@ -2467,7 +2464,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; if (WillFaintFromWeather(battlerAtk) || MoveBlockedBySubstitute(move, battlerAtk, battlerDef) - || GetSpeciesWeight(gBattleMons[battlerDef].species, AI_DATA->defAbility, AI_DATA->defHoldEffect, battlerDef, TRUE) >= 2000) //200.0 kg + || GetSpeciesWeight(gBattleMons[battlerDef].species, AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerDef], battlerDef, TRUE) >= 2000) //200.0 kg score -= 10; break; */ @@ -2516,7 +2513,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (GetMoveDamageResult(move) == MOVE_POWER_OTHER) score--; - switch (AI_GetMoveEffectiveness(move, battlerAtk, battlerDef)) + switch (AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]) { case AI_EFFECTIVENESS_x4: if (WEATHER_HAS_EFFECT @@ -2569,13 +2566,13 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) u16 effect = gBattleMoves[move].effect; u16 target = gBattleMoves[move].target; // ally data - u8 battlerAtkPartner = AI_DATA->battlerAtkPartner; - u16 atkPartnerAbility = AI_DATA->atkPartnerAbility; - u16 atkPartnerHoldEffect = AI_DATA->atkPartnerHoldEffect; + u8 battlerAtkPartner = BATTLE_PARTNER(battlerAtk); + u16 atkPartnerAbility = AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)]; + u16 atkPartnerHoldEffect = AI_DATA->holdEffects[BATTLE_PARTNER(battlerAtk)]; bool32 partnerProtecting = (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_PROTECT); - bool32 attackerHasBadAbility = (GetAbilityRating(AI_DATA->atkAbility) < 0); + bool32 attackerHasBadAbility = (GetAbilityRating(AI_DATA->abilities[battlerAtk]) < 0); bool32 partnerHasBadAbility = (GetAbilityRating(atkPartnerAbility) < 0); - u16 predictedMove = gLastMoves[battlerDef]; //for now + u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); @@ -2598,7 +2595,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_ALWAYS_CRIT: // Ally decided to use Frost Breath on us. we must have Anger Point as our ability - if (AI_DATA->atkAbility == ABILITY_ANGER_POINT) + if (AI_DATA->abilities[battlerAtk] == ABILITY_ANGER_POINT) { if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER) // Partner moving first { @@ -2674,7 +2671,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (GetMoveDamageResult(move) == MOVE_POWER_OTHER) { // partner ability checks - if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) + if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) { switch (atkPartnerAbility) { @@ -2822,14 +2819,14 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SKILL_SWAP: - if (AI_DATA->atkAbility != AI_DATA->atkPartnerAbility && !attackerHasBadAbility) + if (AI_DATA->abilities[battlerAtk] != AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] && !attackerHasBadAbility) { - if (AI_DATA->atkPartnerAbility == ABILITY_TRUANT) + if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_TRUANT) { RETURN_SCORE_PLUS(10); } - else if (AI_DATA->atkAbility == ABILITY_COMPOUND_EYES - && HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, AI_GetAbility(FOE(battlerAtkPartner)), atkPartnerHoldEffect, AI_GetHoldEffect(FOE(battlerAtkPartner)))) + else if (AI_DATA->abilities[battlerAtk] == ABILITY_COMPOUND_EYES + && HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, AI_DATA->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, AI_DATA->holdEffects[FOE(battlerAtkPartner)])) { RETURN_SCORE_PLUS(3); } @@ -2850,7 +2847,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_ENTRAINMENT: - if (partnerHasBadAbility && IsAbilityOfRating(AI_DATA->atkAbility, 0)) + if (partnerHasBadAbility && IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 0)) { RETURN_SCORE_PLUS(1); } @@ -2900,9 +2897,9 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) switch (effect) { case EFFECT_SKILL_SWAP: - if (AI_DATA->atkAbility == ABILITY_TRUANT) + if (AI_DATA->abilities[battlerAtk] == ABILITY_TRUANT) score += 5; - else if (IsAbilityOfRating(AI_DATA->atkAbility, 0) || IsAbilityOfRating(AI_DATA->defAbility, 10)) + else if (IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 0) || IsAbilityOfRating(AI_DATA->abilities[battlerDef], 10)) score += 2; // we want to transfer our bad ability or take their awesome ability break; case EFFECT_EARTHQUAKE: @@ -2933,13 +2930,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { // move data u16 moveEffect = gBattleMoves[move].effect; - u8 effectiveness = AI_GetMoveEffectiveness(move, battlerAtk, battlerDef); + u8 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; u8 atkPriority = GetMovePriority(battlerAtk, move); - u16 predictedMove = gLastMoves[battlerDef]; //for now + u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); u32 i; - u8 atkHpPercent = GetHealthPercentage(battlerAtk); - u8 defHpPercent = GetHealthPercentage(battlerDef); // Targeting partner, check benefits of doing that instead if (IsTargetingPartner(battlerAtk, battlerDef)) @@ -2959,7 +2954,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; // check already dead - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility) + if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) && CanTargetFaintAi(battlerAtk, battlerDef) && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent should go first { @@ -2984,7 +2979,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // check burn if (gBattleMons[battlerAtk].status1 & STATUS1_BURN) { - switch (AI_DATA->atkAbility) + switch (AI_DATA->abilities[battlerAtk]) { case ABILITY_GUTS: break; @@ -3001,7 +2996,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // attacker ability checks - switch (AI_DATA->atkAbility) + switch (AI_DATA->abilities[battlerAtk]) { case ABILITY_MOXIE: case ABILITY_BEAST_BOOST: @@ -3028,7 +3023,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) IncreaseSleepScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_ABSORB: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_BIG_ROOT) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score++; if (effectiveness <= AI_EFFECTIVENESS_x0_5 && AI_RandLessThan(50)) score -= 3; @@ -3037,7 +3032,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_MEMENTO: if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7) { - if (atkHpPercent < 50 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128)) score++; } break; @@ -3055,7 +3050,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < 9) { - if (atkHpPercent > 90 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) { score += 2; break; @@ -3072,11 +3067,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_DEFENSE_UP_3: if (!HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) score -= 2; - if (atkHpPercent > 90 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) score += 2; - else if (atkHpPercent > 70 && AI_RandLessThan(200)) + else if (AI_DATA->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) break; - else if (atkHpPercent < 40) + else if (AI_DATA->hpPercents[battlerAtk] < 40) score -= 2; break; case EFFECT_SPEED_UP: @@ -3101,7 +3096,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < 9) { - if (atkHpPercent > 90 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) { score += 2; break; @@ -3117,29 +3112,29 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SPECIAL_DEFENSE_UP_2: if (!HasMoveWithSplit(battlerDef, SPLIT_SPECIAL)) score -= 2; - if (atkHpPercent > 90 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) score += 2; - else if (GetHealthPercentage(battlerAtk) > 70 && AI_RandLessThan(200)) + else if (AI_DATA->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) break; - else if (GetHealthPercentage(battlerAtk) < 40) + else if (AI_DATA->hpPercents[battlerAtk] < 40) score -= 2; break; case EFFECT_ACCURACY_UP: case EFFECT_ACCURACY_UP_2: if (gBattleMons[battlerAtk].statStages[STAT_ACC] >= 9 && !AI_RandLessThan(50)) score -= 2; - else if (atkHpPercent <= 70) + else if (AI_DATA->hpPercents[battlerAtk] <= 70) score -= 2; else score++; break; case EFFECT_EVASION_UP: case EFFECT_EVASION_UP_2: - if (atkHpPercent > 90 && !AI_RandLessThan(100)) + if (AI_DATA->hpPercents[battlerAtk] > 90 && !AI_RandLessThan(100)) score += 3; if (gBattleMons[battlerAtk].statStages[STAT_EVASION] > 9 && AI_RandLessThan(128)) score--; - if ((gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) && atkHpPercent >= 50 && !AI_RandLessThan(80)) + if ((gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) && AI_DATA->hpPercents[battlerAtk] >= 50 && !AI_RandLessThan(80)) score += 3; if (gStatuses3[battlerDef] & STATUS3_LEECHSEED && !AI_RandLessThan(70)) score += 3; @@ -3147,9 +3142,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; if (gBattleMons[battlerDef].status2 & STATUS2_CURSED && !AI_RandLessThan(70)) score += 3; - if (atkHpPercent < 70 || gBattleMons[battlerAtk].statStages[STAT_EVASION] == DEFAULT_STAT_STAGE) + if (AI_DATA->hpPercents[battlerAtk] < 70 || gBattleMons[battlerAtk].statStages[STAT_EVASION] == DEFAULT_STAT_STAGE) break; - else if (atkHpPercent < 40 || defHpPercent < 40) + else if (AI_DATA->hpPercents[battlerAtk] < 40 || AI_DATA->hpPercents[battlerDef] < 40) score -= 2; else if (!AI_RandLessThan(70)) score -= 2; @@ -3157,24 +3152,24 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // stat lowering effects case EFFECT_ATTACK_DOWN: case EFFECT_ATTACK_DOWN_2: - if (!ShouldLowerAttack(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (!ShouldLowerAttack(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; if (gBattleMons[battlerDef].statStages[STAT_ATK] < DEFAULT_STAT_STAGE) score--; - else if (atkHpPercent <= 90) + else if (AI_DATA->hpPercents[battlerAtk] <= 90) score--; if (gBattleMons[battlerDef].statStages[STAT_ATK] > 3 && !AI_RandLessThan(50)) score -= 2; - else if (defHpPercent < 70) + else if (AI_DATA->hpPercents[battlerDef] < 70) score -= 2; break; case EFFECT_DEFENSE_DOWN: case EFFECT_DEFENSE_DOWN_2: - if (!ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (!ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; - if ((atkHpPercent < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_DEF] <= 3 && !AI_RandLessThan(50))) + if ((AI_DATA->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_DEF] <= 3 && !AI_RandLessThan(50))) score -= 2; - if (defHpPercent <= 70) + if (AI_DATA->hpPercents[battlerDef] <= 70) score -= 2; break; case EFFECT_SPEED_DOWN: @@ -3186,32 +3181,32 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_SPECIAL_ATTACK_DOWN: case EFFECT_SPECIAL_ATTACK_DOWN_2: - if (!ShouldLowerSpAtk(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (!ShouldLowerSpAtk(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; if (gBattleMons[battlerDef].statStages[STAT_SPATK] < DEFAULT_STAT_STAGE) score--; - else if (atkHpPercent <= 90) + else if (AI_DATA->hpPercents[battlerAtk] <= 90) score--; if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 3 && !AI_RandLessThan(50)) score -= 2; - else if (defHpPercent < 70) + else if (AI_DATA->hpPercents[battlerDef] < 70) score -= 2; break; case EFFECT_SPECIAL_DEFENSE_DOWN: case EFFECT_SPECIAL_DEFENSE_DOWN_2: - if (!ShouldLowerSpDef(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (!ShouldLowerSpDef(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; - if ((atkHpPercent < 70 && !AI_RandLessThan(50)) + if ((AI_DATA->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_SPDEF] <= 3 && !AI_RandLessThan(50))) score -= 2; - if (defHpPercent <= 70) + if (AI_DATA->hpPercents[battlerDef] <= 70) score -= 2; break; case EFFECT_ACCURACY_DOWN: case EFFECT_ACCURACY_DOWN_2: - if (ShouldLowerAccuracy(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (ShouldLowerAccuracy(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; - if ((atkHpPercent < 70 || defHpPercent < 70) && AI_RandLessThan(100)) + if ((AI_DATA->hpPercents[battlerAtk] < 70 || AI_DATA->hpPercents[battlerDef] < 70) && AI_RandLessThan(100)) score--; if (gBattleMons[battlerDef].statStages[STAT_ACC] <= 4 && !AI_RandLessThan(80)) score -= 2; @@ -3223,26 +3218,26 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; if (gBattleMons[battlerDef].status2 & STATUS2_CURSED && !AI_RandLessThan(70)) score += 2; - if (atkHpPercent > 70 || gBattleMons[battlerDef].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) + if (AI_DATA->hpPercents[battlerAtk] > 70 || gBattleMons[battlerDef].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) break; - else if (atkHpPercent < 40 || defHpPercent < 40 || !AI_RandLessThan(70)) + else if (AI_DATA->hpPercents[battlerAtk] < 40 || AI_DATA->hpPercents[battlerDef] < 40 || !AI_RandLessThan(70)) score -= 2; break; case EFFECT_EVASION_DOWN: case EFFECT_EVASION_DOWN_2: - if (!ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (!ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) score -= 2; - if ((atkHpPercent < 70 || gBattleMons[battlerDef].statStages[STAT_EVASION] <= 3) && !AI_RandLessThan(50)) + if ((AI_DATA->hpPercents[battlerAtk] < 70 || gBattleMons[battlerDef].statStages[STAT_EVASION] <= 3) && !AI_RandLessThan(50)) score -= 2; - if (defHpPercent <= 70) + if (AI_DATA->hpPercents[battlerDef] <= 70) score -= 2; if (gBattleMons[battlerAtk].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) score++; - if (gBattleMons[battlerDef].statStages[STAT_EVASION] < 7 || AI_DATA->atkAbility == ABILITY_NO_GUARD) + if (gBattleMons[battlerDef].statStages[STAT_EVASION] < 7 || AI_DATA->abilities[battlerAtk] == ABILITY_NO_GUARD) score -= 2; break; case EFFECT_BIDE: - if (atkHpPercent < 90) + if (AI_DATA->hpPercents[battlerAtk] < 90) score -= 2; break; case EFFECT_DREAM_EATER: @@ -3258,7 +3253,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_GROWTH: case EFFECT_ATTACK_SPATK_UP: // work up - if (GetHealthPercentage(battlerAtk) <= 40 || AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->hpPercents[battlerAtk] <= 40 || AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) break; if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) @@ -3267,24 +3262,24 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score); break; case EFFECT_HAZE: - if (AnyStatIsRaised(AI_DATA->battlerAtkPartner) - || PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (AnyStatIsRaised(BATTLE_PARTNER(battlerAtk)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) score -= 3; break; // fallthrough case EFFECT_ROAR: case EFFECT_CLEAR_SMOG: if (isDoubleBattle) - score += min(CountPositiveStatStages(battlerDef) + CountPositiveStatStages(AI_DATA->battlerDefPartner), 7); + score += min(CountPositiveStatStages(battlerDef) + CountPositiveStatStages(BATTLE_PARTNER(battlerDef)), 7); else score += min(CountPositiveStatStages(battlerDef), 4); break; case EFFECT_MULTI_HIT: case EFFECT_DOUBLE_HIT: case EFFECT_TRIPLE_KICK: - if (AI_MoveMakesContact(AI_DATA->atkAbility, AI_DATA->atkHoldEffect, move) - && AI_DATA->atkAbility != ABILITY_MAGIC_GUARD - && AI_DATA->defHoldEffect == HOLD_EFFECT_ROCKY_HELMET) + if (AI_MoveMakesContact(AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk], move) + && AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD + && AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_ROCKY_HELMET) score -= 2; break; case EFFECT_CONVERSION: @@ -3292,7 +3287,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; break; case EFFECT_FLINCH_HIT: - score += ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, move); + score += ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move); break; case EFFECT_SWALLOW: if (gDisableStructs[battlerAtk].stockpileCounter == 0) @@ -3329,7 +3324,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_MOONLIGHT: if (ShouldRecover(battlerAtk, battlerDef, move, 50)) score += 3; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_BIG_ROOT) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score++; break; case EFFECT_TOXIC: @@ -3342,26 +3337,26 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (ShouldSetScreen(battlerAtk, battlerDef, moveEffect)) { score += 5; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_LIGHT_CLAY) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) score += 2; if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SCREENER) score += 2; } break; case EFFECT_REST: - if (!(AI_CanSleep(battlerAtk, AI_DATA->atkAbility))) + if (!(AI_CanSleep(battlerAtk, AI_DATA->abilities[battlerAtk]))) { break; } else if (ShouldRecover(battlerAtk, battlerDef, move, 100)) { - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_CURE_SLP - || AI_DATA->atkHoldEffect == HOLD_EFFECT_CURE_STATUS + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_SLP + || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_STATUS || HasMoveEffect(EFFECT_SLEEP_TALK, battlerAtk) || HasMoveEffect(EFFECT_SNORE, battlerAtk) - || AI_DATA->atkAbility == ABILITY_SHED_SKIN - || AI_DATA->atkAbility == ABILITY_EARLY_BIRD - || (gBattleWeather & B_WEATHER_RAIN && gWishFutureKnock.weatherDuration != 1 && AI_DATA->atkAbility == ABILITY_HYDRATION && AI_DATA->atkHoldEffect != HOLD_EFFECT_UTILITY_UMBRELLA)) + || AI_DATA->abilities[battlerAtk] == ABILITY_SHED_SKIN + || AI_DATA->abilities[battlerAtk] == ABILITY_EARLY_BIRD + || (gBattleWeather & B_WEATHER_RAIN && gWishFutureKnock.weatherDuration != 1 && AI_DATA->abilities[battlerAtk] == ABILITY_HYDRATION && AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) { score += 2; } @@ -3394,14 +3389,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_FOCUS_ENERGY: case EFFECT_LASER_FOCUS: - if (AI_DATA->atkAbility == ABILITY_SUPER_LUCK - || AI_DATA->atkAbility == ABILITY_SNIPER - || AI_DATA->atkHoldEffect == HOLD_EFFECT_SCOPE_LENS + if (AI_DATA->abilities[battlerAtk] == ABILITY_SUPER_LUCK + || AI_DATA->abilities[battlerAtk] == ABILITY_SNIPER + || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_SCOPE_LENS || TestMoveFlagsInMoveset(battlerAtk, FLAG_HIGH_CRIT)) score += 2; break; case EFFECT_CONFUSE_HIT: - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE) score++; //fallthrough case EFFECT_CONFUSE: @@ -3420,7 +3415,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SPECIAL_DEFENSE_DOWN_HIT: case EFFECT_ACCURACY_DOWN_HIT: case EFFECT_EVASION_DOWN_HIT: - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY) score += 2; break; case EFFECT_SPEED_DOWN_HIT: @@ -3428,12 +3423,12 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 2; else if (!AI_RandLessThan(70)) score++; - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY) score++; break; - if (ShouldLowerSpeed(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (ShouldLowerSpeed(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) { - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY) score += 4; else score += 2; @@ -3452,7 +3447,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || HasMoveEffect(battlerDef, EFFECT_CONFUSE) || HasMoveEffect(battlerDef, EFFECT_LEECH_SEED)) score += 2; - if (!gBattleMons[battlerDef].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION && GetHealthPercentage(battlerAtk) > 70)) + if (!gBattleMons[battlerDef].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION && AI_DATA->hpPercents[battlerAtk] > 70)) score++; break; case EFFECT_MIMIC: @@ -3466,8 +3461,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || gStatuses3[battlerDef] & STATUS3_LEECHSEED || HasMoveEffect(battlerDef, EFFECT_RAPID_SPIN) - || AI_DATA->defAbility == ABILITY_LIQUID_OOZE - || AI_DATA->defAbility == ABILITY_MAGIC_GUARD) + || AI_DATA->abilities[battlerDef] == ABILITY_LIQUID_OOZE + || AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; score += 3; if (!HasDamagingMove(battlerDef) || IsBattlerTrapped(battlerDef, FALSE)) @@ -3484,7 +3479,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_PARTING_SHOT: if (!IsDoubleBattle()) { - switch (ShouldPivot(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_THINKING_STRUCT->movesetIndex)) + switch (ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex)) { case 0: // no score -= 10; // technically should go in CheckBadMove, but this is easier/less computationally demanding @@ -3513,7 +3508,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)) // mental herb { if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // AI goes first { @@ -3535,7 +3530,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)) // mental herb { if (IsEncoreEncouragedEffect(gBattleMoves[gLastMoves[battlerDef]].effect)) score += 3; @@ -3545,7 +3540,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { u16 newHp = (gBattleMons[battlerAtk].hp + gBattleMons[battlerDef].hp) / 2; u16 healthBenchmark = (gBattleMons[battlerAtk].hp * 12) / 10; - if (newHp > healthBenchmark && ShouldAbsorb(battlerAtk, battlerDef, move, AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])) + if (newHp > healthBenchmark && ShouldAbsorb(battlerAtk, battlerDef, move, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])) score += 2; } break; @@ -3557,15 +3552,15 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_LOCK_ON: if (HasMoveEffect(battlerAtk, EFFECT_OHKO)) score += 3; - else if (AI_DATA->atkAbility == ABILITY_COMPOUND_EYES && HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + else if (AI_DATA->abilities[battlerAtk] == ABILITY_COMPOUND_EYES && HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) score += 3; - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) score += 3; - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) score++; break; case EFFECT_SPEED_UP_HIT: - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY && !WillAIStrikeFirst()) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY && !WillAIStrikeFirst()) score += 3; break; case EFFECT_DESTINY_BOND: @@ -3590,14 +3585,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER) canSteal = TRUE; - if (canSteal && AI_DATA->atkItem == ITEM_NONE - && AI_DATA->defItem != ITEM_NONE - && CanBattlerGetOrLoseItem(battlerDef, AI_DATA->defItem) - && CanBattlerGetOrLoseItem(battlerAtk, AI_DATA->defItem) + if (canSteal && AI_DATA->items[battlerAtk] == ITEM_NONE + && AI_DATA->items[battlerDef] != ITEM_NONE + && CanBattlerGetOrLoseItem(battlerDef, AI_DATA->items[battlerDef]) + && CanBattlerGetOrLoseItem(battlerAtk, AI_DATA->items[battlerDef]) && !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS) - && AI_DATA->defAbility != ABILITY_STICKY_HOLD) + && AI_DATA->abilities[battlerDef] != ABILITY_STICKY_HOLD) { - switch (AI_DATA->defHoldEffect) + switch (AI_DATA->holdEffects[battlerDef]) { case HOLD_EFFECT_NONE: break; @@ -3607,11 +3602,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; break; case HOLD_EFFECT_TOXIC_ORB: - if (ShouldPoisonSelf(battlerAtk, AI_DATA->atkAbility)) + if (ShouldPoisonSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (ShouldBurnSelf(battlerAtk, AI_DATA->atkAbility)) + if (ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: @@ -3634,9 +3629,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_NIGHTMARE: - if (AI_DATA->defAbility != ABILITY_MAGIC_GUARD + if (AI_DATA->abilities[battlerDef] != ABILITY_MAGIC_GUARD && !(gBattleMons[battlerDef].status2 & STATUS2_NIGHTMARE) - && (AI_DATA->defAbility == ABILITY_COMATOSE || gBattleMons[battlerDef].status1 & STATUS1_SLEEP)) + && (AI_DATA->abilities[battlerDef] == ABILITY_COMATOSE || gBattleMons[battlerDef].status1 & STATUS1_SLEEP)) { score += 5; if (IsBattlerTrapped(battlerDef, TRUE)) @@ -3654,7 +3649,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (AI_DATA->atkAbility == ABILITY_CONTRARY || AI_DATA->defAbility == ABILITY_MAGIC_GUARD) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY || AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < 8) score += (8 - gBattleMons[battlerAtk].statStages[STAT_ATK]); @@ -3680,7 +3675,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (isDoubleBattle && gBattleMoves[AI_DATA->partnerMove].target & MOVE_TARGET_FOES_AND_ALLY) { - if (AI_DATA->atkAbility != ABILITY_TELEPATHY) + if (AI_DATA->abilities[battlerAtk] != ABILITY_TELEPATHY) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); } break; @@ -3696,9 +3691,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case MOVE_KINGS_SHIELD: #if (defined SPECIES_AEGISLASH && defined SPECIES_AEGISLASH_BLADE) - if (AI_DATA->atkAbility == ABILITY_STANCE_CHANGE //Special logic for Aegislash - && AI_DATA->atkSpecies == SPECIES_AEGISLASH_BLADE - && !IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility)) + if (AI_DATA->abilities[battlerAtk] == ABILITY_STANCE_CHANGE //Special logic for Aegislash + && gBattleMons[battlerAtk].species == SPECIES_AEGISLASH_BLADE + && !IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])) { score += 3; break; @@ -3714,7 +3709,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (CanTargetFaintAi(battlerDef, battlerAtk)) { if (gBattleMons[battlerAtk].hp > gBattleMons[battlerAtk].maxHP / 4 // Pinch berry couldn't have activated yet - && IsPinchBerryItemEffect(AI_DATA->atkHoldEffect)) + && IsPinchBerryItemEffect(AI_DATA->holdEffects[battlerAtk])) { score += 3; } @@ -3730,14 +3725,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_STEALTH_ROCK: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: - if (AI_DATA->defAbility == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) + if (AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) break; if (gDisableStructs[battlerAtk].isFirstTurn) score += 2; //TODO - track entire opponent party data to determine hazard effectiveness break; case EFFECT_FORESIGHT: - if (AI_DATA->atkAbility == ABILITY_SCRAPPY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SCRAPPY) break; else if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE || (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) @@ -3755,10 +3750,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case EFFECT_SANDSTORM: - if (ShouldSetSandstorm(battlerAtk, AI_DATA->atkHoldEffect, AI_DATA->atkHoldEffect)) + if (ShouldSetSandstorm(battlerAtk, AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { score++; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_SMOOTH_ROCK) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -3767,14 +3762,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_HAIL: - if (ShouldSetHail(battlerAtk, AI_DATA->atkAbility, AI_DATA->atkHoldEffect)) + if (ShouldSetHail(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { - if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_AURORA_VEIL)) + if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL)) && ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL)) score += 3; score++; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_ICY_ROCK) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -3783,38 +3778,38 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_RAIN_DANCE: - if (ShouldSetRain(battlerAtk, AI_DATA->atkAbility, AI_DATA->atkHoldEffect)) + if (ShouldSetRain(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { score++; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_DAMP_ROCK) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_DAMP_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) || HasMoveEffect(battlerDef, EFFECT_MOONLIGHT)) score += 2; - if (HasMoveWithType(battlerDef, TYPE_FIRE) || HasMoveWithType(AI_DATA->battlerDefPartner, TYPE_FIRE)) + if (HasMoveWithType(battlerDef, TYPE_FIRE) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_FIRE)) score++; } break; case EFFECT_SUNNY_DAY: - if (ShouldSetSun(battlerAtk, AI_DATA->atkAbility, AI_DATA->atkHoldEffect)) + if (ShouldSetSun(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) { score++; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_HEAT_ROCK) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_HEAT_ROCK) score++; - if (HasMoveWithType(battlerDef, TYPE_WATER) || HasMoveWithType(AI_DATA->battlerDefPartner, TYPE_WATER)) + if (HasMoveWithType(battlerDef, TYPE_WATER) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_WATER)) score++; - if (HasMoveEffect(battlerDef, EFFECT_THUNDER) || HasMoveEffect(AI_DATA->battlerDefPartner, EFFECT_THUNDER)) + if (HasMoveEffect(battlerDef, EFFECT_THUNDER) || HasMoveEffect(BATTLE_PARTNER(battlerDef), EFFECT_THUNDER)) score++; } break; case EFFECT_ATTACK_UP_HIT: - if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE) + if (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score); break; case EFFECT_FELL_STINGER: if (gBattleMons[battlerAtk].statStages[STAT_ATK] < MAX_STAT_STAGE - && AI_DATA->atkAbility != ABILITY_CONTRARY + && AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY && CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first @@ -3824,7 +3819,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_BELLY_DRUM: - if (!CanTargetFaintAi(battlerDef, battlerAtk) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && AI_DATA->atkAbility != ABILITY_CONTRARY) + if (!CanTargetFaintAi(battlerDef, battlerAtk) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY) score += (MAX_STAT_STAGE - gBattleMons[battlerAtk].statStages[STAT_ATK]); break; case EFFECT_PSYCH_UP: @@ -3885,7 +3880,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 8; break; case EFFECT_STOCKPILE: - if (AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) break; if (HasMoveEffect(battlerAtk, EFFECT_SWALLOW) || HasMoveEffect(battlerAtk, EFFECT_SPIT_UP)) @@ -3908,7 +3903,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF)) score++; - if (AI_DATA->defAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) score += 2; IncreaseConfusionScore(battlerAtk, battlerDef, move, &score); @@ -3918,17 +3913,17 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF)) score += 2; - if (AI_DATA->defAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) score += 2; IncreaseConfusionScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_FURY_CUTTER: - if (!isDoubleBattle && AI_DATA->atkHoldEffect == HOLD_EFFECT_METRONOME) + if (!isDoubleBattle && AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_METRONOME) score += 3; break; case EFFECT_ATTRACT: - if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->defAbility) + if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef]) && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Target goes first break; // Don't use if the attract won't have a change to activate @@ -3974,15 +3969,15 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // Partner is going to set up hazards - && AI_WhoStrikesFirst(battlerAtk, AI_DATA->battlerAtkPartner) == AI_IS_SLOWER) // Partner going first + && AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk)) == AI_IS_SLOWER) // Partner going first break; // Don't use Defog if partner is going to set up hazards } // check defog lowering evasion - if (ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->defAbility)) + if (ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) { if (gBattleMons[battlerDef].statStages[STAT_EVASION] > 7 - || HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + || HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) score += 2; // encourage lowering evasion if they are evasive or we have a move with low accuracy else score++; @@ -4003,11 +3998,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FOLLOW_ME: if (isDoubleBattle && move != MOVE_SPOTLIGHT - && !IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility) - && (move != MOVE_RAGE_POWDER || IsAffectedByPowder(battlerDef, AI_DATA->defAbility, AI_DATA->defHoldEffect)) // Rage Powder doesn't affect powder immunities - && IsBattlerAlive(AI_DATA->battlerAtkPartner)) + && !IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) + && (move != MOVE_RAGE_POWDER || IsAffectedByPowder(battlerDef, AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerDef])) // Rage Powder doesn't affect powder immunities + && IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) { - u16 predictedMoveOnPartner = gLastMoves[AI_DATA->battlerAtkPartner]; + u16 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)]; if (predictedMoveOnPartner != MOVE_NONE && !IS_MOVE_STATUS(predictedMoveOnPartner)) score += 3; } @@ -4028,7 +4023,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TRICK: case EFFECT_BESTOW: - switch (AI_DATA->atkHoldEffect) + switch (AI_DATA->holdEffects[battlerAtk]) { case HOLD_EFFECT_CHOICE_SCARF: score += 2; // assume its beneficial @@ -4042,15 +4037,15 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; break; case HOLD_EFFECT_TOXIC_ORB: - if (!ShouldPoisonSelf(battlerAtk, AI_DATA->atkAbility)) + if (!ShouldPoisonSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, AI_DATA->atkAbility) && AI_CanBeBurned(battlerAtk, AI_DATA->defAbility)) + if (!ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, AI_DATA->abilities[battlerDef])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: - if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) && AI_DATA->defAbility != ABILITY_MAGIC_GUARD) + if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) && AI_DATA->abilities[battlerDef] != ABILITY_MAGIC_GUARD) score += 3; break; case HOLD_EFFECT_IRON_BALL: @@ -4062,9 +4057,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case HOLD_EFFECT_UTILITY_UMBRELLA: - if (AI_DATA->atkAbility != ABILITY_SOLAR_POWER && AI_DATA->atkAbility != ABILITY_DRY_SKIN && AI_WeatherHasEffect()) + if (AI_DATA->abilities[battlerAtk] != ABILITY_SOLAR_POWER && AI_DATA->abilities[battlerAtk] != ABILITY_DRY_SKIN && AI_WeatherHasEffect()) { - switch (AI_DATA->defAbility) + switch (AI_DATA->abilities[battlerDef]) { case ABILITY_SWIFT_SWIM: if (gBattleWeather & B_WEATHER_RAIN) @@ -4081,26 +4076,26 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case HOLD_EFFECT_EJECT_BUTTON: //if (!IsRaidBattle() && IsDynamaxed(battlerDef) && gNewBS->dynamaxData.timer[battlerDef] > 1 && if (HasDamagingMove(battlerAtk) - || (isDoubleBattle && IsBattlerAlive(AI_DATA->battlerAtkPartner) && HasDamagingMove(AI_DATA->battlerAtkPartner))) + || (isDoubleBattle && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && HasDamagingMove(BATTLE_PARTNER(battlerAtk)))) score += 2; // Force 'em out next turn break; default: - if (move != MOVE_BESTOW && AI_DATA->atkItem == ITEM_NONE) + if (move != MOVE_BESTOW && AI_DATA->items[battlerAtk] == ITEM_NONE) { - switch (AI_DATA->defHoldEffect) + switch (AI_DATA->holdEffects[battlerDef]) { case HOLD_EFFECT_CHOICE_BAND: break; case HOLD_EFFECT_TOXIC_ORB: - if (ShouldPoisonSelf(battlerAtk, AI_DATA->atkAbility)) + if (ShouldPoisonSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (ShouldBurnSelf(battlerAtk, AI_DATA->atkAbility)) + if (ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: - if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON) || AI_DATA->atkAbility == ABILITY_MAGIC_GUARD) + if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON) || AI_DATA->abilities[battlerAtk] == ABILITY_MAGIC_GUARD) score += 3; break; case HOLD_EFFECT_IRON_BALL: @@ -4118,21 +4113,21 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_ROLE_PLAY: - if (!IsRolePlayBannedAbilityAtk(AI_DATA->atkAbility) - && !IsRolePlayBannedAbility(AI_DATA->defAbility) - && !IsAbilityOfRating(AI_DATA->atkAbility, 5) - && IsAbilityOfRating(AI_DATA->defAbility, 5)) + if (!IsRolePlayBannedAbilityAtk(AI_DATA->abilities[battlerAtk]) + && !IsRolePlayBannedAbility(AI_DATA->abilities[battlerDef]) + && !IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 5) + && IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5)) score += 2; break; case EFFECT_INGRAIN: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_BIG_ROOT) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score += 3; else score++; break; case EFFECT_SUPERPOWER: case EFFECT_OVERHEAT: - if (AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) score += 10; break; case EFFECT_MAGIC_COAT: @@ -4144,12 +4139,12 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; if (IsRecycleEncouragedItem(GetUsedHeldItem(battlerAtk))) score++; - if (AI_DATA->atkAbility == ABILITY_RIPEN) + if (AI_DATA->abilities[battlerAtk] == ABILITY_RIPEN) { u16 item = GetUsedHeldItem(battlerAtk); u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item); - if (IsStatBoostingBerry(item) && atkHpPercent > 60) + if (IsStatBoostingBerry(item) && AI_DATA->hpPercents[battlerAtk] > 60) score++; else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0) && ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0)) @@ -4166,9 +4161,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; break; case EFFECT_KNOCK_OFF: - if (CanKnockOffItem(battlerDef, AI_DATA->defItem)) + if (CanKnockOffItem(battlerDef, AI_DATA->items[battlerDef])) { - switch (AI_DATA->defHoldEffect) + switch (AI_DATA->holdEffects[battlerDef]) { case HOLD_EFFECT_IRON_BALL: if (HasMoveEffect(battlerDef, EFFECT_FLING)) @@ -4184,19 +4179,19 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SKILL_SWAP: - if (GetAbilityRating(AI_DATA->defAbility) > GetAbilityRating(AI_DATA->atkAbility)) + if (GetAbilityRating(AI_DATA->abilities[battlerDef]) > GetAbilityRating(AI_DATA->abilities[battlerAtk])) score++; break; case EFFECT_WORRY_SEED: case EFFECT_GASTRO_ACID: case EFFECT_SIMPLE_BEAM: - if (IsAbilityOfRating(AI_DATA->defAbility, 5)) + if (IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5)) score += 2; break; case EFFECT_ENTRAINMENT: - if (IsAbilityOfRating(AI_DATA->defAbility, 5) || GetAbilityRating(AI_DATA->atkAbility) <= 0) + if (IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5) || GetAbilityRating(AI_DATA->abilities[battlerAtk]) <= 0) { - if (AI_DATA->defAbility != AI_DATA->atkAbility && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)) + if (AI_DATA->abilities[battlerDef] != AI_DATA->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)) score += 2; } break; @@ -4236,11 +4231,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TICKLE: if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4 && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) - && AI_DATA->defAbility != ABILITY_CONTRARY && ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->defAbility)) + && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY && ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) { score += 2; } - else if (ShouldLowerAttack(battlerAtk, battlerDef, AI_DATA->defAbility)) + else if (ShouldLowerAttack(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) { score += 2; } @@ -4258,7 +4253,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score); break; case EFFECT_GEOMANCY: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_POWER_HERB) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) score += 3; //fallthrough case EFFECT_QUIVER_DANCE: @@ -4267,7 +4262,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score); break; case EFFECT_SHELL_SMASH: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_POWER_HERB) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) score += 3; IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score); @@ -4345,15 +4340,15 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_BUG_BITE: // And pluck - if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || AI_DATA->defAbility == ABILITY_STICKY_HOLD) + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || AI_DATA->abilities[battlerDef] == ABILITY_STICKY_HOLD) break; - else if (ItemId_GetPocket(AI_DATA->defItem) == POCKET_BERRIES) + else if (ItemId_GetPocket(AI_DATA->items[battlerDef]) == POCKET_BERRIES) score += 3; break; case EFFECT_INCINERATE: - if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || AI_DATA->defAbility == ABILITY_STICKY_HOLD) + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || AI_DATA->abilities[battlerDef] == ABILITY_STICKY_HOLD) break; - else if (ItemId_GetPocket(AI_DATA->defItem) == POCKET_BERRIES || AI_DATA->defHoldEffect == HOLD_EFFECT_GEMS) + else if (ItemId_GetPocket(AI_DATA->items[battlerDef]) == POCKET_BERRIES || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_GEMS) score += 3; break; case EFFECT_SMACK_DOWN: @@ -4362,9 +4357,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_RELIC_SONG: #if (defined SPECIES_MELOETTA && defined SPECIES_MELOETTA_PIROUETTE) - if (AI_DATA->atkSpecies == SPECIES_MELOETTA && gBattleMons[battlerDef].defense < gBattleMons[battlerDef].spDefense) + if (gBattleMons[battlerAtk].species == SPECIES_MELOETTA && gBattleMons[battlerDef].defense < gBattleMons[battlerDef].spDefense) score += 3; // Change to pirouette if can do more damage - else if (AI_DATA->atkSpecies == SPECIES_MELOETTA_PIROUETTE && gBattleMons[battlerDef].spDefense < gBattleMons[battlerDef].defense) + else if (gBattleMons[battlerAtk].species == SPECIES_MELOETTA_PIROUETTE && gBattleMons[battlerDef].spDefense < gBattleMons[battlerDef].defense) score += 3; // Change to Aria if can do more damage #endif break; @@ -4376,13 +4371,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_GRASSY_TERRAIN: case EFFECT_PSYCHIC_TERRAIN: score += 2; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER) score += 2; break; case EFFECT_PLEDGE: if (isDoubleBattle) { - if (HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_PLEDGE)) + if (HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_PLEDGE)) score += 3; // Partner might use pledge move } break; @@ -4394,9 +4389,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_MAGIC_ROOM: score++; - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_NONE && AI_DATA->defHoldEffect != HOLD_EFFECT_NONE) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_NONE && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_NONE) score++; - if (isDoubleBattle && AI_DATA->atkPartnerHoldEffect == HOLD_EFFECT_NONE && AI_DATA->defPartnerHoldEffect != HOLD_EFFECT_NONE) + if (isDoubleBattle && AI_DATA->holdEffects[BATTLE_PARTNER(battlerAtk)] == HOLD_EFFECT_NONE && AI_DATA->holdEffects[BATTLE_PARTNER(battlerDef)] != HOLD_EFFECT_NONE) score++; break; case EFFECT_WONDER_ROOM: @@ -4409,28 +4404,28 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { if (HasSleepMoveWithLowAccuracy(battlerAtk, battlerDef)) // Has Gravity for a move like Hypnosis IncreaseSleepScore(battlerAtk, battlerDef, move, &score); - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) score += 2; else score++; } break; case EFFECT_ION_DELUGE: - if ((AI_DATA->atkAbility == ABILITY_VOLT_ABSORB - || AI_DATA->atkAbility == ABILITY_MOTOR_DRIVE - || AI_DATA->atkAbility == ABILITY_LIGHTNING_ROD) + if ((AI_DATA->abilities[battlerAtk] == ABILITY_VOLT_ABSORB + || AI_DATA->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE + || AI_DATA->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD) && gBattleMoves[predictedMove].type == TYPE_NORMAL) score += 2; break; case EFFECT_FLING: /* TODO - switch (gFlingTable[AI_DATA->atkItem].effect) + switch (gFlingTable[AI_DATA->items[battlerAtk]].effect) { case MOVE_EFFECT_BURN: IncreaseBurnScore(battlerAtk, battlerDef, move, &score); break; case MOVE_EFFECT_FLINCH: - score += ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, move); + score += ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move); break; case MOVE_EFFECT_PARALYSIS: IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score); @@ -4450,7 +4445,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case EFFECT_EMBARGO: - if (AI_DATA->defHoldEffect != HOLD_EFFECT_NONE) + if (AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_NONE) score++; break; case EFFECT_POWDER: @@ -4458,7 +4453,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case EFFECT_TELEKINESIS: - if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect) + if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]) || !IsBattlerGrounded(battlerDef)) score++; break; @@ -4471,8 +4466,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_HEAL_BLOCK: if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect)) score += 3; // Try to cancel healing move - else if (HasHealingEffect(battlerDef) || AI_DATA->defHoldEffect == HOLD_EFFECT_LEFTOVERS - || (AI_DATA->defHoldEffect == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON))) + else if (HasHealingEffect(battlerDef) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_LEFTOVERS + || (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON))) score += 2; break; case EFFECT_SOAK: @@ -4480,14 +4475,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; // Get some super effective moves break; case EFFECT_THIRD_TYPE: - if (AI_DATA->defAbility == ABILITY_WONDER_GUARD) + if (AI_DATA->abilities[battlerDef] == ABILITY_WONDER_GUARD) score += 2; // Give target more weaknesses break; case EFFECT_ELECTRIFY: if (predictedMove != MOVE_NONE && gBattleMoves[predictedMove].type == TYPE_NORMAL - && (AI_DATA->atkAbility == ABILITY_VOLT_ABSORB - || AI_DATA->atkAbility == ABILITY_MOTOR_DRIVE - || AI_DATA->atkAbility == ABILITY_LIGHTNING_ROD)) + && (AI_DATA->abilities[battlerAtk] == ABILITY_VOLT_ABSORB + || AI_DATA->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE + || AI_DATA->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)) { score += 3; } @@ -4505,7 +4500,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_QUASH: if (isDoubleBattle - && AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerDef) == AI_IS_SLOWER) // Attacker partner wouldn't go before target + && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef) == AI_IS_SLOWER) // Attacker partner wouldn't go before target score++; break; case EFFECT_TAILWIND: @@ -4549,10 +4544,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FLAME_BURST: if (isDoubleBattle) { - if (IsBattlerAlive(AI_DATA->battlerDefPartner) - && GetHealthPercentage(AI_DATA->battlerDefPartner) < 12 - && AI_DATA->defPartnerAbility != ABILITY_MAGIC_GUARD - && !IS_BATTLER_OF_TYPE(AI_DATA->battlerDefPartner, TYPE_FIRE)) + if (IsBattlerAlive(BATTLE_PARTNER(battlerDef)) + && AI_DATA->hpPercents[BATTLE_PARTNER(battlerDef)] < 12 + && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD + && !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE)) score++; } break; @@ -4563,11 +4558,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_TWO_TURNS_ATTACK: case EFFECT_SKULL_BASH: case EFFECT_SOLAR_BEAM: - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_POWER_HERB) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) score += 2; break; case EFFECT_COUNTER: - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility) && predictedMove != MOVE_NONE) + if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) && predictedMove != MOVE_NONE) { if (gDisableStructs[battlerDef].tauntTimer != 0) score++; // target must use damaging move @@ -4576,7 +4571,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_MIRROR_COAT: - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility) && predictedMove != MOVE_NONE) + if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) && predictedMove != MOVE_NONE) { if (gDisableStructs[battlerDef].tauntTimer != 0) score++; // target must use damaging move @@ -4587,9 +4582,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FLAIL: if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Ai goes first { - if (GetHealthPercentage(battlerAtk) < 20) + if (AI_DATA->hpPercents[battlerAtk] < 20) score++; - else if (GetHealthPercentage(battlerAtk) < 8) + else if (AI_DATA->hpPercents[battlerAtk] < 8) score += 2; } break; @@ -4607,7 +4602,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FOCUS_PUNCH: if (!isDoubleBattle && effectiveness > AI_EFFECTIVENESS_x0_5) { - if (IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility)) + if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])) score += 2; else if (gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) score++; @@ -4629,10 +4624,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ENDEAVOR: if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent faster { - if (GetHealthPercentage(battlerAtk) < 40) + if (AI_DATA->hpPercents[battlerAtk] < 40) score++; } - else if (GetHealthPercentage(battlerAtk) < 50) + else if (AI_DATA->hpPercents[battlerAtk] < 50) { score++; } @@ -4824,7 +4819,7 @@ static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { if (gBattleResults.battleTurnCounter == 0) score += 5; - else if (GetHealthPercentage(battlerAtk) < 60) + else if (AI_DATA->hpPercents[battlerAtk] < 60) score -= 10; else score++; @@ -4875,21 +4870,21 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (IsTargetingPartner(battlerAtk, battlerDef)) { if ((effect == EFFECT_HEAL_PULSE || effect == EFFECT_HIT_ENEMY_HEAL_ALLY) - || (moveType == TYPE_ELECTRIC && AI_DATA->atkPartnerAbility == ABILITY_VOLT_ABSORB) - || (moveType == TYPE_WATER && (AI_DATA->atkPartnerAbility == ABILITY_DRY_SKIN || AI_DATA->atkPartnerAbility == ABILITY_WATER_ABSORB))) + || (moveType == TYPE_ELECTRIC && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_VOLT_ABSORB) + || (moveType == TYPE_WATER && (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_DRY_SKIN || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_WATER_ABSORB))) { - if (CanTargetFaintAi(FOE(battlerAtk), AI_DATA->battlerAtkPartner) - || (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), AI_DATA->battlerAtkPartner))) + if (CanTargetFaintAi(FOE(battlerAtk), BATTLE_PARTNER(battlerAtk)) + || (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), BATTLE_PARTNER(battlerAtk)))) score--; - if (GetHealthPercentage(battlerDef) <= 50) + if (AI_DATA->hpPercents[battlerDef] <= 50) score++; } } else { // Consider AI HP - if (GetHealthPercentage(battlerAtk) > 70) + if (AI_DATA->hpPercents[battlerAtk] > 70) { // high hp switch (effect) @@ -4915,7 +4910,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; } } - else if (GetHealthPercentage(battlerAtk) > 30) + else if (AI_DATA->hpPercents[battlerAtk] > 30) { // med hp if (IsStatRaisingEffect(effect) || IsStatLoweringEffect(effect)) @@ -4984,12 +4979,12 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (GetHealthPercentage(battlerDef) > 70) + if (AI_DATA->hpPercents[battlerDef] > 70) { // high HP ; // nothing yet } - else if (GetHealthPercentage(battlerDef) > 30) + else if (AI_DATA->hpPercents[battlerDef] > 30) { // med HP - check discouraged effects switch (effect) @@ -5088,7 +5083,7 @@ static s16 AI_Safari(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // First battle logic static s16 AI_FirstBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { - if (GetHealthPercentage(battlerDef) <= 20) + if (AI_DATA->hpPercents[battlerDef] <= 20) AI_Flee(); return score; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index fb53bb8013..54b368729c 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -20,6 +20,8 @@ #include "constants/moves.h" #include "constants/items.h" +static u8 AI_GetEffectiveness(u16 multiplier); + // Const Data static const s8 sAiAbilityRatings[ABILITIES_COUNT] = { @@ -590,14 +592,14 @@ u32 GetHealthPercentage(u8 battlerId) bool32 AtMaxHp(u8 battlerId) { - if (GetHealthPercentage(battlerId) == 100) + if (AI_DATA->hpPercents[battlerId] == 100) return TRUE; return FALSE; } bool32 IsBattlerTrapped(u8 battler, bool8 checkSwitch) { - u8 holdEffect = AI_GetHoldEffect(battler); + u8 holdEffect = AI_DATA->holdEffects[battler]; if (IS_BATTLER_OF_TYPE(battler, TYPE_GHOST) || (checkSwitch && holdEffect == HOLD_EFFECT_SHED_SHELL) || (!checkSwitch && GetBattlerAbility(battler) == ABILITY_RUN_AWAY) @@ -658,7 +660,7 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split) { s32 i, moveType; u32 usable = 0; - u32 unusable = CheckMoveLimitations(attacker, 0, MOVE_LIMITATIONS_ALL); + u32 unusable = AI_DATA->moveLimitations[attacker]; u16 *moves = GetMovesArray(attacker); for (i = 0; i < MAX_MON_MOVES; i++) @@ -713,10 +715,11 @@ static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef) return isCrit; } -s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) +s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness) { s32 dmg, moveType, critDmg, normalDmg; s8 critChance; + u16 effectivenessMultiplier; SaveBattlerData(battlerAtk); SaveBattlerData(battlerDef); @@ -729,7 +732,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) GET_MOVE_TYPE(move, moveType); critChance = GetInverseCritChance(battlerAtk, battlerDef, move); - normalDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE); + normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, &effectivenessMultiplier); critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE); if(critChance == -1) @@ -742,26 +745,26 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) { case EFFECT_LEVEL_DAMAGE: case EFFECT_PSYWAVE: - dmg = gBattleMons[battlerAtk].level * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = gBattleMons[battlerAtk].level * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_DRAGON_RAGE: - dmg = 40 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = 40 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_SONICBOOM: - dmg = 20 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = 20 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_MULTI_HIT: - dmg *= (AI_DATA->atkAbility == ABILITY_SKILL_LINK ? 5 : 3); + dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 5 : 3); break; case EFFECT_TRIPLE_KICK: - dmg *= (AI_DATA->atkAbility == ABILITY_SKILL_LINK ? 6 : 5); + dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 6 : 5); break; case EFFECT_ENDEAVOR: // If target has less HP than user, Endeavor does no damage dmg = max(0, gBattleMons[battlerDef].hp - gBattleMons[battlerAtk].hp); break; case EFFECT_SUPER_FANG: - dmg = (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND + dmg = (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? max(2, gBattleMons[battlerDef].hp * 3 / 4) : max(1, gBattleMons[battlerDef].hp / 2)); break; @@ -778,6 +781,9 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) RestoreBattlerData(battlerAtk); RestoreBattlerData(battlerDef); + + // convert multiper to AI_EFFECTIVENESS_xX + *typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier); return dmg; } @@ -785,10 +791,10 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) // Checks if one of the moves has side effects or perks static u32 WhichMoveBetter(u32 move1, u32 move2) { - s32 defAbility = AI_GetAbility(gBattlerTarget); + s32 defAbility = AI_DATA->abilities[gBattlerTarget]; // Check if physical moves hurt. - if (AI_GetHoldEffect(sBattler_AI) != HOLD_EFFECT_PROTECTIVE_PADS + if (AI_DATA->holdEffects[sBattler_AI] != HOLD_EFFECT_PROTECTIVE_PADS && (BATTLE_HISTORY->itemEffects[gBattlerTarget] == HOLD_EFFECT_ROCKY_HELMET || defAbility == ABILITY_IRON_BARBS || defAbility == ABILITY_ROUGH_SKIN)) { @@ -868,7 +874,7 @@ u8 GetMoveDamageResult(u16 move) && sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END && gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power != 0) { - moveDmgs[checkedMove] = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove]; + moveDmgs[checkedMove] = AI_DATA->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove]; } else { @@ -924,7 +930,7 @@ u8 GetMoveDamageResult(u16 move) u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef) { - int bestDmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; + int bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; return (bestDmg * 100) / gBattleMons[battlerDef].maxHP; } @@ -952,37 +958,28 @@ u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) u8 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) { - u8 damageVar; - u32 effectivenessMultiplier; - gMoveResultFlags = 0; - gCurrentMove = move; - effectivenessMultiplier = AI_GetTypeEffectiveness(gCurrentMove, battlerAtk, battlerDef); + return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef)); +} - switch (effectivenessMultiplier) +static u8 AI_GetEffectiveness(u16 multiplier) +{ + switch (multiplier) { case UQ_4_12(0.0): default: - damageVar = AI_EFFECTIVENESS_x0; - break; + return AI_EFFECTIVENESS_x0; case UQ_4_12(0.25): - damageVar = AI_EFFECTIVENESS_x0_25; - break; + return AI_EFFECTIVENESS_x0_25; case UQ_4_12(0.5): - damageVar = AI_EFFECTIVENESS_x0_5; - break; + return AI_EFFECTIVENESS_x0_5; case UQ_4_12(1.0): - damageVar = AI_EFFECTIVENESS_x1; - break; + return AI_EFFECTIVENESS_x1; case UQ_4_12(2.0): - damageVar = AI_EFFECTIVENESS_x2; - break; + return AI_EFFECTIVENESS_x2; case UQ_4_12(4.0): - damageVar = AI_EFFECTIVENESS_x4; - break; + return AI_EFFECTIVENESS_x4; } - - return damageVar; } /* Checks to see if AI will move ahead of another battler @@ -1032,13 +1029,13 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2) bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk) { s32 i, dmg; - u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL); + u32 unusable = AI_DATA->moveLimitations[battlerDef]; u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef]; for (i = 0; i < MAX_MON_MOVES; i++) { if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && !(unusable & gBitTable[i]) - && AI_CalcDamage(moves[i], battlerDef, battlerAtk) >= gBattleMons[battlerAtk].hp) + && AI_DATA->simulatedDmg[battlerDef][battlerAtk][moves[i]] >= gBattleMons[battlerAtk].hp) { return TRUE; } @@ -1052,7 +1049,7 @@ bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk) bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits) { s32 i, dmg; - u32 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL); + u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; u16 *moves = gBattleMons[battlerAtk].moves; for (i = 0; i < MAX_MON_MOVES; i++) @@ -1060,7 +1057,7 @@ bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits) if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && !(moveLimitations & gBitTable[i])) { // Use the pre-calculated value in simulatedDmg instead of re-calculating it - dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i]; + dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]; if (numHits) dmg *= numHits; @@ -1076,9 +1073,13 @@ bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits) bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits) { s32 i, dmg; - u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL); + u8 effectiveness; + u32 unusable = AI_DATA->moveLimitations[battlerDef]; - if (move != MOVE_NONE && move != 0xFFFF && !(unusable & gBitTable[i]) && AI_CalcDamage(move, battlerDef, battlerAtk) >= gBattleMons[battlerAtk].hp) + if (move != MOVE_NONE + && move != 0xFFFF + && !(unusable & gBitTable[i]) + && AI_CalcDamage(move, battlerDef, battlerAtk, &effectiveness) >= gBattleMons[battlerAtk].hp) return TRUE; return FALSE; @@ -1088,7 +1089,7 @@ bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits) bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod) { u32 i; - u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL); + u32 unusable = AI_DATA->moveLimitations[battlerDef]; s32 dmg; u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef]; u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod; @@ -1098,7 +1099,7 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM for (i = 0; i < MAX_MON_MOVES; i++) { - dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i]; + dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]; if (dmgMod) dmg *= dmgMod; @@ -1113,9 +1114,9 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability) { - if (IsBattlerAlive(battlerId) && AI_GetAbility(battlerId) == ability) + if (IsBattlerAlive(battlerId) && AI_DATA->abilities[battlerId] == ability) return TRUE; - else if (IsBattlerAlive(BATTLE_PARTNER(battlerId)) && AI_GetAbility(BATTLE_PARTNER(battlerId)) == ability) + else if (IsBattlerAlive(BATTLE_PARTNER(battlerId)) && AI_DATA->abilities[BATTLE_PARTNER(battlerId)] == ability) return TRUE; else return FALSE; @@ -1172,7 +1173,7 @@ u16 AI_GetHoldEffect(u32 battlerId) return HOLD_EFFECT_NONE; if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) return HOLD_EFFECT_NONE; - if (AI_GetAbility(battlerId) == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID)) + if (AI_DATA->abilities[battlerId] == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID)) return HOLD_EFFECT_NONE; return holdEffect; @@ -1190,7 +1191,7 @@ bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags) // different from IsBattlerGrounded in that we don't always know battler's hold effect or ability bool32 AI_IsBattlerGrounded(u8 battlerId) { - u32 holdEffect = AI_GetHoldEffect(battlerId); + u32 holdEffect = AI_DATA->holdEffects[battlerId]; if (holdEffect == HOLD_EFFECT_IRON_BALL) return TRUE; @@ -1206,7 +1207,7 @@ bool32 AI_IsBattlerGrounded(u8 battlerId) return FALSE; else if (holdEffect == HOLD_EFFECT_AIR_BALLOON) return FALSE; - else if (AI_GetAbility(battlerId) == ABILITY_LEVITATE) + else if (AI_DATA->abilities[battlerId] == ABILITY_LEVITATE) return FALSE; else if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FLYING)) return FALSE; @@ -1241,14 +1242,7 @@ bool32 AI_WeatherHasEffect(void) if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) return TRUE; // AI doesn't understand weather supression (handicap) - // need to manually check since we don't necessarily know opponent ability - for (i = 0; i < gBattlersCount; i++) - { - if (IsBattlerAlive(i) - && (AI_GetAbility(i) == ABILITY_AIR_LOCK || AI_GetAbility(i) == ABILITY_CLOUD_NINE)) - return FALSE; - } - return TRUE; + return WEATHER_HAS_EFFECT; // weather damping abilities are announced } bool32 IsAromaVeilProtectedMove(u16 move) @@ -1350,70 +1344,10 @@ bool32 IsMoveRedirectionPrevented(u16 move, u16 atkAbility) return FALSE; } -// differs from GetTotalAccuracy in that we need to check AI history for item, ability, etc -u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u8 atkHoldEffect, u8 defHoldEffect, u16 move) +u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 move) { - u32 calc, moveAcc, atkParam, defParam; - s8 buff, accStage, evasionStage; - - gPotentialItemEffectBattler = battlerDef; - accStage = gBattleMons[battlerAtk].statStages[STAT_ACC]; - evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION]; - if (atkAbility == ABILITY_UNAWARE) - evasionStage = DEFAULT_STAT_STAGE; - if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED) - evasionStage = DEFAULT_STAT_STAGE; - if (defAbility == ABILITY_UNAWARE) - accStage = DEFAULT_STAT_STAGE; - - if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED) - buff = accStage; - else - buff = accStage + DEFAULT_STAT_STAGE - evasionStage; - - if (buff < MIN_STAT_STAGE) - buff = MIN_STAT_STAGE; - if (buff > MAX_STAT_STAGE) - buff = MAX_STAT_STAGE; - - moveAcc = gBattleMoves[move].accuracy; - // Check Thunder and Hurricane on sunny weather. - if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SUN - && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) - moveAcc = 50; - // Check Wonder Skin. - if (defAbility == ABILITY_WONDER_SKIN && gBattleMoves[move].power == 0) - moveAcc = 50; - - calc = gAccuracyStageRatios[buff].dividend * moveAcc; - calc /= gAccuracyStageRatios[buff].divisor; - - if (atkAbility == ABILITY_COMPOUND_EYES) - calc = (calc * 130) / 100; // 1.3 compound eyes boost - else if (atkAbility == ABILITY_VICTORY_STAR) - calc = (calc * 110) / 100; // 1.1 victory star boost - if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_VICTORY_STAR) - calc = (calc * 110) / 100; // 1.1 ally's victory star boost - - if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM) - calc = (calc * 80) / 100; // 1.2 sand veil loss - else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL) - calc = (calc * 80) / 100; // 1.2 snow cloak loss - else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) - calc = (calc * 50) / 100; // 1.5 tangled feet loss - - if (atkAbility == ABILITY_HUSTLE && IS_MOVE_PHYSICAL(move)) - calc = (calc * 80) / 100; // 1.2 hustle loss - - if (defHoldEffect == HOLD_EFFECT_EVASION_UP) - calc = (calc * (100 - defParam)) / 100; - - if (atkHoldEffect == HOLD_EFFECT_WIDE_LENS) - calc = (calc * (100 + atkParam)) / 100; - else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)); - calc = (calc * (100 + atkParam)) / 100; - - return calc; + return GetTotalAccuracy(battlerAtk, battlerDef, move, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], + AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]); } bool32 IsSemiInvulnerable(u8 battlerDef, u16 move) @@ -1441,7 +1375,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) if (gStatuses3[battlerDef] & STATUS3_ALWAYS_HITS || gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk) return TRUE; - if (AI_GetAbility(battlerDef) == ABILITY_NO_GUARD || AI_GetAbility(battlerAtk) == ABILITY_NO_GUARD) + if (AI_DATA->abilities[battlerDef] == ABILITY_NO_GUARD || AI_DATA->abilities[battlerAtk] == ABILITY_NO_GUARD) return TRUE; if (B_TOXIC_NEVER_MISS >= GEN_6 && gBattleMoves[move].effect == EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON)) @@ -1466,12 +1400,13 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) return FALSE; } -bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u32 accuracy, u16 move) +bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move) { - u32 holdEffect = AI_GetHoldEffect(battlerDef); + u32 holdEffect = AI_DATA->holdEffects[battlerDef]; + u32 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move); gPotentialItemEffectBattler = battlerDef; - if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < GetBattlerHoldEffectParam(battlerDef)) + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < AI_DATA->holdEffectParams[battlerDef]) return FALSE; //probabilistically speaking, focus band should activate so dont OHKO else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef)) return FALSE; @@ -1599,7 +1534,7 @@ void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s1 { // TODO more sophisticated logic u16 predictedEffect = gBattleMoves[predictedMove].effect; - u8 defAbility = AI_GetAbility(battlerDef); + u8 defAbility = AI_DATA->abilities[battlerDef]; u32 uses = gDisableStructs[battlerAtk].protectUses; /*if (GetMoveResultFlags(predictedMove) & (MOVE_RESULT_NO_EFFECT | MOVE_RESULT_MISSED)) @@ -1814,7 +1749,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility) bool32 CanIndexMoveFaintTarget(u8 battlerAtk, u8 battlerDef, u8 index, u8 numHits) { - s32 dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][index]; + s32 dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index]; if (numHits) dmg *= numHits; @@ -1908,7 +1843,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 { s32 i; u16 *moves = GetMovesArray(battlerAtk); - u8 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL); + u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -1923,7 +1858,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 || gBattleMoves[moves[i]].target & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD)) continue; - if (AI_GetMoveAccuracy(battlerAtk, battlerDef, atkAbility, defAbility, atkHoldEffect, defHoldEffect, moves[i]) <= accCheck) + if (AI_GetMoveAccuracy(battlerAtk, battlerDef, moves[i]) <= accCheck) return TRUE; } } @@ -1933,7 +1868,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef) { - u8 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL); + u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; u32 i; u16 *moves = GetMovesArray(battlerAtk); @@ -1944,7 +1879,7 @@ bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef) if (!(gBitTable[i] & moveLimitations)) { if (gBattleMoves[moves[i]].effect == EFFECT_SLEEP - && AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, moves[i]) < 85) + && AI_GetMoveAccuracy(battlerAtk, battlerDef, moves[i]) < 85) return TRUE; } } @@ -2259,7 +2194,7 @@ static u32 GetTrapDamage(u8 battlerId) { // ai has no knowledge about turns remaining u32 damage = 0; - u32 holdEffect = AI_GetHoldEffect(gBattleStruct->wrappedBy[battlerId]); + u32 holdEffect = AI_DATA->holdEffects[gBattleStruct->wrappedBy[battlerId]]; if (gBattleMons[battlerId].status2 & STATUS2_WRAPPED) { if (holdEffect == HOLD_EFFECT_BINDING_BAND) @@ -2277,7 +2212,7 @@ static u32 GetPoisonDamage(u8 battlerId) { u32 damage = 0; - if (AI_GetAbility(battlerId) == ABILITY_POISON_HEAL) + if (AI_DATA->abilities[battlerId] == ABILITY_POISON_HEAL) return damage; if (gBattleMons[battlerId].status1 & STATUS1_POISON) @@ -2323,8 +2258,8 @@ static bool32 BattlerAffectedByHail(u8 battlerId, u16 ability) static u32 GetWeatherDamage(u8 battlerId) { - u32 ability = AI_GetAbility(battlerId); - u32 holdEffect = AI_GetHoldEffect(battlerId); + u32 ability = AI_DATA->abilities[battlerId]; + u32 holdEffect = AI_DATA->holdEffects[battlerId]; u32 damage = 0; if (!AI_WeatherHasEffect()) return 0; @@ -2358,7 +2293,7 @@ u32 GetBattlerSecondaryDamage(u8 battlerId) { u32 secondaryDamage; - if (AI_GetAbility(battlerId) == ABILITY_MAGIC_GUARD) + if (AI_DATA->abilities[battlerId] == ABILITY_MAGIC_GUARD) return FALSE; secondaryDamage = GetLeechSeedDamage(battlerId) @@ -2485,13 +2420,13 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo return PIVOT; // Won't get the two turns, pivot if (!IS_MOVE_STATUS(move) && (shouldSwitch - || (AtMaxHp(battlerDef) && (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH + || (AtMaxHp(battlerDef) && (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH || defAbility == ABILITY_STURDY || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD)))) return PIVOT; // pivot to break sash/sturdy/multiscale } else if (!hasStatBoost) { - if (!IS_MOVE_STATUS(move) && (AtMaxHp(battlerDef) && (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH + if (!IS_MOVE_STATUS(move) && (AtMaxHp(battlerDef) && (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH || defAbility == ABILITY_STURDY || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD))) return PIVOT; // pivot to break sash/sturdy/multiscale @@ -2502,7 +2437,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo if (gSideStatuses[battlerAtk] & SIDE_STATUS_SPIKES && switchScore >= SWITCHING_INCREASE_CAN_REMOVE_HAZARDS) return PIVOT;*/ - /*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility) && switchScore >= SWITCHING_INCREASE_WALLS_FOE) + /*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) && switchScore >= SWITCHING_INCREASE_WALLS_FOE) return PIVOT;*/ /*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) @@ -2545,7 +2480,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo { if (CanAIFaintTarget(battlerAtk, battlerDef, 0)) { - if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility)) + if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk])) return CAN_TRY_PIVOT; // Use this move to KO if you must } else // Can't KO the foe @@ -2557,7 +2492,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo { if (CanAIFaintTarget(battlerAtk, battlerDef, 0)) { - if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility) // This is the only move that can KO + if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) // This is the only move that can KO && !hasStatBoost) //You're not wasting a valuable stat boost { return CAN_TRY_PIVOT; @@ -2568,7 +2503,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo // can knock out foe in 2 hits if (IS_MOVE_STATUS(move) && (shouldSwitch //Damaging move //&& (switchScore >= SWITCHING_INCREASE_RESIST_ALL_MOVES + SWITCHING_INCREASE_KO_FOE //remove hazards - || (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef)))) + || (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef)))) return DONT_PIVOT; // Pivot to break the sash else return CAN_TRY_PIVOT; @@ -2637,7 +2572,7 @@ bool32 CanKnockOffItem(u8 battler, u16 item) )) && GetBattlerSide(battler) == B_SIDE_PLAYER) return FALSE; - if (AI_GetAbility(battler) == ABILITY_STICKY_HOLD) + if (AI_DATA->abilities[battler] == ABILITY_STICKY_HOLD) return FALSE; if (!CanBattlerGetOrLoseItem(battler, item)) @@ -2684,13 +2619,13 @@ bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, static bool32 AI_CanPoisonType(u8 battlerAttacker, u8 battlerTarget) { - return ((AI_GetAbility(battlerAttacker) == ABILITY_CORROSION && gBattleMoves[gCurrentMove].split == SPLIT_STATUS) + return ((AI_DATA->abilities[battlerAttacker] == ABILITY_CORROSION && gBattleMoves[gCurrentMove].split == SPLIT_STATUS) || !(IS_BATTLER_OF_TYPE(battlerTarget, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerTarget, TYPE_STEEL))); } static bool32 AI_CanBePoisoned(u8 battlerAtk, u8 battlerDef) { - u16 ability = AI_GetAbility(battlerDef); + u16 ability = AI_DATA->abilities[battlerDef]; if (!(AI_CanPoisonType(battlerAtk, battlerDef)) || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD @@ -2729,7 +2664,7 @@ bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 return FALSE; else if (defAbility != ABILITY_CORROSION && (IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL))) return FALSE; - else if (IsValidDoubleBattle(battlerAtk) && AI_GetAbility(BATTLE_PARTNER(battlerDef)) == ABILITY_PASTEL_VEIL) + else if (IsValidDoubleBattle(battlerAtk) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_PASTEL_VEIL) return FALSE; return TRUE; @@ -2854,7 +2789,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move) { - if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->defAbility)) + if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef])) return TRUE; // battler is taking secondary damage with low HP if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL) @@ -2868,11 +2803,11 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move) bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move) { - if (AI_DATA->atkHoldEffect == HOLD_EFFECT_CHOICE_BAND && CountUsablePartyMons(battlerAtk) == 0) + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND && CountUsablePartyMons(battlerAtk) == 0) return FALSE; // don't lock attacker into fake out if can't switch out if (gDisableStructs[battlerAtk].isFirstTurn - && ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, move) + && ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move) && !DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) return TRUE; @@ -2970,7 +2905,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage) if (CanTargetFaintAi(battlerDef, battlerAtk) && !CanTargetFaintAiWithMod(battlerDef, battlerAtk, healDmg, 0)) return TRUE; // target can faint attacker unless they heal - else if (!CanTargetFaintAi(battlerDef, battlerAtk) && GetHealthPercentage(battlerAtk) < 60 && (Random() % 3)) + else if (!CanTargetFaintAi(battlerDef, battlerAtk) && AI_DATA->hpPercents[battlerAtk] < 60 && (Random() % 3)) return TRUE; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing } else @@ -2988,7 +2923,7 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent) if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) { // using item or user going first - s32 damage = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; + s32 damage = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; s32 healAmount = (healPercent * damage) / 100; if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK) healAmount = 0; @@ -2996,7 +2931,7 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent) if (CanTargetFaintAi(battlerDef, battlerAtk) && !CanTargetFaintAiWithMod(battlerDef, battlerAtk, healAmount, 0)) return TRUE; // target can faint attacker unless they heal - else if (!CanTargetFaintAi(battlerDef, battlerAtk) && GetHealthPercentage(battlerAtk) < 60 && (Random() % 3)) + else if (!CanTargetFaintAi(battlerDef, battlerAtk) && AI_DATA->hpPercents[battlerAtk] < 60 && (Random() % 3)) return TRUE; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing } return FALSE; @@ -3039,14 +2974,14 @@ bool32 IsValidDoubleBattle(u8 battlerAtk) return FALSE; } -u16 GetAllyChosenMove(void) +u16 GetAllyChosenMove(u8 battlerId) { - u8 partnerBattler = BATTLE_PARTNER(sBattler_AI); - + u8 partnerBattler = BATTLE_PARTNER(battlerId); + if (!IsBattlerAlive(partnerBattler) || !IsBattlerAIControlled(partnerBattler)) - return MOVE_NONE; // TODO: prediction? - else if (partnerBattler > sBattler_AI) // Battler with the lower id chooses the move first. return MOVE_NONE; + else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first. + return gLastMoves[partnerBattler]; else return gBattleMons[partnerBattler].moves[gBattleStruct->chosenMovePositions[partnerBattler]]; } @@ -3184,7 +3119,7 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move) party = gEnemyParty; if (CountUsablePartyMons(battlerAtk) == 0 - && (CanTargetFaintAi(battlerDef, battlerAtk) || BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility))) + && (CanTargetFaintAi(battlerDef, battlerAtk) || BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]))) return FALSE; // Don't heal if last mon and will faint for (i = 0; i < PARTY_SIZE; i++) @@ -3241,13 +3176,14 @@ s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon { s32 dmg; u32 i; + u8 effectiveness; struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT); for (i = 0; i < MAX_BATTLERS_COUNT; i++) battleMons[i] = gBattleMons[i]; PokemonToBattleMon(mon, &gBattleMons[battlerAtk]); - dmg = AI_CalcDamage(move, battlerAtk, battlerDef); + dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness); for (i = 0; i < MAX_BATTLERS_COUNT; i++) gBattleMons[i] = battleMons[i]; @@ -3442,10 +3378,10 @@ bool32 IsRecycleEncouragedItem(u16 item) #define STAT_UP_STAGE 10 void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) { - if (AI_DATA->atkAbility == ABILITY_CONTRARY) + if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) return; - if (GetHealthPercentage(battlerAtk) < 80 && AI_RandLessThan(128)) + if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128)) return; if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) @@ -3454,7 +3390,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) switch (statId) { case STAT_ATK: - if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && GetHealthPercentage(battlerAtk) > 40) + if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && AI_DATA->hpPercents[battlerAtk] > 40) { if (gBattleMons[battlerAtk].statStages[STAT_ATK] < STAT_UP_2_STAGE) *score += 2; @@ -3466,7 +3402,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) break; case STAT_DEF: if ((HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)|| IS_MOVE_PHYSICAL(gLastMoves[battlerDef])) - && GetHealthPercentage(battlerAtk) > 70) + && AI_DATA->hpPercents[battlerAtk] > 70) { if (gBattleMons[battlerAtk].statStages[STAT_DEF] < STAT_UP_2_STAGE) *score += 2; // seems better to raise def at higher HP @@ -3484,7 +3420,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) } break; case STAT_SPATK: - if (HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL) && GetHealthPercentage(battlerAtk) > 40) + if (HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL) && AI_DATA->hpPercents[battlerAtk] > 40) { if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < STAT_UP_2_STAGE) *score += 2; @@ -3494,7 +3430,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) break; case STAT_SPDEF: if ((HasMoveWithSplit(battlerDef, SPLIT_SPECIAL) || IS_MOVE_SPECIAL(gLastMoves[battlerDef])) - && GetHealthPercentage(battlerAtk) > 70) + && AI_DATA->hpPercents[battlerAtk] > 70) { if (gBattleMons[battlerAtk].statStages[STAT_SPDEF] < STAT_UP_2_STAGE) *score += 2; // seems better to raise spdef at higher HP @@ -3503,13 +3439,13 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) } break; case STAT_ACC: - if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) *score += 2; // has moves with less than 80% accuracy - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect)) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) *(score)++; break; case STAT_EVASION: - if (!BattlerWillFaintFromWeather(battlerAtk, AI_DATA->atkAbility)) + if (!BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk])) { if (!GetBattlerSecondaryDamage(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_ROOTED)) *score += 2; @@ -3525,7 +3461,7 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; - if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove) && GetHealthPercentage(battlerDef) > 20) + if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove) && AI_DATA->hpPercents[battlerDef] > 20) { if (!HasDamagingMove(battlerDef)) *score += 2; @@ -3536,7 +3472,7 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if (HasMoveEffect(battlerAtk, EFFECT_VENOSHOCK) || HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(battlerAtk, EFFECT_VENOM_DRENCH) - || AI_DATA->atkAbility == ABILITY_MERCILESS) + || AI_DATA->abilities[battlerAtk] == ABILITY_MERCILESS) *(score) += 2; else *(score)++; @@ -3548,7 +3484,7 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; - if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) + if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) { (*score)++; // burning is good if (HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) @@ -3557,7 +3493,7 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) *score += 2; // burning the target to stay alive is cool } - if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_HEX)) + if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_HEX)) (*score)++; } } @@ -3567,7 +3503,7 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; - if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) { u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk); u8 defSpeed = GetBattlerTotalSpeedStat(battlerDef); @@ -3588,7 +3524,7 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; - if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove)) + if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) *score += 2; else return; @@ -3597,7 +3533,7 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) && !(HasMoveEffect(battlerDef, EFFECT_SNORE) || HasMoveEffect(battlerDef, EFFECT_SLEEP_TALK))) (*score)++; - if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_HEX)) + if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_HEX)) (*score)++; } @@ -3606,13 +3542,13 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; - if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove) - && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_CONFUSION - && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_STATUS) + if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) + && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_CONFUSION + && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_STATUS) { if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS || gBattleMons[battlerDef].status2 & STATUS2_INFATUATION - || (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && HasMoveEffect(battlerAtk, EFFECT_FLINCH_HIT))) + || (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveEffect(battlerAtk, EFFECT_FLINCH_HIT))) *score += 3; else *score += 2; diff --git a/src/battle_debug.c b/src/battle_debug.c index 2ea46321df..0657ed2ac4 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -714,7 +714,7 @@ void CB2_BattleDebugMenu(void) static void PutMovesPointsText(struct BattleDebugMenu *data) { - u32 i, j, count; + u32 i, j, count, battlerDef; u8 *text = malloc(0x50); FillWindowPixelBuffer(data->aiMovesWindowId, 0x11); @@ -727,13 +727,14 @@ static void PutMovesPointsText(struct BattleDebugMenu *data) { if (data->aiIconSpriteIds[j] == 0xFF) continue; + battlerDef = gSprites[data->aiIconSpriteIds[j]].data[0]; ConvertIntToDecimalStringN(text, - gBattleStruct->aiFinalScore[data->aiBattlerId][gSprites[data->aiIconSpriteIds[j]].data[0]][i], + gBattleStruct->aiFinalScore[data->aiBattlerId][battlerDef][i], STR_CONV_MODE_RIGHT_ALIGN, 3); AddTextPrinterParameterized(data->aiMovesWindowId, 1, text, 83 + count * 54, i * 15, 0, NULL); ConvertIntToDecimalStringN(text, - gBattleStruct->aiSimulatedDamage[data->aiBattlerId][gSprites[data->aiIconSpriteIds[j]].data[0]][i], + AI_DATA->simulatedDmg[data->aiBattlerId][battlerDef][i], STR_CONV_MODE_RIGHT_ALIGN, 3); AddTextPrinterParameterized(data->aiMovesWindowId, 1, text, 110 + count * 54, i * 15, 0, NULL); diff --git a/src/battle_main.c b/src/battle_main.c index 37845472ac..336f76f6cd 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3698,6 +3698,8 @@ static void TryDoEventsBeforeFirstTurn(void) gMoveResultFlags = 0; gRandomTurnNumber = Random(); + + GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers if (gBattleTypeFlags & BATTLE_TYPE_ARENA) { @@ -3786,6 +3788,7 @@ void BattleTurnPassed(void) *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); + GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers gBattleMainFunc = HandleTurnActionSelectionState; gRandomTurnNumber = Random(); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index c624130a6c..d3717b53df 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1657,20 +1657,14 @@ static bool32 AccuracyCalcHelper(u16 move) return FALSE; } -u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move) +u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect) { - u32 calc, moveAcc, atkHoldEffect, atkParam, defHoldEffect, defParam, atkAbility, defAbility; + u32 calc, moveAcc; s8 buff, accStage, evasionStage; + u8 atkParam = GetBattlerHoldEffectParam(battlerAtk); + u8 defParam = GetBattlerHoldEffectParam(battlerDef); - atkAbility = GetBattlerAbility(battlerAtk); - atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE); - atkParam = GetBattlerHoldEffectParam(battlerAtk); - - defAbility = GetBattlerAbility(battlerDef); - defHoldEffect = GetBattlerHoldEffect(battlerDef, TRUE); - defParam = GetBattlerHoldEffectParam(battlerDef); gPotentialItemEffectBattler = battlerDef; - accStage = gBattleMons[battlerAtk].statStages[STAT_ACC]; evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION]; if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE) @@ -1767,7 +1761,8 @@ static void Cmd_accuracycheck(void) return; // final calculation - if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move)) + if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget), + GetBattlerHoldEffect(gBattlerAttacker, TRUE), GetBattlerHoldEffect(gBattlerTarget, TRUE))) { gMoveResultFlags |= MOVE_RESULT_MISSED; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY) diff --git a/src/battle_util.c b/src/battle_util.c index b293b9a5e6..d600a26e73 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8957,13 +8957,11 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move return dmg; } -s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags) +static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, + bool32 isCrit, bool32 randomFactor, bool32 updateFlags, u16 typeEffectivenessModifier) { s32 dmg; - u16 typeEffectivenessModifier; - - typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags); - + // Don't calculate damage if the move has no effect on target. if (typeEffectivenessModifier == UQ_4_12(0)) return 0; @@ -8996,6 +8994,19 @@ s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 return dmg; } +s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags) +{ + return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor, + updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags)); +} + +// for AI - get move damage and effectiveness with one function call +s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier) +{ + *typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE); + return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE, *typeEffectivenessModifier); +} + static void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities) { u16 mod = GetTypeModifier(moveType, defType); diff --git a/src/battle_util2.c b/src/battle_util2.c index c269d86134..2ccc38a8a7 100644 --- a/src/battle_util2.c +++ b/src/battle_util2.c @@ -26,6 +26,7 @@ void AllocateBattleResources(void) gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack)); gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp)); gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai)); + gBattleResources->aiData = AllocZeroed(sizeof(*gBattleResources->aiData)); gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory)); gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE); @@ -57,6 +58,7 @@ void FreeBattleResources(void) FREE_AND_SET_NULL(gBattleResources->battleCallbackStack); FREE_AND_SET_NULL(gBattleResources->beforeLvlUp); FREE_AND_SET_NULL(gBattleResources->ai); + FREE_AND_SET_NULL(gBattleResources->aiData); FREE_AND_SET_NULL(gBattleResources->battleHistory); FREE_AND_SET_NULL(gBattleResources);