cache ai damage, effectiveness calcs

This commit is contained in:
ghoulslash 2022-01-13 11:28:27 -05:00
parent 0441682af5
commit 035b2332af
12 changed files with 690 additions and 759 deletions

View file

@ -243,33 +243,20 @@ struct AI_SavedBattleMon
struct AiLogicData
{
//attacker data
u16 atkAbility;
u16 atkItem;
u16 atkHoldEffect;
u8 atkParam;
u16 atkSpecies;
// target data
u16 defAbility;
u16 defItem;
u16 defHoldEffect;
u8 defParam;
u16 defSpecies;
// attacker partner data
u8 battlerAtkPartner;
u16 abilities[MAX_BATTLERS_COUNT];
u16 items[MAX_BATTLERS_COUNT];
u16 holdEffects[MAX_BATTLERS_COUNT];
u8 holdEffectParams[MAX_BATTLERS_COUNT];
u16 predictedMoves[MAX_BATTLERS_COUNT];
u8 hpPercents[MAX_BATTLERS_COUNT];
u16 partnerMove;
u16 atkPartnerAbility;
u16 atkPartnerHoldEffect;
bool32 targetSameSide;
// target partner data
u8 battlerDefPartner;
u16 defPartnerAbility;
u16 defPartnerHoldEffect;
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 moveLimitations[MAX_BATTLERS_COUNT];
};
struct AI_ThinkingStruct
{
struct AiLogicData data;
u8 aiState;
u8 movesetIndex;
u16 moveConsidered;
@ -278,7 +265,6 @@ struct AI_ThinkingStruct
u32 aiFlags;
u8 aiAction;
u8 aiLogicId;
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, move
struct AI_SavedBattleMon saved[4];
bool8 switchMon; // Because all available moves have no/little effect.
};
@ -321,13 +307,14 @@ struct BattleResources
struct BattleCallbacksStack* battleCallbackStack;
struct StatsArray* beforeLvlUp;
struct AI_ThinkingStruct *ai;
struct AiLogicData *aiData;
struct BattleHistory *battleHistory;
u8 bufferA[MAX_BATTLERS_COUNT][0x200];
u8 bufferB[MAX_BATTLERS_COUNT][0x200];
};
#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai))
#define AI_DATA ((struct AiLogicData *)(&gBattleResources->ai->data))
#define AI_DATA ((struct AiLogicData *)(gBattleResources->aiData))
#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory))
struct BattleResults
@ -593,7 +580,6 @@ struct BattleStruct
bool8 spriteIgnore0Hp;
struct Illusion illusion[MAX_BATTLERS_COUNT];
s8 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier
s32 aiSimulatedDamage[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, move to make debugging easier
u8 soulheartBattlerId;
u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles.
bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once.

View file

@ -24,6 +24,7 @@ void BattleAI_SetupItems(void);
void BattleAI_SetupFlags(void);
void BattleAI_SetupAIData(u8 defaultScoreMoves);
u8 BattleAI_ChooseMoveOrAction(void);
void GetAiLogicData(void);
extern u8 sBattler_AI;

View file

@ -33,7 +33,7 @@ bool32 CanMoveFaintBattler(u16 move, u8 battlerDef, u8 battlerAtk, u8 nHits);
bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgMod);
s32 AI_GetAbility(u32 battlerId);
u16 AI_GetHoldEffect(u32 battlerId);
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);
bool32 DoesBattlerIgnoreAbilityChecks(u16 atkAbility, u16 move);
bool32 AI_WeatherHasEffect(void);
bool32 CanAIFaintTarget(u8 battlerAtk, u8 battlerDef, u8 numHits);
@ -45,7 +45,7 @@ bool32 HasDamagingMoveOfType(u8 battlerId, u8 type);
u32 GetBattlerSecondaryDamage(u8 battlerId);
bool32 BattlerWillFaintFromWeather(u8 battler, u16 ability);
bool32 BattlerWillFaintFromSecondaryDamage(u8 battler, u16 ability);
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);
bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveIndex);
u16 GetBattlerSideSpeedAverage(u8 battler);
bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage);
@ -80,7 +80,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility);
// move checks
bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect);
bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split);
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef);
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness);
u8 GetMoveDamageResult(u16 move);
u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef);
u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
@ -143,7 +143,7 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move);
bool32 IsWakeupTurn(u8 battler);
// partner logic
u16 GetAllyChosenMove(void);
u16 GetAllyChosenMove(u8 battlerId);
bool32 IsValidDoubleBattle(u8 battlerAtk);
bool32 IsTargetingPartner(u8 battlerAtk, u8 battlerDef);
bool32 DoesPartnerHaveSameMoveEffect(u8 battlerAtkPartner, u8 battlerDef, u16 move, u16 partnerMove);

View file

