commit
ead52e0b48
17 changed files with 912 additions and 889 deletions
|
@ -247,33 +247,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;
|
||||
|
@ -282,7 +269,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.
|
||||
};
|
||||
|
@ -326,13 +312,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
|
||||
|
@ -597,7 +584,8 @@ 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 aiMoveOrAction[MAX_BATTLERS_COUNT];
|
||||
u8 aiChosenTarget[MAX_BATTLERS_COUNT];
|
||||
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.
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
return score; \
|
||||
}
|
||||
|
||||
|
||||
u8 ComputeBattleAiScores(u8 battler);
|
||||
void BattleAI_SetupItems(void);
|
||||
void BattleAI_SetupFlags(void);
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves);
|
||||
u8 BattleAI_ChooseMoveOrAction(void);
|
||||
void GetAiLogicData(void);
|
||||
|
||||
extern u8 sBattler_AI;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ void ClearBattlerItemEffectHistory(u8 battlerId);
|
|||
void SaveBattlerData(u8 battlerId);
|
||||
void SetBattlerData(u8 battlerId);
|
||||
void RestoreBattlerData(u8 battlerId);
|
||||
u16 GetAIChosenMove(u8 battlerId);
|
||||
|
||||
bool32 WillAIStrikeFirst(void);
|
||||
u32 GetTotalBaseStat(u32 species);
|
||||
|
@ -27,13 +28,13 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
|||
bool32 AtMaxHp(u8 battler);
|
||||
u32 GetHealthPercentage(u8 battler);
|
||||
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 consideredMove);
|
||||
bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk);
|
||||
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 +46,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);
|
||||
|
@ -81,7 +82,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);
|
||||
|
@ -144,7 +145,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);
|
||||
|
|
|
@ -15,7 +15,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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
|
||||
#define WILD_DOUBLE_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))))
|
||||
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gTrainerBattleOpponent_B == 0xFFFF))
|
||||
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
|
||||
|
||||
// Battle Outcome defines
|
||||
#define B_OUTCOME_WON 1
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
#define AI_TYPE_MOVE 4
|
||||
|
||||
// type effectiveness
|
||||
#define AI_EFFECTIVENESS_x8 320
|
||||
#define AI_EFFECTIVENESS_x4 160
|
||||
#define AI_EFFECTIVENESS_x2 80
|
||||
#define AI_EFFECTIVENESS_x1 40
|
||||
#define AI_EFFECTIVENESS_x0_5 20
|
||||
#define AI_EFFECTIVENESS_x0_25 10
|
||||
#define AI_EFFECTIVENESS_x0_125 5
|
||||
#define AI_EFFECTIVENESS_x8 7
|
||||
#define AI_EFFECTIVENESS_x4 6
|
||||
#define AI_EFFECTIVENESS_x2 5
|
||||
#define AI_EFFECTIVENESS_x1 4
|
||||
#define AI_EFFECTIVENESS_x0_5 3
|
||||
#define AI_EFFECTIVENESS_x0_25 2
|
||||
#define AI_EFFECTIVENESS_x0_125 1
|
||||
#define AI_EFFECTIVENESS_x0 0
|
||||
|
||||
// ai weather
|
||||
|
|
1154
src/battle_ai_main.c
1154
src/battle_ai_main.c
File diff suppressed because it is too large
Load diff
|
@ -18,6 +18,8 @@
|
|||
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
|
||||
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent);
|
||||
static bool8 ShouldUseItem(void);
|
||||
static bool32 AI_ShouldHeal(u32 healAmount);
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount);
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
||||
{
|
||||
|
@ -787,6 +789,25 @@ static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect)
|
|||
return AI_ITEM_NOT_RECOGNIZABLE;
|
||||
}
|
||||
|
||||
static bool32 AiExpectsToFaintPlayer(void)
|
||||
{
|
||||
bool32 canFaintPlayer;
|
||||
u32 i;
|
||||
u8 target = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
|
||||
if (gBattleStruct->aiMoveOrAction[gActiveBattler] > 3)
|
||||
return FALSE; // AI not planning to use move
|
||||
|
||||
if (GetBattlerSide(target) != GetBattlerSide(gActiveBattler)
|
||||
&& CanIndexMoveFaintTarget(gActiveBattler, target, gBattleStruct->aiMoveOrAction[gActiveBattler], 0)
|
||||
&& AI_WhoStrikesFirst(gActiveBattler, target, GetAIChosenMove(gActiveBattler)) == AI_IS_FASTER) {
|
||||
// We expect to faint the target and move first -> dont use an item
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool8 ShouldUseItem(void)
|
||||
{
|
||||
struct Pokemon *party;
|
||||
|
@ -801,6 +822,9 @@ static bool8 ShouldUseItem(void)
|
|||
|
||||
if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO)
|
||||
return FALSE;
|
||||
|
||||
if (AiExpectsToFaintPlayer())
|
||||
return FALSE;
|
||||
|
||||
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
|
||||
party = gPlayerParty;
|
||||
|
@ -842,21 +866,10 @@ static bool8 ShouldUseItem(void)
|
|||
switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2))
|
||||
{
|
||||
case AI_ITEM_FULL_RESTORE:
|
||||
if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp == 0)
|
||||
break;
|
||||
shouldUse = TRUE;
|
||||
shouldUse = AI_ShouldHeal(0);
|
||||
break;
|
||||
case AI_ITEM_HEAL_HP:
|
||||
paramOffset = GetItemEffectParamOffset(item, 4, 4);
|
||||
if (paramOffset == 0)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp == 0)
|
||||
break;
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|
||||
|| gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset])
|
||||
shouldUse = TRUE;
|
||||
shouldUse = AI_ShouldHeal(itemEffects[GetItemEffectParamOffset(item, 4, 4)]);
|
||||
break;
|
||||
case AI_ITEM_CURE_CONDITION:
|
||||
*(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0;
|
||||
|
@ -947,3 +960,32 @@ static bool8 ShouldUseItem(void)
|
|||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 AI_ShouldHeal(u32 healAmount)
|
||||
{
|
||||
bool32 shouldHeal = FALSE;
|
||||
|
||||
if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4
|
||||
|| gBattleMons[gActiveBattler].hp == 0
|
||||
|| (healAmount != 0 && gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > healAmount)) {
|
||||
// We have low enough HP to consider healing
|
||||
shouldHeal = !AI_OpponentCanFaintAiWithMod(healAmount); // if target can kill us even after we heal, why bother
|
||||
}
|
||||
|
||||
return shouldHeal;
|
||||
}
|
||||
|
||||
static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount)
|
||||
{
|
||||
u32 i;
|
||||
// Check special cases to NOT heal
|
||||
for (i = 0; i < gBattlersCount; i++) {
|
||||
if (GetBattlerSide(i) == B_SIDE_PLAYER) {
|
||||
if (CanTargetFaintAiWithMod(i, gActiveBattler, healAmount, 0)) {
|
||||
// Target is expected to faint us
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "constants/moves.h"
|
||||
#include "constants/items.h"
|
||||
|
||||
static u32 AI_GetEffectiveness(u16 multiplier);
|
||||
|
||||
// Const Data
|
||||
static const s8 sAiAbilityRatings[ABILITIES_COUNT] =
|
||||
{
|
||||
|
@ -438,9 +440,14 @@ static const u16 sOtherMoveCallingMoves[] =
|
|||
};
|
||||
|
||||
// Functions
|
||||
u16 GetAIChosenMove(u8 battlerId)
|
||||
{
|
||||
return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]);
|
||||
}
|
||||
|
||||
bool32 WillAIStrikeFirst(void)
|
||||
{
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget) == AI_IS_FASTER);
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget, AI_THINKING_STRUCT->moveConsidered) == AI_IS_FASTER);
|
||||
}
|
||||
|
||||
bool32 AI_RandLessThan(u8 val)
|
||||
|
@ -590,14 +597,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)
|
||||
|
@ -636,7 +643,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
|
|||
u32 move = gBattleResources->battleHistory->usedMoves[opposingBattler][i];
|
||||
if (gBattleMoves[move].effect == EFFECT_PROTECT && move != MOVE_ENDURE)
|
||||
return TRUE;
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler) == AI_IS_SLOWER)
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler, GetAIChosenMove(battlerAI)) == AI_IS_SLOWER)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
|
@ -658,7 +665,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 +720,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);
|
||||
|
@ -728,56 +736,69 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
|
||||
normalDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE);
|
||||
critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE);
|
||||
|
||||
if(critChance == -1)
|
||||
dmg = normalDmg;
|
||||
else
|
||||
dmg = (critDmg + normalDmg * (critChance - 1)) / critChance;
|
||||
|
||||
// Handle dynamic move damage
|
||||
switch (gBattleMoves[move].effect)
|
||||
if (gBattleMoves[move].power)
|
||||
{
|
||||
case EFFECT_LEVEL_DAMAGE:
|
||||
case EFFECT_PSYWAVE:
|
||||
dmg = gBattleMons[battlerAtk].level * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_DRAGON_RAGE:
|
||||
dmg = 40 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_SONICBOOM:
|
||||
dmg = 20 * (AI_DATA->atkAbility == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_MULTI_HIT:
|
||||
dmg *= (AI_DATA->atkAbility == ABILITY_SKILL_LINK ? 5 : 3);
|
||||
break;
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
dmg *= (AI_DATA->atkAbility == 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
|
||||
? max(2, gBattleMons[battlerDef].hp * 3 / 4)
|
||||
: max(1, gBattleMons[battlerDef].hp / 2));
|
||||
break;
|
||||
case EFFECT_FINAL_GAMBIT:
|
||||
dmg = gBattleMons[battlerAtk].hp;
|
||||
break;
|
||||
}
|
||||
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
|
||||
normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, &effectivenessMultiplier);
|
||||
critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE);
|
||||
|
||||
// Handle other multi-strike moves
|
||||
if (gBattleMoves[move].flags & FLAG_TWO_STRIKES)
|
||||
dmg *= 2;
|
||||
else if (move == MOVE_SURGING_STRIKES || (move == MOVE_WATER_SHURIKEN && gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH))
|
||||
dmg *= 3;
|
||||
if (critChance == -1)
|
||||
dmg = normalDmg;
|
||||
else
|
||||
dmg = (critDmg + normalDmg * (critChance - 1)) / critChance;
|
||||
|
||||
// Handle dynamic move damage
|
||||
switch (gBattleMoves[move].effect)
|
||||
{
|
||||
case EFFECT_LEVEL_DAMAGE:
|
||||
case EFFECT_PSYWAVE:
|
||||
dmg = gBattleMons[battlerAtk].level * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_DRAGON_RAGE:
|
||||
dmg = 40 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_SONICBOOM:
|
||||
dmg = 20 * (AI_DATA->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
|
||||
break;
|
||||
case EFFECT_MULTI_HIT:
|
||||
dmg *= (AI_DATA->abilities[battlerAtk] == ABILITY_SKILL_LINK ? 5 : 3);
|
||||
break;
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
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->abilities[battlerAtk] == ABILITY_PARENTAL_BOND
|
||||
? max(2, gBattleMons[battlerDef].hp * 3 / 4)
|
||||
: max(1, gBattleMons[battlerDef].hp / 2));
|
||||
break;
|
||||
case EFFECT_FINAL_GAMBIT:
|
||||
dmg = gBattleMons[battlerAtk].hp;
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle other multi-strike moves
|
||||
if (gBattleMoves[move].flags & FLAG_TWO_STRIKES)
|
||||
dmg *= 2;
|
||||
else if (move == MOVE_SURGING_STRIKES || (move == MOVE_WATER_SHURIKEN && gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH))
|
||||
dmg *= 3;
|
||||
|
||||
if (dmg == 0)
|
||||
dmg = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmg = 0;
|
||||
}
|
||||
|
||||
RestoreBattlerData(battlerAtk);
|
||||
RestoreBattlerData(battlerDef);
|
||||
|
||||
// convert multiper to AI_EFFECTIVENESS_xX
|
||||
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
|
||||
|
||||
return dmg;
|
||||
}
|
||||
|
@ -785,10 +806,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 +889,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 +945,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,42 +973,32 @@ u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||
|
||||
u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
||||
{
|
||||
u32 damageVar, effectivenessMultiplier;
|
||||
|
||||
gMoveResultFlags = 0;
|
||||
gCurrentMove = move;
|
||||
effectivenessMultiplier = AI_GetTypeEffectiveness(gCurrentMove, battlerAtk, battlerDef);
|
||||
return AI_GetEffectiveness(AI_GetTypeEffectiveness(move, battlerAtk, battlerDef));
|
||||
}
|
||||
|
||||
switch (effectivenessMultiplier)
|
||||
static u32 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.125):
|
||||
damageVar = AI_EFFECTIVENESS_x0_125;
|
||||
break;
|
||||
return AI_EFFECTIVENESS_x0_125;
|
||||
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;
|
||||
default:
|
||||
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;
|
||||
case UQ_4_12(8.0):
|
||||
damageVar = AI_EFFECTIVENESS_x8;
|
||||
break;
|
||||
return AI_EFFECTIVENESS_x8;
|
||||
}
|
||||
|
||||
return damageVar;
|
||||
}
|
||||
|
||||
/* Checks to see if AI will move ahead of another battler
|
||||
|
@ -995,23 +1006,25 @@ u32 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||
* AI_IS_FASTER: is user(ai) faster
|
||||
* AI_IS_SLOWER: is target faster
|
||||
*/
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered)
|
||||
{
|
||||
u32 fasterAI = 0, fasterPlayer = 0, i;
|
||||
s8 prioAI = 0;
|
||||
s8 prioPlayer = 0;
|
||||
s8 prioBattler2 = 0;
|
||||
u16 *battler2Moves = GetMovesArray(battler2);
|
||||
|
||||
// Check move priorities first.
|
||||
prioAI = GetMovePriority(battlerAI, AI_THINKING_STRUCT->moveConsidered);
|
||||
prioAI = GetMovePriority(battlerAI, moveConsidered);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBattleMons[battler2].moves[i] == 0 || gBattleMons[battler2].moves[i] == 0xFFFF)
|
||||
if (battler2Moves[i] == 0 || battler2Moves[i] == 0xFFFF)
|
||||
continue;
|
||||
|
||||
prioPlayer = GetMovePriority(battler2, gBattleMons[battler2].moves[i]);
|
||||
if (prioAI > prioPlayer)
|
||||
prioBattler2 = GetMovePriority(battler2, battler2Moves[i]);
|
||||
if (prioAI > prioBattler2)
|
||||
fasterAI++;
|
||||
else if (prioPlayer > prioAI)
|
||||
else if (prioBattler2 > prioAI)
|
||||
fasterPlayer++;
|
||||
}
|
||||
|
||||
|
@ -1025,6 +1038,8 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (prioAI > prioBattler2)
|
||||
return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
|
||||
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
||||
if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0)
|
||||
return AI_IS_FASTER;
|
||||
|
@ -1037,13 +1052,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;
|
||||
}
|
||||
|
@ -1057,7 +1072,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++)
|
||||
|
@ -1065,7 +1080,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;
|
||||
|
@ -1081,9 +1096,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;
|
||||
|
@ -1093,7 +1112,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;
|
||||
|
@ -1103,7 +1122,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;
|
||||
|
||||
|
@ -1118,9 +1137,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;
|
||||
|
@ -1177,7 +1196,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;
|
||||
|
@ -1195,7 +1214,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;
|
||||
|
@ -1211,7 +1230,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;
|
||||
|
@ -1246,14 +1265,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
|
||||
}
|
||||
|
||||
u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move)
|
||||
|
@ -1365,70 +1377,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)
|
||||
|
@ -1456,7 +1408,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))
|
||||
|
@ -1481,12 +1433,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;
|
||||
|
@ -1617,7 +1570,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))
|
||||
|
@ -1832,7 +1785,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;
|
||||
|
@ -1926,7 +1879,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++)
|
||||
{
|
||||
|
@ -1940,7 +1893,8 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32
|
|||
else if ((!IS_MOVE_STATUS(moves[i]) && gBattleMoves[moves[i]].accuracy == 0)
|
||||
|| AI_GetBattlerMoveTargetType(battlerAtk, moves[i]) & (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;
|
||||
}
|
||||
}
|
||||
|
@ -1950,7 +1904,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);
|
||||
|
||||
|
@ -1961,7 +1915,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;
|
||||
}
|
||||
}
|
||||
|
@ -2276,7 +2230,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)
|
||||
|
@ -2294,7 +2248,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)
|
||||
|
@ -2340,8 +2294,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;
|
||||
|
@ -2375,7 +2329,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)
|
||||
|
@ -2491,7 +2445,7 @@ bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 mo
|
|||
/*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost)
|
||||
return PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/
|
||||
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (!CanAIFaintTarget(battlerAtk, battlerDef, 0)) // Can't KO foe otherwise
|
||||
{
|
||||
|
@ -2502,13 +2456,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
|
||||
|
||||
|
@ -2519,7 +2473,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)
|
||||
|
@ -2562,7 +2516,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
|
||||
|
@ -2574,7 +2528,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;
|
||||
|
@ -2585,7 +2539,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;
|
||||
|
@ -2654,7 +2608,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))
|
||||
|
@ -2701,13 +2655,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
|
||||
|
@ -2746,7 +2700,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;
|
||||
|
@ -2851,7 +2805,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili
|
|||
{
|
||||
if (defAbility == ABILITY_INNER_FOCUS
|
||||
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent goes first
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent goes first
|
||||
{
|
||||
return 0; // don't try to flinch
|
||||
}
|
||||
|
@ -2871,7 +2825,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)
|
||||
|
@ -2885,11 +2839,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;
|
||||
|
||||
|
@ -2975,7 +2929,7 @@ bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveI
|
|||
|
||||
bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
{
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user goes first
|
||||
u8 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument;
|
||||
|
@ -2987,7 +2941,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
|
||||
|
@ -3002,10 +2956,10 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
|||
|
||||
bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
||||
{
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == 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;
|
||||
|
@ -3013,7 +2967,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;
|
||||
|
@ -3056,14 +3010,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]];
|
||||
}
|
||||
|
@ -3201,7 +3155,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++)
|
||||
|
@ -3258,13 +3212,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];
|
||||
|
@ -3459,10 +3414,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))
|
||||
|
@ -3471,7 +3426,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;
|
||||
|
@ -3483,7 +3438,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
|
||||
|
@ -3501,7 +3456,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;
|
||||
|
@ -3511,7 +3466,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
|
||||
|
@ -3520,13 +3475,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;
|
||||
|
@ -3542,7 +3497,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;
|
||||
|
@ -3553,7 +3508,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)++;
|
||||
|
@ -3565,7 +3520,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))
|
||||
|
@ -3574,7 +3529,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)++;
|
||||
}
|
||||
}
|
||||
|
@ -3584,7 +3539,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);
|
||||
|
@ -3605,7 +3560,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;
|
||||
|
@ -3614,7 +3569,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)++;
|
||||
}
|
||||
|
||||
|
@ -3623,13 +3578,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;
|
||||
|
|
|
@ -1535,7 +1535,7 @@ static void OpponentHandlePrintSelectionString(void)
|
|||
|
||||
static void OpponentHandleChooseAction(void)
|
||||
{
|
||||
AI_TrySwitchOrUseItem(); // TODO consider move choice first
|
||||
AI_TrySwitchOrUseItem();
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
|
@ -1559,9 +1559,8 @@ static void OpponentHandleChooseMove(void)
|
|||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
|| IsWildMonSmart())
|
||||
{
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
chosenMoveId = gBattleStruct->aiMoveOrAction[gActiveBattler];
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_WATCH:
|
||||
|
|
|
@ -1515,8 +1515,8 @@ static void PlayerPartnerHandleChooseMove(void)
|
|||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);
|
||||
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
chosenMoveId = gBattleStruct->aiMoveOrAction[gActiveBattler];
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[gActiveBattler];
|
||||
|
||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -3710,6 +3710,8 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
gMoveResultFlags = 0;
|
||||
|
||||
gRandomTurnNumber = Random();
|
||||
|
||||
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
{
|
||||
|
@ -3798,6 +3800,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();
|
||||
|
||||
|
@ -3915,6 +3918,11 @@ static void HandleTurnActionSelectionState(void)
|
|||
case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn.
|
||||
RecordedBattle_CopyBattlerMoves();
|
||||
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
|
||||
|
||||
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && IsBattlerAIControlled(gActiveBattler)) {
|
||||
gBattleStruct->aiMoveOrAction[gActiveBattler] = ComputeBattleAiScores(gActiveBattler);
|
||||
}
|
||||
break;
|
||||
case STATE_BEFORE_ACTION_CHOSEN: // Choose an action.
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
|
|
|
@ -1644,20 +1644,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)
|
||||
|
@ -1755,7 +1749,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)
|
||||
|
|
|
@ -4463,7 +4463,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
|||
BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates);
|
||||
effect++;
|
||||
}
|
||||
else if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_PRIMAL_ANY && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
{
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3);
|
||||
|
@ -4476,7 +4476,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
|||
BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates);
|
||||
effect++;
|
||||
}
|
||||
else if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_PRIMAL_ANY && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
{
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3);
|
||||
|
@ -4489,7 +4489,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
|||
BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates);
|
||||
effect++;
|
||||
}
|
||||
else if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_PRIMAL_ANY && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
{
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3);
|
||||
|
@ -4502,7 +4502,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
|||
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivates);
|
||||
effect++;
|
||||
}
|
||||
else if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_PRIMAL_ANY && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone)
|
||||
{
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3);
|
||||
|
@ -5387,9 +5387,9 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
|
|||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||||
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& !(WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM))
|
||||
&& !(gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT))
|
||||
{
|
||||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT)
|
||||
{
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_BlockedByPrimalWeatherRet;
|
||||
|
@ -8069,7 +8069,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_WEATHER_BALL:
|
||||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_ANY)
|
||||
if (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_PURSUIT:
|
||||
|
@ -8280,7 +8280,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
|
|||
break;
|
||||
case ABILITY_SAND_FORCE:
|
||||
if ((moveType == TYPE_STEEL || moveType == TYPE_ROCK || moveType == TYPE_GROUND)
|
||||
&& WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
||||
&& gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT)
|
||||
MulModifier(&modifier, UQ_4_12(1.3));
|
||||
break;
|
||||
case ABILITY_RIVALRY:
|
||||
|
@ -8889,7 +8889,7 @@ static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType,
|
|||
}
|
||||
|
||||
// sandstorm sp.def boost for rock types
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM && !usesDefStat)
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat)
|
||||
MulModifier(&modifier, UQ_4_12(1.5));
|
||||
|
||||
// The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges.
|
||||
|
@ -9062,13 +9062,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;
|
||||
|
@ -9101,6 +9099,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);
|
||||
|
@ -9132,7 +9143,7 @@ static void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 batt
|
|||
mod = UQ_4_12(2.0);
|
||||
|
||||
// B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon
|
||||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_STRONG_WINDS)
|
||||
if (gBattleWeather & B_WEATHER_STRONG_WINDS && WEATHER_HAS_EFFECT)
|
||||
{
|
||||
if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0))
|
||||
mod = UQ_4_12(1.0);
|
||||
|
@ -10089,10 +10100,7 @@ u16 GetUsedHeldItem(u8 battler)
|
|||
|
||||
bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags)
|
||||
{
|
||||
if (!WEATHER_HAS_EFFECT)
|
||||
return FALSE;
|
||||
|
||||
if (gBattleWeather & weatherFlags)
|
||||
if (gBattleWeather & weatherFlags && WEATHER_HAS_EFFECT)
|
||||
{
|
||||
// given weather is active -> check if its sun, rain against utility umbrella ( since only 1 weather can be active at once)
|
||||
if (gBattleWeather & (B_WEATHER_SUN | B_WEATHER_RAIN) && GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_UTILITY_UMBRELLA)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue