AI_FLAG_ATTACKS_PARTNER with a config for bloodthirstiness (#7401)
This commit is contained in:
parent
acc82e7d79
commit
e8b6d40f18
@ -54,6 +54,9 @@ This flag is divided into two components to calculate the best available move fo
|
||||
|
||||
This is different to `AI_FLAG_CHECK_BAD_MOVE` as it calculates how poor a move is and not whether it will fail or not.
|
||||
|
||||
## `AI_FLAG_ATTACKS_PARTNER`
|
||||
This flag is meant for double battles where both of the opponents hate each other. They prioritize damage to their 'partner' over the player.
|
||||
|
||||
## `AI_FLAG_FORCE_SETUP_FIRST_TURN`
|
||||
AI will prioritize using setup moves on the first turn at the expense of all else. These include stat buffs, field effects, status moves, etc. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.
|
||||
|
||||
|
||||
@ -241,8 +241,16 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
|
||||
// partner logic
|
||||
bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef);
|
||||
// IsTargetingPartner includes a check to make sure the adjacent pokemon is truly a partner.
|
||||
u32 GetAllyChosenMove(u32 battlerId);
|
||||
bool32 IsValidDoubleBattle(u32 battlerAtk);
|
||||
bool32 IsBattle1v1();
|
||||
// IsBattle1v1 is distinct from !IsDoubleBattle. If the player is fighting Maxie and Tabitha, with Steven as their partner, and both Tabitha and Steven have run out of Pokemon, the battle is 1v1, even though mechanically it is a Double Battle for how battlers and flags are set.
|
||||
// Most AI checks should be using IsBattle1v1; most engine checks should be using !IsDoubleBattle
|
||||
bool32 HasTwoOpponents(u32 battler);
|
||||
// HasTwoOpponents checks if the opposing side has two pokemon. Partner state is irrelevant. e.g., Dragon Darts hits one time with two opponents and twice with one opponent.
|
||||
bool32 HasPartner(u32 battler);
|
||||
bool32 HasPartnerIgnoreFlags(u32 battler);
|
||||
// HasPartner respects the Attacks Partner AI flag; HasPartnerIgnoreFlags checks only if a live pokemon is adjacent.
|
||||
bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove);
|
||||
bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove);
|
||||
bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove);
|
||||
@ -290,5 +298,6 @@ u32 GetIncomingMove(u32 battler, u32 opposingBattler, struct AiLogicData *aiData
|
||||
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 HasBattlerSideAbility(u32 battlerDef, u32 ability, struct AiLogicData *aiData);
|
||||
u32 GetThinkingBattler(u32 battler);
|
||||
bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget);
|
||||
|
||||
#endif //GUARD_BATTLE_AI_UTIL_H
|
||||
|
||||
@ -101,6 +101,9 @@
|
||||
#define DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE 35 // both pokemon use Trick Room on turn Trick Room expires in the hopes both opponents used Protect to stall, getting a free refresh on the timer
|
||||
#define TAILWIND_IN_TRICK_ROOM_CHANCE 35 // use Tailwind on turn Trick Room expires in the hopes both opponents used Protect to stall
|
||||
|
||||
#define AI_FLAG_ATTACKS_PARTNER_FOCUSES_PARTNER FALSE // if TRUE, AI_FLAG_ATTACKS_PARTNER prefers attacking the partner over the ally.
|
||||
// This is treated as true regardless during wild battles with AI.
|
||||
|
||||
// AI's desired stat changes for Guard Split and Power Split, treated as %
|
||||
#define GUARD_SPLIT_ALLY_PERCENTAGE 200
|
||||
#define GUARD_SPLIT_ENEMY_PERCENTAGE 50
|
||||
|
||||
@ -34,9 +34,10 @@
|
||||
#define AI_FLAG_PREDICT_MOVE (1 << 26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_SMART_TERA (1 << 27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available).
|
||||
#define AI_FLAG_ASSUME_STAB (1 << 28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ASSUME_STATUS_MOVES (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ASSUME_STATUS_MOVES (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ATTACKS_PARTNER (1 << 30) // AI specific to double battles; AI can deliberately attack its 'partner.'
|
||||
|
||||
#define AI_FLAG_COUNT 30
|
||||
#define AI_FLAG_COUNT 31
|
||||
|
||||
// Flags at and after 32 need different formatting, as in
|
||||
// #define AI_FLAG_PLACEHOLDER ((u64)1 << 32)
|
||||
|
||||
@ -57,7 +57,7 @@ bool32 WeatherChecker(u32 battler, u32 weather, enum FieldEffectOutcome desiredR
|
||||
u32 i;
|
||||
u32 battlersOnSide = 1;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
battlersOnSide = 2;
|
||||
|
||||
for (i = 0; i < battlersOnSide; i++)
|
||||
@ -92,7 +92,7 @@ bool32 FieldStatusChecker(u32 battler, u32 fieldStatus, enum FieldEffectOutcome
|
||||
|
||||
u32 battlersOnSide = 1;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
battlersOnSide = 2;
|
||||
|
||||
for (i = 0; i < battlersOnSide; i++)
|
||||
@ -230,7 +230,7 @@ static enum FieldEffectOutcome BenefitsFromSun(u32 battler)
|
||||
if (DoesAbilityBenefitFromWeather(ability, B_WEATHER_SUN)
|
||||
|| HasLightSensitiveMove(battler)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_FIRE)
|
||||
|| HasBattlerSideMoveWithEffect(battler, EFFECT_HYDRO_STEAM))
|
||||
|| HasMoveWithEffect(battler, EFFECT_HYDRO_STEAM))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasMoveWithFlag(battler, MoveHas50AccuracyInSun) || HasDamagingMoveOfType(battler, TYPE_WATER) || gAiLogicData->abilities[battler] == ABILITY_DRY_SKIN)
|
||||
@ -243,8 +243,7 @@ static enum FieldEffectOutcome BenefitsFromSun(u32 battler)
|
||||
static enum FieldEffectOutcome BenefitsFromSandstorm(u32 battler)
|
||||
{
|
||||
if (DoesAbilityBenefitFromWeather(gAiLogicData->abilities[battler], B_WEATHER_SANDSTORM)
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ROCK)
|
||||
|| HasBattlerSideMoveWithEffect(battler, EFFECT_SHORE_UP))
|
||||
|| IS_BATTLER_OF_TYPE(battler, TYPE_ROCK))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_SAFETY_GOGGLES || IS_BATTLER_ANY_TYPE(battler, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL))
|
||||
@ -314,7 +313,7 @@ static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler)
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
if (grounded && HasBattlerSideUsedMoveWithAdditionalEffect(FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
if (grounded && HasBattlerSideMoveWithAdditionalEffect(FOE(battler), MOVE_EFFECT_SLEEP))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (grounded && ((gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
@ -322,6 +321,10 @@ static enum FieldEffectOutcome BenefitsFromElectricTerrain(u32 battler)
|
||||
|| HasDamagingMoveOfType(battler, TYPE_ELECTRIC)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_RISING_VOLTAGE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
@ -331,21 +334,25 @@ static enum FieldEffectOutcome BenefitsFromGrassyTerrain(u32 battler)
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_GRASSY_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_GRASSY_GLIDE))
|
||||
if (HasMoveWithEffect(battler, EFFECT_GRASSY_GLIDE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
if (HasBattlerSideUsedMoveWithAdditionalEffect(battler, MOVE_EFFECT_FLORAL_HEALING))
|
||||
if (HasMoveWithAdditionalEffect(battler, MOVE_EFFECT_FLORAL_HEALING))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
|
||||
// Weaken spamming Earthquake, Magnitude, and Bulldoze.
|
||||
if (grounded && (HasBattlerSideUsedMoveWithEffect(FOE(battler), EFFECT_EARTHQUAKE)
|
||||
|| HasBattlerSideUsedMoveWithEffect(FOE(battler), EFFECT_MAGNITUDE)))
|
||||
if (grounded && (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_EARTHQUAKE)
|
||||
|| HasBattlerSideMoveWithEffect(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))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
|
||||
return FIELD_EFFECT_NEUTRAL;
|
||||
}
|
||||
|
||||
@ -360,7 +367,7 @@ static enum FieldEffectOutcome BenefitsFromMistyTerrain(u32 battler)
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
bool32 allyGrounded = FALSE;
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
allyGrounded = IsBattlerGrounded(BATTLE_PARTNER(battler));
|
||||
|
||||
if (HasMoveWithEffect(FOE(battler), EFFECT_REST) && IsBattlerGrounded(FOE(battler)))
|
||||
@ -387,12 +394,12 @@ static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
|
||||
if (DoesAbilityBenefitFromFieldStatus(gAiLogicData->abilities[battler], STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(battler, EFFECT_EXPANDING_FORCE))
|
||||
if (HasMoveWithEffect(battler, EFFECT_EXPANDING_FORCE))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
bool32 grounded = IsBattlerGrounded(battler);
|
||||
bool32 allyGrounded = FALSE;
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
allyGrounded = IsBattlerGrounded(BATTLE_PARTNER(battler));
|
||||
|
||||
// don't bother if we're not grounded
|
||||
@ -408,6 +415,9 @@ static enum FieldEffectOutcome BenefitsFromPsychicTerrain(u32 battler)
|
||||
if (grounded && (HasDamagingMoveOfType(battler, TYPE_PSYCHIC)))
|
||||
return FIELD_EFFECT_POSITIVE;
|
||||
|
||||
if (HasBattlerSideMoveWithEffect(FOE(battler), EFFECT_EXPANDING_FORCE))
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
|
||||
if (HasBattlerSideAbility(battler, ABILITY_GALE_WINGS, gAiLogicData)
|
||||
|| HasBattlerSideAbility(battler, ABILITY_TRIAGE, gAiLogicData)
|
||||
|| HasBattlerSideAbility(battler, ABILITY_PRANKSTER, gAiLogicData))
|
||||
@ -429,7 +439,7 @@ static enum FieldEffectOutcome BenefitsFromTrickRoom(u32 battler)
|
||||
else
|
||||
return FIELD_EFFECT_NEGATIVE;
|
||||
}
|
||||
|
||||
|
||||
// First checking if we have enough priority for one pokemon to disregard Trick Room entirely.
|
||||
if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN))
|
||||
{
|
||||
|
||||
@ -51,6 +51,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
@ -91,10 +92,10 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
|
||||
[24] = NULL, // AI_FLAG_PREDICT_INCOMING_MON
|
||||
[25] = AI_CheckPpStall, // AI_FLAG_PP_STALL_PREVENTION
|
||||
[26] = NULL, // AI_FLAG_PREDICT_MOVE
|
||||
[27] = NULL, // Unused
|
||||
[28] = NULL, // Unused
|
||||
[29] = NULL, // Unused
|
||||
[30] = NULL, // Unused
|
||||
[27] = NULL, // AI_FLAG_SMART_TERA
|
||||
[28] = NULL, // AI_FLAG_ASSUME_STAB
|
||||
[29] = NULL, // AI_FLAG_ASSUME_STATUS_MOVES
|
||||
[30] = AI_AttacksPartner, // AI_FLAG_ATTACKS_PARTNER
|
||||
[31] = NULL, // Unused
|
||||
[32] = NULL, // Unused
|
||||
[33] = NULL, // Unused
|
||||
@ -247,6 +248,18 @@ void BattleAI_SetupFlags(void)
|
||||
// smart wild AI
|
||||
gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF);
|
||||
gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF);
|
||||
|
||||
// The check is here because wild natural enemies are not symmetrical.
|
||||
if (B_WILD_NATURAL_ENEMIES && IsDoubleBattle())
|
||||
{
|
||||
u32 speciesLeft = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES);
|
||||
u32 speciesRight = GetMonData(&gEnemyParty[1], MON_DATA_SPECIES);
|
||||
if (IsNaturalEnemy(speciesLeft, speciesRight))
|
||||
gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] |= AI_FLAG_ATTACKS_PARTNER;
|
||||
if (IsNaturalEnemy(speciesRight, speciesLeft))
|
||||
gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] |= AI_FLAG_ATTACKS_PARTNER;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1059,6 +1072,9 @@ void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink,
|
||||
// AI_FLAG_CHECK_BAD_MOVE - decreases move scores
|
||||
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
// move data
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
|
||||
u32 nonVolatileStatus = GetMoveNonVolatileStatus(move);
|
||||
@ -1066,7 +1082,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
|
||||
struct AiLogicData *aiData = gAiLogicData;
|
||||
uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex];
|
||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||
bool32 isBattle1v1 = IsBattle1v1();
|
||||
bool32 hasTwoOpponents = HasTwoOpponents(battlerAtk);
|
||||
bool32 hasPartner = HasPartner(battlerAtk);
|
||||
u32 i;
|
||||
u32 weather;
|
||||
u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData);
|
||||
@ -1074,9 +1092,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
u32 abilityDef = aiData->abilities[battlerDef];
|
||||
s32 atkPriority = GetBattleMovePriority(battlerAtk, abilityAtk, move);
|
||||
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
moveType = GetBattleMoveType(move);
|
||||
|
||||
@ -1208,7 +1223,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
} // def ability checks
|
||||
|
||||
// target partner ability checks & not attacking partner
|
||||
if (isDoubleBattle)
|
||||
if (hasTwoOpponents)
|
||||
{
|
||||
switch (aiData->abilities[BATTLE_PARTNER(battlerDef)])
|
||||
{
|
||||
@ -1479,7 +1494,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_ROTOTILLER:
|
||||
if (isDoubleBattle)
|
||||
if (hasPartner)
|
||||
{
|
||||
if (!(IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS)
|
||||
&& IsBattlerGrounded(battlerAtk)
|
||||
@ -1510,12 +1525,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
}
|
||||
else if (!isDoubleBattle)
|
||||
else if (!hasPartner)
|
||||
{
|
||||
ADJUST_SCORE(-10); // no partner and our stats wont rise, so don't use
|
||||
}
|
||||
|
||||
if (isDoubleBattle)
|
||||
if (hasPartner)
|
||||
{
|
||||
if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS)
|
||||
{
|
||||
@ -1541,12 +1556,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
else if (!BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF))
|
||||
ADJUST_SCORE(-8);
|
||||
}
|
||||
else if (!isDoubleBattle)
|
||||
else if (!hasPartner)
|
||||
{
|
||||
ADJUST_SCORE(-10); // our stats wont rise from this move
|
||||
}
|
||||
|
||||
if (isDoubleBattle)
|
||||
if (hasPartner)
|
||||
{
|
||||
if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_PLUS || aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_MINUS)
|
||||
{
|
||||
@ -1875,7 +1890,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-9);
|
||||
break;
|
||||
case EFFECT_PERISH_SONG:
|
||||
if (isDoubleBattle)
|
||||
if (!isBattle1v1)
|
||||
{
|
||||
if (CountUsablePartyMons(battlerAtk) == 0
|
||||
&& aiData->abilities[battlerAtk] != ABILITY_SOUNDPROOF
|
||||
@ -1908,23 +1923,23 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_SANDSTORM:
|
||||
if (weather & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
|| (HasPartner(battlerAtk) && IsMoveEffectWeather(aiData->partnerMove)))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_SUNNY_DAY:
|
||||
if (weather & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
|| (HasPartner(battlerAtk) && IsMoveEffectWeather(aiData->partnerMove)))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_RAIN_DANCE:
|
||||
if (weather & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
|| (HasPartner(battlerAtk) && IsMoveEffectWeather(aiData->partnerMove)))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
case EFFECT_SNOWSCAPE:
|
||||
if (weather & (B_WEATHER_ICY_ANY | B_WEATHER_PRIMAL_ANY)
|
||||
|| IsMoveEffectWeather(aiData->partnerMove))
|
||||
|| (HasPartner(battlerAtk) && IsMoveEffectWeather(aiData->partnerMove)))
|
||||
ADJUST_SCORE(-8);
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
@ -2022,8 +2037,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_FOLLOW_ME:
|
||||
case EFFECT_HELPING_HAND:
|
||||
if (!isDoubleBattle
|
||||
|| !IsBattlerAlive(BATTLE_PARTNER(battlerAtk))
|
||||
if (!hasPartner
|
||||
|| PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove)
|
||||
|| (aiData->partnerMove != MOVE_NONE && IsBattleMoveStatus(aiData->partnerMove))
|
||||
|| gBattleStruct->monToSwitchIntoId[BATTLE_PARTNER(battlerAtk)] != PARTY_SIZE) //Partner is switching out.
|
||||
@ -2102,11 +2116,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_FLOWER_SHIELD:
|
||||
if (!IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GRASS)
|
||||
&& !(isDoubleBattle && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS)))
|
||||
&& !(hasPartner && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), TYPE_GRASS)))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_AROMATIC_MIST:
|
||||
if (!isDoubleBattle || gBattleMons[BATTLE_PARTNER(battlerAtk)].hp == 0 || !BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF))
|
||||
if (!hasPartner || !BattlerStatCanRise(BATTLE_PARTNER(battlerAtk), aiData->abilities[BATTLE_PARTNER(battlerAtk)], STAT_SPDEF))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_BIDE:
|
||||
@ -2243,7 +2257,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
decreased = TRUE;
|
||||
}
|
||||
case PROTECT_CRAFTY_SHIELD:
|
||||
if (!isDoubleBattle)
|
||||
if (!hasPartner)
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
decreased = TRUE;
|
||||
@ -2279,7 +2293,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
else if (gDisableStructs[battlerAtk].protectUses == 1 && Random() % 100 < 50)
|
||||
{
|
||||
if (!isDoubleBattle)
|
||||
if (isBattle1v1)
|
||||
ADJUST_SCORE(-6);
|
||||
else
|
||||
ADJUST_SCORE(-10); //Don't try double protecting in doubles
|
||||
@ -2327,7 +2341,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
}
|
||||
|
||||
if (isDoubleBattle)
|
||||
if (hasPartner)
|
||||
{
|
||||
if (IsHazardMove(aiData->partnerMove) // partner is going to set up hazards
|
||||
&& AI_IsFaster(BATTLE_PARTNER(battlerAtk), battlerAtk, aiData->partnerMove, predictedMove, CONSIDER_PRIORITY)) // partner is going to set up before the potential Defog
|
||||
@ -2540,7 +2554,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_PLEDGE:
|
||||
if (isDoubleBattle && gBattleMons[BATTLE_PARTNER(battlerAtk)].hp > 0)
|
||||
if (hasPartner && gBattleMons[BATTLE_PARTNER(battlerAtk)].hp > 0)
|
||||
{
|
||||
if (aiData->partnerMove != MOVE_NONE
|
||||
&& GetMoveEffect(aiData->partnerMove) == EFFECT_PLEDGE
|
||||
@ -2723,7 +2737,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
ADJUST_SCORE(-10);
|
||||
else if (isDoubleBattle)
|
||||
else if (hasPartner)
|
||||
{
|
||||
if (!IsTargetingPartner(battlerAtk, battlerDef))
|
||||
ADJUST_SCORE(-10);
|
||||
@ -2742,14 +2756,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (!isDoubleBattle
|
||||
if (!hasPartner
|
||||
|| AI_IsSlower(battlerAtk, battlerDef, move, predictedMove, CONSIDER_PRIORITY)
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_AFTER_YOU:
|
||||
if (!IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| !isDoubleBattle
|
||||
|| !hasPartner
|
||||
|| AI_IsSlower(battlerAtk, battlerDef, move, predictedMove, CONSIDER_PRIORITY)
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
@ -2976,15 +2990,16 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
moveType = GetBattleMoveType(move);
|
||||
|
||||
|
||||
bool32 hasPartner = HasPartner(battlerAtk);
|
||||
u32 friendlyFireThreshold = GetFriendlyFireKOThreshold(battlerAtk);
|
||||
u32 noOfHitsToKOPartner = GetNoOfHitsToKOBattler(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING);
|
||||
bool32 wouldPartnerFaint = CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING)
|
||||
&& !partnerProtecting && IsBattlerAlive(battlerAtkPartner);
|
||||
bool32 wouldPartnerFaint = hasPartner && CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING)
|
||||
&& !partnerProtecting;
|
||||
bool32 isFriendlyFireOK = !wouldPartnerFaint && (noOfHitsToKOPartner == 0 || noOfHitsToKOPartner > friendlyFireThreshold);
|
||||
|
||||
// check what effect partner is using
|
||||
if (aiData->partnerMove != 0)
|
||||
if (aiData->partnerMove != 0 && hasPartner)
|
||||
{
|
||||
switch (partnerEffect)
|
||||
{
|
||||
@ -3040,7 +3055,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_HELPING_HAND:
|
||||
if (!IsBattlerAlive(battlerAtkPartner) || !HasDamagingMove(battlerAtkPartner))
|
||||
if (!hasPartner || !HasDamagingMove(battlerAtkPartner))
|
||||
ADJUST_SCORE(-20);
|
||||
break;
|
||||
case EFFECT_PERISH_SONG:
|
||||
@ -3076,7 +3091,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
// Both Pokemon use Trick Room on the final turn of Trick Room to anticipate both opponents Protecting to stall out.
|
||||
// This unsets Trick Room and resets it with a full timer.
|
||||
case EFFECT_TRICK_ROOM:
|
||||
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter
|
||||
if (hasPartner && gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter
|
||||
&& ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM)
|
||||
&& HasMoveWithEffect(battlerAtkPartner, MOVE_TRICK_ROOM)
|
||||
&& RandomPercentage(RNG_AI_REFRESH_TRICK_ROOM_ON_LAST_TURN, DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE))
|
||||
@ -3702,9 +3717,8 @@ static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, u32 move, u32 mov
|
||||
u32 partnerBattler = BATTLE_PARTNER(battlerAtk);
|
||||
u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex, AI_ATTACKING);
|
||||
u32 friendlyFireThreshold = GetFriendlyFireKOThreshold(battlerAtk);
|
||||
return (IsDoubleBattle()
|
||||
return (HasPartnerIgnoreFlags(battlerAtk)
|
||||
&& noOfHitsToFaintPartner != 0 // Immunity check
|
||||
&& IsBattlerAlive(partnerBattler)
|
||||
&& GetBattlerMoveTargetType(battlerAtk, move) == MOVE_TARGET_FOES_AND_ALLY
|
||||
&& !(noOfHitsToFaintPartner < friendlyFireThreshold && hitsToFaintOpposingBattler == 1)
|
||||
&& noOfHitsToFaintPartner < (friendlyFireThreshold * 2));
|
||||
@ -3906,7 +3920,10 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData);
|
||||
u32 predictedType = GetMoveType(predictedMove);
|
||||
u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove);
|
||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||
bool32 isBattle1v1 = IsBattle1v1();
|
||||
bool32 hasTwoOpponents = HasTwoOpponents(battlerAtk);
|
||||
bool32 hasPartner = HasPartner(battlerAtk);
|
||||
bool32 moveTargetsBothOpponents = hasTwoOpponents && (gMovesInfo[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS));
|
||||
u32 i;
|
||||
|
||||
// The AI should understand that while Dynamaxed, status moves function like Protect.
|
||||
@ -4083,7 +4100,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
if (AnyStatIsRaised(BATTLE_PARTNER(battlerAtk))
|
||||
|| PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
|
||||
break;
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, moveTargetsBothOpponents);
|
||||
break;
|
||||
case EFFECT_ROAR:
|
||||
if ((IsSoundMove(move) && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF)
|
||||
@ -4091,7 +4108,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
break;
|
||||
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
break;
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, moveTargetsBothOpponents);
|
||||
break;
|
||||
case EFFECT_MULTI_HIT:
|
||||
case EFFECT_TRIPLE_KICK:
|
||||
@ -4228,7 +4245,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_HIT_ESCAPE:
|
||||
case EFFECT_PARTING_SHOT:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (!IsDoubleBattle())
|
||||
if (!hasPartner)
|
||||
{
|
||||
switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, movesetIndex))
|
||||
{
|
||||
@ -4346,7 +4363,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
}
|
||||
else if (isDoubleBattle && GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
|
||||
else if (hasPartner && GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_TELEPATHY)
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
@ -4516,7 +4533,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
|
||||
break;
|
||||
case EFFECT_SEMI_INVULNERABLE:
|
||||
if (predictedMove != MOVE_NONE && !isDoubleBattle)
|
||||
if (predictedMove != MOVE_NONE && isBattle1v1)
|
||||
{
|
||||
enum BattleMoveEffects predictedEffect = GetMoveEffect(predictedMove);
|
||||
if ((AI_IsFaster(battlerAtk, battlerDef, move, predictedMove, CONSIDER_PRIORITY))
|
||||
@ -4565,11 +4582,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
IncreaseConfusionScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case EFFECT_FURY_CUTTER:
|
||||
if (!isDoubleBattle && aiData->holdEffects[battlerAtk] == HOLD_EFFECT_METRONOME)
|
||||
if (isBattle1v1 && aiData->holdEffects[battlerAtk] == HOLD_EFFECT_METRONOME)
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
break;
|
||||
case EFFECT_ATTRACT:
|
||||
if (!isDoubleBattle
|
||||
if (isBattle1v1
|
||||
&& (AI_IsSlower(battlerAtk, battlerDef, move, predictedMove, CONSIDER_PRIORITY))
|
||||
&& BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef]))
|
||||
break; // Don't use if the attract won't have a change to activate
|
||||
@ -4594,7 +4611,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
else if (!AreAnyHazardsOnSide(GetBattlerSide(battlerDef)) || CountUsablePartyMons(battlerDef) == 0) //Don't blow away hazards if you set them up
|
||||
{
|
||||
if (isDoubleBattle)
|
||||
if (hasPartner)
|
||||
{
|
||||
if (IsHazardMove(aiData->partnerMove) // Partner is going to set up hazards
|
||||
&& AI_IsSlower(battlerAtk, BATTLE_PARTNER(battlerAtk), move, predictedMove, CONSIDER_PRIORITY)) // Partner going first
|
||||
@ -4606,11 +4623,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_TORMENT:
|
||||
break;
|
||||
case EFFECT_FOLLOW_ME:
|
||||
if (isDoubleBattle
|
||||
if (hasPartner
|
||||
&& GetMoveTarget(move) == MOVE_TARGET_USER
|
||||
&& !IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
|
||||
&& (!IsPowderMove(move) || IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])) // Rage Powder doesn't affect powder immunities
|
||||
&& IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
&& (!IsPowderMove(move) || IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])))
|
||||
// Rage Powder doesn't affect powder immunities
|
||||
{
|
||||
u32 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)];
|
||||
if (predictedMoveOnPartner != MOVE_NONE && !IsBattleMoveStatus(predictedMoveOnPartner))
|
||||
@ -4684,7 +4701,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case HOLD_EFFECT_EJECT_BUTTON:
|
||||
//if (!IsRaidBattle() && GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX && gNewBS->dynamaxData.timer[battlerDef] > 1 &&
|
||||
if (HasDamagingMove(battlerAtk)
|
||||
|| (isDoubleBattle && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && HasDamagingMove(BATTLE_PARTNER(battlerAtk))))
|
||||
|| (hasPartner && HasDamagingMove(BATTLE_PARTNER(battlerAtk))))
|
||||
ADJUST_SCORE(DECENT_EFFECT); // Force 'em out next turn
|
||||
break;
|
||||
default:
|
||||
@ -5013,7 +5030,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
}
|
||||
break;
|
||||
case EFFECT_PLEDGE:
|
||||
if (isDoubleBattle && HasMoveWithEffect(BATTLE_PARTNER(battlerAtk), EFFECT_PLEDGE))
|
||||
if (hasPartner && HasMoveWithEffect(BATTLE_PARTNER(battlerAtk), EFFECT_PLEDGE))
|
||||
ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move
|
||||
break;
|
||||
case EFFECT_TRICK_ROOM:
|
||||
@ -5030,7 +5047,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_NONE && aiData->holdEffects[battlerDef] != HOLD_EFFECT_NONE)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
if (isDoubleBattle && aiData->holdEffects[BATTLE_PARTNER(battlerAtk)] == HOLD_EFFECT_NONE && aiData->holdEffects[BATTLE_PARTNER(battlerDef)] != HOLD_EFFECT_NONE)
|
||||
if (!isBattle1v1 && aiData->holdEffects[BATTLE_PARTNER(battlerAtk)] == HOLD_EFFECT_NONE && aiData->holdEffects[BATTLE_PARTNER(battlerDef)] != HOLD_EFFECT_NONE)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
break;
|
||||
case EFFECT_WONDER_ROOM:
|
||||
@ -5125,7 +5142,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
break;
|
||||
case EFFECT_QUASH:
|
||||
if (isDoubleBattle && AI_IsSlower(BATTLE_PARTNER(battlerAtk), battlerDef, aiData->partnerMove, predictedMove, CONSIDER_PRIORITY))
|
||||
if (hasPartner && AI_IsSlower(BATTLE_PARTNER(battlerAtk), battlerDef, aiData->partnerMove, predictedMove, CONSIDER_PRIORITY))
|
||||
ADJUST_SCORE(DECENT_EFFECT); // Attacker partner wouldn't go before target
|
||||
break;
|
||||
case EFFECT_TAILWIND:
|
||||
@ -5133,7 +5150,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
break;
|
||||
case EFFECT_LUCKY_CHANT:
|
||||
if (!isDoubleBattle && CountUsablePartyMons(battlerDef) > 0)
|
||||
if (isBattle1v1 && CountUsablePartyMons(battlerDef) > 0)
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
break;
|
||||
case EFFECT_MAGNET_RISE:
|
||||
@ -5431,7 +5448,7 @@ case EFFECT_GUARD_SPLIT:
|
||||
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case MOVE_EFFECT_CLEAR_SMOG:
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, FALSE);
|
||||
score += AI_TryToClearStats(battlerAtk, battlerDef, moveTargetsBothOpponents);
|
||||
break;
|
||||
case MOVE_EFFECT_BUG_BITE: // And pluck
|
||||
if (gBattleMons[battlerDef].volatiles.substitute || aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
|
||||
@ -5710,6 +5727,31 @@ static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
return score;
|
||||
}
|
||||
|
||||
// Adds score bonus to targeting "partner"
|
||||
static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
if (battlerDef == BATTLE_PARTNER(battlerAtk)
|
||||
// natural enemies in wild battles try to kill each other
|
||||
&& ((IsNaturalEnemy(gBattleMons[battlerAtk].species, gBattleMons[battlerDef].species) && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)))
|
||||
|| AI_FLAG_ATTACKS_PARTNER_FOCUSES_PARTNER))
|
||||
{
|
||||
u32 movesetIndex = gAiThinkingStruct->movesetIndex;
|
||||
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, AI_ATTACKING))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
|
||||
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))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
|
||||
if (hitsToKO > 0)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
// Prefers moves that are good for baton pass
|
||||
static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
|
||||
@ -1337,7 +1337,7 @@ bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
|
||||
enum BattleMoveEffects effect = GetMoveEffect(move);
|
||||
if (!AI_BattlerAtMaxHp(battlerTarget) || effect == EFFECT_MULTI_HIT)
|
||||
return FALSE;
|
||||
if (GetMoveStrikeCount(move) > 1 && !(effect == EFFECT_DRAGON_DARTS && IsValidDoubleBattle(battlerTarget)))
|
||||
if (GetMoveStrikeCount(move) > 1 && !(effect == EFFECT_DRAGON_DARTS && !HasTwoOpponents(battler)))
|
||||
return FALSE;
|
||||
if (gAiLogicData->holdEffects[battlerTarget] == HOLD_EFFECT_FOCUS_SASH)
|
||||
return TRUE;
|
||||
@ -1947,7 +1947,7 @@ s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDoubleBattle())
|
||||
if (!IsBattle1v1())
|
||||
score -= (2 * min(uses, 3));
|
||||
else
|
||||
score -= (min(uses, 3));
|
||||
@ -2225,7 +2225,7 @@ bool32 HasBattlerSideMoveWithEffect(u32 battler, u32 effect)
|
||||
{
|
||||
if (HasMoveWithEffect(battler, effect))
|
||||
return TRUE;
|
||||
if (IsDoubleBattle() && HasMoveWithEffect(BATTLE_OPPOSITE(battler), effect))
|
||||
if (HasPartnerIgnoreFlags(battler) && HasMoveWithEffect(BATTLE_PARTNER(battler), effect))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -2240,7 +2240,7 @@ bool32 HasBattlerSideUsedMoveWithEffect(u32 battler, u32 effect)
|
||||
{
|
||||
if (GetMoveEffect(gBattleHistory->usedMoves[battler][i]) == effect)
|
||||
return TRUE;
|
||||
if (IsDoubleBattle() && GetMoveEffect(gBattleHistory->usedMoves[BATTLE_OPPOSITE(battler)][i]) == effect)
|
||||
if (HasPartnerIgnoreFlags(battler) && GetMoveEffect(gBattleHistory->usedMoves[BATTLE_PARTNER(battler)][i]) == effect)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -2295,7 +2295,7 @@ bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, u32 moveEffect)
|
||||
{
|
||||
if (HasMoveWithAdditionalEffect(battler, moveEffect))
|
||||
return TRUE;
|
||||
if (IsDoubleBattle() && HasMoveWithAdditionalEffect(BATTLE_OPPOSITE(battler), moveEffect))
|
||||
if (HasPartnerIgnoreFlags(battler) && HasMoveWithAdditionalEffect(BATTLE_PARTNER(battler), moveEffect))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -2310,7 +2310,7 @@ bool32 HasBattlerSideUsedMoveWithAdditionalEffect(u32 battler, u32 moveEffect)
|
||||
{
|
||||
if (MoveHasAdditionalEffect(gBattleHistory->usedMoves[battler][i], moveEffect))
|
||||
return TRUE;
|
||||
if (IsDoubleBattle() && MoveHasAdditionalEffect(gBattleHistory->usedMoves[BATTLE_OPPOSITE(battler)][i], moveEffect))
|
||||
if (HasPartnerIgnoreFlags(battler) && MoveHasAdditionalEffect(gBattleHistory->usedMoves[BATTLE_PARTNER(battler)][i], moveEffect))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -3073,7 +3073,7 @@ enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 mov
|
||||
if (PartyBattlerShouldAvoidHazards(battlerAtk, battlerToSwitch))
|
||||
return DONT_PIVOT;
|
||||
|
||||
if (!IsDoubleBattle())
|
||||
if (IsBattle1v1())
|
||||
{
|
||||
if (CountUsablePartyMons(battlerAtk) == 0)
|
||||
return CAN_TRY_PIVOT; // can't switch, but attack might still be useful
|
||||
@ -3543,7 +3543,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof)
|
||||
|
||||
party = GetBattlerParty(battlerId);
|
||||
|
||||
if (IsDoubleBattle())
|
||||
if (HasPartner(battlerId))
|
||||
{
|
||||
battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
battlerOnField2 = gBattlerPartyIndexes[GetPartnerBattler(battlerId)];
|
||||
@ -3595,7 +3595,7 @@ u32 GetBattlerSideSpeedAverage(u32 battler)
|
||||
numBattlersAlive++;
|
||||
}
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
{
|
||||
speed2 = gAiLogicData->speedStats[BATTLE_PARTNER(battler)];
|
||||
numBattlersAlive++;
|
||||
@ -3709,17 +3709,48 @@ bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects mo
|
||||
}
|
||||
|
||||
// Partner Logic
|
||||
bool32 IsValidDoubleBattle(u32 battlerAtk)
|
||||
bool32 IsBattle1v1()
|
||||
{
|
||||
if (IsDoubleBattle()
|
||||
&& ((IsBattlerAlive(BATTLE_OPPOSITE(battlerAtk)) && IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerAtk)))) || IsBattlerAlive(BATTLE_PARTNER(battlerAtk))))
|
||||
&& ((IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_PLAYER_RIGHT))
|
||||
|| (IsBattlerAlive(B_POSITION_OPPONENT_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_RIGHT))))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool32 HasTwoOpponents(u32 battler)
|
||||
{
|
||||
if (IsDoubleBattle()
|
||||
&& IsBattlerAlive(FOE(battler)) && IsBattlerAlive(BATTLE_PARTNER(FOE(battler))))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// TODO: Handling for when the 'partner' is not actually a partner, a la Battle Royale or B_WILD_NATURAL_ENEMIES
|
||||
bool32 HasPartner(u32 battler)
|
||||
{
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
{
|
||||
if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ATTACKS_PARTNER)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 HasPartnerIgnoreFlags(u32 battler)
|
||||
{
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_ATTACKS_PARTNER)
|
||||
return FALSE;
|
||||
return ((battlerAtk) == (battlerDef ^ BIT_FLANK));
|
||||
}
|
||||
|
||||
@ -3738,7 +3769,7 @@ u32 GetAllyChosenMove(u32 battlerId)
|
||||
//PARTNER_MOVE_EFFECT_IS_SAME
|
||||
bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
if (GetMoveEffect(move) == GetMoveEffect(partnerMove)
|
||||
@ -3753,7 +3784,7 @@ bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32
|
||||
//PARTNER_MOVE_EFFECT_IS_SAME_NO_TARGET
|
||||
bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
if (GetMoveEffect(move) == GetMoveEffect(partnerMove)
|
||||
@ -3765,7 +3796,7 @@ bool32 PartnerHasSameMoveEffectWithoutTarget(u32 battlerAtkPartner, u32 move, u3
|
||||
//PARTNER_MOVE_EFFECT_IS_STATUS_SAME_TARGET
|
||||
bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
enum BattleMoveEffects partnerEffect = GetMoveEffect(partnerMove);
|
||||
@ -3799,7 +3830,7 @@ bool32 IsMoveEffectWeather(u32 move)
|
||||
//PARTNER_MOVE_EFFECT_IS_TERRAIN
|
||||
bool32 PartnerMoveEffectIsTerrain(u32 battlerAtkPartner, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
enum BattleMoveEffects partnerEffect = GetMoveEffect(partnerMove);
|
||||
@ -3816,7 +3847,7 @@ bool32 PartnerMoveEffectIsTerrain(u32 battlerAtkPartner, u32 partnerMove)
|
||||
//PARTNER_MOVE_EFFECT_IS
|
||||
bool32 PartnerMoveEffectIs(u32 battlerAtkPartner, u32 partnerMove, enum BattleMoveEffects effectCheck)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
if (partnerMove != MOVE_NONE && GetMoveEffect(partnerMove) == effectCheck)
|
||||
@ -3828,7 +3859,7 @@ bool32 PartnerMoveEffectIs(u32 battlerAtkPartner, u32 partnerMove, enum BattleMo
|
||||
//PARTNER_MOVE_IS_TAILWIND_TRICKROOM
|
||||
bool32 PartnerMoveIs(u32 battlerAtkPartner, u32 partnerMove, u32 moveCheck)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
if (partnerMove != MOVE_NONE && partnerMove == moveCheck)
|
||||
@ -3839,7 +3870,7 @@ bool32 PartnerMoveIs(u32 battlerAtkPartner, u32 partnerMove, u32 moveCheck)
|
||||
//PARTNER_MOVE_IS_SAME
|
||||
bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, u32 move, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
|
||||
if (partnerMove != MOVE_NONE && move == partnerMove && gBattleStruct->moveTarget[battlerAtkPartner] == battlerDef)
|
||||
@ -3850,7 +3881,7 @@ bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, u32 mo
|
||||
//PARTNER_MOVE_IS_SAME_NO_TARGET
|
||||
bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, u32 move, u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!HasPartner(battlerAtkPartner))
|
||||
return FALSE;
|
||||
if (partnerMove != MOVE_NONE && move == partnerMove)
|
||||
return TRUE;
|
||||
@ -3859,7 +3890,7 @@ bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, u32 move, u32 partnerMov
|
||||
|
||||
bool32 PartnerMoveActivatesSleepClause(u32 partnerMove)
|
||||
{
|
||||
if (!IsDoubleBattle() || !IsSleepClauseEnabled())
|
||||
if (IsBattle1v1() || !IsSleepClauseEnabled())
|
||||
return FALSE;
|
||||
return IsMoveSleepClauseTrigger(partnerMove);
|
||||
}
|
||||
@ -3894,7 +3925,7 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsDoubleBattle())
|
||||
if (IsBattle1v1())
|
||||
{
|
||||
switch (GetMoveEffect(move))
|
||||
{
|
||||
@ -4086,7 +4117,7 @@ bool32 PartyHasMoveCategory(u32 battlerId, enum DamageCategory category)
|
||||
|
||||
bool32 SideHasMoveCategory(u32 battlerId, enum DamageCategory category)
|
||||
{
|
||||
if (IsDoubleBattle())
|
||||
if (HasPartnerIgnoreFlags(battlerId))
|
||||
{
|
||||
if (HasMoveWithCategory(battlerId, category) || HasMoveWithCategory(BATTLE_PARTNER(battlerId), category))
|
||||
return TRUE;
|
||||
@ -4612,7 +4643,7 @@ void DecideTerastal(u32 battler)
|
||||
return;
|
||||
|
||||
// TODO: Currently only single battles are considered.
|
||||
if (IsDoubleBattle())
|
||||
if (!IsBattle1v1())
|
||||
return;
|
||||
|
||||
// TODO: A lot of these checks are most effective for an omnicient ai.
|
||||
@ -5030,7 +5061,7 @@ bool32 HasBattlerSideAbility(u32 battler, u32 ability, struct AiLogicData *aiDat
|
||||
{
|
||||
if (aiData->abilities[battler] == ability)
|
||||
return TRUE;
|
||||
if (IsDoubleBattle() && gAiLogicData->abilities[BATTLE_PARTNER(battler)] == ability)
|
||||
if (HasPartnerIgnoreFlags(battler) && gAiLogicData->abilities[BATTLE_PARTNER(battler)] == ability)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@ -5041,6 +5072,8 @@ u32 GetFriendlyFireKOThreshold(u32 battler)
|
||||
return FRIENDLY_FIRE_RISKY_THRESHOLD;
|
||||
if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_CONSERVATIVE)
|
||||
return FRIENDLY_FIRE_CONSERVATIVE_THRESHOLD;
|
||||
if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ATTACKS_PARTNER)
|
||||
return 0;
|
||||
|
||||
return FRIENDLY_FIRE_NORMAL_THRESHOLD;
|
||||
}
|
||||
@ -5192,7 +5225,7 @@ bool32 CanEffectChangeAbility(u32 battlerAtk, u32 battlerDef, u32 effect, struct
|
||||
if (hasSameAbility || gAbilitiesInfo[atkAbility].cantBeSuppressed || gAbilitiesInfo[defAbility].cantBeCopied)
|
||||
return FALSE;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
if (HasPartnerIgnoreFlags(battlerAtk))
|
||||
{
|
||||
u32 partnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)];
|
||||
if (gAbilitiesInfo[partnerAbility].cantBeSuppressed)
|
||||
@ -5293,7 +5326,7 @@ void AbilityChangeScore(u32 battlerAtk, u32 battlerDef, u32 effect, s32 *score,
|
||||
bool32 attackerHasBadAbility = (gAbilitiesInfo[abilityAtk].aiRating < 0);
|
||||
s32 currentAbilityScore, transferredAbilityScore = 0;
|
||||
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
if (HasPartner(battlerAtk))
|
||||
{
|
||||
partnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)];
|
||||
if (!(gAbilitiesInfo[partnerAbility].cantBeSuppressed) && (gAbilitiesInfo[partnerAbility].aiRating < 0))
|
||||
@ -5387,7 +5420,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
case ABILITY_FRIEND_GUARD:
|
||||
case ABILITY_POWER_SPOT:
|
||||
case ABILITY_VICTORY_STAR:
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)) && aiData->abilities[BATTLE_PARTNER(battler)] != ability)
|
||||
if (HasPartner(battler) && aiData->abilities[BATTLE_PARTNER(battler)] != ability)
|
||||
return BEST_EFFECT;
|
||||
break;
|
||||
case ABILITY_GUTS:
|
||||
@ -5414,7 +5447,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(FOE(battler))))
|
||||
if (HasTwoOpponents(battler))
|
||||
{
|
||||
abilityDef = aiData->abilities[BATTLE_PARTNER(FOE(battler))];
|
||||
if (DoesIntimidateRaiseStats(abilityDef))
|
||||
@ -5457,7 +5490,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
|
||||
case ABILITY_SWORD_OF_RUIN:
|
||||
case ABILITY_TABLETS_OF_RUIN:
|
||||
case ABILITY_VESSEL_OF_RUIN:
|
||||
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||||
if (HasPartner(battler))
|
||||
{
|
||||
if (aiData->abilities[BATTLE_PARTNER(battler)] != ability)
|
||||
return GOOD_EFFECT;
|
||||
@ -5480,3 +5513,28 @@ u32 GetThinkingBattler(u32 battler)
|
||||
return gAiLogicData->battlerDoingPrediction;
|
||||
return battler;
|
||||
}
|
||||
|
||||
bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget)
|
||||
{
|
||||
if (B_WILD_NATURAL_ENEMIES != TRUE)
|
||||
return FALSE;
|
||||
|
||||
switch (speciesAttacker)
|
||||
{
|
||||
case SPECIES_ZANGOOSE:
|
||||
return (speciesTarget == SPECIES_SEVIPER);
|
||||
case SPECIES_SEVIPER:
|
||||
return (speciesTarget == SPECIES_ZANGOOSE);
|
||||
case SPECIES_HEATMOR:
|
||||
return (speciesTarget == SPECIES_DURANT);
|
||||
case SPECIES_DURANT:
|
||||
return (speciesTarget == SPECIES_HEATMOR);
|
||||
case SPECIES_SABLEYE:
|
||||
return (speciesTarget == SPECIES_CARBINK);
|
||||
case SPECIES_MAREANIE:
|
||||
return (speciesTarget == SPECIES_CORSOLA);
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -473,31 +473,15 @@ static void OpponentHandleChooseMove(u32 battler)
|
||||
target = GetBattlerAtPosition(Random() & 2);
|
||||
} while (!CanTargetBattler(battler, target, move));
|
||||
|
||||
// Don't bother to loop through table if the move can't attack ally
|
||||
// Don't bother to check if they're enemies if the move can't attack ally
|
||||
if (B_WILD_NATURAL_ENEMIES == TRUE && !(GetBattlerMoveTargetType(battler, move) & MOVE_TARGET_BOTH))
|
||||
{
|
||||
u16 i, speciesAttacker, speciesTarget, isPartnerEnemy = FALSE;
|
||||
static const u16 naturalEnemies[][2] =
|
||||
{
|
||||
// Attacker Target
|
||||
{SPECIES_ZANGOOSE, SPECIES_SEVIPER},
|
||||
{SPECIES_SEVIPER, SPECIES_ZANGOOSE},
|
||||
{SPECIES_HEATMOR, SPECIES_DURANT},
|
||||
{SPECIES_DURANT, SPECIES_HEATMOR},
|
||||
{SPECIES_SABLEYE, SPECIES_CARBINK},
|
||||
{SPECIES_MAREANIE, SPECIES_CORSOLA},
|
||||
};
|
||||
u32 speciesAttacker, speciesTarget;
|
||||
speciesAttacker = gBattleMons[battler].species;
|
||||
speciesTarget = gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(battler))].species;
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(naturalEnemies); i++)
|
||||
{
|
||||
if (speciesAttacker == naturalEnemies[i][0] && speciesTarget == naturalEnemies[i][1])
|
||||
{
|
||||
isPartnerEnemy = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool32 isPartnerEnemy = IsNaturalEnemy(speciesAttacker, speciesTarget);
|
||||
|
||||
if (isPartnerEnemy && CanTargetBattler(battler, target, move))
|
||||
BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8));
|
||||
else
|
||||
|
||||
@ -510,7 +510,7 @@ AI_DOUBLE_BATTLE_TEST("AI sets up weather for its ally")
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_TORNADUS) { Item(ITEM_SAFETY_GOGGLES); Ability(ABILITY_PRANKSTER); Moves(goodWeather, badWeather, MOVE_RETURN, MOVE_TAUNT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(weatherTrigger, MOVE_EARTH_POWER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); Moves(weatherTrigger, MOVE_EARTH_POWER); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponentLeft, goodWeather); }
|
||||
}
|
||||
|
||||
78
test/battle/ai/ai_flag_attacks_partner.c
Normal file
78
test/battle/ai/ai_flag_attacks_partner.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
#include "battle_ai_util.h"
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI_FLAG_ATTACKS_PARTNER is willing to kill either the partner or the player")
|
||||
{
|
||||
ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == MOVE_TARGET_FOES_AND_ALLY);
|
||||
|
||||
u32 move, level;
|
||||
|
||||
PARAMETRIZE { move = MOVE_BRUTAL_SWING; level = 1; }
|
||||
PARAMETRIZE { move = MOVE_MIGHTY_CLEAVE; level = 1; }
|
||||
PARAMETRIZE { move = MOVE_BRUTAL_SWING; level = 100; }
|
||||
PARAMETRIZE { move = MOVE_MIGHTY_CLEAVE; level = 100; }
|
||||
PARAMETRIZE { move = MOVE_BRUTAL_SWING; level = 50; }
|
||||
PARAMETRIZE { move = MOVE_MIGHTY_CLEAVE; level = 50; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMovePower(MOVE_OVERDRIVE) == 80);
|
||||
ASSUME(GetMovePower(MOVE_BRUTAL_SWING) == 60);
|
||||
ASSUME(GetMovePower(MOVE_MIGHTY_CLEAVE) == 95);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZIGZAGOON].baseAttack == gSpeciesInfo[SPECIES_ZIGZAGOON].baseSpAttack);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZIGZAGOON].baseDefense == gSpeciesInfo[SPECIES_ZIGZAGOON].baseSpDefense);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZIGZAGOON].baseHP == 38);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZIGZAGOON].baseAttack == 30);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ZIGZAGOON].baseDefense == 41);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ATTACKS_PARTNER);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Level(50); }
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Level(16); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Level(50); Moves(move, MOVE_OVERDRIVE, MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Level(level); Moves(MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN
|
||||
{
|
||||
if (move == MOVE_MIGHTY_CLEAVE)
|
||||
{
|
||||
if (level == 1)
|
||||
EXPECT_MOVE(opponentLeft, move, target: opponentRight);
|
||||
else
|
||||
EXPECT_MOVE(opponentLeft, move, target: playerRight);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (level == 1 || AI_FLAG_ATTACKS_PARTNER_FOCUSES_PARTNER)
|
||||
EXPECT_MOVE(opponentLeft, move);
|
||||
else
|
||||
EXPECT_MOVE(opponentLeft, MOVE_OVERDRIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI_FLAG_ATTACKS_PARTNER steps on its ally's weather")
|
||||
{
|
||||
u32 weather1, move1, weather2, move2;
|
||||
|
||||
PARAMETRIZE { weather1 = MOVE_SUNNY_DAY; move1 = MOVE_SOLAR_BEAM; weather2 = MOVE_RAIN_DANCE; move2 = MOVE_THUNDER; }
|
||||
PARAMETRIZE { weather1 = MOVE_RAIN_DANCE; move1 = MOVE_THUNDER; weather2 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BEAM; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ATTACKS_PARTNER);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(75); Moves(weather1, move1, MOVE_HEADBUTT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(weather2, move2, MOVE_HEADBUTT); }
|
||||
} WHEN {
|
||||
TURN
|
||||
{
|
||||
EXPECT_MOVE(opponentLeft, weather1);
|
||||
EXPECT_MOVE(opponentRight, weather2);
|
||||
}
|
||||
TURN
|
||||
{
|
||||
EXPECT_MOVE(opponentLeft, move1);
|
||||
EXPECT_MOVE(opponentRight, weather2);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user