mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 19:47:35 +01:00
Merge branch 'battle_engine' of https://github.com/rh-hideout/pokeemerald-expansion into gen_8_moves_batch2
This commit is contained in:
commit
499fb8071d
@ -242,7 +242,7 @@ struct WishFutureKnock
|
||||
u8 futureSightAttacker[MAX_BATTLERS_COUNT];
|
||||
u16 futureSightMove[MAX_BATTLERS_COUNT];
|
||||
u8 wishCounter[MAX_BATTLERS_COUNT];
|
||||
u8 wishMonId[MAX_BATTLERS_COUNT];
|
||||
u8 wishPartyId[MAX_BATTLERS_COUNT];
|
||||
u8 weatherDuration;
|
||||
u8 knockedOffMons[2]; // Each battler is represented by a bit. The array entry is dependent on the battler's side.
|
||||
};
|
||||
|
@ -3,9 +3,10 @@
|
||||
|
||||
// return values for BattleAI_ChooseMoveOrAction
|
||||
// 0 - 3 are move idx
|
||||
#define AI_CHOICE_FLEE 4
|
||||
#define AI_CHOICE_WATCH 5
|
||||
#define AI_CHOICE_SWITCH 7
|
||||
#define AI_CHOICE_FLEE 4
|
||||
#define AI_CHOICE_WATCH 5
|
||||
#define AI_CHOICE_SWITCH 7
|
||||
#define AI_CHOICE_USE_ITEM 8
|
||||
|
||||
#define RETURN_SCORE_PLUS(val) \
|
||||
{ \
|
||||
|
@ -32,7 +32,7 @@ enum {
|
||||
};
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
void AI_TrySwitchOrUseItem(void);
|
||||
u8 AI_TrySwitchOrUseItem(u8 currAction);
|
||||
u8 GetMostSuitableMonToSwitchInto(void);
|
||||
bool32 ShouldSwitch(void);
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
#ifndef GUARD_BATTLE_AI_UTIL_H
|
||||
#define GUARD_BATTLE_AI_UTIL_H
|
||||
|
||||
// for IsAiFaster
|
||||
#define AI_CHECK_FASTER 0 // if_user_faster
|
||||
#define AI_CHECK_SLOWER 1 // if_target_faster
|
||||
// for AI_WhoStrikesFirst
|
||||
#define AI_IS_FASTER 0
|
||||
#define AI_IS_SLOWER 1
|
||||
|
||||
#define FOE(battler) ((battler ^ BIT_SIDE) & BIT_SIDE)
|
||||
|
||||
@ -21,12 +21,13 @@ void SaveBattlerData(u8 battlerId);
|
||||
void SetBattlerData(u8 battlerId);
|
||||
void RestoreBattlerData(u8 battlerId);
|
||||
|
||||
bool32 WillAIStrikeFirst(void);
|
||||
u32 GetTotalBaseStat(u32 species);
|
||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||
bool32 AtMaxHp(u8 battler);
|
||||
u32 GetHealthPercentage(u8 battler);
|
||||
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
||||
bool32 IsAiFaster(u8 battler);
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2);
|
||||
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);
|
||||
@ -52,10 +53,13 @@ bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent);
|
||||
bool32 ShouldSetScreen(u8 battlerAtk, u8 battlerDef, u16 moveEffect);
|
||||
bool32 ShouldPivot(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u8 moveIndex);
|
||||
bool32 IsRecycleEncouragedItem(u16 item);
|
||||
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item);
|
||||
bool32 IsStatBoostingBerry(u16 item);
|
||||
bool32 CanKnockOffItem(u8 battler, u16 item);
|
||||
bool32 IsAbilityOfRating(u16 ability, s8 rating);
|
||||
s8 GetAbilityRating(u16 ability);
|
||||
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
|
||||
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move);
|
||||
|
||||
// stat stage checks
|
||||
bool32 AnyStatIsRaised(u8 battlerId);
|
||||
|
@ -21,6 +21,8 @@ struct UnknownPokemonStruct4
|
||||
/*0x1D*/ u8 language;
|
||||
};
|
||||
|
||||
struct ChooseMoveStruct;
|
||||
|
||||
#define TYPE_NAME_LENGTH 6
|
||||
#define ABILITY_NAME_LENGTH 12
|
||||
|
||||
@ -70,6 +72,7 @@ void RunBattleScriptCommands(void);
|
||||
bool8 TryRunFromBattle(u8 battlerId);
|
||||
void SpecialStatusesClear(void);
|
||||
void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk);
|
||||
void FillChooseMoveStruct(struct ChooseMoveStruct *moveInfo);
|
||||
|
||||
extern struct UnknownPokemonStruct4 gMultiPartnerParty[MULTI_PARTY_SIZE];
|
||||
|
||||
|
@ -92,6 +92,9 @@
|
||||
#define GEN_8 5
|
||||
#endif
|
||||
|
||||
// Mega Evolution settings
|
||||
#define B_MEGA_EVO_TURN_ORDER GEN_7 // In Gen7, a Pokémon's Speed after Mega Evolution is used to determine turn order, not its Speed before.
|
||||
|
||||
// Calculation settings
|
||||
#define B_CRIT_CHANCE GEN_7 // Chances of a critical hit landing. See CalcCritChanceStage.
|
||||
#define B_CRIT_MULTIPLIER GEN_7 // In Gen6+, critical hits multiply damage by 1.5 instead of 2.
|
||||
@ -156,6 +159,7 @@
|
||||
#define B_SKILL_SWAP GEN_7 // In Gen4+, Skill Swap triggers switch-in abilities after use.
|
||||
#define B_BRICK_BREAK GEN_7 // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune.
|
||||
#define B_WISH_HP_SOURCE GEN_7 // In Gen5+, Wish heals half of the user's max HP instead of the target's.
|
||||
#define B_RAMPAGE_CANCELLING GEN_7 // In Gen5+, a failed Thrash, etc, will cancel except on its last turn.
|
||||
|
||||
// Ability settings
|
||||
#define B_ABILITY_WEATHER GEN_7 // In Gen6+, ability-induced weather lasts 5 turns. Before, it lasted until the battle ended or until it was changed by a move or a different weather-affecting ability.
|
||||
|
@ -209,6 +209,10 @@ u8 BattleAI_ChooseMoveOrAction(void)
|
||||
ret = ChooseMoveOrAction_Singles();
|
||||
else
|
||||
ret = ChooseMoveOrAction_Doubles();
|
||||
|
||||
AI_THINKING_STRUCT->movesetIndex = ret;
|
||||
AI_THINKING_STRUCT->moveConsidered = gBattleMons[sBattler_AI].moves[AI_THINKING_STRUCT->movesetIndex];
|
||||
ret = AI_TrySwitchOrUseItem(ret);
|
||||
|
||||
// Clear protect structures, some flags may be set during AI calcs
|
||||
// e.g. pranksterElevated from GetMovePriority
|
||||
@ -278,47 +282,6 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
return AI_CHOICE_WATCH;
|
||||
|
||||
gActiveBattler = sBattler_AI;
|
||||
// If can switch.
|
||||
if (CountUsablePartyMons(sBattler_AI) > 0
|
||||
&& !IsAbilityPreventingEscape(sBattler_AI)
|
||||
&& !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
|
||||
&& !(gStatuses3[gActiveBattler] & STATUS3_ROOTED)
|
||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
|
||||
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
|
||||
{
|
||||
// Consider switching if all moves are worthless to use.
|
||||
if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak.
|
||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
|
||||
{
|
||||
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (AI_THINKING_STRUCT->score[i] > cap)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
return AI_CHOICE_SWITCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Consider switching if your mon with truant is bodied by Protect spam.
|
||||
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
|
||||
if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT
|
||||
&& IsTruantMonVulnerable(sBattler_AI, gBattlerTarget)
|
||||
&& gDisableStructs[sBattler_AI].truantCounter
|
||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
|
||||
{
|
||||
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
return AI_CHOICE_SWITCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
numOfBestMoves = 1;
|
||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||
consideredMoveArray[0] = 0;
|
||||
@ -559,7 +522,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
|
||||
// check off screen
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) != 1)
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
|
||||
|
||||
// check if negates type
|
||||
@ -1258,7 +1221,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to disable
|
||||
@ -1278,7 +1241,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)
|
||||
&& !DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
|
||||
score -= 10; // no anticipated move to encore
|
||||
@ -1392,22 +1355,22 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_SANDSTORM:
|
||||
if (gBattleWeather & WEATHER_SANDSTORM_ANY //TODO | WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (WEATHER_SANDSTORM_ANY | WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_SUNNY_DAY:
|
||||
if (gBattleWeather & WEATHER_SUN_ANY //TODO | WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (WEATHER_SUN_ANY | WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_RAIN_DANCE:
|
||||
if (gBattleWeather & WEATHER_RAIN_ANY //TODO | WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
if (gBattleWeather & WEATHER_HAIL_ANY //TODO | WEATHER_PRIMAL_ANY)
|
||||
if (gBattleWeather & (WEATHER_HAIL_ANY | WEATHER_PRIMAL_ANY)
|
||||
|| PartnerMoveEffectIsWeather(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove))
|
||||
score -= 8;
|
||||
break;
|
||||
@ -1719,7 +1682,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
case EFFECT_MIMIC:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (gLastMoves[battlerDef] == MOVE_NONE
|
||||
|| gLastMoves[battlerDef] == 0xFFFF)
|
||||
@ -1876,7 +1839,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // partner is going to set up hazards
|
||||
&& GetWhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerAtk, FALSE)) // partner is going to set up before the potential Defog
|
||||
&& AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerAtk) == AI_IS_FASTER) // partner is going to set up before the potential Defog
|
||||
{
|
||||
score -= 10;
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
@ -1914,7 +1877,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SEMI_INVULNERABLE:
|
||||
if (predictedMove != MOVE_NONE
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& gBattleMoves[predictedMove].effect == EFFECT_SEMI_INVULNERABLE)
|
||||
score -= 10; // Don't Fly/dig/etc if opponent is going to fly/dig/etc after you
|
||||
|
||||
@ -2095,7 +2058,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_ME_FIRST:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
score -= 10; // Target is predicted to go first, Me First will fail
|
||||
else
|
||||
return AI_CheckBadMove(battlerAtk, battlerDef, predictedMove, score);
|
||||
@ -2282,7 +2245,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_ELECTRIFY:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER
|
||||
//|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
@ -2311,7 +2274,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER)
|
||||
instructedMove = predictedMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerDef];
|
||||
@ -2351,21 +2314,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (!isDoubleBattle
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (!IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| !isDoubleBattle
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
|| PartnerMoveIsSameAsAttacker(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_SUCKER_PUNCH:
|
||||
if (predictedMove != MOVE_NONE)
|
||||
{
|
||||
if (IS_MOVE_STATUS(predictedMove) || GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent going first
|
||||
if (IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent going first
|
||||
score -= 10;
|
||||
}
|
||||
break;
|
||||
@ -2413,11 +2376,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score--;
|
||||
break;
|
||||
case EFFECT_VITAL_THROW:
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && GetHealthPercentage(battlerAtk) < 40)
|
||||
if (WillAIStrikeFirst() && GetHealthPercentage(battlerAtk) < 40)
|
||||
score--; // don't want to move last
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1 // opponent should go first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER // Opponent should go first
|
||||
|| GetHealthPercentage(battlerAtk) > 50)
|
||||
score -= 4;
|
||||
break;
|
||||
@ -2468,7 +2431,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
|
||||
{
|
||||
// this move can faint the target
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 || GetMovePriority(battlerAtk, move) > 0)
|
||||
if (!WillAIStrikeFirst() || GetMovePriority(battlerAtk, move) > 0)
|
||||
score += 4; // we go first or we're using priority move
|
||||
else
|
||||
score += 2;
|
||||
@ -2516,7 +2479,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
|
||||
//AI_TryToFaint_CheckIfDanger
|
||||
if (!IsAiFaster(AI_CHECK_FASTER) && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
if (!WillAIStrikeFirst() && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
{ // AI_TryToFaint_Danger
|
||||
if (GetMoveDamageResult(move) != MOVE_POWER_BEST)
|
||||
score--;
|
||||
@ -2566,7 +2529,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// Ally decided to use Frost Breath on us. we must have Anger Point as our ability
|
||||
if (AI_DATA->atkAbility == ABILITY_ANGER_POINT)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1) // partner moving first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER) // Partner moving first
|
||||
{
|
||||
// discourage raising our attack since it's about to be maxed out
|
||||
if (IsAttackBoostMoveEffect(effect))
|
||||
@ -2833,7 +2796,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_INSTRUCT:
|
||||
{
|
||||
u16 instructedMove;
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 0)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_FASTER)
|
||||
instructedMove = AI_DATA->partnerMove;
|
||||
else
|
||||
instructedMove = gLastMoves[battlerAtkPartner];
|
||||
@ -2847,8 +2810,8 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (GetWhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), TRUE) == 1 // opponent mon 1 goes before partner
|
||||
|| GetWhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), TRUE) == 1) // opponent mon 2 goes before partner
|
||||
if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner) == AI_IS_SLOWER) // Opponent mon 1 goes before partner
|
||||
|| AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)) == AI_IS_SLOWER)) // Opponent mon 2 goes before partner
|
||||
{
|
||||
if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT)
|
||||
break; // These moves need to go last
|
||||
@ -2874,7 +2837,9 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_EARTHQUAKE:
|
||||
case EFFECT_MAGNITUDE:
|
||||
if (!IsBattlerGrounded(battlerAtkPartner)
|
||||
|| (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1 && IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
|
||||
|| (IsBattlerGrounded(battlerAtkPartner)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner) == AI_IS_SLOWER
|
||||
&& IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
|
||||
score += 2;
|
||||
else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE)
|
||||
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC)
|
||||
@ -2925,9 +2890,9 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// check already dead
|
||||
if (!IsBattlerIncapacitated(battlerDef, AI_DATA->defAbility)
|
||||
&& CanTargetFaintAi(battlerAtk, battlerDef)
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent should go first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent should go first
|
||||
{
|
||||
if (atkPriority > 0)
|
||||
if (atkPriority > 0)
|
||||
score++;
|
||||
else
|
||||
score--;
|
||||
@ -2969,7 +2934,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
case ABILITY_MOXIE:
|
||||
case ABILITY_BEAST_BOOST:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first
|
||||
case ABILITY_CHILLING_NEIGH:
|
||||
case ABILITY_GRIM_NEIGH:
|
||||
case ABILITY_AS_ONE_ICE_RIDER:
|
||||
case ABILITY_AS_ONE_SHADOW_RIDER:
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker should go first
|
||||
{
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
score += 8; // prioritize killing target for stat boost
|
||||
@ -2980,7 +2949,6 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// move effect checks
|
||||
switch (moveEffect)
|
||||
{
|
||||
|
||||
case EFFECT_HIT:
|
||||
break;
|
||||
case EFFECT_SLEEP:
|
||||
@ -3042,7 +3010,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPEED_UP:
|
||||
case EFFECT_SPEED_UP_2:
|
||||
if (IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (!WillAIStrikeFirst())
|
||||
{
|
||||
if (!AI_RandLessThan(70))
|
||||
score += 3;
|
||||
@ -3140,7 +3108,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_SPEED_DOWN:
|
||||
case EFFECT_SPEED_DOWN_2:
|
||||
if (IsAiFaster(AI_CHECK_FASTER))
|
||||
if (WillAIStrikeFirst())
|
||||
score -= 3;
|
||||
else if (!AI_RandLessThan(70))
|
||||
score += 2;
|
||||
@ -3243,6 +3211,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_MULTI_HIT:
|
||||
case EFFECT_DOUBLE_HIT:
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
if (AI_MoveMakesContact(AI_DATA->atkAbility, AI_DATA->atkHoldEffect, move)
|
||||
&& AI_DATA->atkAbility != ABILITY_MAGIC_GUARD
|
||||
&& AI_DATA->defHoldEffect == HOLD_EFFECT_ROCKY_HELMET)
|
||||
score -= 2;
|
||||
break;
|
||||
case EFFECT_CONVERSION:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, gBattleMoves[gBattleMons[battlerAtk].moves[0]].type))
|
||||
@ -3381,7 +3353,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 2;
|
||||
break;
|
||||
case EFFECT_SPEED_DOWN_HIT:
|
||||
if (IsAiFaster(AI_CHECK_FASTER))
|
||||
if (WillAIStrikeFirst())
|
||||
score -= 2;
|
||||
else if (!AI_RandLessThan(70))
|
||||
score++;
|
||||
@ -3413,7 +3385,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_MIMIC:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0)
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF)
|
||||
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score);
|
||||
@ -3472,7 +3444,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gDisableStructs[battlerDef].disableTimer == 0
|
||||
&& (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // AI goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // AI goes first
|
||||
{
|
||||
if (gLastMoves[battlerDef] != MOVE_NONE
|
||||
&& gLastMoves[battlerDef] != 0xFFFF)
|
||||
@ -3522,11 +3494,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_SPEED_UP_HIT:
|
||||
if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY && IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (AI_DATA->atkAbility == ABILITY_SERENE_GRACE && AI_DATA->defAbility != ABILITY_CONTRARY && !WillAIStrikeFirst())
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_DESTINY_BOND:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_SPITE:
|
||||
@ -3774,7 +3746,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& AI_DATA->atkAbility != ABILITY_CONTRARY
|
||||
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
score += 9;
|
||||
else
|
||||
score += 3;
|
||||
@ -3819,7 +3791,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
if (predictedMove != MOVE_NONE && !isDoubleBattle)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].effect == EFFECT_EXPLOSION
|
||||
|| gBattleMoves[predictedMove].effect == EFFECT_PROTECT)
|
||||
@ -3886,7 +3858,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->defAbility)
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // Target goes first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Target goes first
|
||||
break; // Don't use if the attract won't have a change to activate
|
||||
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
|
||||
@ -3931,7 +3903,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
if (IsHazardMoveEffect(gBattleMoves[AI_DATA->partnerMove].effect) // Partner is going to set up hazards
|
||||
&& GetWhoStrikesFirst(battlerAtk, AI_DATA->battlerAtkPartner, TRUE) == 1) // Partner going first
|
||||
&& AI_WhoStrikesFirst(battlerAtk, AI_DATA->battlerAtkPartner) == AI_IS_SLOWER) // Partner going first
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
}
|
||||
|
||||
@ -4101,6 +4073,18 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
if (IsRecycleEncouragedItem(GetUsedHeldItem(battlerAtk)))
|
||||
score++;
|
||||
if (AI_DATA->atkAbility == ABILITY_RIPEN)
|
||||
{
|
||||
u16 item = GetUsedHeldItem(battlerAtk);
|
||||
u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item);
|
||||
|
||||
if (IsStatBoostingBerry(item) && atkHpPercent > 60)
|
||||
score++;
|
||||
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
||||
&& ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
||||
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
||||
score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
||||
}
|
||||
break;
|
||||
case EFFECT_BRICK_BREAK:
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_REFLECT)
|
||||
@ -4408,13 +4392,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_THROAT_CHOP:
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0)
|
||||
if (predictedMove != MOVE_NONE && TestMoveFlags(predictedMove, FLAG_SOUND) && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
score += 3; // Ai goes first and predicts the target will use a sound move
|
||||
else if (TestMoveFlagsInMoveset(battlerDef, FLAG_SOUND))
|
||||
score += 3;
|
||||
break;
|
||||
case EFFECT_HEAL_BLOCK:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMoveEffect(gBattleMoves[predictedMove].effect))
|
||||
score += 3; // Try to cancel healing move
|
||||
else if (HasHealingEffect(battlerDef) || AI_DATA->defHoldEffect == HOLD_EFFECT_LEFTOVERS
|
||||
|| (AI_DATA->defHoldEffect == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON)))
|
||||
@ -4450,7 +4434,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (isDoubleBattle
|
||||
&& GetWhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerDef, TRUE) == 1) // Attacker partner wouldn't go before target
|
||||
&& AI_WhoStrikesFirst(AI_DATA->battlerAtkPartner, battlerDef) == AI_IS_SLOWER) // Attacker partner wouldn't go before target
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_TAILWIND:
|
||||
@ -4472,8 +4456,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (IsBattlerGrounded(battlerAtk) && HasDamagingMoveOfType(battlerDef, TYPE_ELECTRIC)
|
||||
&& !(AI_GetTypeEffectiveness(MOVE_EARTHQUAKE, battlerDef, battlerAtk) == AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
|
||||
{
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
{
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (gBattleMoves[predictedMove].type == TYPE_GROUND)
|
||||
score += 3; // Cause the enemy's move to fail
|
||||
break;
|
||||
@ -4487,7 +4471,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_CAMOUFLAGE:
|
||||
if (predictedMove != MOVE_NONE && GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 // Attacker goes first
|
||||
if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER // Attacker goes first
|
||||
&& !IS_MOVE_STATUS(move) && AI_GetTypeEffectiveness(predictedMove, battlerDef, battlerAtk) != AI_EFFECTIVENESS_x0)
|
||||
score++;
|
||||
break;
|
||||
@ -4530,7 +4514,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_FLAIL:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Ai goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Ai goes first
|
||||
{
|
||||
if (GetHealthPercentage(battlerAtk) < 20)
|
||||
score++;
|
||||
@ -4572,7 +4556,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 2;
|
||||
break;
|
||||
case EFFECT_ENDEAVOR:
|
||||
if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // Opponent faster
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent faster
|
||||
{
|
||||
if (GetHealthPercentage(battlerAtk) < 40)
|
||||
score++;
|
||||
@ -4603,7 +4587,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
return score;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
|
||||
&& GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER
|
||||
&& CanTargetFaintAi(battlerDef, battlerAtk)
|
||||
&& GetMovePriority(battlerAtk, move) == 0)
|
||||
{
|
||||
|
@ -12,11 +12,82 @@
|
||||
#include "constants/item_effects.h"
|
||||
#include "constants/items.h"
|
||||
#include "constants/moves.h"
|
||||
#include "constants/battle_ai.h"
|
||||
|
||||
// this file's functions
|
||||
static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng);
|
||||
static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent);
|
||||
static bool8 ShouldUseItem(void);
|
||||
static bool32 AI_ShouldHeal(u8 healAmount);
|
||||
|
||||
// Functions
|
||||
u8 AI_TrySwitchOrUseItem(u8 currAction)
|
||||
{
|
||||
struct Pokemon *party;
|
||||
u8 battlerIn1, battlerIn2;
|
||||
s32 firstId;
|
||||
s32 lastId; // + 1
|
||||
u8 battlerIdentity = GetBattlerPosition(gActiveBattler);
|
||||
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
||||
return currAction;
|
||||
|
||||
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
|
||||
party = gPlayerParty; // Player's partner
|
||||
else
|
||||
party = gEnemyParty; // Enemy trainer
|
||||
|
||||
// Switching logic
|
||||
if (ShouldSwitch())
|
||||
{
|
||||
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
|
||||
{
|
||||
s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
|
||||
if (monToSwitchId == PARTY_SIZE)
|
||||
{
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
|
||||
battlerIn2 = battlerIn1;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
|
||||
battlerIn2 = GetBattlerAtPosition(battlerIdentity ^ BIT_FLANK);
|
||||
}
|
||||
|
||||
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
|
||||
|
||||
for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++)
|
||||
{
|
||||
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
|
||||
continue;
|
||||
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId;
|
||||
}
|
||||
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler);
|
||||
return AI_CHOICE_SWITCH;
|
||||
}
|
||||
|
||||
// Item Logic
|
||||
if (ShouldUseItem())
|
||||
return AI_CHOICE_USE_ITEM;
|
||||
|
||||
return currAction;
|
||||
}
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
||||
{
|
||||
@ -39,11 +110,52 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
|
||||
|
||||
static bool8 ShouldSwitchIfAllBadMoves(void)
|
||||
{
|
||||
if (gBattleResources->ai->switchMon)
|
||||
u32 i;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
|
||||
{
|
||||
gBattleResources->ai->switchMon = 0;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
// TODO double battle bad move switching logic
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single battle. gActiveBattler is the enemy's battler id
|
||||
if (GetTotalBaseStat(gBattleMons[gActiveBattler].species) >= 310 // Mon is not weak.
|
||||
&& gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
|
||||
{
|
||||
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (AI_THINKING_STRUCT->score[i] > cap)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// Consider switching if your mon with truant is bodied by Protect spam.
|
||||
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
|
||||
if (GetBattlerAbility(gActiveBattler) == ABILITY_TRUANT
|
||||
&& IsTruantMonVulnerable(gActiveBattler, gBattlerTarget)
|
||||
&& gDisableStructs[gActiveBattler].truantCounter
|
||||
&& gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2)
|
||||
{
|
||||
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AI_THINKING_STRUCT->switchMon)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = FALSE;
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
@ -58,7 +170,6 @@ static bool8 ShouldSwitchIfPerishSong(void)
|
||||
&& gDisableStructs[gActiveBattler].perishSongTimer == 0)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
@ -125,7 +236,6 @@ static bool8 ShouldSwitchIfWonderGuard(void)
|
||||
{
|
||||
// We found a mon.
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -216,7 +326,6 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
|
||||
{
|
||||
// we found a mon.
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -236,13 +345,11 @@ static bool8 ShouldSwitchIfNaturalCure(void)
|
||||
if ((gLastLandedMoves[gActiveBattler] == 0 || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -254,7 +361,6 @@ static bool8 ShouldSwitchIfNaturalCure(void)
|
||||
if (Random() & 1)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -407,7 +513,6 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent)
|
||||
if (AI_GetTypeEffectiveness(move, gActiveBattler, battlerIn1) >= UQ_4_12(2.0) && Random() % moduloPercent == 0)
|
||||
{
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -436,6 +541,11 @@ bool32 ShouldSwitch(void)
|
||||
return FALSE;
|
||||
|
||||
availableToSwitch = 0;
|
||||
AI_THINKING_STRUCT->switchMon = FALSE;
|
||||
|
||||
if (CountUsablePartyMons(gActiveBattler) == 0) // No pokemon to switch to
|
||||
return FALSE;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
battlerIn1 = gActiveBattler;
|
||||
@ -500,73 +610,6 @@ bool32 ShouldSwitch(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void AI_TrySwitchOrUseItem(void)
|
||||
{
|
||||
struct Pokemon *party;
|
||||
u8 battlerIn1, battlerIn2;
|
||||
s32 firstId;
|
||||
s32 lastId; // + 1
|
||||
u8 battlerIdentity = GetBattlerPosition(gActiveBattler);
|
||||
|
||||
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
|
||||
party = gPlayerParty;
|
||||
else
|
||||
party = gEnemyParty;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||||
{
|
||||
if (ShouldSwitch())
|
||||
{
|
||||
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
|
||||
{
|
||||
s32 monToSwitchId = GetMostSuitableMonToSwitchInto();
|
||||
if (monToSwitchId == PARTY_SIZE)
|
||||
{
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
|
||||
battlerIn2 = battlerIn1;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerIdentity);
|
||||
battlerIn2 = GetBattlerAtPosition(battlerIdentity ^ BIT_FLANK);
|
||||
}
|
||||
|
||||
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
|
||||
|
||||
for (monToSwitchId = firstId; monToSwitchId < lastId; monToSwitchId++)
|
||||
{
|
||||
if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0)
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn1))
|
||||
continue;
|
||||
if (monToSwitchId == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = monToSwitchId;
|
||||
}
|
||||
|
||||
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler);
|
||||
return;
|
||||
}
|
||||
else if (ShouldUseItem())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8);
|
||||
}
|
||||
|
||||
// If there are two(or more) mons to choose from, always choose one that has baton pass
|
||||
// as most often it can't do much on its own.
|
||||
static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount)
|
||||
@ -839,20 +882,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;
|
||||
@ -934,7 +967,6 @@ static bool8 ShouldUseItem(void)
|
||||
|
||||
if (shouldUse)
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0);
|
||||
*(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2) = item;
|
||||
gBattleResources->battleHistory->trainerItems[i] = 0;
|
||||
return shouldUse;
|
||||
@ -943,3 +975,41 @@ static bool8 ShouldUseItem(void)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 AI_ShouldHeal(u8 healAmount)
|
||||
{
|
||||
bool32 shouldHeal = FALSE;
|
||||
u32 i;
|
||||
|
||||
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 = TRUE;
|
||||
|
||||
// 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 even after we heal. So why bother.
|
||||
shouldHeal = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
// AI_THINKING_STRUCT->movesetIndex is the array index of the AI's chosen move
|
||||
if (CanIndexMoveFaintTarget(gActiveBattler, i, AI_THINKING_STRUCT->movesetIndex, 0) && AI_WhoStrikesFirst(gActiveBattler, i) == AI_IS_FASTER)
|
||||
{
|
||||
// We can faint the target and move first -> don't heal
|
||||
shouldHeal = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shouldHeal;
|
||||
}
|
||||
|
@ -439,6 +439,11 @@ static const u16 sOtherMoveCallingMoves[] =
|
||||
};
|
||||
|
||||
// Functions
|
||||
bool32 WillAIStrikeFirst(void)
|
||||
{
|
||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget) == AI_IS_FASTER);
|
||||
}
|
||||
|
||||
bool32 AI_RandLessThan(u8 val)
|
||||
{
|
||||
if ((Random() % 0xFF) < val)
|
||||
@ -632,7 +637,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 && GetWhoStrikesFirst(battlerAI, opposingBattler, TRUE) == 1)
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler) == AI_IS_SLOWER)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -981,48 +986,43 @@ u8 AI_GetMoveEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef)
|
||||
return damageVar;
|
||||
}
|
||||
|
||||
// AI_CHECK_FASTER: is user(ai) faster
|
||||
// AI_CHECK_SLOWER: is target faster
|
||||
bool32 IsAiFaster(u8 battler)
|
||||
/* Checks to see if AI will move ahead of another battler
|
||||
* Output:
|
||||
* AI_IS_FASTER: is user(ai) faster
|
||||
* AI_IS_SLOWER: is target faster
|
||||
*/
|
||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2)
|
||||
{
|
||||
u32 fasterAI = 0, fasterPlayer = 0, i;
|
||||
s8 prioAI, prioPlayer;
|
||||
s8 prioAI = 0;
|
||||
s8 prioPlayer = 0;
|
||||
|
||||
// Check move priorities first.
|
||||
prioAI = GetMovePriority(sBattler_AI, AI_THINKING_STRUCT->moveConsidered);
|
||||
SaveBattlerData(gBattlerTarget);
|
||||
SetBattlerData(gBattlerTarget);
|
||||
prioAI = GetMovePriority(battlerAI, AI_THINKING_STRUCT->moveConsidered);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].moves[i] == 0 || gBattleMons[gBattlerTarget].moves[i] == 0xFFFF)
|
||||
if (gBattleMons[battler2].moves[i] == 0 || gBattleMons[battler2].moves[i] == 0xFFFF)
|
||||
continue;
|
||||
|
||||
prioPlayer = GetMovePriority(gBattlerTarget, gBattleMons[gBattlerTarget].moves[i]);
|
||||
prioPlayer = GetMovePriority(battler2, gBattleMons[battler2].moves[i]);
|
||||
if (prioAI > prioPlayer)
|
||||
fasterAI++;
|
||||
else if (prioPlayer > prioAI)
|
||||
fasterPlayer++;
|
||||
}
|
||||
RestoreBattlerData(gBattlerTarget);
|
||||
|
||||
if (fasterAI > fasterPlayer)
|
||||
{
|
||||
if (battler == 0) // is user (ai) faster
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
return AI_IS_FASTER;
|
||||
}
|
||||
else if (fasterAI < fasterPlayer)
|
||||
{
|
||||
if (battler == 1) // is target (player) faster
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
return AI_IS_SLOWER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
||||
if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) == battler)
|
||||
if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
@ -1090,12 +1090,16 @@ bool32 CanTargetFaintAiWithMod(u8 battlerDef, u8 battlerAtk, s32 hpMod, s32 dmgM
|
||||
{
|
||||
u32 i;
|
||||
u32 unusable = CheckMoveLimitations(battlerDef, 0, 0xFF);
|
||||
s32 dmg;
|
||||
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
|
||||
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
|
||||
|
||||
if (hpCheck > gBattleMons[battlerAtk].maxHP)
|
||||
hpCheck = gBattleMons[battlerAtk].maxHP;
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u32 dmg = AI_CalcDamage(moves[i], battlerDef, battlerAtk);
|
||||
u32 hpCheck = gBattleMons[battlerAtk].hp + hpMod;
|
||||
dmg = AI_THINKING_STRUCT->simulatedDmg[battlerAtk][battlerDef][i];
|
||||
if (dmgMod)
|
||||
dmg *= dmgMod;
|
||||
|
||||
@ -1141,15 +1145,13 @@ s32 AI_GetAbility(u32 battlerId)
|
||||
// Else, guess the ability
|
||||
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
|
||||
{
|
||||
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
|
||||
u16 abilityGuess = ABILITY_NONE;
|
||||
while (abilityGuess == ABILITY_NONE)
|
||||
{
|
||||
// AI has no knowledge of opponent, so it guesses which ability.
|
||||
return gBaseStats[gBattleMons[battlerId].species].abilities[Random() & 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return gBaseStats[gBattleMons[battlerId].species].abilities[0]; // It's definitely ability 1.
|
||||
abilityGuess = gBaseStats[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS];
|
||||
}
|
||||
|
||||
return abilityGuess;
|
||||
}
|
||||
|
||||
return ABILITY_NONE; // Unknown.
|
||||
@ -1709,7 +1711,7 @@ u32 CountNegativeStatStages(u8 battlerId)
|
||||
|
||||
bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
|
||||
@ -1725,7 +1727,7 @@ bool32 ShouldLowerAttack(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
|
||||
@ -1741,10 +1743,10 @@ bool32 ShouldLowerDefense(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (IsAiFaster(AI_CHECK_SLOWER)
|
||||
if (!WillAIStrikeFirst()
|
||||
&& defAbility != ABILITY_CONTRARY
|
||||
&& defAbility != ABILITY_CLEAR_BODY
|
||||
&& defAbility != ABILITY_FULL_METAL_BODY
|
||||
@ -1755,7 +1757,7 @@ bool32 ShouldLowerSpeed(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
|
||||
@ -1770,7 +1772,7 @@ bool32 ShouldLowerSpAtk(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
|
||||
@ -1785,7 +1787,7 @@ bool32 ShouldLowerSpDef(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (defAbility != ABILITY_CONTRARY
|
||||
@ -1799,7 +1801,7 @@ bool32 ShouldLowerAccuracy(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
|
||||
bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility)
|
||||
{
|
||||
if (IsAiFaster(AI_CHECK_FASTER) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||
|
||||
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
|
||||
@ -2472,7 +2474,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 (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // Attacker goes first
|
||||
if (AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER) // Attacker goes first
|
||||
{
|
||||
if (!CanAIFaintTarget(battlerAtk, battlerDef, 0)) // Can't KO foe otherwise
|
||||
{
|
||||
@ -2832,7 +2834,7 @@ u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbili
|
||||
{
|
||||
if (defAbility == ABILITY_INNER_FOCUS
|
||||
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|
||||
|| GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 1) // opponent goes first
|
||||
|| AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_SLOWER) // Opponent goes first
|
||||
{
|
||||
return 0; // don't try to flinch
|
||||
}
|
||||
@ -2956,7 +2958,7 @@ bool32 ShouldUseRecoilMove(u8 battlerAtk, u8 battlerDef, u32 recoilDmg, u8 moveI
|
||||
|
||||
bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
{
|
||||
if (move == 0xFFFF || GetWhoStrikesFirst(battlerAtk, gBattlerTarget, TRUE) == 0)
|
||||
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef) == AI_IS_FASTER)
|
||||
{
|
||||
// using item or user goes first
|
||||
u8 healPercent = (gBattleMoves[move].argument == 0) ? 50 : gBattleMoves[move].argument;
|
||||
@ -2983,7 +2985,7 @@ bool32 ShouldAbsorb(u8 battlerAtk, u8 battlerDef, u16 move, s32 damage)
|
||||
|
||||
bool32 ShouldRecover(u8 battlerAtk, u8 battlerDef, u16 move, u8 healPercent)
|
||||
{
|
||||
if (move == 0xFFFF || GetWhoStrikesFirst(battlerAtk, gBattlerTarget, TRUE) == 0)
|
||||
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];
|
||||
@ -3383,6 +3385,47 @@ static const u16 sRecycleEncouragedItems[] =
|
||||
// TODO expand this
|
||||
};
|
||||
|
||||
// Its assumed that the berry is strategically given, so no need to check benefits of the berry
|
||||
bool32 IsStatBoostingBerry(u16 item)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case ITEM_LIECHI_BERRY:
|
||||
case ITEM_GANLON_BERRY:
|
||||
case ITEM_SALAC_BERRY:
|
||||
case ITEM_PETAYA_BERRY:
|
||||
case ITEM_APICOT_BERRY:
|
||||
//case ITEM_LANSAT_BERRY:
|
||||
case ITEM_STARF_BERRY:
|
||||
#ifdef ITEM_EXPANSION
|
||||
case ITEM_MICLE_BERRY:
|
||||
#endif
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 ShouldRestoreHpBerry(u8 battlerAtk, u16 item)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case ITEM_ORAN_BERRY:
|
||||
if (gBattleMons[battlerAtk].maxHP <= 50)
|
||||
return TRUE; // Only worth it in the early game
|
||||
return FALSE;
|
||||
case ITEM_SITRUS_BERRY:
|
||||
case ITEM_FIGY_BERRY:
|
||||
case ITEM_WIKI_BERRY:
|
||||
case ITEM_MAGO_BERRY:
|
||||
case ITEM_AGUAV_BERRY:
|
||||
case ITEM_IAPAPA_BERRY:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 IsRecycleEncouragedItem(u16 item)
|
||||
{
|
||||
u32 i;
|
||||
@ -3404,6 +3447,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
|
||||
if (GetHealthPercentage(battlerAtk) < 80 && AI_RandLessThan(128))
|
||||
return;
|
||||
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
|
||||
|
||||
switch (statId)
|
||||
{
|
||||
@ -3429,7 +3475,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
}
|
||||
break;
|
||||
case STAT_SPEED:
|
||||
if (IsAiFaster(AI_CHECK_SLOWER))
|
||||
if (!WillAIStrikeFirst())
|
||||
{
|
||||
if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE)
|
||||
*score += 2;
|
||||
@ -3476,6 +3522,9 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
|
||||
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 (!HasDamagingMove(battlerDef))
|
||||
@ -3496,6 +3545,9 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
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))
|
||||
{
|
||||
(*score)++; // burning is good
|
||||
@ -3512,6 +3564,9 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
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))
|
||||
{
|
||||
u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk);
|
||||
@ -3530,6 +3585,9 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
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))
|
||||
*score += 2;
|
||||
else
|
||||
@ -3545,6 +3603,9 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
|
||||
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)
|
||||
@ -3557,3 +3618,12 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
*score += 2;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move)
|
||||
{
|
||||
if (TestMoveFlags(move, FLAG_MAKES_CONTACT)
|
||||
&& ability != ABILITY_LONG_REACH
|
||||
&& holdEffect != HOLD_EFFECT_PROTECTIVE_PADS)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1545,7 +1545,70 @@ static void OpponentHandlePrintSelectionString(void)
|
||||
|
||||
static void OpponentHandleChooseAction(void)
|
||||
{
|
||||
AI_TrySwitchOrUseItem();
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, ChooseMoveAndTargetInBattlePalace());
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct moveInfo;
|
||||
|
||||
FillChooseMoveStruct(&moveInfo);
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER))
|
||||
{
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_USE_ITEM:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0);
|
||||
break;
|
||||
case AI_CHOICE_SWITCH:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
break;
|
||||
case AI_CHOICE_WATCH:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
|
||||
break;
|
||||
case AI_CHOICE_FLEE:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0);
|
||||
break;
|
||||
default:
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo.moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo.moves[chosenMoveId]) & MOVE_TARGET_BOTH)
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||
}
|
||||
|
||||
if (CanMegaEvolve(gActiveBattler)) { // If opponent can mega evolve, do it.
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
} else {
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // Wild pokemon - use random move
|
||||
{
|
||||
u16 move;
|
||||
do
|
||||
{
|
||||
chosenMoveId = Random() & 3;
|
||||
move = moveInfo.moves[chosenMoveId];
|
||||
} while (move == MOVE_NONE);
|
||||
|
||||
if (GetMoveTargetType(gActiveBattler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gActiveBattler << 8));
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
|
||||
}
|
||||
}
|
||||
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
@ -1556,71 +1619,9 @@ static void OpponentHandleYesNoBox(void)
|
||||
|
||||
static void OpponentHandleChooseMove(void)
|
||||
{
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
|
||||
{
|
||||
BtlController_EmitTwoReturnValues(1, 10, ChooseMoveAndTargetInBattlePalace());
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);
|
||||
|
||||
if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER))
|
||||
{
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_WATCH:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0);
|
||||
break;
|
||||
case AI_CHOICE_FLEE:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0);
|
||||
break;
|
||||
case AI_CHOICE_SWITCH:
|
||||
BtlController_EmitTwoReturnValues(1, 10, 0xFFFF);
|
||||
break;
|
||||
case 6:
|
||||
BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget);
|
||||
break;
|
||||
default:
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & MOVE_TARGET_BOTH)
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||||
}
|
||||
if (CanMegaEvolve(gActiveBattler)) // If opponent can mega evolve, do it.
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
break;
|
||||
}
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 move;
|
||||
do
|
||||
{
|
||||
chosenMoveId = Random() & 3;
|
||||
move = moveInfo->moves[chosenMoveId];
|
||||
} while (move == MOVE_NONE);
|
||||
|
||||
if (GetMoveTargetType(gActiveBattler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gActiveBattler << 8));
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8));
|
||||
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
}
|
||||
u8 *bufferB = gBattleResources->bufferB[gActiveBattler];
|
||||
BtlController_EmitTwoReturnValues(1, 10, bufferB[2] | (bufferB[3] << 8));
|
||||
OpponentBufferExecCompleted();
|
||||
}
|
||||
|
||||
static void OpponentHandleChooseItem(void)
|
||||
|
@ -1509,7 +1509,37 @@ static void PlayerPartnerHandlePrintSelectionString(void)
|
||||
|
||||
static void PlayerPartnerHandleChooseAction(void)
|
||||
{
|
||||
AI_TrySwitchOrUseItem();
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);
|
||||
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
switch (chosenMoveId)
|
||||
{
|
||||
case AI_CHOICE_USE_ITEM:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0);
|
||||
break;
|
||||
case AI_CHOICE_SWITCH:
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
|
||||
break;
|
||||
default:
|
||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
}
|
||||
|
||||
if (CanMegaEvolve(gActiveBattler)) // If partner can mega evolve, do it.
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
break;
|
||||
}
|
||||
|
||||
PlayerPartnerBufferExecCompleted();
|
||||
}
|
||||
|
||||
@ -1520,25 +1550,6 @@ static void PlayerPartnerHandleYesNoBox(void)
|
||||
|
||||
static void PlayerPartnerHandleChooseMove(void)
|
||||
{
|
||||
u8 chosenMoveId;
|
||||
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[gActiveBattler][4]);
|
||||
|
||||
BattleAI_SetupAIData(0xF);
|
||||
chosenMoveId = BattleAI_ChooseMoveOrAction();
|
||||
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
gBattlerTarget = gActiveBattler;
|
||||
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & MOVE_TARGET_BOTH)
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
}
|
||||
|
||||
if (CanMegaEvolve(gActiveBattler)) // If partner can mega evolve, do it.
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
|
||||
else
|
||||
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8));
|
||||
PlayerPartnerBufferExecCompleted();
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,7 @@ static void RunTurnActionsFunctions(void);
|
||||
static void SetActionsAndBattlersTurnOrder(void);
|
||||
static void sub_803CDF8(void);
|
||||
static bool8 AllAtActionConfirmed(void);
|
||||
static void TryChangeTurnOrder(void);
|
||||
static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void);
|
||||
static void CheckMegaEvolutionBeforeTurn(void);
|
||||
static void CheckQuickClaw_CustapBerryActivation(void);
|
||||
@ -3876,22 +3877,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
{
|
||||
struct ChooseMoveStruct moveInfo;
|
||||
|
||||
moveInfo.mega = gBattleStruct->mega;
|
||||
moveInfo.species = gBattleMons[gActiveBattler].species;
|
||||
moveInfo.monType1 = gBattleMons[gActiveBattler].type1;
|
||||
moveInfo.monType2 = gBattleMons[gActiveBattler].type2;
|
||||
moveInfo.monType3 = gBattleMons[gActiveBattler].type3;
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
moveInfo.moves[i] = gBattleMons[gActiveBattler].moves[i];
|
||||
moveInfo.currentPp[i] = gBattleMons[gActiveBattler].pp[i];
|
||||
moveInfo.maxPp[i] = CalculatePPWithBonus(
|
||||
gBattleMons[gActiveBattler].moves[i],
|
||||
gBattleMons[gActiveBattler].ppBonuses,
|
||||
i);
|
||||
}
|
||||
|
||||
FillChooseMoveStruct(&moveInfo);
|
||||
BtlController_EmitChooseMove(0, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
}
|
||||
@ -4688,10 +4674,37 @@ static void CheckMegaEvolutionBeforeTurn(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if B_MEGA_EVO_TURN_ORDER <= GEN_6
|
||||
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
|
||||
gBattleStruct->focusPunchBattlerId = 0;
|
||||
#else
|
||||
gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved
|
||||
#endif
|
||||
}
|
||||
|
||||
// In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves
|
||||
static void TryChangeTurnOrder(void)
|
||||
{
|
||||
s32 i, j;
|
||||
for (i = 0; i < gBattlersCount - 1; i++)
|
||||
{
|
||||
for (j = i + 1; j < gBattlersCount; j++)
|
||||
{
|
||||
u8 battler1 = gBattlerByTurnOrder[i];
|
||||
u8 battler2 = gBattlerByTurnOrder[j];
|
||||
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
|
||||
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
|
||||
{
|
||||
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
|
||||
SwapTurnOrder(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
|
||||
gBattleStruct->focusPunchBattlerId = 0;
|
||||
}
|
||||
|
||||
|
||||
static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void)
|
||||
{
|
||||
u32 i;
|
||||
@ -5295,3 +5308,24 @@ void SetTotemBoost(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillChooseMoveStruct(struct ChooseMoveStruct * moveInfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
moveInfo->mega = gBattleStruct->mega;
|
||||
moveInfo->species = gBattleMons[gActiveBattler].species;
|
||||
moveInfo->monType1 = gBattleMons[gActiveBattler].type1;
|
||||
moveInfo->monType2 = gBattleMons[gActiveBattler].type2;
|
||||
moveInfo->monType3 = gBattleMons[gActiveBattler].type3;
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
moveInfo->moves[i] = gBattleMons[gActiveBattler].moves[i];
|
||||
moveInfo->currentPp[i] = gBattleMons[gActiveBattler].pp[i];
|
||||
moveInfo->maxPp[i] = CalculatePPWithBonus(
|
||||
gBattleMons[gActiveBattler].moves[i],
|
||||
gBattleMons[gActiveBattler].ppBonuses,
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
@ -5493,6 +5493,14 @@ static void Cmd_moveend(void)
|
||||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||||
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
||||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget & 0x3;
|
||||
|
||||
#if B_RAMPAGE_CANCELLING >= GEN_5
|
||||
if (gBattleMoves[gCurrentMove].effect == EFFECT_RAMPAGE // If we're rampaging
|
||||
&& (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) // And it is unusable
|
||||
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
|
||||
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
|
||||
#endif
|
||||
|
||||
gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE;
|
||||
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
|
||||
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
|
||||
@ -12418,7 +12426,7 @@ static void Cmd_trywish(void)
|
||||
if (gWishFutureKnock.wishCounter[gBattlerAttacker] == 0)
|
||||
{
|
||||
gWishFutureKnock.wishCounter[gBattlerAttacker] = 2;
|
||||
gWishFutureKnock.wishMonId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
gBattlescriptCurrInstr += 6;
|
||||
}
|
||||
else
|
||||
@ -12427,9 +12435,12 @@ static void Cmd_trywish(void)
|
||||
}
|
||||
break;
|
||||
case 1: // heal effect
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishMonId[gBattlerTarget])
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishPartyId[gBattlerTarget])
|
||||
#if B_WISH_HP_SOURCE >= GEN_5
|
||||
gBattleMoveDamage = max(1, gBattleMons[gWishFutureKnock.wishMonId[gBattlerTarget]].maxHP / 2);
|
||||
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||||
gBattleMoveDamage = max(1, GetMonData(&gPlayerParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||||
else
|
||||
gBattleMoveDamage = max(1, GetMonData(&gEnemyParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||||
#else
|
||||
gBattleMoveDamage = max(1, gBattleMons[gBattlerTarget].maxHP / 2);
|
||||
#endif
|
||||
@ -13200,7 +13211,8 @@ static void Cmd_removelightscreenreflect(void) // brick break
|
||||
bool32 failed;
|
||||
|
||||
#if B_BRICK_BREAK >= GEN_4
|
||||
side = GetBattlerSide(gBattlerAttacker);
|
||||
// From Gen 4 onwards, Brick Break can remove screens on the user's side if used on an ally
|
||||
side = GetBattlerSide(gBattlerTarget);
|
||||
#else
|
||||
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
|
||||
#endif
|
||||
|
@ -2796,6 +2796,7 @@ u8 DoBattlerEndTurnEffects(void)
|
||||
case ENDTURN_FLINCH: // reset flinch
|
||||
gBattleMons[gActiveBattler].status2 &= ~(STATUS2_FLINCHED);
|
||||
gBattleStruct->turnEffectsTracker++;
|
||||
break;
|
||||
case ENDTURN_DISABLE: // disable
|
||||
if (gDisableStructs[gActiveBattler].disableTimer != 0)
|
||||
{
|
||||
@ -7521,8 +7522,13 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move)
|
||||
else if (gProtectStructs[battlerId].protected)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED))
|
||||
|
||||
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
|
||||
// This means extra logic is needed to handle Shell Side Arm.
|
||||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
|
||||
&& (gBattleMoves[move].flags & FLAG_MAKES_CONTACT || (gBattleMoves[move].effect == EFFECT_SHELL_SIDE_ARM && gSwapDamageCategory)))
|
||||
return FALSE;
|
||||
else if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED))
|
||||
return FALSE;
|
||||
else if (gBattleMoves[move].effect == MOVE_EFFECT_FEINT)
|
||||
return FALSE;
|
||||
|
Loading…
Reference in New Issue
Block a user