|
|
|
@ -20,6 +20,8 @@
|
|
|
|
|
#include "constants/moves.h"
|
|
|
|
|
#include "constants/items.h"
|
|
|
|
|
|
|
|
|
|
static u8 AI_GetEffectiveness(u16 multiplier);
|
|
|
|
|
|
|
|
|
|
// Const Data
|
|
|
|
|
static const s8 sAiAbilityRatings[ABILITIES_COUNT] =
|
|
|
|
|
{
|
|
|
|
@ -590,14 +592,14 @@ u32 GetHealthPercentage(u8 battlerId)
|
|
|
|
|
|
|
|
|
|
bool32 AtMaxHp(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
if (GetHealthPercentage(battlerId) == 100)
|
|
|
|
|
if (AI_DATA->hpPercents[battlerId] == 100)
|
|
|
|
|
return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool32 IsBattlerTrapped(u8 battler, bool8 checkSwitch)
|
|
|
|
|
{
|
|
|
|
|
u8 holdEffect = AI_GetHoldEffect(battler);
|
|
|
|
|
u8 holdEffect = AI_DATA->holdEffects[battler];
|
|
|
|
|
if (IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)
|
|
|
|
|
|| (checkSwitch && holdEffect == HOLD_EFFECT_SHED_SHELL)
|
|
|
|
|
|| (!checkSwitch && GetBattlerAbility(battler) == ABILITY_RUN_AWAY)
|
|
|
|
@ -658,7 +660,7 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split)
|
|
|
|
|
{
|
|
|
|
|
s32 i, moveType;
|
|
|
|
|
u32 usable = 0;
|
|
|
|
|
u32 unusable = CheckMoveLimitations(attacker, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u32 unusable = AI_DATA->moveLimitations[attacker];
|
|
|
|
|
u16 *moves = GetMovesArray(attacker);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
|
|
@ -713,10 +715,11 @@ static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
return isCrit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness)
|
|
|
|
|
{
|
|
|
|
|
s32 dmg, moveType, critDmg, normalDmg;
|
|
|
|
|
s8 critChance;
|
|
|
|
|
u16 effectivenessMultiplier;
|
|
|
|
|
|
|
|
|
|
SaveBattlerData(battlerAtk);
|
|
|
|
|
SaveBattlerData(battlerDef);
|
|
|
|
@ -729,7 +732,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
GET_MOVE_TYPE(move, moveType);
|
|
|
|
|
|
|
|
|
|
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
|
|
|
|
|
normalDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE);
|
|
|
|
|
normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, &effectivenessMultiplier);
|
|
|
|
|
critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE);
|
|
|
|
|
|
|
|
|
|
if(critChance == -1)
|
|
|
|
@ -742,26 +745,26 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
{
|
|
|
|
|
case EFFECT_LEVEL_DAMAGE:
|
|
|
|
|
case EFFECT_PSYWAVE:
|
|
|
|
|
dmg = gBattleMons[battlerAtk].level * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
dmg = gBattleMons[battlerAtk].level * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_DRAGON_RAGE:
|
|
|
|
|
dmg = 40 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
dmg = 40 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_SONICBOOM:
|
|
|
|
|
dmg = 20 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
dmg = 20 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_MULTI_HIT:
|
|
|
|
|
dmg *= (AI_DATA->atkAbility == ABILITY_SKILL_LINK ? 5 : 3);
|
|
|
|
|
dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 5 : 3);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_TRIPLE_KICK:
|
|
|
|
|
dmg *= (AI_DATA->atkAbility == ABILITY_SKILL_LINK ? 6 : 5);
|
|
|
|
|
dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 6 : 5);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_ENDEAVOR:
|
|
|
|
|
// If target has less HP than user, Endeavor does no damage
|
|
|
|
|
dmg = max(0, gBattleMons[battlerDef].hp - gBattleMons[battlerAtk].hp);
|
|
|
|
|
break;
|
|
|
|
|
case EFFECT_SUPER_FANG:
|
|
|
|
|
dmg = (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND
|
|
|
|
|
dmg = (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND
|
|
|
|
|
? max(2, gBattleMons[battlerDef].hp * 3 / 4)
|
|
|
|
|
: max(1, gBattleMons[battlerDef].hp / 2));
|
|
|
|
|
break;
|
|
|
|
@ -778,6 +781,9 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
|
|
|
|
|
RestoreBattlerData(battlerAtk);
|
|
|
|
|
RestoreBattlerData(battlerDef);
|
|
|
|
|
|
|
|
|
|
// convert multiper to AI_EFFECTIVENESS_xX
|
|
|
|
|
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
|
|
|
|
|
|
|
|
|
|
return dmg;
|
|
|
|
|
}
|
|
|
|
@ -785,10 +791,10 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
// Checks if one of the moves has side effects or perks
|
|
|
|
|
static u32 WhichMoveBetter(u32 move1, u32 move2)
|
|
|
|
|
{
|
|
|
|
|
s32 defAbility = AI_GetAbility(gBattlerTarget);
|
|
|
|
|
s32 defAbility = AI_DATA->abilities[gBattlerTarget];
|
|
|
|
|
|
|
|
|
|
// Check if physical moves hurt.
|
|
|
|
|
if (AI_GetHoldEffect(sBattler_AI) != HOLD_EFFECT_PROTECTIVE_PADS
|
|
|
|
|
if (AI_DATA->holdEffects[sBattler_AI] != HOLD_EFFECT_PROTECTIVE_PADS
|
|
|
|
|
&& (BATTLE_HISTORY->itemEffects[gBattlerTarget] == HOLD_EFFECT_ROCKY_HELMET
|
|
|
|
|
|| defAbility == ABILITY_IRON_BARBS || defAbility == ABILITY_ROUGH_SKIN))
|
|
|
|
|
{
|
|
|
|
@ -868,7 +874,7 @@ u8 GetMoveDamageResult(u16 move)
|
|
|
|
|
&& sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END
|
|
|
|
|
&& gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power != 0)
|
|
|
|
|
{
|
|
|
|
|
moveDmgs[checkedMove] = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove];
|
|
|
|
|
moveDmgs[checkedMove] = AI_DATA->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -924,7 +930,7 @@ u8 GetMoveDamageResult(u16 move)
|
|
|
|
|
|
|
|
|
|
u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
{
|
|
|
|
|
int bestDmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
|
|
|
|
int bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
|
|
|
|
|
|
|
|
|
return (bestDmg * 100) / gBattleMons[battlerDef].maxHP;
|
|
|
|
|
}
|
|
|
|
@ -952,37 +958,28 @@ u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
|
|
|
|
|
u8 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
{
|
|
|
|
|
u8 damageVar;
|
|
|
|
|
u32 effectivenessMultiplier;
|
|
|
|
|
|
|
|
|
|
gMoveResultFlags = 0;
|
|
|
|
|
gCurrentMove = move;
|
|
|
|
|
effectivenessMultiplier = AI_GetTypeEffectiveness(gCurrentMove, battlerAtk, battlerDef);
|
|
|
|
|
return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (effectivenessMultiplier)
|
|
|
|
|
static u8 AI_GetEffectiveness(u16 multiplier)
|
|
|
|
|
{
|
|
|
|
|
switch (multiplier)
|
|
|
|
|
{
|
|
|
|
|
case UQ_4_12(0.0):
|
|
|
|
|
default:
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x0;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x0;
|
|
|
|
|
case UQ_4_12(0.25):
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x0_25;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x0_25;
|
|
|
|
|
case UQ_4_12(0.5):
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x0_5;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x0_5;
|
|
|
|
|
case UQ_4_12(1.0):
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x1;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x1;
|
|
|
|
|
case UQ_4_12(2.0):
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x2;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x2;
|
|
|
|
|
case UQ_4_12(4.0):
|
|
|
|
|
damageVar = AI_EFFECTIVENESS_x4;
|
|
|
|
|
break;
|
|
|
|
|
return AI_EFFECTIVENESS_x4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return damageVar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Checks to see if AI will move ahead of another battler
|
|
|
|
@ -1032,13 +1029,13 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
|
|
|
|
bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk)
|
|
|
|
|
{
|
|
|
|
|
s32 i, dmg;
|
|
|
|
|
u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u32 unusable = AI_DATA->moveLimitations[battlerDef];
|
|
|
|
|
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
|
|
|
{
|
|
|
|
|
if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && !(unusable & gBitTable[i])
|
|
|
|
|
&& AI_CalcDamage(moves[i], battlerDef, battlerAtk) >= gBattleMons[battlerAtk].hp)
|
|
|
|
|
&& AI_DATA->simulatedDmg[battlerDef][battlerAtk][moves[i]] >= gBattleMons[battlerAtk].hp)
|
|
|
|
|
{
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
@ -1052,7 +1049,7 @@ bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk)
|
|
|
|
|
bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits)
|
|
|
|
|
{
|
|
|
|
|
s32 i, dmg;
|
|
|
|
|
u32 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
|
|
|
|
|
u16 *moves = gBattleMons[battlerAtk].moves;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
|
|
@ -1060,7 +1057,7 @@ bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits)
|
|
|
|
|
if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && !(moveLimitations & gBitTable[i]))
|
|
|
|
|
{
|
|
|
|
|
// Use the pre-calculated value in simulatedDmg instead of re-calculating it
|
|
|
|
|
dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i];
|
|
|
|
|
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i];
|
|
|
|
|
|
|
|
|
|
if (numHits)
|
|
|
|
|
dmg *= numHits;
|
|
|
|
@ -1076,9 +1073,13 @@ bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits)
|
|
|
|
|
bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits)
|
|
|
|
|
{
|
|
|
|
|
s32 i, dmg;
|
|
|
|
|
u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u8 effectiveness;
|
|
|
|
|
u32 unusable = AI_DATA->moveLimitations[battlerDef];
|
|
|
|
|
|
|
|
|
|
if (move != MOVE_NONE && move != 0xFFFF && !(unusable & gBitTable[i]) && AI_CalcDamage(move, battlerDef, battlerAtk) >= gBattleMons[battlerAtk].hp)
|
|
|
|
|
if (move != MOVE_NONE
|
|
|
|
|
&& move != 0xFFFF
|
|
|
|
|
&& !(unusable & gBitTable[i])
|
|
|
|
|
&& AI_CalcDamage(move, battlerDef, battlerAtk, &effectiveness) >= gBattleMons[battlerAtk].hp)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
@ -1088,7 +1089,7 @@ bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits)
|
|
|
|
|
bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod)
|
|
|
|
|
{
|
|
|
|
|
u32 i;
|
|
|
|
|
u32 unusable = CheckMoveLimitations(battlerDef, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u32 unusable = AI_DATA->moveLimitations[battlerDef];
|
|
|
|
|
s32 dmg;
|
|
|
|
|
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
|
|
|
|
|
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
|
|
|
|
@ -1098,7 +1099,7 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
|
|
|
{
|
|
|
|
|
dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i];
|
|
|
|
|
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i];
|
|
|
|
|
if (dmgMod)
|
|
|
|
|
dmg *= dmgMod;
|
|
|
|
|
|
|
|
|
@ -1113,9 +1114,9 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM
|
|
|
|
|
|
|
|
|
|
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability)
|
|
|
|
|
{
|
|
|
|
|
if (IsBattlerAlive(battlerId) && AI_GetAbility(battlerId) == ability)
|
|
|
|
|
if (IsBattlerAlive(battlerId) && AI_DATA->abilities[battlerId] == ability)
|
|
|
|
|
return TRUE;
|
|
|
|
|
else if (IsBattlerAlive(BATTLE_PARTNER(battlerId)) && AI_GetAbility(BATTLE_PARTNER(battlerId)) == ability)
|
|
|
|
|
else if (IsBattlerAlive(BATTLE_PARTNER(battlerId)) && AI_DATA->abilities[BATTLE_PARTNER(battlerId)] == ability)
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
@ -1172,7 +1173,7 @@ u16 AI_GetHoldEffect(u32 battlerId)
|
|
|
|
|
return HOLD_EFFECT_NONE;
|
|
|
|
|
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
|
|
|
|
|
return HOLD_EFFECT_NONE;
|
|
|
|
|
if (AI_GetAbility(battlerId) == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID))
|
|
|
|
|
if (AI_DATA->abilities[battlerId] == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID))
|
|
|
|
|
return HOLD_EFFECT_NONE;
|
|
|
|
|
|
|
|
|
|
return holdEffect;
|
|
|
|
@ -1190,7 +1191,7 @@ bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags)
|
|
|
|
|
// different from IsBattlerGrounded in that we don't always know battler's hold effect or ability
|
|
|
|
|
bool32 AI_IsBattlerGrounded(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
u32 holdEffect = AI_GetHoldEffect(battlerId);
|
|
|
|
|
u32 holdEffect = AI_DATA->holdEffects[battlerId];
|
|
|
|
|
|
|
|
|
|
if (holdEffect == HOLD_EFFECT_IRON_BALL)
|
|
|
|
|
return TRUE;
|
|
|
|
@ -1206,7 +1207,7 @@ bool32 AI_IsBattlerGrounded(u8 battlerId)
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (holdEffect == HOLD_EFFECT_AIR_BALLOON)
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (AI_GetAbility(battlerId) == ABILITY_LEVITATE)
|
|
|
|
|
else if (AI_DATA->abilities[battlerId] == ABILITY_LEVITATE)
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FLYING))
|
|
|
|
|
return FALSE;
|
|
|
|
@ -1241,14 +1242,7 @@ bool32 AI_WeatherHasEffect(void)
|
|
|
|
|
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE)
|
|
|
|
|
return TRUE; // AI doesn't understand weather supression (handicap)
|
|
|
|
|
|
|
|
|
|
// need to manually check since we don't necessarily know opponent ability
|
|
|
|
|
for (i = 0; i < gBattlersCount; i++)
|
|
|
|
|
{
|
|
|
|
|
if (IsBattlerAlive(i)
|
|
|
|
|
&& (AI_GetAbility(i) == ABILITY_AIR_LOCK || AI_GetAbility(i) == ABILITY_CLOUD_NINE))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
return WEATHER_HAS_EFFECT; // weather damping abilities are announced
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool32 IsAromaVeilProtectedMove(u16 move)
|
|
|
|
@ -1350,70 +1344,10 @@ bool32 IsMoveRedirectionPrevented(u16 move, u16 atkAbility)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// differs from GetTotalAccuracy in that we need to check AI history for item, ability, etc
|
|
|
|
|
u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u8 atkHoldEffect, u8 defHoldEffect, u16 move)
|
|
|
|
|
u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
{
|
|
|
|
|
u32 calc, moveAcc, atkParam, defParam;
|
|
|
|
|
s8 buff, accStage, evasionStage;
|
|
|
|
|
|
|
|
|
|
gPotentialItemEffectBattler = battlerDef;
|
|
|
|
|
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
|
|
|
|
|
evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
|
|
|
|
|
if (atkAbility == ABILITY_UNAWARE)
|
|
|
|
|
evasionStage = DEFAULT_STAT_STAGE;
|
|
|
|
|
if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED)
|
|
|
|
|
evasionStage = DEFAULT_STAT_STAGE;
|
|
|
|
|
if (defAbility == ABILITY_UNAWARE)
|
|
|
|
|
accStage = DEFAULT_STAT_STAGE;
|
|
|
|
|
|
|
|
|
|
if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
|
|
|
|
|
buff = accStage;
|
|
|
|
|
else
|
|
|
|
|
buff = accStage + DEFAULT_STAT_STAGE - evasionStage;
|
|
|
|
|
|
|
|
|
|
if (buff < MIN_STAT_STAGE)
|
|
|
|
|
buff = MIN_STAT_STAGE;
|
|
|
|
|
if (buff > MAX_STAT_STAGE)
|
|
|
|
|
buff = MAX_STAT_STAGE;
|
|
|
|
|
|
|
|
|
|
moveAcc = gBattleMoves[move].accuracy;
|
|
|
|
|
// Check Thunder and Hurricane on sunny weather.
|
|
|
|
|
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SUN
|
|
|
|
|
&& (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE))
|
|
|
|
|
moveAcc = 50;
|
|
|
|
|
// Check Wonder Skin.
|
|
|
|
|
if (defAbility == ABILITY_WONDER_SKIN && gBattleMoves[move].power == 0)
|
|
|
|
|
moveAcc = 50;
|
|
|
|
|
|
|
|
|
|
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
|
|
|
|
|
calc /= gAccuracyStageRatios[buff].divisor;
|
|
|
|
|
|
|
|
|
|
if (atkAbility == ABILITY_COMPOUND_EYES)
|
|
|
|
|
calc = (calc * 130) / 100; // 1.3 compound eyes boost
|
|
|
|
|
else if (atkAbility == ABILITY_VICTORY_STAR)
|
|
|
|
|
calc = (calc * 110) / 100; // 1.1 victory star boost
|
|
|
|
|
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_VICTORY_STAR)
|
|
|
|
|
calc = (calc * 110) / 100; // 1.1 ally's victory star boost
|
|
|
|
|
|
|
|
|
|
if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
|
|
|
|
calc = (calc * 80) / 100; // 1.2 sand veil loss
|
|
|
|
|
else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_HAIL)
|
|
|
|
|
calc = (calc * 80) / 100; // 1.2 snow cloak loss
|
|
|
|
|
else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|
|
|
|
|
calc = (calc * 50) / 100; // 1.5 tangled feet loss
|
|
|
|
|
|
|
|
|
|
if (atkAbility == ABILITY_HUSTLE && IS_MOVE_PHYSICAL(move))
|
|
|
|
|
calc = (calc * 80) / 100; // 1.2 hustle loss
|
|
|
|
|
|
|
|
|
|
if (defHoldEffect == HOLD_EFFECT_EVASION_UP)
|
|
|
|
|
calc = (calc * (100 - defParam)) / 100;
|
|
|
|
|
|
|
|
|
|
if (atkHoldEffect == HOLD_EFFECT_WIDE_LENS)
|
|
|
|
|
calc = (calc * (100 + atkParam)) / 100;
|
|
|
|
|
else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef));
|
|
|
|
|
calc = (calc * (100 + atkParam)) / 100;
|
|
|
|
|
|
|
|
|
|
return calc;
|
|
|
|
|
return GetTotalAccuracy(battlerAtk, battlerDef, move, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef],
|
|
|
|
|
AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool32 IsSemiInvulnerable(u8 battlerDef, u16 move)
|
|
|
|
@ -1441,7 +1375,7 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
if (gStatuses3[battlerDef] & STATUS3_ALWAYS_HITS || gDisableStructs[battlerDef].battlerWithSureHit == battlerAtk)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (AI_GetAbility(battlerDef) == ABILITY_NO_GUARD || AI_GetAbility(battlerAtk) == ABILITY_NO_GUARD)
|
|
|
|
|
if (AI_DATA->abilities[battlerDef] == ABILITY_NO_GUARD || AI_DATA->abilities[battlerAtk] == ABILITY_NO_GUARD)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (B_TOXIC_NEVER_MISS >= GEN_6 && gBattleMoves[move].effect == EFFECT_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON))
|
|
|
|
@ -1466,12 +1400,13 @@ bool32 IsMoveEncouragedToHit(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u32 accuracy, u16 move)
|
|
|
|
|
bool32 ShouldTryOHKO(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move)
|
|
|
|
|
{
|
|
|
|
|
u32 holdEffect = AI_GetHoldEffect(battlerDef);
|
|
|
|
|
u32 holdEffect = AI_DATA->holdEffects[battlerDef];
|
|
|
|
|
u32 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, move);
|
|
|
|
|
|
|
|
|
|
gPotentialItemEffectBattler = battlerDef;
|
|
|
|
|
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < GetBattlerHoldEffectParam(battlerDef))
|
|
|
|
|
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < AI_DATA->holdEffectParams[battlerDef])
|
|
|
|
|
return FALSE; //probabilistically speaking, focus band should activate so dont OHKO
|
|
|
|
|
else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef))
|
|
|
|
|
return FALSE;
|
|
|
|
@ -1599,7 +1534,7 @@ void ProtectChecks(u8 battlerAtk, u8 battlerDef, u16 move, u16 predictedMove, s1
|
|
|
|
|
{
|
|
|
|
|
// TODO more sophisticated logic
|
|
|
|
|
u16 predictedEffect = gBattleMoves[predictedMove].effect;
|
|
|
|
|
u8 defAbility = AI_GetAbility(battlerDef);
|
|
|
|
|
u8 defAbility = AI_DATA->abilities[battlerDef];
|
|
|
|
|
u32 uses = gDisableStructs[battlerAtk].protectUses;
|
|
|
|
|
|
|
|
|
|
/*if (GetMoveResultFlags(predictedMove) & (MOVE_RESULT_NO_EFFECT | MOVE_RESULT_MISSED))
|
|
|
|
@ -1814,7 +1749,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
|
|
|
|
|
|
|
|
|
bool32 CanIndexMoveFaintTarget(u8 battlerAtk, u8 battlerDef, u8 index, u8 numHits)
|
|
|
|
|
{
|
|
|
|
|
s32 dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][index];
|
|
|
|
|
s32 dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index];
|
|
|
|
|
|
|
|
|
|
if (numHits)
|
|
|
|
|
dmg *= numHits;
|
|
|
|
@ -1908,7 +1843,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32
|
|
|
|
|
{
|
|
|
|
|
s32 i;
|
|
|
|
|
u16 *moves = GetMovesArray(battlerAtk);
|
|
|
|
|
u8 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
|
|
|
{
|
|
|
|
@ -1923,7 +1858,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32
|
|
|
|
|
|| gBattleMoves[moves[i]].target & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (AI_GetMoveAccuracy(battlerAtk, battlerDef, atkAbility, defAbility, atkHoldEffect, defHoldEffect, moves[i]) <= accCheck)
|
|
|
|
|
if (AI_GetMoveAccuracy(battlerAtk, battlerDef, moves[i]) <= accCheck)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1933,7 +1868,7 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32
|
|
|
|
|
|
|
|
|
|
bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
{
|
|
|
|
|
u8 moveLimitations = CheckMoveLimitations(battlerAtk, 0, MOVE_LIMITATIONS_ALL);
|
|
|
|
|
u8 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
|
|
|
|
|
u32 i;
|
|
|
|
|
u16 *moves = GetMovesArray(battlerAtk);
|
|
|
|
|
|
|
|
|
@ -1944,7 +1879,7 @@ bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
if (!(gBitTable[i] & moveLimitations))
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMoves[moves[i]].effect == EFFECT_SLEEP
|
|
|
|
|
&& AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, moves[i]) < 85)
|
|
|
|
|
&& AI_GetMoveAccuracy(battlerAtk, battlerDef, moves[i]) < 85)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -2259,7 +2194,7 @@ static u32 GetTrapDamage(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
// ai has no knowledge about turns remaining
|
|
|
|
|
u32 damage = 0;
|
|
|
|
|
u32 holdEffect = AI_GetHoldEffect(gBattleStruct->wrappedBy[battlerId]);
|
|
|
|
|
u32 holdEffect = AI_DATA->holdEffects[gBattleStruct->wrappedBy[battlerId]];
|
|
|
|
|
if (gBattleMons[battlerId].status2 & STATUS2_WRAPPED)
|
|
|
|
|
{
|
|
|
|
|
if (holdEffect == HOLD_EFFECT_BINDING_BAND)
|
|
|
|
@ -2277,7 +2212,7 @@ static u32 GetPoisonDamage(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
u32 damage = 0;
|
|
|
|
|
|
|
|
|
|
if (AI_GetAbility(battlerId) == ABILITY_POISON_HEAL)
|
|
|
|
|
if (AI_DATA->abilities[battlerId] == ABILITY_POISON_HEAL)
|
|
|
|
|
return damage;
|
|
|
|
|
|
|
|
|
|
if (gBattleMons[battlerId].status1 & STATUS1_POISON)
|
|
|
|
@ -2323,8 +2258,8 @@ static bool32 BattlerAffectedByHail(u8 battlerId, u16 ability)
|
|
|
|
|
|
|
|
|
|
static u32 GetWeatherDamage(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
u32 ability = AI_GetAbility(battlerId);
|
|
|
|
|
u32 holdEffect = AI_GetHoldEffect(battlerId);
|
|
|
|
|
u32 ability = AI_DATA->abilities[battlerId];
|
|
|
|
|
u32 holdEffect = AI_DATA->holdEffects[battlerId];
|
|
|
|
|
u32 damage = 0;
|
|
|
|
|
if (!AI_WeatherHasEffect())
|
|
|
|
|
return 0;
|
|
|
|
@ -2358,7 +2293,7 @@ u32 GetBattlerSecondaryDamage(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
u32 secondaryDamage;
|
|
|
|
|
|
|
|
|
|
if (AI_GetAbility(battlerId) == ABILITY_MAGIC_GUARD)
|
|
|
|
|
if (AI_DATA->abilities[battlerId] == ABILITY_MAGIC_GUARD)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
secondaryDamage = GetLeechSeedDamage(battlerId)
|
|
|
|
@ -2485,13 +2420,13 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|
|
|
|
return PIVOT; // Won't get the two turns, pivot
|
|
|
|
|
|
|
|
|
|
if (!IS_MOVE_STATUS(move) && (shouldSwitch
|
|
|
|
|
|| (AtMaxHp(battlerDef) && (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH
|
|
|
|
|
|| (AtMaxHp(battlerDef) && (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH
|
|
|
|
|
|| defAbility == ABILITY_STURDY || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD))))
|
|
|
|
|
return PIVOT; // pivot to break sash/sturdy/multiscale
|
|
|
|
|
}
|
|
|
|
|
else if (!hasStatBoost)
|
|
|
|
|
{
|
|
|
|
|
if (!IS_MOVE_STATUS(move) && (AtMaxHp(battlerDef) && (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH
|
|
|
|
|
if (!IS_MOVE_STATUS(move) && (AtMaxHp(battlerDef) && (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH
|
|
|
|
|
|| defAbility == ABILITY_STURDY || defAbility == ABILITY_MULTISCALE || defAbility == ABILITY_SHADOW_SHIELD)))
|
|
|
|
|
return PIVOT; // pivot to break sash/sturdy/multiscale
|
|
|
|
|
|
|
|
|
@ -2502,7 +2437,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|
|
|
|
if (gSideStatuses[battlerAtk] & SIDE_STATUS_SPIKES && switchScore >= SWITCHING_INCREASE_CAN_REMOVE_HAZARDS)
|
|
|
|
|
return PIVOT;*/
|
|
|
|
|
|
|
|
|
|
/*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility) && switchScore >= SWITCHING_INCREASE_WALLS_FOE)
|
|
|
|
|
/*if (BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) && switchScore >= SWITCHING_INCREASE_WALLS_FOE)
|
|
|
|
|
return PIVOT;*/
|
|
|
|
|
|
|
|
|
|
/*if (IsClassDamager(class) && switchScore >= SWITCHING_INCREASE_HAS_SUPER_EFFECTIVE_MOVE)
|
|
|
|
@ -2545,7 +2480,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|
|
|
|
{
|
|
|
|
|
if (CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
|
|
|
|
{
|
|
|
|
|
if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility))
|
|
|
|
|
if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]))
|
|
|
|
|
return CAN_TRY_PIVOT; // Use this move to KO if you must
|
|
|
|
|
}
|
|
|
|
|
else // Can't KO the foe
|
|
|
|
@ -2557,7 +2492,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|
|
|
|
{
|
|
|
|
|
if (CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
|
|
|
|
{
|
|
|
|
|
if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility) // This is the only move that can KO
|
|
|
|
|
if (!BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk]) // This is the only move that can KO
|
|
|
|
|
&& !hasStatBoost) //You're not wasting a valuable stat boost
|
|
|
|
|
{
|
|
|
|
|
return CAN_TRY_PIVOT;
|
|
|
|
@ -2568,7 +2503,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|
|
|
|
// can knock out foe in 2 hits
|
|
|
|
|
if (IS_MOVE_STATUS(move) && (shouldSwitch //Damaging move
|
|
|
|
|
//&& (switchScore >= SWITCHING_INCREASE_RESIST_ALL_MOVES + SWITCHING_INCREASE_KO_FOE //remove hazards
|
|
|
|
|
|| (AI_DATA->defHoldEffect == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef))))
|
|
|
|
|
|| (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_FOCUS_SASH && AtMaxHp(battlerDef))))
|
|
|
|
|
return DONT_PIVOT; // Pivot to break the sash
|
|
|
|
|
else
|
|
|
|
|
return CAN_TRY_PIVOT;
|
|
|
|
@ -2637,7 +2572,7 @@ bool32 CanKnockOffItem(u8 battler, u16 item)
|
|
|
|
|
)) && GetBattlerSide(battler) == B_SIDE_PLAYER)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (AI_GetAbility(battler) == ABILITY_STICKY_HOLD)
|
|
|
|
|
if (AI_DATA->abilities[battler] == ABILITY_STICKY_HOLD)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!CanBattlerGetOrLoseItem(battler, item))
|
|
|
|
@ -2684,13 +2619,13 @@ bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move,
|
|
|
|
|
|
|
|
|
|
static bool32 AI_CanPoisonType(u8 battlerAttacker, u8 battlerTarget)
|
|
|
|
|
{
|
|
|
|
|
return ((AI_GetAbility(battlerAttacker) == ABILITY_CORROSION && gBattleMoves[gCurrentMove].split == SPLIT_STATUS)
|
|
|
|
|
return ((AI_DATA->abilities[battlerAttacker] == ABILITY_CORROSION && gBattleMoves[gCurrentMove].split == SPLIT_STATUS)
|
|
|
|
|
|| !(IS_BATTLER_OF_TYPE(battlerTarget, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerTarget, TYPE_STEEL)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool32 AI_CanBePoisoned(u8 battlerAtk, u8 battlerDef)
|
|
|
|
|
{
|
|
|
|
|
u16 ability = AI_GetAbility(battlerDef);
|
|
|
|
|
u16 ability = AI_DATA->abilities[battlerDef];
|
|
|
|
|
|
|
|
|
|
if (!(AI_CanPoisonType(battlerAtk, battlerDef))
|
|
|
|
|
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|
|
|
|
@ -2729,7 +2664,7 @@ bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (defAbility != ABILITY_CORROSION && (IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL)))
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (IsValidDoubleBattle(battlerAtk) && AI_GetAbility(BATTLE_PARTNER(battlerDef)) == ABILITY_PASTEL_VEIL)
|
|
|
|
|
else if (IsValidDoubleBattle(battlerAtk) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_PASTEL_VEIL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
@ -2854,7 +2789,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili
|
|
|
|
|
|
|
|
|
|
bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
{
|
|
|
|
|
if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->defAbility))
|
|
|
|
|
if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef]))
|
|
|
|
|
return TRUE; // battler is taking secondary damage with low HP
|
|
|
|
|
|
|
|
|
|
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL)
|
|
|
|
@ -2868,11 +2803,11 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
|
|
|
|
|
bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
{
|
|
|
|
|
if (AI_DATA->atkHoldEffect == HOLD_EFFECT_CHOICE_BAND && CountUsablePartyMons(battlerAtk) == 0)
|
|
|
|
|
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND && CountUsablePartyMons(battlerAtk) == 0)
|
|
|
|
|
return FALSE; // don't lock attacker into fake out if can't switch out
|
|
|
|
|
|
|
|
|
|
if (gDisableStructs[battlerAtk].isFirstTurn
|
|
|
|
|
&& ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, move)
|
|
|
|
|
&& ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move)
|
|
|
|
|
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, move))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
@ -2970,7 +2905,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
|
|
|
|
if (CanTargetFaintAi(battlerDef, battlerAtk)
|
|
|
|
|
&& !CanTargetFaintAiWithMod(battlerDef, battlerAtk, healDmg, 0))
|
|
|
|
|
return TRUE; // target can faint attacker unless they heal
|
|
|
|
|
else if (!CanTargetFaintAi(battlerDef, battlerAtk) && GetHealthPercentage(battlerAtk) < 60 && (Random() % 3))
|
|
|
|
|
else if (!CanTargetFaintAi(battlerDef, battlerAtk) && AI_DATA->hpPercents[battlerAtk] < 60 && (Random() % 3))
|
|
|
|
|
return TRUE; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -2988,7 +2923,7 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
|
|
|
|
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
|
|
|
|
{
|
|
|
|
|
// using item or user going first
|
|
|
|
|
s32 damage = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
|
|
|
|
s32 damage = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
|
|
|
|
s32 healAmount = (healPercent * damage) / 100;
|
|
|
|
|
if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK)
|
|
|
|
|
healAmount = 0;
|
|
|
|
@ -2996,7 +2931,7 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
|
|
|
|
if (CanTargetFaintAi(battlerDef, battlerAtk)
|
|
|
|
|
&& !CanTargetFaintAiWithMod(battlerDef, battlerAtk, healAmount, 0))
|
|
|
|
|
return TRUE; // target can faint attacker unless they heal
|
|
|
|
|
else if (!CanTargetFaintAi(battlerDef, battlerAtk) && GetHealthPercentage(battlerAtk) < 60 && (Random() % 3))
|
|
|
|
|
else if (!CanTargetFaintAi(battlerDef, battlerAtk) && AI_DATA->hpPercents[battlerAtk] < 60 && (Random() % 3))
|
|
|
|
|
return TRUE; // target can't faint attacker at all, attacker health is about half, 2/3rds rate of encouraging healing
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
@ -3039,14 +2974,14 @@ bool32 IsValidDoubleBattle(u8 battlerAtk)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u16 GetAllyChosenMove(void)
|
|
|
|
|
u16 GetAllyChosenMove(u8 battlerId)
|
|
|
|
|
{
|
|
|
|
|
u8 partnerBattler = BATTLE_PARTNER(sBattler_AI);
|
|
|
|
|
|
|
|
|
|
u8 partnerBattler = BATTLE_PARTNER(battlerId);
|
|
|
|
|
|
|
|
|
|
if (!IsBattlerAlive(partnerBattler) || !IsBattlerAIControlled(partnerBattler))
|
|
|
|
|
return MOVE_NONE; // TODO: prediction?
|
|
|
|
|
else if (partnerBattler > sBattler_AI) // Battler with the lower id chooses the move first.
|
|
|
|
|
return MOVE_NONE;
|
|
|
|
|
else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first.
|
|
|
|
|
return gLastMoves[partnerBattler];
|
|
|
|
|
else
|
|
|
|
|
return gBattleMons[partnerBattler].moves[gBattleStruct->chosenMovePositions[partnerBattler]];
|
|
|
|
|
}
|
|
|
|
@ -3184,7 +3119,7 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move)
|
|
|
|
|
party = gEnemyParty;
|
|
|
|
|
|
|
|
|
|
if (CountUsablePartyMons(battlerAtk) == 0
|
|
|
|
|
&& (CanTargetFaintAi(battlerDef, battlerAtk) || BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->atkAbility)))
|
|
|
|
|
&& (CanTargetFaintAi(battlerDef, battlerAtk) || BattlerWillFaintFromSecondaryDamage(battlerAtk, AI_DATA->abilities[battlerAtk])))
|
|
|
|
|
return FALSE; // Don't heal if last mon and will faint
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
|
|
@ -3241,13 +3176,14 @@ s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon
|
|
|
|
|
{
|
|
|
|
|
s32 dmg;
|
|
|
|
|
u32 i;
|
|
|
|
|
u8 effectiveness;
|
|
|
|
|
struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
|
|
|
|
battleMons[i] = gBattleMons[i];
|
|
|
|
|
|
|
|
|
|
PokemonToBattleMon(mon, &gBattleMons[battlerAtk]);
|
|
|
|
|
dmg = AI_CalcDamage(move, battlerAtk, battlerDef);
|
|
|
|
|
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
|
|
|
|
gBattleMons[i] = battleMons[i];
|
|
|
|
@ -3442,10 +3378,10 @@ bool32 IsRecycleEncouragedItem(u16 item)
|
|
|
|
|
#define STAT_UP_STAGE 10
|
|
|
|
|
void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
{
|
|
|
|
|
if (AI_DATA->atkAbility == ABILITY_CONTRARY)
|
|
|
|
|
if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (GetHealthPercentage(battlerAtk) < 80 && AI_RandLessThan(128))
|
|
|
|
|
if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
|
|
|
@ -3454,7 +3390,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
switch (statId)
|
|
|
|
|
{
|
|
|
|
|
case STAT_ATK:
|
|
|
|
|
if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && GetHealthPercentage(battlerAtk) > 40)
|
|
|
|
|
if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL) && AI_DATA->hpPercents[battlerAtk] > 40)
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMons[battlerAtk].statStages[STAT_ATK] < STAT_UP_2_STAGE)
|
|
|
|
|
*score += 2;
|
|
|
|
@ -3466,7 +3402,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
break;
|
|
|
|
|
case STAT_DEF:
|
|
|
|
|
if ((HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL)|| IS_MOVE_PHYSICAL(gLastMoves[battlerDef]))
|
|
|
|
|
&& GetHealthPercentage(battlerAtk) > 70)
|
|
|
|
|
&& AI_DATA->hpPercents[battlerAtk] > 70)
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMons[battlerAtk].statStages[STAT_DEF] < STAT_UP_2_STAGE)
|
|
|
|
|
*score += 2; // seems better to raise def at higher HP
|
|
|
|
@ -3484,7 +3420,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STAT_SPATK:
|
|
|
|
|
if (HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL) && GetHealthPercentage(battlerAtk) > 40)
|
|
|
|
|
if (HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL) && AI_DATA->hpPercents[battlerAtk] > 40)
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < STAT_UP_2_STAGE)
|
|
|
|
|
*score += 2;
|
|
|
|
@ -3494,7 +3430,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
break;
|
|
|
|
|
case STAT_SPDEF:
|
|
|
|
|
if ((HasMoveWithSplit(battlerDef, SPLIT_SPECIAL) || IS_MOVE_SPECIAL(gLastMoves[battlerDef]))
|
|
|
|
|
&& GetHealthPercentage(battlerAtk) > 70)
|
|
|
|
|
&& AI_DATA->hpPercents[battlerAtk] > 70)
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMons[battlerAtk].statStages[STAT_SPDEF] < STAT_UP_2_STAGE)
|
|
|
|
|
*score += 2; // seems better to raise spdef at higher HP
|
|
|
|
@ -3503,13 +3439,13 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case STAT_ACC:
|
|
|
|
|
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect))
|
|
|
|
|
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]))
|
|
|
|
|
*score += 2; // has moves with less than 80% accuracy
|
|
|
|
|
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect))
|
|
|
|
|
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]))
|
|
|
|
|
*(score)++;
|
|
|
|
|
break;
|
|
|
|
|
case STAT_EVASION:
|
|
|
|
|
if (!BattlerWillFaintFromWeather(battlerAtk, AI_DATA->atkAbility))
|
|
|
|
|
if (!BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk]))
|
|
|
|
|
{
|
|
|
|
|
if (!GetBattlerSecondaryDamage(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_ROOTED))
|
|
|
|
|
*score += 2;
|
|
|
|
@ -3525,7 +3461,7 @@ 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 (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove) && AI_DATA->hpPercents[battlerDef] > 20)
|
|
|
|
|
{
|
|
|
|
|
if (!HasDamagingMove(battlerDef))
|
|
|
|
|
*score += 2;
|
|
|
|
@ -3536,7 +3472,7 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
|
|
|
|
if (HasMoveEffect(battlerAtk, EFFECT_VENOSHOCK)
|
|
|
|
|
|| HasMoveEffect(battlerAtk, EFFECT_HEX)
|
|
|
|
|
|| HasMoveEffect(battlerAtk, EFFECT_VENOM_DRENCH)
|
|
|
|
|
|| AI_DATA->atkAbility == ABILITY_MERCILESS)
|
|
|
|
|
|| AI_DATA->abilities[battlerAtk] == ABILITY_MERCILESS)
|
|
|
|
|
*(score) += 2;
|
|
|
|
|
else
|
|
|
|
|
*(score)++;
|
|
|
|
@ -3548,7 +3484,7 @@ 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))
|
|
|
|
|
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
|
|
|
|
|
{
|
|
|
|
|
(*score)++; // burning is good
|
|
|
|
|
if (HasMoveWithSplit(battlerDef, SPLIT_PHYSICAL))
|
|
|
|
@ -3557,7 +3493,7 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
|
|
|
|
*score += 2; // burning the target to stay alive is cool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_HEX))
|
|
|
|
|
if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_HEX))
|
|
|
|
|
(*score)++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -3567,7 +3503,7 @@ 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))
|
|
|
|
|
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
|
|
|
|
|
{
|
|
|
|
|
u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk);
|
|
|
|
|
u8 defSpeed = GetBattlerTotalSpeedStat(battlerDef);
|
|
|
|
@ -3588,7 +3524,7 @@ 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))
|
|
|
|
|
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
|
|
|
|
|
*score += 2;
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
@ -3597,7 +3533,7 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
|
|
|
|
&& !(HasMoveEffect(battlerDef, EFFECT_SNORE) || HasMoveEffect(battlerDef, EFFECT_SLEEP_TALK)))
|
|
|
|
|
(*score)++;
|
|
|
|
|
|
|
|
|
|
if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(AI_DATA->battlerAtkPartner, EFFECT_HEX))
|
|
|
|
|
if (HasMoveEffect(battlerAtk, EFFECT_HEX) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_HEX))
|
|
|
|
|
(*score)++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3606,13 +3542,13 @@ 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)
|
|
|
|
|
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
|
|
|
|
|
&& AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_CONFUSION
|
|
|
|
|
&& AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_STATUS)
|
|
|
|
|
{
|
|
|
|
|
if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|
|
|
|
|
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|
|
|
|
|
|| (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && HasMoveEffect(battlerAtk, EFFECT_FLINCH_HIT)))
|
|
|
|
|
|| (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveEffect(battlerAtk, EFFECT_FLINCH_HIT)))
|
|
|
|
|
*score += 3;
|
|
|
|
|
else
|
|
|
|
|
*score += 2;
|
|
|
|
|