diff --git a/include/random.h b/include/random.h index 6e210bcbb4ae..d41e4785435b 100644 --- a/include/random.h +++ b/include/random.h @@ -163,6 +163,10 @@ enum RandomTag RNG_AI_ABILITY, RNG_AI_SWITCH_HASBADODDS, RNG_AI_SWITCH_WONDER_GUARD, + RNG_AI_SWITCH_BADLY_POISONED, + RNG_AI_SWITCH_CURSED, + RNG_AI_SWITCH_NIGHTMARE, + RNG_AI_SWITCH_SEEDED, RNG_SHELL_SIDE_ARM, }; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 9731a6393bb2..0400f18b5310 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -180,7 +180,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) { // 50% chance to stay in regardless - if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) return FALSE; // Switch mon out @@ -203,7 +203,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult) return FALSE; // 50% chance to stay in regardless - if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) + if (RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50)) return FALSE; // Switch mon out @@ -462,16 +462,12 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) u16 holdEffect = AI_DATA->holdEffects[battler]; u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); u8 opposingBattler = GetBattlerAtPosition(opposingPosition); - s32 moduloChance = 4; //25% Chance Default - s32 chanceReducer = 1; //No Reduce default. Increase to reduce + bool32 hasStatRaised = AnyStatIsRaised(battler); s32 firstId; s32 lastId; s32 i; struct Pokemon *party; - if (AnyStatIsRaised(battler)) - chanceReducer = 5; // Reduce switchout probability by factor of 5 if setup - //Perish Song if (gStatuses3[battler] & STATUS3_PERISH_SONG && gDisableStructs[battler].perishSongTimer == 0 @@ -567,28 +563,24 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) && !AiExpectsToFaintPlayer(battler)) { //Toxic - moduloChance = 2; //50% if (((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >= STATUS1_TOXIC_TURN(2)) && gBattleMons[battler].hp >= (gBattleMons[battler].maxHP / 3) - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 20) : RandomPercentage(RNG_AI_SWITCH_BADLY_POISONED, 50))) switchMon = TRUE; //Cursed - moduloChance = 2; //50% if (gBattleMons[battler].status2 & STATUS2_CURSED - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_CURSED, 20) : RandomPercentage(RNG_AI_SWITCH_CURSED, 50))) switchMon = TRUE; //Nightmare - moduloChance = 3; //33.3% if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 15) : RandomPercentage(RNG_AI_SWITCH_NIGHTMARE, 33))) switchMon = TRUE; //Leech Seed - moduloChance = 4; //25% if (gStatuses3[battler] & STATUS3_LEECHSEED - && (Random() % (moduloChance*chanceReducer)) == 0) + && (hasStatRaised ? RandomPercentage(RNG_AI_SWITCH_SEEDED, 10) : RandomPercentage(RNG_AI_SWITCH_SEEDED, 25))) switchMon = TRUE; } @@ -606,7 +598,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) if (FindMonThatAbsorbsOpponentsMove(battler, FALSE)) // Switch if absorber found. Note: FindMonThatAbsorbsOpponentsMove already provides id of the mon to switch into to gBattleStruct->AI_monToSwitchIntoId. switchMon = TRUE, monIdChosen = TRUE; if (!AI_OpponentCanFaintAiWithMod(battler, 0) - && AnyStatIsRaised(battler)) + && hasStatRaised) switchMon = FALSE; if (AiExpectsToFaintPlayer(battler) && AI_IsSlower(battler, opposingBattler, 0) diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index f3b2429dd843..a70163af18ae 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -486,3 +486,65 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it can't d TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); } } } + +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has been Toxic'd for at least two turns 50% of the time with more than 1/3 HP remaining") +{ + PASSES_RANDOMLY(50, 100, RNG_AI_SWITCH_BADLY_POISONED); + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE, MOVE_CELEBRATE, MOVE_TOXIC); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); } + } +} + +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); + GIVEN { + ASSUME(gMovesInfo[MOVE_CURSE].effect == EFFECT_CURSE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_DUSCLOPS) { Moves(MOVE_FIRE_PUNCH, MOVE_CURSE); } + PLAYER(SPECIES_MILOTIC) { Moves(MOVE_WATER_GUN); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + } WHEN { + TURN { MOVE(player, MOVE_CURSE) ; EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); } + TURN { MOVE(player, MOVE_FIRE_PUNCH); EXPECT_SWITCH(opponent, 1); } + } +} + +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); + GIVEN { + ASSUME(gMovesInfo[MOVE_NIGHTMARE].effect == EFFECT_NIGHTMARE); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_GENGAR) { Moves(MOVE_NIGHTMARE); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_SHADOW_BALL); } + } WHEN { + TURN { MOVE(player, MOVE_NIGHTMARE) ; EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); } + TURN { MOVE(player, MOVE_NIGHTMARE) ; EXPECT_SWITCH(opponent, 1); } + } +} + +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); + GIVEN { + ASSUME(gMovesInfo[MOVE_LEECH_SEED].effect == EFFECT_LEECH_SEED); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); + PLAYER(SPECIES_WHIMSICOTT) { Moves(MOVE_LEECH_SEED); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { MOVE(player, MOVE_LEECH_SEED) ; EXPECT_MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_LEECH_SEED); EXPECT_SWITCH(opponent, 1); } + } +}