Clean up AI code duplication and unify checks (#6348)
This commit is contained in:
parent
4c18e88282
commit
c707df358e
@ -99,7 +99,7 @@ u32 AI_GetBattlerAbility(u32 battler);
|
||||
|
||||
// stat stage checks
|
||||
bool32 AnyStatIsRaised(u32 battlerId);
|
||||
bool32 ShouldLowerStat(u32 battlerAtk, u32 battlerDef, u32 battlerAbility, u32 stat);
|
||||
bool32 ShouldLowerStat(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 stat);
|
||||
bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat);
|
||||
bool32 AreBattlersStatsMaxed(u32 battler);
|
||||
u32 CountPositiveStatStages(u32 battlerId);
|
||||
|
||||
@ -835,6 +835,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
u32 i;
|
||||
u32 weather;
|
||||
u32 predictedMove = aiData->lastUsedMove[battlerDef];
|
||||
u32 abilityDef = aiData->abilities[battlerDef];
|
||||
|
||||
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
|
||||
return score;
|
||||
@ -895,145 +896,115 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
}
|
||||
|
||||
if (DoesBattlerIgnoreAbilityChecks(battlerAtk, aiData->abilities[battlerAtk], move))
|
||||
abilityDef = ABILITY_NONE;
|
||||
|
||||
// check non-user target
|
||||
if (!(moveTarget & MOVE_TARGET_USER))
|
||||
{
|
||||
// target ability checks
|
||||
if (!DoesBattlerIgnoreAbilityChecks(battlerAtk, aiData->abilities[battlerAtk], move))
|
||||
if (CanAbilityBlockMove(battlerAtk, battlerDef, move, abilityDef, ABILITY_CHECK_TRIGGER))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
|
||||
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, abilityDef, move, moveType, ABILITY_CHECK_TRIGGER))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
|
||||
switch (abilityDef)
|
||||
{
|
||||
if (CanAbilityBlockMove(battlerAtk, battlerDef, move, aiData->abilities[battlerDef], ABILITY_CHECK_TRIGGER))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
|
||||
if (CanAbilityAbsorbMove(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, moveType, ABILITY_CHECK_TRIGGER))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
|
||||
switch (aiData->abilities[battlerDef])
|
||||
case ABILITY_MAGIC_GUARD:
|
||||
switch (moveEffect)
|
||||
{
|
||||
case ABILITY_MAGIC_GUARD:
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_POISON:
|
||||
case EFFECT_WILL_O_WISP:
|
||||
case EFFECT_TOXIC:
|
||||
case EFFECT_LEECH_SEED:
|
||||
ADJUST_SCORE(-5);
|
||||
break;
|
||||
case EFFECT_CURSE:
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) // Don't use Curse if you're a ghost type vs a Magic Guard user, they'll take no damage.
|
||||
ADJUST_SCORE(-5);
|
||||
break;
|
||||
}
|
||||
case EFFECT_POISON:
|
||||
case EFFECT_WILL_O_WISP:
|
||||
case EFFECT_TOXIC:
|
||||
case EFFECT_LEECH_SEED:
|
||||
ADJUST_SCORE(-5);
|
||||
break;
|
||||
case ABILITY_WONDER_GUARD:
|
||||
if (effectiveness < UQ_4_12(2.0))
|
||||
case EFFECT_CURSE:
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST)) // Don't use Curse if you're a ghost type vs a Magic Guard user, they'll take no damage.
|
||||
ADJUST_SCORE(-5);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ABILITY_WONDER_GUARD:
|
||||
if (effectiveness < UQ_4_12(2.0))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_JUSTIFIED:
|
||||
if (moveType == TYPE_DARK && !IsBattleMoveStatus(move))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_RATTLED:
|
||||
if (!IsBattleMoveStatus(move)
|
||||
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_AROMA_VEIL:
|
||||
if (IsAromaVeilProtectedEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_SWEET_VEIL:
|
||||
if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_FLOWER_VEIL:
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) && (IsNonVolatileStatusMoveEffect(moveEffect)))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_MAGIC_BOUNCE:
|
||||
if (MoveCanBeBouncedBack(move))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_CONTRARY:
|
||||
if (IsStatLoweringEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_COMATOSE:
|
||||
if (IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_SHIELDS_DOWN:
|
||||
if (IsShieldsDownProtected(battlerAtk, aiData->abilities[battlerAtk]) && IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_LEAF_GUARD:
|
||||
if ((AI_GetWeather() & B_WEATHER_SUN)
|
||||
&& aiData->holdEffects[battlerDef] != HOLD_EFFECT_UTILITY_UMBRELLA
|
||||
&& IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
} // def ability checks
|
||||
|
||||
// target partner ability checks & not attacking partner
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
switch (aiData->abilities[BATTLE_PARTNER(battlerDef)])
|
||||
{
|
||||
case ABILITY_LIGHTNING_ROD:
|
||||
if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(battlerAtk, move, aiData->abilities[battlerAtk]))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_JUSTIFIED:
|
||||
if (moveType == TYPE_DARK && !IsBattleMoveStatus(move))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
case ABILITY_STORM_DRAIN:
|
||||
if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(battlerAtk, move, aiData->abilities[battlerAtk]))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_RATTLED:
|
||||
if (!IsBattleMoveStatus(move)
|
||||
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG))
|
||||
case ABILITY_MAGIC_BOUNCE:
|
||||
if (MoveCanBeBouncedBack(move) && moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_SWEET_VEIL:
|
||||
if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_FLOWER_VEIL:
|
||||
if ((IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS)) && (IsNonVolatileStatusMoveEffect(moveEffect) || IsStatLoweringEffect(moveEffect)))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_AROMA_VEIL:
|
||||
if (IsAromaVeilProtectedEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_SWEET_VEIL:
|
||||
if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_FLOWER_VEIL:
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) && (IsNonVolatileStatusMoveEffect(moveEffect) || IsStatLoweringEffect(moveEffect)))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_MAGIC_BOUNCE:
|
||||
if (MoveCanBeBouncedBack(move))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_CONTRARY:
|
||||
if (IsStatLoweringEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_CLEAR_BODY:
|
||||
case ABILITY_FULL_METAL_BODY:
|
||||
case ABILITY_WHITE_SMOKE:
|
||||
if (IsStatLoweringEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_HYPER_CUTTER:
|
||||
if ((moveEffect == EFFECT_ATTACK_DOWN || moveEffect == EFFECT_ATTACK_DOWN_2)
|
||||
&& move != MOVE_PLAY_NICE && move != MOVE_NOBLE_ROAR && move != MOVE_TEARFUL_LOOK && move != MOVE_VENOM_DRENCH)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_ILLUMINATE:
|
||||
if (B_ILLUMINATE_EFFECT < GEN_9)
|
||||
break;
|
||||
// fallthrough
|
||||
case ABILITY_KEEN_EYE:
|
||||
case ABILITY_MINDS_EYE:
|
||||
if (moveEffect == EFFECT_ACCURACY_DOWN || moveEffect == EFFECT_ACCURACY_DOWN_2)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_BIG_PECKS:
|
||||
if (moveEffect == EFFECT_DEFENSE_DOWN || moveEffect == EFFECT_DEFENSE_DOWN_2)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_DEFIANT:
|
||||
case ABILITY_COMPETITIVE:
|
||||
if (IsStatLoweringEffect(moveEffect) && !IS_TARGETING_PARTNER(battlerAtk, battlerDef))
|
||||
RETURN_SCORE_MINUS(8);
|
||||
break;
|
||||
case ABILITY_COMATOSE:
|
||||
if (IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_SHIELDS_DOWN:
|
||||
if (IsShieldsDownProtected(battlerAtk, aiData->abilities[battlerAtk]) && IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_LEAF_GUARD:
|
||||
if ((AI_GetWeather() & B_WEATHER_SUN)
|
||||
&& aiData->holdEffects[battlerDef] != HOLD_EFFECT_UTILITY_UMBRELLA
|
||||
&& IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
} // def ability checks
|
||||
|
||||
// target partner ability checks & not attacking partner
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
switch (aiData->abilities[BATTLE_PARTNER(battlerDef)])
|
||||
{
|
||||
case ABILITY_LIGHTNING_ROD:
|
||||
if (moveType == TYPE_ELECTRIC && !IsMoveRedirectionPrevented(battlerAtk, move, aiData->abilities[battlerAtk]))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_STORM_DRAIN:
|
||||
if (moveType == TYPE_WATER && !IsMoveRedirectionPrevented(battlerAtk, move, aiData->abilities[battlerAtk]))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_MAGIC_BOUNCE:
|
||||
if (MoveCanBeBouncedBack(move) && moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_SWEET_VEIL:
|
||||
if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_FLOWER_VEIL:
|
||||
if ((IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS)) && (IsNonVolatileStatusMoveEffect(moveEffect) || IsStatLoweringEffect(moveEffect)))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_AROMA_VEIL:
|
||||
if (IsAromaVeilProtectedEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
}
|
||||
} // def partner ability checks
|
||||
} // ignore def ability check
|
||||
}
|
||||
} // def partner ability checks
|
||||
|
||||
// gen7+ dark type mons immune to priority->elevated moves from prankster
|
||||
if (B_PRANKSTER_DARK_TYPES >= GEN_7 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK)
|
||||
@ -1108,7 +1079,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
default:
|
||||
break; // check move damage
|
||||
case EFFECT_SLEEP:
|
||||
if (!AI_CanPutToSleep(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
|
||||
if (!AI_CanPutToSleep(battlerAtk, battlerDef, abilityDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
if (PartnerMoveActivatesSleepClause(aiData->partnerMove))
|
||||
ADJUST_SCORE(-20);
|
||||
@ -1360,47 +1331,40 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
// stat lowering effects
|
||||
case EFFECT_ATTACK_DOWN:
|
||||
case EFFECT_ATTACK_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_ATK)) //|| !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->abilities[battlerDef] == ABILITY_HYPER_CUTTER)
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_ATK)) //|| !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_DEFENSE_DOWN:
|
||||
case EFFECT_DEFENSE_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_DEF))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_DEF))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_SPEED_DOWN:
|
||||
case EFFECT_SPEED_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPEED))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->abilities[battlerDef] == ABILITY_SPEED_BOOST)
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPEED))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_SPECIAL_ATTACK_DOWN:
|
||||
case EFFECT_SPECIAL_ATTACK_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPATK)) //|| !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPATK)) //|| !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_SPECIAL_DEFENSE_DOWN:
|
||||
case EFFECT_SPECIAL_DEFENSE_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPDEF))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPDEF))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_ACCURACY_DOWN:
|
||||
case EFFECT_ACCURACY_DOWN_2:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_ACC))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_ACC))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->abilities[battlerDef] == ABILITY_KEEN_EYE || aiData->abilities[battlerDef] == ABILITY_MINDS_EYE
|
||||
|| (B_ILLUMINATE_EFFECT >= GEN_9 && aiData->abilities[battlerDef] == ABILITY_ILLUMINATE))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_EVASION_DOWN:
|
||||
case EFFECT_EVASION_DOWN_2:
|
||||
case EFFECT_TICKLE:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_ATK))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_ATK))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_DEF))
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_DEF))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_VENOM_DRENCH:
|
||||
@ -1410,18 +1374,18 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPEED))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPEED))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPATK))
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPATK))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_ATK))
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_ATK))
|
||||
ADJUST_SCORE(-6);
|
||||
}
|
||||
break;
|
||||
case EFFECT_NOBLE_ROAR:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPATK))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPATK))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_ATK))
|
||||
else if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_ATK))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_CAPTIVATE:
|
||||
@ -1455,7 +1419,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < UQ_4_12(2.0))
|
||||
ADJUST_SCORE(-10);
|
||||
if (HasDamagingMove(battlerDef) && !((gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE)
|
||||
|| IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
|
||||
|| IsBattlerIncapacitated(battlerDef, abilityDef)
|
||||
|| gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION)))
|
||||
ADJUST_SCORE(-10);
|
||||
if (HasMoveEffect(battlerAtk, EFFECT_SUBSTITUTE) && !(gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE))
|
||||
@ -1482,12 +1446,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_TOXIC_THREAD:
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, aiData->abilities[battlerDef], STAT_SPEED))
|
||||
if (!ShouldLowerStat(battlerAtk, battlerDef, abilityDef, STAT_SPEED))
|
||||
ADJUST_SCORE(-1); // may still want to just poison
|
||||
//fallthrough
|
||||
case EFFECT_POISON:
|
||||
case EFFECT_TOXIC:
|
||||
if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
|
||||
if (!AI_CanPoison(battlerAtk, battlerDef, abilityDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_LIGHT_SCREEN:
|
||||
|
||||
@ -1836,34 +1836,53 @@ void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove,
|
||||
}
|
||||
|
||||
// stat stages
|
||||
bool32 ShouldLowerStat(u32 battlerAtk, u32 battlerDef, u32 battlerAbility, u32 stat)
|
||||
bool32 ShouldLowerStat(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 stat)
|
||||
{
|
||||
if (gBattleMons[battlerDef].statStages[stat] > MIN_STAT_STAGE && battlerAbility != ABILITY_CONTRARY)
|
||||
{
|
||||
if (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CLEAR_AMULET
|
||||
|| battlerAbility == ABILITY_CLEAR_BODY
|
||||
|| battlerAbility == ABILITY_WHITE_SMOKE
|
||||
|| battlerAbility == ABILITY_FULL_METAL_BODY)
|
||||
return FALSE;
|
||||
if (gBattleMons[battlerDef].statStages[stat] == MIN_STAT_STAGE)
|
||||
return FALSE;
|
||||
|
||||
switch (stat)
|
||||
{
|
||||
case STAT_ATK:
|
||||
return !(battlerAbility == ABILITY_HYPER_CUTTER);
|
||||
case STAT_DEF:
|
||||
return !(battlerAbility == ABILITY_BIG_PECKS);
|
||||
case STAT_SPEED:
|
||||
// If AI is faster and doesn't have any mons left, lowering speed doesn't give any
|
||||
return !(AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
|
||||
&& CountUsablePartyMons(battlerAtk) == 0
|
||||
&& !HasMoveEffect(battlerAtk, EFFECT_ELECTRO_BALL));
|
||||
case STAT_ACC:
|
||||
return !(battlerAbility == ABILITY_KEEN_EYE || (B_ILLUMINATE_EFFECT >= GEN_9 && battlerAbility == ABILITY_ILLUMINATE));
|
||||
}
|
||||
return TRUE;
|
||||
if (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CLEAR_AMULET)
|
||||
return FALSE;
|
||||
|
||||
switch (abilityDef)
|
||||
{
|
||||
case ABILITY_SPEED_BOOST:
|
||||
if (stat == STAT_SPEED)
|
||||
return FALSE;
|
||||
case ABILITY_HYPER_CUTTER:
|
||||
if (stat == STAT_ATK)
|
||||
return FALSE;
|
||||
case ABILITY_BIG_PECKS:
|
||||
if (stat == STAT_DEF)
|
||||
return FALSE;
|
||||
case ABILITY_ILLUMINATE:
|
||||
if (B_ILLUMINATE_EFFECT < GEN_9)
|
||||
break;
|
||||
case ABILITY_KEEN_EYE:
|
||||
case ABILITY_MINDS_EYE:
|
||||
if (stat == STAT_ACC)
|
||||
return FALSE;
|
||||
case ABILITY_FLOWER_VEIL:
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS))
|
||||
return FALSE;
|
||||
break;
|
||||
case ABILITY_CONTRARY:
|
||||
case ABILITY_CLEAR_BODY:
|
||||
case ABILITY_WHITE_SMOKE:
|
||||
case ABILITY_FULL_METAL_BODY:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
// This should be a viability check
|
||||
if (stat == STAT_SPEED)
|
||||
{
|
||||
// If AI is faster and doesn't have any mons left, lowering speed doesn't give any
|
||||
return !(AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
|
||||
&& CountUsablePartyMons(battlerAtk) == 0
|
||||
&& !HasMoveEffect(battlerAtk, EFFECT_ELECTRO_BALL));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool32 BattlerStatCanRise(u32 battler, u32 battlerAbility, u32 stat)
|
||||
|
||||
@ -44,28 +44,27 @@ SINGLE_BATTLE_TEST("Mind's Eye doesn't bypass a Ghost-type's Wonder Guard")
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI doesn't use accuracy-lowering moves if it knows that the foe has Mind's Eye")
|
||||
{
|
||||
u32 abilityAI = ABILITY_NONE, moveAI = MOVE_NONE, j = 0;
|
||||
u32 abilityAI = ABILITY_NONE;
|
||||
|
||||
for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++)
|
||||
{
|
||||
if (GetMoveEffect(j) == EFFECT_ACCURACY_DOWN || GetMoveEffect(j) == EFFECT_ACCURACY_DOWN_2) {
|
||||
PARAMETRIZE { moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; }
|
||||
PARAMETRIZE { moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; }
|
||||
}
|
||||
}
|
||||
PARAMETRIZE { abilityAI = ABILITY_SWIFT_SWIM; }
|
||||
PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_MINDS_EYE); }
|
||||
OPPONENT(SPECIES_BASCULEGION) { Moves(MOVE_CELEBRATE, moveAI); Ability(abilityAI); }
|
||||
PLAYER(SPECIES_URSALUNA_BLOODMOON) { Ability(ABILITY_MINDS_EYE); }
|
||||
OPPONENT(SPECIES_BASCULEGION) { Moves(MOVE_CELEBRATE, MOVE_SAND_ATTACK); Ability(abilityAI); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_TACKLE);
|
||||
if (abilityAI == ABILITY_MOLD_BREAKER) { SCORE_GT(opponent, moveAI, MOVE_CELEBRATE); }
|
||||
else { SCORE_EQ(opponent, moveAI, MOVE_CELEBRATE); }
|
||||
}
|
||||
TURN {
|
||||
if (abilityAI == ABILITY_MOLD_BREAKER) {
|
||||
SCORE_GT(opponent, MOVE_SAND_ATTACK, MOVE_CELEBRATE);
|
||||
} else {
|
||||
SCORE_EQ(opponent, MOVE_SAND_ATTACK, MOVE_CELEBRATE);
|
||||
}
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
if (abilityAI == ABILITY_MOLD_BREAKER) { ANIMATION(ANIM_TYPE_MOVE, moveAI, opponent); }
|
||||
if (abilityAI == ABILITY_MOLD_BREAKER) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SAND_ATTACK, opponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
test/battle/ai/check_bad_move.c
Normal file
48
test/battle/ai/check_bad_move.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
#include "battle_ai_util.h"
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will not try to lower opposing stats if target is protected by it's ability")
|
||||
{
|
||||
u16 ability, species, move;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SPEED_BOOST; species = SPECIES_TORCHIC; move = MOVE_SCARY_FACE; }
|
||||
PARAMETRIZE { ability = ABILITY_HYPER_CUTTER; species = SPECIES_KRABBY; move = MOVE_GROWL; }
|
||||
PARAMETRIZE { ability = ABILITY_BIG_PECKS; species = SPECIES_PIDGEY; move = MOVE_SCREECH; }
|
||||
PARAMETRIZE { ability = ABILITY_ILLUMINATE; species = SPECIES_STARYU; move = MOVE_SAND_ATTACK; }
|
||||
PARAMETRIZE { ability = ABILITY_KEEN_EYE; species = SPECIES_PIDGEY; move = MOVE_SAND_ATTACK; }
|
||||
PARAMETRIZE { ability = ABILITY_CONTRARY; species = SPECIES_SNIVY; move = MOVE_NOBLE_ROAR; }
|
||||
PARAMETRIZE { ability = ABILITY_CLEAR_BODY; species = SPECIES_BELDUM; move = MOVE_NOBLE_ROAR; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, move); }
|
||||
} WHEN {
|
||||
TURN { SCORE_LT_VAL(opponent, move, AI_SCORE_DEFAULT); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI will not try to lower opposing stats if target is protected by Flower Veil")
|
||||
{
|
||||
u16 move;
|
||||
|
||||
PARAMETRIZE { move = MOVE_SCARY_FACE; }
|
||||
PARAMETRIZE { move = MOVE_GROWL; }
|
||||
PARAMETRIZE { move = MOVE_SCREECH; }
|
||||
PARAMETRIZE { move = MOVE_SAND_ATTACK; }
|
||||
PARAMETRIZE { move = MOVE_SAND_ATTACK; }
|
||||
PARAMETRIZE { move = MOVE_NOBLE_ROAR; }
|
||||
PARAMETRIZE { move = MOVE_NOBLE_ROAR; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
|
||||
PLAYER(SPECIES_BULBASAUR);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE, move); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { SCORE_LT_VAL(opponentLeft, move, AI_SCORE_DEFAULT, target: playerRight); }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user