Merge pull request #1876 from ghoulslash/be/ai_updates
Some more CanAIFaintTarget checks and other AI updates
This commit is contained in:
commit
1696e60b9f
3 changed files with 107 additions and 19 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue