AI score changes, mostly IncreaseStatUpScore + few others (#4036)

This commit is contained in:
Alex 2024-01-26 17:48:51 +01:00 committed by GitHub
parent e2d70d440e
commit d0a35eec1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 348 additions and 293 deletions

View File

@ -1,12 +1,34 @@
#ifndef GUARD_BATTLE_AI_MAIN_H
#define GUARD_BATTLE_AI_MAIN_H
#define UNKNOWN_NO_OF_HITS UINT32_MAX
// return vals for BattleAI_ChooseMoveOrAction
// 0 - 3 are move idx
#define AI_CHOICE_FLEE 4
#define AI_CHOICE_WATCH 5
#define AI_CHOICE_SWITCH 7
// for AI_WhoStrikesFirst
#define AI_IS_FASTER 1
#define AI_IS_SLOWER -1
// for stat increasing / decreasing scores
#define STAT_CHANGE_ATK 0
#define STAT_CHANGE_DEF 1
#define STAT_CHANGE_SPEED 2
#define STAT_CHANGE_SPATK 3
#define STAT_CHANGE_SPDEF 4
#define STAT_CHANGE_ATK_2 5
#define STAT_CHANGE_DEF_2 6
#define STAT_CHANGE_SPEED_2 7
#define STAT_CHANGE_SPATK_2 8
#define STAT_CHANGE_SPDEF_2 9
#define STAT_CHANGE_ACC 10
#define STAT_CHANGE_EVASION 11
#include "test_runner.h"
// Logs for debugging AI tests.

View File

@ -1,10 +1,6 @@
#ifndef GUARD_BATTLE_AI_UTIL_H
#define GUARD_BATTLE_AI_UTIL_H
// for AI_WhoStrikesFirst
#define AI_IS_FASTER 1
#define AI_IS_SLOWER -1
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
#define AI_STRIKES_FIRST(battlerAi, battlerDef, move)((AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER))
@ -32,6 +28,8 @@ u32 GetHealthPercentage(u32 battler);
bool32 IsBattlerTrapped(u32 battler, bool32 switching);
s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk);
u32 GetBestDmgMoveFromTarget(u32 battlerAtk, u32 battlerDef);
bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits);
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
s32 AI_DecideKnownAbilityForTurn(u32 battlerId);
@ -130,7 +128,6 @@ bool32 IsAttackBoostMoveEffect(u32 effect);
bool32 IsUngroundingEffect(u32 effect);
bool32 IsSemiInvulnerable(u32 battlerDef, u32 move);
bool32 HasSubstituteIgnoringMove(u32 battler);
bool32 HasSoundMove(u32 battler);
bool32 HasHighCritRatioMove(u32 battler);
bool32 HasMagicCoatAffectedMove(u32 battler);
bool32 HasSnatchAffectedMove(u32 battler);

View File

@ -6,7 +6,7 @@
// still has them in the ROM. This is because the developers forgot
// to define NDEBUG before release, however this has been changed as
// Ruby's actual debug build does not use the AGBPrint features.
#define NDEBUG
// #define NDEBUG
// To enable printf debugging, comment out "#define NDEBUG". This allows
// the various AGBPrint functions to be used. (See include/gba/isagbprint.h).

View File

