diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index 8f482c581271..18148a89b04e 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -29,7 +29,8 @@ #define STAT_CHANGE_ACC 10 #define STAT_CHANGE_EVASION 11 -#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target // Temporary scores that are added together to determine a final score at the at of AI_CalcMoveScore #define WEAK_EFFECT 1 diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index cfdb791e0ece..2d541ea73e5d 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -189,6 +189,6 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle); s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index a353fbb35710..10634aad43d0 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -354,7 +354,7 @@ #define MOVE_EFFECT_PREVENT_ESCAPE 33 #define MOVE_EFFECT_NIGHTMARE 34 #define MOVE_EFFECT_ALL_STATS_UP 35 -#define MOVE_EFFECT_RAPIDSPIN 36 +#define MOVE_EFFECT_RAPID_SPIN 36 #define MOVE_EFFECT_REMOVE_STATUS 37 #define MOVE_EFFECT_ATK_DEF_DOWN 38 #define MOVE_EFFECT_ATK_PLUS_2 39 diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index ad489a4dd618..2093312ea619 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -34,20 +34,20 @@ #define AI_FLAG_PREFER_BATON_PASS (1 << 6) #define AI_FLAG_DOUBLE_BATTLE (1 << 7) // removed, split between AI_FLAG_CHECK_BAD_MOVE & AI_FLAG_CHECK_GOOD_MOVE #define AI_FLAG_HP_AWARE (1 << 8) +#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target // New, Trainer Handicap Flags -#define AI_FLAG_NEGATE_UNAWARE (1 << 9) // AI is NOT aware of negating effects like wonder room, mold breaker, etc -#define AI_FLAG_WILL_SUICIDE (1 << 10) // AI will use explosion / self destruct / final gambit / etc +#define AI_FLAG_NEGATE_UNAWARE (1 << 10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc +#define AI_FLAG_WILL_SUICIDE (1 << 11) // AI will use explosion / self destruct / final gambit / etc // New, Trainer Strategy Flags -#define AI_FLAG_HELP_PARTNER (1 << 11) // AI can try to help partner. If not set, will tend not to target partner -#define AI_FLAG_PREFER_STATUS_MOVES (1 << 12) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves -#define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished -#define AI_FLAG_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished +#define AI_FLAG_HELP_PARTNER (1 << 12) // AI can try to help partner. If not set, will tend not to target partner +#define AI_FLAG_PREFER_STATUS_MOVES (1 << 13) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves +#define AI_FLAG_STALL (1 << 14) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished #define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks #define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining. #define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items #define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Pairs very well with AI_FLAG_SMART_SWITCHING. -#define AI_FLAG_COUNT 18 +#define AI_FLAG_COUNT 19 // 'other' ai logic flags #define AI_FLAG_ROAMING (1 << 29) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d3ef2dca0e89..fdf364c79ba7 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -52,6 +52,7 @@ 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 s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = @@ -65,10 +66,10 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = [6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS [7] = AI_DoubleBattle, // AI_FLAG_DOUBLE_BATTLE [8] = AI_HPAware, // AI_FLAG_HP_AWARE - [9] = NULL, // AI_FLAG_NEGATE_UNAWARE - [10] = NULL, // AI_FLAG_WILL_SUICIDE - [11] = NULL, // AI_FLAG_HELP_PARTNER - [12] = NULL, // Unused + [9] = AI_PowerfulStatus, // AI_FLAG_POWERFUL_STATUS + [10] = NULL, // AI_FLAG_NEGATE_UNAWARE + [11] = NULL, // AI_FLAG_WILL_SUICIDE + [12] = NULL, // AI_FLAG_HELP_PARTNER [13] = NULL, // Unused [14] = NULL, // Unused [15] = NULL, // Unused @@ -2341,15 +2342,18 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { ADJUST_SCORE(-10); } - else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + else if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) { - if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side - ADJUST_SCORE(-10); // Keep the Trick Room up - } - else - { - if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side - ADJUST_SCORE(-10); // Keep the Trick Room down + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + { + if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side + ADJUST_SCORE(-10); // Keep the Trick Room up + } + else + { + if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side + ADJUST_SCORE(-10); // Keep the Trick Room down + } } break; case EFFECT_MAGIC_ROOM: @@ -3489,8 +3493,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(BEST_EFFECT); if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) ADJUST_SCORE(DECENT_EFFECT); - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); } break; case EFFECT_REST: @@ -3518,10 +3520,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) if (ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(GOOD_EFFECT); break; - case EFFECT_MIST: - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); - break; case EFFECT_FOCUS_ENERGY: case EFFECT_LASER_FOCUS: if (aiData->abilities[battlerAtk] == ABILITY_SUPER_LUCK @@ -3562,7 +3560,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_LEECH_SEED: if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || gStatuses3[battlerDef] & STATUS3_LEECHSEED - || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) + || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) || aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; @@ -3721,7 +3719,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_STEALTH_ROCK: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case EFFECT_FORESIGHT: if (aiData->abilities[battlerAtk] == ABILITY_SCRAPPY || aiData->abilities[battlerAtk] == ABILITY_MINDS_EYE) @@ -4254,10 +4258,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move break; case EFFECT_TRICK_ROOM: - if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); - else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) + { + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + } break; case EFFECT_MAGIC_ROOM: ADJUST_SCORE(WEAK_EFFECT); @@ -4505,7 +4512,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score); } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0) || (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED)) ADJUST_SCORE(GOOD_EFFECT); @@ -4638,7 +4645,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) break; case MOVE_EFFECT_STEALTH_ROCK: case MOVE_EFFECT_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case MOVE_EFFECT_FEINT: if (gMovesInfo[predictedMove].effect == EFFECT_PROTECT) @@ -4654,7 +4667,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case MOVE_EFFECT_WRAP: - if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && ShouldTrap(battlerAtk, battlerDef, move)) + if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(BEST_EFFECT); break; } @@ -5124,6 +5137,96 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) return score; } +static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) +{ + u32 moveEffect = gMovesInfo[move].effect; + + if (gMovesInfo[move].category != DAMAGE_CATEGORY_STATUS || gMovesInfo[AI_DATA->partnerMove].effect == moveEffect) + return score; + + switch (moveEffect) + { + case EFFECT_TAILWIND: + if (!gSideTimers[GetBattlerSide(battlerAtk)].tailwindTimer && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 1)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_TRICK_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !HasMoveEffect(battlerDef, EFFECT_TRICK_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MAGIC_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) && !HasMoveEffect(battlerDef, EFFECT_MAGIC_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_WONDER_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_WONDER_ROOM) && !HasMoveEffect(battlerDef, EFFECT_WONDER_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRAVITY: + if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SAFEGUARD: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_SAFEGUARD)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MIST: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_MIST)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_LIGHT_SCREEN: + case EFFECT_REFLECT: + case EFFECT_AURORA_VEIL: + if (ShouldSetScreen(battlerAtk, battlerDef, moveEffect)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SPIKES: + case EFFECT_STEALTH_ROCK: + case EFFECT_STICKY_WEB: + case EFFECT_TOXIC_SPIKES: + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, AI_DATA)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRASSY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_ELECTRIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_PSYCHIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MISTY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SANDSTORM: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SUNNY_DAY: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_RAIN_DANCE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_HAIL: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SNOWSCAPE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + } + + return score; +} + static void AI_Flee(void) { AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index cb903dae541b..4a64f350135d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2860,6 +2860,10 @@ bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent) bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect) { u32 atkSide = GetBattlerSide(battlerAtk); + + if (HasMoveEffect(battlerDef, EFFECT_BRICK_BREAK)) // Don't waste a turn if screens will be broken + return FALSE; + switch (moveEffect) { case EFFECT_AURORA_VEIL: @@ -3567,10 +3571,13 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) } //TODO - track entire opponent party data to determine hazard effectiveness -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) { - if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) - return 0; + if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE + || CountUsablePartyMons(battlerDef) == 0 + || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) + || HasMoveEffect(battlerDef, EFFECT_DEFOG)) + return FALSE; - return 2 * gDisableStructs[battlerAtk].isFirstTurn; + return TRUE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index de0e478d100b..f4b2385773b2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3445,7 +3445,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_AllStatsUp; } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_RapidSpinAway; break; diff --git a/src/battle_tv.c b/src/battle_tv.c index cb70d1deedd4..80cb0020f81f 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -942,7 +942,7 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3) // Various cases to add/remove points if (gMovesInfo[arg2].recoil > 0) baseFromEffect++; // Recoil moves - if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPIDSPIN)) + if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPID_SPIN)) baseFromEffect++; if (MoveHasMoveEffect(arg2, MOVE_EFFECT_SP_ATK_TWO_DOWN) || MoveHasMoveEffect(arg2, MOVE_EFFECT_ATK_DEF_DOWN)) baseFromEffect += 2; // Overheat, Superpower, etc. diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 88873e4f2893..a4e1f3c74390 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -5638,7 +5638,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, } #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 @@ -18925,7 +18925,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sheerForceBoost = TRUE, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, }, { diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c index 4cd95de61978..cac3dfac4c7f 100644 --- a/test/battle/ability/shield_dust.c +++ b/test/battle/ability/shield_dust.c @@ -94,7 +94,7 @@ SINGLE_BATTLE_TEST("Shield Dust does not block self-targeting effects, primary o GIVEN { ASSUME(MoveHasMoveEffectSelf(MOVE_POWER_UP_PUNCH, MOVE_EFFECT_ATK_PLUS_1) == TRUE); - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_LEAF_STORM, MOVE_EFFECT_SP_ATK_TWO_DOWN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index 69794804c743..a2cd33ed2cb6 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -179,7 +179,6 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% c PARAMETRIZE { ability = ABILITY_SERENE_GRACE; } GIVEN { - AI_LOG; ASSUME(MoveHasMoveEffectWithChance(MOVE_SHADOW_BALL, MOVE_EFFECT_SP_DEF_MINUS_1, 20)); ASSUME(MoveHasMoveEffectWithChance(MOVE_OCTAZOOKA, MOVE_EFFECT_ACC_MINUS_1, 50)); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); diff --git a/test/battle/ai_powerful_status.c b/test/battle/ai_powerful_status.c new file mode 100644 index 000000000000..b625b1126f7a --- /dev/null +++ b/test/battle/ai_powerful_status.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test/battle.h" +#include "battle_ai_util.h" + +AI_SINGLE_BATTLE_TEST("AI prefers to set up a powerful Status over fainting a target") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TRICK_ROOM].effect == EFFECT_TRICK_ROOM); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_TRICK_ROOM, MOVE_TACKLE); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TRICK_ROOM); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will try to do damage on target instead of setting up hazards if target has a way to remove them") +{ + GIVEN { + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].effect == EFFECT_STEALTH_ROCK); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_POWERFUL_STATUS | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); Moves(MOVE_RAPID_SPIN, MOVE_DEFOG, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GLIGAR) { Moves(MOVE_STEALTH_ROCK, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will not set up Rain if it is already raining") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_RAIN_DANCE].effect == EFFECT_RAIN_DANCE); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_RAIN_DANCE, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_RAIN_DANCE); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); } + } +} diff --git a/test/battle/move_effect/rapid_spin.c b/test/battle/move_effect/rapid_spin.c index 2874aa45270b..1d078ef71e02 100644 --- a/test/battle/move_effect/rapid_spin.c +++ b/test/battle/move_effect/rapid_spin.c @@ -3,11 +3,11 @@ ASSUMPTIONS { - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_SPD_PLUS_1) == TRUE); #endif - ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON) == TRUE); }