From 83b7732faed3edc3bc2cc6ee41cfc22ad3446a3f Mon Sep 17 00:00:00 2001 From: Pawkkie Date: Thu, 8 May 2025 14:19:43 -0400 Subject: [PATCH] Store predictingMove, config for chance --- include/battle.h | 5 +++-- include/config/ai.h | 1 + include/random.h | 1 + src/battle_ai_main.c | 3 +++ src/battle_ai_switch_items.c | 18 ++++++++---------- test/battle/ai/ai_flag_predict_move.c | 1 + 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/battle.h b/include/battle.h index e463b94714..4fa543053b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -331,9 +331,10 @@ struct AiLogicData u8 weatherHasEffect:1; // The same as HasWeatherEffect(). Stored here, so it's called only once. u8 ejectButtonSwitch:1; // Tracks whether current switch out was from Eject Button u8 ejectPackSwitch:1; // Tracks whether current switch out was from Eject Pack - u8 predictingSwitch:1; // Determines whether AI will use predictions this turn or not + u8 predictingSwitch:1; // Determines whether AI will use switch predictions this turn or not + u8 predictingMove:1; // Determines whether AI will use move predictions this turn or not u8 aiSwitchPredictionInProgress:1; // Tracks whether the AI is in the middle of running prediction calculations - u8 padding:3; + u8 padding:2; u8 shouldSwitch; // Stores result of ShouldSwitch, which decides whether a mon should be switched out u8 aiCalcInProgress:1; u8 battlerDoingPrediction; // Stores which battler is currently running its prediction calcs diff --git a/include/config/ai.h b/include/config/ai.h index 31fca355dd..c734eb2c1d 100644 --- a/include/config/ai.h +++ b/include/config/ai.h @@ -62,6 +62,7 @@ // AI prediction chances #define PREDICT_SWITCH_CHANCE 50 +#define PREDICT_MOVE_CHANCE 100 // AI PP Stall detection chance per roll #define PP_STALL_DISREGARD_MOVE_PERCENTAGE 50 diff --git a/include/random.h b/include/random.h index d47260c7f0..b9a0ab4311 100644 --- a/include/random.h +++ b/include/random.h @@ -196,6 +196,7 @@ enum RandomTag RNG_RANDOM_TARGET, RNG_AI_PREDICT_ABILITY, RNG_AI_PREDICT_SWITCH, + RNG_AI_PREDICT_MOVE, RNG_AI_STATUS_FOCUS_PUNCH, RNG_HEALER, RNG_DEXNAV_ENCOUNTER_LEVEL, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index f31dfae6bd..2af6e3067d 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -330,6 +330,9 @@ void SetupAIPredictionData(u32 battler, enum SwitchType switchType) AI_DATA->predictedMove[opposingBattler] = gBattleMons[opposingBattler].moves[BattleAI_PredictMove(battler, opposingBattler)]; DebugPrintf("Predicted move: %d", AI_DATA->predictedMove[opposingBattler]); ModifySwitchAfterMoveScoring(opposingBattler); + + // Determine whether AI will use predictions this turn + AI_DATA->predictingMove = RandomPercentage(RNG_AI_PREDICT_MOVE, PREDICT_MOVE_CHANCE); } } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 37532d3055..d1a489580c 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -456,10 +456,8 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) struct Pokemon *party; u16 monAbility, aiMove; u32 opposingBattler = GetOppositeBattler(battler); - u32 incomingMove = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES) ? AI_DATA->predictedMove[opposingBattler] : AI_DATA->lastUsedMove[opposingBattler]; + u32 incomingMove = ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES) && AI_DATA->predictingMove) ? AI_DATA->predictedMove[opposingBattler] : AI_DATA->lastUsedMove[opposingBattler]; u32 incomingType = GetMoveType(incomingMove); - u32 predictedMove = incomingMove; // Update for move prediction - u32 predictedType = GetMoveType(predictedMove); bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove)); s32 i, j; @@ -502,38 +500,38 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) } // Create an array of possible absorb abilities so the AI considers all of them - if (predictedType == TYPE_FIRE) + if (incomingType == TYPE_FIRE) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE; } - else if (predictedType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER)) + else if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN; if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN; } - else if (predictedType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC)) + else if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE; if (B_REDIRECT_ABILITY_IMMUNITY >= GEN_5) absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD; } - else if (predictedType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS)) + else if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER; } - else if (predictedType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND)) + else if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LEVITATE; } - else if (IsSoundMove(predictedMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove))) + else if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove))) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SOUNDPROOF; } - else if (IsWindMove(predictedMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove))) + else if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove))) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WIND_RIDER; } diff --git a/test/battle/ai/ai_flag_predict_move.c b/test/battle/ai/ai_flag_predict_move.c index 4e0486b6a1..80c5d81c16 100644 --- a/test/battle/ai/ai_flag_predict_move.c +++ b/test/battle/ai/ai_flag_predict_move.c @@ -4,6 +4,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_MOVE: AI will predict player's move") { + PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE); 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 | AI_FLAG_PREDICT_MOVES); PLAYER(SPECIES_VAPOREON) { Ability(ABILITY_WATER_ABSORB); Moves(MOVE_SURF, MOVE_TACKLE); }