diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index d5fbc701a5..f6cabcc684 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -1,6 +1,39 @@ #ifndef GUARD_BATTLE_AI_SWITCH_ITEMS_H #define GUARD_BATTLE_AI_SWITCH_ITEMS_H +enum ShouldSwitchScenario +{ + SHOULD_SWITCH_WONDER_GUARD, + SHOULD_SWITCH_ABSORBS_MOVE, + SHOULD_SWITCH_TRAPPER, + SHOULD_SWITCH_FREE_TURN, + SHOULD_SWITCH_TRUANT, + SHOULD_SWITCH_ALL_MOVES_BAD, + SHOULD_SWITCH_PERISH_SONG, + SHOULD_SWITCH_YAWN, + SHOULD_SWITCH_BADLY_POISONED, + SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED, + SHOULD_SWITCH_CURSED, + SHOULD_SWITCH_CURSED_STATS_RAISED, + SHOULD_SWITCH_NIGHTMARE, + SHOULD_SWITCH_NIGHTMARE_STATS_RAISED, + SHOULD_SWITCH_SEEDED, + SHOULD_SWITCH_SEEDED_STATS_RAISED, + SHOULD_SWITCH_INFATUATION, + SHOULD_SWITCH_HASBADODDS, + SHOULD_SWITCH_NATURAL_CURE_STRONG, + SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED, + SHOULD_SWITCH_NATURAL_CURE_WEAK, + SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED, + SHOULD_SWITCH_REGENERATOR, + SHOULD_SWITCH_REGENERATOR_STATS_RAISED, + SHOULD_SWITCH_ENCORE_STATUS, + SHOULD_SWITCH_ENCORE_DAMAGE, + SHOULD_SWITCH_CHOICE_LOCKED, + SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO, + SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS, +}; + enum SwitchType { SWITCH_AFTER_KO, diff --git a/include/config/ai.h b/include/config/ai.h new file mode 100644 index 0000000000..686ac14c22 --- /dev/null +++ b/include/config/ai.h @@ -0,0 +1,49 @@ +#ifndef GUARD_CONFIG_AI_H +#define GUARD_CONFIG_AI_H + +// For the details on what specific factors the switching functions are considering, go read the corresponding function inside ShouldSwitch in src/battle_ai_switch_items.c +// These configuration options control how likely the AI is to switch if it determines that a switch meets all of its criteria +// Think of them almost like success rates; if the AI has determined that it needs to switch out to hit Wonder Guard, how often do you want it to actually take that course of action? Etc. + +// AI switch chances; if you want more complex behaviour, modify GetSwitchChance +#define SHOULD_SWITCH_WONDER_GUARD_PERCENTAGE 100 +#define SHOULD_SWITCH_TRUANT_PERCENTAGE 100 +#define SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE 100 + +// AI smart switching chances; if you want more complex behaviour, modify GetSwitchChance +#define SHOULD_SWITCH_ABSORBS_MOVE_PERCENTAGE 100 +#define SHOULD_SWITCH_TRAPPER_PERCENTAGE 100 +#define SHOULD_SWITCH_FREE_TURN_PERCENTAGE 100 +#define STAY_IN_ABSORBING_PERCENTAGE 66 // Chance to stay in if outgoing mon has super effective move against player, will prevent switching out for an absorber with this likelihood +#define SHOULD_SWITCH_HASBADODDS_PERCENTAGE 50 +#define SHOULD_SWITCH_ENCORE_STATUS_PERCENTAGE 100 +#define SHOULD_SWITCH_ENCORE_DAMAGE_PERCENTAGE 50 +#define SHOULD_SWITCH_CHOICE_LOCKED_PERCENTAGE 100 // Only if locked into status move +#define SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO_PERCENTAGE 50 +#define SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS_PERCENTAGE 100 + +// AI smart switching chances for bad statuses +#define SHOULD_SWITCH_PERISH_SONG_PERCENTAGE 100 +#define SHOULD_SWITCH_YAWN_PERCENTAGE 100 +#define SHOULD_SWITCH_BADLY_POISONED_PERCENTAGE 50 +#define SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED_PERCENTAGE 20 +#define SHOULD_SWITCH_CURSED_PERCENTAGE 50 +#define SHOULD_SWITCH_CURSED_STATS_RAISED_PERCENTAGE 20 +#define SHOULD_SWITCH_NIGHTMARE_PERCENTAGE 33 +#define SHOULD_SWITCH_NIGHTMARE_STATS_RAISED_PERCENTAGE 15 +#define SHOULD_SWITCH_SEEDED_PERCENTAGE 25 +#define SHOULD_SWITCH_SEEDED_STATS_RAISED_PERCENTAGE 10 +#define SHOULD_SWITCH_INFATUATION_PERCENTAGE 100 + +// AI smart switching chances for beneficial abilities +#define SHOULD_SWITCH_NATURAL_CURE_STRONG_PERCENTAGE 66 +#define SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED_PERCENTAGE 10 +#define SHOULD_SWITCH_NATURAL_CURE_WEAK_PERCENTAGE 25 +#define SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED_PERCENTAGE 10 +#define SHOULD_SWITCH_REGENERATOR_PERCENTAGE 50 +#define SHOULD_SWITCH_REGENERATOR_STATS_RAISED_PERCENTAGE 20 + +// AI prediction chances +#define PREDICT_SWITCH_CHANCE 50 + +#endif // GUARD_CONFIG_AI_H diff --git a/include/constants/global.h b/include/constants/global.h index f8c70bd736..8ca3861709 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -10,6 +10,7 @@ #include "config/overworld.h" #include "config/dexnav.h" #include "config/summary_screen.h" +#include "config/ai.h" // Invalid Versions show as "----------" in Gen 4 and Gen 5's summary screen. // In Gens 6 and 7, invalid versions instead show "a distant land" in the summary screen. diff --git a/include/random.h b/include/random.h index 80e4ea8a24..c8b4a039b5 100644 --- a/include/random.h +++ b/include/random.h @@ -171,12 +171,22 @@ enum RandomTag RNG_AI_SWITCH_CURSED, RNG_AI_SWITCH_NIGHTMARE, RNG_AI_SWITCH_SEEDED, + RNG_AI_SWITCH_YAWN, + RNG_AI_SWITCH_PERISH_SONG, + RNG_AI_SWITCH_INFATUATION, RNG_AI_SWITCH_ABSORBING, + RNG_AI_SWITCH_ABSORBING_STAY_IN, RNG_AI_SWITCH_NATURAL_CURE, RNG_AI_SWITCH_REGENERATOR, RNG_AI_SWITCH_ENCORE, + RNG_AI_SWITCH_CHOICE_LOCKED, RNG_AI_SWITCH_STATS_LOWERED, RNG_AI_SWITCH_SE_DEFENSIVE, + RNG_AI_SWITCH_TRUANT, + RNG_AI_SWITCH_WONDER_GUARD, + RNG_AI_SWITCH_TRAPPER, + RNG_AI_SWITCH_FREE_TURN, + RNG_AI_SWITCH_ALL_MOVES_BAD, RNG_SHELL_SIDE_ARM, RNG_RANDOM_TARGET, RNG_AI_PREDICT_ABILITY, diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index df865cb6cb..59baf4362e 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -37,6 +37,74 @@ static void InitializeSwitchinCandidate(struct Pokemon *mon) AI_DATA->switchinCandidate.hypotheticalStatus = FALSE; } +u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario) +{ + // Modify these cases if you want unique behaviour based on other data (trainer class, difficulty, etc.) + switch(shouldSwitchScenario) + { + case SHOULD_SWITCH_WONDER_GUARD: + return SHOULD_SWITCH_WONDER_GUARD_PERCENTAGE; + case SHOULD_SWITCH_ABSORBS_MOVE: + return SHOULD_SWITCH_ABSORBS_MOVE_PERCENTAGE; + case SHOULD_SWITCH_TRAPPER: + return SHOULD_SWITCH_TRAPPER_PERCENTAGE; + case SHOULD_SWITCH_FREE_TURN: + return SHOULD_SWITCH_FREE_TURN_PERCENTAGE; + case SHOULD_SWITCH_TRUANT: + return SHOULD_SWITCH_TRUANT_PERCENTAGE; + case SHOULD_SWITCH_ALL_MOVES_BAD: + return SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE; + case SHOULD_SWITCH_PERISH_SONG: + return SHOULD_SWITCH_PERISH_SONG_PERCENTAGE; + case SHOULD_SWITCH_YAWN: + return SHOULD_SWITCH_YAWN_PERCENTAGE; + case SHOULD_SWITCH_BADLY_POISONED: + return SHOULD_SWITCH_BADLY_POISONED_PERCENTAGE; + case SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED: + return SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_CURSED: + return SHOULD_SWITCH_CURSED_PERCENTAGE; + case SHOULD_SWITCH_CURSED_STATS_RAISED: + return SHOULD_SWITCH_CURSED_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_NIGHTMARE: + return SHOULD_SWITCH_NIGHTMARE_PERCENTAGE; + case SHOULD_SWITCH_NIGHTMARE_STATS_RAISED: + return SHOULD_SWITCH_NIGHTMARE_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_SEEDED: + return SHOULD_SWITCH_SEEDED_PERCENTAGE; + case SHOULD_SWITCH_SEEDED_STATS_RAISED: + return SHOULD_SWITCH_SEEDED_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_INFATUATION: + return SHOULD_SWITCH_INFATUATION_PERCENTAGE; + case SHOULD_SWITCH_HASBADODDS: + return SHOULD_SWITCH_HASBADODDS_PERCENTAGE; + case SHOULD_SWITCH_NATURAL_CURE_STRONG: + return SHOULD_SWITCH_NATURAL_CURE_STRONG_PERCENTAGE; + case SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED: + return SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_NATURAL_CURE_WEAK: + return SHOULD_SWITCH_NATURAL_CURE_WEAK_PERCENTAGE; + case SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED: + return SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_REGENERATOR: + return SHOULD_SWITCH_REGENERATOR_PERCENTAGE; + case SHOULD_SWITCH_REGENERATOR_STATS_RAISED: + return SHOULD_SWITCH_REGENERATOR_STATS_RAISED_PERCENTAGE; + case SHOULD_SWITCH_ENCORE_STATUS: + return SHOULD_SWITCH_ENCORE_STATUS_PERCENTAGE; + case SHOULD_SWITCH_ENCORE_DAMAGE: + return SHOULD_SWITCH_ENCORE_DAMAGE_PERCENTAGE; + case SHOULD_SWITCH_CHOICE_LOCKED: + return SHOULD_SWITCH_CHOICE_LOCKED_PERCENTAGE; + case SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO: + return SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO_PERCENTAGE; + case SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS: + return SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS_PERCENTAGE; + default: + return 100; + } +} + u32 GetThinkingBattler(u32 battler) { if (AI_DATA->aiSwitchPredictionInProgress) @@ -198,7 +266,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) { // 50% chance to stay in regardless - if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50) && !AI_DATA->aiSwitchPredictionInProgress) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) return FALSE; // Switch mon out @@ -218,7 +286,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) return FALSE; // 50% chance to stay in regardless - if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50) && !AI_DATA->aiSwitchPredictionInProgress) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, (100 - GetSwitchChance(SHOULD_SWITCH_HASBADODDS))) && !AI_DATA->aiSwitchPredictionInProgress) return FALSE; // Switch mon out @@ -237,7 +305,8 @@ static bool32 ShouldSwitchIfTruant(u32 battler) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) { - return SetSwitchinAndSwitch(battler, PARTY_SIZE); + if (RandomPercentage(RNG_AI_SWITCH_TRUANT, GetSwitchChance(SHOULD_SWITCH_TRUANT))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); } return FALSE; } @@ -271,7 +340,9 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler) } } - return SetSwitchinAndSwitch(battler, PARTY_SIZE); + if (RandomPercentage(RNG_AI_SWITCH_ALL_MOVES_BAD, GetSwitchChance(SHOULD_SWITCH_ALL_MOVES_BAD))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); + return FALSE; } static bool32 FindMonThatHitsWonderGuard(u32 battler) @@ -320,7 +391,7 @@ static bool32 FindMonThatHitsWonderGuard(u32 battler) if (move != MOVE_NONE) { // Found a mon - if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0)) + if (AI_GetMoveEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0) && RandomPercentage(RNG_AI_SWITCH_WONDER_GUARD, GetSwitchChance(SHOULD_SWITCH_WONDER_GUARD))) return SetSwitchinAndSwitch(battler, i); } } @@ -351,7 +422,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) if (gBattleStruct->prevTurnSpecies[battler] != gBattleMons[battler].species) // AI mon has changed, player's behaviour no longer reliable; note to override this if using AI_FLAG_PREDICT_MOVE return FALSE; - if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING, 66) || AI_DATA->aiSwitchPredictionInProgress)) + if (HasSuperEffectiveMoveAgainstOpponents(battler, TRUE) && (RandomPercentage(RNG_AI_SWITCH_ABSORBING_STAY_IN, STAY_IN_ABSORBING_PERCENTAGE) || AI_DATA->aiSwitchPredictionInProgress)) return FALSE; if (IsDoubleBattle()) @@ -436,7 +507,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) for (j = 0; j < numAbsorbingAbilities; j++) { // Found a mon - if (absorbingTypeAbilities[j] == monAbility) + if (absorbingTypeAbilities[j] == monAbility && RandomPercentage(RNG_AI_SWITCH_ABSORBING, GetSwitchChance(SHOULD_SWITCH_ABSORBS_MOVE))) return SetSwitchinAndSwitch(battler, i); } } @@ -452,7 +523,7 @@ static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler) if (IsDoubleBattle() || !(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING)) return FALSE; - if (isOpposingBattlerChargingOrInvulnerable && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) + if (isOpposingBattlerChargingOrInvulnerable && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); return FALSE; @@ -489,7 +560,7 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler) if (CanAbilityTrapOpponent(monAbility, opposingBattler) || (CanAbilityTrapOpponent(AI_GetBattlerAbility(opposingBattler), opposingBattler) && monAbility == ABILITY_TRACE)) { // If mon in slot i is the most suitable switchin candidate, then it's a trapper than wins 1v1 - if (i == AI_DATA->mostSuitableMonId[battler]) + if (i == AI_DATA->mostSuitableMonId[battler] && RandomPercentage(RNG_AI_SWITCH_FREE_TURN, GetSwitchChance(SHOULD_SWITCH_FREE_TURN))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } } @@ -508,15 +579,17 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler) //Perish Song if (gStatuses3[battler] & STATUS3_PERISH_SONG && gDisableStructs[battler].perishSongTimer == 0 - && monAbility != ABILITY_SOUNDPROOF) - switchMon = TRUE; + && monAbility != ABILITY_SOUNDPROOF + && RandomPercentage(RNG_AI_SWITCH_PERISH_SONG, GetSwitchChance(SHOULD_SWITCH_PERISH_SONG))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING) { //Yawn if (gStatuses3[battler] & STATUS3_YAWN && CanBeSlept(battler, monAbility, BLOCKED_BY_SLEEP_CLAUSE) - && gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3) + && gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3 + && RandomPercentage(RNG_AI_SWITCH_YAWN, GetSwitchChance(SHOULD_SWITCH_YAWN))) { switchMon = TRUE; @@ -546,6 +619,9 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler) && !(gBattleMons[battler].status2 & STATUS2_FORESIGHT) && !(gStatuses3[battler] & STATUS3_MIRACLE_EYED)) switchMon = FALSE; + + if (switchMon) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // Secondary Damage @@ -556,35 +632,33 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler) if (((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2)) && gBattleMons[battler].hp >= (gBattleMons[battler].maxHP / 3) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 20) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 50))) - switchMon = TRUE; + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, GetSwitchChance(SHOULD_SWITCH_BADLY_POISONED)))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); //Cursed if (gBattleMons[battler].status2 & STATUS2_CURSED - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_CURSED, 20) : RandomPercentage(RNG_AI_SWITCH_CURSED, 50))) - switchMon = TRUE; + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_CURSED, GetSwitchChance(SHOULD_SWITCH_CURSED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_CURSED, GetSwitchChance(SHOULD_SWITCH_CURSED)))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); //Nightmare if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 15) : RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 33))) - switchMon = TRUE; + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, GetSwitchChance(SHOULD_SWITCH_NIGHTMARE_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, GetSwitchChance(SHOULD_SWITCH_NIGHTMARE)))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); //Leech Seed if (gStatuses3[battler] & STATUS3_LEECHSEED - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_SEEDED, 10) : RandomPercentage(RNG_AI_SWITCH_SEEDED, 25))) - switchMon = TRUE; + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_SEEDED, GetSwitchChance(SHOULD_SWITCH_SEEDED_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_SEEDED, GetSwitchChance(SHOULD_SWITCH_SEEDED)))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // Infatuation if (gBattleMons[battler].status2 & STATUS2_INFATUATION - && !AiExpectsToFaintPlayer(battler)) - switchMon = TRUE; + && !AiExpectsToFaintPlayer(battler) + && RandomPercentage(RNG_AI_SWITCH_INFATUATION, GetSwitchChance(SHOULD_SWITCH_INFATUATION))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); } - if (switchMon) - return SetSwitchinAndSwitch(battler, PARTY_SIZE); - else - return FALSE; + return FALSE; } static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) @@ -602,13 +676,13 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) //Attempt to cure bad ailment if (gBattleMons[battler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, 10) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, 66))) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_STRONG)))) break; //Attempt to cure lesser ailment if ((gBattleMons[battler].status1 & STATUS1_ANY) && (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, 10) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, 25))) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_NATURAL_CURE, GetSwitchChance(SHOULD_SWITCH_NATURAL_CURE_WEAK)))) break; return FALSE; @@ -619,7 +693,7 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) return FALSE; if ((gBattleMons[battler].hp <= ((gBattleMons[battler].maxHP * 2) / 3)) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE - && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_REGENERATOR, 20) : RandomPercentage(RNG_AI_SWITCH_REGENERATOR, 50))) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR_STATS_RAISED)) : RandomPercentage(RNG_AI_SWITCH_REGENERATOR, GetSwitchChance(SHOULD_SWITCH_REGENERATOR)))) break; return FALSE; @@ -858,7 +932,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler) return FALSE; // Switch out if status move - if (GetMoveCategory(encoredMove) == DAMAGE_CATEGORY_STATUS) + if (GetMoveCategory(encoredMove) == DAMAGE_CATEGORY_STATUS && RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_STATUS))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); // Stay in if effective move @@ -866,7 +940,7 @@ static bool32 ShouldSwitchIfEncored(u32 battler) return FALSE; // Switch out 50% of the time otherwise - else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, 50) || AI_DATA->aiSwitchPredictionInProgress) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) + else if ((RandomPercentage(RNG_AI_SWITCH_ENCORE, GetSwitchChance(SHOULD_SWITCH_ENCORE_DAMAGE)) || AI_DATA->aiSwitchPredictionInProgress) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE) return SetSwitchinAndSwitch(battler, PARTY_SIZE); return FALSE; @@ -878,7 +952,7 @@ static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) if (HOLD_EFFECT_CHOICE(holdEffect) && gBattleMons[battler].ability != ABILITY_KLUTZ) { - if (GetMoveCategory(AI_DATA->lastUsedMove[battler]) == DAMAGE_CATEGORY_STATUS) + if (GetMoveCategory(AI_DATA->lastUsedMove[battler]) == DAMAGE_CATEGORY_STATUS && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } @@ -904,11 +978,11 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) // 50% chance if attack at -2 and have a good candidate mon else if (attackingStage == DEFAULT_STAT_STAGE - 2) { - if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, 50) || AI_DATA->aiSwitchPredictionInProgress)) + if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // If at -3 or worse, switch out regardless - else if (attackingStage < DEFAULT_STAT_STAGE - 2) + else if ((attackingStage < DEFAULT_STAT_STAGE - 2) && RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } @@ -921,11 +995,11 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) // 50% chance if attack at -2 and have a good candidate mon else if (spAttackingStage == DEFAULT_STAT_STAGE - 2) { - if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, 50) || AI_DATA->aiSwitchPredictionInProgress)) + if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO)) || AI_DATA->aiSwitchPredictionInProgress)) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } // If at -3 or worse, switch out regardless - else if (spAttackingStage < DEFAULT_STAT_STAGE - 2) + else if ((spAttackingStage < DEFAULT_STAT_STAGE - 2) && RandomPercentage(RNG_AI_SWITCH_STATS_LOWERED, GetSwitchChance(SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } return FALSE; diff --git a/src/battle_main.c b/src/battle_main.c index 3000005917..5be19de6fc 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4160,7 +4160,7 @@ void SetupAISwitchingData(u32 battler, enum SwitchType switchType) gBattleStruct->prevTurnSpecies[opposingBattler] = gBattleMons[opposingBattler].species; // Determine whether AI will use predictions this turn - AI_DATA->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, 50); + AI_DATA->predictingSwitch = RandomPercentage(RNG_AI_PREDICT_SWITCH, PREDICT_SWITCH_CHANCE); } // AI's data diff --git a/test/battle/ai/ai_choice.c b/test/battle/ai/ai_choice.c index c3d3e02c03..b19af6a073 100644 --- a/test/battle/ai/ai_choice.c +++ b/test/battle/ai/ai_choice.c @@ -24,6 +24,8 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon switch out after using a status move onc PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; } } + PASSES_RANDOMLY(SHOULD_SWITCH_CHOICE_LOCKED_PERCENTAGE, 100, RNG_AI_SWITCH_CHOICE_LOCKED); + GIVEN { ASSUME(GetMoveCategory(MOVE_YAWN) == DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); diff --git a/test/battle/ai/ai_flag_predict_switch.c b/test/battle/ai/ai_flag_predict_switch.c index a5e74ff2d7..132ddf4175 100644 --- a/test/battle/ai/ai_flag_predict_switch.c +++ b/test/battle/ai/ai_flag_predict_switch.c @@ -27,18 +27,6 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: AI would switch out in Pursuit sc } } -AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: AI would switch out in Wonder Guard scenario") -{ - GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); - PLAYER(SPECIES_SHEDINJA) { Moves(MOVE_PURSUIT, MOVE_CRUNCH); } - OPPONENT(SPECIES_GENGAR) { Moves(MOVE_PSYCHIC); } - OPPONENT(SPECIES_SWELLOW) { Moves(MOVE_PECK); } - } WHEN { - TURN { MOVE(player, MOVE_PURSUIT); EXPECT_SWITCH(opponent, 1); } - } -} - AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: AI will use hit escape moves on predicted switches") { PASSES_RANDOMLY(5, 10, RNG_AI_PREDICT_SWITCH); @@ -137,7 +125,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH | AI_FLAG_PREDICT_INCOMING_MON: AI AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_SWITCH: AI would switch out in predicted-incoming-mon scenario") { - PASSES_RANDOMLY(5, 10, RNG_AI_SWITCH_HASBADODDS); + PASSES_RANDOMLY(SHOULD_SWITCH_HASBADODDS_PERCENTAGE, 100, RNG_AI_SWITCH_HASBADODDS); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); PLAYER(SPECIES_TYRANITAR) { Moves(MOVE_CRUNCH, MOVE_SPORE); } diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 82ad5440aa..227909e400 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -24,6 +24,7 @@ AI_SINGLE_BATTLE_TEST("AI gets baited by Protect Switch tactics") // This behavi // General switching behaviour AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill") { + PASSES_RANDOMLY(SHOULD_SWITCH_PERISH_SONG_PERCENTAGE, 100, RNG_AI_SWITCH_PERISH_SONG); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET); @@ -46,6 +47,7 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spot PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; } PARAMETRIZE {flags = 0; } + PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags); PLAYER(SPECIES_RATTATA); @@ -67,8 +69,22 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spot } } +AI_SINGLE_BATTLE_TEST("AI will switch out if it has no move that affects the player") +{ + PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_RATTATA); + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); } + OPPONENT(SPECIES_RATTATA) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { EXPECT_SWITCH(opponent, 1); } + } +} + AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spots in a double battle (Wonder Guard)") { + PASSES_RANDOMLY(SHOULD_SWITCH_WONDER_GUARD_PERCENTAGE, 100, RNG_AI_SWITCH_WONDER_GUARD); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING); PLAYER(SPECIES_SHEDINJA); @@ -430,7 +446,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch in trapping mon m u32 aiSmartSwitchingFlag = 0; PARAMETRIZE { aiSmartSwitchingFlag = 0; } PARAMETRIZE { aiSmartSwitchingFlag = AI_FLAG_SMART_SWITCHING; } - + PASSES_RANDOMLY(SHOULD_SWITCH_TRAPPER_PERCENTAGE, 100, RNG_AI_SWITCH_TRAPPER); GIVEN { ASSUME(gSpeciesInfo[SPECIES_GOLURK].types[0] == TYPE_GROUND); ASSUME(gSpeciesInfo[SPECIES_GOLURK].types[1] == TYPE_GHOST); @@ -488,6 +504,7 @@ AI_SINGLE_BATTLE_TEST("AI won't use trapping behaviour if player only has 1 mon AI_SINGLE_BATTLE_TEST("AI will trap player using Trace if player has a trapper") { + PASSES_RANDOMLY(SHOULD_SWITCH_TRAPPER_PERCENTAGE, 100, RNG_AI_SWITCH_TRAPPER); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING); PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_ARENA_TRAP); Moves(MOVE_ROCK_TOMB); } @@ -501,7 +518,7 @@ AI_SINGLE_BATTLE_TEST("AI will trap player using Trace if player has a trapper") AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would be OKHO'd and they have a good switchin 50% of the time") { - PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_HASBADODDS); + PASSES_RANDOMLY(SHOULD_SWITCH_HASBADODDS_PERCENTAGE, 100, RNG_AI_SWITCH_HASBADODDS); GIVEN { ASSUME(gSpeciesInfo[SPECIES_RHYDON].types[0] == TYPE_GROUND); ASSUME(gSpeciesInfo[SPECIES_PELIPPER].types[0] == TYPE_WATER); @@ -520,6 +537,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if it can't deal damage to a mon with Wonder Guard") { + PASSES_RANDOMLY(SHOULD_SWITCH_WONDER_GUARD_PERCENTAGE, 100, RNG_AI_SWITCH_WONDER_GUARD); GIVEN { ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[0] == TYPE_BUG); ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[1] == TYPE_GHOST); @@ -539,6 +557,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if it can't deal damage to AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it can't deal damage to a mon with Wonder Guard") { + PASSES_RANDOMLY(SHOULD_SWITCH_WONDER_GUARD_PERCENTAGE, 100, RNG_AI_SWITCH_WONDER_GUARD); GIVEN { ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[0] == TYPE_BUG); ASSUME(gSpeciesInfo[SPECIES_SHEDINJA].types[1] == TYPE_GHOST); @@ -560,7 +579,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee { u32 species = SPECIES_NONE, odds = 0; PARAMETRIZE { species = SPECIES_ZIGZAGOON, odds = 0; } - PARAMETRIZE { species = SPECIES_HARIYAMA, odds = 50; } + PARAMETRIZE { species = SPECIES_HARIYAMA, odds = SHOULD_SWITCH_BADLY_POISONED_PERCENTAGE; } PASSES_RANDOMLY(odds, 100, RNG_AI_SWITCH_BADLY_POISONED); GIVEN { ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC); @@ -577,7 +596,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Curse'd 50% of the time") { - PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_CURSED); + PASSES_RANDOMLY(SHOULD_SWITCH_CURSED_PERCENTAGE, 100, RNG_AI_SWITCH_CURSED); GIVEN { ASSUME(GetMoveEffect(MOVE_CURSE) == EFFECT_CURSE); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -593,7 +612,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Nightmare'd 33% of the time") { - PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_NIGHTMARE); + PASSES_RANDOMLY(SHOULD_SWITCH_NIGHTMARE_PERCENTAGE, 100, RNG_AI_SWITCH_NIGHTMARE); GIVEN { ASSUME(GetMoveEffect(MOVE_NIGHTMARE) == EFFECT_NIGHTMARE); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -608,7 +627,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Leech Seed'd 25% of the time") { - PASSES_RANDOMLY(25, 100, RNG_AI_SWITCH_SEEDED); + PASSES_RANDOMLY(SHOULD_SWITCH_SEEDED_PERCENTAGE, 100, RNG_AI_SWITCH_SEEDED); GIVEN { ASSUME(GetMoveEffect(MOVE_LEECH_SEED) == EFFECT_LEECH_SEED); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -623,6 +642,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been infatuated") { + PASSES_RANDOMLY(SHOULD_SWITCH_INFATUATION_PERCENTAGE, 100, RNG_AI_SWITCH_INFATUATION); GIVEN { ASSUME(GetMoveEffect(MOVE_ATTRACT) == EFFECT_ATTRACT); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -640,6 +660,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee u32 hp; PARAMETRIZE { hp = 30; } PARAMETRIZE { hp = 10; } + PASSES_RANDOMLY(SHOULD_SWITCH_YAWN_PERCENTAGE, 100, RNG_AI_SWITCH_YAWN); GIVEN { ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -660,6 +681,7 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee u32 hp; PARAMETRIZE { hp = 30; } PARAMETRIZE { hp = 10; } + PASSES_RANDOMLY(SHOULD_SWITCH_YAWN_PERCENTAGE, 100, RNG_AI_SWITCH_YAWN); GIVEN { ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -680,6 +702,7 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's mon is semi-invulnerable and it has an absorber") { + PASSES_RANDOMLY(SHOULD_SWITCH_FREE_TURN_PERCENTAGE, 100, RNG_AI_SWITCH_FREE_TURN); GIVEN { ASSUME(GetMoveType(MOVE_DIVE) == TYPE_WATER); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -694,7 +717,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's m AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an absorber but current mon has SE move 33% of the time") { - PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_ABSORBING); + PASSES_RANDOMLY(33, 100, RNG_AI_SWITCH_ABSORBING_STAY_IN); GIVEN { ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -709,7 +732,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's mon is charging and it has an absorber") { - PASSES_RANDOMLY(100, 100, RNG_AI_SWITCH_ABSORBING); + PASSES_RANDOMLY(SHOULD_SWITCH_ABSORBS_MOVE_PERCENTAGE, 100, RNG_AI_SWITCH_ABSORBING); GIVEN { ASSUME(GetMoveType(MOVE_SOLAR_BEAM) == TYPE_GRASS); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -724,6 +747,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's m AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's mon is charging and it has a good switchin immunity (type)") { + PASSES_RANDOMLY(SHOULD_SWITCH_FREE_TURN_PERCENTAGE, 100, RNG_AI_SWITCH_FREE_TURN); GIVEN { ASSUME(GetMoveType(MOVE_DIG) == TYPE_GROUND); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -738,6 +762,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's m AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if player's mon is charging and it has a good switchin immunity (ability)") { + PASSES_RANDOMLY(SHOULD_SWITCH_FREE_TURN_PERCENTAGE, 100, RNG_AI_SWITCH_FREE_TURN); GIVEN { ASSUME(GetMoveType(MOVE_DIG) == TYPE_GROUND); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -799,7 +824,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if opponent u AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if badly statused with >= 50% HP remaining and has Natural Cure and a good switchin 66% of the time") { - PASSES_RANDOMLY(66, 100, RNG_AI_SWITCH_NATURAL_CURE); + PASSES_RANDOMLY(SHOULD_SWITCH_NATURAL_CURE_STRONG_PERCENTAGE, 100, RNG_AI_SWITCH_NATURAL_CURE); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_ODDISH) { Moves(MOVE_TOXIC, MOVE_TACKLE); } @@ -813,7 +838,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if badly statused with >= 5 AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if it has <= 66% HP remaining and has Regenerator and a good switchin 50% of the time") { - PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_REGENERATOR); + PASSES_RANDOMLY(SHOULD_SWITCH_REGENERATOR_PERCENTAGE, 100, RNG_AI_SWITCH_REGENERATOR); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } @@ -826,6 +851,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if it has <= 66% HP remaini AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Encore'd into a status move") { + PASSES_RANDOMLY(SHOULD_SWITCH_ENCORE_STATUS_PERCENTAGE, 100, RNG_AI_SWITCH_ENCORE); GIVEN { ASSUME(GetMoveEffect(MOVE_ENCORE) == EFFECT_ENCORE); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -855,7 +881,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will stay in if Encore'd into AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if Encore'd into neutral move with good switchin 50% of the time") { KNOWN_FAILING; // AI still switches even if ShouldSwitch is set to immediately return FALSE, something external seems to be triggering this - PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_ENCORE); + PASSES_RANDOMLY(SHOULD_SWITCH_ENCORE_DAMAGE_PERCENTAGE, 100, RNG_AI_SWITCH_ENCORE); GIVEN { ASSUME(GetMoveEffect(MOVE_ENCORE) == EFFECT_ENCORE); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); @@ -870,6 +896,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if Encore'd i AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and opponent has Protect") { + PASSES_RANDOMLY(SHOULD_SWITCH_TRUANT_PERCENTAGE, 100, RNG_AI_SWITCH_TRUANT); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); PLAYER(SPECIES_ARON) { Moves(MOVE_TACKLE, MOVE_PROTECT); } @@ -883,6 +910,7 @@ AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and oppon AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch out if mon has Truant and opponent has invulnerability move and is faster") { + PASSES_RANDOMLY(SHOULD_SWITCH_TRUANT_PERCENTAGE, 100, RNG_AI_SWITCH_TRUANT); GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); PLAYER(SPECIES_SWELLOW) { Speed(5); Moves(MOVE_FLY); } @@ -898,7 +926,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if main attac { u32 aiSpecies = SPECIES_NONE, aiMove = MOVE_NONE, move = MOVE_NONE; - PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_STATS_LOWERED); + PASSES_RANDOMLY(SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO_PERCENTAGE, 100, RNG_AI_SWITCH_STATS_LOWERED); PARAMETRIZE {move = MOVE_CHARM; aiSpecies = SPECIES_FLAREON; aiMove = MOVE_FIRE_FANG; }; PARAMETRIZE {move = MOVE_EERIE_IMPULSE; aiSpecies = SPECIES_ESPEON; aiMove = MOVE_CONFUSION; }; @@ -919,7 +947,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if main attac { u32 aiSpecies = SPECIES_NONE, aiMove = MOVE_NONE, move = MOVE_NONE, move2 = MOVE_NONE; - PASSES_RANDOMLY(100, 100, RNG_AI_SWITCH_STATS_LOWERED); + PASSES_RANDOMLY(SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS_PERCENTAGE, 100, RNG_AI_SWITCH_STATS_LOWERED); PARAMETRIZE {move = MOVE_GROWL; move2 = MOVE_CHARM; aiSpecies = SPECIES_FLAREON; aiMove = MOVE_FIRE_FANG; }; PARAMETRIZE {move = MOVE_CONFIDE; move2 = MOVE_EERIE_IMPULSE; aiSpecies = SPECIES_ESPEON; aiMove = MOVE_STORED_POWER; };