@ -16,7 +16,7 @@ struct StatFractions
s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility);
s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
u8 GetBattlerTurnOrderNum(u8 battlerId);
bool32 NoAliveMonsForEitherParty(void);
void SetMoveEffect(bool32 primary, u32 certain);

View file

@ -129,6 +129,7 @@ bool32 IsBattlerAlive(u8 battlerId);
u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move);
u32 GetBattlerWeight(u8 battlerId);
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier);
u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities);
u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
u16 GetTypeModifier(u8 atkType, u8 defType);

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -714,7 +714,7 @@ void CB2_BattleDebugMenu(void)
static void PutMovesPointsText(struct BattleDebugMenu *data)
{
u32 i, j, count;
u32 i, j, count, battlerDef;
u8 *text = malloc(0x50);
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
@ -727,13 +727,14 @@ static void PutMovesPointsText(struct BattleDebugMenu *data)
{
if (data->aiIconSpriteIds[j] == 0xFF)
continue;
battlerDef = gSprites[data->aiIconSpriteIds[j]].data[0];
ConvertIntToDecimalStringN(text,
gBattleStruct->aiFinalScore[data->aiBattlerId][gSprites[data->aiIconSpriteIds[j]].data[0]][i],
gBattleStruct->aiFinalScore[data->aiBattlerId][battlerDef][i],
STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, 1, text, 83 + count * 54, i * 15, 0, NULL);
ConvertIntToDecimalStringN(text,
gBattleStruct->aiSimulatedDamage[data->aiBattlerId][gSprites[data->aiIconSpriteIds[j]].data[0]][i],
AI_DATA->simulatedDmg[data->aiBattlerId][battlerDef][i],
STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, 1, text, 110 + count * 54, i * 15, 0, NULL);

View file

@ -3698,6 +3698,8 @@ static void TryDoEventsBeforeFirstTurn(void)
gMoveResultFlags = 0;
gRandomTurnNumber = Random();
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
{
@ -3786,6 +3788,7 @@ void BattleTurnPassed(void)
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
gBattleMainFunc = HandleTurnActionSelectionState;
gRandomTurnNumber = Random();

View file

@ -1657,20 +1657,14 @@ static bool32 AccuracyCalcHelper(u16 move)
return FALSE;
}
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move)
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
{
u32 calc, moveAcc, atkHoldEffect, atkParam, defHoldEffect, defParam, atkAbility, defAbility;
u32 calc, moveAcc;
s8 buff, accStage, evasionStage;
u8 atkParam = GetBattlerHoldEffectParam(battlerAtk);
u8 defParam = GetBattlerHoldEffectParam(battlerDef);
atkAbility = GetBattlerAbility(battlerAtk);
atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
atkParam = GetBattlerHoldEffectParam(battlerAtk);
defAbility = GetBattlerAbility(battlerDef);
defHoldEffect = GetBattlerHoldEffect(battlerDef, TRUE);
defParam = GetBattlerHoldEffectParam(battlerDef);
gPotentialItemEffectBattler = battlerDef;
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE)
@ -1767,7 +1761,8 @@ static void Cmd_accuracycheck(void)
return;
// final calculation
if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move))
if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget),
GetBattlerHoldEffect(gBattlerAttacker, TRUE), GetBattlerHoldEffect(gBattlerTarget, TRUE)))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY)

View file

@ -8957,13 +8957,11 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move
return dmg;
}
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags)
static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower,
bool32 isCrit, bool32 randomFactor, bool32 updateFlags, u16 typeEffectivenessModifier)
{
s32 dmg;
u16 typeEffectivenessModifier;
typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags);
// Don't calculate damage if the move has no effect on target.
if (typeEffectivenessModifier == UQ_4_12(0))
return 0;
@ -8996,6 +8994,19 @@ s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32
return dmg;
}
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags)
{
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor,
updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags));
}
// for AI - get move damage and effectiveness with one function call
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier)
{
*typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE);
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE, *typeEffectivenessModifier);
}
static void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities)
{
u16 mod = GetTypeModifier(moveType, defType);

View file

@ -26,6 +26,7 @@ void AllocateBattleResources(void)
gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack));
gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp));
gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai));
gBattleResources->aiData = AllocZeroed(sizeof(*gBattleResources->aiData));
gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory));
gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
@ -57,6 +58,7 @@ void FreeBattleResources(void)
FREE_AND_SET_NULL(gBattleResources->battleCallbackStack);
FREE_AND_SET_NULL(gBattleResources->beforeLvlUp);
FREE_AND_SET_NULL(gBattleResources->ai);
FREE_AND_SET_NULL(gBattleResources->aiData);
FREE_AND_SET_NULL(gBattleResources->battleHistory);
FREE_AND_SET_NULL(gBattleResources);