@ -402,10 +402,11 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
u32 battlerDef, i, weather;
u16 *moves;
// Simulate dmg for both ai controlled mons and for player controlled mons.
SaveBattlerData(battlerAtk);
moves = GetMovesArray(battlerAtk);
weather = AI_GetWeather(aiData);
// Simulate dmg for both ai controlled mons and for player controlled mons.
for (battlerDef = 0; battlerDef < battlersCount; battlerDef++)
{
if (battlerAtk == battlerDef)
@ -426,7 +427,6 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather);
aiData->moveAccuracy[battlerAtk][battlerDef][i] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
}
aiData->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
aiData->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
}
@ -3235,8 +3235,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
{
case EFFECT_SLEEP:
case EFFECT_YAWN:
if (AI_RandLessThan(128))
IncreaseSleepScore(battlerAtk, battlerDef, move, &score);
IncreaseSleepScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_ABSORB:
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT && effectiveness >= AI_EFFECTIVENESS_x1)
@ -3253,107 +3252,47 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (predictedMove != MOVE_NONE)
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score);
break;
// stat raising effects
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_2:
case EFFECT_ATTACK_UP_USER_ALLY:
if (MovesWithCategoryUnusable(battlerAtk, battlerDef, BATTLE_CATEGORY_PHYSICAL))
{
ADJUST_SCORE(-8);
break;
}
else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < 9)
{
if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128))
{
ADJUST_SCORE(2);
break;
}
}
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
break;
case EFFECT_ATTACK_UP_2:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK_2, &score);
break;
case EFFECT_DEFENSE_UP:
case EFFECT_DEFENSE_UP_2:
case EFFECT_DEFENSE_UP_3:
if (!HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL))
ADJUST_SCORE(-2);
if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128))
ADJUST_SCORE(2);
else if (aiData->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200))
break;
else if (aiData->hpPercents[battlerAtk] < 40)
ADJUST_SCORE(-2);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
break;
case EFFECT_DEFENSE_UP_2:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF_2, &score);
break;
case EFFECT_SPEED_UP:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
break;
case EFFECT_SPEED_UP_2:
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
{
if (!AI_RandLessThan(70))
ADJUST_SCORE(3);
}
else
{
ADJUST_SCORE(-3);
}
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED_2, &score);
break;
case EFFECT_SPECIAL_ATTACK_UP:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score);
break;
case EFFECT_SPECIAL_ATTACK_UP_2:
case EFFECT_SPECIAL_ATTACK_UP_3:
if (MovesWithCategoryUnusable(battlerAtk, battlerDef, BATTLE_CATEGORY_SPECIAL))
{
ADJUST_SCORE(-8);
break;
}
else if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < 9)
{
if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128))
{
ADJUST_SCORE(2);
break;
}
}
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK_2, &score);
break;
case EFFECT_SPECIAL_DEFENSE_UP:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_SPECIAL_DEFENSE_UP_2:
if (!HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL))
ADJUST_SCORE(-2);
if (aiData->hpPercents[battlerAtk] > 90 && AI_RandLessThan(128))
ADJUST_SCORE(2);
else if (aiData->hpPercents[battlerAtk] > 70 && AI_RandLessThan(200))
break;
else if (aiData->hpPercents[battlerAtk] < 40)
ADJUST_SCORE(-2);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF_2, &score);
break;
case EFFECT_ACCURACY_UP:
case EFFECT_ACCURACY_UP_2:
if (gBattleMons[battlerAtk].statStages[STAT_ACC] >= 9 && !AI_RandLessThan(50))
ADJUST_SCORE(-2);
else if (aiData->hpPercents[battlerAtk] <= 70)
ADJUST_SCORE(-2);
else
ADJUST_SCORE(1);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ACC, &score);
break;
case EFFECT_EVASION_UP:
case EFFECT_EVASION_UP_2:
if (aiData->hpPercents[battlerAtk] > 90 && !AI_RandLessThan(100))
ADJUST_SCORE(3);
if (gBattleMons[battlerAtk].statStages[STAT_EVASION] > 9 && AI_RandLessThan(128))
ADJUST_SCORE(-1);
if ((gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) && aiData->hpPercents[battlerAtk] >= 50 && !AI_RandLessThan(80))
ADJUST_SCORE(3);
if (gStatuses3[battlerDef] & STATUS3_LEECHSEED && !AI_RandLessThan(70))
ADJUST_SCORE(3);
if (gStatuses3[battlerAtk] & STATUS3_ROOTED && AI_RandLessThan(128))
ADJUST_SCORE(2);
if (gBattleMons[battlerDef].status2 & STATUS2_CURSED && !AI_RandLessThan(70))
ADJUST_SCORE(3);
if (aiData->hpPercents[battlerAtk] < 70 || gBattleMons[battlerAtk].statStages[STAT_EVASION] == DEFAULT_STAT_STAGE)
break;
else if (aiData->hpPercents[battlerAtk] < 40 || aiData->hpPercents[battlerDef] < 40)
ADJUST_SCORE(-2);
else if (!AI_RandLessThan(70))
ADJUST_SCORE(-2);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_EVASION, &score);
break;
// stat lowering effects
case EFFECT_ATTACK_DOWN:
case EFFECT_ATTACK_DOWN_2:
if (!ShouldLowerAttack(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
@ -3442,39 +3381,27 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_BIDE:
if (aiData->hpPercents[battlerAtk] < 90)
ADJUST_SCORE(-2);
break;
ADJUST_SCORE(-2); // Should be either removed or turned into increasing score
case EFFECT_ACUPRESSURE:
break;
case EFFECT_ATTACK_ACCURACY_UP: // hone claws
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ACC, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ACC, &score);
break;
case EFFECT_GROWTH:
case EFFECT_ATTACK_SPATK_UP: // work up
if (aiData->hpPercents[battlerAtk] <= 40 || aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
break;
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL))
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
else if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL))
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score);
break;
case EFFECT_HAZE:
if (AnyStatIsRaised(BATTLE_PARTNER(battlerAtk))
|| PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
{
ADJUST_SCORE(-3);
break;
}
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
break;
case EFFECT_ROAR:
if ((gBattleMoves[move].soundMove && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF) || aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS)
{
ADJUST_SCORE(-3);
break;
}
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
break;
case EFFECT_MULTI_HIT:
@ -3570,8 +3497,8 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(5);
break;
case EFFECT_MEAN_LOOK:
if (!IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(5);
if (ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(2);
break;
case EFFECT_MIST:
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER)
@ -3628,7 +3555,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_DO_NOTHING:
//todo - check z splash, z celebrate, z happy hour (lol)
break;
case EFFECT_TELEPORT:
case EFFECT_TELEPORT: // Either remove or add better logic
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER) || GetBattlerSide(battlerAtk) != B_SIDE_PLAYER)
break;
//fallthrough
@ -3689,7 +3616,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB))
{
if (gBattleMoveEffects[gBattleMoves[gLastMoves[battlerDef]].effect].encourageEncore)
ADJUST_SCORE(3);
ADJUST_SCORE(6);
}
break;
case EFFECT_PAIN_SPLIT:
@ -3845,7 +3772,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_SANDSTORM:
if (ShouldSetSandstorm(battlerAtk, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk]))
{
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK)
ADJUST_SCORE(1);
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
@ -3861,7 +3788,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
&& ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
ADJUST_SCORE(3);
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
ADJUST_SCORE(1);
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
@ -3877,7 +3804,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
&& ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
ADJUST_SCORE(3);
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
ADJUST_SCORE(1);
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
@ -3889,7 +3816,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_RAIN_DANCE:
if (ShouldSetRain(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
{
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_DAMP_ROCK)
ADJUST_SCORE(1);
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
@ -3904,7 +3831,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_SUNNY_DAY:
if (ShouldSetSun(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
{
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_HEAT_ROCK)
ADJUST_SCORE(1);
if (HasMoveWithType(battlerDef, TYPE_WATER) || HasMoveWithType(BATTLE_PARTNER(battlerDef), TYPE_WATER))
@ -3915,18 +3842,16 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_FELL_STINGER:
if (gBattleMons[battlerAtk].statStages[STAT_ATK] < MAX_STAT_STAGE
&& aiData->abilities[battlerAtk] != ABILITY_CONTRARY
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0))
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
ADJUST_SCORE(9);
else
ADJUST_SCORE(3);
}
&& aiData->abilities[battlerAtk] != ABILITY_CONTRARY
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0))
ADJUST_SCORE(10);
break;
case EFFECT_BELLY_DRUM:
if (!CanTargetFaintAi(battlerDef, battlerAtk) && HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL) && aiData->abilities[battlerAtk] != ABILITY_CONTRARY)
score += (MAX_STAT_STAGE - gBattleMons[battlerAtk].statStages[STAT_ATK]);
if (!CanTargetFaintAi(battlerDef, battlerAtk)
&& gBattleMons[battlerAtk].statStages[STAT_ATK] < MAX_STAT_STAGE - 2
&& HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL)
&& aiData->abilities[battlerAtk] != ABILITY_CONTRARY)
ADJUST_SCORE(10);
break;
case EFFECT_PSYCH_UP:
score += AI_ShouldCopyStatChanges(battlerAtk, battlerDef);
@ -3950,7 +3875,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_DEFENSE_CURL:
if (HasMoveEffect(battlerAtk, EFFECT_ROLLOUT) && !(gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL))
ADJUST_SCORE(1);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
break;
case EFFECT_FAKE_OUT:
if (move == MOVE_FAKE_OUT) // filter out first impression
@ -3964,28 +3889,17 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_STOCKPILE:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
break;
if (HasMoveEffect(battlerAtk, EFFECT_SWALLOW)
|| HasMoveEffect(battlerAtk, EFFECT_SPIT_UP))
if (HasMoveEffect(battlerAtk, EFFECT_SWALLOW) || HasMoveEffect(battlerAtk, EFFECT_SPIT_UP))
ADJUST_SCORE(2);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_SWAGGER:
if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY)
|| HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|| HasMoveWithMoveEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF))
ADJUST_SCORE(1);
if (aiData->abilities[battlerDef] == ABILITY_CONTRARY)
ADJUST_SCORE(2);
IncreaseConfusionScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_FLATTER:
if (HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|| HasMoveWithMoveEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF))
ADJUST_SCORE(2);
if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY)
|| HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|| HasMoveWithMoveEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF))
ADJUST_SCORE(1);
if (aiData->abilities[battlerDef] == ABILITY_CONTRARY)
ADJUST_SCORE(2);
@ -3997,13 +3911,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(3);
break;
case EFFECT_ATTRACT:
if (!isDoubleBattle && BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef])
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Target goes first
if (!isDoubleBattle
&& (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
&& BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef]))
break; // Don't use if the attract won't have a change to activate
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
|| (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| IsBattlerTrapped(battlerDef, TRUE))
|| (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| IsBattlerTrapped(battlerDef, TRUE))
ADJUST_SCORE(2);
else
ADJUST_SCORE(1);
@ -4058,13 +3972,11 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(3);
}
break;
case EFFECT_NATURE_POWER:
return AI_CheckViability(battlerAtk, battlerDef, GetNaturePowerMove(), score);
case EFFECT_CHARGE:
if (HasDamagingMoveOfType(battlerAtk, TYPE_ELECTRIC))
ADJUST_SCORE(2);
if (B_CHARGE_SPDEF_RAISE >= GEN_5)
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_TAUNT:
if (IS_MOVE_STATUS(predictedMove))
@ -4200,15 +4112,15 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_BRICK_BREAK:
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_REFLECT)
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_LIGHTSCREEN)
ADJUST_SCORE(1);
ADJUST_SCORE(2);
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_AURORA_VEIL)
ADJUST_SCORE(1);
ADJUST_SCORE(2);
break;
case EFFECT_SKILL_SWAP:
if (gAbilities[aiData->abilities[battlerDef]].aiRating > gAbilities[aiData->abilities[battlerAtk]].aiRating)
ADJUST_SCORE(1);
ADJUST_SCORE(2);
break;
case EFFECT_WORRY_SEED:
case EFFECT_GASTRO_ACID:
@ -4277,43 +4189,42 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_COSMIC_POWER:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_BULK_UP:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
break;
case EFFECT_CALM_MIND:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_GEOMANCY:
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB)
ADJUST_SCORE(3);
//fallthrough
case EFFECT_QUIVER_DANCE:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
break;
case EFFECT_VICTORY_DANCE:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
break;
case EFFECT_SHELL_SMASH:
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_RESTORE_STATS)
ADJUST_SCORE(1);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
break;
case EFFECT_DRAGON_DANCE:
case EFFECT_SHIFT_GEAR:
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score);
break;
case EFFECT_GUARD_SWAP:
if (gBattleMons[battlerDef].statStages[STAT_DEF] > gBattleMons[battlerAtk].statStages[STAT_DEF]
@ -4332,12 +4243,10 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(1);
break;
case EFFECT_POWER_TRICK:
if (!(gStatuses3[battlerAtk] & STATUS3_POWER_TRICK))
{
if (gBattleMons[battlerAtk].defense > gBattleMons[battlerAtk].attack && HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL))
ADJUST_SCORE(2);
break;
}
if (!(gStatuses3[battlerAtk] & STATUS3_POWER_TRICK)
&& gBattleMons[battlerAtk].defense > gBattleMons[battlerAtk].attack
&& HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL))
ADJUST_SCORE(2);
break;
case EFFECT_HEART_SWAP:
{
@ -4505,11 +4414,8 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(1);
break;
case EFFECT_FAIRY_LOCK:
if (!IsBattlerTrapped(battlerDef, TRUE))
{
if (ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(8);
}
if (ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(8);
break;
case EFFECT_QUASH:
if (isDoubleBattle
@ -4556,7 +4462,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_TOXIC_THREAD:
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
break;
case EFFECT_COUNTER:
if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE)
@ -4638,6 +4544,9 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
// Consider move effects that target self
if (gBattleMoves[move].additionalEffects[i].self)
{
u32 oneStageStatId = STAT_CHANGE_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1;
u32 twoStageStatId = STAT_CHANGE_ATK_2 + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1;
switch (gBattleMoves[move].additionalEffects[i].moveEffect)
{
case MOVE_EFFECT_SPD_PLUS_2:
@ -4651,12 +4560,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case MOVE_EFFECT_SP_DEF_PLUS_1:
case MOVE_EFFECT_ACC_PLUS_1:
case MOVE_EFFECT_EVS_PLUS_1:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_1,
&score
);
IncreaseStatUpScore(battlerAtk, battlerDef, oneStageStatId, &score);
break;
case MOVE_EFFECT_ATK_PLUS_2:
case MOVE_EFFECT_DEF_PLUS_2:
@ -4664,12 +4568,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case MOVE_EFFECT_SP_DEF_PLUS_2:
case MOVE_EFFECT_ACC_PLUS_2:
case MOVE_EFFECT_EVS_PLUS_2:
IncreaseStatUpScore(
battlerAtk,
battlerDef,
STAT_ATK + gBattleMoves[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_PLUS_2,
&score
);
IncreaseStatUpScore(battlerAtk, battlerDef, twoStageStatId, &score);
break;
// Effects that lower stat(s) - only need to consider Contrary
case MOVE_EFFECT_ATK_MINUS_1:
@ -4677,23 +4576,23 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_V_CREATE:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_SP_ATK_TWO_DOWN:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
ADJUST_SCORE(3);
IncreaseStatUpScore(battlerAtk, battlerDef, oneStageStatId, &score);
case MOVE_EFFECT_V_CREATE:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
{
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score);
}
break;
case MOVE_EFFECT_RAPIDSPIN:
if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0)
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
{
|| (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED))
ADJUST_SCORE(3);
break;
}
//Spin checks
if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY))
ADJUST_SCORE(-6);
break;
}
}
@ -4830,21 +4729,16 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_THROAT_CHOP:
if (HasSoundMove(battlerDef) && gBattleMoves[predictedMove].soundMove && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
ADJUST_SCORE(3);
break;
case MOVE_EFFECT_FLAME_BURST:
if (isDoubleBattle)
if (gBattleMoves[GetBestDmgMoveFromTarget(battlerDef, battlerAtk)].soundMove)
{
if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))
&& aiData->hpPercents[BATTLE_PARTNER(battlerDef)] < 12
&& aiData->abilities[BATTLE_PARTNER(battlerDef)] != ABILITY_MAGIC_GUARD
&& !IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerDef), TYPE_FIRE))
ADJUST_SCORE(1);
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
ADJUST_SCORE(4);
else
ADJUST_SCORE(2);
}
break;
case MOVE_EFFECT_WRAP:
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && !IsBattlerTrapped(battlerDef, TRUE) && ShouldTrap(battlerAtk, battlerDef, move))
if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && ShouldTrap(battlerAtk, battlerDef, move))
ADJUST_SCORE(5);
break;
}
@ -5075,8 +4969,6 @@ static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32
// Prefers moves that are good for baton pass
static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
u32 i;
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)
|| CountUsablePartyMons(battlerAtk) == 0
|| gBattleMoves[move].power != 0
@ -5112,10 +5004,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
ADJUST_SCORE(2);
break;
case EFFECT_BATON_PASS:
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
{
IncreaseStatUpScore(battlerAtk, battlerDef, i, &score);
}
// TODO: Increase Score based on current stats.
if (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING))
ADJUST_SCORE(2);
if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED)

