diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 6ea9660168..a0898c7ef7 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -127,7 +127,7 @@ bool32 IsAromaVeilProtectedEffect(u32 moveEffect); bool32 IsNonVolatileStatusMoveEffect(u32 moveEffect); bool32 IsMoveRedirectionPrevented(u32 battlerAtk, u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); -bool32 IsHazardMoveEffect(u32 moveEffect); +bool32 IsHazardMove(u32 move); bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move); void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); @@ -154,6 +154,7 @@ bool32 HasSubstituteIgnoringMove(u32 battler); bool32 HasHighCritRatioMove(u32 battler); bool32 HasMagicCoatAffectedMove(u32 battler); bool32 HasSnatchAffectedMove(u32 battler); +bool32 IsHazardClearingMove(u32 move); // status checks bool32 AI_CanGetFrostbite(u32 battler, u32 ability); @@ -217,5 +218,6 @@ void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData); void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); bool32 IsBattlerPredictedToSwitch(u32 battler); +bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/config/ai.h b/include/config/ai.h index 48205b733e..faa13f6b18 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -44,7 +44,7 @@ #define SHOULD_SWITCH_REGENERATOR_STATS_RAISED_PERCENTAGE 20 // AI held item-based move scoring -#define BLUNDER_POLICY_ACCURACY_THRESHOLD 75 // Moves with accuracy equal below this value are prioritized when holding Blunder Policy +#define LOW_ACCURACY_THRESHOLD 75 // Moves with accuracy equal OR below this value are considered low accuracy // AI prediction chances #define PREDICT_SWITCH_CHANCE 50 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index efda602fef..09cdd3a9f3 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2112,7 +2112,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (isDoubleBattle) { - if (IsHazardMoveEffect(GetMoveEffect(aiData->partnerMove)) // partner is going to set up hazards + if (IsHazardMove(aiData->partnerMove) // partner is going to set up hazards && AI_IsFaster(BATTLE_PARTNER(battlerAtk), battlerAtk, aiData->partnerMove)) // partner is going to set up before the potential Defog { ADJUST_SCORE(-10); @@ -3308,7 +3308,7 @@ static u32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) { u32 moveAcc = aiData->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]; - if (moveAcc <= BLUNDER_POLICY_ACCURACY_THRESHOLD) + if (moveAcc <= LOW_ACCURACY_THRESHOLD) { ADJUST_SCORE(GOOD_EFFECT); } @@ -4015,7 +4015,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) { if (isDoubleBattle) { - if (IsHazardMoveEffect(GetMoveEffect(aiData->partnerMove)) // Partner is going to set up hazards + if (IsHazardMove(aiData->partnerMove) // Partner is going to set up hazards && AI_IsSlower(battlerAtk, BATTLE_PARTNER(battlerAtk), move)) // Partner going first break; // Don't use Defog if partner is going to set up hazards } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index fcd8b5ce71..16db84beb2 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -908,14 +908,8 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler) for (j = 0; j < MAX_MON_MOVES; j++) { aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL); - u32 aiEffect = GetMoveEffect(aiMove); - if (aiEffect == EFFECT_RAPID_SPIN - || (B_DEFOG_EFFECT_CLEARING >= GEN_6 && aiEffect == EFFECT_DEFOG) - || aiEffect == EFFECT_TIDY_UP) - { - // Have a mon that can clear the hazards, so switching out is okay + if (IsHazardClearingMove(aiMove)) // Have a mon that can clear the hazards, so switching out is okay return TRUE; - } } } // Faints to hazards and party can't clear them, don't switch out diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 7807872b59..7b45c539e5 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1471,8 +1471,10 @@ bool32 IsConfusionMoveEffect(u32 moveEffect) } } -bool32 IsHazardMoveEffect(u32 moveEffect) +bool32 IsHazardMove(u32 move) { + // Hazard setting moves like Stealth Rock, Spikes, etc. + u32 i, moveEffect = gMovesInfo[move].effect; switch (moveEffect) { case EFFECT_SPIKES: @@ -1480,9 +1482,48 @@ bool32 IsHazardMoveEffect(u32 moveEffect) case EFFECT_STICKY_WEB: case EFFECT_STEALTH_ROCK: return TRUE; - default: - return FALSE; } + + u32 additionalEffectCount = GetMoveAdditionalEffectCount(move); + for (i = 0; i < additionalEffectCount; i++) + { + const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(move, i); + switch (additionalEffect->moveEffect) + { + case MOVE_EFFECT_STEELSURGE: + return TRUE; + } + } + return FALSE; +} + +bool32 IsHazardClearingMove(u32 move) +{ + // Hazard clearing effects like Rapid Spin, Tidy Up, etc. + u32 i, moveEffect = gMovesInfo[move].effect; + switch (moveEffect) + { + case EFFECT_RAPID_SPIN: + case EFFECT_TIDY_UP: + return TRUE; + case EFFECT_DEFOG: + if (B_DEFOG_EFFECT_CLEARING >= GEN_6) + return TRUE; + break; + } + + u32 additionalEffectCount = GetMoveAdditionalEffectCount(move); + for (i = 0; i < additionalEffectCount; i++) + { + const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(move, i); + switch (additionalEffect->moveEffect) + { + case MOVE_EFFECT_DEFOG: + return TRUE; + } + } + + return FALSE; } bool32 IsMoveRedirectionPrevented(u32 battlerAtk, u32 move, u32 atkAbility) @@ -2957,7 +2998,7 @@ 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 - if (gBattleMons[battler].status1 & STATUS1_SLEEP) + if (gBattleMons[battler].status1 & STATUS1_SLEEP && !HasMoveEffect(battler, EFFECT_SLEEP_TALK)) return TRUE; if (gBattleMons[battler].status2 & STATUS2_RECHARGE || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0)) @@ -3713,6 +3754,20 @@ static const u16 sRecycleEncouragedItems[] = ITEM_CUSTAP_BERRY, ITEM_MENTAL_HERB, ITEM_FOCUS_SASH, + ITEM_SALAC_BERRY, + ITEM_LIECHI_BERRY, + ITEM_AGUAV_BERRY, + ITEM_FIGY_BERRY, + ITEM_IAPAPA_BERRY, + ITEM_MAGO_BERRY, + ITEM_WIKI_BERRY, + ITEM_MENTAL_HERB, + ITEM_POWER_HERB, + ITEM_BERRY_JUICE, + ITEM_WEAKNESS_POLICY, + ITEM_BLUNDER_POLICY, + ITEM_KEE_BERRY, + ITEM_MARANGA_BERRY, // TODO expand this }; @@ -4091,6 +4146,7 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) case STAT_SPATK: return (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL)); case STAT_ACC: + return (HasLowAccuracyMove(battlerAtk, battlerDef)); case STAT_EVASION: case STAT_SPEED: return TRUE; @@ -4110,7 +4166,10 @@ bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0 || HasMoveEffect(battlerDef, EFFECT_RAPID_SPIN) - || HasMoveEffect(battlerDef, EFFECT_DEFOG)) + || HasMoveEffect(battlerDef, EFFECT_TIDY_UP) + || HasMoveEffect(battlerDef, EFFECT_DEFOG) + || HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_DEFOG) + || HasMoveEffect(battlerDef, EFFECT_MAGIC_COAT)) return FALSE; return TRUE; @@ -4211,3 +4270,14 @@ void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 * if (AI_DATA->hpPercents[battlerAtk] > 70) ADJUST_SCORE_PTR(WEAK_EFFECT); } + +bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef) +{ + int i; + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] <= LOW_ACCURACY_THRESHOLD) + return TRUE; + } + return FALSE; +}