Fix roll handling in AI damage calcs (#6396)

Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
Pawkkie 2025-03-08 03:53:14 -05:00 committed by GitHub
parent 46b821c196
commit b12f32667f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 246 additions and 189 deletions

View File

@ -335,8 +335,9 @@ struct SwitchinCandidate
struct SimulatedDamage
{
s32 expected;
s32 minimum;
u16 minimum;
u16 median;
u16 maximum;
};
// Ai Data used when deciding which move to use, computed only once before each turn's start.

View File

@ -15,6 +15,12 @@ enum DamageRollType
DMG_ROLL_HIGHEST,
};
enum DamageCalcContext
{
AI_DEFENDING,
AI_ATTACKING,
};
enum AIPivot
{
DONT_PIVOT,
@ -25,7 +31,7 @@ enum AIPivot
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_RandLessThan(u32 val);
u32 GetDmgRollType(u32 battlerAtk);
u32 AI_GetDamage(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData);
bool32 IsAiVsAiBattle(void);
bool32 BattlerHasAi(u32 battlerId);
bool32 IsAiBattlerAware(u32 battlerId);
@ -50,8 +56,8 @@ 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 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef);
u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget);
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext);
u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext calcContext);
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);
@ -59,7 +65,8 @@ u32 AI_DecideHoldEffectForTurn(u32 battlerId);
bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, u32 atkAbility, u32 move);
u32 AI_GetWeather(void);
bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits);
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits);
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, enum DamageCalcContext calcContext);
bool32 CanIndexMoveGuaranteeFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index);
bool32 HasDamagingMove(u32 battlerId);
bool32 HasDamagingMoveOfType(u32 battlerId, u32 type);
u32 GetBattlerSecondaryDamage(u32 battlerId);
@ -69,7 +76,7 @@ bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbil
bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex);
u32 GetBattlerSideSpeedAverage(u32 battler);
bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage);
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent);
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent, enum DamageCalcContext calcContext);
bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect);
enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 moveIndex);
bool32 IsRecycleEncouragedItem(u32 item);
@ -101,13 +108,13 @@ bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility);
bool32 IsAffectedByPowder(u32 battler, u32 ability, u32 holdEffect);
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category);
s32 AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType);
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower);
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather);
bool32 AI_IsDamagedByRecoil(u32 battler);
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef);
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext);
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext);
uq4_12_t AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
u16 *GetMovesArray(u32 battler);
bool32 IsConfusionMoveEffect(u32 moveEffect);
@ -210,7 +217,7 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool32 isPartyMonAttacker, enum DamageRollType rollType);
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool32 isPartyMonAttacker);
u32 AI_WhoStrikesFirstPartyMon(u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, u32 moveConsidered);
s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle);
bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);

View File

@ -23,6 +23,7 @@ struct PickupItem
};
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk);
s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk);
s32 GetCritHitOdds(s32 critChanceIndex);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
u8 GetBattlerTurnOrderNum(u8 battlerId);

View File

