From 3ee93c69ecf5349f88f4114a9548309621016bc8 Mon Sep 17 00:00:00 2001 From: surskitty Date: Thu, 10 Jul 2025 23:38:08 -0400 Subject: [PATCH 1/9] Preliminary handling for AI_FLAG_ASSUME_POWERFUL_STATUS, not actually yet called. --- docs/tutorials/ai_flags.md | 4 ++ include/battle_ai_util.h | 2 + include/config/ai.h | 5 ++ include/constants/battle_ai.h | 3 +- src/battle_ai_main.c | 11 +++++ src/battle_ai_util.c | 90 +++++++++++++++++++++++++++++++++++ 6 files changed, 114 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/ai_flags.md b/docs/tutorials/ai_flags.md index 7910049fbe..9f39090b65 100644 --- a/docs/tutorials/ai_flags.md +++ b/docs/tutorials/ai_flags.md @@ -147,6 +147,10 @@ AI has full knowledge of player moves, abilities, and hold items, and can use th ## `AI_FLAG_ASSUME_STAB` A significantly more restricted version of `AI_FLAG_OMNISCIENT`, the AI only knows the player's STAB moves, as their existence would be reasonable to assume in almost any case. +## `AI_FLAG_ASSUME_POWERFUL_STATUS` +A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know if the player has certain iconic status moves, plus also Fake Out and fixed percentage moves like Super Fang. + + ## `AI_FLAG_SMART_MON_CHOICES` Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when `AI_FLAG_SMART_SWITCHING` is enabled. diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index b0ad69f5d9..1e03090dde 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -61,6 +61,8 @@ bool32 IsAiVsAiBattle(void); bool32 BattlerHasAi(u32 battlerId); bool32 IsAiBattlerAware(u32 battlerId); bool32 IsAiBattlerAssumingStab(void); +bool32 IsAiBattlerAssumingPowerfulStatus(void); +bool32 ShouldRecordStatusMove(u32 move); void ClearBattlerMoveHistory(u32 battlerId); void RecordLastUsedMoveBy(u32 battlerId, u32 move); void RecordAllMoves(u32 battler); diff --git a/include/config/ai.h b/include/config/ai.h index 57fc7ecb60..be091649af 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -76,6 +76,11 @@ // AI_FLAG_ASSUME_STAB settings #define ASSUME_STAB_SEES_ABILITY FALSE // Flag also gives omniscience for player's ability. Can use AI_FLAG_WEIGH_ABILITY_PREDICTION instead for smarter prediction without omniscience. +// AI_FLAG_ASSUME_POWERFUL_STATUS settings +#define ASSUME_POWERFUL_STATUS_HIGH_ODDS 95 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore +#define ASSUME_POWERFUL_STATUS_MEDIUM_ODDS 75 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect +#define ASSUME_POWERFUL_STATUS_LOW_ODDS 50 // Chance for AI to see niche moves a pokemon may have but probably won't, like Trick Room or Speed Swap + // AI_FLAG_SMART_SWITCHING settings #define SMART_SWITCHING_OMNISCIENT FALSE // AI will use omniscience for switching calcs, regardless of omniscience setting otherwise diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index 4fc13aaa1b..fa2a96ee5d 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -34,8 +34,9 @@ #define AI_FLAG_PREDICT_MOVE (1 << 26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT #define AI_FLAG_SMART_TERA (1 << 27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available). #define AI_FLAG_ASSUME_STAB (1 << 28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT. +#define AI_FLAG_ASSUME_POWERFUL_STATUS (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT. -#define AI_FLAG_COUNT 29 +#define AI_FLAG_COUNT 30 // Flags at and after 32 need different formatting, as in // #define AI_FLAG_PLACEHOLDER ((u64)1 << 32) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 43d01dfa2e..a69edaa48f 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -544,6 +544,17 @@ void RecordMovesBasedOnStab(u32 battler) } } +void RecordPowerfulStatusMoves(u32 battler) +{ + u32 i; + for (i = 0; i < MAX_MON_MOVES; i++) + { + u32 playerMove = gBattleMons[battler].moves[i]; + if (ShouldRecordStatusMove(playerMove)) + RecordKnownMove(battler, playerMove); + } +} + void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) { u32 ability, holdEffect; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 48403d4e11..b6a208a9c7 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -249,6 +249,96 @@ void SaveBattlerData(u32 battlerId) gAiThinkingStruct->saved[battlerId].types[1] = gBattleMons[battlerId].types[1]; } +bool32 ShouldRecordStatusMove(u32 move) +{ + u32 rand = Random() % 100; + + if (rand >= ASSUME_POWERFUL_STATUS_HIGH_ODDS) + return FALSE; + + switch (GetMoveEffect(move)) + { + // variable odds by additional effect + case EFFECT_NON_VOLATILE_STATUS: + if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP) + return TRUE; + else if (rand < ASSUME_POWERFUL_STATUS_MEDIUM_ODDS) + return TRUE; + break; + // High odds + case EFFECT_AURORA_VEIL: + case EFFECT_CHILLY_RECEPTION: + case EFFECT_CONVERSION: + case EFFECT_FIRST_TURN_ONLY: + case EFFECT_FOLLOW_ME: + case EFFECT_INSTRUCT: + case EFFECT_JUNGLE_HEALING: + case EFFECT_REVIVAL_BLESSING: + case EFFECT_SHED_TAIL: + case EFFECT_STICKY_WEB: + return TRUE; + // Medium odds + case EFFECT_AFTER_YOU: + case EFFECT_DEFOG: + case EFFECT_ENCORE: + case EFFECT_HAZE: + case EFFECT_HEAL_BELL: + case EFFECT_HEALING_WISH: + case EFFECT_LIFE_DEW: + case EFFECT_MEMENTO: + case EFFECT_PARTING_SHOT: + case EFFECT_PROTECT: + case EFFECT_RESTORE_HP: + case EFFECT_ROAR: + case EFFECT_TAUNT: + case EFFECT_TAILWIND: + case EFFECT_TELEPORT: + case EFFECT_TRICK: + // defoggables / screens and hazards + case EFFECT_LIGHT_SCREEN: + case EFFECT_REFLECT: + case EFFECT_SPIKES: + case EFFECT_STEALTH_ROCK: + case EFFECT_TOXIC_SPIKES: + // field status + case EFFECT_HAIL: + case EFFECT_RAIN_DANCE: + case EFFECT_SANDSTORM: + case EFFECT_SNOWSCAPE: + case EFFECT_SUNNY_DAY: + case EFFECT_TRICK_ROOM: + case EFFECT_ELECTRIC_TERRAIN: + case EFFECT_GRASSY_TERRAIN: + case EFFECT_MISTY_TERRAIN: + case EFFECT_PSYCHIC_TERRAIN: + if (rand < ASSUME_POWERFUL_STATUS_MEDIUM_ODDS) + return TRUE; + break; + // Low odds + case EFFECT_COURT_CHANGE: + case EFFECT_DOODLE: + case EFFECT_ENTRAINMENT: + case EFFECT_FIXED_PERCENT_DAMAGE: + case EFFECT_GASTRO_ACID: + case EFFECT_GUARD_SPLIT: + case EFFECT_IMPRISON: + case EFFECT_PERISH_SONG: + case EFFECT_POWER_SPLIT: + case EFFECT_QUASH: + case EFFECT_ROLE_PLAY: + case EFFECT_SKILL_SWAP: + case EFFECT_SPEED_SWAP: + case EFFECT_WORRY_SEED: + if (rand < ASSUME_POWERFUL_STATUS_LOW_ODDS) + return TRUE; + break; + default: + break; + } + + return FALSE; +} + static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId) { u32 i, j; From 026b1f25f24072ee36f8d4c2e81c37a40bb38729 Mon Sep 17 00:00:00 2001 From: surskitty Date: Fri, 11 Jul 2025 00:31:47 -0400 Subject: [PATCH 2/9] I suspect the test is set up wrong, more than I have the logic wrong. Though that could also be true. --- include/random.h | 5 +++ src/battle_ai_main.c | 3 ++ src/battle_ai_util.c | 30 +++++++------- test/battle/ai/ai_assume_powerful_status.c | 48 ++++++++++++++++++++++ 4 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 test/battle/ai/ai_assume_powerful_status.c diff --git a/include/random.h b/include/random.h index f23ff2f184..df749312fd 100644 --- a/include/random.h +++ b/include/random.h @@ -202,6 +202,11 @@ enum RandomTag RNG_AI_BOOST_INTO_HAZE, RNG_HEALER, RNG_DEXNAV_ENCOUNTER_LEVEL, + RNG_AI_ASSUME_POWERFUL_STATUS_SLEEP, + RNG_AI_ASSUME_POWERFUL_STATUS_NONVOLATILE, + RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS, + RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, + RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a69edaa48f..a14ebd4c1a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -570,6 +570,9 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) if (IsAiBattlerAssumingStab()) RecordMovesBasedOnStab(battler); + + if (IsAiBattlerAssumingPowerfulStatus()) + RecordPowerfulStatusMoves(battler); } #define BYPASSES_ACCURACY_CALC 101 // 101 indicates for ai that the move will always hit diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index b6a208a9c7..f09fe2d132 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -141,6 +141,15 @@ bool32 IsAiBattlerAssumingStab() return FALSE; } +bool32 IsAiBattlerAssumingPowerfulStatus() +{ + if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_ASSUME_POWERFUL_STATUS + || gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_ASSUME_POWERFUL_STATUS) + return TRUE; + + return FALSE; +} + bool32 IsAiBattlerPredictingAbility(u32 battlerId) { if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_WEIGH_ABILITY_PREDICTION @@ -251,18 +260,13 @@ void SaveBattlerData(u32 battlerId) bool32 ShouldRecordStatusMove(u32 move) { - u32 rand = Random() % 100; - - if (rand >= ASSUME_POWERFUL_STATUS_HIGH_ODDS) - return FALSE; - switch (GetMoveEffect(move)) { // variable odds by additional effect case EFFECT_NON_VOLATILE_STATUS: - if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP) + if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP && RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_SLEEP, ASSUME_POWERFUL_STATUS_HIGH_ODDS)) return TRUE; - else if (rand < ASSUME_POWERFUL_STATUS_MEDIUM_ODDS) + else if (RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_NONVOLATILE, ASSUME_POWERFUL_STATUS_MEDIUM_ODDS)) return TRUE; break; // High odds @@ -276,7 +280,7 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_REVIVAL_BLESSING: case EFFECT_SHED_TAIL: case EFFECT_STICKY_WEB: - return TRUE; + return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS, ASSUME_POWERFUL_STATUS_HIGH_ODDS); // Medium odds case EFFECT_AFTER_YOU: case EFFECT_DEFOG: @@ -288,8 +292,10 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_MEMENTO: case EFFECT_PARTING_SHOT: case EFFECT_PROTECT: + case EFFECT_REST: case EFFECT_RESTORE_HP: case EFFECT_ROAR: + case EFFECT_SLEEP_TALK: case EFFECT_TAUNT: case EFFECT_TAILWIND: case EFFECT_TELEPORT: @@ -311,9 +317,7 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_GRASSY_TERRAIN: case EFFECT_MISTY_TERRAIN: case EFFECT_PSYCHIC_TERRAIN: - if (rand < ASSUME_POWERFUL_STATUS_MEDIUM_ODDS) - return TRUE; - break; + return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, ASSUME_POWERFUL_STATUS_MEDIUM_ODDS); // Low odds case EFFECT_COURT_CHANGE: case EFFECT_DOODLE: @@ -329,9 +333,7 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_SKILL_SWAP: case EFFECT_SPEED_SWAP: case EFFECT_WORRY_SEED: - if (rand < ASSUME_POWERFUL_STATUS_LOW_ODDS) - return TRUE; - break; + return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS, ASSUME_POWERFUL_STATUS_LOW_ODDS); default: break; } diff --git a/test/battle/ai/ai_assume_powerful_status.c b/test/battle/ai/ai_assume_powerful_status.c new file mode 100644 index 0000000000..7c1f08f352 --- /dev/null +++ b/test/battle/ai/ai_assume_powerful_status.c @@ -0,0 +1,48 @@ +#include "global.h" +#include "test/battle.h" +#include "battle_ai_util.h" + +AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_POWERFUL_STATUS correctly records assumed status moves") +{ + PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_HIGH_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS); + PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS); + PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_LOW_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS); + + u32 aiFlag = 0; + PARAMETRIZE { aiFlag = AI_FLAG_ASSUME_POWERFUL_STATUS; } + PARAMETRIZE { aiFlag = 0; } + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiFlag); + PLAYER(SPECIES_TYPHLOSION) { Moves(MOVE_TACKLE, MOVE_COURT_CHANGE, MOVE_FAKE_OUT); } + PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_HAIL, MOVE_SHED_TAIL, MOVE_THUNDERBOLT); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target:opponentLeft); MOVE(playerRight, MOVE_THUNDERBOLT, target:opponentRight); } + } THEN { + if (aiFlag == AI_FLAG_ASSUME_POWERFUL_STATUS) + { + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_COURT_CHANGE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_FAKE_OUT); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE); + + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_HAIL); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_SHED_TAIL); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); + } + else if (aiFlag == 0) + { + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_NONE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_NONE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE); + + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_NONE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_NONE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); + } + } +} From 4e0a8167be5bfd9ef2d96a3ca34db27f4edf503b Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 00:37:11 -0400 Subject: [PATCH 3/9] Test works? I think? --- test/battle/ai/ai_assume_powerful_status.c | 36 ++++++---------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/test/battle/ai/ai_assume_powerful_status.c b/test/battle/ai/ai_assume_powerful_status.c index 7c1f08f352..1fd16adcf4 100644 --- a/test/battle/ai/ai_assume_powerful_status.c +++ b/test/battle/ai/ai_assume_powerful_status.c @@ -8,11 +8,8 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_POWERFUL_STATUS correctly records assumed PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS); PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_LOW_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS); - u32 aiFlag = 0; - PARAMETRIZE { aiFlag = AI_FLAG_ASSUME_POWERFUL_STATUS; } - PARAMETRIZE { aiFlag = 0; } GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiFlag); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_POWERFUL_STATUS); PLAYER(SPECIES_TYPHLOSION) { Moves(MOVE_TACKLE, MOVE_COURT_CHANGE, MOVE_FAKE_OUT); } PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_HAIL, MOVE_SHED_TAIL, MOVE_THUNDERBOLT); } OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } @@ -20,29 +17,14 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_POWERFUL_STATUS correctly records assumed } WHEN { TURN { MOVE(playerLeft, MOVE_TACKLE, target:opponentLeft); MOVE(playerRight, MOVE_THUNDERBOLT, target:opponentRight); } } THEN { - if (aiFlag == AI_FLAG_ASSUME_POWERFUL_STATUS) - { - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_COURT_CHANGE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_FAKE_OUT); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_COURT_CHANGE); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_FAKE_OUT); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_HAIL); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_SHED_TAIL); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); - } - else if (aiFlag == 0) - { - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_NONE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_NONE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE); - - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_NONE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_NONE); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT); - EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); - } + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_HAIL); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_SHED_TAIL); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT); + EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); } } From 3defa242826df1e8b35bbf81efab194b0f1cfda6 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 00:41:14 -0400 Subject: [PATCH 4/9] Adding other healing move effects. --- src/battle_ai_util.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index f09fe2d132..9f8d6c94ea 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -290,12 +290,18 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_HEALING_WISH: case EFFECT_LIFE_DEW: case EFFECT_MEMENTO: + case EFFECT_MOONLIGHT: + case EFFECT_MORNING_SUN: case EFFECT_PARTING_SHOT: case EFFECT_PROTECT: case EFFECT_REST: case EFFECT_RESTORE_HP: case EFFECT_ROAR: + case EFFECT_ROOST: + case EFFECT_SHORE_UP: case EFFECT_SLEEP_TALK: + case EFFECT_SOFTBOILED: + case EFFECT_SYNTHESIS: case EFFECT_TAUNT: case EFFECT_TAILWIND: case EFFECT_TELEPORT: From 96274c772d8c446cd399b38bf96a26eb5d79da10 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 00:42:46 -0400 Subject: [PATCH 5/9] Also adding Helping Hand. --- src/battle_ai_util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 9f8d6c94ea..2c7c850d1e 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -288,6 +288,7 @@ bool32 ShouldRecordStatusMove(u32 move) case EFFECT_HAZE: case EFFECT_HEAL_BELL: case EFFECT_HEALING_WISH: + case EFFECT_HELPING_HAND: case EFFECT_LIFE_DEW: case EFFECT_MEMENTO: case EFFECT_MOONLIGHT: From c7b8787f59738d6d887cb4f81495cd0a3fc1d42a Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 00:55:52 -0400 Subject: [PATCH 6/9] Better description in the documentation. --- docs/tutorials/ai_flags.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/ai_flags.md b/docs/tutorials/ai_flags.md index 9f39090b65..6b8a2e941f 100644 --- a/docs/tutorials/ai_flags.md +++ b/docs/tutorials/ai_flags.md @@ -148,8 +148,9 @@ AI has full knowledge of player moves, abilities, and hold items, and can use th A significantly more restricted version of `AI_FLAG_OMNISCIENT`, the AI only knows the player's STAB moves, as their existence would be reasonable to assume in almost any case. ## `AI_FLAG_ASSUME_POWERFUL_STATUS` -A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know if the player has certain iconic status moves, plus also Fake Out and fixed percentage moves like Super Fang. +A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know if the player has certain strong status moves, plus also Fake Out and fixed percentage moves like Super Fang. The intention is so that if the AI has a counterplay implemented, it will seem to have guessed if the player's pokemon has a move, without giving the AI perfect information. For example, with Omniscient set, the AI will not put a pokemon to sleep if it has Sleep Talk; with neither Assume Powerful Status nor Omniscient set, the AI will always assume the pokemon does not have Sleep Talk. +Percentages for the three groupings (high odds, medium odds, and low odds) are defined in `include/config/ai.h` under `ASSUME_POWERFUL_STATUS_HIGH_ODDS`, `ASSUME_POWERFUL_STATUS_MEDIUM_ODDS`, and `ASSUME_POWERFUL_STATUS_LOW_ODDS`. Moves are sorted in `src/battle_ai_util.c` within `ShouldRecordStatusMove()` ## `AI_FLAG_SMART_MON_CHOICES` Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when `AI_FLAG_SMART_SWITCHING` is enabled. From 9ccb1fbc3a528b001a8f79394e5642d8fb3a8089 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 01:01:18 -0400 Subject: [PATCH 7/9] Lowering the default odds to 90%, 70%, and 40%. --- include/config/ai.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/config/ai.h b/include/config/ai.h index be091649af..8a97430d4b 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -77,9 +77,9 @@ #define ASSUME_STAB_SEES_ABILITY FALSE // Flag also gives omniscience for player's ability. Can use AI_FLAG_WEIGH_ABILITY_PREDICTION instead for smarter prediction without omniscience. // AI_FLAG_ASSUME_POWERFUL_STATUS settings -#define ASSUME_POWERFUL_STATUS_HIGH_ODDS 95 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore -#define ASSUME_POWERFUL_STATUS_MEDIUM_ODDS 75 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect -#define ASSUME_POWERFUL_STATUS_LOW_ODDS 50 // Chance for AI to see niche moves a pokemon may have but probably won't, like Trick Room or Speed Swap +#define ASSUME_POWERFUL_STATUS_HIGH_ODDS 90 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore +#define ASSUME_POWERFUL_STATUS_MEDIUM_ODDS 70 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect +#define ASSUME_POWERFUL_STATUS_LOW_ODDS 40 // Chance for AI to see niche moves a pokemon may have but probably won't, like Trick Room or Speed Swap // AI_FLAG_SMART_SWITCHING settings #define SMART_SWITCHING_OMNISCIENT FALSE // AI will use omniscience for switching calcs, regardless of omniscience setting otherwise From f6b0a1584157f2acefc08876b2e400b165feb2f6 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sat, 12 Jul 2025 18:50:08 -0400 Subject: [PATCH 8/9] Renaming to AI_ASSUME_STATUS_MOVES and making a config for universal status move guessing + disabling the custom tuning. --- docs/tutorials/ai_flags.md | 8 +- include/config/ai.h | 11 +- include/constants/battle_ai.h | 2 +- include/random.h | 11 +- src/battle_ai_util.c | 140 +++++++----------- ...rful_status.c => ai_assume_status_moves.c} | 29 +++- 6 files changed, 98 insertions(+), 103 deletions(-) rename test/battle/ai/{ai_assume_powerful_status.c => ai_assume_status_moves.c} (53%) diff --git a/docs/tutorials/ai_flags.md b/docs/tutorials/ai_flags.md index 6b8a2e941f..98657210d9 100644 --- a/docs/tutorials/ai_flags.md +++ b/docs/tutorials/ai_flags.md @@ -147,10 +147,12 @@ AI has full knowledge of player moves, abilities, and hold items, and can use th ## `AI_FLAG_ASSUME_STAB` A significantly more restricted version of `AI_FLAG_OMNISCIENT`, the AI only knows the player's STAB moves, as their existence would be reasonable to assume in almost any case. -## `AI_FLAG_ASSUME_POWERFUL_STATUS` -A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know if the player has certain strong status moves, plus also Fake Out and fixed percentage moves like Super Fang. The intention is so that if the AI has a counterplay implemented, it will seem to have guessed if the player's pokemon has a move, without giving the AI perfect information. For example, with Omniscient set, the AI will not put a pokemon to sleep if it has Sleep Talk; with neither Assume Powerful Status nor Omniscient set, the AI will always assume the pokemon does not have Sleep Talk. +## `AI_FLAG_ASSUME_STATUS_MOVES` +A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know what status moves the player has, plus additionally Fake Out and fixed percentage moves like Super Fang. The intention is so that if the AI has a counterplay implemented, it will seem to have guessed if the player's pokemon has a move, without giving the AI perfect information. For example, with Omniscient set, the AI will not usually put a pokemon to sleep if it has Sleep Talk; with neither Assume Powerful Status nor Omniscient set, the AI will always assume the pokemon does not have Sleep Talk. -Percentages for the three groupings (high odds, medium odds, and low odds) are defined in `include/config/ai.h` under `ASSUME_POWERFUL_STATUS_HIGH_ODDS`, `ASSUME_POWERFUL_STATUS_MEDIUM_ODDS`, and `ASSUME_POWERFUL_STATUS_LOW_ODDS`. Moves are sorted in `src/battle_ai_util.c` within `ShouldRecordStatusMove()` +By default, there are three groups of higher likelihood status moves defined in `include/config/ai.h` under `ASSUME_STATUS_HIGH_ODDS`, `ASSUME_STATUS_MEDIUM_ODDS`, and `ASSUME_STATUS_LOW_ODDS`. Moves are sorted in `src/battle_ai_util.c` within `ShouldRecordStatusMove()`. + +Any move that is not special cased is then potentially caught by `ASSUME_ALL_STATUS_ODDS`. ## `AI_FLAG_SMART_MON_CHOICES` Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when `AI_FLAG_SMART_SWITCHING` is enabled. diff --git a/include/config/ai.h b/include/config/ai.h index 8a97430d4b..44d2f3236d 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -76,10 +76,13 @@ // AI_FLAG_ASSUME_STAB settings #define ASSUME_STAB_SEES_ABILITY FALSE // Flag also gives omniscience for player's ability. Can use AI_FLAG_WEIGH_ABILITY_PREDICTION instead for smarter prediction without omniscience. -// AI_FLAG_ASSUME_POWERFUL_STATUS settings -#define ASSUME_POWERFUL_STATUS_HIGH_ODDS 90 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore -#define ASSUME_POWERFUL_STATUS_MEDIUM_ODDS 70 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect -#define ASSUME_POWERFUL_STATUS_LOW_ODDS 40 // Chance for AI to see niche moves a pokemon may have but probably won't, like Trick Room or Speed Swap +// AI_FLAG_ASSUME_STATUS_MOVES settings +#define ASSUME_STATUS_MOVES_HAS_TUNING TRUE // Flag has varying rates for different kinds of status move. + // Setting to false also means it will not alert on Fake Out or Super Fang. +#define ASSUME_STATUS_HIGH_ODDS 90 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore +#define ASSUME_STATUS_MEDIUM_ODDS 70 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect +#define ASSUME_STATUS_LOW_ODDS 40 // Chance for AI to see niche moves a pokemon may have but probably won't, like Entrainment +#define ASSUME_ALL_STATUS_ODDS 25 // Chance for the AI to see any kind of status move. // AI_FLAG_SMART_SWITCHING settings #define SMART_SWITCHING_OMNISCIENT FALSE // AI will use omniscience for switching calcs, regardless of omniscience setting otherwise diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index fa2a96ee5d..db386be4b0 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -34,7 +34,7 @@ #define AI_FLAG_PREDICT_MOVE (1 << 26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT #define AI_FLAG_SMART_TERA (1 << 27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available). #define AI_FLAG_ASSUME_STAB (1 << 28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT. -#define AI_FLAG_ASSUME_POWERFUL_STATUS (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT. +#define AI_FLAG_ASSUME_STATUS_MOVES (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT. #define AI_FLAG_COUNT 30 diff --git a/include/random.h b/include/random.h index df749312fd..130fb32e69 100644 --- a/include/random.h +++ b/include/random.h @@ -202,11 +202,12 @@ enum RandomTag RNG_AI_BOOST_INTO_HAZE, RNG_HEALER, RNG_DEXNAV_ENCOUNTER_LEVEL, - RNG_AI_ASSUME_POWERFUL_STATUS_SLEEP, - RNG_AI_ASSUME_POWERFUL_STATUS_NONVOLATILE, - RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS, - RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, - RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS, + RNG_AI_ASSUME_STATUS_SLEEP, + RNG_AI_ASSUME_STATUS_NONVOLATILE, + RNG_AI_ASSUME_STATUS_HIGH_ODDS, + RNG_AI_ASSUME_STATUS_MEDIUM_ODDS, + RNG_AI_ASSUME_STATUS_LOW_ODDS, + RNG_AI_ASSUME_ALL_STATUS, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 2c7c850d1e..c14dddacf5 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -143,8 +143,8 @@ bool32 IsAiBattlerAssumingStab() bool32 IsAiBattlerAssumingPowerfulStatus() { - if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_ASSUME_POWERFUL_STATUS - || gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_ASSUME_POWERFUL_STATUS) + if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_ASSUME_STATUS_MOVES + || gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_ASSUME_STATUS_MOVES) return TRUE; return FALSE; @@ -260,92 +260,62 @@ void SaveBattlerData(u32 battlerId) bool32 ShouldRecordStatusMove(u32 move) { - switch (GetMoveEffect(move)) + if (ASSUME_STATUS_MOVES_HAS_TUNING) { - // variable odds by additional effect - case EFFECT_NON_VOLATILE_STATUS: - if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP && RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_SLEEP, ASSUME_POWERFUL_STATUS_HIGH_ODDS)) - return TRUE; - else if (RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_NONVOLATILE, ASSUME_POWERFUL_STATUS_MEDIUM_ODDS)) - return TRUE; - break; - // High odds - case EFFECT_AURORA_VEIL: - case EFFECT_CHILLY_RECEPTION: - case EFFECT_CONVERSION: - case EFFECT_FIRST_TURN_ONLY: - case EFFECT_FOLLOW_ME: - case EFFECT_INSTRUCT: - case EFFECT_JUNGLE_HEALING: - case EFFECT_REVIVAL_BLESSING: - case EFFECT_SHED_TAIL: - case EFFECT_STICKY_WEB: - return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS, ASSUME_POWERFUL_STATUS_HIGH_ODDS); - // Medium odds - case EFFECT_AFTER_YOU: - case EFFECT_DEFOG: - case EFFECT_ENCORE: - case EFFECT_HAZE: - case EFFECT_HEAL_BELL: - case EFFECT_HEALING_WISH: - case EFFECT_HELPING_HAND: - case EFFECT_LIFE_DEW: - case EFFECT_MEMENTO: - case EFFECT_MOONLIGHT: - case EFFECT_MORNING_SUN: - case EFFECT_PARTING_SHOT: - case EFFECT_PROTECT: - case EFFECT_REST: - case EFFECT_RESTORE_HP: - case EFFECT_ROAR: - case EFFECT_ROOST: - case EFFECT_SHORE_UP: - case EFFECT_SLEEP_TALK: - case EFFECT_SOFTBOILED: - case EFFECT_SYNTHESIS: - case EFFECT_TAUNT: - case EFFECT_TAILWIND: - case EFFECT_TELEPORT: - case EFFECT_TRICK: - // defoggables / screens and hazards - case EFFECT_LIGHT_SCREEN: - case EFFECT_REFLECT: - case EFFECT_SPIKES: - case EFFECT_STEALTH_ROCK: - case EFFECT_TOXIC_SPIKES: - // field status - case EFFECT_HAIL: - case EFFECT_RAIN_DANCE: - case EFFECT_SANDSTORM: - case EFFECT_SNOWSCAPE: - case EFFECT_SUNNY_DAY: - case EFFECT_TRICK_ROOM: - case EFFECT_ELECTRIC_TERRAIN: - case EFFECT_GRASSY_TERRAIN: - case EFFECT_MISTY_TERRAIN: - case EFFECT_PSYCHIC_TERRAIN: - return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, ASSUME_POWERFUL_STATUS_MEDIUM_ODDS); - // Low odds - case EFFECT_COURT_CHANGE: - case EFFECT_DOODLE: - case EFFECT_ENTRAINMENT: - case EFFECT_FIXED_PERCENT_DAMAGE: - case EFFECT_GASTRO_ACID: - case EFFECT_GUARD_SPLIT: - case EFFECT_IMPRISON: - case EFFECT_PERISH_SONG: - case EFFECT_POWER_SPLIT: - case EFFECT_QUASH: - case EFFECT_ROLE_PLAY: - case EFFECT_SKILL_SWAP: - case EFFECT_SPEED_SWAP: - case EFFECT_WORRY_SEED: - return RandomPercentage(RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS, ASSUME_POWERFUL_STATUS_LOW_ODDS); - default: - break; + switch (GetMoveEffect(move)) + { + // variable odds by additional effect + case EFFECT_NON_VOLATILE_STATUS: + if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP && RandomPercentage(RNG_AI_ASSUME_STATUS_SLEEP, ASSUME_STATUS_HIGH_ODDS)) + return TRUE; + else if (RandomPercentage(RNG_AI_ASSUME_STATUS_NONVOLATILE, ASSUME_STATUS_MEDIUM_ODDS)) + return TRUE; + break; + // High odds + case EFFECT_AURORA_VEIL: + case EFFECT_CHILLY_RECEPTION: + case EFFECT_FIRST_TURN_ONLY: + case EFFECT_FOLLOW_ME: + case EFFECT_INSTRUCT: + case EFFECT_JUNGLE_HEALING: + case EFFECT_SHED_TAIL: + return RandomPercentage(RNG_AI_ASSUME_STATUS_HIGH_ODDS, ASSUME_STATUS_HIGH_ODDS); + // Medium odds + case EFFECT_AFTER_YOU: + case EFFECT_DOODLE: + case EFFECT_ENCORE: + case EFFECT_HAZE: + case EFFECT_PARTING_SHOT: + case EFFECT_PROTECT: + case EFFECT_REST: + case EFFECT_ROAR: + case EFFECT_ROOST: + case EFFECT_SLEEP_TALK: + case EFFECT_TAUNT: + case EFFECT_TAILWIND: + case EFFECT_TRICK: + case EFFECT_TRICK_ROOM: + // defoggables / screens and hazards + case EFFECT_LIGHT_SCREEN: + case EFFECT_REFLECT: + case EFFECT_SPIKES: + case EFFECT_STEALTH_ROCK: + case EFFECT_STICKY_WEB: + case EFFECT_TOXIC_SPIKES: + return RandomPercentage(RNG_AI_ASSUME_STATUS_MEDIUM_ODDS, ASSUME_STATUS_MEDIUM_ODDS); + // Low odds + case EFFECT_ENTRAINMENT: + case EFFECT_FIXED_PERCENT_DAMAGE: + case EFFECT_GASTRO_ACID: + case EFFECT_IMPRISON: + case EFFECT_TELEPORT: + return RandomPercentage(RNG_AI_ASSUME_STATUS_LOW_ODDS, ASSUME_STATUS_LOW_ODDS); + default: + break; + } } - return FALSE; + return RandomPercentage(RNG_AI_ASSUME_ALL_STATUS, ASSUME_ALL_STATUS_ODDS) && IsBattleMoveStatus(move); } static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId) diff --git a/test/battle/ai/ai_assume_powerful_status.c b/test/battle/ai/ai_assume_status_moves.c similarity index 53% rename from test/battle/ai/ai_assume_powerful_status.c rename to test/battle/ai/ai_assume_status_moves.c index 1fd16adcf4..8fbaf01101 100644 --- a/test/battle/ai/ai_assume_powerful_status.c +++ b/test/battle/ai/ai_assume_status_moves.c @@ -2,14 +2,15 @@ #include "test/battle.h" #include "battle_ai_util.h" -AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_POWERFUL_STATUS correctly records assumed status moves") +AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_STATUS_MOVES correctly records assumed status moves") { - PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_HIGH_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_HIGH_ODDS); - PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_MEDIUM_ODDS); - PASSES_RANDOMLY(ASSUME_POWERFUL_STATUS_LOW_ODDS, 100, RNG_AI_ASSUME_POWERFUL_STATUS_LOW_ODDS); + PASSES_RANDOMLY(ASSUME_STATUS_HIGH_ODDS, 100, RNG_AI_ASSUME_STATUS_HIGH_ODDS); + PASSES_RANDOMLY(ASSUME_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_STATUS_MEDIUM_ODDS); + PASSES_RANDOMLY(ASSUME_STATUS_LOW_ODDS, 100, RNG_AI_ASSUME_STATUS_LOW_ODDS); + PASSES_RANDOMLY(ASSUME_ALL_STATUS_ODDS, 100, RNG_AI_ASSUME_ALL_STATUS); GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_POWERFUL_STATUS); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_STATUS_MOVES); PLAYER(SPECIES_TYPHLOSION) { Moves(MOVE_TACKLE, MOVE_COURT_CHANGE, MOVE_FAKE_OUT); } PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_HAIL, MOVE_SHED_TAIL, MOVE_THUNDERBOLT); } OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); } @@ -28,3 +29,21 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_POWERFUL_STATUS correctly records assumed EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE); } } + +AI_SINGLE_BATTLE_TEST("AI_FLAG_ASSUME_STATUS_MOVES changes behavior") +{ + if (ASSUME_STATUS_MOVES_HAS_TUNING) + PASSES_RANDOMLY(ASSUME_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_STATUS_MEDIUM_ODDS); + else + PASSES_RANDOMLY(ASSUME_ALL_STATUS_ODDS, 100, RNG_AI_ASSUME_ALL_STATUS); + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_STATUS_MOVES); + PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_REST, MOVE_HEADBUTT); } + OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_WORRY_SEED, MOVE_HEADBUTT); } + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT); EXPECT_MOVE(opponent, MOVE_WORRY_SEED); } + } +} + + From f0da7c1707f5973bf15dc44760f451a7c843e4f2 Mon Sep 17 00:00:00 2001 From: surskitty Date: Sun, 13 Jul 2025 22:00:00 -0400 Subject: [PATCH 9/9] Renaming Powerful status functions. --- include/battle_ai_util.h | 2 +- src/battle_ai_main.c | 6 +++--- src/battle_ai_util.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 1e03090dde..059809d6a5 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -61,7 +61,7 @@ bool32 IsAiVsAiBattle(void); bool32 BattlerHasAi(u32 battlerId); bool32 IsAiBattlerAware(u32 battlerId); bool32 IsAiBattlerAssumingStab(void); -bool32 IsAiBattlerAssumingPowerfulStatus(void); +bool32 IsAiBattlerAssumingStatusMoves(void); bool32 ShouldRecordStatusMove(u32 move); void ClearBattlerMoveHistory(u32 battlerId); void RecordLastUsedMoveBy(u32 battlerId, u32 move); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a14ebd4c1a..a659c6befa 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -544,7 +544,7 @@ void RecordMovesBasedOnStab(u32 battler) } } -void RecordPowerfulStatusMoves(u32 battler) +void RecordStatusMoves(u32 battler) { u32 i; for (i = 0; i < MAX_MON_MOVES; i++) @@ -571,8 +571,8 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) if (IsAiBattlerAssumingStab()) RecordMovesBasedOnStab(battler); - if (IsAiBattlerAssumingPowerfulStatus()) - RecordPowerfulStatusMoves(battler); + if (IsAiBattlerAssumingStatusMoves()) + RecordStatusMoves(battler); } #define BYPASSES_ACCURACY_CALC 101 // 101 indicates for ai that the move will always hit diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index c14dddacf5..77aabccb11 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -141,7 +141,7 @@ bool32 IsAiBattlerAssumingStab() return FALSE; } -bool32 IsAiBattlerAssumingPowerfulStatus() +bool32 IsAiBattlerAssumingStatusMoves() { if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_ASSUME_STATUS_MOVES || gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_ASSUME_STATUS_MOVES)