Merge pull request #1876 from ghoulslash/be/ai_updates

Some more CanAIFaintTarget checks and other AI updates
This commit is contained in:
Eduardo Quezada D'Ottone 2021-11-19 17:33:21 -03:00 committed by GitHub
commit 1696e60b9f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 19 deletions

View file

@ -52,10 +52,13 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent);
bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect);
bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 moveIndex);
bool32 IsRecycleEncouragedItem(u16 item);
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item);
bool32 IsStatBoostingBerry(u16 item);
bool32 CanKnockOffItem(u8 battler, u16 item);
bool32 IsAbilityOfRating(u16 ability, s8 rating);
s8 GetAbilityRating(u16 ability);
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move);
// stat stage checks
bool32 AnyStatIsRaised(u8 battlerId);

View file

@ -1392,22 +1392,22 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
}
break;
case EFFECT_SANDSTORM:
if (gBattleWeather & WEATHER_SANDSTORM_ANY //TODO | WEATHER_PRIMAL_ANY)
if (gBattleWeather & (WEATHER_SANDSTORM_ANY | WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
score -= 8;
break;
case EFFECT_SUNNY_DAY:
if (gBattleWeather & WEATHER_SUN_ANY //TODO | WEATHER_PRIMAL_ANY)
if (gBattleWeather & (WEATHER_SUN_ANY | WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
score -= 8;
break;
case EFFECT_RAIN_DANCE:
if (gBattleWeather & WEATHER_RAIN_ANY //TODO | WEATHER_PRIMAL_ANY)
if (gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
score -= 8;
break;
case EFFECT_HAIL:
if (gBattleWeather & WEATHER_HAIL_ANY //TODO | WEATHER_PRIMAL_ANY)
if (gBattleWeather & (WEATHER_HAIL_ANY | WEATHER_PRIMAL_ANY)
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
score -= 8;
break;
@ -2969,6 +2969,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
{
case ABILITY_MOXIE:
case ABILITY_BEAST_BOOST:
case ABILITY_CHILLING_NEIGH:
case ABILITY_GRIM_NEIGH:
case ABILITY_AS_ONE_ICE_RIDER:
case ABILITY_AS_ONE_SHADOW_RIDER:
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
{
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
@ -2980,7 +2984,6 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
// move effect checks
switch (moveEffect)
{
case EFFECT_HIT:
break;
case EFFECT_SLEEP:
@ -3243,6 +3246,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_MULTI_HIT:
case EFFECT_DOUBLE_HIT:
case EFFECT_TRIPLE_KICK:
if (AI_MoveMakesContact(AI_DATA->atkAbility, AI_DATA->atkHoldEffect, move)
&& AI_DATA->atkAbility != ABILITY_MAGIC_GUARD
&& AI_DATA->defHoldEffect == HOLD_EFFECT_ROCKY_HELMET)
score -= 2;
break;
case EFFECT_CONVERSION:
if (!IS_BATTLER_OF_TYPE(battlerAtk, gBattleMoves[gBattleMons[battlerAtk].moves[0]].type))
@ -4101,6 +4108,18 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
score++;
if (IsRecycleEncouragedItem(GetUsedHeldItem(battlerAtk)))
score++;
if (AI_DATA->atkAbility == ABILITY_RIPEN)
{
u16 item = GetUsedHeldItem(battlerAtk);
u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item);
if (IsStatBoostingBerry(item) && atkHpPercent > 60)
score++;
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
&& ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
}
break;
case EFFECT_BRICK_BREAK:
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_REFLECT)

View file

@ -1141,15 +1141,13 @@ s32 AI_GetAbility(u32 battlerId)
// Else, guess the ability
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
{
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
u16 abilityGuess = ABILITY_NONE;
while (abilityGuess == ABILITY_NONE)
{
// AI has no knowledge of opponent, so it guesses which ability.
return gBaseStats[gBattleMons[battlerId].species].abilities[Random() & 1];
}
else
{
return gBaseStats[gBattleMons[battlerId].species].abilities[0]; // It's definitely ability 1.
abilityGuess = gBaseStats[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS];
}
return abilityGuess;
}
return ABILITY_NONE; // Unknown.
@ -1709,7 +1707,7 @@ u32 CountNegativeStatStages(u8 battlerId)
bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
@ -1725,7 +1723,7 @@ bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
@ -1741,7 +1739,7 @@ bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (IsAiFaster(AI_CHECK_SLOWER)
@ -1755,7 +1753,7 @@ bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
@ -1770,7 +1768,7 @@ bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
@ -1785,7 +1783,7 @@ bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (defAbility != ABILITY_CONTRARY
@ -1799,7 +1797,7 @@ bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility)
{
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (IsAiFaster(AI_CHECK_FASTER) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
@ -3384,6 +3382,47 @@ static const u16 sRecycleEncouragedItems[] =
// TODO expand this
};
// Its assumed that the berry is strategically given, so no need to check benefits of the berry
bool32 IsStatBoostingBerry(u16 item)
{
switch (item)
{
case ITEM_LIECHI_BERRY:
case ITEM_GANLON_BERRY:
case ITEM_SALAC_BERRY:
case ITEM_PETAYA_BERRY:
case ITEM_APICOT_BERRY:
//case ITEM_LANSAT_BERRY:
case ITEM_STARF_BERRY:
#ifdef ITEM_EXPANSION
case ITEM_MICLE_BERRY:
#endif
return TRUE;
default:
return FALSE;
}
}
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item)
{
switch (item)
{
case ITEM_ORAN_BERRY:
if (gBattleMons[battlerAtk].maxHP <= 50)
return TRUE; // Only worth it in the early game
return FALSE;
case ITEM_SITRUS_BERRY:
case ITEM_FIGY_BERRY:
case ITEM_WIKI_BERRY:
case ITEM_MAGO_BERRY:
case ITEM_AGUAV_BERRY:
case ITEM_IAPAPA_BERRY:
return TRUE;
default:
return FALSE;
}
}
bool32 IsRecycleEncouragedItem(u16 item)
{
u32 i;
@ -3405,6 +3444,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
if (GetHealthPercentage(battlerAtk) < 80 && AI_RandLessThan(128))
return;
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
switch (statId)
{
@ -3477,6 +3519,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove) && GetHealthPercentage(battlerDef) > 20)
{
if (!HasDamagingMove(battlerDef))
@ -3497,6 +3542,9 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove))
{
(*score)++; // burning is good
@ -3513,6 +3561,9 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove))
{
u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk);
@ -3531,6 +3582,9 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->defAbility, move, AI_DATA->partnerMove))
*score += 2;
else
@ -3546,6 +3600,9 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->defAbility, AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)
&& AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_CONFUSION
&& AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_STATUS)
@ -3558,3 +3615,12 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
*score += 2;
}
}
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move)
{
if (TestMoveFlags(move, FLAG_MAKES_CONTACT)
&& ability != ABILITY_LONG_REACH
&& holdEffect != HOLD_EFFECT_PROTECTIVE_PADS)
return TRUE;
return FALSE;
}