View File

@ -499,6 +499,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE;
return dmg;
}
@ -847,6 +848,46 @@ bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
return FALSE;
}
u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk)
{
u32 i;
u32 currNumberOfHits;
u32 leastNumberOfHits = UNKNOWN_NO_OF_HITS;
for (i = 0; i < MAX_MON_MOVES; i++)
{
currNumberOfHits = GetNoOfHitsToKOBattler(battlerDef, battlerAtk, i);
if (currNumberOfHits != 0)
{
if (currNumberOfHits < leastNumberOfHits)
leastNumberOfHits = currNumberOfHits;
else if (leastNumberOfHits == UNKNOWN_NO_OF_HITS)
leastNumberOfHits = currNumberOfHits;
}
}
return leastNumberOfHits;
}
u32 GetBestDmgMoveFromTarget(u32 battlerDef, u32 battlerAtk)
{
u32 i;
u32 move = 0;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
u16 *moves = GetMovesArray(battlerDef);
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i])
&& bestDmg < AI_DATA->simulatedDmg[battlerDef][battlerAtk][i])
{
bestDmg = AI_DATA->simulatedDmg[battlerDef][battlerAtk][i];
move = moves[i];
}
}
return move;
}
// Check if AI mon has the means to faint the target with any of its moves.
// If numHits > 1, check if the target will be KO'ed by that number of hits (ignoring healing effects)
bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
@ -1951,11 +1992,6 @@ bool32 HasSubstituteIgnoringMove(u32 battler)
CHECK_MOVE_FLAG(ignoresSubstitute);
}
bool32 HasSoundMove(u32 battler)
{
CHECK_MOVE_FLAG(soundMove);
}
bool32 HasHighCritRatioMove(u32 battler)
{
s32 i;
@ -2672,6 +2708,9 @@ u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbi
bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (IsBattlerTrapped(battlerDef, TRUE))
return FALSE;
if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef]))
return TRUE; // battler is taking secondary damage with low HP
@ -3243,85 +3282,82 @@ bool32 IsRecycleEncouragedItem(u32 item)
return FALSE;
}
// score increases
#define STAT_UP_2_STAGE 8
#define STAT_UP_STAGE 10
void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score)
{
u32 noOfHitsToFaint = NoOfHitsForTargetToFaintAI(battlerDef, battlerAtk);
u32 aiIsFaster = GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) == AI_IS_FASTER;
u32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS);
if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY)
return;
if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128))
// Don't increase stat if AI is at +4
if (gBattleMons[battlerAtk].statStages[statId] >= MAX_STAT_STAGE - 2)
return;
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
// Don't increase stat if AI has less then 70% HP and number of hits isn't known
if (AI_DATA->hpPercents[battlerAtk] < 70 && noOfHitsToFaint == UNKNOWN_NO_OF_HITS)
return;
// Don't set up if AI is dead to residual damage from weather
if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk]))
return;
// Don't increase stats if opposing battler has Opportunist
if (AI_DATA->abilities[battlerDef] == ABILITY_OPPORTUNIST)
return;
switch (statId)
{
case STAT_ATK:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL) && AI_DATA->hpPercents[battlerAtk] > 40)
{
if (gBattleMons[battlerAtk].statStages[STAT_ATK] < STAT_UP_2_STAGE)
ADJUST_SCORE_PTR(2);
else if (gBattleMons[battlerAtk].statStages[STAT_ATK] < STAT_UP_STAGE)
ADJUST_SCORE_PTR(1);
}
if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY))
ADJUST_SCORE_PTR(1);
case STAT_CHANGE_ATK:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL) && shouldSetUp)
ADJUST_SCORE_PTR(2);
break;
case STAT_DEF:
if ((HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL)|| IS_MOVE_PHYSICAL(gLastMoves[battlerDef]))
&& AI_DATA->hpPercents[battlerAtk] > 70)
{
if (gBattleMons[battlerAtk].statStages[STAT_DEF] < STAT_UP_2_STAGE)
ADJUST_SCORE_PTR(2); // seems better to raise def at higher HP
else if (gBattleMons[battlerAtk].statStages[STAT_DEF] < STAT_UP_STAGE)
ADJUST_SCORE_PTR(1);
}
case STAT_CHANGE_DEF:
if (HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL))
ADJUST_SCORE_PTR(2);
break;
case STAT_SPEED:
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered))
{
if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE)
ADJUST_SCORE_PTR(2);
else if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_STAGE)
ADJUST_SCORE_PTR(1);
}
case STAT_CHANGE_SPEED:
if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0)
ADJUST_SCORE_PTR(2);
break;
case STAT_SPATK:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL) && AI_DATA->hpPercents[battlerAtk] > 40)
{
if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < STAT_UP_2_STAGE)
ADJUST_SCORE_PTR(2);
else if (gBattleMons[battlerAtk].statStages[STAT_SPATK] < STAT_UP_STAGE)
ADJUST_SCORE_PTR(1);
}
case STAT_CHANGE_SPATK:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL) && shouldSetUp)
ADJUST_SCORE_PTR(2);
break;
case STAT_SPDEF:
if ((HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL) || IS_MOVE_SPECIAL(gLastMoves[battlerDef]))
&& AI_DATA->hpPercents[battlerAtk] > 70)
{
if (gBattleMons[battlerAtk].statStages[STAT_SPDEF] < STAT_UP_2_STAGE)
ADJUST_SCORE_PTR(2); // seems better to raise spdef at higher HP
else if (gBattleMons[battlerAtk].statStages[STAT_SPDEF] < STAT_UP_STAGE)
ADJUST_SCORE_PTR(1);
}
case STAT_CHANGE_SPDEF:
if (HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL))
ADJUST_SCORE_PTR(2);
break;
case STAT_ACC:
if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 80, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]))
ADJUST_SCORE_PTR(2); // has moves with less than 80% accuracy
else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 90, TRUE, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], AI_DATA->holdEffects[battlerAtk], AI_DATA->holdEffects[battlerDef]))
ADJUST_SCORE_PTR(1);
case STAT_CHANGE_ATK_2:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL) && shouldSetUp)
ADJUST_SCORE_PTR(4);
break;
case STAT_EVASION:
if (!BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk]))
{
if (!GetBattlerSecondaryDamage(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_ROOTED))
ADJUST_SCORE_PTR(2);
else
ADJUST_SCORE_PTR(1);
}
case STAT_CHANGE_DEF_2:
if (HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL))
ADJUST_SCORE_PTR(4);
break;
case STAT_CHANGE_SPEED_2:
if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0)
ADJUST_SCORE_PTR(4);
break;
case STAT_CHANGE_SPATK_2:
if (HasMoveWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL) && shouldSetUp)
ADJUST_SCORE_PTR(4);
break;
case STAT_CHANGE_SPDEF_2:
if (HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, BATTLE_CATEGORY_PHYSICAL))
ADJUST_SCORE_PTR(4);
break;
case STAT_CHANGE_ACC:
if (gBattleMons[battlerAtk].statStages[STAT_ACC] <= 3) // Increase only if necessary
ADJUST_SCORE_PTR(2);
break;
case STAT_CHANGE_EVASION:
if (GetBattlerSecondaryDamage(battlerAtk) && ((noOfHitsToFaint > 3) || noOfHitsToFaint == 0))
ADJUST_SCORE_PTR(4);
else
ADJUST_SCORE_PTR(2);
break;
}
}