@ -96,6 +96,10 @@ enum ItemEffect
#define DMG_ROLL_PERCENT_LO 85
#define DMG_ROLL_PERCENT_HI 100
// Crit chance exceptions
#define CRITICAL_HIT_BLOCKED -1
#define CRITICAL_HIT_ALWAYS -2
// for Natural Gift and Fling
struct TypePower
{

View File

@ -47,6 +47,10 @@
// AI held item-based move scoring
#define LOW_ACCURACY_THRESHOLD 75 // Moves with accuracy equal OR below this value are considered low accuracy
// AI damage calc considerations
#define RISKY_AI_CRIT_STAGE_THRESHOLD 2 // Stat stages at which Risky will assume it gets a crit
#define RISKY_AI_CRIT_THRESHOLD_GEN_1 128 // "Stat stage" at which Risky will assume it gets a crit with gen 1 mechanics (this translates to an X / 255 % crit threshold)
// AI prediction chances
#define PREDICT_SWITCH_CHANCE 50

View File

@ -413,7 +413,6 @@ static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 ba
static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 weather)
{
u32 moveIndex, move;
u32 rollType = GetDmgRollType(battlerAtk);
u16 *moves = GetMovesArray(battlerAtk);
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
@ -427,7 +426,7 @@ static void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u
//&& !IsBattleMoveStatus(move) /* we want to get effectiveness and accuracy of status moves */
&& !(aiData->moveLimitations[battlerAtk] & (1u << moveIndex)))
{
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, rollType);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather);
aiData->moveAccuracy[battlerAtk][battlerDef][moveIndex] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
}
aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex] = dmg;
@ -2734,8 +2733,10 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IsBattleMoveStatus(move))
return score; // status moves aren't accounted here
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0) && GetMoveEffect(move) != EFFECT_EXPLOSION)
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, AI_ATTACKING) && GetMoveEffect(move) != EFFECT_EXPLOSION)
{
if (CanIndexMoveGuaranteeFaintTarget(battlerAtk, battlerDef, movesetIndex))
ADJUST_SCORE(1); // Bonus point if the KO is guaranteed
if (AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE(FAST_KILL);
else
@ -2895,7 +2896,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (MoveAlwaysCrits(move)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& AI_IsFaster(battlerAtk, battlerAtkPartner, move)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING))
{
RETURN_SCORE_PLUS(GOOD_EFFECT);
}
@ -2939,7 +2940,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) >= 4)
if (moveType == TYPE_WATER && GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) >= 4)
{
RETURN_SCORE_PLUS(WEAK_EFFECT); // only mon with this ability is weak to water so only make it okay if we do very little damage
}
@ -2966,7 +2967,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& !IsBattleMoveStatus(move)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING))
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@ -2975,7 +2976,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (!IsBattleMoveStatus(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING))
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@ -3032,7 +3033,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& !IsBattleMoveStatus(move)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 0))
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING))
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@ -3180,7 +3181,7 @@ static s32 CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1,
static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, u32 move, u32 moveIndex, u32 hitsToFaintOpposingBattler)
{
u32 partnerBattler = BATTLE_PARTNER(battlerAtk);
u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex);
u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex, AI_ATTACKING);
return (IsDoubleBattle()
&& noOfHitsToFaintPartner != 0 // Immunity check
&& IsBattlerAlive(partnerBattler)
@ -3205,7 +3206,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
{
if (moves[i] != MOVE_NONE && GetMovePower(moves[i]) != 0)
{
noOfHits[i] = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, i);
noOfHits[i] = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, i, AI_ATTACKING);
if (ShouldUseSpreadDamageMove(battlerAtk,moves[i], i, noOfHits[i]))
{
noOfHits[i] = -1;
@ -3225,9 +3226,6 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
viableMoveScores[i] = 0;
isTwoTurnNotSemiInvulnerableMove[i] = FALSE;
}
/*
Test_MgbaPrintf("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]);
*/
}
// Priority list:
@ -3581,7 +3579,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
}
if (ShouldRecover(battlerAtk, battlerDef, move, healPercent))
if (ShouldRecover(battlerAtk, battlerDef, move, healPercent, AI_DEFENDING))
ADJUST_SCORE(DECENT_EFFECT);
}
break;
@ -3591,7 +3589,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_MORNING_SUN:
case EFFECT_SYNTHESIS:
case EFFECT_MOONLIGHT:
if (ShouldRecover(battlerAtk, battlerDef, move, 50))
if (ShouldRecover(battlerAtk, battlerDef, move, 50, AI_DEFENDING))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_TOXIC:
@ -3613,7 +3611,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
{
break;
}
else if (ShouldRecover(battlerAtk, battlerDef, move, 100))
else if (ShouldRecover(battlerAtk, battlerDef, move, 100, AI_DEFENDING))
{
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_SLP
|| aiData->holdEffects[battlerAtk] == HOLD_EFFECT_CURE_STATUS
@ -3933,7 +3931,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_FELL_STINGER:
if (gBattleMons[battlerAtk].statStages[STAT_ATK] < MAX_STAT_STAGE
&& aiData->abilities[battlerAtk] != ABILITY_CONTRARY
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0))
&& CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, AI_ATTACKING))
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_BELLY_DRUM:
@ -3965,7 +3963,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_FIRST_TURN_ONLY:
if (ShouldFakeOut(battlerAtk, battlerDef, move) && MoveHasAdditionalEffectWithChance(move, MOVE_EFFECT_FLINCH, 100))
ADJUST_SCORE(GOOD_EFFECT);
else if (gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move)
else if (gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef, AI_ATTACKING) == move)
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_STOCKPILE:
@ -4538,20 +4536,20 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_COUNTER:
if ((!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE)
&& (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot) >= 2)
&& (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot, AI_DEFENDING) >= 2)
&& (GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_PHYSICAL))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_MIRROR_COAT:
if ((!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE)
&& (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot) >= 2)
&& (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot, AI_DEFENDING) >= 2)
&& (GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_SPECIAL))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_SHORE_UP:
if ((AI_GetWeather() & B_WEATHER_SANDSTORM) && ShouldRecover(battlerAtk, battlerDef, move, 67))
if ((AI_GetWeather() & B_WEATHER_SANDSTORM) && ShouldRecover(battlerAtk, battlerDef, move, 67, AI_DEFENDING))
ADJUST_SCORE(DECENT_EFFECT);
else if (ShouldRecover(battlerAtk, battlerDef, move, 50))
else if (ShouldRecover(battlerAtk, battlerDef, move, 50, AI_DEFENDING))
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_ENDEAVOR:
@ -4577,8 +4575,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
//case EFFECT_SKY_DROP
//break;
case EFFECT_JUNGLE_HEALING:
if (ShouldRecover(battlerAtk, battlerDef, move, 25)
|| ShouldRecover(BATTLE_PARTNER(battlerAtk), battlerDef, move, 25)
if (ShouldRecover(battlerAtk, battlerDef, move, 25, AI_DEFENDING)
|| ShouldRecover(BATTLE_PARTNER(battlerAtk), battlerDef, move, 25, AI_DEFENDING)
|| gBattleMons[battlerAtk].status1 & STATUS1_ANY
|| gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY)
ADJUST_SCORE(GOOD_EFFECT);
@ -4816,7 +4814,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case MOVE_EFFECT_THROAT_CHOP:
if (IsSoundMove(GetBestDmgMoveFromBattler(battlerDef, battlerAtk)))
if (IsSoundMove(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING)))
{
if (AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE(GOOD_EFFECT);
@ -4849,12 +4847,12 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
if (GetMovePower(move) != 0)
{
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 0)
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 0)
ADJUST_AND_RETURN_SCORE(NO_DAMAGE_OR_FAILS); // No point in checking the move further so return early
else
{
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & (AI_FLAG_RISKY | AI_FLAG_PREFER_HIGHEST_DAMAGE_MOVE)
&& GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move)
&& GetBestDmgMoveFromBattler(battlerAtk, battlerDef, AI_ATTACKING) == move)
ADJUST_SCORE(BEST_DAMAGE_MOVE);
else
ADJUST_SCORE(AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex));
@ -5078,9 +5076,9 @@ static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score;
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 1)
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 1)
ADJUST_SCORE(BEST_EFFECT);
else if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 2)
else if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING) == 2)
ADJUST_SCORE(DECENT_EFFECT);
return score;
@ -5256,7 +5254,7 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
// consider target HP
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING))
{
ADJUST_SCORE(DECENT_EFFECT);
}
@ -5437,7 +5435,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
case EFFECT_PURSUIT:
{
u32 hitsToKO = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
u32 hitsToKO = GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, AI_ATTACKING);
if (hitsToKO == 2)
ADJUST_SCORE(GOOD_EFFECT);
else if (hitsToKO == 1)

