Adjusted AI handling for Gravity; AI for weather/field status additional effects. (#7651)
This commit is contained in:
parent
3c94074750
commit
fab4dc1163
@ -4,7 +4,10 @@
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_field_statuses.h"
|
||||
|
||||
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
||||
// Left and right are determined by how they're referred to in tests and everywhere else.
|
||||
// Left is battlers 0 and 1, right 2 and 3; if you assume the battler referencing them is south, left is to the northeast and right to the northwest.
|
||||
#define LEFT_FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
||||
#define RIGHT_FOE(battler) (((BATTLE_OPPOSITE(battler)) & BIT_SIDE) | BIT_FLANK)
|
||||
|
||||
// Roll boundaries used by AI when scoring. Doesn't affect actual damage dealt.
|
||||
#define MAX_ROLL_PERCENTAGE DMG_ROLL_PERCENT_HI
|
||||
@ -182,7 +185,7 @@ bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, u32 moveEffect);
|
||||
bool32 HasMoveWithCriticalHitChance(u32 battlerId);
|
||||
bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, enum BattleMoveEffects exception);
|
||||
bool32 HasMoveThatLowersOwnStats(u32 battlerId);
|
||||
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
|
||||
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus);
|
||||
bool32 HasAnyKnownMove(u32 battlerId);
|
||||
bool32 IsAromaVeilProtectedEffect(enum BattleMoveEffects moveEffect);
|
||||
bool32 IsNonVolatileStatusMove(u32 moveEffect);
|
||||
@ -299,7 +302,6 @@ bool32 IsBattlerItemEnabled(u32 battler);
|
||||
bool32 IsBattlerPredictedToSwitch(u32 battler);
|
||||
u32 GetIncomingMove(u32 battler, u32 opposingBattler, struct AiLogicData *aiData);
|
||||
u32 GetIncomingMoveSpeedCheck(u32 battler, u32 opposingBattler, struct AiLogicData *aiData);
|
||||
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 HasBattlerSideAbility(u32 battlerDef, u32 ability, struct AiLogicData *aiData);
|
||||
bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget);
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromMistyTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromGravity(u32 battler);
|
||||
static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler);
|
||||
|
||||
bool32 WeatherChecker(u32 battler, u32 weather, enum FieldEffectOutcome desiredResult)
|
||||
@ -108,6 +109,8 @@ bool32 FieldStatusChecker(u32 battler, u32 fieldStatus, enum FieldEffectOutcome
|
||||
result = BenefitsFromPsychicTerrain(battler);
|
||||
|
||||
// other field statuses
|
||||
if (fieldStatus & STATUS_FIELD_GRAVITY)
|
||||
result = BenefitsFromGravity(battler);
|
||||
if (fieldStatus & STATUS_FIELD_TRICK_ROOM)
|
||||
result = BenefitsFromTrickRoom(battler);
|
||||
|
||||
@ -248,9 +251,9 @@ static enum FieldEffectOutcome BenefitsFromSandstorm(u32 battler)
|
||||
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_SAFETY_GOGGLES || IS_BATTLER_ANY_TYPE(battler, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
{
|
||||
if (!(IS_BATTLER_ANY_TYPE(FOE(battler), TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
|| gAiLogicData->holdEffects[FOE(battler)] == HOLD_EFFECT_SAFETY_GOGGLES
|
||||
|| DoesAbilityBenefitFromWeather(gAiLogicData->abilities[FOE(battler)], B_WEATHER_SANDSTORM))
|
||||
if (!(IS_BATTLER_ANY_TYPE(LEFT_FOE(battler), TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
|| gAiLogicData->holdEffects[LEFT_FOE(battler)] == HOLD_EFFECT_SAFETY_GOGGLES
|
||||
|| DoesAbilityBenefitFromWeather(gAiLogicData->abilities[LEFT_FOE(battler)], B_WEATHER_SANDSTORM))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
else
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
@ -274,7 +277,7 @@ static enum FieldEffectOutcome BenefitsFromHailOrSnow(u32 battler, u32 weather)
|
||||
if (HasLightSensitiveMove(battler))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasMoveWithFlag(FOE(battler), MoveAlwaysHitsInHailSnow))
|
||||
if (HasMoveWithFlag(LEFT_FOE(battler), MoveAlwaysHitsInHailSnow))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
@ -294,7 +297,7 @@ static enum FieldEffectOutcome BenefitsFromRain(u32 battler)
|
||||
if (HasLightSensitiveMove(battler) || HasDamagingMoveOfType(battler, TYPE_FIRE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasMoveWithFlag(FOE(battler), MoveAlwaysHitsInRain))
|
||||
if (HasMoveWithFlag(LEFT_FOE(battler), MoveAlwaysHitsInRain))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
@ -309,11 +312,12 @@ static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler)
|
||||
if (HasMoveWithEffect(battler, EFFECT_RISING_VOLTAGE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithEffect(FOE(battler), EFFECT_REST) && IsBattlerGrounded(FOE(battler)))
|
||||
if ((HasMoveWithEffect(LEFT_FOE(battler), EFFECT_REST) && IsBattlerGrounded(LEFT_FOE(battler)))
|
||||
|| (HasMoveWithEffect(RIGHT_FOE(battler), EFFECT_REST) && IsBattlerGrounded(RIGHT_FOE(battler))))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
if (grounded && HasBattlerSideMoveWithAdditionalEffect(FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
if (grounded && HasBattlerSideMoveWithAdditionalEffect(LEFT_FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && ((gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
@ -321,7 +325,7 @@ static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_ELECTRIC)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_RISING_VOLTAGE))
|
||||
if (HasBattlerSideMoveWithEffect(LEFT_FOE(battler), EFFECT_RISING_VOLTAGE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
|
||||
@ -334,7 +338,7 @@ static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler)
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_GRASSY_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithEffect(battler, EFFECT_GRASSY_GLIDE))
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_GRASSY_GLIDE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
if (HasMoveWithAdditionalEffect(battler, MOVE_EFFECT_FLORAL_HEALING))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
@ -342,14 +346,14 @@ static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler)
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
|
||||
// Weaken spamming Earthquake, Magnitude, and Bulldoze.
|
||||
if (grounded && (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_EARTHQUAKE)
|
||||
|| HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_MAGNITUDE)))
|
||||
if (grounded && (HasBattlerSideMoveWithEffect(LEFT_FOE(battler), EFFECT_EARTHQUAKE)
|
||||
|| HasBattlerSideMoveWithEffect(LEFT_FOE(battler), EFFECT_MAGNITUDE)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && HasDamagingMoveOfType(battler, TYPE_GRASS))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_GRASSY_GLIDE))
|
||||
if (HasBattlerSideMoveWithEffect(LEFT_FOE(battler), EFFECT_GRASSY_GLIDE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
|
||||
@ -370,16 +374,17 @@ static enum FieldEffectOutcome BenefitsFromMistyTerrain(u32 battler)
|
||||
if (HasPartner(battler))
|
||||
allyGrounded = IsBattlerGrounded(BATTLE_PARTNER(battler));
|
||||
|
||||
if (HasMoveWithEffect(FOE(battler), EFFECT_REST) && IsBattlerGrounded(FOE(battler)))
|
||||
if ((HasMoveWithEffect(LEFT_FOE(battler), EFFECT_REST) && IsBattlerGrounded(LEFT_FOE(battler)))
|
||||
|| (HasMoveWithEffect(RIGHT_FOE(battler), EFFECT_REST) && IsBattlerGrounded(RIGHT_FOE(battler))))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
// harass dragons
|
||||
if ((grounded || allyGrounded)
|
||||
&& (HasDamagingMoveOfType(FOE(battler), TYPE_DRAGON) || HasDamagingMoveOfType(BATTLE_PARTNER(FOE(battler)), TYPE_DRAGON)))
|
||||
&& (HasDamagingMoveOfType(LEFT_FOE(battler), TYPE_DRAGON) || HasDamagingMoveOfType(RIGHT_FOE(battler), TYPE_DRAGON)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if ((grounded || allyGrounded)
|
||||
&& (HasNonVolatileMoveEffect(FOE(battler), MOVE_EFFECT_SLEEP) || HasNonVolatileMoveEffect(BATTLE_PARTNER(FOE(battler)), MOVE_EFFECT_SLEEP)))
|
||||
&& (HasNonVolatileMoveEffect(LEFT_FOE(battler), MOVE_EFFECT_SLEEP) || HasNonVolatileMoveEffect(RIGHT_FOE(battler), MOVE_EFFECT_SLEEP)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && (gBattleMons[battler].status1 & STATUS1_SLEEP || gBattleMons[battler].volatiles.yawn))
|
||||
@ -406,16 +411,16 @@ static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
|
||||
if (grounded || allyGrounded)
|
||||
{
|
||||
// harass priority
|
||||
if (HasBattlerSideAbility(FOE(battler), ABILITY_GALE_WINGS, gAiLogicData)
|
||||
|| HasBattlerSideAbility(FOE(battler), ABILITY_TRIAGE, gAiLogicData)
|
||||
|| HasBattlerSideAbility(FOE(battler), ABILITY_PRANKSTER, gAiLogicData))
|
||||
if (HasBattlerSideAbility(LEFT_FOE(battler), ABILITY_GALE_WINGS, gAiLogicData)
|
||||
|| HasBattlerSideAbility(LEFT_FOE(battler), ABILITY_TRIAGE, gAiLogicData)
|
||||
|| HasBattlerSideAbility(LEFT_FOE(battler), ABILITY_PRANKSTER, gAiLogicData))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
|
||||
if (grounded && (HasDamagingMoveOfType(battler, TYPE_PSYCHIC)))
|
||||
if (grounded && HasDamagingMoveOfType(battler, TYPE_PSYCHIC))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_EXPANDING_FORCE))
|
||||
if (HasBattlerSideMoveWithEffect(LEFT_FOE(battler), EFFECT_EXPANDING_FORCE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasBattlerSideAbility(battler, ABILITY_GALE_WINGS, gAiLogicData)
|
||||
@ -426,15 +431,44 @@ static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
static enum FieldEffectOutcome BenefitsFromGravity(u32 battler)
|
||||
{
|
||||
if (!IsBattlerGrounded(battler))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasBattlerSideAbility(battler, ABILITY_HUSTLE, gAiLogicData))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithFlag(battler, IsMoveGravityBanned))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (IsBattlerAlive(LEFT_FOE(battler)))
|
||||
{
|
||||
if (HasMoveWithLowAccuracy(battler, LEFT_FOE(battler), LOW_ACCURACY_THRESHOLD, FALSE)
|
||||
|| (!IsBattlerGrounded(LEFT_FOE(battler)) && HasDamagingMoveOfType(battler, TYPE_GROUND)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
|
||||
if (IsBattlerAlive(RIGHT_FOE(battler)))
|
||||
{
|
||||
if (HasMoveWithLowAccuracy(battler, RIGHT_FOE(battler), LOW_ACCURACY_THRESHOLD, FALSE)
|
||||
|| (!IsBattlerGrounded(RIGHT_FOE(battler)) && HasDamagingMoveOfType(battler, TYPE_GROUND)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
}
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
|
||||
static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler)
|
||||
{
|
||||
// If we're in singles, we literally only care about speed.
|
||||
if (IsBattle1v1())
|
||||
{
|
||||
if (gAiLogicData->speedStats[battler] < gAiLogicData->speedStats[FOE(battler)])
|
||||
if (gAiLogicData->speedStats[battler] < gAiLogicData->speedStats[LEFT_FOE(battler)])
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
// If we tie, we shouldn't change trick room state.
|
||||
else if (gAiLogicData->speedStats[battler] == gAiLogicData->speedStats[FOE(battler)])
|
||||
else if (gAiLogicData->speedStats[battler] == gAiLogicData->speedStats[LEFT_FOE(battler)])
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
else
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
@ -455,7 +489,7 @@ static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler)
|
||||
}
|
||||
|
||||
// If we are faster or tie, we don't want trick room.
|
||||
if ((gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[FOE(battler)]) || (gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[BATTLE_PARTNER(FOE(battler))]))
|
||||
if ((gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[LEFT_FOE(battler)]) || (gAiLogicData->speedStats[battler] >= gAiLogicData->speedStats[RIGHT_FOE(battler)]))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// Note that FOE specifically returns the left-side battler; BATTLE_OPPOSITE is the diagonal.
|
||||
|
||||
#include "global.h"
|
||||
#include "main.h"
|
||||
#include "malloc.h"
|
||||
@ -1892,15 +1890,15 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
if (CountUsablePartyMons(battlerAtk) == 0
|
||||
&& aiData->abilities[battlerAtk] != ABILITY_SOUNDPROOF
|
||||
&& aiData->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_SOUNDPROOF
|
||||
&& CountUsablePartyMons(FOE(battlerAtk)) >= 1)
|
||||
&& CountUsablePartyMons(battlerDef) >= 1
|
||||
&& (aiData->abilities[BATTLE_PARTNER(battlerAtk)] != ABILITY_SOUNDPROOF || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk))))
|
||||
{
|
||||
ADJUST_SCORE(-10); //Don't wipe your team if you're going to lose
|
||||
}
|
||||
else if ((!IsBattlerAlive(FOE(battlerAtk)) || aiData->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF
|
||||
|| gBattleMons[FOE(battlerAtk)].volatiles.perishSong)
|
||||
&& (!IsBattlerAlive(BATTLE_PARTNER(FOE(battlerAtk))) || aiData->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_SOUNDPROOF
|
||||
|| gBattleMons[BATTLE_PARTNER(FOE(battlerAtk))].volatiles.perishSong))
|
||||
else if ((!IsBattlerAlive(LEFT_FOE(battlerAtk)) || aiData->abilities[LEFT_FOE(battlerAtk)] == ABILITY_SOUNDPROOF
|
||||
|| gBattleMons[LEFT_FOE(battlerAtk)].volatiles.perishSong)
|
||||
&& (!IsBattlerAlive(RIGHT_FOE(battlerAtk)) || aiData->abilities[RIGHT_FOE(battlerAtk)] == ABILITY_SOUNDPROOF
|
||||
|| gBattleMons[RIGHT_FOE(battlerAtk)].volatiles.perishSong))
|
||||
{
|
||||
ADJUST_SCORE(-10); //Both enemies are perish songed
|
||||
}
|
||||
@ -1915,7 +1913,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
&& CountUsablePartyMons(battlerDef) >= 1)
|
||||
ADJUST_SCORE(-10);
|
||||
|
||||
if (gBattleMons[FOE(battlerAtk)].volatiles.perishSong || aiData->abilities[FOE(battlerAtk)] == ABILITY_SOUNDPROOF)
|
||||
if (gBattleMons[battlerDef].volatiles.perishSong || aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF)
|
||||
ADJUST_SCORE(-10);
|
||||
}
|
||||
break;
|
||||
@ -2345,11 +2343,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-9);
|
||||
break;
|
||||
case EFFECT_COURT_CHANGE:
|
||||
if (gSideStatuses[GetBattlerSide(FOE(battlerAtk))] & SIDE_STATUS_BAD_COURT)
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_BAD_COURT)
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_GOOD_COURT)
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
if (AreAnyHazardsOnSide(GetBattlerSide(FOE(battlerAtk))) && CountUsablePartyMons(battlerAtk) != 0)
|
||||
if (AreAnyHazardsOnSide(GetBattlerSide(battlerDef)) && CountUsablePartyMons(battlerAtk) != 0)
|
||||
ADJUST_SCORE(WORST_EFFECT);
|
||||
if (hasPartner)
|
||||
{
|
||||
@ -3116,38 +3114,38 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 ownHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtk, BATTLE_OPPOSITE(battlerAtk), AI_ATTACKING);
|
||||
u32 partnerHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtk), AI_ATTACKING);
|
||||
u32 ownHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), AI_ATTACKING);
|
||||
u32 partnerHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtkPartner, BATTLE_OPPOSITE(battlerAtkPartner), AI_ATTACKING);
|
||||
u32 ownHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtk, LEFT_FOE(battlerAtk), AI_ATTACKING);
|
||||
u32 partnerHitsToKOFoe1 = GetBestNoOfHitsToKO(battlerAtkPartner, LEFT_FOE(battlerAtk), AI_ATTACKING);
|
||||
u32 ownHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtk, RIGHT_FOE(battlerAtk), AI_ATTACKING);
|
||||
u32 partnerHitsToKOFoe2 = GetBestNoOfHitsToKO(battlerAtkPartner, RIGHT_FOE(battlerAtk), AI_ATTACKING);
|
||||
|
||||
if (hasTwoOpponents)
|
||||
{
|
||||
// Might be about to die
|
||||
if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtk), battlerAtk) && CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtkPartner), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)
|
||||
&& AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, LEFT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)
|
||||
&& AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
|
||||
if (ownHitsToKOFoe1 > partnerHitsToKOFoe1 && partnerHitsToKOFoe1 > 1
|
||||
&& ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1)
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
}
|
||||
else if (IsBattlerAlive(BATTLE_OPPOSITE(battlerAtk)))
|
||||
else if (IsBattlerAlive(LEFT_FOE(battlerAtk)))
|
||||
{
|
||||
// Might be about to die
|
||||
if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtk), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, LEFT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
|
||||
if (ownHitsToKOFoe1 > partnerHitsToKOFoe1 && partnerHitsToKOFoe1 > 1)
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
}
|
||||
else if (IsBattlerAlive(BATTLE_OPPOSITE(battlerAtkPartner)))
|
||||
else if (IsBattlerAlive(RIGHT_FOE(battlerAtk)))
|
||||
{
|
||||
// Might be about to die
|
||||
if (CanTargetFaintAi(BATTLE_OPPOSITE(battlerAtkPartner), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, BATTLE_OPPOSITE(battlerAtkPartner), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
if (CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
|
||||
&& AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
|
||||
if (ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1)
|
||||
@ -3663,8 +3661,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && HasMoveWithEffect(battlerAtkPartner, EFFECT_TRICK_ROOM))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
|
||||
if (AI_IsSlower(battlerAtkPartner, FOE(battlerAtkPartner), aiData->partnerMove, predictedMoveSpeedCheck, CONSIDER_PRIORITY) // Opponent mon 1 goes before partner
|
||||
&& AI_IsSlower(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), aiData->partnerMove, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Opponent mon 2 goes before partner
|
||||
if (AI_IsSlower(battlerAtkPartner, LEFT_FOE(battlerAtk), aiData->partnerMove, predictedMoveSpeedCheck, CONSIDER_PRIORITY) // Opponent mon 1 goes before partner
|
||||
&& AI_IsSlower(battlerAtkPartner, RIGHT_FOE(battlerAtk), aiData->partnerMove, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Opponent mon 2 goes before partner
|
||||
{
|
||||
if (partnerEffect == EFFECT_COUNTER || partnerEffect == EFFECT_MIRROR_COAT)
|
||||
break; // These moves need to go last
|
||||
@ -3673,8 +3671,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_HEAL_PULSE:
|
||||
case EFFECT_HIT_ENEMY_HEAL_ALLY:
|
||||
if (AI_IsFaster(battlerAtk, FOE(battlerAtk), move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)
|
||||
&& AI_IsFaster(battlerAtk, BATTLE_PARTNER(FOE(battlerAtk)), move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)
|
||||
if (AI_IsFaster(battlerAtk, LEFT_FOE(battlerAtk), move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)
|
||||
&& AI_IsFaster(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)
|
||||
&& gBattleMons[battlerAtkPartner].hp < gBattleMons[battlerAtkPartner].maxHP / 2)
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
break;
|
||||
@ -4260,16 +4258,16 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_ATK));
|
||||
ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_SPATK));
|
||||
}
|
||||
if (IS_BATTLER_OF_TYPE(FOE(battlerAtk), TYPE_GRASS) && IsBattlerGrounded(FOE(battlerAtk)))
|
||||
if (IS_BATTLER_OF_TYPE(LEFT_FOE(battlerAtk), TYPE_GRASS) && IsBattlerGrounded(LEFT_FOE(battlerAtk)))
|
||||
{
|
||||
if (aiData->abilities[FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
if (aiData->abilities[LEFT_FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(AWFUL_EFFECT);
|
||||
}
|
||||
if (IS_BATTLER_OF_TYPE(BATTLE_PARTNER(FOE(battlerAtk)), TYPE_GRASS) && IsBattlerGrounded(BATTLE_PARTNER(FOE(battlerAtk))))
|
||||
if (IS_BATTLER_OF_TYPE(RIGHT_FOE(battlerAtk), TYPE_GRASS) && IsBattlerGrounded(RIGHT_FOE(battlerAtk)))
|
||||
{
|
||||
if (aiData->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_CONTRARY)
|
||||
if (aiData->abilities[RIGHT_FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(AWFUL_EFFECT);
|
||||
@ -4284,16 +4282,16 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
ADJUST_SCORE(IncreaseStatUpScore(BATTLE_PARTNER(battlerAtk), battlerDef, STAT_CHANGE_DEF));
|
||||
}
|
||||
if (IS_BATTLER_OF_TYPE(FOE(battlerAtk), TYPE_GRASS))
|
||||
if (IS_BATTLER_OF_TYPE(LEFT_FOE(battlerAtk), TYPE_GRASS))
|
||||
{
|
||||
if (aiData->abilities[FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
if (aiData->abilities[LEFT_FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(AWFUL_EFFECT);
|
||||
}
|
||||
if (IS_BATTLER_OF_TYPE(BATTLE_PARTNER(FOE(battlerAtk)), TYPE_GRASS))
|
||||
if (IS_BATTLER_OF_TYPE(RIGHT_FOE(battlerAtk), TYPE_GRASS))
|
||||
{
|
||||
if (aiData->abilities[BATTLE_PARTNER(FOE(battlerAtk))] == ABILITY_CONTRARY)
|
||||
if (aiData->abilities[RIGHT_FOE(battlerAtk)] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(AWFUL_EFFECT);
|
||||
@ -4529,7 +4527,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_LOCK_ON:
|
||||
if (HasMoveWithEffect(battlerAtk, EFFECT_OHKO) || HasMoveWithEffect(battlerAtk, EFFECT_SHEER_COLD))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]))
|
||||
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
break;
|
||||
case EFFECT_DESTINY_BOND:
|
||||
@ -4818,7 +4816,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
//ADJUST_SCORE(8);
|
||||
break;
|
||||
case EFFECT_COURT_CHANGE:
|
||||
if (gSideStatuses[GetBattlerSide(FOE(battlerAtk))] & SIDE_STATUS_GOOD_COURT)
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_GOOD_COURT)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_BAD_COURT)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
@ -5294,11 +5292,16 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_GRAVITY:
|
||||
if (!(gFieldStatuses & STATUS_FIELD_GRAVITY))
|
||||
if (!(gFieldStatuses & STATUS_FIELD_GRAVITY || ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_GRAVITY)))
|
||||
{
|
||||
if (HasSleepMoveWithLowAccuracy(battlerAtk, battlerDef)) // Has Gravity for a move like Hypnosis
|
||||
// improve accuracy of Hypnosis
|
||||
if (HasSleepMoveWithLowAccuracy(battlerAtk, battlerDef)
|
||||
|| HasSleepMoveWithLowAccuracy(BATTLE_PARTNER(battlerAtk), battlerDef))
|
||||
IncreaseSleepScore(battlerAtk, battlerDef, move, &score);
|
||||
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]))
|
||||
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE)
|
||||
|| HasMoveWithLowAccuracy(BATTLE_PARTNER(battlerAtk), battlerDef, 90, TRUE))
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_GRAVITY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
break;
|
||||
@ -5341,8 +5344,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_TELEKINESIS:
|
||||
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef])
|
||||
|| !IsBattlerGrounded(battlerDef))
|
||||
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, FALSE) || !IsBattlerGrounded(battlerDef))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_HEAL_BLOCK:
|
||||
@ -5404,8 +5406,8 @@ case EFFECT_GUARD_SPLIT:
|
||||
u32 tailwindScore = 0;
|
||||
u32 speed = aiData->speedStats[battlerAtk];
|
||||
u32 partnerSpeed = aiData->speedStats[BATTLE_PARTNER(battlerAtk)];
|
||||
u32 foe1Speed = aiData->speedStats[FOE(battlerAtk)];
|
||||
u32 foe2Speed = aiData->speedStats[BATTLE_PARTNER(FOE(battlerAtk))];
|
||||
u32 foe1Speed = aiData->speedStats[LEFT_FOE(battlerAtk)];
|
||||
u32 foe2Speed = aiData->speedStats[RIGHT_FOE(battlerAtk)];
|
||||
|
||||
if (speed <= foe1Speed && (speed * 2) > foe1Speed)
|
||||
tailwindScore += 1;
|
||||
@ -5795,6 +5797,78 @@ case EFFECT_GUARD_SPLIT:
|
||||
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_WATER) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_SUN:
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_SUN))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (ShouldClearWeather(battlerAtk, B_WEATHER_SUN))
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_RAIN:
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_RAIN))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (ShouldClearWeather(battlerAtk, B_WEATHER_RAIN))
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_SANDSTORM:
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_SANDSTORM))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (ShouldClearWeather(battlerAtk, B_WEATHER_SANDSTORM))
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_HAIL:
|
||||
if (ShouldSetWeather(battlerAtk, B_WEATHER_HAIL))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
if (ShouldClearWeather(battlerAtk, B_WEATHER_HAIL))
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_MISTY_TERRAIN:
|
||||
if (ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_MISTY_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
}
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_MISTY_TERRAIN)
|
||||
|| ShouldClearFieldStatus(battlerAtk, gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_GRASSY_TERRAIN:
|
||||
if (ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_GRASSY_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
}
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_GRASSY_TERRAIN)
|
||||
|| ShouldClearFieldStatus(battlerAtk, gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_ELECTRIC_TERRAIN:
|
||||
if (ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
}
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN)
|
||||
|| ShouldClearFieldStatus(battlerAtk, gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_PSYCHIC_TERRAIN:
|
||||
if (ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
{
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
break;
|
||||
}
|
||||
if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN)
|
||||
|| ShouldClearFieldStatus(battlerAtk, gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_GRAVITY:
|
||||
if (!(gFieldStatuses & STATUS_FIELD_GRAVITY) && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_GRAVITY))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_AURORA_VEIL:
|
||||
if (ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -6048,7 +6122,7 @@ static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
u32 hitsToKO = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, AI_ATTACKING);
|
||||
|
||||
if (GetMoveTarget(move) == MOVE_TARGET_FOES_AND_ALLY && hitsToKO > 0 &&
|
||||
(GetNoOfHitsToKOBattler(battlerAtk, FOE(battlerAtk), gAiThinkingStruct->movesetIndex, AI_ATTACKING) > 0 || GetNoOfHitsToKOBattler(battlerAtk, FOE(battlerDef), gAiThinkingStruct->movesetIndex, AI_ATTACKING) > 0))
|
||||
(GetNoOfHitsToKOBattler(battlerAtk, LEFT_FOE(battlerAtk), gAiThinkingStruct->movesetIndex, AI_ATTACKING) > 0 || GetNoOfHitsToKOBattler(battlerAtk, LEFT_FOE(battlerDef), gAiThinkingStruct->movesetIndex, AI_ATTACKING) > 0))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
|
||||
if (hitsToKO > 0)
|
||||
@ -6128,8 +6202,8 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (gBattleMons[battlerDef].volatiles.healBlock)
|
||||
return 0;
|
||||
|
||||
if (CanTargetFaintAi(FOE(battlerAtk), BATTLE_PARTNER(battlerAtk))
|
||||
|| (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), BATTLE_PARTNER(battlerAtk))))
|
||||
if (CanTargetFaintAi(LEFT_FOE(battlerAtk), BATTLE_PARTNER(battlerAtk))
|
||||
|| CanTargetFaintAi(RIGHT_FOE(battlerAtk), BATTLE_PARTNER(battlerAtk)))
|
||||
ADJUST_SCORE(-1);
|
||||
|
||||
if (gAiLogicData->hpPercents[battlerDef] <= 50)
|
||||
|
||||
@ -2558,7 +2558,7 @@ bool32 HasMoveThatRaisesOwnStats(u32 battlerId)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
|
||||
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus)
|
||||
{
|
||||
s32 i;
|
||||
u16 *moves = GetMovesArray(battlerAtk);
|
||||
@ -3884,7 +3884,7 @@ bool32 IsBattle1v1()
|
||||
bool32 HasTwoOpponents(u32 battler)
|
||||
{
|
||||
if (IsDoubleBattle()
|
||||
&& IsBattlerAlive(FOE(battler)) && IsBattlerAlive(BATTLE_PARTNER(FOE(battler))))
|
||||
&& IsBattlerAlive(LEFT_FOE(battler)) && IsBattlerAlive(RIGHT_FOE(battler)))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -5262,7 +5262,7 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef)
|
||||
case STAT_SPATK:
|
||||
return (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL));
|
||||
case STAT_ACC:
|
||||
return (HasLowAccuracyMove(battlerAtk, battlerDef));
|
||||
return HasMoveWithLowAccuracy(battlerAtk, battlerDef, LOW_ACCURACY_THRESHOLD, FALSE);
|
||||
case STAT_EVASION:
|
||||
case STAT_SPEED:
|
||||
return TRUE;
|
||||
@ -5330,7 +5330,7 @@ bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, st
|
||||
if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] == MAX_STAT_STAGE
|
||||
|| partnerAbility == ABILITY_CONTRARY
|
||||
|| partnerAbility == ABILITY_GOOD_AS_GOLD
|
||||
|| HasBattlerSideMoveWithEffect(FOE(battlerAtk), EFFECT_FOUL_PLAY))
|
||||
|| HasBattlerSideMoveWithEffect(LEFT_FOE(battlerAtk), EFFECT_FOUL_PLAY))
|
||||
return FALSE;
|
||||
|
||||
preventsStatLoss = !CanLowerStat(battlerAtk, battlerAtkPartner, aiData, STAT_DEF);
|
||||
@ -5393,17 +5393,6 @@ u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
return scoreIncrease;
|
||||
}
|
||||
|
||||
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gAiLogicData->moveAccuracy[battlerAtk][battlerDef][i] <= LOW_ACCURACY_THRESHOLD)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsBattlerItemEnabled(u32 battler)
|
||||
{
|
||||
if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_NEGATE_UNAWARE)
|
||||
@ -5768,7 +5757,8 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
return GOOD_EFFECT;
|
||||
// Conditional ability logic goes here.
|
||||
case ABILITY_COMPOUND_EYES:
|
||||
if (HasMoveWithLowAccuracy(battler, FOE(battler), 90, TRUE, aiData->abilities[battler], aiData->abilities[FOE(battler)], aiData->holdEffects[battler], aiData->holdEffects[FOE(battler)]))
|
||||
if (HasMoveWithLowAccuracy(battler, LEFT_FOE(battler), 90, FALSE)
|
||||
|| HasMoveWithLowAccuracy(battler, RIGHT_FOE(battler), 90, FALSE))
|
||||
return GOOD_EFFECT;
|
||||
break;
|
||||
case ABILITY_CONTRARY:
|
||||
@ -5800,7 +5790,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
break;
|
||||
case ABILITY_INTIMIDATE:
|
||||
{
|
||||
u32 abilityDef = aiData->abilities[FOE(battler)];
|
||||
u32 abilityDef = aiData->abilities[LEFT_FOE(battler)];
|
||||
if (DoesIntimidateRaiseStats(abilityDef))
|
||||
{
|
||||
return AWFUL_EFFECT;
|
||||
@ -5809,26 +5799,27 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
{
|
||||
if (HasTwoOpponents(battler))
|
||||
{
|
||||
abilityDef = aiData->abilities[BATTLE_PARTNER(FOE(battler))];
|
||||
abilityDef = aiData->abilities[RIGHT_FOE(battler)];
|
||||
if (DoesIntimidateRaiseStats(abilityDef))
|
||||
{
|
||||
return AWFUL_EFFECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 score1 = IncreaseStatDownScore(battler, FOE(battler), STAT_ATK);
|
||||
s32 score2 = IncreaseStatDownScore(battler, BATTLE_PARTNER(FOE(battler)), STAT_ATK);
|
||||
s32 score1 = IncreaseStatDownScore(battler, LEFT_FOE(battler), STAT_ATK);
|
||||
s32 score2 = IncreaseStatDownScore(battler, RIGHT_FOE(battler), STAT_ATK);
|
||||
if (score1 > score2)
|
||||
return score1;
|
||||
else
|
||||
return score2;
|
||||
}
|
||||
}
|
||||
return IncreaseStatDownScore(battler, FOE(battler), STAT_ATK);
|
||||
return IncreaseStatDownScore(battler, LEFT_FOE(battler), STAT_ATK);
|
||||
}
|
||||
}
|
||||
case ABILITY_NO_GUARD:
|
||||
if (HasLowAccuracyMove(battler, FOE(battler)))
|
||||
if (HasMoveWithLowAccuracy(battler, LEFT_FOE(battler), LOW_ACCURACY_THRESHOLD, FALSE)
|
||||
|| HasMoveWithLowAccuracy(battler, RIGHT_FOE(battler), LOW_ACCURACY_THRESHOLD, FALSE))
|
||||
return GOOD_EFFECT;
|
||||
break;
|
||||
// Toxic counter ticks upward while Poison Healed; losing Poison Heal while Toxiced can KO.
|
||||
|
||||
@ -313,7 +313,6 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 301-400")
|
||||
case EFFECT_COPYCAT:
|
||||
case EFFECT_LAST_RESORT:
|
||||
case EFFECT_AQUA_RING:
|
||||
case EFFECT_GRAVITY:
|
||||
case EFFECT_HEALING_WISH:
|
||||
|
||||
//TODO: AI TESTS
|
||||
@ -327,6 +326,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 301-400")
|
||||
case EFFECT_MAGNET_RISE:
|
||||
|
||||
// tests exist elsewhere
|
||||
case EFFECT_GRAVITY:
|
||||
case EFFECT_HEAL_BELL:
|
||||
case EFFECT_ATTACK_UP_USER_ALLY:
|
||||
|
||||
|
||||
@ -45,3 +45,36 @@ DOUBLE_BATTLE_TEST("Gravity cancels fly and sky drop if they are in the air")
|
||||
EXPECT_EQ(gLastMoves[0], MOVE_GRAVITY);
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI uses Gravity")
|
||||
{
|
||||
u32 move, friendItem, foeItem;
|
||||
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
|
||||
|
||||
PARAMETRIZE { move = MOVE_THUNDER; friendItem = ITEM_NONE; foeItem = ITEM_NONE; }
|
||||
PARAMETRIZE { move = MOVE_HEADBUTT; friendItem = ITEM_AIR_BALLOON; foeItem = ITEM_NONE; }
|
||||
PARAMETRIZE { move = MOVE_HEADBUTT; friendItem = ITEM_AIR_BALLOON; foeItem = ITEM_AIR_BALLOON; }
|
||||
PARAMETRIZE { move = MOVE_HEADBUTT; friendItem = ITEM_NONE; foeItem = ITEM_AIR_BALLOON; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
move = MOVE_THUNDER; friendItem = ITEM_NONE; foeItem = ITEM_NONE; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
move = MOVE_HEADBUTT; friendItem = ITEM_AIR_BALLOON; foeItem = ITEM_NONE; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
move = MOVE_HEADBUTT; friendItem = ITEM_AIR_BALLOON; foeItem = ITEM_AIR_BALLOON; }
|
||||
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
|
||||
move = MOVE_HEADBUTT; friendItem = ITEM_NONE; foeItem = ITEM_AIR_BALLOON; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(aiFlags);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(foeItem); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_GRAVITY, MOVE_HEADBUTT, MOVE_TAUNT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_EARTH_POWER); Item(friendItem); }
|
||||
} WHEN {
|
||||
if (move == MOVE_THUNDER || (foeItem == ITEM_AIR_BALLOON && friendItem != ITEM_AIR_BALLOON))
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_GRAVITY); }
|
||||
else
|
||||
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_GRAVITY); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user