Merge remote-tracking branch 'upstream/upcoming' into move-prediction

This commit is contained in:
Pawkkie 2025-04-28 13:23:33 -04:00
commit 787a8a0d9d
6 changed files with 28 additions and 4 deletions

View File

@ -50,6 +50,9 @@
// AI held item-based move scoring
#define LOW_ACCURACY_THRESHOLD 75 // Moves with accuracy equal OR below this value are considered low accuracy
// AI move scoring
#define STATUS_MOVE_FOCUS_PUNCH_CHANCE 50 // Chance the AI will use a status move if the player's best move is Focus Punch
// AI damage calc considerations
#define RISKY_AI_CRIT_STAGE_THRESHOLD 2 // Stat stages at which Risky will assume it gets a crit
#define RISKY_AI_CRIT_THRESHOLD_GEN_1 128 // "Stat stage" at which Risky will assume it gets a crit with gen 1 mechanics (this translates to an X / 255 % crit threshold)

View File

@ -191,6 +191,7 @@ enum RandomTag
RNG_RANDOM_TARGET,
RNG_AI_PREDICT_ABILITY,
RNG_AI_PREDICT_SWITCH,
RNG_AI_STATUS_FOCUS_PUNCH,
RNG_HEALER,
RNG_DEXNAV_ENCOUNTER_LEVEL,
};

View File

@ -975,6 +975,13 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (gBattleStruct->battlerState[battlerDef].commandingDondozo)
RETURN_SCORE_MINUS(20);
// Don't setup into expected Focus Punch. Revisit alongside predictedMove with move prediction
if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS && moveEffect != EFFECT_SLEEP
&& GetMoveEffect(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING)) == EFFECT_FOCUS_PUNCH && RandomPercentage(RNG_AI_STATUS_FOCUS_PUNCH, STATUS_MOVE_FOCUS_PUNCH_CHANCE))
{
RETURN_SCORE_MINUS(20);
}
// Don't use anything but super effective thawing moves if target is frozen if any other attack available
if (((GetMoveType(move) == TYPE_FIRE && GetMovePower(move) != 0) || CanBurnHitThaw(move)) && effectiveness < UQ_4_12(2.0) && (gBattleMons[battlerDef].status1 & (STATUS1_FROSTBITE | STATUS1_FREEZE)))
{

View File

@ -181,11 +181,10 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
//Variable initialization
u8 opposingPosition, atkType1, atkType2, defType1, defType2;
s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0;
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = AI_DATA->abilities[battler], opposingBattler, weather = AI_GetWeather();
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = AI_DATA->abilities[battler], opposingBattler;
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
u16 typeEffectiveness = UQ_4_12(1.0); //baseline typing damage
enum BattleMoveEffects aiMoveEffect;
uq4_12_t effectiveness;
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_SMART_SWITCHING))
@ -256,7 +255,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
playerMove = gBattleMons[opposingBattler].moves[i];
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH)
{
damageTaken = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather).median;
damageTaken = AI_GetDamage(opposingBattler, battler, i, AI_DEFENDING, AI_DATA);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
{
return maxDamageTaken = damageTaken;

View File

@ -556,7 +556,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would
ASSUME(GetMoveType(MOVE_THUNDERBOLT) == TYPE_ELECTRIC);
ASSUME(GetMoveType(MOVE_EARTHQUAKE) == TYPE_GROUND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_ELECTRODE) { Moves(MOVE_THUNDERBOLT, MOVE_THUNDER_WAVE, MOVE_THUNDER_SHOCK); }
OPPONENT(SPECIES_PELIPPER) { Moves(MOVE_EARTHQUAKE); };
OPPONENT(SPECIES_RHYDON) { Moves(MOVE_EARTHQUAKE); Ability(ABILITY_ROCK_HEAD); }

View File

@ -99,3 +99,17 @@ AI_SINGLE_BATTLE_TEST("AI will Incapacitate -> Substitute -> Focus Punch if able
TURN { MOVE(player, MOVE_DISCHARGE); EXPECT_MOVE(opponent, MOVE_FOCUS_PUNCH); }
}
}
AI_SINGLE_BATTLE_TEST("AI won't use status moves if the player's best attacking move is Focus Punch")
{
PASSES_RANDOMLY(STATUS_MOVE_FOCUS_PUNCH_CHANCE, 100, RNG_AI_STATUS_FOCUS_PUNCH);
GIVEN {
ASSUME(GetMoveEffect(MOVE_FOCUS_PUNCH) == EFFECT_FOCUS_PUNCH);
ASSUME(GetMoveCategory(MOVE_SWORDS_DANCE) == DAMAGE_CATEGORY_STATUS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_SNORLAX) { Moves(MOVE_FOCUS_PUNCH, MOVE_TACKLE); }
OPPONENT(SPECIES_CLEFABLE) { Moves(MOVE_PLAY_ROUGH, MOVE_SWORDS_DANCE); }
} WHEN {
TURN { MOVE(player, MOVE_FOCUS_PUNCH); EXPECT_MOVE(opponent, MOVE_PLAY_ROUGH); }
}
}