diff --git a/include/battle.h b/include/battle.h index 9809b686d..60c583e50 100644 --- a/include/battle.h +++ b/include/battle.h @@ -277,6 +277,7 @@ struct AIPartyData // Opposing battlers - party mons. u8 count[NUM_BATTLE_SIDES]; }; +// Ai Data used when deciding which move to use, computed only once before each turn's start. struct AiLogicData { u16 abilities[MAX_BATTLERS_COUNT]; @@ -286,11 +287,14 @@ struct AiLogicData u16 predictedMoves[MAX_BATTLERS_COUNT]; u8 hpPercents[MAX_BATTLERS_COUNT]; u16 partnerMove; + u16 speedStats[MAX_BATTLERS_COUNT]; // Speed stats for all battles, calculated only once, same way as damages + u8 moveDmgResult[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // MOVE_POWER defines for GetMoveDamageResult ; attacker, target, moveIndex s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex u8 moveLimitations[MAX_BATTLERS_COUNT]; bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler. u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch. + bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once. }; struct AI_ThinkingStruct @@ -298,7 +302,7 @@ struct AI_ThinkingStruct u8 aiState; u8 movesetIndex; u16 moveConsidered; - s8 score[MAX_MON_MOVES]; + s32 score[MAX_MON_MOVES]; u32 funcResult; u32 aiFlags; u8 aiAction; @@ -640,7 +644,7 @@ struct BattleStruct u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk 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 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier u8 aiMoveOrAction[MAX_BATTLERS_COUNT]; u8 aiChosenTarget[MAX_BATTLERS_COUNT]; u8 soulheartBattlerId; diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index f8eb2b77b..8bb2293e9 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -19,15 +19,15 @@ return score; \ } -u8 ComputeBattleAiScores(u8 battler); +u32 ComputeBattleAiScores(u32 battler); void BattleAI_SetupItems(void); void BattleAI_SetupFlags(void); void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler); -u8 BattleAI_ChooseMoveOrAction(void); +u32 BattleAI_ChooseMoveOrAction(void); void Ai_InitPartyStruct(void); void Ai_UpdateSwitchInData(u32 battler); void Ai_UpdateFaintData(u32 battler); -void SetAiLogicDataForTurn(void); +void SetAiLogicDataForTurn(struct AiLogicData *aiData); extern u8 sBattler_AI; diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index cec324137..b91865a08 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -7,184 +7,186 @@ #define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE) -bool32 AI_RandLessThan(u8 val); -void RecordLastUsedMoveByTarget(void); +#define AI_STRIKES_FIRST(battlerAi, battlerDef, move)((AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER)) + +bool32 AI_RandLessThan(u32 val); bool32 IsAiVsAiBattle(void); bool32 BattlerHasAi(u32 battlerId); bool32 IsAiBattlerAware(u32 battlerId); -void ClearBattlerMoveHistory(u8 battlerId); +void ClearBattlerMoveHistory(u32 battlerId); void RecordLastUsedMoveBy(u32 battlerId, u32 move); void RecordAllMoves(u32 battler); -void RecordKnownMove(u8 battlerId, u32 move); -void RecordAbilityBattle(u8 battlerId, u16 abilityId); -void ClearBattlerAbilityHistory(u8 battlerId); -void RecordItemEffectBattle(u8 battlerId, u8 itemEffect); -void ClearBattlerItemEffectHistory(u8 battlerId); -void SaveBattlerData(u8 battlerId); -void SetBattlerData(u8 battlerId); -void RestoreBattlerData(u8 battlerId); -u16 GetAIChosenMove(u8 battlerId); - -bool32 WillAIStrikeFirst(void); +void RecordKnownMove(u32 battlerId, u32 move); +void RecordAbilityBattle(u32 battlerId, u32 abilityId); +void ClearBattlerAbilityHistory(u32 battlerId); +void RecordItemEffectBattle(u32 battlerId, u32 itemEffect); +void ClearBattlerItemEffectHistory(u32 battlerId); +void SaveBattlerData(u32 battlerId); +void SetBattlerData(u32 battlerId); +void RestoreBattlerData(u32 battlerId); +u32 GetAIChosenMove(u32 battlerId); u32 GetTotalBaseStat(u32 species); bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler); -bool32 AtMaxHp(u8 battler); -u32 GetHealthPercentage(u8 battler); -bool32 IsBattlerTrapped(u8 battler, bool8 switching); -u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 consideredMove); -bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk); -bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits); -bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod); +bool32 AtMaxHp(u32 battler); +u32 GetHealthPercentage(u32 battler); +bool32 IsBattlerTrapped(u32 battler, bool32 switching); +u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered); +bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk); +bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits); +bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod); s32 AI_GetAbility(u32 battlerId); -u16 AI_GetHoldEffect(u32 battlerId); -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); -bool32 CanIndexMoveFaintTarget(u8 battlerAtk, u8 battlerDef, u8 index, u8 numHits); -bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags); -bool32 AI_IsBattlerGrounded(u8 battlerId); -bool32 HasDamagingMove(u8 battlerId); -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, 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); -bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent); -bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect); -bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 moveIndex); -bool32 IsRecycleEncouragedItem(u16 item); -bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item); -bool32 IsStatBoostingBerry(u16 item); -bool32 CanKnockOffItem(u8 battler, u16 item); -bool32 IsAbilityOfRating(u16 ability, s8 rating); -s8 GetAbilityRating(u16 ability); +u32 AI_GetHoldEffect(u32 battlerId); +u32 AI_GetMoveAccuracy(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move); +u32 AI_GetWeather(struct AiLogicData *aiData); +bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits); +bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits); +bool32 AI_IsTerrainAffected(u32 battlerId, u32 flags); +bool32 AI_IsBattlerGrounded(u32 battlerId); +bool32 HasDamagingMove(u32 battlerId); +bool32 HasDamagingMoveOfType(u32 battlerId, u32 type); +u32 GetBattlerSecondaryDamage(u32 battlerId); +bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability); +bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability); +bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move); +bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex); +u32 GetBattlerSideSpeedAverage(u32 battler); +bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage); +bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent); +bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect); +bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex); +bool32 IsRecycleEncouragedItem(u32 item); +bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item); +bool32 IsStatBoostingBerry(u32 item); +bool32 CanKnockOffItem(u32 battler, u32 item); +bool32 IsAbilityOfRating(u32 ability, s8 rating); +s8 GetAbilityRating(u32 ability); bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability); -bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move); -u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move); -bool32 ShouldUseZMove(u8 activeId, u8 targetId, u16 chosenMove); +bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move); +u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move); +bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove); // stat stage checks -bool32 AnyStatIsRaised(u8 battlerId); -bool32 ShouldLowerStat(u8 battler, u16 battlerAbility, u8 stat); -bool32 BattlerStatCanRise(u8 battler, u16 battlerAbility, u8 stat); -bool32 AreBattlersStatsMaxed(u8 battler); -bool32 BattlerHasAnyStatRaised(u8 battlerId); -u32 CountPositiveStatStages(u8 battlerId); -u32 CountNegativeStatStages(u8 battlerId); -bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility); +bool32 AnyStatIsRaised(u32 battlerId); +bool32 ShouldLowerStat(u32 battler, u32 battlerAbility, u32 stat); +bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat); +bool32 AreBattlersStatsMaxed(u32 battler); +bool32 BattlerHasAnyStatRaised(u32 battlerId); +u32 CountPositiveStatStages(u32 battlerId); +u32 CountNegativeStatStages(u32 battlerId); +bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility); // move checks -bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect); +bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect); bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split); -s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness, bool32 considerZPower); +s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower); +s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather); u32 GetNoOfHitsToKO(u32 dmg, s32 hp); -u8 GetMoveDamageResult(u16 move); -u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef); -uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef); -u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef); +void SetMoveDamageResult(u32 battlerAtk, u16 *moves); +u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex); +u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef); +uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef); +u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef); u16 *GetMovesArray(u32 battler); -bool32 IsConfusionMoveEffect(u16 moveEffect); +bool32 IsConfusionMoveEffect(u32 moveEffect); bool32 HasMove(u32 battlerId, u32 move); bool32 HasOnlyMovesWithSplit(u32 battlerId, u32 split, bool32 onlyOffensive); bool32 HasMoveWithSplit(u32 battler, u32 split); -bool32 HasMoveWithType(u32 battler, u8 type); -bool32 HasMoveWithTypeAndSplit(u32 battler, u8 type, u8 split); -bool32 HasMoveEffect(u32 battlerId, u16 moveEffect); -bool32 HasMoveWithLowAccuracy(u8, u8, u8, bool32, u16, u16, u16, u16); -bool32 IsAromaVeilProtectedMove(u16 move); -bool32 IsNonVolatileStatusMoveEffect(u16 moveEffect); -bool32 IsStatLoweringMoveEffect(u16 moveEffect); -bool32 IsMoveRedirectionPrevented(u16 move, u16 atkAbility); -bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move); -bool32 IsHazardMoveEffect(u16 moveEffect); -bool32 MoveCallsOtherMove(u16 move); -bool32 MoveRequiresRecharging(u16 move); -bool32 IsEncoreEncouragedEffect(u16 moveEffect); -void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score); -bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect); -bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect); -bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect); -bool32 ShouldSetRain(u8 battlerAtk, u16 ability, u16 holdEffect); -bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect); -bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef); -bool32 IsHealingMoveEffect(u16 effect); +bool32 HasMoveWithType(u32 battler, u32 type); +bool32 HasMoveWithTypeAndSplit(u32 battler, u32 type, u32 split); +bool32 HasMoveEffect(u32 battlerId, u32 moveEffect); +bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); +bool32 IsAromaVeilProtectedMove(u32 move); +bool32 IsNonVolatileStatusMoveEffect(u32 moveEffect); +bool32 IsStatLoweringMoveEffect(u32 moveEffect); +bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); +bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 IsHazardMoveEffect(u32 moveEffect); +bool32 MoveCallsOtherMove(u32 move); +bool32 MoveRequiresRecharging(u32 move); +bool32 IsEncoreEncouragedEffect(u32 moveEffect); +void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); +bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); +bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect); +bool32 ShouldSetSnow(u32 battler, u32 ability, u32 holdEffect); +bool32 ShouldSetRain(u32 battlerAtk, u32 ability, u32 holdEffect); +bool32 ShouldSetSun(u32 battlerAtk, u32 atkAbility, u32 holdEffect); +bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef); +bool32 IsHealingMoveEffect(u32 effect); bool32 HasHealingEffect(u32 battler); -bool32 IsTrappingMoveEffect(u16 effect); -bool32 HasTrappingMoveEffect(u8 battler); -bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move); -bool32 HasThawingMove(u8 battler); -bool32 IsStatRaisingEffect(u16 effect); -bool32 IsStatLoweringEffect(u16 effect); -bool32 IsStatRaisingEffect(u16 effect); -bool32 IsAttackBoostMoveEffect(u16 effect); -bool32 IsUngroundingEffect(u16 effect); -bool32 IsSemiInvulnerable(u8 battlerDef, u16 move); -bool32 HasSoundMove(u8 battler); -bool32 HasHighCritRatioMove(u8 battler); -bool32 HasMagicCoatAffectedMove(u8 battler); -bool32 HasSnatchAffectedMove(u8 battler); +bool32 IsTrappingMoveEffect(u32 effect); +bool32 HasTrappingMoveEffect(u32 battler); +bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 HasThawingMove(u32 battler); +bool32 IsStatRaisingEffect(u32 effect); +bool32 IsStatLoweringEffect(u32 effect); +bool32 IsStatRaisingEffect(u32 effect); +bool32 IsAttackBoostMoveEffect(u32 effect); +bool32 IsUngroundingEffect(u32 effect); +bool32 IsSemiInvulnerable(u32 battlerDef, u32 move); +bool32 HasSoundMove(u32 battler); +bool32 HasHighCritRatioMove(u32 battler); +bool32 HasMagicCoatAffectedMove(u32 battler); +bool32 HasSnatchAffectedMove(u32 battler); // status checks -bool32 AI_CanBeBurned(u8 battler, u16 ability); -bool32 AI_CanGetFrostbite(u8 battler, u16 ability); -bool32 AI_CanBeConfused(u8 battler, u16 ability); -bool32 AI_CanSleep(u8 battler, u16 ability); -bool32 IsBattlerIncapacitated(u8 battler, u16 ability); -bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove); -bool32 ShouldPoisonSelf(u8 battler, u16 ability); -bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove); -bool32 AI_CanParalyze(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove); -bool32 AI_CanConfuse(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove); -bool32 ShouldBurnSelf(u8 battler, u16 ability); -bool32 AI_CanBurn(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove); -bool32 AI_CanGiveFrostbite(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove); -bool32 AI_CanBeInfatuated(u8 battlerAtk, u8 battlerDef, u16 defAbility); -bool32 AnyPartyMemberStatused(u8 battlerId, bool32 checkSoundproof); -u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move); -bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move); -bool32 IsWakeupTurn(u8 battler); -bool32 AI_IsBattlerAsleepOrComatose(u8 battlerId); +bool32 AI_CanBeBurned(u32 battler, u32 ability); +bool32 AI_CanGetFrostbite(u32 battler, u32 ability); +bool32 AI_CanBeConfused(u32 battler, u32 ability); +bool32 AI_CanSleep(u32 battler, u32 ability); +bool32 IsBattlerIncapacitated(u32 battler, u32 ability); +bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); +bool32 ShouldPoisonSelf(u32 battler, u32 ability); +bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); +bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); +bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove); +bool32 ShouldBurnSelf(u32 battler, u32 ability); +bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove); +bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove); +bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof); +u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move); +bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 IsWakeupTurn(u32 battler); +bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId); // partner logic -u16 GetAllyChosenMove(u8 battlerId); -bool32 IsValidDoubleBattle(u8 battlerAtk); -bool32 IsTargetingPartner(u8 battlerAtk, u8 battlerDef); -bool32 DoesPartnerHaveSameMoveEffect(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove); -bool32 PartnerHasSameMoveEffectWithoutTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove); -bool32 PartnerMoveEffectIsStatusSameTarget(u8 battlerAtkPartner, u8 battlerDef, u16 partnerMove); -bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove); -bool32 PartnerMoveEffectIsTerrain(u8 battlerAtkPartner, u16 partnerMove); -bool32 PartnerMoveIs(u8 battlerAtkPartner, u16 partnerMove, u16 moveCheck); -bool32 PartnerMoveIsSameAsAttacker(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove); -bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove); -bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move); +#define IS_TARGETING_PARTNER(battlerAtk, battlerDef)((battlerAtk) == (battlerDef ^ BIT_FLANK)) +u32 GetAllyChosenMove(u32 battlerId); +bool32 IsValidDoubleBattle(u32 battlerAtk); +bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef); +bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove); +bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove); +bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove); +bool32 PartnerMoveEffectIsWeather(u32 battlerAtkPartner, u32 partnerMove); +bool32 PartnerMoveEffectIsTerrain(u32 battlerAtkPartner, u32 partnerMove); +bool32 PartnerMoveIs(u32 battlerAtkPartner, u32 partnerMove, u32 moveCheck); +bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove); +bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove); +bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move); // party logic struct BattlePokemon *AllocSaveBattleMons(void); void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons); s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon); -s32 CountUsablePartyMons(u8 battlerId); -bool32 IsPartyFullyHealedExceptBattler(u8 battler); -bool32 PartyHasMoveSplit(u8 battlerId, u8 split); -bool32 SideHasMoveSplit(u8 battlerId, u8 split); +s32 CountUsablePartyMons(u32 battlerId); +bool32 IsPartyFullyHealedExceptBattler(u32 battler); +bool32 PartyHasMoveSplit(u32 battlerId, u32 split); +bool32 SideHasMoveSplit(u32 battlerId, u32 split); // score increases -void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score); -void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); -void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); -void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); -void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); -void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); -void IncreaseFrostbiteScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score); +void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score); +void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); +void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/battle_main.h b/include/battle_main.h index 0ae62cd35..91e9fd90e 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -57,17 +57,19 @@ void SwitchInClearSetData(u32 battler); void FaintClearSetData(u32 battler); void BattleTurnPassed(void); u8 IsRunningFromBattleImpossible(u32 battler); -void SwitchPartyOrder(u8 battlerId); +void SwitchPartyOrder(u32 battlerId); void SwapTurnOrder(u8 id1, u8 id2); -u32 GetBattlerTotalSpeedStat(u8 battlerId); +u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect); +u32 GetBattlerTotalSpeedStat(u32 battler); s8 GetChosenMovePriority(u32 battlerId); s8 GetMovePriority(u32 battlerId, u16 move); -u8 GetWhoStrikesFirst(u8 battlerId1, u8 battlerId2, bool8 ignoreChosenMoves); +u32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2, + u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2); +u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves); void RunBattleScriptCommands_PopCallbacksStack(void); void RunBattleScriptCommands(void); -bool8 TryRunFromBattle(u8 battlerId); void SpecialStatusesClear(void); -void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk); +void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk); bool32 IsWildMonSmart(void); u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags); void ModifyPersonalityForNature(u32 *personality, u32 newNature); diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index dcf601e80..ed08b5eb4 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -16,8 +16,9 @@ struct StatFractions u8 divisor; }; -s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility); -s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move); +s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk); +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility); +s32 GetCritHitChance(s32 critChanceIndex); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); u8 GetBattlerTurnOrderNum(u8 battlerId); bool32 NoAliveMonsForEitherParty(void); @@ -28,8 +29,8 @@ void BattleCreateYesNoCursorAt(u8 cursorPosition); void BufferMoveToLearnIntoBattleTextBuff2(void); void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags); bool8 UproarWakeUpCheck(u8 battlerId); -bool32 DoesSubstituteBlockMove(u8 battlerAtk, u8 battlerDef, u32 move); -bool32 DoesDisguiseBlockMove(u8 battlerAtk, u8 battlerDef, u32 move); +bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move); +bool32 DoesDisguiseBlockMove(u32 battlerAtk, u32 battlerDef, u32 move); bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget); bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget); bool32 CanUseLastResort(u8 battlerId); @@ -47,6 +48,7 @@ u32 GetHighestStatId(u32 battlerId); bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType); bool32 DoSwitchInAbilitiesItems(u32 battlerId); u8 GetFirstFaintedPartyIndex(u8 battlerId); +bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler); extern void (* const gBattleScriptingCommandsTable[])(void); extern const u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4]; diff --git a/include/battle_util.h b/include/battle_util.h index 36fb7a6e5..9cdd41cab 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -102,6 +102,7 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move); void HandleAction_UseMove(void); void HandleAction_Switch(void); void HandleAction_UseItem(void); +bool32 TryRunFromBattle(u32 battler); void HandleAction_Run(void); void HandleAction_WatchesCarefully(void); void HandleAction_SafariZoneBallThrow(void); @@ -116,106 +117,106 @@ u8 GetBattlerForBattleScript(u8 caseId); void PressurePPLose(u8 target, u8 attacker, u16 move); void PressurePPLoseOnUsingPerishSong(u8 attacker); void PressurePPLoseOnUsingImprison(u8 attacker); -bool32 IsBattlerMarkedForControllerExec(u8 battler); -void MarkBattlerForControllerExec(u8 battler); -void MarkBattlerReceivedLinkData(u8 battler); -void CancelMultiTurnMoves(u8 battler); -bool8 WasUnableToUseMove(u8 battler); -void PrepareStringBattle(u16 stringId, u8 battler); +bool32 IsBattlerMarkedForControllerExec(u32 battler); +void MarkBattlerForControllerExec(u32 battler); +void MarkBattlerReceivedLinkData(u32 battler); +void CancelMultiTurnMoves(u32 battler); +bool32 WasUnableToUseMove(u32 battler); +void PrepareStringBattle(u16 stringId, u32 battler); void ResetSentPokesToOpponentValue(void); -void OpponentSwitchInResetSentPokesToOpponentValue(u8 battler); -void UpdateSentPokesToOpponentValue(u8 battler); +void OpponentSwitchInResetSentPokesToOpponentValue(u32 battler); +void UpdateSentPokesToOpponentValue(u32 battler); void BattleScriptPush(const u8 *bsPtr); void BattleScriptPushCursor(void); void BattleScriptPop(void); u32 TrySetCantSelectMoveBattleScript(u32 battler); -u8 CheckMoveLimitations(u8 battler, u8 unusableMoves, u16 check); +u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check); bool32 AreAllMovesUnusable(u32 battler); -u8 GetImprisonedMovesCount(u8 battler, u16 move); +u8 GetImprisonedMovesCount(u32 battler, u16 move); u8 DoFieldEndTurnEffects(void); s32 GetDrainedBigRootHp(u32 battler, s32 hp); u8 DoBattlerEndTurnEffects(void); -bool8 HandleWishPerishSongOnTurnEnd(void); -bool8 HandleFaintedMonActions(void); +bool32 HandleWishPerishSongOnTurnEnd(void); +bool32 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); u8 AtkCanceller_UnableToUseMove(u32 moveType); void SetAtkCancellerForCalledMove(void); u8 AtkCanceller_UnableToUseMove2(void); -bool8 HasNoMonsToSwitch(u8 battler, u8 r1, u8 r2); -bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility); -u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 moveArg); -bool32 TryPrimalReversion(u8 battler); +bool32 HasNoMonsToSwitch(u32 battler, u8 r1, u8 r2); +bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility); +u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg); +bool32 TryPrimalReversion(u32 battler); bool32 IsNeutralizingGasOnField(void); -u32 GetBattlerAbility(u8 battler); +u32 GetBattlerAbility(u32 battler); u32 IsAbilityOnSide(u32 battler, u32 ability); u32 IsAbilityOnOpposingSide(u32 battler, u32 ability); u32 IsAbilityOnField(u32 ability); u32 IsAbilityOnFieldExcept(u32 battler, u32 ability); u32 IsAbilityPreventingEscape(u32 battler); -bool32 IsBattlerProtected(u8 battler, u16 move); +bool32 IsBattlerProtected(u32 battler, u32 move); bool32 CanBattlerEscape(u32 battler); // no ability check void BattleScriptExecute(const u8 *BS_ptr); void BattleScriptPushCursorAndCallback(const u8 *BS_ptr); -u8 ItemBattleEffects(u8 caseID, u8 battler, bool8 moveTurn); -void ClearFuryCutterDestinyBondGrudge(u8 battler); +u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn); +void ClearFuryCutterDestinyBondGrudge(u32 battler); void HandleAction_RunBattleScript(void); u32 SetRandomTarget(u32 battler); u32 GetMoveTarget(u16 move, u8 setTarget); u8 IsMonDisobedient(void); -u32 GetBattlerHoldEffect(u8 battler, bool32 checkNegating); -u32 GetBattlerHoldEffectParam(u8 battler); -bool32 IsMoveMakingContact(u16 move, u8 battlerAtk); -bool32 IsBattlerGrounded(u8 battler); -bool32 IsBattlerAlive(u8 battler); -u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move); -u32 GetBattlerWeight(u8 battler); +u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating); +u32 GetBattlerHoldEffectParam(u32 battler); +bool32 IsMoveMakingContact(u32 move, u32 battlerAtk); +bool32 IsBattlerGrounded(u32 battler); +bool32 IsBattlerAlive(u32 battler); +u32 GetMoveSlot(u16 *moves, u32 move); +u32 GetBattlerWeight(u32 battler); u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer); u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter); -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, s32 fixedBasePower, uq4_12_t *typeEffectivenessModifier); -uq4_12_t CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities); +s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags); +s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier, + u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef); +uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities); uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef); -uq4_12_t GetTypeModifier(u8 atkType, u8 defType); -s32 GetStealthHazardDamage(u8 hazardType, u8 battler); +uq4_12_t GetTypeModifier(u32 atkType, u32 defType); +s32 GetStealthHazardDamage(u8 hazardType, u32 battler); s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp); -bool32 CanMegaEvolve(u8 battler); -bool32 CanUltraBurst(u8 battler); -bool32 IsBattlerMegaEvolved(u8 battler); -bool32 IsBattlerPrimalReverted(u8 battler); -bool32 IsBattlerUltraBursted(u8 battler); -u16 GetBattleFormChangeTargetSpecies(u8 battler, u16 method); -bool32 TryBattleFormChange(u8 battler, u16 method); +bool32 CanMegaEvolve(u32 battler); +bool32 CanUltraBurst(u32 battler); +bool32 IsBattlerMegaEvolved(u32 battler); +bool32 IsBattlerPrimalReverted(u32 battler); +bool32 IsBattlerUltraBursted(u32 battler); +u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method); +bool32 TryBattleFormChange(u32 battler, u16 method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); -bool32 CanBattlerGetOrLoseItem(u8 battler, u16 itemId); +bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); u32 GetIllusionMonSpecies(u32 battler); struct Pokemon *GetIllusionMonPtr(u32 battler); void ClearIllusionMon(u32 battler); bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); -bool8 ShouldGetStatBadgeBoost(u16 flagId, u8 battler); +bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); u8 GetBattleMoveSplit(u32 moveId); bool32 CanFling(u32 battler); bool32 IsTelekinesisBannedSpecies(u16 species); bool32 IsHealBlockPreventingMove(u32 battler, u32 move); bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId); -bool32 IsPartnerMonFromSameTrainer(u8 battler); -u8 GetSplitBasedOnStats(u8 battler); -bool32 TestSheerForceFlag(u8 battler, u16 move); +bool32 IsPartnerMonFromSameTrainer(u32 battler); +u8 GetSplitBasedOnStats(u32 battler); +bool32 TestSheerForceFlag(u32 battler, u16 move); void TryRestoreHeldItems(void); -bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item); -void TrySaveExchangedItem(u8 battler, u16 stolenItem); -bool32 IsPartnerMonFromSameTrainer(u8 battler); -u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute); -bool32 IsBattlerAffectedByHazards(u8 battler, bool32 toxicSpikes); -void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast); -bool32 CompareStat(u8 battler, u8 statId, u8 cmpTo, u8 cmpKind); -bool32 TryRoomService(u8 battler); -void BufferStatChange(u8 battler, u8 statId, u8 stringId); -bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget); -u16 GetUsedHeldItem(u8 battler); -bool32 IsBattlerWeatherAffected(u8 battler, u32 weatherFlags); -u32 GetBattlerMoveTargetType(u8 battler, u16 move); -bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move); -bool8 IsMoveAffectedByParentalBond(u16 move, u8 battler); +bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item); +void TrySaveExchangedItem(u32 battler, u16 stolenItem); +bool32 IsPartnerMonFromSameTrainer(u32 battler); +u8 TryHandleSeed(u32 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute); +bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes); +void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); +bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind); +bool32 TryRoomService(u32 battler); +void BufferStatChange(u32 battler, u8 statId, u8 stringId); +bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); +u16 GetUsedHeldItem(u32 battler); +bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags); +u32 GetBattlerMoveTargetType(u32 battler, u32 move); +bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move); void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon); void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon); void RecalcBattlerStats(u32 battler, struct Pokemon *mon); @@ -231,21 +232,21 @@ bool32 IsGastroAcidBannedAbility(u16 ability); bool32 IsEntrainmentBannedAbilityAttacker(u16 ability); bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability); -bool32 CanSleep(u8 battler); -bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget); -bool32 CanBeBurned(u8 battler); -bool32 CanBeParalyzed(u8 battler); -bool32 CanBeFrozen(u8 battler); -bool32 CanGetFrostbite(u8 battler); -bool32 CanBeConfused(u8 battler); -bool32 IsBattlerTerrainAffected(u8 battler, u32 terrainFlag); -u32 GetBattlerFriendshipScore(u8 battler); -u32 CountBattlerStatIncreases(u8 battler, bool32 countEvasionAcc); +bool32 CanSleep(u32 battler); +bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); +bool32 CanBeBurned(u32 battler); +bool32 CanBeParalyzed(u32 battler); +bool32 CanBeFrozen(u32 battler); +bool32 CanGetFrostbite(u32 battler); +bool32 CanBeConfused(u32 battler); +bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag); +u32 GetBattlerFriendshipScore(u32 battler); +u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc); bool32 IsMyceliumMightOnField(void); -bool8 ChangeTypeBasedOnTerrain(u8 battler); -void RemoveConfusionStatus(u8 battler); -u8 GetBattlerGender(u8 battler); -bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2); -u32 CalcSecondaryEffectChance(u8 battler, u8 secondaryEffectChance); +bool32 ChangeTypeBasedOnTerrain(u32 battler); +void RemoveConfusionStatus(u32 battler); +u8 GetBattlerGender(u32 battler); +bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2); +u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/item.h b/include/item.h index eac8fead9..d130500c0 100644 --- a/include/item.h +++ b/include/item.h @@ -63,8 +63,8 @@ bool8 AddPyramidBagItem(u16 itemId, u16 count); bool8 RemovePyramidBagItem(u16 itemId, u16 count); const u8 *ItemId_GetName(u16 itemId); u16 ItemId_GetPrice(u16 itemId); -u8 ItemId_GetHoldEffect(u16 itemId); -u8 ItemId_GetHoldEffectParam(u16 itemId); +u32 ItemId_GetHoldEffect(u32 itemId); +u32 ItemId_GetHoldEffectParam(u32 itemId); const u8 *ItemId_GetDescription(u16 itemId); u8 ItemId_GetImportance(u16 itemId); u8 ItemId_GetPocket(u16 itemId); @@ -72,7 +72,7 @@ u8 ItemId_GetType(u16 itemId); ItemUseFunc ItemId_GetFieldFunc(u16 itemId); u8 ItemId_GetBattleUsage(u16 itemId); u8 ItemId_GetSecondaryId(u16 itemId); -u8 ItemId_GetFlingPower(u16 itemId); +u32 ItemId_GetFlingPower(u32 itemId); u32 GetItemStatus1Mask(u16 itemId); u32 GetItemStatus2Mask(u16 itemId); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 1805b2d9d..4f778530d 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -28,39 +28,30 @@ #define AI_ACTION_WATCH (1 << 2) #define AI_ACTION_DO_NOT_ATTACK (1 << 3) -// AI states -enum -{ - AIState_SettingUp, - AIState_Processing, - AIState_FinishedProcessing, - AIState_DoNotProcess -}; - -static u8 ChooseMoveOrAction_Singles(void); -static u8 ChooseMoveOrAction_Doubles(void); -static void BattleAI_DoAIProcessing(void); -static bool32 IsPinchBerryItemEffect(u16 holdEffect); +static u32 ChooseMoveOrAction_Singles(u32 battlerAi); +static u32 ChooseMoveOrAction_Doubles(u32 battlerAi); +static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battler); +static bool32 IsPinchBerryItemEffect(u32 holdEffect); // ewram EWRAM_DATA const u8 *gAIScriptPtr = NULL; // Still used in contests EWRAM_DATA u8 sBattler_AI = 0; // const rom data -static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_PreferStrongestMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_Safari(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_FirstBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); -static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score); +static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); -static s16 (*const sBattleAiFuncTable[])(u8, u8, u16, s16) = +static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = { [0] = AI_CheckBadMove, // AI_FLAG_CHECK_BAD_MOVE [1] = AI_TryToFaint, // AI_FLAG_TRY_TO_FAINT @@ -126,7 +117,7 @@ void BattleAI_SetupItems(void) static u32 GetWildAiFlags(void) { - u8 avgLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); + u32 avgLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); u32 flags; if (IsDoubleBattle()) @@ -216,26 +207,23 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler) gBattleStruct->aiChosenTarget[sBattler_AI] = gBattlerTarget; } -u8 BattleAI_ChooseMoveOrAction(void) +u32 BattleAI_ChooseMoveOrAction(void) { - u32 savedCurrentMove = gCurrentMove; - u8 ret; + u32 ret; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) - ret = ChooseMoveOrAction_Singles(); + ret = ChooseMoveOrAction_Singles(sBattler_AI); else - ret = ChooseMoveOrAction_Doubles(); + ret = ChooseMoveOrAction_Doubles(sBattler_AI); // Clear protect structures, some flags may be set during AI calcs // e.g. pranksterElevated from GetMovePriority memset(&gProtectStructs, 0, MAX_BATTLERS_COUNT * sizeof(struct ProtectStruct)); - - gCurrentMove = savedCurrentMove; return ret; } // damages/other info computed in GetAIDataAndCalcDmg -u8 ComputeBattleAiScores(u8 battler) +u32 ComputeBattleAiScores(u32 battler) { sBattler_AI = battler; BattleAI_SetupAIData(0xF, sBattler_AI); @@ -336,69 +324,76 @@ void Ai_UpdateFaintData(u32 battler) aiMon->isFainted = TRUE; } -static void SetBattlerAiData(u32 battler) +static void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) { - AI_DATA->abilities[battler] = AI_GetAbility(battler); - AI_DATA->items[battler] = gBattleMons[battler].item; - AI_DATA->holdEffects[battler] = AI_GetHoldEffect(battler); - AI_DATA->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler); - AI_DATA->predictedMoves[battler] = gLastMoves[battler]; - AI_DATA->hpPercents[battler] = GetHealthPercentage(battler); - AI_DATA->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL); + u32 ability, holdEffect; + + ability = aiData->abilities[battler] = AI_GetAbility(battler); + aiData->items[battler] = gBattleMons[battler].item; + holdEffect = aiData->holdEffects[battler] = AI_GetHoldEffect(battler); + aiData->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler); + aiData->predictedMoves[battler] = gLastMoves[battler]; + aiData->hpPercents[battler] = GetHealthPercentage(battler); + aiData->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL); + aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); } -void SetAiLogicDataForTurn(void) +static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlersCount) { - u32 battlerAtk, battlerDef, i, move; - u8 effectiveness; - s32 dmg; + u32 battlerDef, i, weather; + u16 *moves; - memset(AI_DATA, 0, sizeof(struct AiLogicData)); + // Simulate dmg for both ai controlled mons and for player controlled mons. + SaveBattlerData(battlerAtk); + moves = GetMovesArray(battlerAtk); + weather = AI_GetWeather(aiData); + for (battlerDef = 0; battlerDef < battlersCount; battlerDef++) + { + if (battlerAtk == battlerDef) + continue; + SaveBattlerData(battlerDef); + for (i = 0; i < MAX_MON_MOVES; i++) + { + s32 dmg = 0; + u8 effectiveness = AI_EFFECTIVENESS_x0; + u32 move = moves[i]; + + if (move != 0 + && move != 0xFFFF + //&& gBattleMoves[move].power != 0 /* we want to get effectiveness of status moves */ + && !(aiData->moveLimitations[battlerAtk] & gBitTable[i])) { + dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather); + } + + aiData->simulatedDmg[battlerAtk][battlerDef][i] = dmg; + aiData->effectiveness[battlerAtk][battlerDef][i] = effectiveness; + } + } + SetMoveDamageResult(battlerAtk, moves); +} + +void SetAiLogicDataForTurn(struct AiLogicData *aiData) +{ + u32 battlerAtk, battlersCount; + + memset(aiData, 0, sizeof(struct AiLogicData)); if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart()) return; + // Set delay timer to count how long it takes for AI to choose action/move gBattleStruct->aiDelayTimer = gMain.vblankCounter1; - // get/assume all battler data - for (i = 0; i < gBattlersCount; i++) + aiData->weatherHasEffect = WEATHER_HAS_EFFECT; + // get/assume all battler data and simulate AI damage + battlersCount = gBattlersCount; + for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { - if (IsBattlerAlive(i)) { - SetBattlerAiData(i); - } - } - - // simulate AI damage - for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++) - { - if (!IsBattlerAlive(battlerAtk) - || !IsAiBattlerAware(battlerAtk)) { + if (!IsBattlerAlive(battlerAtk)) continue; - } - 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 /* we want to get effectiveness of status moves */ - && !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i])) { - dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE); - } - - AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg; - AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness; - } - } + SetBattlerAiData(battlerAtk, aiData); + SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } } @@ -474,7 +469,7 @@ static bool32 AI_ShouldSwitchIfBadMoves(u32 battler, bool32 doubleBattle) return FALSE; } -static u8 ChooseMoveOrAction_Singles(void) +static u32 ChooseMoveOrAction_Singles(u32 battlerAi) { u8 currentMoveArray[MAX_MON_MOVES]; u8 consideredMoveArray[MAX_MON_MOVES]; @@ -487,16 +482,14 @@ static u8 ChooseMoveOrAction_Singles(void) { if (flags & 1) { - AI_THINKING_STRUCT->aiState = AIState_SettingUp; - BattleAI_DoAIProcessing(); + BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi); } flags >>= 1; AI_THINKING_STRUCT->aiLogicId++; - AI_THINKING_STRUCT->movesetIndex = 0; } for (i = 0; i < MAX_MON_MOVES; i++) { - gBattleStruct->aiFinalScore[sBattler_AI][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; + gBattleStruct->aiFinalScore[battlerAi][gBattlerTarget][i] = AI_THINKING_STRUCT->score[i]; } // Check special AI actions. @@ -506,7 +499,7 @@ static u8 ChooseMoveOrAction_Singles(void) return AI_CHOICE_WATCH; // Switch mon if there are no good moves to use. - if (AI_ShouldSwitchIfBadMoves(sBattler_AI, FALSE)) + if (AI_ShouldSwitchIfBadMoves(battlerAi, FALSE)) return AI_CHOICE_SWITCH; numOfBestMoves = 1; @@ -515,7 +508,7 @@ static u8 ChooseMoveOrAction_Singles(void) for (i = 1; i < MAX_MON_MOVES; i++) { - if (gBattleMons[sBattler_AI].moves[i] != MOVE_NONE) + if (gBattleMons[battlerAi].moves[i] != MOVE_NONE) { // In ruby, the order of these if statements is reversed. if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) @@ -534,22 +527,22 @@ static u8 ChooseMoveOrAction_Singles(void) return consideredMoveArray[Random() % numOfBestMoves]; } -static u8 ChooseMoveOrAction_Doubles(void) +static u32 ChooseMoveOrAction_Doubles(u32 battlerAi) { s32 i, j; u32 flags; - s16 bestMovePointsForTarget[MAX_BATTLERS_COUNT]; - s8 mostViableTargetsArray[MAX_BATTLERS_COUNT]; + s32 bestMovePointsForTarget[MAX_BATTLERS_COUNT]; + u8 mostViableTargetsArray[MAX_BATTLERS_COUNT]; u8 actionOrMoveIndex[MAX_BATTLERS_COUNT]; - u8 mostViableMovesScores[MAX_MON_MOVES]; + s32 mostViableMovesScores[MAX_MON_MOVES]; u8 mostViableMovesIndices[MAX_MON_MOVES]; - s32 mostViableTargetsNo; - s32 mostViableMovesNo; - s16 mostMovePoints; + u32 mostViableTargetsNo; + u32 mostViableMovesNo; + s32 mostMovePoints; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { - if (i == sBattler_AI || gBattleMons[i].hp == 0) + if (i == battlerAi || gBattleMons[i].hp == 0) { actionOrMoveIndex[i] = 0xFF; bestMovePointsForTarget[i] = -1; @@ -557,13 +550,11 @@ static u8 ChooseMoveOrAction_Doubles(void) else { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - BattleAI_SetupAIData(gBattleStruct->palaceFlags >> 4, sBattler_AI); + BattleAI_SetupAIData(gBattleStruct->palaceFlags >> 4, battlerAi); else - BattleAI_SetupAIData(0xF, sBattler_AI); + BattleAI_SetupAIData(0xF, battlerAi); gBattlerTarget = i; - if ((i & BIT_SIDE) != (sBattler_AI & BIT_SIDE)) - RecordLastUsedMoveByTarget(); AI_DATA->partnerMove = GetAllyChosenMove(i); AI_THINKING_STRUCT->aiLogicId = 0; @@ -573,12 +564,10 @@ static u8 ChooseMoveOrAction_Doubles(void) { if (flags & 1) { - AI_THINKING_STRUCT->aiState = AIState_SettingUp; - BattleAI_DoAIProcessing(); + BattleAI_DoAIProcessing(AI_THINKING_STRUCT, battlerAi); } flags >>= 1; AI_THINKING_STRUCT->aiLogicId++; - AI_THINKING_STRUCT->movesetIndex = 0; } if (AI_THINKING_STRUCT->aiAction & AI_ACTION_FLEE) @@ -596,9 +585,9 @@ static u8 ChooseMoveOrAction_Doubles(void) mostViableMovesNo = 1; for (j = 1; j < MAX_MON_MOVES; j++) { - if (gBattleMons[sBattler_AI].moves[j] != 0) + if (gBattleMons[battlerAi].moves[j] != 0) { - if (!CanTargetBattler(sBattler_AI, i, gBattleMons[sBattler_AI].moves[j])) + if (!CanTargetBattler(battlerAi, i, gBattleMons[battlerAi].moves[j])) continue; if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j]) @@ -619,20 +608,20 @@ static u8 ChooseMoveOrAction_Doubles(void) bestMovePointsForTarget[i] = mostViableMovesScores[0]; // Don't use a move against ally if it has less than 100 points. - if (i == BATTLE_PARTNER(sBattler_AI) && bestMovePointsForTarget[i] < 100) + if (i == BATTLE_PARTNER(battlerAi) && bestMovePointsForTarget[i] < 100) { bestMovePointsForTarget[i] = -1; } } for (j = 0; j < MAX_MON_MOVES; j++) { - gBattleStruct->aiFinalScore[sBattler_AI][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; + gBattleStruct->aiFinalScore[battlerAi][gBattlerTarget][j] = AI_THINKING_STRUCT->score[j]; } } } // Switch mon if all of the moves are bad to use against any of the target. - if (AI_ShouldSwitchIfBadMoves(sBattler_AI, TRUE)) + if (AI_ShouldSwitchIfBadMoves(battlerAi, TRUE)) return AI_CHOICE_SWITCH; mostMovePoints = bestMovePointsForTarget[0]; @@ -655,80 +644,64 @@ static u8 ChooseMoveOrAction_Doubles(void) } gBattlerTarget = mostViableTargetsArray[Random() % mostViableTargetsNo]; - gBattleStruct->aiChosenTarget[sBattler_AI] = gBattlerTarget; + gBattleStruct->aiChosenTarget[battlerAi] = gBattlerTarget; return actionOrMoveIndex[gBattlerTarget]; } -static void BattleAI_DoAIProcessing(void) +static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battler) { - while (AI_THINKING_STRUCT->aiState != AIState_FinishedProcessing) + do { - switch (AI_THINKING_STRUCT->aiState) - { - case AIState_DoNotProcess: // Needed to match. - break; - case AIState_SettingUp: - if (gBattleMons[sBattler_AI].pp[AI_THINKING_STRUCT->movesetIndex] == 0) - { - AI_THINKING_STRUCT->moveConsidered = 0; - } - else - { - AI_THINKING_STRUCT->moveConsidered = gBattleMons[sBattler_AI].moves[AI_THINKING_STRUCT->movesetIndex]; - } - AI_THINKING_STRUCT->aiState++; - break; - case AIState_Processing: - if (AI_THINKING_STRUCT->moveConsidered != MOVE_NONE - && AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] > 0) - { - if (AI_THINKING_STRUCT->aiLogicId < ARRAY_COUNT(sBattleAiFuncTable) - && sBattleAiFuncTable[AI_THINKING_STRUCT->aiLogicId] != NULL) - { - // Call AI function - AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = - sBattleAiFuncTable[AI_THINKING_STRUCT->aiLogicId](sBattler_AI, - gBattlerTarget, - AI_THINKING_STRUCT->moveConsidered, - AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex]); - } - } - else - { - AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0; - } + if (gBattleMons[battler].pp[aiThink->movesetIndex] == 0) + aiThink->moveConsidered = MOVE_NONE; + else + aiThink->moveConsidered = gBattleMons[battler].moves[aiThink->movesetIndex]; - AI_THINKING_STRUCT->movesetIndex++; - if (AI_THINKING_STRUCT->movesetIndex < MAX_MON_MOVES && !(AI_THINKING_STRUCT->aiAction & AI_ACTION_DO_NOT_ATTACK)) - AI_THINKING_STRUCT->aiState = AIState_SettingUp; - else - AI_THINKING_STRUCT->aiState++; - break; + if (aiThink->moveConsidered != MOVE_NONE + && aiThink->score[aiThink->movesetIndex] > 0) + { + if (aiThink->aiLogicId < ARRAY_COUNT(sBattleAiFuncTable) + && sBattleAiFuncTable[aiThink->aiLogicId] != NULL) + { + // Call AI function + aiThink->score[aiThink->movesetIndex] = + sBattleAiFuncTable[aiThink->aiLogicId](battler, + gBattlerTarget, + aiThink->moveConsidered, + aiThink->score[aiThink->movesetIndex]); + } } - } + else + { + aiThink->score[aiThink->movesetIndex] = 0; + } + aiThink->movesetIndex++; + } while (aiThink->movesetIndex < MAX_MON_MOVES && !(aiThink->aiAction & AI_ACTION_DO_NOT_ATTACK)); + + aiThink->movesetIndex = 0; } // AI Score Functions // AI_FLAG_CHECK_BAD_MOVE - decreases move scores -static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { // move data - u8 atkPriority = GetMovePriority(battlerAtk, move); - u16 moveEffect = gBattleMoves[move].effect; + s8 atkPriority = GetMovePriority(battlerAtk, move); + u32 moveEffect = gBattleMoves[move].effect; s32 moveType; - u16 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); - u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move); - u32 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; + u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); + u32 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move); + struct AiLogicData *aiData = AI_DATA; + u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); u32 i; - u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; + u32 weather; + u32 predictedMove = aiData->predictedMoves[battlerDef]; - SetTypeBeforeUsingMove(move, battlerAtk); - GET_MOVE_TYPE(move, moveType); - - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) return score; + SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); // check non-user target @@ -736,7 +709,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { // handle negative checks on non-user target // check powder moves - if (gBattleMoves[move].powderMove && !IsAffectedByPowder(battlerDef, AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerDef])) + if (gBattleMoves[move].powderMove && !IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])) { RETURN_SCORE_MINUS(20); } @@ -744,9 +717,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // check ground immunities if (moveType == TYPE_GROUND && !IsBattlerGrounded(battlerDef) - && ((AI_DATA->abilities[battlerDef] == ABILITY_LEVITATE - && DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_AIR_BALLOON + && ((aiData->abilities[battlerDef] == ABILITY_LEVITATE + && DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_AIR_BALLOON || (gStatuses3[battlerDef] & (STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS))) && move != MOVE_THOUSAND_ARROWS) { @@ -770,9 +743,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // target ability checks - if (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + if (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) { - switch (AI_DATA->abilities[battlerDef]) + switch (aiData->abilities[battlerDef]) { case ABILITY_MAGIC_GUARD: switch (moveEffect) @@ -876,7 +849,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case ABILITY_DEFIANT: case ABILITY_COMPETITIVE: - if (IsStatLoweringMoveEffect(moveEffect) && !IsTargetingPartner(battlerAtk, battlerDef)) + if (IsStatLoweringMoveEffect(moveEffect) && !IS_TARGETING_PARTNER(battlerAtk, battlerDef)) RETURN_SCORE_MINUS(8); break; case ABILITY_COMATOSE: @@ -888,8 +861,8 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) RETURN_SCORE_MINUS(10); break; case ABILITY_LEAF_GUARD: - if (AI_WeatherHasEffect() && (gBattleWeather & B_WEATHER_SUN) - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_UTILITY_UMBRELLA + if ((AI_GetWeather(aiData) & B_WEATHER_SUN) + && aiData->holdEffects[battlerDef] != HOLD_EFFECT_UTILITY_UMBRELLA && IsNonVolatileStatusMoveEffect(moveEffect)) RETURN_SCORE_MINUS(10); break; @@ -898,14 +871,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->abilities[BATTLE_PARTNER(battlerDef)]) + switch (aiData->abilities[BATTLE_PARTNER(battlerDef)]) { case ABILITY_LIGHTNING_ROD: - if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(move, AI_DATA->abilities[battlerAtk])) + if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(move, aiData->abilities[battlerAtk])) RETURN_SCORE_MINUS(20); break; case ABILITY_STORM_DRAIN: - if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(move, AI_DATA->abilities[battlerAtk])) + if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(move, aiData->abilities[battlerAtk])) RETURN_SCORE_MINUS(20); break; case ABILITY_MAGIC_BOUNCE: @@ -935,7 +908,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_DARK_TYPES >= GEN_7 - if (AI_DATA->abilities[battlerAtk] == ABILITY_PRANKSTER && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK) && IS_MOVE_STATUS(move) + if (aiData->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 @@ -967,29 +940,28 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // heal block check if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(battlerAtk, move)) return 0; // Can't even select heal blocked move + // primal weather check - if (WEATHER_HAS_EFFECT) + weather = AI_GetWeather(aiData); + if (weather & B_WEATHER_PRIMAL_ANY) { - if (gBattleWeather & B_WEATHER_PRIMAL_ANY) + switch (move) { - switch (move) - { - case MOVE_SUNNY_DAY: - case MOVE_RAIN_DANCE: - case MOVE_HAIL: - case MOVE_SANDSTORM: - RETURN_SCORE_MINUS(30); - } + case MOVE_SUNNY_DAY: + case MOVE_RAIN_DANCE: + case MOVE_HAIL: + case MOVE_SANDSTORM: + RETURN_SCORE_MINUS(30); } if (!IS_MOVE_STATUS(move)) { - if (gBattleWeather & B_WEATHER_SUN_PRIMAL) + if (weather & B_WEATHER_SUN_PRIMAL) { if (moveType == TYPE_WATER) RETURN_SCORE_MINUS(30); } - else if (gBattleWeather & B_WEATHER_RAIN_PRIMAL) + else if (weather & B_WEATHER_RAIN_PRIMAL) { if (moveType == TYPE_FIRE) RETURN_SCORE_MINUS(30); @@ -1004,7 +976,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->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanPutToSleep(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case EFFECT_EXPLOSION: @@ -1015,7 +987,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { score -= 10; } - else if (IsAbilityOnField(ABILITY_DAMP) && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + else if (IsAbilityOnField(ABILITY_DAMP) && !DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) { score -= 10; } @@ -1037,7 +1009,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ATTACK_UP: case EFFECT_ATTACK_UP_2: case EFFECT_ATTACK_UP_USER_ALLY: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; break; case EFFECT_STUFF_CHEEKS: @@ -1048,65 +1020,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->abilities[battlerAtk], STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, aiData->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->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->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->abilities[battlerAtk], STAT_SPDEF)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 10; break; case EFFECT_ACCURACY_UP: case EFFECT_ACCURACY_UP_2: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ACC)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ACC)) score -= 10; break; case EFFECT_EVASION_UP: case EFFECT_EVASION_UP_2: case EFFECT_MINIMIZE: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_EVASION)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_EVASION)) score -= 10; break; case EFFECT_COSMIC_POWER: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 8; break; case EFFECT_BULK_UP: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 8; break; case EFFECT_CALM_MIND: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 8; break; case EFFECT_DRAGON_DANCE: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPEED)) score -= 8; break; case EFFECT_COIL: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ACC)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ACC)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 6; break; case EFFECT_ATTACK_ACCURACY_UP: //hone claws - if (AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY) + if (aiData->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))) @@ -1124,7 +1096,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) else if (!HasMoveWithType(battlerAtk, TYPE_ELECTRIC)) score -= 10; #if B_CHARGE_SPDEF_RAISE >= GEN_5 - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 5; #endif break; @@ -1132,46 +1104,46 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_GEOMANCY: if (gBattleMons[battlerAtk].statStages[STAT_SPATK] >= MAX_STAT_STAGE || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPEED)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 6; break; case EFFECT_VICTORY_DANCE: if (gBattleMons[battlerAtk].statStages[STAT_ATK] >= MAX_STAT_STAGE || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPEED)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 6; break; case EFFECT_SHIFT_GEAR: - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPEED)) score -= 8; break; case EFFECT_SHELL_SMASH: - if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) + if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY) { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 8; } else { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 8; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPEED)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPEED)) score -= 6; } break; case EFFECT_GROWTH: case EFFECT_ATTACK_SPATK_UP: // work up - if ((!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) && !BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK)) + if ((!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK)) || (!HasDamagingMove(battlerAtk))) score -= 10; break; @@ -1180,30 +1152,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->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK))) + && (BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, aiData->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)))) + && aiData->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_CONTRARY + && (BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_ATK) + || BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPATK)))) { score -= 10; } } else if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS) && AI_IsBattlerGrounded(battlerAtk) - && (BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK)))) + && (BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK)))) { score -= 10; } break; case EFFECT_GEAR_UP: - if (AI_DATA->abilities[battlerAtk] == ABILITY_PLUS || AI_DATA->abilities[battlerAtk] == ABILITY_MINUS) + if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) { // same as growth, work up - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL)) score -= 8; break; } @@ -1214,13 +1186,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { - if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) + if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { - 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))) + if ((!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + && (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL))) score -= 10; } - else if (AI_DATA->abilities[battlerAtk] != ABILITY_PLUS && AI_DATA->abilities[battlerAtk] != ABILITY_MINUS) + else if (aiData->abilities[battlerAtk] != ABILITY_PLUS && aiData->abilities[battlerAtk] != ABILITY_MINUS) { score -= 10; // nor our or our partner's ability is plus/minus } @@ -1231,11 +1203,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_MAGNETIC_FLUX: - if (AI_DATA->abilities[battlerAtk] == ABILITY_PLUS || AI_DATA->abilities[battlerAtk] == ABILITY_MINUS) + if (aiData->abilities[battlerAtk] == ABILITY_PLUS || aiData->abilities[battlerAtk] == ABILITY_MINUS) { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 8; } else if (!isDoubleBattle) @@ -1245,14 +1217,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { - if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) + if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS) { - if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_DEF)) + if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_DEF)) score -= 10; - else if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) + else if (!BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) score -= 8; } - else if (AI_DATA->abilities[battlerAtk] != ABILITY_PLUS && AI_DATA->abilities[battlerAtk] != ABILITY_MINUS) + else if (aiData->abilities[battlerAtk] != ABILITY_PLUS && aiData->abilities[battlerAtk] != ABILITY_MINUS) { score -= 10; // nor our or our partner's ability is plus/minus } @@ -1261,49 +1233,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->abilities[battlerDef], STAT_ATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_HYPER_CUTTER) + else if (aiData->abilities[battlerDef] == ABILITY_HYPER_CUTTER) score -= 10; break; case EFFECT_DEFENSE_DOWN: case EFFECT_DEFENSE_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_DEF)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_DEF)) score -= 10; break; case EFFECT_SPEED_DOWN: case EFFECT_SPEED_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_SPEED)) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_SPEED_BOOST) + else if (aiData->abilities[battlerDef] == ABILITY_SPEED_BOOST) score -= 10; break; case EFFECT_SPECIAL_ATTACK_DOWN: case EFFECT_SPECIAL_ATTACK_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) //|| !HasMoveWithSplit(battlerDef, SPLIT_SPECIAL)) + if (!ShouldLowerStat(battlerDef, aiData->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->abilities[battlerDef], STAT_SPDEF)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_SPDEF)) score -= 10; break; case EFFECT_ACCURACY_DOWN: case EFFECT_ACCURACY_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ACC)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ACC)) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_KEEN_EYE) + else if (aiData->abilities[battlerDef] == ABILITY_KEEN_EYE) score -= 8; break; case EFFECT_EVASION_DOWN: case EFFECT_EVASION_DOWN_2: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_EVASION)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_EVASION)) score -= 10; break; case EFFECT_TICKLE: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_DEF)) + else if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_DEF)) score -= 8; break; case EFFECT_VENOM_DRENCH: @@ -1313,18 +1285,18 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_SPEED)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) + else if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_SPATK)) score -= 8; - else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) score -= 6; } break; case EFFECT_NOBLE_ROAR: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPATK)) + if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_SPATK)) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) score -= 8; break; case EFFECT_CAPTIVATE: @@ -1333,7 +1305,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; // other case EFFECT_HAZE: - if (PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) { score -= 10; // partner already using haze } @@ -1369,12 +1341,12 @@ 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->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) + if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) score -= 10; break; case EFFECT_COUNTER: case EFFECT_MIRROR_COAT: - if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) + if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) score--; if (predictedMove == MOVE_NONE || GetBattleMoveSplit(predictedMove) == SPLIT_STATUS || DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove)) @@ -1384,32 +1356,32 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ROAR: if (CountUsablePartyMons(battlerDef) == 0) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_SUCTION_CUPS) + else if (aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS) score -= 10; break; case EFFECT_TOXIC_THREAD: - if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_SPEED)) + if (!ShouldLowerStat(battlerDef, aiData->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->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case EFFECT_LIGHT_SCREEN: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_LIGHTSCREEN - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_REFLECT: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_REFLECT - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_AURORA_VEIL: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_AURORA_VEIL - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) - || !(gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) + || !(weather & (B_WEATHER_HAIL | B_WEATHER_SNOW))) score -= 10; break; case EFFECT_OHKO: @@ -1417,12 +1389,12 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE)) return 0; #endif - if (!ShouldTryOHKO(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move)) + if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move)) score -= 10; break; case EFFECT_MIST: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_MIST - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_FOCUS_ENERGY: @@ -1432,17 +1404,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->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (!AI_CanConfuse(battlerAtk, battlerDef, aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_PARALYZE: - if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case EFFECT_SUBSTITUTE: - if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || AI_DATA->abilities[battlerDef] == ABILITY_INFILTRATOR) + if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR) score -= 8; - else if (AI_DATA->hpPercents[battlerAtk] <= 25) + else if (aiData->hpPercents[battlerAtk] <= 25) score -= 10; #if B_SOUND_SUBSTITUTE >= GEN_6 else if (HasSoundMove(battlerDef)) @@ -1452,17 +1424,17 @@ 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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_LIQUID_OOZE) + else if (aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE) score -= 3; break; case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 #if B_MENTAL_HERB >= GEN_5 - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB + && aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB #endif - && !PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + && !PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first { @@ -1482,9 +1454,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 #if B_MENTAL_HERB >= GEN_5 - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB + && aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB #endif - && !DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + && !DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first { @@ -1507,7 +1479,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; // if mon will wake up, is not asleep, or is not comatose break; case EFFECT_MEAN_LOOK: - if (IsBattlerTrapped(battlerDef, TRUE) || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (IsBattlerTrapped(battlerDef, TRUE) || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_NIGHTMARE: @@ -1515,48 +1487,48 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; else if (!AI_IsBattlerAsleepOrComatose(battlerDef)) score -= 8; - else if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + else if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_CURSE: if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) { if (gBattleMons[battlerDef].status2 & STATUS2_CURSED - || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] <= 50) + else if (aiData->hpPercents[battlerAtk] <= 50) score -= 6; } else // regular curse { - if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) + if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_ATK) || !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; - else if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_DEF)) + else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_DEF)) score -= 8; } break; case EFFECT_SPIKES: if (gSideTimers[GetBattlerSide(battlerDef)].spikesAmount >= 3) score -= 10; - else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) //Only one mon needs to set up Stealth Rocks + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].toxicSpikesAmount == 1) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].stickyWebAmount) + else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) && gSideTimers[GetBattlerSide(battlerDef)].stickyWebAmount) score -= 10; // only one mon needs to set up Sticky Web break; case EFFECT_FORESIGHT: @@ -1564,81 +1536,81 @@ 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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 9; break; case EFFECT_PERISH_SONG: if (isDoubleBattle) { if (CountUsablePartyMons(battlerAtk) == 0 - && AI_DATA->abilities[battlerAtk] != ABILITY_SOUNDPROOF - && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_SOUNDPROOF + && aiData->abilities[battlerAtk] != ABILITY_SOUNDPROOF + && aiData->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_DATA->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF + else if ((!IsBattlerAlive(FOE(battlerAtk)) || aiData->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF || gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG) - && (!IsBattlerAlive(BATTLE_PARTNER(FOE(battlerAtk))) || AI_DATA->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_SOUNDPROOF + && (!IsBattlerAlive(BATTLE_PARTNER(FOE(battlerAtk))) || aiData->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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + else if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) { score -= 10; } } else { - if (CountUsablePartyMons(battlerAtk) == 0 && AI_DATA->abilities[battlerAtk] != ABILITY_SOUNDPROOF + if (CountUsablePartyMons(battlerAtk) == 0 && aiData->abilities[battlerAtk] != ABILITY_SOUNDPROOF && CountUsablePartyMons(battlerDef) >= 1) score -= 10; - if (gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG || AI_DATA->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF) + if (gStatuses3[FOE(battlerAtk)] & STATUS3_PERISH_SONG || aiData->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF) score -= 10; } break; case EFFECT_SANDSTORM: - if (gBattleWeather & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + if (weather & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), aiData->partnerMove)) score -= 8; break; case EFFECT_SUNNY_DAY: - if (gBattleWeather & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + if (weather & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), aiData->partnerMove)) score -= 8; break; case EFFECT_RAIN_DANCE: - if (gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + if (weather & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), aiData->partnerMove)) score -= 8; break; case EFFECT_HAIL: - if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + if (weather & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), aiData->partnerMove)) score -= 8; - else if (gBattleWeather & B_WEATHER_SNOW) + else if (weather & B_WEATHER_SNOW) score -= 2; // mainly to prevent looping between hail and snow break; case EFFECT_SNOWSCAPE: - if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) - || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove)) + if (weather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) + || PartnerMoveEffectIsWeather(BATTLE_PARTNER(battlerAtk), aiData->partnerMove)) score -= 8; - else if (gBattleWeather & B_WEATHER_HAIL) + else if (weather & B_WEATHER_HAIL) score -= 2; // mainly to prevent looping between hail and snow break; case EFFECT_ATTRACT: - if (!AI_CanBeInfatuated(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (!AI_CanBeInfatuated(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 10; break; case EFFECT_SAFEGUARD: if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_SAFEGUARD - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_MAGNITUDE: - if (AI_DATA->abilities[battlerDef] == ABILITY_LEVITATE) + if (aiData->abilities[battlerDef] == ABILITY_LEVITATE) score -= 10; break; case EFFECT_PARTING_SHOT: @@ -1665,9 +1637,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 6; break; case EFFECT_BELLY_DRUM: - if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) + if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] <= 60) + else if (aiData->hpPercents[battlerAtk] <= 60) score -= 10; break; case EFFECT_FUTURE_SIGHT: @@ -1701,28 +1673,28 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { if (AtMaxHp(battlerAtk)) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] >= 80) + else if (aiData->hpPercents[battlerAtk] >= 80) score -= 5; // do it if nothing better } break; case EFFECT_TORMENT: if (gBattleMons[battlerDef].status2 & STATUS2_TORMENT - || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) { score -= 10; break; } #if B_MENTAL_HERB >= GEN_5 - if (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_MENTAL_HERB) + if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_MENTAL_HERB) score -= 6; #endif break; case EFFECT_WILL_O_WISP: - if (!AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (!AI_CanBurn(battlerAtk, battlerDef, aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_MEMENTO: - if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; else if (gBattleMons[battlerDef].statStages[STAT_ATK] == MIN_STAT_STAGE && gBattleMons[battlerDef].statStages[STAT_SPATK] == MIN_STAT_STAGE) score -= 10; @@ -1731,14 +1703,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_HELPING_HAND: if (!isDoubleBattle || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove) - || (AI_DATA->partnerMove != MOVE_NONE && IS_MOVE_STATUS(AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) + || (aiData->partnerMove != MOVE_NONE && IS_MOVE_STATUS(aiData->partnerMove)) || *(gBattleStruct->monToSwitchIntoId + BATTLE_PARTNER(battlerAtk)) != PARTY_SIZE) //Partner is switching out. score -= 10; break; case EFFECT_TRICK: case EFFECT_KNOCK_OFF: - if (AI_DATA->abilities[battlerDef] == ABILITY_STICKY_HOLD) + if (aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD) score -= 10; break; case EFFECT_INGRAIN: @@ -1762,17 +1734,17 @@ 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->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY && !AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; else if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && !AI_CanBurn(battlerAtk, battlerDef, - AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; else if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && !AI_CanGiveFrostbite(battlerAtk, battlerDef, - AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; - else if (gBattleMons[battlerAtk].status1 & STATUS1_PARALYSIS && !AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + else if (gBattleMons[battlerAtk].status1 & STATUS1_PARALYSIS && !AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; - else if (gBattleMons[battlerAtk].status1 & STATUS1_SLEEP && !AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + else if (gBattleMons[battlerAtk].status1 & STATUS1_SLEEP && !AI_CanPutToSleep(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; else score -= 10; // attacker has no status to transmit @@ -1780,23 +1752,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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_WATER_SPORT: if (gFieldStatuses & STATUS_FIELD_WATERSPORT || gStatuses4[battlerAtk] & STATUS4_WATER_SPORT - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_ABSORB: - if (AI_DATA->abilities[battlerDef] == ABILITY_LIQUID_OOZE) + if (aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE) score -= 6; break; case EFFECT_STRENGTH_SAP: - if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) + if (aiData->abilities[battlerDef] == ABILITY_CONTRARY) score -= 10; - else if (!ShouldLowerStat(battlerDef, AI_DATA->abilities[battlerDef], STAT_ATK)) + else if (!ShouldLowerStat(battlerDef, aiData->abilities[battlerDef], STAT_ATK)) score -= 10; break; case EFFECT_COPYCAT: @@ -1808,19 +1780,19 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_AROMATIC_MIST: - if (!isDoubleBattle || gBattleMons[BATTLE_PARTNER(battlerAtk)].hp == 0 || !BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) + if (!isDoubleBattle || gBattleMons[BATTLE_PARTNER(battlerAtk)].hp == 0 || !BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF)) score -= 10; break; case EFFECT_BIDE: if (!HasDamagingMove(battlerDef) - || AI_DATA->hpPercents[battlerAtk] < 30 //Close to death + || aiData->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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; // don't scare away pokemon twice - else if (AI_DATA->hpPercents[battlerDef] < 10 && GetBattlerSecondaryDamage(battlerDef)) + else if (aiData->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; @@ -1831,7 +1803,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->abilities[battlerAtk])) + if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])) score -= 10; //fallthrough case EFFECT_RESTORE_HP: @@ -1839,17 +1811,17 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ROOST: if (AtMaxHp(battlerAtk)) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] >= 90) + else if (aiData->hpPercents[battlerAtk] >= 90) score -= 9; //No point in healing, but should at least do it if nothing better break; case EFFECT_MORNING_SUN: case EFFECT_SYNTHESIS: case EFFECT_MOONLIGHT: - if (AI_WeatherHasEffect() && (gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_HAIL))) + if ((AI_GetWeather(aiData) & (B_WEATHER_RAIN | B_WEATHER_SANDSTORM | B_WEATHER_HAIL))) score -= 3; else if (AtMaxHp(battlerAtk)) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] >= 90) + else if (aiData->hpPercents[battlerAtk] >= 90) score -= 9; //No point in healing, but should at least do it if nothing better break; case EFFECT_PURIFY: @@ -1859,21 +1831,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; //Always heal your ally else if (AtMaxHp(battlerAtk)) score -= 10; - else if (AI_DATA->hpPercents[battlerAtk] >= 90) + else if (aiData->hpPercents[battlerAtk] >= 90) score -= 8; //No point in healing, but should at least do it if nothing better break; case EFFECT_SUPER_FANG: - if (AI_DATA->hpPercents[battlerDef] < 50) + if (aiData->hpPercents[battlerDef] < 50) score -= 4; break; case EFFECT_RECOIL_IF_MISS: - if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_GetMoveAccuracy(battlerAtk, battlerDef, move) < 75) + if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_GetMoveAccuracy(battlerAtk, battlerDef, move) < 75) score -= 6; break; case EFFECT_RECOIL_25: - if (AI_DATA->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) + if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && aiData->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 4); + u32 recoilDmg = max(1, aiData->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 4); if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex)) score -= 10; break; @@ -1881,18 +1853,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->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) + if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && aiData->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 3); + u32 recoilDmg = max(1, aiData->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->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->abilities[battlerAtk] != ABILITY_ROCK_HEAD) + if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && aiData->abilities[battlerAtk] != ABILITY_ROCK_HEAD) { - u32 recoilDmg = max(1, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 2); + u32 recoilDmg = max(1, aiData->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] / 2); if (!ShouldUseRecoilMove(battlerAtk, battlerDef, recoilDmg, AI_THINKING_STRUCT->movesetIndex)) score -= 10; break; @@ -1900,11 +1872,11 @@ 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->abilities[battlerAtk], move) && AI_DATA->abilities[battlerDef] == ABILITY_OWN_TEMPO) + || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->abilities[battlerDef] == ABILITY_OWN_TEMPO) || (IsBattlerGrounded(battlerDef) && AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) || (DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) && ((gBattleMons[BATTLE_PARTNER(battlerDef)].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_OWN_TEMPO) + || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->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)))) { @@ -1917,13 +1889,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_TWO_TURNS_ATTACK: - if (AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) + if (aiData->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) score -= 6; break; case EFFECT_RECHARGE: - if (AI_DATA->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) + if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) score -= 10; - else if (AI_DATA->abilities[battlerAtk] != ABILITY_TRUANT + else if (aiData->abilities[battlerAtk] != ABILITY_TRUANT && !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) score -= 2; break; @@ -1954,15 +1926,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->abilities[battlerAtk] == ABILITY_NO_GUARD - || AI_DATA->abilities[battlerDef] == ABILITY_NO_GUARD - || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || aiData->abilities[battlerAtk] == ABILITY_NO_GUARD + || aiData->abilities[battlerDef] == ABILITY_NO_GUARD + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_LASER_FOCUS: if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS) score -= 10; - else if (AI_DATA->abilities[battlerDef] == ABILITY_SHELL_ARMOR || AI_DATA->abilities[battlerDef] == ABILITY_BATTLE_ARMOR) + else if (aiData->abilities[battlerDef] == ABILITY_SHELL_ARMOR || aiData->abilities[battlerDef] == ABILITY_BATTLE_ARMOR) score -= 8; break; case EFFECT_SKETCH: @@ -1977,7 +1949,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // TODO break; case EFFECT_HEAL_BELL: - if (!AnyPartyMemberStatused(battlerAtk, gBattleMoves[move].soundMove) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (!AnyPartyMemberStatused(battlerAtk, gBattleMoves[move].soundMove) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_HIT_PREVENT_ESCAPE: @@ -2011,7 +1983,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (decreased) break; - if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])) + if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])) { score -= 10; break; @@ -2022,8 +1994,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->abilities[battlerDef] != ABILITY_MOXIE - && AI_DATA->abilities[battlerDef] != ABILITY_BEAST_BOOST) + && aiData->abilities[battlerDef] != ABILITY_MOXIE + && aiData->abilities[battlerDef] != ABILITY_BEAST_BOOST) { score -= 10; //Don't protect if you're going to faint after protecting } @@ -2057,7 +2029,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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 9; break; case EFFECT_BURN_UP: @@ -2074,7 +2046,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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) { score -= 10; //Only need one hazards removal break; @@ -2089,8 +2061,8 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { - if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // partner is going to set up hazards - && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk, AI_DATA->partnerMove) == AI_IS_FASTER) // partner is going to set up before the potential Defog + if (IsHazardMoveEffect(gBattleMoves[aiData->partnerMove].effect) // partner is going to set up hazards + && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk, aiData->partnerMove) == AI_IS_FASTER) // partner is going to set up before the potential Defog { score -= 10; break; // Don't use Defog if partner is going to set up hazards @@ -2099,7 +2071,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->abilities[battlerDef] == ABILITY_CONTRARY) && !IsTargetingPartner(battlerAtk, battlerDef))) // don't want to raise target stats unless its your partner + || ((aiData->abilities[battlerDef] == ABILITY_CONTRARY) && !IS_TARGETING_PARTNER(battlerAtk, battlerDef))) // don't want to raise target stats unless its your partner score -= 10; break; @@ -2120,8 +2092,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->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB - || (AI_WeatherHasEffect() && gBattleWeather & B_WEATHER_SUN && AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB + || ((AI_GetWeather(aiData) & B_WEATHER_SUN) && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) break; if (CanTargetFaintAi(battlerDef, battlerAtk)) //Attacker can be knocked out score -= 4; @@ -2132,39 +2104,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->abilities[battlerAtk]) + if (BattlerWillFaintFromWeather(battlerAtk, aiData->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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; else if (IsPartyFullyHealedExceptBattler(battlerAtk)) score -= 10; break; case EFFECT_FINAL_GAMBIT: - if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (CountUsablePartyMons(battlerAtk) == 0 || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_NATURE_POWER: return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(), score); case EFFECT_TAUNT: if (gDisableStructs[battlerDef].tauntTimer > 0 - || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score--; break; case EFFECT_BESTOW: - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_NONE + if (aiData->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->abilities[battlerAtk] == AI_DATA->abilities[battlerDef] - || AI_DATA->abilities[battlerDef] == ABILITY_NONE - || IsRolePlayBannedAbilityAtk(AI_DATA->abilities[battlerAtk]) - || IsRolePlayBannedAbility(AI_DATA->abilities[battlerDef])) + if (aiData->abilities[battlerAtk] == aiData->abilities[battlerDef] + || aiData->abilities[battlerDef] == ABILITY_NONE + || IsRolePlayBannedAbilityAtk(aiData->abilities[battlerAtk]) + || IsRolePlayBannedAbility(aiData->abilities[battlerDef])) score -= 10; - else if (IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 5)) + else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 5)) score -= 4; break; case EFFECT_WISH: @@ -2186,68 +2158,68 @@ 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->abilities[battlerDef], move, AI_DATA->partnerMove)) + else if (!AI_CanPutToSleep(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case EFFECT_SKILL_SWAP: - if (AI_DATA->abilities[battlerAtk] == ABILITY_NONE || AI_DATA->abilities[battlerDef] == ABILITY_NONE - || IsSkillSwapBannedAbility(AI_DATA->abilities[battlerAtk]) || IsSkillSwapBannedAbility(AI_DATA->abilities[battlerDef]) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) + if (aiData->abilities[battlerAtk] == ABILITY_NONE || aiData->abilities[battlerDef] == ABILITY_NONE + || IsSkillSwapBannedAbility(aiData->abilities[battlerAtk]) || IsSkillSwapBannedAbility(aiData->abilities[battlerDef]) + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) score -= 10; break; case EFFECT_WORRY_SEED: - if (AI_DATA->abilities[battlerDef] == ABILITY_INSOMNIA - || IsWorrySeedBannedAbility(AI_DATA->abilities[battlerDef]) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) + if (aiData->abilities[battlerDef] == ABILITY_INSOMNIA + || IsWorrySeedBannedAbility(aiData->abilities[battlerDef]) + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) score -= 10; break; case EFFECT_GASTRO_ACID: if (gStatuses3[battlerDef] & STATUS3_GASTRO_ACID - || IsGastroAcidBannedAbility(AI_DATA->abilities[battlerDef])) + || IsGastroAcidBannedAbility(aiData->abilities[battlerDef])) score -= 10; break; case EFFECT_ENTRAINMENT: - if (AI_DATA->abilities[battlerAtk] == ABILITY_NONE - || IsEntrainmentBannedAbilityAttacker(AI_DATA->abilities[battlerAtk]) - || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->abilities[battlerDef]) - || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ABILITY_SHIELD) + if (aiData->abilities[battlerAtk] == ABILITY_NONE + || IsEntrainmentBannedAbilityAttacker(aiData->abilities[battlerAtk]) + || IsEntrainmentTargetOrSimpleBeamBannedAbility(aiData->abilities[battlerDef]) + || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ABILITY_SHIELD) score -= 10; break; case EFFECT_CORE_ENFORCER: break; case EFFECT_SIMPLE_BEAM: - if (AI_DATA->abilities[battlerDef] == ABILITY_SIMPLE - || IsEntrainmentTargetOrSimpleBeamBannedAbility(AI_DATA->abilities[battlerDef]) - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) + if (aiData->abilities[battlerDef] == ABILITY_SIMPLE + || IsEntrainmentTargetOrSimpleBeamBannedAbility(aiData->abilities[battlerDef]) + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD) score -= 10; break; case EFFECT_SNATCH: if (!HasSnatchAffectedMove(battlerDef) - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_POWER_TRICK: - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) score -= 10; else if (gBattleMons[battlerAtk].defense >= gBattleMons[battlerAtk].attack && !HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) score -= 10; break; case EFFECT_POWER_SWAP: // Don't use if attacker's stat stages are higher than opponents - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) score -= 10; else if (gBattleMons[battlerAtk].statStages[STAT_ATK] >= gBattleMons[battlerDef].statStages[STAT_ATK] && gBattleMons[battlerAtk].statStages[STAT_SPATK] >= gBattleMons[battlerDef].statStages[STAT_SPATK]) score -= 10; break; case EFFECT_GUARD_SWAP: // Don't use if attacker's stat stages are higher than opponents - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) score -= 10; else if (gBattleMons[battlerAtk].statStages[STAT_DEF] >= gBattleMons[battlerDef].statStages[STAT_DEF] && gBattleMons[battlerAtk].statStages[STAT_SPDEF] >= gBattleMons[battlerDef].statStages[STAT_SPDEF]) score -= 10; break; case EFFECT_SPEED_SWAP: - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { score -= 10; } @@ -2260,7 +2232,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_HEART_SWAP: - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { score -= 10; } @@ -2277,16 +2249,16 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_POWER_SPLIT: - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { score -= 10; } else { - u8 atkAttack = gBattleMons[battlerAtk].attack; - u8 defAttack = gBattleMons[battlerDef].attack; - u8 atkSpAttack = gBattleMons[battlerAtk].spAttack; - u8 defSpAttack = gBattleMons[battlerDef].spAttack; + u32 atkAttack = gBattleMons[battlerAtk].attack; + u32 defAttack = gBattleMons[battlerDef].attack; + u32 atkSpAttack = gBattleMons[battlerAtk].spAttack; + u32 defSpAttack = gBattleMons[battlerDef].spAttack; if (atkAttack + atkSpAttack >= defAttack + defSpAttack) // Combined attacker stats are > than combined target stats score -= 10; @@ -2294,16 +2266,16 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_GUARD_SPLIT: - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { score -= 10; } else { - u8 atkDefense = gBattleMons[battlerAtk].defense; - u8 defDefense = gBattleMons[battlerDef].defense; - u8 atkSpDefense = gBattleMons[battlerAtk].spDefense; - u8 defSpDefense = gBattleMons[battlerDef].spDefense; + u32 atkDefense = gBattleMons[battlerAtk].defense; + u32 defDefense = gBattleMons[battlerDef].defense; + u32 atkSpDefense = gBattleMons[battlerAtk].spDefense; + u32 defSpDefense = gBattleMons[battlerDef].spDefense; if (atkDefense + atkSpDefense >= defDefense + defSpDefense) //Combined attacker stats are > than combined target stats score -= 10; @@ -2324,33 +2296,33 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_NATURAL_GIFT: - if (AI_DATA->abilities[battlerAtk] == ABILITY_KLUTZ + if (aiData->abilities[battlerAtk] == ABILITY_KLUTZ || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || GetPocketByItemId(gBattleMons[battlerAtk].item) != POCKET_BERRIES) score -= 10; break; case EFFECT_GRASSY_TERRAIN: - if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) || gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) score -= 10; break; case EFFECT_ELECTRIC_TERRAIN: - if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) || gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) score -= 10; break; case EFFECT_PSYCHIC_TERRAIN: - if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) || gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) score -= 10; break; case EFFECT_MISTY_TERRAIN: - if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) || gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + if (PartnerMoveEffectIsTerrain(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) || gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) score -= 10; break; case EFFECT_PLEDGE: 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 (aiData->partnerMove != MOVE_NONE + && gBattleMoves[aiData->partnerMove].effect == EFFECT_PLEDGE + && move != aiData->partnerMove) // Different pledge moves { 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 @@ -2359,7 +2331,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_TRICK_ROOM: - if (PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_TRICK_ROOM)) + if (PartnerMoveIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, MOVE_TRICK_ROOM)) { score -= 10; } @@ -2375,23 +2347,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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_WONDER_ROOM: - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_GRAVITY: if ((gFieldStatuses & STATUS_FIELD_GRAVITY && !IS_BATTLER_OF_TYPE(battlerAtk, TYPE_FLYING) - && AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_AIR_BALLOON) // Should revert Gravity in this case - || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_AIR_BALLOON) // Should revert Gravity in this case + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_ION_DELUGE: if (gFieldStatuses & STATUS_FIELD_ION_DELUGE - || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_FLING: @@ -2406,19 +2378,19 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) switch (effect) { case MOVE_EFFECT_BURN: - if (!AI_CanBurn(battlerAtk, battlerDef, BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if (!AI_CanBurn(battlerAtk, battlerDef, BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case MOVE_EFFECT_PARALYSIS: - if (!AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case MOVE_EFFECT_POISON: - if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case MOVE_EFFECT_TOXIC: - if (!AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) + if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove)) score -= 10; break; case MOVE_EFFECT_FREEZE: @@ -2430,34 +2402,34 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_EMBARGO: - if (AI_DATA->abilities[battlerDef] == ABILITY_KLUTZ + if (aiData->abilities[battlerDef] == ABILITY_KLUTZ || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || gDisableStructs[battlerDef].embargoTimer != 0 - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_POWDER: if (!HasMoveWithType(battlerDef, TYPE_FIRE) - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_TELEKINESIS: if (gStatuses3[battlerDef] & (STATUS3_TELEKINESIS | STATUS3_ROOTED | STATUS3_SMACKED_DOWN) || gFieldStatuses & STATUS_FIELD_GRAVITY - || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_IRON_BALL + || aiData->holdEffects[battlerDef] == HOLD_EFFECT_IRON_BALL || IsTelekinesisBannedSpecies(gBattleMons[battlerDef].species) - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_THROAT_CHOP: break; case EFFECT_HEAL_BLOCK: if (gDisableStructs[battlerDef].healBlockTimer != 0 - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_SOAK: - if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove) + if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || (gBattleMons[battlerDef].type1 == TYPE_WATER && gBattleMons[battlerDef].type2 == TYPE_WATER && gBattleMons[battlerDef].type3 == TYPE_MYSTERY)) @@ -2467,24 +2439,24 @@ 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(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case MOVE_FORESTS_CURSE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; } break; case EFFECT_HEAL_PULSE: // and floral healing - if (!IsTargetingPartner(battlerAtk, battlerDef)) // Don't heal enemies + if (!IS_TARGETING_PARTNER(battlerAtk, battlerDef)) // Don't heal enemies { score -= 10; break; } // fallthrough case EFFECT_HIT_ENEMY_HEAL_ALLY: // pollen puff - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK) return 0; @@ -2497,17 +2469,17 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ELECTRIFY: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER //|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_TOPSY_TURVY: - if (!IsTargetingPartner(battlerAtk, battlerDef)) + if (!IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { - u8 targetPositiveStages = CountPositiveStatStages(battlerDef); - u8 targetNegativeStages = CountNegativeStatStages(battlerDef); + u32 targetPositiveStages = CountPositiveStatStages(battlerDef); + u32 targetNegativeStages = CountNegativeStatStages(battlerDef); if (targetPositiveStages == 0 //No good stat changes to make bad - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; else if (targetNegativeStages < targetPositiveStages) @@ -2515,7 +2487,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(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + if ((gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_DO_NOTHING: @@ -2536,13 +2508,13 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || IsZMove(instructedMove) || (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF) || gBattleMons[battlerDef].status2 & STATUS2_MULTIPLETURNS - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) { score -= 10; } else if (isDoubleBattle) { - if (!IsTargetingPartner(battlerAtk, battlerDef)) + if (!IS_TARGETING_PARTNER(battlerAtk, battlerDef)) score -= 10; } else @@ -2563,14 +2535,14 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_QUASH: if (!isDoubleBattle || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_AFTER_YOU: - if (!IsTargetingPartner(battlerAtk, battlerDef) + if (!IS_TARGETING_PARTNER(battlerAtk, battlerDef) || !isDoubleBattle || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER - || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, AI_DATA->partnerMove)) + || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) score -= 10; break; case EFFECT_SUCKER_PUNCH: @@ -2582,19 +2554,19 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TAILWIND: if (gSideTimers[GetBattlerSide(battlerAtk)].tailwindTimer != 0 - || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_TAILWIND) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), aiData->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[GetBattlerSide(battlerAtk)].luckyChantTimer != 0 - || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 10; break; case EFFECT_MAGNET_RISE: if (gFieldStatuses & STATUS_FIELD_GRAVITY || gDisableStructs[battlerAtk].magnetRiseTimer != 0 - || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_IRON_BALL + || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_IRON_BALL || gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_MAGNET_RISE | STATUS3_SMACKED_DOWN) || !IsBattlerGrounded(battlerAtk)) score -= 10; @@ -2609,7 +2581,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->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET + if (aiData->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)) @@ -2620,16 +2592,16 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ERUPTION: if (effectiveness <= AI_EFFECTIVENESS_x0_5) score--; - if (AI_DATA->hpPercents[battlerDef] < 50) + if (aiData->hpPercents[battlerDef] < 50) score--; break; case EFFECT_VITAL_THROW: - if (WillAIStrikeFirst() && AI_DATA->hpPercents[battlerAtk] < 40) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move) && aiData->hpPercents[battlerAtk] < 40) score--; // don't want to move last break; case EFFECT_FLAIL: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER // Opponent should go first - || AI_DATA->hpPercents[battlerAtk] > 50) + || aiData->hpPercents[battlerAtk] > 50) score -= 4; break; //TODO @@ -2642,7 +2614,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SKY_DROP: if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_FLYING)) score -= 10; - if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk]) + if (BattlerWillFaintFromWeather(battlerAtk, aiData->abilities[battlerAtk]) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || GetBattlerWeight(battlerDef) >= 2000) //200.0 kg score -= 10; @@ -2677,11 +2649,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TAKE_HEART: if ((!(gBattleMons[battlerAtk].status1 & STATUS1_ANY) - || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_JUNGLE_HEALING) - || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_HEAL_BELL) - || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove, MOVE_AROMATHERAPY)) - && !BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) - && !BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, MOVE_JUNGLE_HEALING) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, MOVE_HEAL_BELL) + || PartnerMoveIs(BATTLE_PARTNER(battlerAtk), aiData->partnerMove, MOVE_AROMATHERAPY)) + && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK) + && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score -= 10; break; case EFFECT_PLACEHOLDER: @@ -2694,18 +2666,22 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) return score; } -static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - if (IsTargetingPartner(battlerAtk, battlerDef)) + u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex; + bool32 aiFaster; + + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) return score; if (gBattleMoves[move].power == 0) return score; // can't make anything faint with no power - if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION) + aiFaster = AI_STRIKES_FIRST(battlerAtk, battlerDef, move); + if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION) { // this move can faint the target - if (WillAIStrikeFirst() || GetMovePriority(battlerAtk, move) > 0) + if (aiFaster || GetMovePriority(battlerAtk, move) > 0) score += 4; // we go first or we're using priority move else score += 2; @@ -2716,10 +2692,10 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gBattleMoves[move].highCritRatio) score += 2; // crit makes it more likely to make them faint - if (GetMoveDamageResult(move) == MOVE_POWER_OTHER) + if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) == MOVE_POWER_OTHER) score--; - switch (AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]) + switch (AI_DATA->effectiveness[battlerAtk][battlerDef][movesetIndex]) { case AI_EFFECTIVENESS_x8: score += 8; @@ -2737,9 +2713,9 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } //AI_TryToFaint_CheckIfDanger - if (!WillAIStrikeFirst() && CanTargetFaintAi(battlerDef, battlerAtk)) + if (!aiFaster && CanTargetFaintAi(battlerDef, battlerAtk)) { // AI_TryToFaint_Danger - if (GetMoveDamageResult(move) != MOVE_POWER_BEST) + if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) != MOVE_POWER_BEST) score--; else score++; @@ -2749,28 +2725,29 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // double battle logic -static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { // move data - u8 moveType = gBattleMoves[move].type; - u16 effect = gBattleMoves[move].effect; - u16 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); + u32 moveType = gBattleMoves[move].type; + u32 effect = gBattleMoves[move].effect; + u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); // ally data - 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->abilities[battlerAtk]) < 0); + u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk); + struct AiLogicData *aiData = AI_DATA; + u32 atkPartnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)]; + u32 atkPartnerHoldEffect = aiData->holdEffects[BATTLE_PARTNER(battlerAtk)]; + bool32 partnerProtecting = (gBattleMoves[aiData->partnerMove].effect == EFFECT_PROTECT); + bool32 attackerHasBadAbility = (GetAbilityRating(aiData->abilities[battlerAtk]) < 0); bool32 partnerHasBadAbility = (GetAbilityRating(atkPartnerAbility) < 0); - u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; + u32 predictedMove = aiData->predictedMoves[battlerDef]; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); // check what effect partner is using - if (AI_DATA->partnerMove != 0) + if (aiData->partnerMove != 0) { - switch (gBattleMoves[AI_DATA->partnerMove].effect) + switch (gBattleMoves[aiData->partnerMove].effect) { case EFFECT_HELPING_HAND: if (!IS_MOVE_STATUS(move)) @@ -2785,7 +2762,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->abilities[battlerAtk] == ABILITY_ANGER_POINT) + if (aiData->abilities[battlerAtk] == ABILITY_ANGER_POINT) { if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER) // Partner moving first { @@ -2801,16 +2778,15 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } } // check partner move effect - // consider our move effect relative to partner state switch (effect) { case EFFECT_HELPING_HAND: - if (AI_DATA->partnerMove != 0 && !HasDamagingMove(battlerAtkPartner)) + if (aiData->partnerMove != 0 && !HasDamagingMove(battlerAtkPartner)) score -= 5; break; case EFFECT_PERISH_SONG: - if (AI_DATA->partnerMove != 0 && HasTrappingMoveEffect(battlerAtkPartner)) + if (aiData->partnerMove != 0 && HasTrappingMoveEffect(battlerAtkPartner)) score++; break; case EFFECT_MAGNET_RISE: @@ -2823,7 +2799,6 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; } // our effect relative to partner - // consider global move effects switch (effect) { @@ -2861,14 +2836,13 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; } // global move effect check - // check specific target - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { - if (GetMoveDamageResult(move) == MOVE_POWER_OTHER) + if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_OTHER) { // partner ability checks - if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move)) { switch (atkPartnerAbility) { @@ -2908,7 +2882,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case ABILITY_WATER_COMPACTION: - if (moveType == TYPE_WATER && GetMoveDamageResult(move) == MOVE_POWER_WEAK) + if (moveType == TYPE_WATER && GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_WEAK) { RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage } @@ -3016,14 +2990,14 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SKILL_SWAP: - if (AI_DATA->abilities[battlerAtk] != AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] && !attackerHasBadAbility) + if (aiData->abilities[battlerAtk] != aiData->abilities[BATTLE_PARTNER(battlerAtk)] && !attackerHasBadAbility) { - if (AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_TRUANT) + if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_TRUANT) { RETURN_SCORE_PLUS(10); } - 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)])) + else if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES + && HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, aiData->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, aiData->holdEffects[FOE(battlerAtkPartner)])) { RETURN_SCORE_PLUS(3); } @@ -3044,7 +3018,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_ENTRAINMENT: - if (partnerHasBadAbility && IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 0)) + if (partnerHasBadAbility && IsAbilityOfRating(aiData->abilities[battlerAtk], 0)) { RETURN_SCORE_PLUS(1); } @@ -3062,7 +3036,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { u16 instructedMove; if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_FASTER) - instructedMove = AI_DATA->partnerMove; + instructedMove = aiData->partnerMove; else instructedMove = gLastMoves[battlerAtkPartner]; @@ -3075,10 +3049,10 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_AFTER_YOU: - if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), AI_DATA->partnerMove) == AI_IS_SLOWER // Opponent mon 1 goes before partner - || AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), AI_DATA->partnerMove) == AI_IS_SLOWER) // Opponent mon 2 goes before partner + if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), aiData->partnerMove) == AI_IS_SLOWER // Opponent mon 1 goes before partner + || AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), aiData->partnerMove) == AI_IS_SLOWER) // Opponent mon 2 goes before partner { - if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT) + if (gBattleMoves[aiData->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[aiData->partnerMove].effect == EFFECT_MIRROR_COAT) break; // These moves need to go last RETURN_SCORE_PLUS(1); } @@ -3101,9 +3075,9 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) switch (effect) { case EFFECT_SKILL_SWAP: - if (AI_DATA->abilities[battlerAtk] == ABILITY_TRUANT) + if (aiData->abilities[battlerAtk] == ABILITY_TRUANT) score += 5; - else if (IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 0) || IsAbilityOfRating(AI_DATA->abilities[battlerDef], 10)) + else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 0) || IsAbilityOfRating(aiData->abilities[battlerDef], 10)) score += 2; // we want to transfer our bad ability or take their awesome ability break; case EFFECT_EARTHQUAKE: @@ -3111,7 +3085,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (!IsBattlerGrounded(battlerAtkPartner) || (IsBattlerGrounded(battlerAtkPartner) && AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER - && IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect))) + && IsUngroundingEffect(gBattleMoves[aiData->partnerMove].effect))) score += 2; else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE) || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC) @@ -3129,7 +3103,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) return score; } -static bool32 IsPinchBerryItemEffect(u16 holdEffect) +static bool32 IsPinchBerryItemEffect(u32 holdEffect) { switch (holdEffect) { @@ -3148,7 +3122,7 @@ static bool32 IsPinchBerryItemEffect(u16 holdEffect) return FALSE; } -static u32 GetAIMostDamagingMoveId(u8 battlerAtk, u8 battlerDef) +static u32 GetAIMostDamagingMoveId(u32 battlerAtk, u32 battlerDef) { u32 i, id = 0; u32 mostDmg = 0; @@ -3162,20 +3136,22 @@ static u32 GetAIMostDamagingMoveId(u8 battlerAtk, u8 battlerDef) } // AI_FLAG_CHECK_VIABILITY - a weird mix of increasing and decreasing scores -static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { // move data - u16 moveEffect = gBattleMoves[move].effect; - u32 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; - u8 atkPriority = GetMovePriority(battlerAtk, move); - u16 predictedMove = AI_DATA->predictedMoves[battlerDef]; + u32 moveEffect = gBattleMoves[move].effect; + struct AiLogicData *aiData = AI_DATA; + u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; + s8 atkPriority = GetMovePriority(battlerAtk, move); + u32 predictedMove = aiData->predictedMoves[battlerDef]; + u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove); bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); u32 i; // We only check for moves that have a 20% chance or more for their secondary effect to happen because moves with a smaller chance are rather worthless. We don't want the AI to use those. - bool32 sereneGraceBoost = (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && (gBattleMoves[move].secondaryEffectChance >= 20 && gBattleMoves[move].secondaryEffectChance < 100)); + bool32 sereneGraceBoost = (aiData->abilities[battlerAtk] == ABILITY_SERENE_GRACE && (gBattleMoves[move].secondaryEffectChance >= 20 && gBattleMoves[move].secondaryEffectChance < 100)); // Targeting partner, check benefits of doing that instead - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) return score; // check always hits @@ -3185,7 +3161,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gBattleMons[battlerDef].statStages[STAT_EVASION] > 6 || gBattleMons[battlerAtk].statStages[STAT_ACC] < 6) { u32 mostDmgMoveId = GetAIMostDamagingMoveId(battlerAtk, battlerDef); - u32 *dmgs = AI_DATA->simulatedDmg[battlerAtk][battlerDef]; + u32 *dmgs = aiData->simulatedDmg[battlerAtk][battlerDef]; if (GetNoOfHitsToKO(dmgs[mostDmgMoveId], gBattleMons[battlerDef].hp) == GetNoOfHitsToKO(dmgs[AI_THINKING_STRUCT->movesetIndex], gBattleMons[battlerDef].hp)) score++; } @@ -3200,7 +3176,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; // check already dead - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) + if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && CanTargetFaintAi(battlerAtk, battlerDef) && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent should go first { @@ -3211,7 +3187,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // check damage - if (gBattleMoves[move].power != 0 && GetMoveDamageResult(move) == MOVE_POWER_WEAK) + if (gBattleMoves[move].power != 0 && GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_WEAK) score--; // check status move preference @@ -3225,7 +3201,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // check burn if (gBattleMons[battlerAtk].status1 & STATUS1_BURN) { - switch (AI_DATA->abilities[battlerAtk]) + switch (aiData->abilities[battlerAtk]) { case ABILITY_GUTS: break; @@ -3244,7 +3220,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // check frostbite if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE) { - switch (AI_DATA->abilities[battlerAtk]) + switch (aiData->abilities[battlerAtk]) { case ABILITY_GUTS: break; @@ -3261,7 +3237,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // attacker ability checks - switch (AI_DATA->abilities[battlerAtk]) + switch (aiData->abilities[battlerAtk]) { case ABILITY_MOXIE: case ABILITY_BEAST_BOOST: @@ -3288,7 +3264,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->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score++; if (effectiveness <= AI_EFFECTIVENESS_x0_5 && AI_RandLessThan(50)) score -= 3; @@ -3297,7 +3273,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 (AI_DATA->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128)) + if (aiData->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128)) score++; } break; @@ -3316,7 +3292,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < 9) { - if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) + if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) { score += 2; break; @@ -3333,16 +3309,16 @@ 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 (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) + if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) score += 2; - else if (AI_DATA->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) + else if (aiData->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) break; - else if (AI_DATA->hpPercents[battlerAtk] < 40) + else if (aiData->hpPercents[battlerAtk] < 40) score -= 2; break; case EFFECT_SPEED_UP: case EFFECT_SPEED_UP_2: - if (!WillAIStrikeFirst()) + if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) { if (!AI_RandLessThan(70)) score += 3; @@ -3362,7 +3338,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < 9) { - if (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) + if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) { score += 2; break; @@ -3378,29 +3354,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 (AI_DATA->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) + if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128)) score += 2; - else if (AI_DATA->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) + else if (aiData->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200)) break; - else if (AI_DATA->hpPercents[battlerAtk] < 40) + else if (aiData->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 (AI_DATA->hpPercents[battlerAtk] <= 70) + else if (aiData->hpPercents[battlerAtk] <= 70) score -= 2; else score++; break; case EFFECT_EVASION_UP: case EFFECT_EVASION_UP_2: - if (AI_DATA->hpPercents[battlerAtk] > 90 && !AI_RandLessThan(100)) + if (aiData->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) && AI_DATA->hpPercents[battlerAtk] >= 50 && !AI_RandLessThan(80)) + if ((gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) && aiData->hpPercents[battlerAtk] >= 50 && !AI_RandLessThan(80)) score += 3; if (gStatuses3[battlerDef] & STATUS3_LEECHSEED && !AI_RandLessThan(70)) score += 3; @@ -3408,9 +3384,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 (AI_DATA->hpPercents[battlerAtk] < 70 || gBattleMons[battlerAtk].statStages[STAT_EVASION] == DEFAULT_STAT_STAGE) + if (aiData->hpPercents[battlerAtk] < 70 || gBattleMons[battlerAtk].statStages[STAT_EVASION] == DEFAULT_STAT_STAGE) break; - else if (AI_DATA->hpPercents[battlerAtk] < 40 || AI_DATA->hpPercents[battlerDef] < 40) + else if (aiData->hpPercents[battlerAtk] < 40 || aiData->hpPercents[battlerDef] < 40) score -= 2; else if (!AI_RandLessThan(70)) score -= 2; @@ -3418,61 +3394,61 @@ 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->abilities[battlerDef])) + if (!ShouldLowerAttack(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; if (gBattleMons[battlerDef].statStages[STAT_ATK] < DEFAULT_STAT_STAGE) score--; - else if (AI_DATA->hpPercents[battlerAtk] <= 90) + else if (aiData->hpPercents[battlerAtk] <= 90) score--; if (gBattleMons[battlerDef].statStages[STAT_ATK] > 3 && !AI_RandLessThan(50)) score -= 2; - else if (AI_DATA->hpPercents[battlerDef] < 70) + else if (aiData->hpPercents[battlerDef] < 70) score -= 2; break; case EFFECT_DEFENSE_DOWN: case EFFECT_DEFENSE_DOWN_2: - if (!ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (!ShouldLowerDefense(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; - if ((AI_DATA->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_DEF] <= 3 && !AI_RandLessThan(50))) + if ((aiData->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_DEF] <= 3 && !AI_RandLessThan(50))) score -= 2; - if (AI_DATA->hpPercents[battlerDef] <= 70) + if (aiData->hpPercents[battlerDef] <= 70) score -= 2; break; case EFFECT_SPEED_DOWN: case EFFECT_SPEED_DOWN_2: - if (WillAIStrikeFirst()) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) score -= 3; else if (!AI_RandLessThan(70)) score += 2; break; case EFFECT_SPECIAL_ATTACK_DOWN: case EFFECT_SPECIAL_ATTACK_DOWN_2: - if (!ShouldLowerSpAtk(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (!ShouldLowerSpAtk(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; if (gBattleMons[battlerDef].statStages[STAT_SPATK] < DEFAULT_STAT_STAGE) score--; - else if (AI_DATA->hpPercents[battlerAtk] <= 90) + else if (aiData->hpPercents[battlerAtk] <= 90) score--; if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 3 && !AI_RandLessThan(50)) score -= 2; - else if (AI_DATA->hpPercents[battlerDef] < 70) + else if (aiData->hpPercents[battlerDef] < 70) score -= 2; break; case EFFECT_SPECIAL_DEFENSE_DOWN: case EFFECT_SPECIAL_DEFENSE_DOWN_2: - if (!ShouldLowerSpDef(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (!ShouldLowerSpDef(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; - if ((AI_DATA->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) + if ((aiData->hpPercents[battlerAtk] < 70 && !AI_RandLessThan(50)) || (gBattleMons[battlerDef].statStages[STAT_SPDEF] <= 3 && !AI_RandLessThan(50))) score -= 2; - if (AI_DATA->hpPercents[battlerDef] <= 70) + if (aiData->hpPercents[battlerDef] <= 70) score -= 2; break; case EFFECT_ACCURACY_DOWN: case EFFECT_ACCURACY_DOWN_2: - if (ShouldLowerAccuracy(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (ShouldLowerAccuracy(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; - if ((AI_DATA->hpPercents[battlerAtk] < 70 || AI_DATA->hpPercents[battlerDef] < 70) && AI_RandLessThan(100)) + if ((aiData->hpPercents[battlerAtk] < 70 || aiData->hpPercents[battlerDef] < 70) && AI_RandLessThan(100)) score--; if (gBattleMons[battlerDef].statStages[STAT_ACC] <= 4 && !AI_RandLessThan(80)) score -= 2; @@ -3484,26 +3460,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 (AI_DATA->hpPercents[battlerAtk] > 70 || gBattleMons[battlerDef].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) + if (aiData->hpPercents[battlerAtk] > 70 || gBattleMons[battlerDef].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) break; - else if (AI_DATA->hpPercents[battlerAtk] < 40 || AI_DATA->hpPercents[battlerDef] < 40 || !AI_RandLessThan(70)) + else if (aiData->hpPercents[battlerAtk] < 40 || aiData->hpPercents[battlerDef] < 40 || !AI_RandLessThan(70)) score -= 2; break; case EFFECT_EVASION_DOWN: case EFFECT_EVASION_DOWN_2: - if (!ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (!ShouldLowerEvasion(battlerAtk, battlerDef, aiData->abilities[battlerDef])) score -= 2; - if ((AI_DATA->hpPercents[battlerAtk] < 70 || gBattleMons[battlerDef].statStages[STAT_EVASION] <= 3) && !AI_RandLessThan(50)) + if ((aiData->hpPercents[battlerAtk] < 70 || gBattleMons[battlerDef].statStages[STAT_EVASION] <= 3) && !AI_RandLessThan(50)) score -= 2; - if (AI_DATA->hpPercents[battlerDef] <= 70) + if (aiData->hpPercents[battlerDef] <= 70) score -= 2; if (gBattleMons[battlerAtk].statStages[STAT_ACC] < DEFAULT_STAT_STAGE) score++; - if (gBattleMons[battlerDef].statStages[STAT_EVASION] < 7 || AI_DATA->abilities[battlerAtk] == ABILITY_NO_GUARD) + if (gBattleMons[battlerDef].statStages[STAT_EVASION] < 7 || aiData->abilities[battlerAtk] == ABILITY_NO_GUARD) score -= 2; break; case EFFECT_BIDE: - if (AI_DATA->hpPercents[battlerAtk] < 90) + if (aiData->hpPercents[battlerAtk] < 90) score -= 2; break; case EFFECT_DREAM_EATER: @@ -3519,7 +3495,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 (AI_DATA->hpPercents[battlerAtk] <= 40 || AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) + if (aiData->hpPercents[battlerAtk] <= 40 || aiData->abilities[battlerAtk] == ABILITY_CONTRARY) break; if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL)) @@ -3529,7 +3505,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_HAZE: if (AnyStatIsRaised(BATTLE_PARTNER(battlerAtk)) - || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)) + || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)) score -= 3; break; // fallthrough @@ -3542,9 +3518,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_MULTI_HIT: case EFFECT_TRIPLE_KICK: - 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) + if (AI_MoveMakesContact(aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk], move) + && aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD + && aiData->holdEffects[battlerDef] == HOLD_EFFECT_ROCKY_HELMET) score -= 2; break; case EFFECT_CONVERSION: @@ -3552,7 +3528,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->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move); + score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move); break; case EFFECT_SWALLOW: if (gDisableStructs[battlerAtk].stockpileCounter == 0) @@ -3589,7 +3565,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->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score++; break; case EFFECT_TOXIC: @@ -3604,26 +3580,26 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (ShouldSetScreen(battlerAtk, battlerDef, moveEffect)) { score += 5; - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) + if (aiData->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->abilities[battlerAtk]))) + if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))) { break; } else if (ShouldRecover(battlerAtk, battlerDef, move, 100)) { - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_SLP - || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_STATUS + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_SLP + || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_STATUS || HasMoveEffect(EFFECT_SLEEP_TALK, battlerAtk) || HasMoveEffect(EFFECT_SNORE, battlerAtk) - || 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)) + || aiData->abilities[battlerAtk] == ABILITY_SHED_SKIN + || aiData->abilities[battlerAtk] == ABILITY_EARLY_BIRD + || (AI_GetWeather(aiData) & B_WEATHER_RAIN && gWishFutureKnock.weatherDuration != 1 && aiData->abilities[battlerAtk] == ABILITY_HYDRATION && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) { score += 2; } @@ -3653,9 +3629,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_FOCUS_ENERGY: case EFFECT_LASER_FOCUS: - if (AI_DATA->abilities[battlerAtk] == ABILITY_SUPER_LUCK - || AI_DATA->abilities[battlerAtk] == ABILITY_SNIPER - || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_SCOPE_LENS + if (aiData->abilities[battlerAtk] == ABILITY_SUPER_LUCK + || aiData->abilities[battlerAtk] == ABILITY_SNIPER + || aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SCOPE_LENS || HasHighCritRatioMove(battlerAtk)) score += 2; break; @@ -3679,17 +3655,17 @@ 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 (sereneGraceBoost && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY) + if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY) score += 2; break; case EFFECT_SPEED_DOWN_HIT: - if (WillAIStrikeFirst()) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) score -= 2; else if (!AI_RandLessThan(70)) score++; - if (ShouldLowerSpeed(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef])) { - if (sereneGraceBoost && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY) + if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY) score += 5; else score += 2; @@ -3708,7 +3684,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 && AI_DATA->hpPercents[battlerAtk] > 70)) + if (!gBattleMons[battlerDef].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION && aiData->hpPercents[battlerAtk] > 70)) score++; break; case EFFECT_MIMIC: @@ -3722,8 +3698,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->abilities[battlerDef] == ABILITY_LIQUID_OOZE - || AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_GUARD) + || aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE + || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; score += 3; if (!HasDamagingMove(battlerDef) || IsBattlerTrapped(battlerDef, FALSE)) @@ -3740,7 +3716,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->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex)) + switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex)) { case 0: // no score -= 10; // technically should go in CheckBadMove, but this is easier/less computationally demanding @@ -3770,7 +3746,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 #if B_MENTAL_HERB >= GEN_5 - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB // mental herb + && aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB // mental herb #endif ) { @@ -3779,10 +3755,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF) { - /* TODO predicted moves if (gLastMoves[battlerDef] == predictedMove) score += 3; - else */if (CanMoveFaintBattler(gLastMoves[battlerDef], battlerDef, battlerAtk, 1)) + else if (CanTargetMoveFaintAi(gLastMoves[battlerDef], battlerDef, battlerAtk, 1)) score += 2; //Disable move that can kill attacker } } @@ -3795,7 +3770,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 #if B_MENTAL_HERB >= GEN_5 - && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB // mental herb + && aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB // mental herb #endif ) { @@ -3805,9 +3780,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_PAIN_SPLIT: { - u16 newHp = (gBattleMons[battlerAtk].hp + gBattleMons[battlerDef].hp) / 2; - u16 healthBenchmark = (gBattleMons[battlerAtk].hp * 12) / 10; - if (newHp > healthBenchmark && ShouldAbsorb(battlerAtk, battlerDef, move, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])) + u32 newHp = (gBattleMons[battlerAtk].hp + gBattleMons[battlerDef].hp) / 2; + u32 healthBenchmark = (gBattleMons[battlerAtk].hp * 12) / 10; + if (newHp > healthBenchmark && ShouldAbsorb(battlerAtk, battlerDef, move, aiData->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])) score += 2; } break; @@ -3819,15 +3794,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->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])) + else if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES && HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])) score += 3; - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])) score += 3; - else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])) score++; break; case EFFECT_SPEED_UP_HIT: - if (sereneGraceBoost && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY && !WillAIStrikeFirst()) + if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) score += 3; break; case EFFECT_DESTINY_BOND: @@ -3852,14 +3827,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->items[battlerAtk] == ITEM_NONE - && AI_DATA->items[battlerDef] != ITEM_NONE - && CanBattlerGetOrLoseItem(battlerDef, AI_DATA->items[battlerDef]) - && CanBattlerGetOrLoseItem(battlerAtk, AI_DATA->items[battlerDef]) + if (canSteal && aiData->items[battlerAtk] == ITEM_NONE + && aiData->items[battlerDef] != ITEM_NONE + && CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef]) + && CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef]) && !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS) - && AI_DATA->abilities[battlerDef] != ABILITY_STICKY_HOLD) + && aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD) { - switch (AI_DATA->holdEffects[battlerDef]) + switch (aiData->holdEffects[battlerDef]) { case HOLD_EFFECT_NONE: break; @@ -3869,11 +3844,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->abilities[battlerAtk])) + if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) + if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: @@ -3896,7 +3871,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_NIGHTMARE: - if (AI_DATA->abilities[battlerDef] != ABILITY_MAGIC_GUARD + if (aiData->abilities[battlerDef] != ABILITY_MAGIC_GUARD && !(gBattleMons[battlerDef].status2 & STATUS2_NIGHTMARE) && AI_IsBattlerAsleepOrComatose(battlerDef)) { @@ -3916,7 +3891,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY || AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_GUARD) + if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < 8) score += (8 - gBattleMons[battlerAtk].statStages[STAT_ATK]); @@ -3940,9 +3915,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); } - else if (isDoubleBattle && AI_GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), AI_DATA->partnerMove) & MOVE_TARGET_FOES_AND_ALLY) + else if (isDoubleBattle && AI_GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY) { - if (AI_DATA->abilities[battlerAtk] != ABILITY_TELEPATHY) + if (aiData->abilities[battlerAtk] != ABILITY_TELEPATHY) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); } break; @@ -3957,9 +3932,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); break; case MOVE_KINGS_SHIELD: - if (AI_DATA->abilities[battlerAtk] == ABILITY_STANCE_CHANGE //Special logic for Aegislash + if (aiData->abilities[battlerAtk] == ABILITY_STANCE_CHANGE //Special logic for Aegislash && gBattleMons[battlerAtk].species == SPECIES_AEGISLASH_BLADE - && !IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])) + && !IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])) { score += 3; break; @@ -3974,7 +3949,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->holdEffects[battlerAtk])) + && IsPinchBerryItemEffect(aiData->holdEffects[battlerAtk])) { score += 3; } @@ -3991,14 +3966,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->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) + if (aiData->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->abilities[battlerAtk] == ABILITY_SCRAPPY) + if (aiData->abilities[battlerAtk] == ABILITY_SCRAPPY) break; else if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE || (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) @@ -4016,10 +3991,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case EFFECT_SANDSTORM: - if (ShouldSetSandstorm(battlerAtk, AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + if (ShouldSetSandstorm(battlerAtk, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk])) { score++; - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -4028,14 +4003,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_HAIL: - if (ShouldSetHail(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + if (ShouldSetHail(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk])) { 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->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -4044,14 +4019,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SNOWSCAPE: - if (ShouldSetSnow(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + if (ShouldSetSnow(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk])) { 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->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -4060,10 +4035,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_RAIN_DANCE: - if (ShouldSetRain(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + if (ShouldSetRain(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk])) { score++; - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_DAMP_ROCK) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_DAMP_ROCK) score++; if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN) || HasMoveEffect(battlerDef, EFFECT_SYNTHESIS) @@ -4074,10 +4049,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SUNNY_DAY: - if (ShouldSetSun(battlerAtk, AI_DATA->abilities[battlerAtk], AI_DATA->holdEffects[battlerAtk])) + if (ShouldSetSun(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk])) { score++; - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_HEAT_ROCK) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_HEAT_ROCK) score++; if (HasMoveWithType(battlerDef, TYPE_WATER) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_WATER)) score++; @@ -4095,7 +4070,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_FELL_STINGER: if (gBattleMons[battlerAtk].statStages[STAT_ATK] < MAX_STAT_STAGE - && AI_DATA->abilities[battlerAtk] != ABILITY_CONTRARY + && aiData->abilities[battlerAtk] != ABILITY_CONTRARY && CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) { if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first @@ -4105,7 +4080,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->abilities[battlerAtk] != ABILITY_CONTRARY) + if (!CanTargetFaintAi(battlerDef, battlerAtk) && HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && aiData->abilities[battlerAtk] != ABILITY_CONTRARY) score += (MAX_STAT_STAGE - gBattleMons[battlerAtk].statStages[STAT_ATK]); break; case EFFECT_PSYCH_UP: @@ -4170,7 +4145,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_STOCKPILE: - if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) + if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY) break; if (HasMoveEffect(battlerAtk, EFFECT_SWALLOW) || HasMoveEffect(battlerAtk, EFFECT_SPIT_UP)) @@ -4193,7 +4168,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF)) score++; - if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) + if (aiData->abilities[battlerDef] == ABILITY_CONTRARY) score += 2; IncreaseConfusionScore(battlerAtk, battlerDef, move, &score); @@ -4203,17 +4178,17 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF)) score += 2; - if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) + if (aiData->abilities[battlerDef] == ABILITY_CONTRARY) score += 2; IncreaseConfusionScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_FURY_CUTTER: - if (!isDoubleBattle && AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_METRONOME) + if (!isDoubleBattle && aiData->holdEffects[battlerAtk] == HOLD_EFFECT_METRONOME) score += 3; break; case EFFECT_ATTRACT: - if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef]) + if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef]) && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Target goes first break; // Don't use if the attract won't have a change to activate @@ -4258,16 +4233,16 @@ 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 + if (IsHazardMoveEffect(gBattleMoves[aiData->partnerMove].effect) // Partner is going to set up hazards && AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk), move) == AI_IS_SLOWER) // Partner going first break; // Don't use Defog if partner is going to set up hazards } // check defog lowering evasion - if (ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + if (ShouldLowerEvasion(battlerAtk, battlerDef, aiData->abilities[battlerDef])) { if (gBattleMons[battlerDef].statStages[STAT_EVASION] > 7 - || HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) + || HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])) score += 2; // encourage lowering evasion if they are evasive or we have a move with low accuracy else score++; @@ -4289,11 +4264,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->abilities[battlerDef]) - && (move != MOVE_RAGE_POWDER || IsAffectedByPowder(battlerDef, AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerDef])) // Rage Powder doesn't affect powder immunities + && !IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) + && (move != MOVE_RAGE_POWDER || IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])) // Rage Powder doesn't affect powder immunities && IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) { - u16 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)]; + u32 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)]; if (predictedMoveOnPartner != MOVE_NONE && !IS_MOVE_STATUS(predictedMoveOnPartner)) score += 3; } @@ -4315,7 +4290,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TRICK: case EFFECT_BESTOW: - switch (AI_DATA->holdEffects[battlerAtk]) + switch (aiData->holdEffects[battlerAtk]) { case HOLD_EFFECT_CHOICE_SCARF: score += 2; // assume its beneficial @@ -4329,15 +4304,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->abilities[battlerAtk])) + if (!ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, AI_DATA->abilities[battlerDef])) + if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: - if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) && AI_DATA->abilities[battlerDef] != ABILITY_MAGIC_GUARD) + if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) && aiData->abilities[battlerDef] != ABILITY_MAGIC_GUARD) score += 3; break; case HOLD_EFFECT_IRON_BALL: @@ -4349,17 +4324,17 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case HOLD_EFFECT_UTILITY_UMBRELLA: - if (AI_DATA->abilities[battlerAtk] != ABILITY_SOLAR_POWER && AI_DATA->abilities[battlerAtk] != ABILITY_DRY_SKIN && AI_WeatherHasEffect()) + if (aiData->abilities[battlerAtk] != ABILITY_SOLAR_POWER && aiData->abilities[battlerAtk] != ABILITY_DRY_SKIN) { - switch (AI_DATA->abilities[battlerDef]) + switch (aiData->abilities[battlerDef]) { case ABILITY_SWIFT_SWIM: - if (gBattleWeather & B_WEATHER_RAIN) + if (AI_GetWeather(aiData) & B_WEATHER_RAIN) score += 3; // Slow 'em down break; case ABILITY_CHLOROPHYLL: case ABILITY_FLOWER_GIFT: - if (gBattleWeather & B_WEATHER_SUN) + if (AI_GetWeather(aiData) & B_WEATHER_SUN) score += 3; // Slow 'em down break; } @@ -4372,22 +4347,22 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; // Force 'em out next turn break; default: - if (move != MOVE_BESTOW && AI_DATA->items[battlerAtk] == ITEM_NONE) + if (move != MOVE_BESTOW && aiData->items[battlerAtk] == ITEM_NONE) { - switch (AI_DATA->holdEffects[battlerDef]) + switch (aiData->holdEffects[battlerDef]) { case HOLD_EFFECT_CHOICE_BAND: break; case HOLD_EFFECT_TOXIC_ORB: - if (ShouldPoisonSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) + if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (ShouldBurnSelf(battlerAtk, AI_DATA->abilities[battlerAtk])) + if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk])) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: - if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON) || AI_DATA->abilities[battlerAtk] == ABILITY_MAGIC_GUARD) + if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON) || aiData->abilities[battlerAtk] == ABILITY_MAGIC_GUARD) score += 3; break; case HOLD_EFFECT_IRON_BALL: @@ -4405,14 +4380,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_ROLE_PLAY: - if (!IsRolePlayBannedAbilityAtk(AI_DATA->abilities[battlerAtk]) - && !IsRolePlayBannedAbility(AI_DATA->abilities[battlerDef]) - && !IsAbilityOfRating(AI_DATA->abilities[battlerAtk], 5) - && IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5)) + if (!IsRolePlayBannedAbilityAtk(aiData->abilities[battlerAtk]) + && !IsRolePlayBannedAbility(aiData->abilities[battlerDef]) + && !IsAbilityOfRating(aiData->abilities[battlerAtk], 5) + && IsAbilityOfRating(aiData->abilities[battlerDef], 5)) score += 2; break; case EFFECT_INGRAIN: - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT) score += 3; else score++; @@ -4420,7 +4395,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SUPERPOWER: case EFFECT_OVERHEAT: case EFFECT_MAKE_IT_RAIN: - if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) + if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY) score += 3; break; case EFFECT_MAGIC_COAT: @@ -4432,15 +4407,15 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; if (IsRecycleEncouragedItem(GetUsedHeldItem(battlerAtk))) score++; - if (AI_DATA->abilities[battlerAtk] == ABILITY_RIPEN) + if (aiData->abilities[battlerAtk] == ABILITY_RIPEN) { - u16 item = GetUsedHeldItem(battlerAtk); - u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item); + u32 item = GetUsedHeldItem(battlerAtk); + u32 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item); - if (IsStatBoostingBerry(item) && AI_DATA->hpPercents[battlerAtk] > 60) + if (IsStatBoostingBerry(item) && aiData->hpPercents[battlerAtk] > 60) score++; else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0) - && ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0)) + && ((GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0)) || !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0))) score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry } @@ -4454,9 +4429,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; break; case EFFECT_KNOCK_OFF: - if (CanKnockOffItem(battlerDef, AI_DATA->items[battlerDef])) + if (CanKnockOffItem(battlerDef, aiData->items[battlerDef])) { - switch (AI_DATA->holdEffects[battlerDef]) + switch (aiData->holdEffects[battlerDef]) { case HOLD_EFFECT_IRON_BALL: if (HasMoveEffect(battlerDef, EFFECT_FLING)) @@ -4472,19 +4447,19 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_SKILL_SWAP: - if (GetAbilityRating(AI_DATA->abilities[battlerDef]) > GetAbilityRating(AI_DATA->abilities[battlerAtk])) + if (GetAbilityRating(aiData->abilities[battlerDef]) > GetAbilityRating(aiData->abilities[battlerAtk])) score++; break; case EFFECT_WORRY_SEED: case EFFECT_GASTRO_ACID: case EFFECT_SIMPLE_BEAM: - if (IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5)) + if (IsAbilityOfRating(aiData->abilities[battlerDef], 5)) score += 2; break; case EFFECT_ENTRAINMENT: - if (IsAbilityOfRating(AI_DATA->abilities[battlerDef], 5) || GetAbilityRating(AI_DATA->abilities[battlerAtk]) <= 0) + if (IsAbilityOfRating(aiData->abilities[battlerDef], 5) || GetAbilityRating(aiData->abilities[battlerAtk]) <= 0) { - if (AI_DATA->abilities[battlerDef] != AI_DATA->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)) + if (aiData->abilities[battlerDef] != aiData->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)) score += 2; } break; @@ -4500,8 +4475,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_TAKE_HEART: if (gBattleMons[battlerAtk].status1 & STATUS1_ANY - || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) - || BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF)) + || BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPATK) + || BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) score += 2; break; case EFFECT_PSYCHO_SHIFT: @@ -4532,11 +4507,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->abilities[battlerDef] != ABILITY_CONTRARY && ShouldLowerDefense(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + && aiData->abilities[battlerDef] != ABILITY_CONTRARY && ShouldLowerDefense(battlerAtk, battlerDef, aiData->abilities[battlerDef])) { score += 2; } - else if (ShouldLowerAttack(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef])) + else if (ShouldLowerAttack(battlerAtk, battlerDef, aiData->abilities[battlerDef])) { score += 2; } @@ -4554,7 +4529,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->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) score += 3; //fallthrough case EFFECT_QUIVER_DANCE: @@ -4568,7 +4543,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score); break; case EFFECT_SHELL_SMASH: - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_RESTORE_STATS) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_RESTORE_STATS) score += 1; IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score); @@ -4627,8 +4602,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_GUARD_SPLIT: { // TODO also kind of cheating... - u16 newDefense = (gBattleMons[battlerAtk].defense + gBattleMons[battlerDef].defense) / 2; - u16 newSpDef = (gBattleMons[battlerAtk].spDefense + gBattleMons[battlerDef].spDefense) / 2; + u32 newDefense = (gBattleMons[battlerAtk].defense + gBattleMons[battlerDef].defense) / 2; + u32 newSpDef = (gBattleMons[battlerAtk].spDefense + gBattleMons[battlerDef].spDefense) / 2; if ((newDefense > gBattleMons[battlerAtk].defense && newSpDef >= gBattleMons[battlerAtk].spDefense) || (newSpDef > gBattleMons[battlerAtk].spDefense && newDefense >= gBattleMons[battlerAtk].defense)) @@ -4637,8 +4612,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_POWER_SPLIT: { - u16 newAttack = (gBattleMons[battlerAtk].attack + gBattleMons[battlerDef].attack) / 2; - u16 newSpAtk = (gBattleMons[battlerAtk].spAttack + gBattleMons[battlerDef].spAttack) / 2; + u32 newAttack = (gBattleMons[battlerAtk].attack + gBattleMons[battlerDef].attack) / 2; + u32 newSpAtk = (gBattleMons[battlerAtk].spAttack + gBattleMons[battlerDef].spAttack) / 2; if ((newAttack > gBattleMons[battlerAtk].attack && newSpAtk >= gBattleMons[battlerAtk].spAttack) || (newSpAtk > gBattleMons[battlerAtk].spAttack && newAttack >= gBattleMons[battlerAtk].attack)) @@ -4646,15 +4621,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->abilities[battlerDef] == ABILITY_STICKY_HOLD) + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD) break; - else if (ItemId_GetPocket(AI_DATA->items[battlerDef]) == POCKET_BERRIES) + else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES) score += 3; break; case EFFECT_INCINERATE: - if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || AI_DATA->abilities[battlerDef] == ABILITY_STICKY_HOLD) + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD) break; - else if (ItemId_GetPocket(AI_DATA->items[battlerDef]) == POCKET_BERRIES || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_GEMS) + else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS) score += 3; break; case EFFECT_SMACK_DOWN: @@ -4678,7 +4653,7 @@ 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->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER) score += 2; break; case EFFECT_PLEDGE: @@ -4696,9 +4671,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_MAGIC_ROOM: score++; - if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_NONE && AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_NONE) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_NONE && aiData->holdEffects[battlerDef] != HOLD_EFFECT_NONE) score++; - if (isDoubleBattle && AI_DATA->holdEffects[BATTLE_PARTNER(battlerAtk)] == HOLD_EFFECT_NONE && AI_DATA->holdEffects[BATTLE_PARTNER(battlerDef)] != HOLD_EFFECT_NONE) + if (isDoubleBattle && aiData->holdEffects[BATTLE_PARTNER(battlerAtk)] == HOLD_EFFECT_NONE && aiData->holdEffects[BATTLE_PARTNER(battlerDef)] != HOLD_EFFECT_NONE) score++; break; case EFFECT_WONDER_ROOM: @@ -4711,28 +4686,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->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef])) + else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])) score += 2; else score++; } break; case EFFECT_ION_DELUGE: - if ((AI_DATA->abilities[battlerAtk] == ABILITY_VOLT_ABSORB - || AI_DATA->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE - || AI_DATA->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD) + if ((aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB + || aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE + || aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD) && gBattleMoves[predictedMove].type == TYPE_NORMAL) score += 2; break; case EFFECT_FLING: /* TODO - switch (gFlingTable[AI_DATA->items[battlerAtk]].effect) + switch (gFlingTable[aiData->items[battlerAtk]].effect) { case MOVE_EFFECT_BURN: IncreaseBurnScore(battlerAtk, battlerDef, move, &score); break; case MOVE_EFFECT_FLINCH: - score += ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move); + score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move); break; case MOVE_EFFECT_PARALYSIS: IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score); @@ -4752,7 +4727,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 3; break; case EFFECT_EMBARGO: - if (AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_NONE) + if (aiData->holdEffects[battlerDef] != HOLD_EFFECT_NONE) score++; break; case EFFECT_POWDER: @@ -4760,7 +4735,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->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]) + if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]) || !IsBattlerGrounded(battlerDef)) score++; break; @@ -4773,8 +4748,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_HEAL_BLOCK: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect)) score += 3; // Try to cancel healing move - else if (HasHealingEffect(battlerDef) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_LEFTOVERS - || (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON))) + else if (HasHealingEffect(battlerDef) || aiData->holdEffects[battlerDef] == HOLD_EFFECT_LEFTOVERS + || (aiData->holdEffects[battlerDef] == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON))) score += 2; break; case EFFECT_SOAK: @@ -4782,14 +4757,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->abilities[battlerDef] == ABILITY_WONDER_GUARD) + if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD) score += 2; // Give target more weaknesses break; case EFFECT_ELECTRIFY: if (predictedMove != MOVE_NONE - && (AI_DATA->abilities[battlerAtk] == ABILITY_VOLT_ABSORB - || AI_DATA->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE - || AI_DATA->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)) + && (aiData->abilities[battlerAtk] == ABILITY_VOLT_ABSORB + || aiData->abilities[battlerAtk] == ABILITY_MOTOR_DRIVE + || aiData->abilities[battlerAtk] == ABILITY_LIGHTNING_ROD)) { score += 3; } @@ -4807,7 +4782,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_QUASH: if (isDoubleBattle - && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef, AI_DATA->partnerMove) == AI_IS_SLOWER) // Attacker partner wouldn't go before target + && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef, aiData->partnerMove) == AI_IS_SLOWER) // Attacker partner wouldn't go before target score++; break; case EFFECT_TAILWIND: @@ -4852,8 +4827,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (isDoubleBattle) { if (IsBattlerAlive(BATTLE_PARTNER(battlerDef)) - && AI_DATA->hpPercents[BATTLE_PARTNER(battlerDef)] < 12 - && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD + && aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12 + && aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD && !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE)) score++; } @@ -4865,38 +4840,38 @@ 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->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) + if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) score += 2; break; case EFFECT_COUNTER: - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) && predictedMove != MOVE_NONE) + if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE) { if (gDisableStructs[battlerDef].tauntTimer != 0) score++; // target must use damaging move - if (GetMoveDamageResult(predictedMove) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL) + if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL) score += 3; } break; case EFFECT_MIRROR_COAT: - if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]) && predictedMove != MOVE_NONE) + if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE) { if (gDisableStructs[battlerDef].tauntTimer != 0) score++; // target must use damaging move - if (GetMoveDamageResult(predictedMove) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL) + if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL) score += 3; } break; case EFFECT_FLAIL: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Ai goes first { - if (AI_DATA->hpPercents[battlerAtk] < 20) + if (aiData->hpPercents[battlerAtk] < 20) score++; - else if (AI_DATA->hpPercents[battlerAtk] < 8) + else if (aiData->hpPercents[battlerAtk] < 8) score += 2; } break; case EFFECT_SHORE_UP: - if (AI_WeatherHasEffect() && (gBattleWeather & B_WEATHER_SANDSTORM) + if ((AI_GetWeather(aiData) & B_WEATHER_SANDSTORM) && ShouldRecover(battlerAtk, battlerDef, move, 67)) score += 3; else if (ShouldRecover(battlerAtk, battlerDef, move, 50)) @@ -4909,7 +4884,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->abilities[battlerDef])) + if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])) score += 2; else if (gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)) score++; @@ -4931,10 +4906,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_ENDEAVOR: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent faster { - if (AI_DATA->hpPercents[battlerAtk] < 40) + if (aiData->hpPercents[battlerAtk] < 40) score++; } - else if (AI_DATA->hpPercents[battlerAtk] < 50) + else if (aiData->hpPercents[battlerAtk] < 50) { score++; } @@ -4968,9 +4943,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // Effects that are encouraged on the first turn of battle -static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - if (IsTargetingPartner(battlerAtk, battlerDef) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef) || gBattleResults.battleTurnCounter != 0) return score; @@ -5079,9 +5054,9 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // Adds score bonus to 'riskier' move effects and high crit moves -static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) return score; if (gBattleMoves[move].highCritRatio) @@ -5118,25 +5093,25 @@ static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // Adds score bonus to best powered move -static s16 AI_PreferStrongestMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) return score; - if (GetMoveDamageResult(move) == MOVE_POWER_BEST) + if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_BEST) score += 2; return score; } // Prefers moves that are good for baton pass -static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { u32 i; - if (IsTargetingPartner(battlerAtk, battlerDef) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef) || CountUsablePartyMons(battlerAtk) == 0 - || GetMoveDamageResult(move) != MOVE_POWER_OTHER + || GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) != MOVE_POWER_OTHER || !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS) || IsBattlerTrapped(battlerAtk, TRUE)) return score; @@ -5185,15 +5160,15 @@ static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) return score; } -static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - u16 effect = gBattleMoves[move].effect; - u8 moveType = gBattleMoves[move].type; + u32 effect = gBattleMoves[move].effect; + u32 moveType = gBattleMoves[move].type; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); - if (IsTargetingPartner(battlerAtk, battlerDef)) + if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)) { if ((effect == EFFECT_HEAL_PULSE || effect == EFFECT_HIT_ENEMY_HEAL_ALLY) || (moveType == TYPE_ELECTRIC && AI_DATA->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_VOLT_ABSORB) @@ -5388,7 +5363,7 @@ static void AI_Watch(void) } // Roaming pokemon logic -static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { if (IsBattlerTrapped(battlerAtk, FALSE)) return score; @@ -5398,9 +5373,9 @@ static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } // Safari pokemon logic -static s16 AI_Safari(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) +static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { - u8 safariFleeRate = gBattleStruct->safariEscapeFactor * 5; // Safari flee rate, from 0-20. + u32 safariFleeRate = gBattleStruct->safariEscapeFactor * 5; // Safari flee rate, from 0-20. if ((Random() % 100) < safariFleeRate) AI_Flee(); @@ -5411,7 +5386,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) +static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { if (AI_DATA->hpPercents[battlerDef] <= 20) AI_Flee(); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 1c12ecdfd..b2f56326f 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -400,7 +400,7 @@ static bool8 ShouldSwitchIfGameStatePrompt(u32 battler) && AnyStatIsRaised(battler)) switchMon = FALSE; if (AiExpectsToFaintPlayer(battler) - && !WillAIStrikeFirst() + && !AI_STRIKES_FIRST(battler, opposingBattler, 0) && !AI_OpponentCanFaintAiWithMod(battler, 0)) switchMon = FALSE; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 10668e07d..0a65f4083 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -419,28 +419,18 @@ static const u16 sOtherMoveCallingMoves[] = }; // Functions -u16 GetAIChosenMove(u8 battlerId) +u32 GetAIChosenMove(u32 battlerId) { return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]); } -bool32 WillAIStrikeFirst(void) -{ - return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget, AI_THINKING_STRUCT->moveConsidered) == AI_IS_FASTER); -} - -bool32 AI_RandLessThan(u8 val) +bool32 AI_RandLessThan(u32 val) { if ((Random() % 0xFF) < val) return TRUE; return FALSE; } -void RecordLastUsedMoveByTarget(void) -{ - RecordKnownMove(gBattlerTarget, gLastMoves[gBattlerTarget]); -} - bool32 IsAiVsAiBattle(void) { return (B_FLAG_AI_VS_AI_BATTLE && FlagGet(B_FLAG_AI_VS_AI_BATTLE)); @@ -475,7 +465,7 @@ bool32 IsAiBattlerAware(u32 battlerId) return BattlerHasAi(battlerId); } -void ClearBattlerMoveHistory(u8 battlerId) +void ClearBattlerMoveHistory(u32 battlerId) { memset(BATTLE_HISTORY->usedMoves[battlerId], 0, sizeof(BATTLE_HISTORY->usedMoves[battlerId])); memset(BATTLE_HISTORY->moveHistory[battlerId], 0, sizeof(BATTLE_HISTORY->moveHistory[battlerId])); @@ -491,7 +481,7 @@ void RecordLastUsedMoveBy(u32 battlerId, u32 move) BATTLE_HISTORY->moveHistory[battlerId][*index] = move; } -void RecordKnownMove(u8 battlerId, u32 move) +void RecordKnownMove(u32 battlerId, u32 move) { s32 i; for (i = 0; i < MAX_MON_MOVES; i++) @@ -512,29 +502,29 @@ void RecordAllMoves(u32 battler) memcpy(AI_PARTY->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].moves, gBattleMons[battler].moves, MAX_MON_MOVES * sizeof(u16)); } -void RecordAbilityBattle(u8 battlerId, u16 abilityId) +void RecordAbilityBattle(u32 battlerId, u32 abilityId) { BATTLE_HISTORY->abilities[battlerId] = abilityId; AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].ability = abilityId; } -void ClearBattlerAbilityHistory(u8 battlerId) +void ClearBattlerAbilityHistory(u32 battlerId) { BATTLE_HISTORY->abilities[battlerId] = ABILITY_NONE; } -void RecordItemEffectBattle(u8 battlerId, u8 itemEffect) +void RecordItemEffectBattle(u32 battlerId, u32 itemEffect) { BATTLE_HISTORY->itemEffects[battlerId] = itemEffect; AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect = itemEffect; } -void ClearBattlerItemEffectHistory(u8 battlerId) +void ClearBattlerItemEffectHistory(u32 battlerId) { BATTLE_HISTORY->itemEffects[battlerId] = 0; } -void SaveBattlerData(u8 battlerId) +void SaveBattlerData(u32 battlerId) { if (!BattlerHasAi(battlerId)) { @@ -551,7 +541,7 @@ void SaveBattlerData(u8 battlerId) AI_THINKING_STRUCT->saved[battlerId].types[1] = gBattleMons[battlerId].type2; } -static bool32 ShouldFailForIllusion(u16 illusionSpecies, u32 battlerId) +static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId) { u32 i, j; @@ -561,7 +551,7 @@ static bool32 ShouldFailForIllusion(u16 illusionSpecies, u32 battlerId) // Don't fall for Illusion if the mon used a move it cannot know. for (i = 0; i < MAX_MON_MOVES; i++) { - u16 move = BATTLE_HISTORY->usedMoves[battlerId][i]; + u32 move = BATTLE_HISTORY->usedMoves[battlerId][i]; if (move == MOVE_NONE) continue; @@ -585,7 +575,7 @@ static bool32 ShouldFailForIllusion(u16 illusionSpecies, u32 battlerId) return TRUE; } -void SetBattlerData(u8 battlerId) +void SetBattlerData(u32 battlerId) { if (!BattlerHasAi(battlerId)) { @@ -629,7 +619,7 @@ void SetBattlerData(u8 battlerId) } } -void RestoreBattlerData(u8 battlerId) +void RestoreBattlerData(u32 battlerId) { if (!BattlerHasAi(battlerId)) { @@ -645,21 +635,21 @@ void RestoreBattlerData(u8 battlerId) gBattleMons[battlerId].type2 = AI_THINKING_STRUCT->saved[battlerId].types[1]; } -u32 GetHealthPercentage(u8 battlerId) +u32 GetHealthPercentage(u32 battlerId) { return (u32)((100 * gBattleMons[battlerId].hp) / gBattleMons[battlerId].maxHP); } -bool32 AtMaxHp(u8 battlerId) +bool32 AtMaxHp(u32 battlerId) { if (AI_DATA->hpPercents[battlerId] == 100) return TRUE; return FALSE; } -bool32 IsBattlerTrapped(u8 battler, bool8 checkSwitch) +bool32 IsBattlerTrapped(u32 battler, bool32 checkSwitch) { - u8 holdEffect = AI_DATA->holdEffects[battler]; + u32 holdEffect = AI_DATA->holdEffects[battler]; #if B_GHOSTS_ESCAPE >= GEN_6 if (IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) @@ -709,7 +699,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) } // move checks -bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect) +bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect) { if (ability == ABILITY_OVERCOAT #if B_POWDER_GRASS >= GEN_6 @@ -738,7 +728,7 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split) { SetTypeBeforeUsingMove(moves[i], attacker); GET_MOVE_TYPE(moves[i], moveType); - if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, FALSE) != 0) + if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, AI_DATA->abilities[target], FALSE) != 0) usable |= gBitTable[i]; } } @@ -746,46 +736,22 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split) return (usable == 0); } -static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef) +// To save computation time this function has 2 variants. One saves, sets and restores battlers, while the other doesn't. +s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower) { - bool32 isCrit; - - switch (CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE)) - { - case -1: - case 0: - default: - isCrit = FALSE; - break; - case 1: - if (gBattleMoves[move].highCritRatio && (Random() % 5 == 0)) - isCrit = TRUE; - else - isCrit = FALSE; - break; - case 2: - if (gBattleMoves[move].highCritRatio && (Random() % 2 == 0)) - isCrit = TRUE; - else if (!(gBattleMoves[move].highCritRatio) && (Random() % 4) == 0) - isCrit = TRUE; - else - isCrit = FALSE; - break; - case -2: - case 3: - case 4: - isCrit = TRUE; - break; - } - - return isCrit; + SaveBattlerData(battlerAtk); + SaveBattlerData(battlerDef); + AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather(AI_DATA)); } -s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, bool32 considerZPower) +s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather) { - s32 dmg, moveType, critDmg, normalDmg, fixedBasePower, n; - s8 critChance; + s32 dmg, moveType; uq4_12_t effectivenessMultiplier; + struct AiLogicData *aiData = AI_DATA; + + SetBattlerData(battlerAtk); + SetBattlerData(battlerDef); if (considerZPower && IsViableZMove(battlerAtk, move)) { @@ -794,12 +760,6 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, gBattleStruct->zmove.active = TRUE; } - SaveBattlerData(battlerAtk); - SaveBattlerData(battlerDef); - - SetBattlerData(battlerAtk); - SetBattlerData(battlerDef); - gBattleStruct->dynamicMoveType = 0; if (move == MOVE_NATURE_POWER) @@ -808,10 +768,12 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); + effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE); if (gBattleMoves[move].power) { - ProteanTryChangeType(battlerAtk, AI_DATA->abilities[battlerAtk], move, moveType); - critChance = GetInverseCritChance(battlerAtk, battlerDef, move); + s32 critChanceIndex, normalDmg, fixedBasePower, n; + + ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType); // Certain moves like Rollout calculate damage based on values which change during the move execution, but before calling dmg calc. switch (gBattleMoves[move].effect) { @@ -826,13 +788,26 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, fixedBasePower = 0; break; } - normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, fixedBasePower, &effectivenessMultiplier); - critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, fixedBasePower, TRUE, FALSE, FALSE); + normalDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, + effectivenessMultiplier, weather, FALSE, + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], + aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); - if (critChance == -1) - dmg = normalDmg; + critChanceIndex = CalcCritChanceStageArgs(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]); + if (critChanceIndex > 1) // Consider crit damage only if a move has at least +1 crit chance + { + s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, + effectivenessMultiplier, weather, TRUE, + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], + aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + u32 critChance = GetCritHitChance(critChanceIndex); + // With critChance getting closer to 1, dmg gets closer to critDmg. + dmg = (critDmg + normalDmg * (critChance - 1)) / (critChance); + } else - dmg = (critDmg + normalDmg * (critChance - 1)) / critChance; + { + dmg = normalDmg; + } if (!gBattleStruct->zmove.active) { @@ -841,23 +816,23 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, { case EFFECT_LEVEL_DAMAGE: case EFFECT_PSYWAVE: - dmg = gBattleMons[battlerAtk].level * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = gBattleMons[battlerAtk].level * (aiData->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_DRAGON_RAGE: - dmg = 40 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = 40 * (aiData->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_SONICBOOM: - dmg = 20 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); + dmg = 20 * (aiData->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1); break; case EFFECT_MULTI_HIT: - dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 5 : 3); + dmg *= (aiData->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 5 : 3); 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->abilities[battlerAtk] == ABILITY_PARENTAL_BOND + dmg = (aiData->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? max(2, gBattleMons[battlerDef].hp * 3 / 4) : max(1, gBattleMons[battlerDef].hp / 2)); break; @@ -892,7 +867,6 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, } else { - effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE); dmg = 0; } @@ -950,98 +924,104 @@ u32 GetNoOfHitsToKO(u32 dmg, s32 hp) return hp / (dmg + 1) + 1; } -u8 GetMoveDamageResult(u16 move) +void SetMoveDamageResult(u32 battlerAtk, u16 *moves) { - s32 i, checkedMove, bestId, currId, hp; + s32 i, j, battlerDef, bestId, currId, hp, result; s32 moveDmgs[MAX_MON_MOVES]; - u8 result; + bool32 isNotConsidered[MAX_MON_MOVES]; - for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++) + for (i = 0; i < MAX_MON_MOVES; i++) { - if (gBattleMoves[move].effect == sIgnoredPowerfulMoveEffects[i]) - break; - } - - if (gBattleMoves[move].power != 0 && sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END) - { - // Considered move has power and is not in sIgnoredPowerfulMoveEffects - // Check all other moves and calculate their power - for (checkedMove = 0; checkedMove < MAX_MON_MOVES; checkedMove++) + u32 move = moves[i]; + for (j = 0; sIgnoredPowerfulMoveEffects[j] != IGNORED_MOVES_END; j++) { - for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++) - { - if (gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].effect == sIgnoredPowerfulMoveEffects[i]) - break; - } + if (gBattleMoves[move].effect == sIgnoredPowerfulMoveEffects[j]) + break; + } + if (move == 0 || move == 0xFFFF || gBattleMoves[move].power == 0 || sIgnoredPowerfulMoveEffects[j] != IGNORED_MOVES_END) + isNotConsidered[i] = TRUE; + else + isNotConsidered[i] = FALSE; - if (gBattleMons[sBattler_AI].moves[checkedMove] != MOVE_NONE - && sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END - && gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power != 0) + for (battlerDef = 0; battlerDef < MAX_BATTLERS_COUNT; battlerDef++) + { + if (battlerDef == battlerAtk) + continue; + + if (isNotConsidered[i]) { - moveDmgs[checkedMove] = AI_DATA->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove]; + AI_DATA->moveDmgResult[battlerAtk][battlerDef][i] = MOVE_POWER_OTHER; // Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects } else { - moveDmgs[checkedMove] = 0; - } - } - - hp = gBattleMons[gBattlerTarget].hp + (20 * gBattleMons[gBattlerTarget].hp / 100); // 20 % add to make sure the battler is always fainted - // If a move can faint battler, it doesn't matter how much damage it does - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (moveDmgs[i] > hp) - moveDmgs[i] = hp; - } - - for (bestId = 0, i = 1; i < MAX_MON_MOVES; i++) - { - if (moveDmgs[i] > moveDmgs[bestId]) - bestId = i; - if (moveDmgs[i] == moveDmgs[bestId]) - { - switch (WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[i])) + // Considered move has power and is not in sIgnoredPowerfulMoveEffects + // Check all other moves and calculate their power + for (j = 0; j < MAX_MON_MOVES; j++) { - case 2: - if (Random() & 1) - break; - case 1: - bestId = i; - break; + if (!isNotConsidered[j]) + moveDmgs[j] = AI_DATA->simulatedDmg[battlerAtk][battlerDef][j]; + else + moveDmgs[j] = 0; } + + hp = gBattleMons[battlerDef].hp + (20 * gBattleMons[battlerDef].hp / 100); // 20 % add to make sure the battler is always fainted + // If a move can faint battler, it doesn't matter how much damage it does + for (j = 0; j < MAX_MON_MOVES; j++) + { + if (moveDmgs[j] > hp) + moveDmgs[j] = hp; + } + + for (bestId = 0, j = 1; j < MAX_MON_MOVES; j++) + { + if (moveDmgs[j] > moveDmgs[bestId]) + bestId = j; + if (moveDmgs[j] == moveDmgs[bestId]) + { + switch (WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[j])) + { + case 2: + if (Random() & 1) + break; + case 1: + bestId = j; + break; + } + } + } + + currId = i; + if (currId == bestId) + result = MOVE_POWER_BEST; + else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can + && GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move. + && WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[currId]) != 0) + result = MOVE_POWER_GOOD; + else + result = MOVE_POWER_WEAK; + + AI_DATA->moveDmgResult[battlerAtk][battlerDef][i] = result; } } - - currId = AI_THINKING_STRUCT->movesetIndex; - if (currId == bestId) - AI_THINKING_STRUCT->funcResult = MOVE_POWER_BEST; - else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can - && GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move. - && WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[currId]) != 0) - AI_THINKING_STRUCT->funcResult = MOVE_POWER_GOOD; - else - AI_THINKING_STRUCT->funcResult = MOVE_POWER_WEAK; } - else - { - // Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects - AI_THINKING_STRUCT->funcResult = MOVE_POWER_OTHER; - } - - return AI_THINKING_STRUCT->funcResult; } -u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef) +u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex) +{ + return AI_DATA->moveDmgResult[battlerAtk][battlerDef][moveIndex]; +} + +u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef) { int bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; return (bestDmg * 100) / gBattleMons[battlerDef].maxHP; } -uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) +uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef) { uq4_12_t typeEffectiveness; - u16 moveType; + u32 moveType; SaveBattlerData(battlerAtk); SaveBattlerData(battlerDef); @@ -1052,7 +1032,7 @@ uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) gBattleStruct->dynamicMoveType = 0; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); - typeEffectiveness = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE); + typeEffectiveness = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], FALSE); RestoreBattlerData(battlerAtk); RestoreBattlerData(battlerDef); @@ -1060,7 +1040,7 @@ uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) return typeEffectiveness; } -u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef) +u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef) { gMoveResultFlags = 0; return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef)); @@ -1095,7 +1075,7 @@ static u32 AI_GetEffectiveness(uq4_12_t multiplier) * AI_IS_FASTER: is user(ai) faster * AI_IS_SLOWER: is target faster */ -u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered) +u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered) { u32 fasterAI = 0, fasterPlayer = 0, i; s8 prioAI = 0; @@ -1131,7 +1111,11 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered) if (prioAI > prioBattler2) return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move // Priorities are the same(at least comparing to moves the AI is aware of), decide by speed. - if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0) + if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE, + AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2], + AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2], + AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2], + prioAI, prioBattler2) == 0) return AI_IS_FASTER; else return AI_IS_SLOWER; @@ -1139,7 +1123,7 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered) } // Check if target has means to faint ai mon. -bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk) +bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk) { s32 i, dmg; u32 unusable = AI_DATA->moveLimitations[battlerDef]; @@ -1159,7 +1143,7 @@ bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk) // Check if AI mon has the means to faint the target with any of its moves. // If numHits > 1, check if the target will be KO'ed by that number of hits (ignoring healing effects) -bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits) +bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits) { s32 i, dmg; u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; @@ -1183,23 +1167,19 @@ bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits) return FALSE; } -bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits) +bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits) { - s32 i, dmg; - u8 effectiveness; - u32 unusable = AI_DATA->moveLimitations[battlerDef]; - - if (move != MOVE_NONE - && move != 0xFFFF - && !(unusable & gBitTable[i]) - && AI_CalcDamage(move, battlerDef, battlerAtk, &effectiveness, FALSE) >= gBattleMons[battlerAtk].hp) - return TRUE; - + u32 indexSlot = GetMoveSlot(GetMovesArray(battlerDef), move); + if (indexSlot < MAX_MON_MOVES) + { + if (GetNoOfHitsToKO(AI_DATA->simulatedDmg[battlerDef][battlerAtk][indexSlot], gBattleMons[battlerAtk].hp) <= nHits) + return TRUE; + } return FALSE; } // Check if target has means to faint ai mon after modding hp/dmg -bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod) +bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod) { u32 i; u32 unusable = AI_DATA->moveLimitations[battlerDef]; @@ -1262,7 +1242,7 @@ s32 AI_GetAbility(u32 battlerId) // Else, guess the ability if (gSpeciesInfo[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE) { - u16 abilityGuess = ABILITY_NONE; + u32 abilityGuess = ABILITY_NONE; while (abilityGuess == ABILITY_NONE) { abilityGuess = gSpeciesInfo[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS]; @@ -1274,7 +1254,7 @@ s32 AI_GetAbility(u32 battlerId) return ABILITY_NONE; // Unknown. } -u16 AI_GetHoldEffect(u32 battlerId) +u32 AI_GetHoldEffect(u32 battlerId) { u32 holdEffect; @@ -1296,7 +1276,7 @@ u16 AI_GetHoldEffect(u32 battlerId) return holdEffect; } -bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags) +bool32 AI_IsTerrainAffected(u32 battlerId, u32 flags) { if (gStatuses3[battlerId] & STATUS3_SEMI_INVULNERABLE) return FALSE; @@ -1306,7 +1286,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) +bool32 AI_IsBattlerGrounded(u32 battlerId) { u32 holdEffect = AI_DATA->holdEffects[battlerId]; @@ -1332,7 +1312,7 @@ bool32 AI_IsBattlerGrounded(u8 battlerId) return TRUE; } -bool32 DoesBattlerIgnoreAbilityChecks(u16 atkAbility, u16 move) +bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move) { u32 i; @@ -1353,16 +1333,24 @@ bool32 DoesBattlerIgnoreAbilityChecks(u16 atkAbility, u16 move) return FALSE; } -bool32 AI_WeatherHasEffect(void) +static inline bool32 AI_WeatherHasEffect(struct AiLogicData *aiData) { - u32 i; if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) return TRUE; // AI doesn't understand weather supression (handicap) - return WEATHER_HAS_EFFECT; // weather damping abilities are announced + return aiData->weatherHasEffect; // weather damping abilities are announced } -u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move) +u32 AI_GetWeather(struct AiLogicData *aiData) +{ + if (gBattleWeather == B_WEATHER_NONE) + return B_WEATHER_NONE; + if (!AI_WeatherHasEffect(aiData)) + return B_WEATHER_NONE; + return gBattleWeather; +} + +u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move) { u32 target; @@ -1372,7 +1360,7 @@ u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move) return gBattleMoves[move].target; } -bool32 IsAromaVeilProtectedMove(u16 move) +bool32 IsAromaVeilProtectedMove(u32 move) { u32 i; @@ -1390,7 +1378,7 @@ bool32 IsAromaVeilProtectedMove(u16 move) } } -bool32 IsNonVolatileStatusMoveEffect(u16 moveEffect) +bool32 IsNonVolatileStatusMoveEffect(u32 moveEffect) { switch (moveEffect) { @@ -1406,7 +1394,7 @@ bool32 IsNonVolatileStatusMoveEffect(u16 moveEffect) } } -bool32 IsConfusionMoveEffect(u16 moveEffect) +bool32 IsConfusionMoveEffect(u32 moveEffect) { switch (moveEffect) { @@ -1420,7 +1408,7 @@ bool32 IsConfusionMoveEffect(u16 moveEffect) } } -bool32 IsStatLoweringMoveEffect(u16 moveEffect) +bool32 IsStatLoweringMoveEffect(u32 moveEffect) { switch (moveEffect) { @@ -1444,7 +1432,7 @@ bool32 IsStatLoweringMoveEffect(u16 moveEffect) } } -bool32 IsHazardMoveEffect(u16 moveEffect) +bool32 IsHazardMoveEffect(u32 moveEffect) { switch (moveEffect) { @@ -1458,7 +1446,7 @@ bool32 IsHazardMoveEffect(u16 moveEffect) } } -bool32 IsMoveRedirectionPrevented(u16 move, u16 atkAbility) +bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility) { if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE) return FALSE; @@ -1471,13 +1459,13 @@ bool32 IsMoveRedirectionPrevented(u16 move, u16 atkAbility) return FALSE; } -u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 move) +u32 AI_GetMoveAccuracy(u32 battlerAtk, u32 battlerDef, u32 move) { 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) +bool32 IsSemiInvulnerable(u32 battlerDef, u32 move) { if (gStatuses3[battlerDef] & STATUS3_PHANTOM_FORCE) return TRUE; @@ -1491,8 +1479,9 @@ bool32 IsSemiInvulnerable(u8 battlerDef, u16 move) return FALSE; } -bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) +bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move) { + u32 weather; if (IsSemiInvulnerable(battlerDef, move)) return FALSE; @@ -1511,14 +1500,14 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) #endif // discouraged from hitting - if (AI_WeatherHasEffect() && (gBattleWeather & B_WEATHER_SUN) + weather = AI_GetWeather(AI_DATA); + if ((weather & B_WEATHER_SUN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) return FALSE; // increased accuracy but don't always hit - if ((AI_WeatherHasEffect() && - (((gBattleWeather & B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) - || (((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD)))) + if ((((weather & B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)) + || (((weather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD))) || (gBattleMoves[move].effect == EFFECT_VITAL_THROW) #if B_MINIMIZE_DMG_ACC >= GEN_6 || ((gStatuses3[battlerDef] & STATUS3_MINIMIZED) && gBattleMoves[move].minimizeDoubleDamage) @@ -1531,7 +1520,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move) return FALSE; } -bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move) +bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move) { u32 holdEffect = AI_DATA->holdEffects[battlerDef]; u32 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move); @@ -1554,9 +1543,9 @@ bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbilit } else // test the odds { - u16 odds = accuracy + (gBattleMons[battlerAtk].level - gBattleMons[battlerDef].level); + u32 odds = accuracy + (gBattleMons[battlerAtk].level - gBattleMons[battlerDef].level); #if B_SHEER_COLD_ACC >= GEN_7 - if (gCurrentMove == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)) + if (move == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)) odds -= 10; #endif if (Random() % 100 + 1 < odds && gBattleMons[battlerAtk].level >= gBattleMons[battlerDef].level) @@ -1565,11 +1554,10 @@ bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbilit return FALSE; } -bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect) +bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect) { - if (!AI_WeatherHasEffect()) - return FALSE; - else if (gBattleWeather & B_WEATHER_SANDSTORM) + u32 weather = AI_GetWeather(AI_DATA); + if (weather & B_WEATHER_SANDSTORM) return FALSE; if (ability == ABILITY_SAND_VEIL @@ -1589,11 +1577,10 @@ bool32 ShouldSetSandstorm(u8 battler, u16 ability, u16 holdEffect) return FALSE; } -bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect) +bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect) { - if (!AI_WeatherHasEffect()) - return FALSE; - else if (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) + u32 weather = AI_GetWeather(AI_DATA); + if (weather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) return FALSE; if (ability == ABILITY_SNOW_CLOAK @@ -1613,11 +1600,10 @@ bool32 ShouldSetHail(u8 battler, u16 ability, u16 holdEffect) return FALSE; } -bool32 ShouldSetRain(u8 battlerAtk, u16 atkAbility, u16 holdEffect) +bool32 ShouldSetRain(u32 battlerAtk, u32 atkAbility, u32 holdEffect) { - if (!AI_WeatherHasEffect()) - return FALSE; - else if (gBattleWeather & B_WEATHER_RAIN) + u32 weather = AI_GetWeather(AI_DATA); + if (weather & B_WEATHER_RAIN) return FALSE; if (holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA @@ -1636,11 +1622,10 @@ bool32 ShouldSetRain(u8 battlerAtk, u16 atkAbility, u16 holdEffect) return FALSE; } -bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect) +bool32 ShouldSetSun(u32 battlerAtk, u32 atkAbility, u32 holdEffect) { - if (!AI_WeatherHasEffect()) - return FALSE; - else if (gBattleWeather & B_WEATHER_SUN) + u32 weather = AI_GetWeather(AI_DATA); + if (weather & B_WEATHER_SUN) return FALSE; if (holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA @@ -1663,11 +1648,10 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect) return FALSE; } -bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) +bool32 ShouldSetSnow(u32 battler, u32 ability, u32 holdEffect) { - if (!AI_WeatherHasEffect()) - return FALSE; - else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL)) + u32 weather = AI_GetWeather(AI_DATA); + if (weather & (B_WEATHER_SNOW | B_WEATHER_HAIL)) return FALSE; if (ability == ABILITY_SNOW_CLOAK @@ -1684,11 +1668,11 @@ bool32 ShouldSetSnow(u8 battler, u16 ability, u16 holdEffect) return FALSE; } -void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s16 *score) +void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score) { // TODO more sophisticated logic - u16 predictedEffect = gBattleMoves[predictedMove].effect; - u8 defAbility = AI_DATA->abilities[battlerDef]; + u32 predictedEffect = gBattleMoves[predictedMove].effect; + u32 defAbility = AI_DATA->abilities[battlerDef]; u32 uses = gDisableStructs[battlerAtk].protectUses; /*if (GetMoveResultFlags(predictedMove) & (MOVE_RESULT_NO_EFFECT | MOVE_RESULT_MISSED)) @@ -1726,7 +1710,7 @@ void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s1 } // stat stages -bool32 ShouldLowerStat(u8 battler, u16 battlerAbility, u8 stat) +bool32 ShouldLowerStat(u32 battler, u32 battlerAbility, u32 stat) { if ((gBattleMons[battler].statStages[stat] > MIN_STAT_STAGE && battlerAbility != ABILITY_CONTRARY) || (battlerAbility == ABILITY_CONTRARY && gBattleMons[battler].statStages[stat] < MAX_STAT_STAGE)) @@ -1743,7 +1727,7 @@ bool32 ShouldLowerStat(u8 battler, u16 battlerAbility, u8 stat) return FALSE; } -bool32 BattlerStatCanRise(u8 battler, u16 battlerAbility, u8 stat) +bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat) { if ((gBattleMons[battler].statStages[stat] < MAX_STAT_STAGE && battlerAbility != ABILITY_CONTRARY) || (battlerAbility == ABILITY_CONTRARY && gBattleMons[battler].statStages[stat] > MIN_STAT_STAGE)) @@ -1751,7 +1735,7 @@ bool32 BattlerStatCanRise(u8 battler, u16 battlerAbility, u8 stat) return FALSE; } -bool32 AreBattlersStatsMaxed(u8 battlerId) +bool32 AreBattlersStatsMaxed(u32 battlerId) { u32 i; for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) @@ -1762,7 +1746,7 @@ bool32 AreBattlersStatsMaxed(u8 battlerId) return TRUE; } -bool32 AnyStatIsRaised(u8 battlerId) +bool32 AnyStatIsRaised(u32 battlerId) { u32 i; @@ -1774,7 +1758,7 @@ bool32 AnyStatIsRaised(u8 battlerId) return FALSE; } -u32 CountPositiveStatStages(u8 battlerId) +u32 CountPositiveStatStages(u32 battlerId) { u32 count = 0; u32 i; @@ -1786,7 +1770,7 @@ u32 CountPositiveStatStages(u8 battlerId) return count; } -u32 CountNegativeStatStages(u8 battlerId) +u32 CountNegativeStatStages(u32 battlerId) { u32 count = 0; u32 i; @@ -1798,9 +1782,9 @@ u32 CountNegativeStatStages(u8 battlerId) return count; } -bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4 @@ -1815,9 +1799,9 @@ bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4 @@ -1832,12 +1816,12 @@ bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. - if (!WillAIStrikeFirst() + if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && defAbility != ABILITY_CONTRARY && defAbility != ABILITY_CLEAR_BODY && defAbility != ABILITY_FULL_METAL_BODY @@ -1847,9 +1831,9 @@ bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4 @@ -1863,9 +1847,9 @@ bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4 @@ -1879,9 +1863,9 @@ bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (defAbility != ABILITY_CONTRARY @@ -1894,9 +1878,9 @@ bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility) { - if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) + if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return FALSE; // Don't bother lowering stats if can kill enemy. if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE @@ -1909,7 +1893,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility) return FALSE; } -bool32 CanIndexMoveFaintTarget(u8 battlerAtk, u8 battlerDef, u8 index, u8 numHits) +bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits) { s32 dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index]; @@ -1959,7 +1943,7 @@ bool32 HasMoveWithSplit(u32 battler, u32 split) return FALSE; } -bool32 HasMoveWithType(u32 battler, u8 type) +bool32 HasMoveWithType(u32 battler, u32 type) { s32 i; u16 *moves = GetMovesArray(battler); @@ -1973,7 +1957,7 @@ bool32 HasMoveWithType(u32 battler, u8 type) return FALSE; } -bool32 HasMoveEffect(u32 battlerId, u16 moveEffect) +bool32 HasMoveEffect(u32 battlerId, u32 moveEffect) { s32 i; u16 *moves = GetMovesArray(battlerId); @@ -2001,11 +1985,11 @@ bool32 HasMove(u32 battlerId, u32 move) return FALSE; } -bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 ignoreStatus, u16 atkAbility, u16 defAbility, u16 atkHoldEffect, u16 defHoldEffect) +bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect) { s32 i; u16 *moves = GetMovesArray(battlerAtk); - u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; + u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -2028,9 +2012,9 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 return FALSE; } -bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef) +bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef) { - u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; + u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk]; u32 i; u16 *moves = GetMovesArray(battlerAtk); @@ -2048,7 +2032,7 @@ bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef) return FALSE; } -bool32 IsHealingMoveEffect(u16 effect) +bool32 IsHealingMoveEffect(u32 effect) { switch (effect) { @@ -2084,7 +2068,7 @@ bool32 HasHealingEffect(u32 battlerId) return FALSE; } -bool32 IsTrappingMoveEffect(u16 effect) +bool32 IsTrappingMoveEffect(u32 effect) { switch (effect) { @@ -2099,7 +2083,7 @@ bool32 IsTrappingMoveEffect(u16 effect) } } -bool32 HasTrappingMoveEffect(u8 battler) +bool32 HasTrappingMoveEffect(u32 battler) { s32 i; u16 *moves = GetMovesArray(battler); @@ -2113,12 +2097,12 @@ bool32 HasTrappingMoveEffect(u8 battler) return FALSE; } -bool32 HasThawingMove(u8 battler) +bool32 HasThawingMove(u32 battler) { CHECK_MOVE_FLAG(thawsUser); } -bool32 IsUngroundingEffect(u16 effect) +bool32 IsUngroundingEffect(u32 effect) { switch (effect) { @@ -2130,7 +2114,7 @@ bool32 IsUngroundingEffect(u16 effect) } // for anger point -bool32 IsAttackBoostMoveEffect(u16 effect) +bool32 IsAttackBoostMoveEffect(u32 effect) { switch (effect) { @@ -2149,7 +2133,7 @@ bool32 IsAttackBoostMoveEffect(u16 effect) } } -bool32 IsStatRaisingEffect(u16 effect) +bool32 IsStatRaisingEffect(u32 effect) { switch (effect) { @@ -2195,7 +2179,7 @@ bool32 IsStatRaisingEffect(u16 effect) } } -bool32 IsStatLoweringEffect(u16 effect) +bool32 IsStatLoweringEffect(u32 effect) { // ignore other potentially-beneficial effects like defog, gravity switch (effect) @@ -2223,7 +2207,7 @@ bool32 IsStatLoweringEffect(u16 effect) } } -bool32 HasDamagingMove(u8 battlerId) +bool32 HasDamagingMove(u32 battlerId) { u32 i; u16 *moves = GetMovesArray(battlerId); @@ -2237,7 +2221,7 @@ bool32 HasDamagingMove(u8 battlerId) return FALSE; } -bool32 HasDamagingMoveOfType(u8 battlerId, u8 type) +bool32 HasDamagingMoveOfType(u32 battlerId, u32 type) { s32 i; u16 *moves = GetMovesArray(battlerId); @@ -2252,27 +2236,27 @@ bool32 HasDamagingMoveOfType(u8 battlerId, u8 type) return FALSE; } -bool32 HasSoundMove(u8 battler) +bool32 HasSoundMove(u32 battler) { CHECK_MOVE_FLAG(soundMove); } -bool32 HasHighCritRatioMove(u8 battler) +bool32 HasHighCritRatioMove(u32 battler) { CHECK_MOVE_FLAG(highCritRatio); } -bool32 HasMagicCoatAffectedMove(u8 battler) +bool32 HasMagicCoatAffectedMove(u32 battler) { CHECK_MOVE_FLAG(magicCoatAffected); } -bool32 HasSnatchAffectedMove(u8 battler) +bool32 HasSnatchAffectedMove(u32 battler) { CHECK_MOVE_FLAG(snatchAffected); } -bool32 IsEncoreEncouragedEffect(u16 moveEffect) +bool32 IsEncoreEncouragedEffect(u32 moveEffect) { u32 i; @@ -2284,7 +2268,7 @@ bool32 IsEncoreEncouragedEffect(u16 moveEffect) return FALSE; } -bool32 MoveRequiresRecharging(u16 move) +bool32 MoveRequiresRecharging(u32 move) { u32 i; for (i = 0; i < ARRAY_COUNT(sRechargeMoves); i++) @@ -2295,7 +2279,7 @@ bool32 MoveRequiresRecharging(u16 move) return FALSE; } -bool32 MoveCallsOtherMove(u16 move) +bool32 MoveCallsOtherMove(u32 move) { u32 i; for (i = 0; i < ARRAY_COUNT(sOtherMoveCallingMoves); i++) @@ -2306,7 +2290,7 @@ bool32 MoveCallsOtherMove(u16 move) return FALSE; } -static u32 GetLeechSeedDamage(u8 battlerId) +static u32 GetLeechSeedDamage(u32 battlerId) { u32 damage = 0; if ((gStatuses3[battlerId] & STATUS3_LEECHSEED) @@ -2319,7 +2303,7 @@ static u32 GetLeechSeedDamage(u8 battlerId) return damage; } -static u32 GetNightmareDamage(u8 battlerId) +static u32 GetNightmareDamage(u32 battlerId) { u32 damage = 0; if ((gBattleMons[battlerId].status2 & STATUS2_NIGHTMARE) && gBattleMons[battlerId].status1 & STATUS1_SLEEP) @@ -2331,7 +2315,7 @@ static u32 GetNightmareDamage(u8 battlerId) return damage; } -static u32 GetCurseDamage(u8 battlerId) +static u32 GetCurseDamage(u32 battlerId) { u32 damage = 0; if (gBattleMons[battlerId].status2 & STATUS2_CURSED) @@ -2343,7 +2327,7 @@ static u32 GetCurseDamage(u8 battlerId) return damage; } -static u32 GetTrapDamage(u8 battlerId) +static u32 GetTrapDamage(u32 battlerId) { // ai has no knowledge about turns remaining u32 damage = 0; @@ -2366,7 +2350,7 @@ static u32 GetTrapDamage(u8 battlerId) return damage; } -static u32 GetPoisonDamage(u8 battlerId) +static u32 GetPoisonDamage(u32 battlerId) { u32 damage = 0; @@ -2391,7 +2375,7 @@ static u32 GetPoisonDamage(u8 battlerId) return damage; } -static bool32 BattlerAffectedBySandstorm(u8 battlerId, u16 ability) +static bool32 BattlerAffectedBySandstorm(u32 battlerId, u32 ability) { if (!IS_BATTLER_OF_TYPE(battlerId, TYPE_ROCK) && !IS_BATTLER_OF_TYPE(battlerId, TYPE_GROUND) @@ -2404,7 +2388,7 @@ static bool32 BattlerAffectedBySandstorm(u8 battlerId, u16 ability) return FALSE; } -static bool32 BattlerAffectedByHail(u8 battlerId, u16 ability) +static bool32 BattlerAffectedByHail(u32 battlerId, u32 ability) { if (!IS_BATTLER_OF_TYPE(battlerId, TYPE_ICE) && ability != ABILITY_SNOW_CLOAK @@ -2414,15 +2398,16 @@ static bool32 BattlerAffectedByHail(u8 battlerId, u16 ability) return FALSE; } -static u32 GetWeatherDamage(u8 battlerId) +static u32 GetWeatherDamage(u32 battlerId) { u32 ability = AI_DATA->abilities[battlerId]; u32 holdEffect = AI_DATA->holdEffects[battlerId]; u32 damage = 0; - if (!AI_WeatherHasEffect()) + u32 weather = AI_GetWeather(AI_DATA); + if (!weather) return 0; - if (gBattleWeather & B_WEATHER_SANDSTORM) + if (weather & B_WEATHER_SANDSTORM) { if (BattlerAffectedBySandstorm(battlerId, ability) && !(gStatuses3[battlerId] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) @@ -2433,7 +2418,7 @@ static u32 GetWeatherDamage(u8 battlerId) damage = 1; } } - if ((gBattleWeather & B_WEATHER_HAIL) && ability != ABILITY_ICE_BODY) + if ((weather & B_WEATHER_HAIL) && ability != ABILITY_ICE_BODY) { if (BattlerAffectedByHail(battlerId, ability) && !(gStatuses3[battlerId] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) @@ -2447,7 +2432,7 @@ static u32 GetWeatherDamage(u8 battlerId) return damage; } -u32 GetBattlerSecondaryDamage(u8 battlerId) +u32 GetBattlerSecondaryDamage(u32 battlerId) { u32 secondaryDamage; @@ -2464,7 +2449,7 @@ u32 GetBattlerSecondaryDamage(u8 battlerId) return secondaryDamage; } -bool32 BattlerWillFaintFromWeather(u8 battler, u16 ability) +bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability) { if ((BattlerAffectedBySandstorm(battler, ability) || BattlerAffectedByHail(battler, ability)) && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16) @@ -2473,7 +2458,7 @@ bool32 BattlerWillFaintFromWeather(u8 battler, u16 ability) return FALSE; } -bool32 BattlerWillFaintFromSecondaryDamage(u8 battler, u16 ability) +bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability) { if (GetBattlerSecondaryDamage(battler) != 0 && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16) @@ -2481,9 +2466,9 @@ bool32 BattlerWillFaintFromSecondaryDamage(u8 battler, u16 ability) return FALSE; } -static bool32 AnyUsefulStatIsRaised(u8 battler) +static bool32 AnyUsefulStatIsRaised(u32 battler) { - u8 statId; + u32 statId; for (statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) { @@ -2508,7 +2493,7 @@ static bool32 AnyUsefulStatIsRaised(u8 battler) return FALSE; } -struct Pokemon *GetPartyBattlerPartyData(u8 battlerId, u8 switchBattler) +struct Pokemon *GetPartyBattlerPartyData(u32 battlerId, u32 switchBattler) { struct Pokemon *mon; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) @@ -2518,16 +2503,16 @@ struct Pokemon *GetPartyBattlerPartyData(u8 battlerId, u8 switchBattler) return mon; } -static bool32 PartyBattlerShouldAvoidHazards(u8 currBattler, u8 switchBattler) +static bool32 PartyBattlerShouldAvoidHazards(u32 currBattler, u32 switchBattler) { struct Pokemon *mon = GetPartyBattlerPartyData(currBattler, switchBattler); - u16 ability = GetMonAbility(mon); // we know our own party data - u16 holdEffect; - u16 species = GetMonData(mon, MON_DATA_SPECIES); + u32 ability = GetMonAbility(mon); // we know our own party data + u32 holdEffect; + u32 species = GetMonData(mon, MON_DATA_SPECIES); u32 flags = gSideStatuses[GetBattlerSide(currBattler)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES); s32 hazardDamage = 0; - u8 type1 = gSpeciesInfo[species].types[0]; - u8 type2 = gSpeciesInfo[species].types[1]; + u32 type1 = gSpeciesInfo[species].types[0]; + u32 type2 = gSpeciesInfo[species].types[1]; u32 maxHp = GetMonData(mon, MON_DATA_MAX_HP); if (flags == 0) @@ -2549,7 +2534,7 @@ static bool32 PartyBattlerShouldAvoidHazards(u8 currBattler, u8 switchBattler) && ability != ABILITY_LEVITATE && holdEffect != HOLD_EFFECT_AIR_BALLOON) || holdEffect == HOLD_EFFECT_IRON_BALL || gFieldStatuses & STATUS_FIELD_GRAVITY)) { - u8 spikesDmg = maxHp / ((5 - gSideTimers[GetBattlerSide(currBattler)].spikesAmount) * 2); + s32 spikesDmg = maxHp / ((5 - gSideTimers[GetBattlerSide(currBattler)].spikesAmount) * 2); if (spikesDmg == 0) spikesDmg = 1; hazardDamage += spikesDmg; @@ -2565,11 +2550,11 @@ enum { CAN_TRY_PIVOT, PIVOT, }; -bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 moveIndex) +bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex) { - bool8 hasStatBoost = AnyUsefulStatIsRaised(battlerAtk) || gBattleMons[battlerDef].statStages[STAT_EVASION] >= 9; //Significant boost in evasion for any class + bool32 hasStatBoost = AnyUsefulStatIsRaised(battlerAtk) || gBattleMons[battlerDef].statStages[STAT_EVASION] >= 9; //Significant boost in evasion for any class bool32 shouldSwitch; - u8 battlerToSwitch; + u32 battlerToSwitch; shouldSwitch = ShouldSwitch(battlerAtk); battlerToSwitch = *(gBattleStruct->AI_monToSwitchIntoId + battlerAtk); @@ -2627,8 +2612,8 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo /*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) { - bool8 physMoveInMoveset = PhysicalMoveInMoveset(battlerAtk); - bool8 specMoveInMoveset = SpecialMoveInMoveset(battlerAtk); + bool32 physMoveInMoveset = PhysicalMoveInMoveset(battlerAtk); + bool32 specMoveInMoveset = SpecialMoveInMoveset(battlerAtk); //Pivot if attacking stats are bad if (physMoveInMoveset && !specMoveInMoveset) @@ -2710,8 +2695,8 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo /*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE) { - bool8 physMoveInMoveset = PhysicalMoveInMoveset(battlerAtk); - bool8 specMoveInMoveset = SpecialMoveInMoveset(battlerAtk); + bool32 physMoveInMoveset = PhysicalMoveInMoveset(battlerAtk); + bool32 specMoveInMoveset = SpecialMoveInMoveset(battlerAtk); //Pivot if attacking stats are bad if (physMoveInMoveset && !specMoveInMoveset) @@ -2741,7 +2726,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo return DONT_PIVOT; } -bool32 CanKnockOffItem(u8 battler, u16 item) +bool32 CanKnockOffItem(u32 battler, u32 item) { if (item == ITEM_NONE) return FALSE; @@ -2767,7 +2752,7 @@ bool32 CanKnockOffItem(u8 battler, u16 item) } // status checks -bool32 IsBattlerIncapacitated(u8 battler, u16 ability) +bool32 IsBattlerIncapacitated(u32 battler, u32 ability) { if ((gBattleMons[battler].status1 & STATUS1_FREEZE) && !HasThawingMove(battler)) return TRUE; // if battler has thawing move we assume they will definitely use it, and thus being frozen should be neglected @@ -2781,7 +2766,7 @@ bool32 IsBattlerIncapacitated(u8 battler, u16 ability) return FALSE; } -bool32 AI_CanSleep(u8 battler, u16 ability) +bool32 AI_CanSleep(u32 battler, u32 ability) { if (ability == ABILITY_INSOMNIA || ability == ABILITY_VITAL_SPIRIT @@ -2794,7 +2779,7 @@ bool32 AI_CanSleep(u8 battler, u16 ability) return TRUE; } -bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove) +bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { if (!AI_CanSleep(battlerDef, defAbility) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -2803,17 +2788,17 @@ bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, return TRUE; } -static bool32 AI_CanPoisonType(u8 battlerAttacker, u8 battlerTarget) +static bool32 AI_CanPoisonType(u32 battlerAttacker, u32 battlerTarget, u32 move) { - return ((AI_DATA->abilities[battlerAttacker] == ABILITY_CORROSION && gBattleMoves[gCurrentMove].split == SPLIT_STATUS) + return ((AI_DATA->abilities[battlerAttacker] == ABILITY_CORROSION && gBattleMoves[move].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) +static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move) { - u16 ability = AI_DATA->abilities[battlerDef]; + u32 ability = AI_DATA->abilities[battlerDef]; - if (!(AI_CanPoisonType(battlerAtk, battlerDef)) + if (!(AI_CanPoisonType(battlerAtk, battlerDef, move)) || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || gBattleMons[battlerDef].status1 & STATUS1_ANY || ability == ABILITY_IMMUNITY @@ -2826,9 +2811,9 @@ static bool32 AI_CanBePoisoned(u8 battlerAtk, u8 battlerDef) return TRUE; } -bool32 ShouldPoisonSelf(u8 battler, u16 ability) +bool32 ShouldPoisonSelf(u32 battler, u32 ability) { - if (AI_CanBePoisoned(battler, battler) && ( + if (AI_CanBePoisoned(battler, battler, 0) && ( ability == ABILITY_MARVEL_SCALE || ability == ABILITY_POISON_HEAL || ability == ABILITY_QUICK_FEET @@ -2841,9 +2826,9 @@ bool32 ShouldPoisonSelf(u8 battler, u16 ability) return FALSE; } -bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove) +bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBePoisoned(battlerAtk, battlerDef) + if (!AI_CanBePoisoned(battlerAtk, battlerDef, move) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) @@ -2856,7 +2841,7 @@ bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 return TRUE; } -static bool32 AI_CanBeParalyzed(u8 battler, u16 ability) +static bool32 AI_CanBeParalyzed(u32 battler, u32 ability) { if (ability == ABILITY_LIMBER || ability == ABILITY_COMATOSE @@ -2867,7 +2852,7 @@ static bool32 AI_CanBeParalyzed(u8 battler, u16 ability) return TRUE; } -bool32 AI_CanParalyze(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove) +bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { if (!AI_CanBeParalyzed(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 @@ -2878,7 +2863,7 @@ bool32 AI_CanParalyze(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u1 return TRUE; } -bool32 AI_CanBeConfused(u8 battler, u16 ability) +bool32 AI_CanBeConfused(u32 battler, u32 ability) { if ((gBattleMons[battler].status2 & STATUS2_CONFUSION) || (ability == ABILITY_OWN_TEMPO) @@ -2887,7 +2872,7 @@ bool32 AI_CanBeConfused(u8 battler, u16 ability) return TRUE; } -bool32 AI_CanConfuse(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove) +bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { if (!AI_CanBeConfused(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 @@ -2901,7 +2886,7 @@ bool32 AI_CanConfuse(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtk return TRUE; } -bool32 AI_CanBeBurned(u8 battler, u16 ability) +bool32 AI_CanBeBurned(u32 battler, u32 ability) { if (ability == ABILITY_WATER_VEIL || ability == ABILITY_WATER_BUBBLE @@ -2914,7 +2899,7 @@ bool32 AI_CanBeBurned(u8 battler, u16 ability) return TRUE; } -bool32 AI_CanGetFrostbite(u8 battler, u16 ability) +bool32 AI_CanGetFrostbite(u32 battler, u32 ability) { if (ability == ABILITY_MAGMA_ARMOR || ability == ABILITY_COMATOSE @@ -2926,7 +2911,7 @@ bool32 AI_CanGetFrostbite(u8 battler, u16 ability) return TRUE; } -bool32 ShouldBurnSelf(u8 battler, u16 ability) +bool32 ShouldBurnSelf(u32 battler, u32 ability) { if (AI_CanBeBurned(battler, ability) && ( ability == ABILITY_QUICK_FEET @@ -2940,7 +2925,7 @@ bool32 ShouldBurnSelf(u8 battler, u16 ability) return FALSE; } -bool32 AI_CanBurn(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove) +bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { if (!AI_CanBeBurned(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 @@ -2952,7 +2937,7 @@ bool32 AI_CanBurn(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPar return TRUE; } -bool32 AI_CanGiveFrostbite(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove) +bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { if (!AI_CanGetFrostbite(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 @@ -2964,7 +2949,7 @@ bool32 AI_CanGiveFrostbite(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 batt return TRUE; } -bool32 AI_CanBeInfatuated(u8 battlerAtk, u8 battlerDef, u16 defAbility) +bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility) { if ((gBattleMons[battlerDef].status2 & STATUS2_INFATUATION) || AI_GetMoveEffectiveness(AI_THINKING_STRUCT->moveConsidered, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 @@ -2975,7 +2960,7 @@ bool32 AI_CanBeInfatuated(u8 battlerAtk, u8 battlerDef, u16 defAbility) return TRUE; } -u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move) +u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbility, u32 move) { if (((AI_DATA->abilities[battlerAtk] != ABILITY_MOLD_BREAKER && (defAbility == ABILITY_SHIELD_DUST || defAbility == ABILITY_INNER_FOCUS)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK @@ -2996,7 +2981,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili return 0; // don't try to flinch } -bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move) +bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move) { if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef])) return TRUE; // battler is taking secondary damage with low HP @@ -3010,7 +2995,7 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move) return FALSE; } -bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move) +bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move) { if (!gDisableStructs[battlerAtk].isFirstTurn || AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS @@ -3035,7 +3020,7 @@ static u32 FindMoveUsedXTurnsAgo(u32 battlerId, u32 x) return BATTLE_HISTORY->moveHistory[battlerId][index]; } -bool32 IsWakeupTurn(u8 battler) +bool32 IsWakeupTurn(u32 battler) { // Check if rest was used 2 turns ago if ((gBattleMons[battler].status1 & STATUS1_SLEEP) == 1 && FindMoveUsedXTurnsAgo(battler, 2) == MOVE_REST) @@ -3044,7 +3029,7 @@ bool32 IsWakeupTurn(u8 battler) return FALSE; } -bool32 AnyPartyMemberStatused(u8 battlerId, bool32 checkSoundproof) +bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) { struct Pokemon *party; u32 i; @@ -3066,28 +3051,28 @@ bool32 AnyPartyMemberStatused(u8 battlerId, bool32 checkSoundproof) return FALSE; } -u16 GetBattlerSideSpeedAverage(u8 battler) +u32 GetBattlerSideSpeedAverage(u32 battler) { - u16 speed1 = 0; - u16 speed2 = 0; - u8 numBattlersAlive = 0; + u32 speed1 = 0; + u32 speed2 = 0; + u32 numBattlersAlive = 0; if (IsBattlerAlive(battler)) { - speed1 = GetBattlerTotalSpeedStat(battler); + speed1 = AI_DATA->speedStats[battler]; numBattlersAlive++; } if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler))) { - speed2 = GetBattlerTotalSpeedStat(BATTLE_PARTNER(battler)); + speed2 = AI_DATA->speedStats[BATTLE_PARTNER(battler)]; numBattlersAlive++; } return (speed1 + speed2) / numBattlersAlive; } -bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveIndex) +bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex) { if (recoilDmg >= gBattleMons[battlerAtk].hp //Recoil kills attacker && CountUsablePartyMons(battlerDef) != 0) //Foe has more than 1 target left @@ -3101,12 +3086,12 @@ bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveI return TRUE; } -bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage) +bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage) { if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) { // using item or user goes first - u8 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument; + u32 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument; s32 healDmg = (healPercent * damage) / 100; if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK) @@ -3128,7 +3113,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage) return FALSE; } -bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent) +bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent) { if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) { @@ -3147,14 +3132,14 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent) return FALSE; } -bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect) +bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect) { - u8 atkSide = GetBattlerSide(battlerAtk); + u32 atkSide = GetBattlerSide(battlerAtk); switch (moveEffect) { case EFFECT_AURORA_VEIL: // Use only in Hail and only if AI doesn't already have Reflect, Light Screen or Aurora Veil itself active. - if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) + if ((AI_GetWeather(AI_DATA) & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && !(gSideStatuses[atkSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL))) return TRUE; break; @@ -3176,7 +3161,7 @@ bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect) } // Partner Logic -bool32 IsValidDoubleBattle(u8 battlerAtk) +bool32 IsValidDoubleBattle(u32 battlerAtk) { if (IsDoubleBattle() && ((IsBattlerAlive(BATTLE_OPPOSITE(battlerAtk)) && IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerAtk)))) || IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))) @@ -3184,9 +3169,9 @@ bool32 IsValidDoubleBattle(u8 battlerAtk) return FALSE; } -u16 GetAllyChosenMove(u8 battlerId) +u32 GetAllyChosenMove(u32 battlerId) { - u8 partnerBattler = BATTLE_PARTNER(battlerId); + u32 partnerBattler = BATTLE_PARTNER(battlerId); if (!IsBattlerAlive(partnerBattler) || !IsAiBattlerAware(partnerBattler)) return MOVE_NONE; @@ -3196,16 +3181,8 @@ u16 GetAllyChosenMove(u8 battlerId) return gBattleMons[partnerBattler].moves[gBattleStruct->chosenMovePositions[partnerBattler]]; } -bool32 IsTargetingPartner(u8 battlerAtk, u8 battlerDef) -{ - if ((battlerAtk & BIT_SIDE) == (battlerDef & BIT_SIDE)) - return TRUE; - - return FALSE; -} - //PARTNER_MOVE_EFFECT_IS_SAME -bool32 DoesPartnerHaveSameMoveEffect(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove) +bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3220,7 +3197,7 @@ bool32 DoesPartnerHaveSameMoveEffect(u8 battlerAtkPartner, u8 battlerDef, u16 mo } //PARTNER_MOVE_EFFECT_IS_SAME_NO_TARGET -bool32 PartnerHasSameMoveEffectWithoutTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove) +bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3232,7 +3209,7 @@ bool32 PartnerHasSameMoveEffectWithoutTarget(u8 battlerAtkPartner, u16 move, u16 } //PARTNER_MOVE_EFFECT_IS_STATUS_SAME_TARGET -bool32 PartnerMoveEffectIsStatusSameTarget(u8 battlerAtkPartner, u8 battlerDef, u16 partnerMove) +bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3250,7 +3227,7 @@ bool32 PartnerMoveEffectIsStatusSameTarget(u8 battlerAtkPartner, u8 battlerDef, } //PARTNER_MOVE_EFFECT_IS_WEATHER -bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove) +bool32 PartnerMoveEffectIsWeather(u32 battlerAtkPartner, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3267,7 +3244,7 @@ bool32 PartnerMoveEffectIsWeather(u8 battlerAtkPartner, u16 partnerMove) } //PARTNER_MOVE_EFFECT_IS_TERRAIN -bool32 PartnerMoveEffectIsTerrain(u8 battlerAtkPartner, u16 partnerMove) +bool32 PartnerMoveEffectIsTerrain(u32 battlerAtkPartner, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3283,7 +3260,7 @@ bool32 PartnerMoveEffectIsTerrain(u8 battlerAtkPartner, u16 partnerMove) } //PARTNER_MOVE_IS_TAILWIND_TRICKROOM -bool32 PartnerMoveIs(u8 battlerAtkPartner, u16 partnerMove, u16 moveCheck) +bool32 PartnerMoveIs(u32 battlerAtkPartner, u32 partnerMove, u32 moveCheck) { if (!IsDoubleBattle()) return FALSE; @@ -3294,7 +3271,7 @@ bool32 PartnerMoveIs(u8 battlerAtkPartner, u16 partnerMove, u16 moveCheck) } //PARTNER_MOVE_IS_SAME -bool32 PartnerMoveIsSameAsAttacker(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove) +bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3305,7 +3282,7 @@ bool32 PartnerMoveIsSameAsAttacker(u8 battlerAtkPartner, u8 battlerDef, u16 move } //PARTNER_MOVE_IS_SAME_NO_TARGET -bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove) +bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove) { if (!IsDoubleBattle()) return FALSE; @@ -3314,7 +3291,7 @@ bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove return FALSE; } -bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move) +bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move) { u32 i; u32 firstId, lastId; @@ -3335,8 +3312,8 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move) for (i = 0; i < PARTY_SIZE; i++) { - u16 currHp = GetMonData(&party[i], MON_DATA_HP); - u16 maxHp = GetMonData(&party[i], MON_DATA_MAX_HP); + u32 currHp = GetMonData(&party[i], MON_DATA_HP); + u32 maxHp = GetMonData(&party[i], MON_DATA_MAX_HP); if (!GetMonData(&party[i], MON_DATA_IS_EGG, NULL) && currHp > 0) { @@ -3418,7 +3395,7 @@ s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon if (move != MOVE_NONE && gBattleMoves[move].power != 0) { - dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE); + dmg = AI_CalcDamageSaveBattlers(move, battlerAtk, battlerDef, &effectiveness, FALSE); if (dmg > bestDmg) bestDmg = dmg; } @@ -3428,7 +3405,7 @@ s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon return dmg; } -s32 CountUsablePartyMons(u8 battlerId) +s32 CountUsablePartyMons(u32 battlerId) { s32 battlerOnField1, battlerOnField2, i, ret; struct Pokemon *party; @@ -3464,7 +3441,7 @@ s32 CountUsablePartyMons(u8 battlerId) return ret; } -bool32 IsPartyFullyHealedExceptBattler(u8 battlerId) +bool32 IsPartyFullyHealedExceptBattler(u32 battlerId) { struct Pokemon *party; u32 i; @@ -3486,9 +3463,9 @@ bool32 IsPartyFullyHealedExceptBattler(u8 battlerId) return TRUE; } -bool32 PartyHasMoveSplit(u8 battlerId, u8 split) +bool32 PartyHasMoveSplit(u32 battlerId, u32 split) { - u8 firstId, lastId; + u32 firstId, lastId; struct Pokemon *party = GetBattlerParty(battlerId); u32 i, j; @@ -3499,8 +3476,8 @@ bool32 PartyHasMoveSplit(u8 battlerId, u8 split) for (j = 0; j < MAX_MON_MOVES; j++) { - u16 move = GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL); - u16 pp = GetMonData(&party[i], MON_DATA_PP1 + j, NULL); + u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL); + u32 pp = GetMonData(&party[i], MON_DATA_PP1 + j, NULL); if (pp > 0 && move != MOVE_NONE) { @@ -3514,7 +3491,7 @@ bool32 PartyHasMoveSplit(u8 battlerId, u8 split) return FALSE; } -bool32 SideHasMoveSplit(u8 battlerId, u8 split) +bool32 SideHasMoveSplit(u32 battlerId, u32 split) { if (IsDoubleBattle()) { @@ -3529,14 +3506,14 @@ bool32 SideHasMoveSplit(u8 battlerId, u8 split) return FALSE; } -bool32 IsAbilityOfRating(u16 ability, s8 rating) +bool32 IsAbilityOfRating(u32 ability, s8 rating) { if (sAiAbilityRatings[ability] >= rating) return TRUE; return FALSE; } -s8 GetAbilityRating(u16 ability) +s8 GetAbilityRating(u32 ability) { return sAiAbilityRatings[ability]; } @@ -3555,7 +3532,7 @@ static const u16 sRecycleEncouragedItems[] = }; // Its assumed that the berry is strategically given, so no need to check benefits of the berry -bool32 IsStatBoostingBerry(u16 item) +bool32 IsStatBoostingBerry(u32 item) { switch (item) { @@ -3573,7 +3550,7 @@ bool32 IsStatBoostingBerry(u16 item) } } -bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item) +bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item) { switch (item) { @@ -3593,7 +3570,7 @@ bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item) } } -bool32 IsRecycleEncouragedItem(u16 item) +bool32 IsRecycleEncouragedItem(u32 item) { u32 i; for (i = 0; i < ARRAY_COUNT(sRecycleEncouragedItems); i++) @@ -3607,7 +3584,7 @@ bool32 IsRecycleEncouragedItem(u16 item) // score increases #define STAT_UP_2_STAGE 8 #define STAT_UP_STAGE 10 -void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) +void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) { if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY) return; @@ -3642,7 +3619,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) } break; case STAT_SPEED: - if (!WillAIStrikeFirst()) + if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)) { if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE) *score += 2; @@ -3687,7 +3664,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score) } } -void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -3711,7 +3688,7 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) } } -void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -3731,7 +3708,7 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) } } -void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -3739,8 +3716,8 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove)) { - u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk); - u8 defSpeed = GetBattlerTotalSpeedStat(battlerDef); + u32 atkSpeed = AI_DATA->speedStats[battlerAtk]; + u32 defSpeed = AI_DATA->speedStats[battlerDef]; if ((defSpeed >= atkSpeed && defSpeed / 2 < atkSpeed) // You'll go first after paralyzing foe || HasMoveEffect(battlerAtk, EFFECT_HEX) @@ -3753,7 +3730,7 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) } } -void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -3772,7 +3749,7 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) (*score)++; } -void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -3791,7 +3768,7 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) } } -void IncreaseFrostbiteScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) +void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) { if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; @@ -3810,7 +3787,7 @@ void IncreaseFrostbiteScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score) } } -bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move) +bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move) { if (gBattleMoves[move].makesContact && ability != ABILITY_LONG_REACH @@ -3820,7 +3797,7 @@ bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move) } //TODO - this could use some more sophisticated logic -bool32 ShouldUseZMove(u8 battlerAtk, u8 battlerDef, u16 chosenMove) +bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove) { // simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && battlerDef == BATTLE_PARTNER(battlerAtk)) @@ -3842,7 +3819,7 @@ bool32 ShouldUseZMove(u8 battlerAtk, u8 battlerDef, u16 chosenMove) else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove)) return FALSE; - if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamage(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE) >= gBattleMons[battlerDef].hp) + if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE) >= gBattleMons[battlerDef].hp) return FALSE; // don't waste damaging z move if can otherwise faint target return TRUE; @@ -3851,7 +3828,7 @@ bool32 ShouldUseZMove(u8 battlerAtk, u8 battlerDef, u16 chosenMove) return FALSE; } -bool32 AI_IsBattlerAsleepOrComatose(u8 battlerId) +bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId) { return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerId] == ABILITY_COMATOSE; } diff --git a/src/battle_main.c b/src/battle_main.c index da102b0e9..8461873f0 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -750,7 +750,7 @@ static void SetAllPlayersBerryData(void) { s32 numPlayers; struct BattleEnigmaBerry *src; - u8 battler; + u32 battler; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { @@ -2267,7 +2267,7 @@ static void EndLinkBattleInSteps(void) case 2: if (!gPaletteFade.active) { - u8 battlerCount; + u32 battlerCount; gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag(); @@ -3723,7 +3723,7 @@ static void DoBattleIntro(void) gBattleStruct->switchInAbilitiesCounter = 0; gBattleStruct->switchInItemsCounter = 0; gBattleStruct->overworldWeatherDone = FALSE; - SetAiLogicDataForTurn(); // get assumed abilities, hold effects, etc of all battlers + SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. gBattleMainFunc = TryDoEventsBeforeFirstTurn; } @@ -3770,7 +3770,7 @@ static void TryDoEventsBeforeFirstTurn(void) { for (j = i + 1; j < gBattlersCount; j++) { - if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0) + if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0) SwapTurnOrder(i, j); } } @@ -3856,7 +3856,7 @@ static void TryDoEventsBeforeFirstTurn(void) gRandomTurnNumber = Random(); - SetAiLogicDataForTurn(); // get assumed abilities, hold effects, etc of all battlers + SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers if (gBattleTypeFlags & BATTLE_TYPE_ARENA) { @@ -3948,7 +3948,7 @@ void BattleTurnPassed(void) *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); - SetAiLogicDataForTurn(); // get assumed abilities, hold effects, etc of all battlers + SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers gBattleMainFunc = HandleTurnActionSelectionState; gRandomTurnNumber = Random(); @@ -4020,11 +4020,10 @@ u8 IsRunningFromBattleImpossible(u32 battler) return BATTLE_RUN_SUCCESS; } -void SwitchPartyOrder(u8 battler) +void SwitchPartyOrder(u32 battler) { s32 i; - u8 partyId1; - u8 partyId2; + u32 partyId1, partyId2; for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)); @@ -4070,7 +4069,7 @@ static void HandleTurnActionSelectionState(void) gBattleCommunication[ACTIONS_CONFIRMED_COUNT] = 0; for (battler = 0; battler < gBattlersCount; battler++) { - u8 position = GetBattlerPosition(battler); + u32 position = GetBattlerPosition(battler); switch (gBattleCommunication[battler]) { case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn. @@ -4083,7 +4082,7 @@ static void HandleTurnActionSelectionState(void) { gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler); } - break; + // fallthrough case STATE_BEFORE_ACTION_CHOSEN: // Choose an action. *(gBattleStruct->monToSwitchIntoId + battler) = PARTY_SIZE; if (gBattleTypeFlags & BATTLE_TYPE_MULTI @@ -4597,11 +4596,10 @@ void SwapTurnOrder(u8 id1, u8 id2) SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp); } -u32 GetBattlerTotalSpeedStat(u8 battler) +// For AI, so it doesn't 'cheat' by knowing player's ability +u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) { u32 speed = gBattleMons[battler].speed; - u32 ability = GetBattlerAbility(battler); - u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); u32 highestStat = GetHighestStatId(battler); // weather abilities @@ -4668,6 +4666,13 @@ u32 GetBattlerTotalSpeedStat(u8 battler) return speed; } +u32 GetBattlerTotalSpeedStat(u32 battler) +{ + u32 ability = GetBattlerAbility(battler); + u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); + return GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); +} + s8 GetChosenMovePriority(u32 battler) { u16 move; @@ -4732,17 +4737,13 @@ s8 GetMovePriority(u32 battler, u16 move) return priority; } -u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) +// Function for AI with variables provided as arguments to speed the computation time +u32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2, + u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2) { - u8 strikesFirst = 0; - u32 speedBattler1 = 0, speedBattler2 = 0; - u32 holdEffectBattler1 = 0, holdEffectBattler2 = 0; - s8 priority1 = 0, priority2 = 0; - u16 ability1 = GetBattlerAbility(battler1), ability2 = GetBattlerAbility(battler2); + u32 strikesFirst = 0; // Battler 1 - speedBattler1 = GetBattlerTotalSpeedStat(battler1); - holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE); // Quick Draw if (!ignoreChosenMoves && ability1 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler1]) && Random() % 100 < 30) gProtectStructs[battler1].quickDraw = TRUE; @@ -4753,8 +4754,6 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) gProtectStructs[battler1].usedCustapBerry = TRUE; // Battler 2 - speedBattler2 = GetBattlerTotalSpeedStat(battler2); - holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE); // Quick Draw if (!ignoreChosenMoves && ability2 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler2]) && Random() % 100 < 30) gProtectStructs[battler2].quickDraw = TRUE; @@ -4764,14 +4763,6 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) || (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, 4, gBattleMons[battler2].item)))) gProtectStructs[battler2].usedCustapBerry = TRUE; - if (!ignoreChosenMoves) - { - if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE) - priority1 = GetChosenMovePriority(battler1); - if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE) - priority2 = GetChosenMovePriority(battler2); - } - if (priority1 == priority2) { // QUICK CLAW / CUSTAP - always first @@ -4834,6 +4825,28 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) return strikesFirst; } +u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves) +{ + s32 priority1 = 0, priority2 = 0; + u32 ability1 = GetBattlerAbility(battler1); + u32 speedBattler1 = GetBattlerTotalSpeedStat(battler1); + u32 holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE); + u32 speedBattler2 = GetBattlerTotalSpeedStat(battler2); + u32 holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE); + u32 ability2 = GetBattlerAbility(battler2); + + if (!ignoreChosenMoves) + { + if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE) + priority1 = GetChosenMovePriority(battler1); + if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE) + priority2 = GetChosenMovePriority(battler2); + } + + return GetWhichBattlerFasterArgs(battler1, battler2, ignoreChosenMoves, ability1, ability2, + holdEffectBattler1, holdEffectBattler2, speedBattler1, speedBattler2, priority1, priority2); +} + static void SetActionsAndBattlersTurnOrder(void) { s32 turnOrderId = 0; @@ -4927,7 +4940,7 @@ static void SetActionsAndBattlersTurnOrder(void) && gActionsByTurnOrder[i] != B_ACTION_THROW_BALL && gActionsByTurnOrder[j] != B_ACTION_THROW_BALL) { - if (GetWhoStrikesFirst(battler1, battler2, FALSE)) + if (GetWhichBattlerFaster(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } } @@ -5091,7 +5104,7 @@ static void TryChangeTurnOrder(void) if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) { - if (GetWhoStrikesFirst(battler1, battler2, FALSE)) + if (GetWhichBattlerFaster(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } } @@ -5574,7 +5587,7 @@ void RunBattleScriptCommands(void) gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } -void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) +void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) { u32 moveType, ateType, attackerAbility; u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE); @@ -5713,8 +5726,8 @@ void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) // var8001 - var8007: stat changes void SetTotemBoost(void) { - u8 battler = gSpecialVar_0x8000; - u8 i; + u32 battler = gSpecialVar_0x8000; + u32 i; for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index af6b9bf68..25fd8abe1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1753,10 +1753,11 @@ static void Cmd_accuracycheck(void) { CMD_ARGS(const u8 *failInstr, u16 move); - u16 type, move = cmd->move; - u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); - u16 gBattlerAttackerAbility = GetBattlerAbility(gBattlerAttacker); - u8 gBattlerAttackerHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); + u32 type, move = cmd->move; + u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); + u32 abilityDef = GetBattlerAbility(gBattlerTarget); + u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE); if (move == ACC_CURR_MOVE) move = gCurrentMove; @@ -1772,7 +1773,7 @@ static void Cmd_accuracycheck(void) } else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT || (gSpecialStatuses[gBattlerAttacker].multiHitOn - && (gBattlerAttackerAbility == ABILITY_SKILL_LINK || gBattlerAttackerHoldEffect == HOLD_EFFECT_LOADED_DICE + && (abilityAtk == ABILITY_SKILL_LINK || holdEffectAtk == HOLD_EFFECT_LOADED_DICE || !(gBattleMoves[move].effect == EFFECT_TRIPLE_KICK || gBattleMoves[move].effect == EFFECT_POPULATION_BOMB)))) { // No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb @@ -1792,16 +1793,16 @@ static void Cmd_accuracycheck(void) gBattlerAttacker, gBattlerTarget, move, - gBattlerAttackerAbility, - GetBattlerAbility(gBattlerTarget), - gBattlerAttackerHoldEffect, + abilityAtk, + abilityDef, + holdEffectAtk, GetBattlerHoldEffect(gBattlerTarget, TRUE) ); if (!RandomPercentage(RNG_ACCURACY, accuracy)) { gMoveResultFlags |= MOVE_RESULT_MISSED; - if (gBattlerAttackerHoldEffect == HOLD_EFFECT_BLUNDER_POLICY) + if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY) gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && @@ -1811,7 +1812,7 @@ static void Cmd_accuracycheck(void) gBattleCommunication[MISS_TYPE] = B_MSG_MISSED; if (gBattleMoves[move].power) - CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, TRUE); + CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, abilityDef, TRUE); } JumpIfMoveFailed(7, move); } @@ -1908,24 +1909,15 @@ static void Cmd_ppreduce(void) #endif // B_CRIT_CHANCE #define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD)) -s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility) +s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk) { s32 critChance = 0; - u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); - u32 abilityDef = GetBattlerAbility(gBattlerTarget); - u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT - || gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) + || gStatuses3[battlerAtk] & STATUS3_CANT_SCORE_A_CRIT) { critChance = -1; } - else if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR) - { - if (recordAbility) - RecordAbilityBattle(battlerDef, abilityDef); - critChance = -1; - } else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS || gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) @@ -1935,28 +1927,42 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi } else { - critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) + critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0) + (gBattleMoves[gCurrentMove].highCritRatio) + (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS) - + 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY) + + 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[battlerAtk].species == SPECIES_CHANSEY) + 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk) #if B_AFFECTION_MECHANICS == TRUE - + 2 * (GetBattlerFriendshipScore(gBattlerAttacker) >= FRIENDSHIP_200_TO_254) + + 2 * (GetBattlerFriendshipScore(battlerAtk) >= FRIENDSHIP_200_TO_254) #endif + (abilityAtk == ABILITY_SUPER_LUCK); + if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR) + { + if (recordAbility && critChance >= 2) // Record ability only if move had at least +1 chance to get a crit. + RecordAbilityBattle(battlerDef, abilityDef); + critChance = -1; + } + if (critChance >= ARRAY_COUNT(sCriticalHitChance)) critChance = ARRAY_COUNT(sCriticalHitChance) - 1; } return critChance; } + +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility) +{ + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); + u32 abilityDef = GetBattlerAbility(gBattlerTarget); + u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + return CalcCritChanceStageArgs(battlerAtk, battlerDef, move, recordAbility, abilityAtk, abilityDef, holdEffectAtk); +} #undef BENEFITS_FROM_LEEK -s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move) +s32 GetCritHitChance(s32 critChanceIndex) { - s32 critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE); - if(critChanceIndex < 0) + if (critChanceIndex < 0) return -1; else return sCriticalHitChance[critChanceIndex]; @@ -2006,7 +2012,7 @@ static void Cmd_typecalc(void) u8 moveType; GET_MOVE_TYPE(gCurrentMove, moveType); - CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, TRUE); + CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -5679,6 +5685,7 @@ static void Cmd_moveend(void) if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) { gLastMoves[gBattlerAttacker] = gChosenMove; + RecordKnownMove(gBattlerAttacker, gChosenMove); gLastResultingMoves[gBattlerAttacker] = gCurrentMove; } } @@ -8197,7 +8204,7 @@ static void RemoveAllTerrains(void) } \ } -static bool32 TryDefogClear(u8 battlerAtk, bool32 clear) +static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) { s32 i; for (i = 0; i < 2; i++) @@ -14703,7 +14710,7 @@ static void Cmd_settypebasedhalvers(void) gBattlescriptCurrInstr = cmd->failInstr; } -bool32 DoesSubstituteBlockMove(u8 battlerAtk, u8 battlerDef, u32 move) +bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move) { if (!(gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE)) return FALSE; @@ -14719,7 +14726,7 @@ bool32 DoesSubstituteBlockMove(u8 battlerAtk, u8 battlerDef, u32 move) return TRUE; } -bool32 DoesDisguiseBlockMove(u8 battlerAtk, u8 battlerDef, u32 move) +bool32 DoesDisguiseBlockMove(u32 battlerAtk, u32 battlerDef, u32 move) { if (gBattleMons[battlerDef].species != SPECIES_MIMIKYU || gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED @@ -15781,7 +15788,7 @@ static const u16 sParentalBondBannedEffects[] = EFFECT_UPROAR, }; -bool8 IsMoveAffectedByParentalBond(u16 move, u8 battler) +bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler) { if (move != MOVE_NONE && move != MOVE_STRUGGLE && gBattleMoves[move].split != SPLIT_STATUS diff --git a/src/battle_util.c b/src/battle_util.c index 79a711830..76ac8ee87 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -57,14 +57,14 @@ match the ROM; this is also why sSoundMovesTable's declaration is in the middle functions instead of at the top of the file with the other declarations. */ -static bool32 TryRemoveScreens(u8 battler); -static bool32 IsUnnerveAbilityOnOpposingSide(u8 battler); -static u8 GetFlingPowerFromItemId(u16 itemId); +static bool32 TryRemoveScreens(u32 battler); +static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler); +static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); -static u32 GetBattlerItemHoldEffectParam(u8 battler, u16 item); +static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static uq4_12_t GetInverseTypeMultiplier(uq4_12_t multiplier); -static uq4_12_t GetSupremeOverlordModifier(u8 battler); -static bool8 CanBeInfinitelyConfused(u8 battler); +static uq4_12_t GetSupremeOverlordModifier(u32 battler); +static bool32 CanBeInfinitelyConfused(u32 battler); extern const u8 *const gBattleScriptsForMoveEffects[]; extern const u8 *const gBattlescriptsForRunningByItem[]; @@ -557,9 +557,9 @@ void HandleAction_UseItem(void) gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } -bool8 TryRunFromBattle(u8 battler) +bool32 TryRunFromBattle(u32 battler) { - bool8 effect = FALSE; + bool32 effect = FALSE; u8 holdEffect; u8 pyramidMultiplier; u8 speedVar; @@ -864,8 +864,8 @@ void HandleAction_ActionFinished(void) { for (j = i + 1; j < gBattlersCount; j++) { - u8 battler1 = gBattlerByTurnOrder[i]; - u8 battler2 = gBattlerByTurnOrder[j]; + u32 battler1 = gBattlerByTurnOrder[i]; + u32 battler2 = gBattlerByTurnOrder[j]; if (gProtectStructs[battler1].quash || gProtectStructs[battler2].quash || gProtectStructs[battler1].shellTrap || gProtectStructs[battler2].shellTrap) @@ -875,12 +875,12 @@ void HandleAction_ActionFinished(void) // have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)) { - if (GetWhoStrikesFirst(battler1, battler2, FALSE)) + if (GetWhichBattlerFaster(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } else if ((gActionsByTurnOrder[i] == B_ACTION_SWITCH && gActionsByTurnOrder[j] == B_ACTION_SWITCH)) { - if (GetWhoStrikesFirst(battler1, battler2, TRUE)) // If the actions chosen are switching, we recalc order but ignoring the moves + if (GetWhichBattlerFaster(battler1, battler2, TRUE)) // If the actions chosen are switching, we recalc order but ignoring the moves SwapTurnOrder(i, j); } } @@ -1315,7 +1315,7 @@ static void MarkAllBattlersForControllerExec(void) } } -bool32 IsBattlerMarkedForControllerExec(u8 battler) +bool32 IsBattlerMarkedForControllerExec(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) return (gBattleControllerExecFlags & (gBitTable[battler] << 0x1C)) != 0; @@ -1323,7 +1323,7 @@ bool32 IsBattlerMarkedForControllerExec(u8 battler) return (gBattleControllerExecFlags & (gBitTable[battler])) != 0; } -void MarkBattlerForControllerExec(u8 battler) +void MarkBattlerForControllerExec(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleControllerExecFlags |= gBitTable[battler] << (32 - MAX_BATTLERS_COUNT); @@ -1331,7 +1331,7 @@ void MarkBattlerForControllerExec(u8 battler) gBattleControllerExecFlags |= gBitTable[battler]; } -void MarkBattlerReceivedLinkData(u8 battler) +void MarkBattlerReceivedLinkData(u32 battler) { s32 i; @@ -1341,7 +1341,7 @@ void MarkBattlerReceivedLinkData(u8 battler) gBattleControllerExecFlags &= ~((1 << 28) << battler); } -void CancelMultiTurnMoves(u8 battler) +void CancelMultiTurnMoves(u32 battler) { u8 i; gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS); @@ -1422,7 +1422,7 @@ void CancelMultiTurnMoves(u8 battler) gDisableStructs[battler].furyCutterCounter = 0; } -bool8 WasUnableToUseMove(u8 battler) +bool32 WasUnableToUseMove(u32 battler) { if (gProtectStructs[battler].prlzImmobility || gProtectStructs[battler].usedImprisonedMove @@ -1441,7 +1441,7 @@ bool8 WasUnableToUseMove(u8 battler) return FALSE; } -void PrepareStringBattle(u16 stringId, u8 battler) +void PrepareStringBattle(u16 stringId, u32 battler) { u32 targetSide = GetBattlerSide(gBattlerTarget); u16 battlerAbility = GetBattlerAbility(battler); @@ -1512,7 +1512,7 @@ void ResetSentPokesToOpponentValue(void) gSentPokesToOpponent[(i & BIT_FLANK) >> 1] = bits; } -void OpponentSwitchInResetSentPokesToOpponentValue(u8 battler) +void OpponentSwitchInResetSentPokesToOpponentValue(u32 battler) { s32 i = 0; u32 bits = 0; @@ -1531,7 +1531,7 @@ void OpponentSwitchInResetSentPokesToOpponentValue(u8 battler) } } -void UpdateSentPokesToOpponentValue(u8 battler) +void UpdateSentPokesToOpponentValue(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { @@ -1843,7 +1843,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) return limitations; } -u8 CheckMoveLimitations(u8 battler, u8 unusableMoves, u16 check) +u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) { u8 holdEffect = GetBattlerHoldEffect(battler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; @@ -1925,11 +1925,11 @@ bool32 AreAllMovesUnusable(u32 battler) return (unusable == ALL_MOVES_MASK); } -u8 GetImprisonedMovesCount(u8 battler, u16 move) +u8 GetImprisonedMovesCount(u32 battler, u16 move) { s32 i; u8 imprisonedMoves = 0; - u8 battlerSide = GetBattlerSide(battler); + u32 battlerSide = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { @@ -1949,7 +1949,7 @@ u8 GetImprisonedMovesCount(u8 battler, u16 move) return imprisonedMoves; } -u32 GetBattlerFriendshipScore(u8 battler) +u32 GetBattlerFriendshipScore(u32 battler) { u8 side = GetBattlerSide(battler); struct Pokemon *party = GetSideParty(side); @@ -2061,7 +2061,7 @@ u8 DoFieldEndTurnEffects(void) { if (!gProtectStructs[i].quash && !gProtectStructs[j].quash - && GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE)) + && GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE)) SwapTurnOrder(i, j); } } @@ -3135,7 +3135,7 @@ u8 DoBattlerEndTurnEffects(void) return 0; } -bool8 HandleWishPerishSongOnTurnEnd(void) +bool32 HandleWishPerishSongOnTurnEnd(void) { u32 battler; @@ -3238,7 +3238,7 @@ bool8 HandleWishPerishSongOnTurnEnd(void) #define FAINTED_ACTIONS_MAX_CASE 8 -bool8 HandleFaintedMonActions(void) +bool32 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return FALSE; @@ -3846,7 +3846,7 @@ u8 AtkCanceller_UnableToUseMove2(void) return effect; } -bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) +bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { u32 i, side, playerId, flankId; struct Pokemon *party; @@ -4001,7 +4001,7 @@ static const u16 sWeatherFlagsInfo[][3] = [ENUM_WEATHER_SNOW] = {B_WEATHER_SNOW_TEMPORARY, B_WEATHER_SNOW_PERMANENT, HOLD_EFFECT_ICY_ROCK}, }; -static void ShouldChangeFormInWeather(u8 battler) +static void ShouldChangeFormInWeather(u32 battler) { int i; int side = GetBattlerSide(battler); @@ -4016,7 +4016,7 @@ static void ShouldChangeFormInWeather(u8 battler) } } -bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) +bool32 TryChangeBattleWeather(u32 battler, u32 weatherEnumId, bool32 viaAbility) { u16 battlerAbility = GetBattlerAbility(battler); if (gBattleWeather & B_WEATHER_PRIMAL_ANY @@ -4066,7 +4066,7 @@ static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u8 *timer) return FALSE; } -static u8 ForewarnChooseMove(u32 battler) +static void ForewarnChooseMove(u32 battler) { struct Forewarn { u8 battler; @@ -4124,9 +4124,9 @@ static u8 ForewarnChooseMove(u32 battler) Free(data); } -bool8 ChangeTypeBasedOnTerrain(u8 battler) +bool32 ChangeTypeBasedOnTerrain(u32 battler) { - u8 battlerType; + u32 battlerType; if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) battlerType = TYPE_ELECTRIC; @@ -4146,12 +4146,12 @@ bool8 ChangeTypeBasedOnTerrain(u8 battler) // Supreme Overlord adds a damage boost for each fainted ally. // The first ally adds a x1.2 boost, and subsequent allies add an extra x0.1 boost each. -static uq4_12_t GetSupremeOverlordModifier(u8 battler) +static uq4_12_t GetSupremeOverlordModifier(u32 battler) { u32 i; struct Pokemon *party = GetBattlerParty(battler); uq4_12_t modifier = UQ_4_12(1.0); - bool8 appliedFirstBoost = FALSE; + bool32 appliedFirstBoost = FALSE; for (i = 0; i < PARTY_SIZE; i++) { @@ -4165,9 +4165,9 @@ static uq4_12_t GetSupremeOverlordModifier(u8 battler) return modifier; } -u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 moveArg) +u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) { - u8 effect = 0; + u32 effect = 0; u32 speciesAtk, speciesDef; u32 moveType, move; u32 i, j; @@ -4403,7 +4403,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move { move = gBattleMons[i].moves[j]; GET_MOVE_TYPE(move, moveType); - if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, FALSE) >= UQ_4_12(2.0)) + if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, ABILITY_ANTICIPATION, FALSE) >= UQ_4_12(2.0)) { effect++; break; @@ -5411,7 +5411,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && IsBattlerAlive(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker)) { - u8 battler; + u32 battler; if ((battler = IsAbilityOnField(ABILITY_DAMP))) { gBattleScripting.battler = battler - 1; @@ -6099,7 +6099,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move return effect; } -bool32 TryPrimalReversion(u8 battler) +bool32 TryPrimalReversion(u32 battler) { if (GetBattlerHoldEffect(battler, FALSE) == HOLD_EFFECT_PRIMAL_ORB && GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) != SPECIES_NONE) @@ -6169,7 +6169,7 @@ bool32 IsMyceliumMightOnField(void) return FALSE; } -u32 GetBattlerAbility(u8 battler) +u32 GetBattlerAbility(u32 battler) { if (gStatuses3[battler] & STATUS3_GASTRO_ACID) return ABILITY_NONE; @@ -6302,7 +6302,7 @@ enum ITEM_STATS_CHANGE, }; -bool32 IsBattlerTerrainAffected(u8 battler, u32 terrainFlag) +bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag) { if (!(gFieldStatuses & terrainFlag)) return FALSE; @@ -6312,7 +6312,7 @@ bool32 IsBattlerTerrainAffected(u8 battler, u32 terrainFlag) return IsBattlerGrounded(battler); } -bool32 CanSleep(u8 battler) +bool32 CanSleep(u32 battler) { u16 ability = GetBattlerAbility(battler); if (ability == ABILITY_INSOMNIA @@ -6327,7 +6327,7 @@ bool32 CanSleep(u8 battler) return TRUE; } -bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget) +bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget) { u16 ability = GetBattlerAbility(battlerTarget); @@ -6343,7 +6343,7 @@ bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget) return TRUE; } -bool32 CanBeBurned(u8 battler) +bool32 CanBeBurned(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) @@ -6359,7 +6359,7 @@ bool32 CanBeBurned(u8 battler) return TRUE; } -bool32 CanBeParalyzed(u8 battler) +bool32 CanBeParalyzed(u32 battler) { u16 ability = GetBattlerAbility(battler); if ( @@ -6376,7 +6376,7 @@ bool32 CanBeParalyzed(u8 battler) return TRUE; } -bool32 CanBeFrozen(u8 battler) +bool32 CanBeFrozen(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE) @@ -6391,7 +6391,7 @@ bool32 CanBeFrozen(u8 battler) return TRUE; } -bool32 CanGetFrostbite(u8 battler) +bool32 CanGetFrostbite(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE) @@ -6405,7 +6405,7 @@ bool32 CanGetFrostbite(u8 battler) return TRUE; } -bool32 CanBeConfused(u8 battler) +bool32 CanBeConfused(u32 battler) { if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO || gBattleMons[battler].status2 & STATUS2_CONFUSION @@ -6445,7 +6445,7 @@ bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId) #define CONFUSE_BERRY_HP_FRACTION 2 #endif -static u8 HealConfuseBerry(u32 battler, u32 itemId, u8 flavorId, bool32 end2) +static u8 HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, bool32 end2) { if (HasEnoughHpToEatBerry(battler, CONFUSE_BERRY_HP_FRACTION, itemId) #if B_HEAL_BLOCKING >= GEN_5 @@ -6610,7 +6610,7 @@ static u8 TrySetEnigmaBerry(u32 battler) return 0; } -static u8 DamagedStatBoostBerryEffect(u8 battler, u8 statId, u8 split) +static u8 DamagedStatBoostBerryEffect(u32 battler, u8 statId, u8 split) { if (IsBattlerAlive(battler) && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) @@ -6637,7 +6637,7 @@ static u8 DamagedStatBoostBerryEffect(u8 battler, u8 statId, u8 split) return 0; } -u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute) +u8 TryHandleSeed(u32 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute) { if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { @@ -6747,7 +6747,7 @@ static bool32 UnnerveOn(u32 battler, u32 itemId) return FALSE; } -static bool32 GetMentalHerbEffect(u8 battler) +static bool32 GetMentalHerbEffect(u32 battler) { bool32 ret = FALSE; @@ -6802,7 +6802,7 @@ static bool32 GetMentalHerbEffect(u8 battler) return ret; } -static u8 TryConsumeMirrorHerb(u8 battler, bool32 execute) +static u8 TryConsumeMirrorHerb(u32 battler, bool32 execute) { u8 effect = 0; @@ -7045,12 +7045,12 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) return effect; } -u8 ItemBattleEffects(u8 caseID, u8 battler, bool8 moveTurn) +u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) { int i = 0, moveType; u8 effect = ITEM_NO_EFFECT; u8 changedPP = 0; - u8 battlerHoldEffect, atkHoldEffect; + u32 battlerHoldEffect, atkHoldEffect; u8 atkHoldEffectParam; u16 atkItem; @@ -7918,7 +7918,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battler, bool8 moveTurn) return effect; } -void ClearFuryCutterDestinyBondGrudge(u8 battler) +void ClearFuryCutterDestinyBondGrudge(u32 battler) { gDisableStructs[battler].furyCutterCounter = 0; gBattleMons[battler].status2 &= ~STATUS2_DESTINY_BOND; @@ -8032,7 +8032,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget) return targetBattler; } -static bool32 IsBattlerModernFatefulEncounter(u8 battler) +static bool32 IsBattlerModernFatefulEncounter(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) return TRUE; @@ -8169,7 +8169,7 @@ u8 IsMonDisobedient(void) } } -u32 GetBattlerHoldEffect(u8 battler, bool32 checkNegating) +u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating) { if (checkNegating) { @@ -8189,7 +8189,7 @@ u32 GetBattlerHoldEffect(u8 battler, bool32 checkNegating) return ItemId_GetHoldEffect(gBattleMons[battler].item); } -static u32 GetBattlerItemHoldEffectParam(u8 battler, u16 item) +static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item) { if (item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battler].holdEffectParam; @@ -8197,7 +8197,7 @@ static u32 GetBattlerItemHoldEffectParam(u8 battler, u16 item) return ItemId_GetHoldEffectParam(item); } -u32 GetBattlerHoldEffectParam(u8 battler) +u32 GetBattlerHoldEffectParam(u32 battler) { if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battler].holdEffectParam; @@ -8205,9 +8205,9 @@ u32 GetBattlerHoldEffectParam(u8 battler) return ItemId_GetHoldEffectParam(gBattleMons[battler].item); } -bool32 IsMoveMakingContact(u16 move, u8 battlerAtk) +bool32 IsMoveMakingContact(u32 move, u32 battlerAtk) { - u16 atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE); + u32 atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE); if (!gBattleMoves[move].makesContact) { @@ -8228,7 +8228,7 @@ bool32 IsMoveMakingContact(u16 move, u8 battlerAtk) } } -bool32 IsBattlerProtected(u8 battler, u16 move) +bool32 IsBattlerProtected(u32 battler, u32 move) { // Decorate bypasses protect and detect, but not crafty shield if (move == MOVE_DECORATE) @@ -8286,7 +8286,7 @@ bool32 IsBattlerProtected(u8 battler, u16 move) } // Only called directly when calculating damage type effectiveness -static bool32 IsBattlerGrounded2(u8 battler, bool32 considerInverse) +static bool32 IsBattlerGrounded2(u32 battler, bool32 considerInverse) { u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); @@ -8313,12 +8313,12 @@ static bool32 IsBattlerGrounded2(u8 battler, bool32 considerInverse) return TRUE; } -bool32 IsBattlerGrounded(u8 battler) +bool32 IsBattlerGrounded(u32 battler) { return IsBattlerGrounded2(battler, FALSE); } -bool32 IsBattlerAlive(u8 battler) +bool32 IsBattlerAlive(u32 battler) { if (gBattleMons[battler].hp == 0) return FALSE; @@ -8330,19 +8330,19 @@ bool32 IsBattlerAlive(u8 battler) return TRUE; } -u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move) +u32 GetMoveSlot(u16 *moves, u32 move) { - u8 i; + u32 i; for (i = 0; i < MAX_MON_MOVES; i++) { - if (battleMon->moves[i] == move) + if (moves[i] == move) break; } return i; } -u32 GetBattlerWeight(u8 battler) +u32 GetBattlerWeight(u32 battler) { u32 i; u32 weight = GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[battler].species), 1); @@ -8376,7 +8376,7 @@ u32 GetBattlerWeight(u8 battler) return weight; } -u32 CountBattlerStatIncreases(u8 battler, bool32 countEvasionAcc) +u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc) { u32 i; u32 count = 0; @@ -8392,7 +8392,7 @@ u32 CountBattlerStatIncreases(u8 battler, bool32 countEvasionAcc) return count; } -u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef) +u32 GetMoveTargetCount(u32 move, u32 battlerAtk, u32 battlerDef) { switch (GetBattlerMoveTargetType(gBattlerAttacker, move)) { @@ -8531,10 +8531,10 @@ u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter) return basePower; } -static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) +static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 weather) { u32 i; - u16 basePower = gBattleMoves[move].power; + u32 basePower = gBattleMoves[move].power; u32 weight, hpFraction, speed; if (gBattleStruct->zmove.active) @@ -8595,7 +8595,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower *= 2; break; case EFFECT_WEATHER_BALL: - if (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT) + if (weather & B_WEATHER_ANY) basePower *= 2; break; case EFFECT_PURSUIT: @@ -8606,7 +8606,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower = gNaturalGiftTable[ITEM_TO_BERRY(gBattleMons[battlerAtk].item)].power; break; case EFFECT_WAKE_UP_SLAP: - if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP || GetBattlerAbility(battlerDef) == ABILITY_COMATOSE) + if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP || abilityDef == ABILITY_COMATOSE) basePower *= 2; break; case EFFECT_SMELLINGSALT: @@ -8618,7 +8618,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) break; case EFFECT_HEX: case EFFECT_INFERNAL_PARADE: - if (gBattleMons[battlerDef].status1 & STATUS1_ANY || GetBattlerAbility(battlerDef) == ABILITY_COMATOSE) + if (gBattleMons[battlerDef].status1 & STATUS1_ANY || abilityDef == ABILITY_COMATOSE) basePower *= 2; break; case EFFECT_ASSURANCE: @@ -8626,8 +8626,8 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) basePower *= 2; break; case EFFECT_TRUMP_CARD: - i = GetBattleMonMoveSlot(&gBattleMons[battlerAtk], move); - if (i != 4) + i = GetMoveSlot(gBattleMons[battlerAtk].moves, move); + if (i != MAX_MON_MOVES) { if (gBattleMons[battlerAtk].pp[i] >= ARRAY_COUNT(sTrumpCardPowerTable)) basePower = sTrumpCardPowerTable[ARRAY_COUNT(sTrumpCardPowerTable) - 1]; @@ -8787,16 +8787,14 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) return basePower; } -static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 updateFlags) +static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectAtk, u32 weather) { u32 i; - u32 holdEffectAtk, holdEffectParamAtk; - u16 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef); + u32 holdEffectParamAtk; + u32 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef, defAbility, weather); uq4_12_t holdEffectModifier; uq4_12_t modifier = UQ_4_12(1.0); u32 atkSide = GetBattlerSide(battlerAtk); - u16 atkAbility = GetBattlerAbility(battlerAtk); - u16 defAbility = GetBattlerAbility(battlerDef); // move effect switch (gBattleMoves[move].effect) @@ -8906,7 +8904,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe break; case ABILITY_SAND_FORCE: if ((moveType == TYPE_STEEL || moveType == TYPE_ROCK || moveType == TYPE_GROUND) - && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT) + && weather & B_WEATHER_SANDSTORM) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); break; case ABILITY_RIVALRY: @@ -8986,7 +8984,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe case ABILITY_PROTOSYNTHESIS: { u8 atkHighestStat = GetHighestStatId(battlerAtk); - if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT + if (weather & B_WEATHER_SUN && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK))) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); } @@ -9000,7 +8998,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe } break; case ABILITY_ORICHALCUM_PULSE: - if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT) + if (weather & B_WEATHER_SUN) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); break; case ABILITY_HADRON_ENGINE: @@ -9076,7 +9074,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); - if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT + if (weather & B_WEATHER_SUN && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF))) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); } @@ -9091,7 +9089,6 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe break; } - holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); holdEffectParamAtk = GetBattlerHoldEffectParam(battlerAtk); if (holdEffectParamAtk > 100) holdEffectParamAtk = 100; @@ -9170,7 +9167,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe } #undef TERRAIN_TYPE_BOOST -static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit, bool32 updateFlags) +static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 isCrit, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectAtk) { u8 atkStage; u32 atkStat; @@ -9215,7 +9212,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b if (isCrit && atkStage < DEFAULT_STAT_STAGE) atkStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore attack stat changes while taking damage - if (GetBattlerAbility(battlerDef) == ABILITY_UNAWARE) + if (defAbility == ABILITY_UNAWARE) atkStage = DEFAULT_STAT_STAGE; atkStat *= gStatStageRatios[atkStage][0]; @@ -9225,7 +9222,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b modifier = UQ_4_12(1.0); // attacker's abilities - switch (GetBattlerAbility(battlerAtk)) + switch (atkAbility) { case ABILITY_HUGE_POWER: case ABILITY_PURE_POWER: @@ -9303,7 +9300,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b } // target's abilities - switch (GetBattlerAbility(battlerDef)) + switch (defAbility) { case ABILITY_THICK_FAT: if (moveType == TYPE_FIRE || moveType == TYPE_ICE) @@ -9328,7 +9325,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b } // attacker's hold effect - switch (GetBattlerHoldEffect(battlerAtk, TRUE)) + switch (holdEffectAtk) { case HOLD_EFFECT_THICK_CLUB: if ((atkBaseSpeciesId == SPECIES_CUBONE || atkBaseSpeciesId == SPECIES_MAROWAK) && IS_MOVE_PHYSICAL(move)) @@ -9374,7 +9371,7 @@ static bool32 CanEvolve(u32 species) return FALSE; } -static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit, bool32 updateFlags) +static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 isCrit, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectDef, u32 weather) { bool32 usesDefStat; u8 defStage; @@ -9415,7 +9412,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, if (isCrit && defStage > DEFAULT_STAT_STAGE) defStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore defense stat changes while dealing damage - if (GetBattlerAbility(battlerAtk) == ABILITY_UNAWARE) + if (atkAbility == ABILITY_UNAWARE) defStage = DEFAULT_STAT_STAGE; // certain moves also ignore stat changes if (gBattleMoves[move].ignoresTargetDefenseEvasionStages) @@ -9428,7 +9425,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, modifier = UQ_4_12(1.0); // target's abilities - switch (GetBattlerAbility(battlerDef)) + switch (defAbility) { case ABILITY_MARVEL_SCALE: if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat) @@ -9477,7 +9474,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, } // target's hold effects - switch (GetBattlerHoldEffect(battlerDef, TRUE)) + switch (holdEffectDef) { case HOLD_EFFECT_DEEP_SEA_SCALE: if (gBattleMons[battlerDef].species == SPECIES_CLAMPERL && !usesDefStat) @@ -9506,10 +9503,10 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, } // sandstorm sp.def boost for rock types - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && weather & B_WEATHER_SANDSTORM && !usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); // snow def boost for ice types - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && gBattleWeather & B_WEATHER_SNOW && WEATHER_HAS_EFFECT && usesDefStat) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && weather & B_WEATHER_SNOW && usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. @@ -9574,22 +9571,22 @@ static inline uq4_12_t GetSameTypeAttackBonusModifier(u32 battlerAtk, u32 moveTy } // Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. -static uq4_12_t GetWeatherDamageModifier(u32 battlerAtk, u32 move, u32 moveType, u32 holdEffectAtk, u32 holdEffectDef) +static uq4_12_t GetWeatherDamageModifier(u32 battlerAtk, u32 move, u32 moveType, u32 holdEffectAtk, u32 holdEffectDef, u32 weather) { - if (!WEATHER_HAS_EFFECT) + if (weather == B_WEATHER_NONE) return UQ_4_12(1.0); - if (gBattleMoves[move].effect == EFFECT_HYDRO_STEAM && (gBattleWeather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) + if (gBattleMoves[move].effect == EFFECT_HYDRO_STEAM && (weather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) return UQ_4_12(1.5); if (holdEffectDef == HOLD_EFFECT_UTILITY_UMBRELLA) return UQ_4_12(1.0); - if (gBattleWeather & B_WEATHER_RAIN) + if (weather & B_WEATHER_RAIN) { if (moveType != TYPE_FIRE && moveType != TYPE_WATER) return UQ_4_12(1.0); return (moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); } - if (gBattleWeather & B_WEATHER_SUN) + if (weather & B_WEATHER_SUN) { if (moveType != TYPE_FIRE && moveType != TYPE_WATER) return UQ_4_12(1.0); @@ -9653,13 +9650,12 @@ static inline uq4_12_t GetAirborneModifier(u32 move, u32 battlerDef) return UQ_4_12(1.0); } -static inline uq4_12_t GetScreensModifier(u32 move, u32 battlerAtk, u32 battlerDef, bool32 isCrit) +static inline uq4_12_t GetScreensModifier(u32 move, u32 battlerAtk, u32 battlerDef, bool32 isCrit, u32 abilityAtk) { u32 sideStatus = gSideStatuses[GetBattlerSide(battlerDef)]; bool32 lightScreen = (sideStatus & SIDE_STATUS_LIGHTSCREEN) && IS_MOVE_SPECIAL(move); bool32 reflect = (sideStatus & SIDE_STATUS_REFLECT) && IS_MOVE_PHYSICAL(move); bool32 auroraVeil = sideStatus & SIDE_STATUS_AURORA_VEIL; - u32 abilityAtk = GetBattlerAbility(battlerAtk); if (isCrit || abilityAtk == ABILITY_INFILTRATOR || gProtectStructs[battlerAtk].confusionSelfDmg) return UQ_4_12(1.0); @@ -9675,9 +9671,8 @@ static inline uq4_12_t GetCollisionCourseElectroDriftModifier(u32 move, uq4_12_t return UQ_4_12(1.0); } -static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, bool32 isCrit) +static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, bool32 isCrit, u32 abilityAtk) { - u32 abilityAtk = GetBattlerAbility(battlerAtk); switch (abilityAtk) { case ABILITY_NEUROFORCE: @@ -9696,9 +9691,8 @@ static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typ return UQ_4_12(1.0); } -static inline uq4_12_t GetDefenderAbilitiesModifier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t typeEffectivenessModifier) +static inline uq4_12_t GetDefenderAbilitiesModifier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t typeEffectivenessModifier, u32 abilityDef) { - u32 abilityDef = GetBattlerAbility(battlerDef); switch (abilityDef) { case ABILITY_MULTISCALE: @@ -9744,9 +9738,8 @@ static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef return UQ_4_12(1.0); } -static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier) +static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, u32 holdEffectAtk) { - u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); u32 percentBoost; switch (holdEffectAtk) { @@ -9765,12 +9758,10 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff return UQ_4_12(1.0); } -static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq4_12_t typeEffectivenessModifier, bool32 updateFlags) +static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq4_12_t typeEffectivenessModifier, bool32 updateFlags, u32 abilityDef, u32 holdEffectDef) { - u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); u32 holdEffectDefParam = GetBattlerHoldEffectParam(battlerDef); u32 itemDef = gBattleMons[battlerDef].item; - u32 abilityDef = GetBattlerAbility(battlerDef); switch (holdEffectDef) { @@ -9798,9 +9789,9 @@ static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq // https://bulbapedia.bulbagarden.net/wiki/Damage#Generation_V_onward // Please Note: Fixed Point Multiplication is not associative. // The order of operations is relevant. -static uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 isCrit, uq4_12_t typeEffectivenessModifier, bool32 updateFlags) +static inline uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 isCrit, uq4_12_t typeEffectivenessModifier, bool32 updateFlags, + u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk, u32 holdEffectDef) { - u32 abilityAtk = GetBattlerAbility(battlerAtk); uq4_12_t finalModifier = UQ_4_12(1.0); u32 battlerDefPartner = BATTLE_PARTNER(battlerDef); u32 unmodifiedAttackerSpeed = gBattleMons[battlerAtk].speed; @@ -9810,24 +9801,24 @@ static uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 ba DAMAGE_MULTIPLY_MODIFIER(GetUndergroundModifier(move, battlerDef)); DAMAGE_MULTIPLY_MODIFIER(GetDiveModifier(move, battlerDef)); DAMAGE_MULTIPLY_MODIFIER(GetAirborneModifier(move, battlerDef)); - DAMAGE_MULTIPLY_MODIFIER(GetScreensModifier(move, battlerAtk, battlerDef, isCrit)); + DAMAGE_MULTIPLY_MODIFIER(GetScreensModifier(move, battlerAtk, battlerDef, isCrit, abilityAtk)); DAMAGE_MULTIPLY_MODIFIER(GetCollisionCourseElectroDriftModifier(move, typeEffectivenessModifier)); if (unmodifiedAttackerSpeed >= unmodifiedDefenderSpeed) { - DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit, abilityAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef)); DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags, abilityDef, holdEffectDef)); } else { - DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef)); DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit, abilityAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags, abilityDef, holdEffectDef)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk)); } return finalModifier; } @@ -9838,31 +9829,26 @@ static uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 ba dmg = uq4_12_multiply_by_int_half_down(modifier, dmg); \ } while (0) -static s32 DoMoveDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, - bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier) +static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, + bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather, + u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef) { s32 dmg; u32 userFinalAttack; u32 targetFinalDefense; - u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); - u32 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); - u32 abilityAtk = GetBattlerAbility(battlerAtk); - - if (typeEffectivenessModifier == UQ_4_12(0.0)) - return 0; if (fixedBasePower) gBattleMovePower = fixedBasePower; else - gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType, updateFlags); + gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType, updateFlags, abilityAtk, abilityDef, holdEffectAtk, weather); - userFinalAttack = CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); - targetFinalDefense = CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); + userFinalAttack = CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags, abilityAtk, abilityDef, holdEffectAtk); + targetFinalDefense = CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags, abilityAtk, abilityDef, holdEffectDef, weather); dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, gBattleMons[battlerAtk].level, targetFinalDefense); DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(move, battlerAtk, battlerDef)); DAMAGE_APPLY_MODIFIER(GetParentalBondModifier(battlerAtk)); - DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(battlerAtk, move, moveType, holdEffectAtk, holdEffectDef)); + DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(battlerAtk, move, moveType, holdEffectAtk, holdEffectDef, weather)); DAMAGE_APPLY_MODIFIER(GetCriticalModifier(isCrit)); // TODO: Glaive Rush (Gen IX effect) if (randomFactor) @@ -9875,29 +9861,56 @@ static s32 DoMoveDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveTy DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(GetZMoveAgainstProtectionModifier(battlerDef)); - DAMAGE_APPLY_MODIFIER(GetOtherModifiers(move, moveType, battlerAtk, battlerDef, isCrit, typeEffectivenessModifier, updateFlags)); + DAMAGE_APPLY_MODIFIER(GetOtherModifiers(move, moveType, battlerAtk, battlerDef, isCrit, typeEffectivenessModifier, updateFlags, abilityAtk, abilityDef, holdEffectAtk, holdEffectDef)); if (dmg == 0) dmg = 1; return dmg; } +static inline s32 DoMoveDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, + bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather) +{ + u32 holdEffectAtk, holdEffectDef, abilityAtk, abilityDef; + + if (typeEffectivenessModifier == UQ_4_12(0.0)) + return 0; + + holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); + abilityAtk = GetBattlerAbility(battlerAtk); + abilityDef = GetBattlerAbility(battlerDef); + + return DoMoveDamageCalcVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor, + updateFlags, typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef); +} + #undef DAMAGE_APPLY_MODIFIER -s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags) +static u32 GetWeather(void) +{ + if (gBattleWeather == B_WEATHER_NONE || !WEATHER_HAS_EFFECT) + return B_WEATHER_NONE; + else + return gBattleWeather; +} + +s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 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)); + updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), updateFlags), + GetWeather()); } -// for AI - get move damage and effectiveness with one function call -s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, uq4_12_t *typeEffectivenessModifier) +// for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once +s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier, + u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef) { - *typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE); - return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, FALSE, FALSE, FALSE, *typeEffectivenessModifier); + return DoMoveDamageCalcVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, FALSE, FALSE, + typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef); } -static void MulByTypeEffectiveness(uq4_12_t *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities) +static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defType, u32 battlerAtk, bool32 recordAbilities) { uq4_12_t mod = GetTypeModifier(moveType, defType); @@ -9937,7 +9950,7 @@ static void MulByTypeEffectiveness(uq4_12_t *modifier, u16 move, u8 moveType, u8 *modifier = uq4_12_multiply(*modifier, mod); } -static void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t resultingModifier, u32 illusionSpecies) +static inline void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t resultingModifier, u32 illusionSpecies) { // Check if the type effectiveness would've been different if the pokemon really had the types as the disguise. uq4_12_t presumedModifier = UQ_4_12(1.0); @@ -9972,10 +9985,9 @@ static void UpdateMoveResultFlags(uq4_12_t modifier) } } -static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities, uq4_12_t modifier) +static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 recordAbilities, uq4_12_t modifier, u32 defAbility) { u32 illusionSpecies; - u16 defAbility = GetBattlerAbility(battlerDef); MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1, battlerAtk, recordAbilities); if (gBattleMons[battlerDef].type2 != gBattleMons[battlerDef].type1) @@ -10045,15 +10057,15 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat return modifier; } -uq4_12_t CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities) +uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities) { uq4_12_t modifier = UQ_4_12(1.0); if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { - modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier); + modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); if (gBattleMoves[move].effect == EFFECT_TWO_TYPED_MOVE) - modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier); + modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); } if (recordAbilities) @@ -10097,7 +10109,7 @@ static uq4_12_t GetInverseTypeMultiplier(uq4_12_t multiplier) } } -uq4_12_t GetTypeModifier(u8 atkType, u8 defType) +uq4_12_t GetTypeModifier(u32 atkType, u32 defType) { #if B_FLAG_INVERSE_BATTLE != 0 if (FlagGet(B_FLAG_INVERSE_BATTLE)) @@ -10150,7 +10162,7 @@ s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 ma return dmg; } -s32 GetStealthHazardDamage(u8 hazardType, u8 battler) +s32 GetStealthHazardDamage(u8 hazardType, u32 battler) { u8 type1 = gBattleMons[battler].type1; u8 type2 = gBattleMons[battler].type2; @@ -10159,7 +10171,7 @@ s32 GetStealthHazardDamage(u8 hazardType, u8 battler) return GetStealthHazardDamageByTypesAndHP(hazardType, type1, type2, maxHp); } -bool32 IsPartnerMonFromSameTrainer(u8 battler) +bool32 IsPartnerMonFromSameTrainer(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) return FALSE; @@ -10195,11 +10207,11 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) return FALSE; } -bool32 CanMegaEvolve(u8 battler) +bool32 CanMegaEvolve(u32 battler) { u32 itemId, holdEffect, species; struct Pokemon *mon; - u8 battlerPosition = GetBattlerPosition(battler); + u32 battlerPosition = GetBattlerPosition(battler); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battler)); struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]))->mega); @@ -10259,11 +10271,11 @@ bool32 CanMegaEvolve(u8 battler) return FALSE; } -bool32 CanUltraBurst(u8 battler) +bool32 CanUltraBurst(u32 battler) { u32 itemId, holdEffect, species; struct Pokemon *mon; - u8 battlerPosition = GetBattlerPosition(battler); + u32 battlerPosition = GetBattlerPosition(battler); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battler)); // Check if Player has a Z Ring @@ -10314,7 +10326,7 @@ bool32 CanUltraBurst(u8 battler) return FALSE; } -bool32 IsBattlerMegaEvolved(u8 battler) +bool32 IsBattlerMegaEvolved(u32 battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution. if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED) @@ -10322,7 +10334,7 @@ bool32 IsBattlerMegaEvolved(u8 battler) return (gSpeciesInfo[gBattleMons[battler].species].flags & SPECIES_FLAG_MEGA_EVOLUTION); } -bool32 IsBattlerPrimalReverted(u8 battler) +bool32 IsBattlerPrimalReverted(u32 battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Primal Revesion. if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED) @@ -10330,7 +10342,7 @@ bool32 IsBattlerPrimalReverted(u8 battler) return (gSpeciesInfo[gBattleMons[battler].species].flags & SPECIES_FLAG_PRIMAL_REVERSION); } -bool32 IsBattlerUltraBursted(u8 battler) +bool32 IsBattlerUltraBursted(u32 battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst. if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED) @@ -10339,7 +10351,7 @@ bool32 IsBattlerUltraBursted(u8 battler) } // Returns SPECIES_NONE if no form change is possible -u16 GetBattleFormChangeTargetSpecies(u8 battler, u16 method) +u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method) { u32 i, j; u16 targetSpecies = SPECIES_NONE; @@ -10426,7 +10438,7 @@ u16 GetBattleFormChangeTargetSpecies(u8 battler, u16 method) return targetSpecies; } -bool32 CanBattlerFormChange(u8 battler, u16 method) +bool32 CanBattlerFormChange(u32 battler, u16 method) { // Can't change form if transformed. if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED @@ -10440,7 +10452,7 @@ bool32 CanBattlerFormChange(u8 battler, u16 method) return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } -bool32 TryBattleFormChange(u8 battler, u16 method) +bool32 TryBattleFormChange(u32 battler, u16 method) { u8 monId = gBattlerPartyIndexes[battler]; u8 side = GetBattlerSide(battler); @@ -10467,7 +10479,7 @@ bool32 TryBattleFormChange(u8 battler, u16 method) } else if (gBattleStruct->changedSpecies[side][monId] != SPECIES_NONE) { - bool8 restoreSpecies = FALSE; + bool32 restoreSpecies = FALSE; // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables. if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) @@ -10510,7 +10522,7 @@ bool32 DoBattlersShareType(u32 battler1, u32 battler2) return FALSE; } -bool32 CanBattlerGetOrLoseItem(u8 battler, u16 itemId) +bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId) { u16 species = gBattleMons[battler].species; u16 holdEffect = ItemId_GetHoldEffect(itemId); @@ -10600,7 +10612,7 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battler) return FALSE; } -bool8 ShouldGetStatBadgeBoost(u16 badgeFlag, u8 battler) +bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) { #if B_BADGE_BOOST == GEN_3 if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER)) @@ -10634,10 +10646,10 @@ u8 GetBattleMoveSplit(u32 moveId) #endif } -static bool32 TryRemoveScreens(u8 battler) +static bool32 TryRemoveScreens(u32 battler) { bool32 removed = FALSE; - u8 battlerSide = GetBattlerSide(battler); + u32 battlerSide = GetBattlerSide(battler); u8 enemySide = GetBattlerSide(BATTLE_OPPOSITE(battler)); // try to remove from battler's side @@ -10663,7 +10675,7 @@ static bool32 TryRemoveScreens(u8 battler) return removed; } -static bool32 IsUnnerveAbilityOnOpposingSide(u8 battler) +static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) { if (IsAbilityOnOpposingSide(battler, ABILITY_UNNERVE) || IsAbilityOnOpposingSide(battler, ABILITY_AS_ONE_ICE_RIDER) @@ -10673,7 +10685,7 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u8 battler) } // Photon geyser & light that burns the sky -u8 GetSplitBasedOnStats(u8 battler) +u8 GetSplitBasedOnStats(u32 battler) { u32 attack = gBattleMons[battler].attack; u32 spAttack = gBattleMons[battler].spAttack; @@ -10690,11 +10702,11 @@ u8 GetSplitBasedOnStats(u8 battler) return SPLIT_PHYSICAL; } -static u8 GetFlingPowerFromItemId(u16 itemId) +static u32 GetFlingPowerFromItemId(u32 itemId) { if (itemId >= ITEM_TM01 && itemId <= ITEM_HM08) { - u8 power = gBattleMoves[ItemIdToBattleMoveId(itemId)].power; + u32 power = gBattleMoves[ItemIdToBattleMoveId(itemId)].power; if (power > 1) return power; return 10; // Status moves and moves with variable power always return 10 power. @@ -10800,7 +10812,7 @@ bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) // Sort an array of battlers by speed // Useful for effects like pickpocket, eject button, red card, dancer -void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) +void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast) { int i, j, currSpeed, currBattler; u16 speeds[MAX_BATTLERS_COUNT] = {0}; @@ -10856,7 +10868,7 @@ void TryRestoreHeldItems(void) } } -bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item) +bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item) { u8 stealerSide = GetBattlerSide(battlerStealing); @@ -10896,7 +10908,7 @@ bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item) return TRUE; } -void TrySaveExchangedItem(u8 battler, u16 stolenItem) +void TrySaveExchangedItem(u32 battler, u16 stolenItem) { // Because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original // So, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally) @@ -10910,7 +10922,7 @@ void TrySaveExchangedItem(u8 battler, u16 stolenItem) #endif } -bool32 IsBattlerAffectedByHazards(u8 battler, bool32 toxicSpikes) +bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) { bool32 ret = TRUE; u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); @@ -10927,7 +10939,7 @@ bool32 IsBattlerAffectedByHazards(u8 battler, bool32 toxicSpikes) return ret; } -bool32 TestSheerForceFlag(u8 battler, u16 move) +bool32 TestSheerForceFlag(u32 battler, u16 move) { if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].sheerForceBoost) return TRUE; @@ -10936,9 +10948,9 @@ bool32 TestSheerForceFlag(u8 battler, u16 move) } // This function is the body of "jumpifstat", but can be used dynamically in a function -bool32 CompareStat(u8 battler, u8 statId, u8 cmpTo, u8 cmpKind) +bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind) { - bool8 ret = FALSE; + bool32 ret = FALSE; u8 statValue = gBattleMons[battler].statStages[statId]; // Because this command is used as a way of checking if a stat can be lowered/raised, @@ -10987,9 +10999,9 @@ bool32 CompareStat(u8 battler, u8 statId, u8 cmpTo, u8 cmpKind) return ret; } -void BufferStatChange(u8 battler, u8 statId, u8 stringId) +void BufferStatChange(u32 battler, u8 statId, u8 stringId) { - bool8 hasContrary = (GetBattlerAbility(battler) == ABILITY_CONTRARY); + bool32 hasContrary = (GetBattlerAbility(battler) == ABILITY_CONTRARY); PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); if (stringId == STRINGID_STATFELL) @@ -11012,7 +11024,7 @@ void BufferStatChange(u8 battler, u8 statId, u8 stringId) } } -bool32 TryRoomService(u8 battler) +bool32 TryRoomService(u32 battler) { if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) { @@ -11030,7 +11042,7 @@ bool32 TryRoomService(u8 battler) } } -bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget) +bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget) { #if B_PRANKSTER_DARK_TYPES >= GEN_7 if (!gProtectStructs[battlerPrankster].pranksterElevated) @@ -11049,12 +11061,12 @@ bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 chec return FALSE; } -u16 GetUsedHeldItem(u8 battler) +u16 GetUsedHeldItem(u32 battler) { return gBattleStruct->usedHeldItems[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]; } -bool32 IsBattlerWeatherAffected(u8 battler, u32 weatherFlags) +bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) { if (gBattleWeather & weatherFlags && WEATHER_HAS_EFFECT) { @@ -11069,7 +11081,7 @@ bool32 IsBattlerWeatherAffected(u8 battler, u32 weatherFlags) // Gets move target before redirection effects etc. are applied // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED -u32 GetBattlerMoveTargetType(u8 battler, u16 move) +u32 GetBattlerMoveTargetType(u32 battler, u32 move) { u32 target; @@ -11080,7 +11092,7 @@ u32 GetBattlerMoveTargetType(u8 battler, u16 move) return gBattleMoves[move].target; } -bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move) +bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move) { if (gBattleMoves[move].effect == EFFECT_HIT_ENEMY_HEAL_ALLY && GetBattlerSide(battlerAtk) == GetBattlerSide(battlerDef) @@ -11134,13 +11146,13 @@ void RecalcBattlerStats(u32 battler, struct Pokemon *mon) CopyMonAbilityAndTypesToBattleMon(battler, mon); } -void RemoveConfusionStatus(u8 battler) +void RemoveConfusionStatus(u32 battler) { gBattleMons[battler].status2 &= ~STATUS2_CONFUSION; gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION; } -static bool8 CanBeInfinitelyConfused(u8 battler) +static bool32 CanBeInfinitelyConfused(u32 battler) { if (gBattleMons[battler].ability == ABILITY_OWN_TEMPO || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN) @@ -11151,13 +11163,13 @@ static bool8 CanBeInfinitelyConfused(u8 battler) return TRUE; } -u8 GetBattlerGender(u8 battler) +u8 GetBattlerGender(u32 battler) { return GetGenderFromSpeciesAndPersonality(gBattleMons[battler].species, gBattleMons[battler].personality); } -bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2) +bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2) { u8 gender1 = GetBattlerGender(battler1); u8 gender2 = GetBattlerGender(battler2); @@ -11165,7 +11177,7 @@ bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2) return (gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS && gender1 != gender2); } -u32 CalcSecondaryEffectChance(u8 battler, u8 secondaryEffectChance) +u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance) { if (GetBattlerAbility(battler) == ABILITY_SERENE_GRACE) secondaryEffectChance *= 2; diff --git a/src/item.c b/src/item.c index 077d885d1..7c19b316d 100644 --- a/src/item.c +++ b/src/item.c @@ -884,12 +884,12 @@ u16 ItemId_GetPrice(u16 itemId) return gItems[SanitizeItemId(itemId)].price; } -u8 ItemId_GetHoldEffect(u16 itemId) +u32 ItemId_GetHoldEffect(u32 itemId) { return gItems[SanitizeItemId(itemId)].holdEffect; } -u8 ItemId_GetHoldEffectParam(u16 itemId) +u32 ItemId_GetHoldEffectParam(u32 itemId) { return gItems[SanitizeItemId(itemId)].holdEffectParam; } @@ -956,7 +956,7 @@ u8 ItemId_GetSecondaryId(u16 itemId) return gItems[SanitizeItemId(itemId)].secondaryId; } -u8 ItemId_GetFlingPower(u16 itemId) +u32 ItemId_GetFlingPower(u32 itemId) { return gItems[SanitizeItemId(itemId)].flingPower; }