View File

@ -4804,7 +4804,6 @@ s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMov
{
strikesFirst = 1; // battler1's move has greater priority
}
return strikesFirst;
}

View File

@ -0,0 +1,112 @@
#include "global.h"
#include "test/battle.h"
#include "battle_ai_util.h"
AI_SINGLE_BATTLE_TEST("AI will not further increase Attack / Sp. Atk stat if it knows it faints to target: AI faster")
{
u16 move;
PARAMETRIZE { move = MOVE_HOWL; }
PARAMETRIZE { move = MOVE_CALM_MIND; }
GIVEN {
ASSUME(gBattleMoves[MOVE_SKY_UPPERCUT].power == 85);
ASSUME(gBattleMoves[MOVE_HOWL].effect == EFFECT_ATTACK_UP_USER_ALLY);
ASSUME(gBattleMoves[MOVE_CALM_MIND].effect == EFFECT_CALM_MIND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_COMBUSKEN) { Speed(15); Moves(MOVE_SKY_UPPERCUT, MOVE_CELEBRATE); };
OPPONENT(SPECIES_KANGASKHAN) { Speed(20); Moves(MOVE_CHIP_AWAY, MOVE_SWIFT, move); }
} WHEN {
TURN { MOVE(player, MOVE_SKY_UPPERCUT); EXPECT_MOVE(opponent, move); }
TURN { EXPECT_MOVE(opponent, MOVE_CHIP_AWAY); MOVE(player, MOVE_SKY_UPPERCUT); }
}
}
AI_SINGLE_BATTLE_TEST("AI will not further increase Attack / Sp. Atk stat if it knows it faints to target: AI slower")
{
u16 move;
PARAMETRIZE { move = MOVE_HOWL; }
PARAMETRIZE { move = MOVE_CALM_MIND; }
GIVEN {
ASSUME(gBattleMoves[MOVE_SKY_UPPERCUT].power == 85);
ASSUME(gBattleMoves[MOVE_HOWL].effect == EFFECT_ATTACK_UP_USER_ALLY);
ASSUME(gBattleMoves[MOVE_CALM_MIND].effect == EFFECT_CALM_MIND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_COMBUSKEN) { Speed(20); Moves(MOVE_DOUBLE_KICK, MOVE_CELEBRATE); };
OPPONENT(SPECIES_KANGASKHAN) { Speed(15); Moves(MOVE_CHIP_AWAY, MOVE_SWIFT, move); }
} WHEN {
TURN { MOVE(player, MOVE_DOUBLE_KICK); EXPECT_MOVE(opponent, move); }
TURN { EXPECT_MOVE(opponent, MOVE_CHIP_AWAY); MOVE(player, MOVE_DOUBLE_KICK); }
}
}
AI_SINGLE_BATTLE_TEST("AI will increase speed if it is slower")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_COMBUSKEN) { Speed(20); Moves(MOVE_DOUBLE_KICK, MOVE_CELEBRATE); };
OPPONENT(SPECIES_KANGASKHAN) { Speed(15); Moves(MOVE_CHIP_AWAY, MOVE_AGILITY); }
} WHEN {
TURN { MOVE(player, MOVE_DOUBLE_KICK); EXPECT_MOVE(opponent, MOVE_AGILITY); }
TURN { EXPECT_MOVE(opponent, MOVE_CHIP_AWAY); MOVE(player, MOVE_DOUBLE_KICK); }
}
}
AI_SINGLE_BATTLE_TEST("AI will correctly predict what move the opposing mon going to use")
{
u16 move;
PARAMETRIZE { move = MOVE_HOWL; }
PARAMETRIZE { move = MOVE_CALM_MIND; }
KNOWN_FAILING;
GIVEN {
ASSUME(gBattleMoves[MOVE_SKY_UPPERCUT].power == 85);
ASSUME(gBattleMoves[MOVE_HOWL].effect == EFFECT_ATTACK_UP_USER_ALLY);
ASSUME(gBattleMoves[MOVE_CALM_MIND].effect == EFFECT_CALM_MIND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_COMBUSKEN) { Speed(15); Moves(MOVE_SKY_UPPERCUT, MOVE_DOUBLE_KICK, MOVE_FLAME_WHEEL, MOVE_CELEBRATE); };
OPPONENT(SPECIES_KANGASKHAN) { Speed(20); Moves(MOVE_CHIP_AWAY, MOVE_SWIFT, move); }
} WHEN {
TURN { MOVE(player, MOVE_DOUBLE_KICK); EXPECT_MOVE(opponent, move); }
TURN { EXPECT_MOVE(opponent, MOVE_CHIP_AWAY); MOVE(player, MOVE_SKY_UPPERCUT); }
}
}
AI_SINGLE_BATTLE_TEST("AI will not use Throat Chop if opposing mon has a better move")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_PSYCHIC_FANGS].power == 85);
ASSUME(gBattleMoves[MOVE_THROAT_CHOP].power == 80);
ASSUME(gBattleMoves[MOVE_DISARMING_VOICE].power == 40);
ASSUME(gBattleMoves[MOVE_FLAME_BURST].power == 70);
ASSUME(MoveHasMoveEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP) == TRUE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_REGIROCK) { Speed(15); Moves(MOVE_DISARMING_VOICE, MOVE_FLAME_BURST); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(20); Moves(MOVE_THROAT_CHOP, MOVE_PSYCHIC_FANGS); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_PSYCHIC_FANGS); MOVE(player, MOVE_FLAME_BURST); }
TURN { EXPECT_MOVE(opponent, MOVE_PSYCHIC_FANGS); MOVE(player, MOVE_DISARMING_VOICE); }
TURN { EXPECT_MOVE(opponent, MOVE_PSYCHIC_FANGS); MOVE(player, MOVE_FLAME_BURST);}
}
}
AI_SINGLE_BATTLE_TEST("AI will select Throat Chop if the sound move is the best damaging move from opposing mon")
{
GIVEN {
ASSUME(MoveHasMoveEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP) == TRUE);
ASSUME(gBattleMoves[MOVE_PSYCHIC_FANGS].power == 85);
ASSUME(gBattleMoves[MOVE_THROAT_CHOP].power == 80);
ASSUME(gBattleMoves[MOVE_FLAME_BURST].power == 70);
ASSUME(gBattleMoves[MOVE_HYPER_VOICE].power == 90);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_REGIROCK) { Speed(15); Moves(MOVE_HYPER_VOICE, MOVE_FLAME_BURST); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(20); Moves(MOVE_THROAT_CHOP, MOVE_PSYCHIC_FANGS); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_PSYCHIC_FANGS); MOVE(player, MOVE_FLAME_BURST); }
TURN { EXPECT_MOVE(opponent, MOVE_PSYCHIC_FANGS); MOVE(player, MOVE_HYPER_VOICE); }
TURN { EXPECT_MOVE(opponent, MOVE_THROAT_CHOP); MOVE(player, MOVE_HYPER_VOICE);}
}
}