Move functions to battle_ai_util.c

This commit is contained in:
Nephrite 2024-01-12 04:01:33 +09:00
parent baefa08bee
commit f6efc75c1a
3 changed files with 278 additions and 277 deletions

View file

@ -190,5 +190,9 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker); s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker);
s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle);
s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle);
bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);
s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
#endif //GUARD_BATTLE_AI_UTIL_H #endif //GUARD_BATTLE_AI_UTIL_H

View file

@ -51,9 +51,6 @@ static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle);
static bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);
static s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
@ -758,235 +755,6 @@ static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u3
aiThink->movesetIndex = 0; aiThink->movesetIndex = 0;
} }
static s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle)
{
u8 i;
// check move additional effects that are likely to happen
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
{
// Only consider effects with a guaranteed chance to happen
if (!MoveEffectIsGuaranteed(battlerAtk, aiData->abilities[battlerAtk], &gBattleMoves[move].additionalEffects[i]))
continue;
// Consider move effects that target self
if (gBattleMoves[move].additionalEffects[i].self)
{
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
{
case MOVE_EFFECT_SPD_PLUS_2:
case MOVE_EFFECT_SPD_PLUS_1:
if (aiData->abilities[battlerAtk] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_ATK_PLUS_1:
case MOVE_EFFECT_DEF_PLUS_1:
case MOVE_EFFECT_SP_ATK_PLUS_1:
case MOVE_EFFECT_SP_DEF_PLUS_1:
case MOVE_EFFECT_ACC_PLUS_1:
case MOVE_EFFECT_EVS_PLUS_1:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1,
&score
);
break;
case MOVE_EFFECT_ATK_PLUS_2:
case MOVE_EFFECT_DEF_PLUS_2:
case MOVE_EFFECT_SP_ATK_PLUS_2:
case MOVE_EFFECT_SP_DEF_PLUS_2:
case MOVE_EFFECT_ACC_PLUS_2:
case MOVE_EFFECT_EVS_PLUS_2:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_2,
&score
);
break;
// Effects that lower stat(s) - only need to consider Contrary
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_V_CREATE:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_SP_ATK_TWO_DOWN:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_RAPIDSPIN:
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
{
ADJUST_SCORE(3);
break;
}
//Spin checks
if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY))
ADJUST_SCORE(-6);
break;
}
}
else // consider move effects that hinder the target
{
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
{
case MOVE_EFFECT_FLINCH:
score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move);
break;
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
if (!ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
break;
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_1:
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(2);
break;
case MOVE_EFFECT_ATK_MINUS_2:
case MOVE_EFFECT_DEF_MINUS_2:
case MOVE_EFFECT_SP_ATK_MINUS_2:
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_POISON:
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
break;
case MOVE_EFFECT_CLEAR_SMOG:
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
break;
case MOVE_EFFECT_SPECTRAL_THIEF:
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
break;
case MOVE_EFFECT_BUG_BITE: // And pluck
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_INCINERATE:
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_SMACK_DOWN:
if (!IsBattlerGrounded(battlerDef) && HasDamagingMoveOfType(battlerAtk, TYPE_GROUND))
ADJUST_SCORE(1);
break;
case MOVE_EFFECT_KNOCK_OFF:
if (CanKnockOffItem(battlerDef, aiData->items[battlerDef]))
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_IRON_BALL:
if (HasMoveEffect(battlerDef, EFFECT_FLING))
ADJUST_SCORE(4);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(3);
break;
}
}
break;
case MOVE_EFFECT_STEAL_ITEM:
{
bool32 canSteal = FALSE;
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
canSteal = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
canSteal = TRUE;
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
&& aiData->items[battlerDef] != ITEM_NONE
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
&& !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS)
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_NONE:
break;
case HOLD_EFFECT_CHOICE_BAND:
case HOLD_EFFECT_CHOICE_SCARF:
case HOLD_EFFECT_CHOICE_SPECS:
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_IRON_BALL:
if (HasMoveEffect(battlerAtk, EFFECT_FLING))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(1);
break;
}
}
break;
}
break;
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData);
break;
case MOVE_EFFECT_FEINT:
if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_THROAT_CHOP:
if (HasSoundMove(battlerDef) && gBattleMoves[predictedMove].soundMove && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_FLAME_BURST:
if (isDoubleBattle)
{
if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))
&& aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12
&& aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD
&& !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
ADJUST_SCORE(1);
}
break;
case MOVE_EFFECT_WRAP:
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && !IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(5);
break;
}
}
}
return score;
}
// AI Score Functions // AI Score Functions
// AI_FLAG_CHECK_BAD_MOVE - decreases move scores // AI_FLAG_CHECK_BAD_MOVE - decreases move scores
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
@ -2916,51 +2684,6 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
return score; return score;
} }
static s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle)
{
if (isDoubleBattle)
return min(CountPositiveStatStages(battlerDef) + CountPositiveStatStages(BATTLE_PARTNER(battlerDef)), 7);
else
return min(CountPositiveStatStages(battlerDef), 4);
}
static bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef)
{
u8 i;
// Want to copy positive stat changes
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
{
if (gBattleMons[battlerDef].statStages[i] > gBattleMons[battlerAtk].statStages[i])
{
switch (i)
{
case STAT_ATK:
return (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL));
case STAT_SPATK:
return (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL));
case STAT_ACC:
case STAT_EVASION:
case STAT_SPEED:
return TRUE;
case STAT_DEF:
case STAT_SPDEF:
return (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL);
}
}
}
return FALSE;
}
//TODO - track entire opponent party data to determine hazard effectiveness
static s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData)
{
if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0)
return 0;
return 2 * gDisableStructs[battlerAtk].isFirstTurn;
}
// double battle logic // double battle logic
static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {

View file

@ -3586,3 +3586,277 @@ bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId)
{ {
return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerId] == ABILITY_COMATOSE; return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerId] == ABILITY_COMATOSE;
} }
s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle)
{
u8 i;
// check move additional effects that are likely to happen
for (i = 0; i < gBattleMoves[move].numAdditionalEffects; i++)
{
// Only consider effects with a guaranteed chance to happen
if (!MoveEffectIsGuaranteed(battlerAtk, aiData->abilities[battlerAtk], &gBattleMoves[move].additionalEffects[i]))
continue;
// Consider move effects that target self
if (gBattleMoves[move].additionalEffects[i].self)
{
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
{
case MOVE_EFFECT_SPD_PLUS_2:
case MOVE_EFFECT_SPD_PLUS_1:
if (aiData->abilities[battlerAtk] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_ATK_PLUS_1:
case MOVE_EFFECT_DEF_PLUS_1:
case MOVE_EFFECT_SP_ATK_PLUS_1:
case MOVE_EFFECT_SP_DEF_PLUS_1:
case MOVE_EFFECT_ACC_PLUS_1:
case MOVE_EFFECT_EVS_PLUS_1:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1,
&score
);
break;
case MOVE_EFFECT_ATK_PLUS_2:
case MOVE_EFFECT_DEF_PLUS_2:
case MOVE_EFFECT_SP_ATK_PLUS_2:
case MOVE_EFFECT_SP_DEF_PLUS_2:
case MOVE_EFFECT_ACC_PLUS_2:
case MOVE_EFFECT_EVS_PLUS_2:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_2,
&score
);
break;
// Effects that lower stat(s) - only need to consider Contrary
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_V_CREATE:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_SP_ATK_TWO_DOWN:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_RAPIDSPIN:
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
{
ADJUST_SCORE(3);
break;
}
//Spin checks
if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY))
ADJUST_SCORE(-6);
break;
}
}
else // consider move effects that hinder the target
{
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
{
case MOVE_EFFECT_FLINCH:
score += ShouldTryToFlinch(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move);
break;
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
if (!ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
break;
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_1:
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(2);
break;
case MOVE_EFFECT_ATK_MINUS_2:
case MOVE_EFFECT_DEF_MINUS_2:
case MOVE_EFFECT_SP_ATK_MINUS_2:
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
if (aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_POISON:
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
break;
case MOVE_EFFECT_CLEAR_SMOG:
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
break;
case MOVE_EFFECT_SPECTRAL_THIEF:
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
break;
case MOVE_EFFECT_BUG_BITE: // And pluck
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_INCINERATE:
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
break;
else if (ItemId_GetPocket(aiData->items[battlerDef]) == POCKET_BERRIES || aiData->holdEffects[battlerDef] == HOLD_EFFECT_GEMS)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_SMACK_DOWN:
if (!IsBattlerGrounded(battlerDef) && HasDamagingMoveOfType(battlerAtk, TYPE_GROUND))
ADJUST_SCORE(1);
break;
case MOVE_EFFECT_KNOCK_OFF:
if (CanKnockOffItem(battlerDef, aiData->items[battlerDef]))
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_IRON_BALL:
if (HasMoveEffect(battlerDef, EFFECT_FLING))
ADJUST_SCORE(4);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(3);
break;
}
}
break;
case MOVE_EFFECT_STEAL_ITEM:
{
bool32 canSteal = FALSE;
if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE)
canSteal = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
canSteal = TRUE;
if (canSteal && aiData->items[battlerAtk] == ITEM_NONE
&& aiData->items[battlerDef] != ITEM_NONE
&& CanBattlerGetOrLoseItem(battlerDef, aiData->items[battlerDef])
&& CanBattlerGetOrLoseItem(battlerAtk, aiData->items[battlerDef])
&& !HasMoveEffect(battlerAtk, EFFECT_ACROBATICS)
&& aiData->abilities[battlerDef] != ABILITY_STICKY_HOLD)
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_NONE:
break;
case HOLD_EFFECT_CHOICE_BAND:
case HOLD_EFFECT_CHOICE_SCARF:
case HOLD_EFFECT_CHOICE_SPECS:
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_TOXIC_ORB:
if (ShouldPoisonSelf(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_IRON_BALL:
if (HasMoveEffect(battlerAtk, EFFECT_FLING))
ADJUST_SCORE(2);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(1);
break;
}
}
break;
}
break;
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData);
break;
case MOVE_EFFECT_FEINT:
if (gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_THROAT_CHOP:
if (HasSoundMove(battlerDef) && gBattleMoves[predictedMove].soundMove && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_FLAME_BURST:
if (isDoubleBattle)
{
if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))
&& aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12
&& aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD
&& !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
ADJUST_SCORE(1);
}
break;
case MOVE_EFFECT_WRAP:
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && !IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(5);
break;
}
}
}
return score;
}
s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle)
{
if (isDoubleBattle)
return min(CountPositiveStatStages(battlerDef) + CountPositiveStatStages(BATTLE_PARTNER(battlerDef)), 7);
else
return min(CountPositiveStatStages(battlerDef), 4);
}
bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef)
{
u8 i;
// Want to copy positive stat changes
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
{
if (gBattleMons[battlerDef].statStages[i] > gBattleMons[battlerAtk].statStages[i])
{
switch (i)
{
case STAT_ATK:
return (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL));
case STAT_SPATK:
return (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL));
case STAT_ACC:
case STAT_EVASION:
case STAT_SPEED:
return TRUE;
case STAT_DEF:
case STAT_SPDEF:
return (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL);
}
}
}
return FALSE;
}
//TODO - track entire opponent party data to determine hazard effectiveness
s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData)
{
if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0)
return 0;
return 2 * gDisableStructs[battlerAtk].isFirstTurn;
}