View File

@ -220,7 +220,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
hasSuperEffectiveMove = TRUE;
// Get maximum damage mon can deal
damageDealt = AI_DATA->simulatedDmg[battler][opposingBattler][i].expected;
damageDealt = AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, AI_DATA);
if(damageDealt > maxDamageDealt)
{
maxDamageDealt = damageDealt;
@ -248,7 +248,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
playerMove = gBattleMons[opposingBattler].moves[i];
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH)
{
damageTaken = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather, DMG_ROLL_HIGHEST).expected;
damageTaken = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather).median;
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
{
return maxDamageTaken = damageTaken;
@ -454,7 +454,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
// Only check damage if it's a damaging move
if (!IsBattleMoveStatus(aiMove))
{
if (AI_DATA->simulatedDmg[battler][opposingBattler][i].expected > gBattleMons[opposingBattler].hp)
if (AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, AI_DATA) > gBattleMons[opposingBattler].hp)
return FALSE;
}
}
@ -1355,7 +1355,6 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
int i, j;
int dmg, bestDmg = 0;
int bestMonId = PARTY_SIZE;
u32 rollType = GetDmgRollType(battler);
u32 aiMove;
@ -1371,7 +1370,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, rollType);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE);
if (bestDmg < dmg)
{
bestDmg = dmg;
@ -1826,7 +1825,7 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle
playerMove = gBattleMons[opposingBattler].moves[i];
if (playerMove != MOVE_NONE && !IsBattleMoveStatus(playerMove) && GetMoveEffect(playerMove) != EFFECT_FOCUS_PUNCH)
{
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, FALSE, DMG_ROLL_HIGHEST);
damageTaken = AI_CalcPartyMonDamage(playerMove, opposingBattler, battler, battleMon, FALSE);
if (playerMove == gBattleStruct->choicedMove[opposingBattler]) // If player is choiced, only care about the choice locked move
return damageTaken;
if (damageTaken > maxDamageTaken)
@ -1959,9 +1958,9 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
if (aiMove != MOVE_NONE && !IsBattleMoveStatus(aiMove))
{
if (AI_THINKING_STRUCT->aiFlags[GetThinkingBattler(battler)] & AI_FLAG_CONSERVATIVE)
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_LOWEST);
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE);
else
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_DEFAULT);
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE);
}
// Offensive switchin decisions are based on which whether switchin moves first and whether it can win a 1v1
@ -2208,7 +2207,7 @@ static bool32 AiExpectsToFaintPlayer(u32 battler)
return FALSE; // AI not planning to use move
if (GetBattlerSide(target) != GetBattlerSide(battler)
&& CanIndexMoveFaintTarget(battler, target, gBattleStruct->aiMoveOrAction[battler], 0)
&& CanIndexMoveFaintTarget(battler, target, gBattleStruct->aiMoveOrAction[battler], AI_ATTACKING)
&& AI_IsFaster(battler, target, GetAIChosenMove(battler)))
{
// We expect to faint the target and move first -> dont use an item

View File

@ -24,14 +24,29 @@
#include "constants/items.h"
// Functions
u32 GetDmgRollType(u32 battlerAtk)
u32 AI_GetDamage(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData)
{
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)
return DMG_ROLL_HIGHEST;
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)
return DMG_ROLL_LOWEST;
if (calcContext == AI_ATTACKING && BattlerHasAi(battlerAtk))
{
return DMG_ROLL_DEFAULT;
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY) && !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)) // Risky assumes it deals max damage
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].maximum;
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE) && !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)) // Conservative assumes it deals min damage
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].minimum;
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].median; // Default assumes it deals median damage
}
else if (calcContext == AI_DEFENDING && BattlerHasAi(battlerDef))
{
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY) && !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)) // Risky assumes it takes min damage
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].minimum;
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE) && !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)) // Conservative assumes it takes max damage
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].maximum;
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].median; // Default assumes it takes median damage
}
else
{
return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].median;
}
}
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move)
@ -385,7 +400,7 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
}
// To save computation time this function has 2 variants. One saves, sets and restores battlers, while the other doesn't.
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType)
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower)
{
struct SimulatedDamage dmg;
@ -393,7 +408,7 @@ struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 b
SaveBattlerData(battlerDef);
SetBattlerData(battlerAtk);
SetBattlerData(battlerDef);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather(), rollType);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather());
RestoreBattlerData(battlerAtk);
RestoreBattlerData(battlerDef);
return dmg;
@ -538,58 +553,63 @@ static inline void AI_RestoreBattlerTypes(u32 battlerAtk, u32 *types)
gBattleMons[battlerAtk].types[2] = types[2];
}
static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCalcData, s32 *expectedDamage, s32 *minimumDamage, u32 holdEffectAtk, u32 abilityAtk)
static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCalcData, u16 *medianDamage, u16 *minimumDamage, u16 *maximumDamage, u32 holdEffectAtk, u32 abilityAtk)
{
u32 move = damageCalcData->move;
u32 effect = GetMoveEffect(move);
s32 expected = *expectedDamage;
s32 minimum = *minimumDamage;
u16 median = *medianDamage;
u16 minimum = *minimumDamage;
u16 maximum = *maximumDamage;
switch (effect)
{
case EFFECT_LEVEL_DAMAGE:
expected = minimum = gBattleMons[damageCalcData->battlerAtk].level * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
median = maximum = minimum = gBattleMons[damageCalcData->battlerAtk].level * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
break;
case EFFECT_PSYWAVE:
expected = minimum = gBattleMons[damageCalcData->battlerAtk].level * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
median = maximum = minimum = gBattleMons[damageCalcData->battlerAtk].level * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
break;
case EFFECT_FIXED_DAMAGE_ARG:
expected = minimum = GetMoveFixedDamage(move) * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
median = maximum = minimum = GetMoveFixedDamage(move) * (abilityAtk == ABILITY_PARENTAL_BOND ? 2 : 1);
break;
case EFFECT_MULTI_HIT:
if (move == MOVE_WATER_SHURIKEN && gBattleMons[damageCalcData->battlerAtk].species == SPECIES_GRENINJA_ASH)
{
expected *= 3;
median *= 3;
minimum *= 3;
maximum *= 3;
}
else if (abilityAtk == ABILITY_SKILL_LINK)
{
expected *= 5;
median *= 5;
minimum *= 5;
maximum *= 5;
}
else if (holdEffectAtk == HOLD_EFFECT_LOADED_DICE)
{
expected *= 9;
expected /= 2;
median *= 9;
median /= 2;
minimum *= 4;
maximum *= 5;
}
else
{
expected *= 3;
median *= 3;
minimum *= 2;
maximum *= 5;
}
break;
case EFFECT_ENDEAVOR:
// If target has less HP than user, Endeavor does no damage
expected = minimum = max(0, gBattleMons[damageCalcData->battlerDef].hp - gBattleMons[damageCalcData->battlerAtk].hp);
median = maximum = minimum = max(0, gBattleMons[damageCalcData->battlerDef].hp - gBattleMons[damageCalcData->battlerAtk].hp);
break;
case EFFECT_SUPER_FANG:
expected = minimum = (abilityAtk == ABILITY_PARENTAL_BOND
median = maximum = minimum = (abilityAtk == ABILITY_PARENTAL_BOND
? max(2, gBattleMons[damageCalcData->battlerDef].hp * 3 / 4)
: max(1, gBattleMons[damageCalcData->battlerDef].hp / 2));
break;
case EFFECT_FINAL_GAMBIT:
expected = minimum = gBattleMons[damageCalcData->battlerAtk].hp;
median = maximum = minimum = gBattleMons[damageCalcData->battlerAtk].hp;
break;
case EFFECT_BEAT_UP:
if (B_BEAT_UP >= GEN_5)
@ -598,10 +618,10 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal
u32 i;
gBattleStruct->beatUpSlot = 0;
damageCalcData->isCrit = FALSE;
expected = 0;
median = 0;
for (i = 0; i < partyCount; i++)
expected += CalculateMoveDamage(damageCalcData, 0);
minimum = expected;
median += CalculateMoveDamage(damageCalcData, 0);
maximum = minimum = median;
gBattleStruct->beatUpSlot = 0;
}
break;
@ -611,20 +631,39 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal
u32 strikeCount = GetMoveStrikeCount(move);
if (strikeCount > 1 && effect != EFFECT_TRIPLE_KICK)
{
expected *= strikeCount;
median *= strikeCount;
minimum *= strikeCount;
maximum *= strikeCount;
}
if (expected == 0)
expected = 1;
if (median == 0)
median = 1;
if (minimum == 0)
minimum = 1;
if (maximum == 0)
maximum = 1;
*expectedDamage = expected;
*medianDamage = median;
*minimumDamage = minimum;
*maximumDamage = maximum;
}
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType)
static inline bool32 ShouldCalcCritDamage(s32 critChanceIndex, u32 battlerAtk)
{
if (critChanceIndex == CRITICAL_HIT_ALWAYS)
return TRUE;
if (critChanceIndex >= RISKY_AI_CRIT_STAGE_THRESHOLD // Not guaranteed but above Risky threshold
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)
&& GetGenConfig(GEN_CONFIG_CRIT_CHANCE) != GEN_1)
return TRUE;
if (critChanceIndex >= RISKY_AI_CRIT_THRESHOLD_GEN_1 // Not guaranteed but above Risky threshold
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)
&& GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
return TRUE;
return FALSE;
}
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, bool32 considerZPower, u32 weather)
{
struct SimulatedDamage simDamage;
s32 moveType;
@ -678,57 +717,40 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
damageCalcData.randomFactor = FALSE;
damageCalcData.updateFlags = FALSE;
critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
if (critChanceIndex > 1) // Consider crit damage only if a move has at least +2 crit chance
// Get crit chance
if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1)
critChanceIndex = CalcCritChanceStageGen1(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
else
critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
// Use crit damage
if (ShouldCalcCritDamage(critChanceIndex, battlerAtk))
{
damageCalcData.isCrit = FALSE;
s32 nonCritDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
nonCritDmg = ApplyModifiersAfterDmgRoll(nonCritDmg, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
damageCalcData.isCrit = TRUE;
s32 critDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.expected = GetDamageByRollType(critDmg, rollType);
simDamage.expected = ApplyModifiersAfterDmgRoll(simDamage.expected, &damageCalcData, effectivenessMultiplier,
simDamage.minimum = GetDamageByRollType(critDmg, DMG_ROLL_LOWEST);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.minimum = (GetCritHitOdds(critChanceIndex) == 1) ? LowestRollDmg(critDmg) : LowestRollDmg(nonCritDmg);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.median = GetDamageByRollType(critDmg, DMG_ROLL_DEFAULT);
simDamage.median = ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.maximum = GetDamageByRollType(critDmg, DMG_ROLL_HIGHEST);
simDamage.maximum = ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
}
else if (critChanceIndex == -2) // Guaranteed critical
{
damageCalcData.isCrit = TRUE;
s32 critDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.expected = GetDamageByRollType(critDmg, rollType);
simDamage.minimum = LowestRollDmg(critDmg);
simDamage.expected = ApplyModifiersAfterDmgRoll(simDamage.expected, &damageCalcData, effectivenessMultiplier,
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
}
// Use non-crit damage
else
{
s32 nonCritDmg = 0;
s32 nonCritDmg = 0, tripleKickDmgMin = 0, tripleKickDmgDefault = 0, tripleKickDmgMax = 0;
if (moveEffect == EFFECT_TRIPLE_KICK)
{
for (gMultiHitCounter = GetMoveStrikeCount(move); gMultiHitCounter > 0; gMultiHitCounter--) // The global is used to simulate actual damage done
@ -738,17 +760,24 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.expected = GetDamageByRollType(oneTripleKickHit, rollType);
simDamage.minimum = LowestRollDmg(oneTripleKickHit);
nonCritDmg += ApplyModifiersAfterDmgRoll(simDamage.expected, &damageCalcData, effectivenessMultiplier,
simDamage.minimum = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_LOWEST);
tripleKickDmgMin += ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
nonCritDmg += ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
simDamage.median = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_DEFAULT);
tripleKickDmgDefault += ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.maximum = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_HIGHEST);
tripleKickDmgMax += ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
}
simDamage.minimum = tripleKickDmgMin;
simDamage.median = tripleKickDmgDefault;
simDamage.maximum = tripleKickDmgMax;
}
else
{
@ -757,24 +786,29 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.expected = GetDamageByRollType(nonCritDmg, rollType);
simDamage.minimum = LowestRollDmg(nonCritDmg);
simDamage.minimum = GetDamageByRollType(nonCritDmg, DMG_ROLL_LOWEST);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.expected = ApplyModifiersAfterDmgRoll(simDamage.expected, &damageCalcData, effectivenessMultiplier,
simDamage.median = GetDamageByRollType(nonCritDmg, DMG_ROLL_DEFAULT);
simDamage.median = ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.maximum = GetDamageByRollType(nonCritDmg, DMG_ROLL_HIGHEST);
simDamage.maximum = ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
}
}
if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
CalcDynamicMoveDamage(&damageCalcData,
&simDamage.expected,
&simDamage.median,
&simDamage.minimum,
&simDamage.maximum,
aiData->holdEffects[battlerAtk],
aiData->abilities[battlerAtk]);
}
@ -783,8 +817,9 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
}
else
{
simDamage.expected = 0;
simDamage.minimum = 0;
simDamage.median = 0;
simDamage.maximum = 0;
}
// convert multiper to AI_EFFECTIVENESS_xX
@ -1065,14 +1100,14 @@ u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef)
return GetNoOfHitsToKO(dmg, gBattleMons[battlerDef].hp);
}
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex)
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext)
{
return GetNoOfHitsToKOBattlerDmg(AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex].expected, battlerDef);
return GetNoOfHitsToKOBattlerDmg(AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA), battlerDef);
}
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef)
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext)
{
int bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex].expected;
int bestDmg = AI_GetDamage(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, calcContext, AI_DATA);
return (bestDmg * 100) / gBattleMons[battlerDef].maxHP;
}
@ -1182,15 +1217,15 @@ static bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
// Check if target has means to faint ai mon.
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
{
s32 i;
s32 moveIndex;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
u16 *moves = GetMovesArray(battlerDef);
for (i = 0; i < MAX_MON_MOVES; i++)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i))
&& AI_DATA->simulatedDmg[battlerDef][battlerAtk][i].expected >= gBattleMons[battlerAtk].hp
&& !CanEndureHit(battlerDef, battlerAtk, moves[i]))
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex))
&& AI_GetDamage(battlerDef, battlerAtk, moveIndex, AI_DEFENDING, AI_DATA) >= gBattleMons[battlerAtk].hp
&& !CanEndureHit(battlerDef, battlerAtk, moves[moveIndex]))
{
return TRUE;
}
@ -1207,7 +1242,7 @@ u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk)
for (i = 0; i < MAX_MON_MOVES; i++)
{
currNumberOfHits = GetNoOfHitsToKOBattler(battlerDef, battlerAtk, i);
currNumberOfHits = GetNoOfHitsToKOBattler(battlerDef, battlerAtk, i, AI_DEFENDING);
if (currNumberOfHits != 0)
{
if (currNumberOfHits < leastNumberOfHits)
@ -1217,41 +1252,41 @@ u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk)
return leastNumberOfHits;
}
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef)
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext)
{
u32 i;
u32 moveIndex;
u32 move = 0;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battlerAtk];
u16 *moves = GetMovesArray(battlerAtk);
for (i = 0; i < MAX_MON_MOVES; i++)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i))
&& bestDmg < AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected)
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex))
&& bestDmg < AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA))
{
bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected;
move = moves[i];
bestDmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA);
move = moves[moveIndex];
}
}
return move;
}
u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget)
u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext calcContext)
{
u32 i;
u32 moveIndex;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battler];
u16 *moves = GetMovesArray(battler);
for (i = 0; i < MAX_MON_MOVES; i++)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[i] != MOVE_NONE
&& moves[i] != MOVE_UNAVAILABLE
&& !(unusable & (1u << i))
&& bestDmg < AI_DATA->simulatedDmg[battler][battlerTarget][i].expected)
if (moves[moveIndex] != MOVE_NONE
&& moves[moveIndex] != MOVE_UNAVAILABLE
&& !(unusable & (1u << moveIndex))
&& bestDmg < AI_GetDamage(battler, battlerTarget, moveIndex, calcContext, AI_DATA))
{
bestDmg = AI_DATA->simulatedDmg[battler][battlerTarget][i].expected;
bestDmg = AI_GetDamage(battler, battlerTarget, moveIndex, calcContext, AI_DATA);
}
}
@ -1262,16 +1297,16 @@ u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget)
// 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)
{
s32 i, dmg;
s32 moveIndex, dmg;
u32 moveLimitations = AI_DATA->moveLimitations[battlerAtk];
u16 *moves = gBattleMons[battlerAtk].moves;
for (i = 0; i < MAX_MON_MOVES; i++)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(moveLimitations & (1u << i)))
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(moveLimitations & (1u << moveIndex)))
{
// Use the pre-calculated value in simulatedDmg instead of re-calculating it
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected;
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_ATTACKING, AI_DATA);
if (numHits)
dmg *= numHits;
@ -1281,7 +1316,7 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
if (numHits > 1)
return TRUE;
if (!CanEndureHit(battlerAtk, battlerDef, moves[i]))
if (!CanEndureHit(battlerAtk, battlerDef, moves[moveIndex]))
return TRUE;
}
}
@ -1295,7 +1330,7 @@ bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits)
u32 indexSlot = GetMoveSlot(GetMovesArray(battlerDef), move);
if (indexSlot < MAX_MON_MOVES)
{
if (GetNoOfHitsToKO(AI_DATA->simulatedDmg[battlerDef][battlerAtk][indexSlot].expected, gBattleMons[battlerAtk].hp) <= nHits)
if (GetNoOfHitsToKO(AI_GetDamage(battlerDef, battlerAtk, indexSlot, AI_DEFENDING, AI_DATA), gBattleMons[battlerAtk].hp) <= nHits)
return TRUE;
}
return FALSE;
@ -1304,7 +1339,7 @@ bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits)
// Check if target has means to faint ai mon after modding hp/dmg
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod)
{
u32 i;
u32 moveIndex;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
s32 dmg;
u16 *moves = gBattleResources->battleHistory->usedMoves[battlerDef];
@ -1313,13 +1348,13 @@ bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dm
if (hpCheck > gBattleMons[battlerAtk].maxHP)
hpCheck = gBattleMons[battlerAtk].maxHP;
for (i = 0; i < MAX_MON_MOVES; i++)
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected;
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, AI_DEFENDING, AI_DATA);
if (dmgMod)
dmg *= dmgMod;
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i)) && dmg >= hpCheck)
if (moves[moveIndex] != MOVE_NONE && moves[moveIndex] != MOVE_UNAVAILABLE && !(unusable & (1u << moveIndex)) && dmg >= hpCheck)
{
return TRUE;
}
@ -2009,17 +2044,29 @@ bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility)
return FALSE;
}
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits)
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext)
{
s32 dmg;
u16 *moves = gBattleMons[battlerAtk].moves;
if (numHits)
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index].expected * numHits;
if (IsDoubleBattle() && battlerDef == BATTLE_PARTNER(battlerAtk))
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex].maximum; // Attacking partner, be careful
else
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index].minimum;
dmg = AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, AI_DATA);
if (gBattleMons[battlerDef].hp <= dmg && !CanEndureHit(battlerAtk, battlerDef, moves[index]))
if (gBattleMons[battlerDef].hp <= dmg && !CanEndureHit(battlerAtk, battlerDef, moves[moveIndex]))
return TRUE;
return FALSE;
}
bool32 CanIndexMoveGuaranteeFaintTarget(u32 battlerAtk, u32 battlerDef, u32 moveIndex)
{
s32 dmg;
u16 *moves = gBattleMons[battlerAtk].moves;
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex].minimum; // Explictly care about guaranteed KOs universally
if (gBattleMons[battlerDef].hp <= dmg && !CanEndureHit(battlerAtk, battlerDef, moves[moveIndex]))
return TRUE;
return FALSE;
}
@ -3362,12 +3409,12 @@ bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage)
return FALSE;
}
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent)
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent, enum DamageCalcContext calcContext)
{
if (move == 0xFFFF || AI_IsFaster(battlerAtk, battlerDef, move))
{
// using item or user going first
s32 damage = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex].expected;
s32 damage = AI_GetDamage(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, calcContext, AI_DATA);
s32 healAmount = (healPercent * damage) / 100;
if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK)
healAmount = 0;
@ -3612,7 +3659,7 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move)
switch (GetMoveEffect(move))
{
case EFFECT_WISH:
return ShouldRecover(battlerAtk, battlerDef, move, 50); // Switch recovery isn't good idea in doubles
return ShouldRecover(battlerAtk, battlerDef, move, 50, AI_DEFENDING); // Switch recovery isn't good idea in doubles
case EFFECT_HEAL_BELL:
if (hasStatus)
return TRUE;
@ -3638,7 +3685,7 @@ void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons)
}
// party logic
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool32 isPartyMonAttacker, enum DamageRollType rollType)
s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool32 isPartyMonAttacker)
{
struct SimulatedDamage dmg;
uq4_12_t effectiveness;
@ -3659,7 +3706,7 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl
AI_THINKING_STRUCT->saved[battlerAtk].saved = FALSE;
}
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE, AI_GetWeather(), rollType);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, FALSE, AI_GetWeather());
// restores original gBattleMon struct
FreeRestoreBattleMons(savedBattleMons);
@ -3668,7 +3715,7 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl
else
SetBattlerAiData(battlerDef, AI_DATA);
return dmg.expected;
return dmg.median;
}
u32 AI_WhoStrikesFirstPartyMon(u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, u32 moveConsidered)
@ -4034,7 +4081,7 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects physical attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack + 10))
{
if (GetMoveCategory(GetBestDmgMoveFromBattler(battlerDef, battlerAtk)) == DAMAGE_CATEGORY_PHYSICAL)
if (GetMoveCategory(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING)) == DAMAGE_CATEGORY_PHYSICAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
@ -4070,7 +4117,7 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0) && GetMoveEffect(GetBestDmgMoveFromBattler(battlerAtk, battlerDef)) != EFFECT_FOCUS_PUNCH)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0) && GetMoveEffect(GetBestDmgMoveFromBattler(battlerAtk, battlerDef, AI_ATTACKING)) != EFFECT_FOCUS_PUNCH)
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
@ -4118,7 +4165,7 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects special attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack + 10))
{
if (GetMoveCategory(GetBestDmgMoveFromBattler(battlerDef, battlerAtk)) == DAMAGE_CATEGORY_SPECIAL)
if (GetMoveCategory(GetBestDmgMoveFromBattler(battlerDef, battlerAtk, AI_DEFENDING)) == DAMAGE_CATEGORY_SPECIAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
@ -4168,7 +4215,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
else if (!IsBattleMoveStatus(chosenMove) && IsBattleMoveStatus(zMove))
return FALSE;
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT);
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE);
if (!IsBattleMoveStatus(chosenMove) && dmg.minimum >= gBattleMons[battlerDef].hp)
return FALSE; // don't waste damaging z move if can otherwise faint target
@ -4299,13 +4346,13 @@ u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
u32 scoreIncrease = 0;
if (effect == EFFECT_SUBSTITUTE) // Substitute specific
{
if (HasAnyKnownMove(battlerDef) && GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 4)
if (HasAnyKnownMove(battlerDef) && GetBestDmgFromBattler(battlerDef, battlerAtk, AI_DEFENDING) < gBattleMons[battlerAtk].maxHP / 4)
scoreIncrease += GOOD_EFFECT;
}
else if (effect == EFFECT_SHED_TAIL) // Shed Tail specific
{
if ((ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex))
&& (HasAnyKnownMove(battlerDef) && (GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 2)))
&& (HasAnyKnownMove(battlerDef) && (GetBestDmgFromBattler(battlerDef, battlerAtk, AI_DEFENDING) < gBattleMons[battlerAtk].maxHP / 2)))
scoreIncrease += BEST_EFFECT;
}

View File

@ -946,7 +946,7 @@ static void PutMovesPointsText(struct BattleDebugMenu *data)
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 83 + count * 54, i * 15, 0, NULL);
ConvertIntToDecimalStringN(text,
AI_DATA->simulatedDmg[data->aiBattlerId][battlerDef][i].expected,
AI_GetDamage(data->aiBattlerId, battlerDef, i, AI_ATTACKING, AI_DATA),
STR_CONV_MODE_RIGHT_ALIGN, 3);
AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, text, 110 + count * 54, i * 15, 0, NULL);

View File

@ -1830,8 +1830,6 @@ static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, u32 holdEffect)
return critStageIncrease;
}
#define CRITICAL_HIT_BLOCKED -1
#define CRITICAL_HIT_ALWAYS -2
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk)
{
s32 critChance = 0;
@ -1940,8 +1938,6 @@ s32 GetCritHitOdds(s32 critChanceIndex)
else
return GetCriticalHitOdds(critChanceIndex);
}
#undef CRITICAL_HIT_BLOCKED
#undef CRITICAL_HIT_ALWAYS
static void Cmd_critcalc(void)
{