fix double battle ai logic

This commit is contained in:
Evan 2021-01-13 11:08:43 -07:00
parent 56395a060f
commit 09dc1b7e89
3 changed files with 379 additions and 231 deletions

View File

@ -105,6 +105,8 @@ bool32 ShouldSetSun(u8 battlerAtk, u16 atkAbility, u16 holdEffect);
bool32 HasSleepMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef);
bool32 IsHealingMoveEffect(u16 effect);
bool32 HasHealingEffect(u32 battler);
bool32 IsTrappingMoveEffect(u16 effect);
bool32 HasTrappingMoveEffect(u8 battler);
bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move);
bool32 IsThawingMove(u16 move);
bool32 HasThawingMove(u8 battlerId);
@ -112,6 +114,7 @@ bool32 IsStatRaisingEffect(u16 effect);
bool32 IsStatLoweringEffect(u16 effect);
bool32 IsStatRaisingEffect(u16 effect);
bool32 IsAttackBoostMoveEffect(u16 effect);
bool32 IsUngroundingEffect(u16 effect);
// status checks
bool32 CanBeBurned(u8 battler, u16 ability);

View File

@ -57,7 +57,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 AI_Safari(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 AI_FirstBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 AI_TryOnAlly(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);
static s16 (*const sBattleAiFuncTable[])(u8, u8, u16, s16) =
{
@ -68,7 +68,7 @@ static s16 (*const sBattleAiFuncTable[])(u8, u8, u16, s16) =
[4] = AI_Risky, // AI_FLAG_RISKY
[5] = AI_PreferStrongestMove, // AI_FLAG_PREFER_STRONGEST_MOVE
[6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS
[7] = AI_TryOnAlly, // AI_FLAG_DOUBLE_BATTLE.
[7] = AI_DoubleBattle, // AI_FLAG_DOUBLE_BATTLE.
[8] = AI_HPAware, // AI_FLAG_HP_AWARE
[9] = NULL, // AI_FLAG_NEGATE_UNAWARE
[10] = NULL, // AI_FLAG_WILL_SUICIDE
@ -484,7 +484,7 @@ static void BattleAI_DoAIProcessing(void)
{
// Call AI function
AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] =
sBattleAiFuncTable[AI_THINKING_STRUCT->aiLogicId](gBattlerAttacker,
sBattleAiFuncTable[AI_THINKING_STRUCT->aiLogicId](sBattler_AI,
gBattlerTarget,
AI_THINKING_STRUCT->moveConsidered,
AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex]);
@ -614,7 +614,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
RETURN_SCORE_MINUS(10);
break;
case ABILITY_MAGIC_BOUNCE:
if (TestMoveFlags(move, FLAG_MAGICCOAT_AFFECTED))
if (TestMoveFlags(move, FLAG_MAGIC_COAT_AFFECTED))
RETURN_SCORE_MINUS(20);
break;
case ABILITY_CONTRARY:
@ -679,7 +679,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
RETURN_SCORE_MINUS(20);
break;
case ABILITY_MAGIC_BOUNCE:
if (TestMoveFlags(move, FLAG_MAGICCOAT_AFFECTED) && moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD))
if (TestMoveFlags(move, FLAG_MAGIC_COAT_AFFECTED) && moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD))
RETURN_SCORE_MINUS(20);
break;
case ABILITY_SWEET_VEIL:
@ -1902,7 +1902,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
score -= 10; // no teammates to assist from
break;
case EFFECT_MAGIC_COAT:
if (!TestMoveFlagsInMoveset(battlerDef, FLAG_MAGICCOAT_AFFECTED))
if (!TestMoveFlagsInMoveset(battlerDef, FLAG_MAGIC_COAT_AFFECTED))
score -= 10;
break;
case EFFECT_BELCH:
@ -2442,7 +2442,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
}
// double battle logic
static s16 AI_TryOnAlly(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
{
// move data
u8 moveType = gBattleMoves[move].type;
@ -2453,249 +2453,354 @@ static s16 AI_TryOnAlly(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
u16 atkPartnerAbility = AI_DATA->atkPartnerAbility;
u16 atkPartnerHoldEffect = AI_DATA->atkPartnerHoldEffect;
bool32 partnerProtecting = (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_PROTECT);
bool32 attackerHasBadAbility = (AI_DATA->atkAbility == ABILITY_TRUANT || AI_DATA->atkAbility == ABILITY_SLOW_START || AI_DATA->atkAbility == ABILITY_DEFEATIST);
bool32 partnerHasBadAbility = (atkPartnerAbility == ABILITY_TRUANT || atkPartnerAbility == ABILITY_SLOW_START || atkPartnerAbility == ABILITY_DEFEATIST);
bool32 attackerHasBadAbility = (GetAbilityRating(AI_DATA->atkAbility) < 0);
bool32 partnerHasBadAbility = (GetAbilityRating(atkPartnerAbility) < 0);
u16 predictedMove = gLastMoves[battlerDef]; //for now
if (!IsBattlerAlive(battlerAtkPartner))
return score;
// partner ability checks
if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(battlerAtk, move))
{
switch (atkPartnerAbility)
{
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
RETURN_SCORE_MINUS(10);
break; // handled in AI_HPAware
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
RETURN_SCORE_MINUS(10);
break; // handled in AI_HPAware
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetMoveDamageResult(move) == MOVE_POWER_WEAK)
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
RETURN_SCORE_MINUS(10);
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE
&& HasMoveWithType(battlerAtkPartner, TYPE_FIRE)
&& !(gBattleResources->flags->flags[battlerAtkPartner] & RESOURCE_FLAG_FLASH_FIRE))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_JUSTIFIED:
if (moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_RATTLED:
if (!IS_MOVE_STATUS(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_CONTRARY:
if (IsStatLoweringEffect(effect))
RETURN_SCORE_PLUS(2);
break;
case ABILITY_DEFIANT:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
RETURN_SCORE_PLUS(1);
break;
case ABILITY_COMPETITIVE:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
RETURN_SCORE_PLUS(1);
break;
}
} // ability checks
// partner move effects
if (AI_DATA->partnerMove != MOVE_NONE)
// check what effect partner is using
if (AI_DATA->partnerMove != 0)
{
switch (gBattleMoves[AI_DATA->partnerMove].effect)
{
case EFFECT_HELPING_HAND:
if (IS_MOVE_STATUS(move))
score += 5;
break;
case EFFECT_PERISH_SONG:
if (!IsBattlerTrapped(battlerDef, TRUE)
&& (gBattleMoves[move].effect == EFFECT_MEAN_LOOK
|| gBattleMoves[move].effect == EFFECT_TRAP))
RETURN_SCORE_PLUS(1); // target not trapped, but ally will trap them so perish song is good
if (!(gBattleMons[battlerDef].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)))
{
if (IsTrappingMoveEffect(effect) || predictedMove == MOVE_INGRAIN)
score++;
}
break;
case EFFECT_ALWAYS_CRIT:
if (AI_DATA->atkAbility == ABILITY_ANGER_POINT // ai has anger point
&& GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1 // partner goes first
&& CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 0))
// Ally decided to use Frost Breath on us. we must have Anger Point as our ability
if (AI_DATA->atkAbility == ABILITY_ANGER_POINT)
{
// encourage multiple target moves
if (!IsAttackBoostMoveEffect(gBattleMoves[move].effect)
&& !IS_MOVE_STATUS(move)
&& gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
RETURN_SCORE_PLUS(3);
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1) // partner moving first
{
// discourage raising our attack since it's about to be maxed out
if (IsAttackBoostMoveEffect(effect))
score -= 3;
// encourage moves hitting multiple opponents
if (!IS_MOVE_STATUS(move) && (gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
score += 3;
}
}
break;
} // partner move effects
}
// attacker move effects
switch (gBattleMoves[move].effect)
{
case EFFECT_EVASION_UP:
if (move == MOVE_ACUPRESSURE && !partnerProtecting)
RETURN_SCORE_PLUS(1);
break;
case EFFECT_PURIFY:
if (gBattleMons[battlerAtkPartner].status1 & STATUS1_ANY)
RETURN_SCORE_PLUS(1);
break;
case EFFECT_SWAGGER:
if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& (!CanBeConfused(battlerAtkPartner, TRUE)
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_FLATTER:
if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& (!CanBeConfused(battlerAtkPartner, TRUE)
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_SANDSTORM:
if (ShouldSetSandstorm(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_RAIN_DANCE:
if (ShouldSetRain(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_SUNNY_DAY:
if (ShouldSetSun(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_HAIL:
if (IsBattlerAlive(battlerAtkPartner)
&& ShouldSetHail(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
RETURN_SCORE_PLUS(2);
break;
case EFFECT_BEAT_UP:
if (atkPartnerAbility == ABILITY_JUSTIFIED
&& moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 0))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_HELPING_HAND:
if (AI_DATA->partnerMove != MOVE_NONE
&& !partnerProtecting
&& !IS_MOVE_STATUS(AI_DATA->partnerMove))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_SKILL_SWAP:
if (!partnerProtecting && AI_DATA->atkAbility != AI_DATA->atkPartnerAbility && !attackerHasBadAbility)
{
if (AI_DATA->atkPartnerAbility == ABILITY_TRUANT)
{
RETURN_SCORE_PLUS(10);
}
else if (AI_DATA->atkAbility == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, AI_GetAbility(FOE(battlerAtkPartner)), atkPartnerHoldEffect, AI_GetHoldEffect(FOE(battlerAtkPartner))))
{
RETURN_SCORE_PLUS(3);
}
//else
//score -= 30; // we get there after breaking out
}
} // check partner move effect
// consider our move effect relative to partner state
switch (effect)
{
case EFFECT_HELPING_HAND:
if (AI_DATA->partnerMove != 0 && !HasDamagingMove(battlerAtkPartner))
score -= 5;
break;
case EFFECT_ROLE_PLAY:
if (!partnerProtecting && attackerHasBadAbility && !partnerHasBadAbility)
RETURN_SCORE_PLUS(1);
break;
case EFFECT_WORRY_SEED:
case EFFECT_GASTRO_ACID:
case EFFECT_SIMPLE_BEAM:
if (!partnerProtecting && partnerHasBadAbility)
RETURN_SCORE_PLUS(2);
break;
case EFFECT_ENTRAINMENT:
if (!partnerProtecting && partnerHasBadAbility && IsAbilityOfRating(AI_DATA->atkAbility, 0))
RETURN_SCORE_PLUS(1);
break;
case EFFECT_SOAK:
if (!partnerProtecting && atkPartnerAbility == ABILITY_WONDER_GUARD
&& (gBattleMons[battlerAtkPartner].type1 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type2 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type3 != TYPE_WATER))
RETURN_SCORE_PLUS(1);
case EFFECT_PERISH_SONG:
if (AI_DATA->partnerMove != 0 && HasTrappingMoveEffect(battlerAtkPartner))
score++;
break;
case EFFECT_MAGNET_RISE:
if (IsBattlerGrounded(battlerAtk)
&& (HasMove(battlerAtkPartner, MOVE_EARTHQUAKE) || HasMove(battlerAtkPartner, MOVE_MAGNITUDE))
&& (AI_GetMoveEffectiveness(MOVE_EARTHQUAKE) != AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
RETURN_SCORE_PLUS(2);
{
RETURN_SCORE_PLUS(2); // partner has earthquake or magnitude -> good idea to use magnet rise
}
break;
case EFFECT_INSTRUCT:
} // our effect relative to partner
// consider global move effects
switch (effect)
{
case EFFECT_SANDSTORM:
if (ShouldSetSandstorm(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
{
RETURN_SCORE_PLUS(1); // our partner benefits from sandstorm
}
break;
case EFFECT_RAIN_DANCE:
if (ShouldSetRain(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
{
RETURN_SCORE_PLUS(1); // our partner benefits from rain
}
break;
case EFFECT_SUNNY_DAY:
if (ShouldSetSun(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
{
RETURN_SCORE_PLUS(1); // our partner benefits from sun
}
break;
case EFFECT_HAIL:
if (IsBattlerAlive(battlerAtkPartner)
&& ShouldSetHail(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
{
RETURN_SCORE_PLUS(2); // our partner benefits from hail
}
break;
} // global move effect check
// check specific target
if (IsTargetingPartner(battlerAtk, battlerDef))
{
if (GetMoveDamageResult(move) == MOVE_POWER_DISCOURAGED)
{
// partner ability checks
if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(battlerAtk, move))
{
switch (atkPartnerAbility)
{
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
break; // handled in AI_HPAware
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
break; // handled in AI_HPAware
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetMoveDamageResult(move) == MOVE_POWER_WEAK)
{
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
}
RETURN_SCORE_MINUS(10);
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE
&& HasMoveWithType(battlerAtkPartner, TYPE_FIRE)
&& !(gBattleResources->flags->flags[battlerAtkPartner] & RESOURCE_FLAG_FLASH_FIRE))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_JUSTIFIED:
if (moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_RATTLED:
if (!IS_MOVE_STATUS(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_CONTRARY:
if (IsStatLoweringEffect(effect))
{
RETURN_SCORE_PLUS(2);
}
break;
case ABILITY_DEFIANT:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_COMPETITIVE:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
}
} // ability checks
} // move power check
// attacker move effects specifically targeting partner
if (!partnerProtecting)
{
u16 instructedMove;
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 0)
instructedMove = AI_DATA->partnerMove;
else
instructedMove = gLastMoves[battlerAtkPartner];
if (instructedMove != MOVE_NONE
&& !IS_MOVE_STATUS(instructedMove)
&& gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) //Use instruct on multi-target moves
switch (effect)
{
RETURN_SCORE_PLUS(1);
}
}
break;
case EFFECT_AFTER_YOU:
if (GetWhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), TRUE) == 1 // opponent mon 1 goes before partner
|| GetWhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), TRUE) == 1) // opponent mon 2 goes before partner
case EFFECT_PURIFY:
if (gBattleMons[battlerAtkPartner].status1 & STATUS1_ANY)
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_SWAGGER:
if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& (!CanBeConfused(battlerAtkPartner, TRUE)
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_FLATTER:
if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& (!CanBeConfused(battlerAtkPartner, TRUE)
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_BEAT_UP:
if (atkPartnerAbility == ABILITY_JUSTIFIED
&& moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanAttackerFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 0))
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_SKILL_SWAP:
if (AI_DATA->atkAbility != AI_DATA->atkPartnerAbility && !attackerHasBadAbility)
{
if (AI_DATA->atkPartnerAbility == ABILITY_TRUANT)
{
RETURN_SCORE_PLUS(10);
}
else if (AI_DATA->atkAbility == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, AI_GetAbility(FOE(battlerAtkPartner)), atkPartnerHoldEffect, AI_GetHoldEffect(FOE(battlerAtkPartner))))
{
RETURN_SCORE_PLUS(3);
}
}
break;
case EFFECT_ROLE_PLAY:
if (attackerHasBadAbility && !partnerHasBadAbility)
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_WORRY_SEED:
case EFFECT_GASTRO_ACID:
case EFFECT_SIMPLE_BEAM:
if (partnerHasBadAbility)
{
RETURN_SCORE_PLUS(2);
}
break;
case EFFECT_ENTRAINMENT:
if (partnerHasBadAbility && IsAbilityOfRating(AI_DATA->atkAbility, 0))
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_SOAK:
if (atkPartnerAbility == ABILITY_WONDER_GUARD
&& (gBattleMons[battlerAtkPartner].type1 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type2 != TYPE_WATER
|| gBattleMons[battlerAtkPartner].type3 != TYPE_WATER))
{
RETURN_SCORE_PLUS(1);
}
break;
case EFFECT_INSTRUCT:
{
u16 instructedMove;
if (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 0)
instructedMove = AI_DATA->partnerMove;
else
instructedMove = gLastMoves[battlerAtkPartner];
if (instructedMove != MOVE_NONE
&& !IS_MOVE_STATUS(instructedMove)
&& gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) //Use instruct on multi-target moves
{
RETURN_SCORE_PLUS(1);
}
}
break;
case EFFECT_AFTER_YOU:
if (GetWhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), TRUE) == 1 // opponent mon 1 goes before partner
|| GetWhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), TRUE) == 1) // opponent mon 2 goes before partner
{
if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT)
break; // These moves need to go last
RETURN_SCORE_PLUS(1);
}
break;
} // attacker move effects
} // check partner protecting
score -= 30; // otherwise, don't target partner
}
else // checking opponent
{
// these checks mostly handled in AI_CheckBadMove and AI_CheckViability
switch (effect)
{
if (gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_COUNTER || gBattleMoves[AI_DATA->partnerMove].effect == EFFECT_MIRROR_COAT)
break; // These moves need to go last
RETURN_SCORE_PLUS(1);
case EFFECT_SKILL_SWAP:
if (AI_DATA->atkAbility == ABILITY_TRUANT)
score += 5;
else if (IsAbilityOfRating(AI_DATA->atkAbility, 0) || IsAbilityOfRating(AI_DATA->defAbility, 10))
score += 2; // we want to transfer our bad ability or take their awesome ability
break;
case EFFECT_EARTHQUAKE:
case EFFECT_MAGNITUDE:
if (!IsBattlerGrounded(battlerAtkPartner)
|| (GetWhoStrikesFirst(battlerAtk, battlerAtkPartner, TRUE) == 1 && IsUngroundingEffect(gBattleMoves[AI_DATA->partnerMove].effect)))
score += 2;
else if (IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_FIRE)
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ELECTRIC)
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_POISON)
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ROCK))
score -= 10; // partner will be hit by earthquake and is weak to it
else
score -= 3;
break;
}
break;
} // attacker move effects
RETURN_SCORE_MINUS(30); // otherwise, we do not want to target our ally
// lightning rod, flash fire against enemy handled in AI_CheckBadMove
}
return score;
}
// AI_FLAG_CHECK_VIABILITY - a weird mix of increasing and decreasing scores

View File

@ -1854,6 +1854,35 @@ bool32 HasHealingEffect(u32 battlerId)
return FALSE;
}
bool32 IsTrappingMoveEffect(u16 effect)
{
switch (effect)
{
case EFFECT_MEAN_LOOK:
case EFFECT_TRAP:
case EFFECT_HIT_PREVENT_ESCAPE:
case EFFECT_FAIRY_LOCK:
//case EFFECT_NO_RETREAT: // TODO
return TRUE;
default:
return FALSE;
}
}
bool32 HasTrappingMoveEffect(u8 battler)
{
s32 i;
u16 *moves = GetMovesArray(battler);
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && IsTrappingMoveEffect(gBattleMoves[moves[i]].effect))
return TRUE;
}
return FALSE;
}
bool32 IsThawingMove(u16 move)
{
switch (move)
@ -1887,6 +1916,18 @@ bool32 HasThawingMove(u8 battlerId)
return FALSE;
}
bool32 IsUngroundingEffect(u16 effect)
{
switch (effect)
{
case EFFECT_MAGNET_RISE:
return TRUE;
default:
return FALSE;
}
}
// for anger point
bool32 IsAttackBoostMoveEffect(u16 effect)
{
switch (effect)
@ -1899,6 +1940,7 @@ bool32 IsAttackBoostMoveEffect(u16 effect)
case EFFECT_COIL:
case EFFECT_BELLY_DRUM:
case EFFECT_BULK_UP:
case EFFECT_GROWTH:
return TRUE;
default:
return FALSE;
@ -2884,9 +2926,6 @@ u16 GetAllyChosenMove(void)
bool32 IsTargetingPartner(u8 battlerAtk, u8 battlerDef)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
return FALSE;
if ((battlerAtk & BIT_SIDE) == (battlerDef & BIT_SIDE))
return TRUE;
@ -3196,6 +3235,7 @@ bool32 IsAbilityOfRating(u16 ability, s8 rating)
{
if (sAiAbilityRatings[ability] >= rating)
return TRUE;
return FALSE;
}
s8 GetAbilityRating(u16 ability)