Combines CalculateMoveDamage arguments into a struct (#5570)
This commit is contained in:
parent
13a26c462e
commit
d5f8e9cc0e
@ -15,7 +15,7 @@ enum DamageRollType
|
||||
DMG_ROLL_HIGHEST,
|
||||
};
|
||||
|
||||
enum AIPivot
|
||||
enum AIPivot
|
||||
{
|
||||
DONT_PIVOT,
|
||||
CAN_TRY_PIVOT,
|
||||
@ -25,6 +25,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);
|
||||
bool32 IsAiVsAiBattle(void);
|
||||
bool32 BattlerHasAi(u32 battlerId);
|
||||
bool32 IsAiBattlerAware(u32 battlerId);
|
||||
@ -99,8 +100,8 @@ 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, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType);
|
||||
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType);
|
||||
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 battlerAtk, u32 battlerDef, u32 move, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType);
|
||||
struct SimulatedDamage AI_CalcDamage(u32 battlerAtk, u32 battlerDef, u32 move, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType);
|
||||
bool32 AI_IsDamagedByRecoil(u32 battler);
|
||||
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
||||
u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef);
|
||||
|
||||
@ -134,6 +134,18 @@ enum {
|
||||
|
||||
extern const struct TypePower gNaturalGiftTable[];
|
||||
|
||||
struct DamageCalculationData
|
||||
{
|
||||
u32 battlerAtk:3;
|
||||
u32 battlerDef:3;
|
||||
u32 move:16;
|
||||
u32 moveType:5;
|
||||
u32 isCrit:1;
|
||||
u32 randomFactor:1;
|
||||
u32 updateFlags:1;
|
||||
u32 padding:2;
|
||||
};
|
||||
|
||||
void HandleAction_ThrowBall(void);
|
||||
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move);
|
||||
void HandleAction_UseMove(void);
|
||||
@ -212,9 +224,9 @@ u32 GetMoveSlot(u16 *moves, u32 move);
|
||||
u32 GetBattlerWeight(u32 battler);
|
||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||
s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
|
||||
s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef);
|
||||
s32 CalculateMoveDamage(struct DamageCalculationData *damageCalcData, u32 fixedBasePower);
|
||||
s32 CalculateMoveDamageVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef);
|
||||
uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u32 defAbility, bool32 recordAbilities);
|
||||
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
|
||||
uq4_12_t GetTypeModifier(u32 atkType, u32 defType);
|
||||
|
||||
@ -407,14 +407,13 @@ static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 ba
|
||||
return accuracy;
|
||||
}
|
||||
|
||||
static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlersCount)
|
||||
static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlersCount, u32 weather)
|
||||
{
|
||||
u32 battlerDef, i, weather;
|
||||
u16 *moves;
|
||||
|
||||
u32 battlerDef, moveIndex, move;
|
||||
u32 rollType = GetDmgRollType(battlerAtk);
|
||||
SaveBattlerData(battlerAtk);
|
||||
moves = GetMovesArray(battlerAtk);
|
||||
weather = AI_GetWeather(aiData);
|
||||
|
||||
SetBattlerData(battlerAtk);
|
||||
|
||||
@ -426,27 +425,22 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
|
||||
|
||||
SaveBattlerData(battlerDef);
|
||||
SetBattlerData(battlerDef);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
|
||||
{
|
||||
struct SimulatedDamage dmg = {0};
|
||||
u8 effectiveness = AI_EFFECTIVENESS_x0;
|
||||
u32 move = moves[i];
|
||||
move = moves[moveIndex];
|
||||
|
||||
if (move != 0
|
||||
&& move != 0xFFFF
|
||||
//&& gMovesInfo[move].power != 0 /* we want to get effectiveness and accuracy of status moves */
|
||||
&& !(aiData->moveLimitations[battlerAtk] & (1u << i)))
|
||||
&& !(aiData->moveLimitations[battlerAtk] & (1u << moveIndex)))
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_HIGHEST);
|
||||
else if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_LOWEST);
|
||||
else
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_DEFAULT);
|
||||
aiData->moveAccuracy[battlerAtk][battlerDef][i] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
|
||||
dmg = AI_CalcDamage(battlerAtk, battlerDef, move, &effectiveness, TRUE, weather, rollType);
|
||||
aiData->moveAccuracy[battlerAtk][battlerDef][moveIndex] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
|
||||
}
|
||||
aiData->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
|
||||
aiData->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
|
||||
aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex] = dmg;
|
||||
aiData->effectiveness[battlerAtk][battlerDef][moveIndex] = effectiveness;
|
||||
}
|
||||
RestoreBattlerData(battlerDef);
|
||||
}
|
||||
@ -455,7 +449,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
|
||||
|
||||
void SetAiLogicDataForTurn(struct AiLogicData *aiData)
|
||||
{
|
||||
u32 battlerAtk, battlersCount;
|
||||
u32 battlerAtk, battlersCount, weather;
|
||||
|
||||
memset(aiData, 0, sizeof(struct AiLogicData));
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart())
|
||||
@ -465,6 +459,8 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
|
||||
gBattleStruct->aiDelayTimer = gMain.vblankCounter1;
|
||||
|
||||
aiData->weatherHasEffect = WEATHER_HAS_EFFECT;
|
||||
weather = AI_GetWeather(aiData);
|
||||
|
||||
// get/assume all battler data and simulate AI damage
|
||||
battlersCount = gBattlersCount;
|
||||
|
||||
@ -482,7 +478,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
|
||||
if (!IsBattlerAlive(battlerAtk))
|
||||
continue;
|
||||
|
||||
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount);
|
||||
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount, weather);
|
||||
}
|
||||
AI_DATA->aiCalcInProgress = FALSE;
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
|
||||
playerMove = gBattleMons[opposingBattler].moves[i];
|
||||
if (playerMove != MOVE_NONE && gMovesInfo[playerMove].power != 0)
|
||||
{
|
||||
damageTaken = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather, DMG_ROLL_HIGHEST).expected;
|
||||
damageTaken = AI_CalcDamage(opposingBattler, battler, playerMove, &effectiveness, FALSE, weather, DMG_ROLL_HIGHEST).expected;
|
||||
if (damageTaken > maxDamageTaken)
|
||||
maxDamageTaken = damageTaken;
|
||||
}
|
||||
@ -430,7 +430,7 @@ static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler)
|
||||
u32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
|
||||
u32 incomingMove = AI_DATA->lastUsedMove[opposingBattler];
|
||||
bool32 isOpposingBattlerChargingOrInvulnerable = (IsSemiInvulnerable(opposingBattler, incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove));
|
||||
|
||||
|
||||
if (IsDoubleBattle() || !(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
|
||||
return FALSE;
|
||||
|
||||
@ -840,9 +840,9 @@ static bool32 ShouldSwitchIfEncored(u32 battler)
|
||||
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
|
||||
|
||||
// Stay in if effective move
|
||||
else if (AI_GetMoveEffectiveness(encoredMove, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
|
||||
else if (AI_GetMoveEffectiveness(encoredMove, battler, opposingBattler) >= AI_EFFECTIVENESS_x2)
|
||||
return FALSE;
|
||||
|
||||
|
||||
// Switch out 50% of the time otherwise
|
||||
else if (RandomPercentage(RNG_AI_SWITCH_ENCORE, 50) && AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE)
|
||||
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
|
||||
@ -1224,6 +1224,8 @@ 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;
|
||||
|
||||
gMoveResultFlags = 0;
|
||||
@ -1239,10 +1241,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
if (aiMove != MOVE_NONE && gMovesInfo[aiMove].power != 0)
|
||||
{
|
||||
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j);
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_CONSERVATIVE)
|
||||
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_LOWEST);
|
||||
else
|
||||
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_DEFAULT);
|
||||
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, rollType);
|
||||
if (bestDmg < dmg)
|
||||
{
|
||||
bestDmg = dmg;
|
||||
|
||||
@ -35,6 +35,16 @@
|
||||
static u32 AI_GetEffectiveness(uq4_12_t multiplier);
|
||||
|
||||
// Functions
|
||||
u32 GetDmgRollType(u32 battlerAtk)
|
||||
{
|
||||
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;
|
||||
|
||||
return DMG_ROLL_DEFAULT;
|
||||
}
|
||||
|
||||
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move)
|
||||
{
|
||||
return (AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER);
|
||||
@ -364,14 +374,15 @@ 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, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType)
|
||||
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 battlerAtk, u32 battlerDef, u32 move, u8 *typeEffectiveness, bool32 considerZPower, enum DamageRollType rollType)
|
||||
{
|
||||
struct SimulatedDamage dmg;
|
||||
|
||||
SaveBattlerData(battlerAtk);
|
||||
SaveBattlerData(battlerDef);
|
||||
SetBattlerData(battlerAtk);
|
||||
SetBattlerData(battlerDef);
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, typeEffectiveness, considerZPower, AI_GetWeather(AI_DATA), rollType);
|
||||
dmg = AI_CalcDamage(battlerAtk, battlerDef, move, typeEffectiveness, considerZPower, AI_GetWeather(AI_DATA), rollType);
|
||||
RestoreBattlerData(battlerAtk);
|
||||
RestoreBattlerData(battlerDef);
|
||||
return dmg;
|
||||
@ -398,12 +409,11 @@ static inline s32 DmgRoll(s32 dmg)
|
||||
return dmg;
|
||||
}
|
||||
|
||||
bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
bool32 IsDamageMoveUnusable(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveType)
|
||||
{
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
u32 battlerDefAbility;
|
||||
u32 partnerBattlerDefAbility;
|
||||
u32 moveType = GetMoveType(move);
|
||||
|
||||
if (DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
|
||||
{
|
||||
@ -491,7 +501,7 @@ static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType)
|
||||
return DmgRoll(dmg);
|
||||
}
|
||||
|
||||
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType)
|
||||
struct SimulatedDamage AI_CalcDamage(u32 battlerAtk, u32 battlerDef, u32 move, u8 *typeEffectiveness, bool32 considerZPower, u32 weather, enum DamageRollType rollType)
|
||||
{
|
||||
struct SimulatedDamage simDamage;
|
||||
s32 moveType;
|
||||
@ -517,7 +527,6 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]);
|
||||
}
|
||||
|
||||
moveEffect = gMovesInfo[move].effect;
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_PHOTON_GEYSER:
|
||||
@ -544,7 +553,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE);
|
||||
|
||||
if (gMovesInfo[move].power)
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(move, battlerAtk, battlerDef);
|
||||
isDamageMoveUnusable = IsDamageMoveUnusable(battlerAtk, battlerDef, move, moveType);
|
||||
|
||||
if (gMovesInfo[move].power && !isDamageMoveUnusable)
|
||||
{
|
||||
@ -566,15 +575,26 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
break;
|
||||
}
|
||||
|
||||
struct DamageCalculationData damageCalcData;
|
||||
damageCalcData.battlerAtk = battlerAtk;
|
||||
damageCalcData.battlerDef = battlerDef;
|
||||
damageCalcData.move = move;
|
||||
damageCalcData.moveType = moveType;
|
||||
damageCalcData.isCrit = FALSE;
|
||||
damageCalcData.randomFactor = FALSE;
|
||||
damageCalcData.updateFlags = FALSE;
|
||||
|
||||
critChanceIndex = CalcCritChanceStageArgs(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
|
||||
{
|
||||
s32 nonCritDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
damageCalcData.isCrit = FALSE;
|
||||
s32 nonCritDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
|
||||
effectivenessMultiplier, weather,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
damageCalcData.isCrit = TRUE;
|
||||
s32 critDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
|
||||
effectivenessMultiplier, weather,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
@ -588,8 +608,9 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
}
|
||||
else if (critChanceIndex == -2) // Guaranteed critical
|
||||
{
|
||||
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
damageCalcData.isCrit = TRUE;
|
||||
s32 critDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
|
||||
effectivenessMultiplier, weather,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
@ -603,16 +624,16 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
{
|
||||
for (gMultiHitCounter = gMovesInfo[move].strikeCount; gMultiHitCounter > 0; gMultiHitCounter--) // The global is used to simulate actual damage done
|
||||
{
|
||||
nonCritDmg += CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
nonCritDmg += CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
|
||||
effectivenessMultiplier, weather,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCritDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
nonCritDmg = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
|
||||
effectivenessMultiplier, weather,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
}
|
||||
@ -676,10 +697,11 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
u32 partyCount = CalculatePartyCount(GetBattlerParty(battlerAtk));
|
||||
u32 i;
|
||||
gBattleStruct->beatUpSlot = 0;
|
||||
damageCalcData.isCrit = FALSE;
|
||||
simDamage.expected = 0;
|
||||
for (i = 0; i < partyCount; i++)
|
||||
{
|
||||
simDamage.expected += CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE);
|
||||
simDamage.expected += CalculateMoveDamage(&damageCalcData, 0);
|
||||
}
|
||||
simDamage.minimum = simDamage.expected;
|
||||
gBattleStruct->beatUpSlot = 0;
|
||||
@ -3444,8 +3466,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(AI_DATA), rollType);
|
||||
dmg = AI_CalcDamage(battlerAtk, battlerDef, move, &effectiveness, FALSE, AI_GetWeather(AI_DATA), rollType);
|
||||
// restores original gBattleMon struct
|
||||
FreeRestoreBattleMons(savedBattleMons);
|
||||
|
||||
@ -3913,7 +3934,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(zMove))
|
||||
return FALSE;
|
||||
|
||||
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT);
|
||||
dmg = AI_CalcDamageSaveBattlers(battlerAtk, battlerDef, chosenMove, &effectiveness, FALSE, DMG_ROLL_DEFAULT);
|
||||
|
||||
if (!IS_MOVE_STATUS(chosenMove) && dmg.minimum >= gBattleMons[battlerDef].hp)
|
||||
return FALSE; // don't waste damaging z move if can otherwise faint target
|
||||
|
||||
@ -1303,7 +1303,14 @@ static void Cmd_attackcanceler(void)
|
||||
return;
|
||||
case DISOBEYS_HITS_SELF:
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE);
|
||||
struct DamageCalculationData damageCalcData;
|
||||
damageCalcData.battlerAtk = damageCalcData.battlerDef = gBattlerAttacker;
|
||||
damageCalcData.move = MOVE_NONE;
|
||||
damageCalcData.moveType = TYPE_MYSTERY;
|
||||
damageCalcData.isCrit = FALSE;
|
||||
damageCalcData.randomFactor = FALSE;
|
||||
damageCalcData.updateFlags = TRUE;
|
||||
gBattleMoveDamage = CalculateMoveDamage(&damageCalcData, 40);
|
||||
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
|
||||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||||
gHitMarker |= HITMARKER_OBEYS;
|
||||
@ -2123,10 +2130,19 @@ static void Cmd_damagecalc(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
u32 moveType = GetMoveType(gCurrentMove);
|
||||
if (gMovesInfo[gCurrentMove].effect == EFFECT_SHELL_SIDE_ARM)
|
||||
gBattleStruct->swapDamageCategory = (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] != gMovesInfo[gCurrentMove].category);
|
||||
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
|
||||
|
||||
struct DamageCalculationData damageCalcData;
|
||||
damageCalcData.battlerAtk = gBattlerAttacker;
|
||||
damageCalcData.battlerDef = gBattlerTarget;
|
||||
damageCalcData.move = gCurrentMove;
|
||||
damageCalcData.moveType = GetMoveType(gCurrentMove);
|
||||
damageCalcData.isCrit = gIsCriticalHit;
|
||||
damageCalcData.randomFactor = TRUE;
|
||||
damageCalcData.updateFlags = TRUE;
|
||||
|
||||
gBattleMoveDamage = CalculateMoveDamage(&damageCalcData, 0);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
||||
@ -1250,7 +1250,15 @@ static void TrySetBattleSeminarShow(void)
|
||||
powerOverride = 0;
|
||||
if (ShouldCalculateDamage(gCurrentMove, &dmgByMove[i], &powerOverride))
|
||||
{
|
||||
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, gMovesInfo[gCurrentMove].type, powerOverride, FALSE, FALSE, FALSE);
|
||||
struct DamageCalculationData damageCalcData;
|
||||
damageCalcData.battlerAtk = gBattlerAttacker;
|
||||
damageCalcData.battlerDef = gBattlerTarget;
|
||||
damageCalcData.move = gCurrentMove;
|
||||
damageCalcData.moveType = gMovesInfo[gCurrentMove].type;
|
||||
damageCalcData.isCrit = FALSE;
|
||||
damageCalcData.randomFactor = FALSE;
|
||||
damageCalcData.updateFlags = FALSE;
|
||||
gBattleMoveDamage = CalculateMoveDamage(&damageCalcData, powerOverride);
|
||||
dmgByMove[i] = gBattleMoveDamage;
|
||||
if (dmgByMove[i] == 0 && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||||
dmgByMove[i] = 1;
|
||||
|
||||
@ -3391,7 +3391,14 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
|
||||
{
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = TRUE;
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE);
|
||||
struct DamageCalculationData damageCalcData;
|
||||
damageCalcData.battlerAtk = damageCalcData.battlerDef = gBattlerAttacker;
|
||||
damageCalcData.move = MOVE_NONE;
|
||||
damageCalcData.moveType = TYPE_MYSTERY;
|
||||
damageCalcData.isCrit = FALSE;
|
||||
damageCalcData.randomFactor = FALSE;
|
||||
damageCalcData.updateFlags = TRUE;
|
||||
gBattleMoveDamage = CalculateMoveDamage(&damageCalcData, 40);
|
||||
gProtectStructs[gBattlerAttacker].confusionSelfDmg = TRUE;
|
||||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||||
}
|
||||
@ -8735,8 +8742,12 @@ u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc)
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 GetMoveTargetCount(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
u32 GetMoveTargetCount(struct DamageCalculationData *damageCalcData)
|
||||
{
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
|
||||
switch (GetBattlerMoveTargetType(battlerAtk, move))
|
||||
{
|
||||
case MOVE_TARGET_BOTH:
|
||||
@ -8874,8 +8885,12 @@ u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter)
|
||||
return basePower;
|
||||
}
|
||||
|
||||
static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 weather)
|
||||
static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData, u32 abilityDef, u32 weather)
|
||||
{
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
|
||||
u32 i;
|
||||
u32 basePower = gMovesInfo[move].power;
|
||||
u32 weight, hpFraction, speed;
|
||||
@ -9137,11 +9152,16 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
|
||||
return basePower;
|
||||
}
|
||||
|
||||
static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectAtk, u32 weather)
|
||||
static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, u32 holdEffectAtk, u32 weather)
|
||||
{
|
||||
u32 i;
|
||||
u32 holdEffectParamAtk;
|
||||
u32 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef, defAbility, weather);
|
||||
u32 basePower = CalcMoveBasePower(damageCalcData, defAbility, weather);
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
uq4_12_t holdEffectModifier;
|
||||
uq4_12_t modifier = UQ_4_12(1.0);
|
||||
u32 atkSide = GetBattlerSide(battlerAtk);
|
||||
@ -9343,7 +9363,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
|
||||
if (moveType == TYPE_FIRE)
|
||||
{
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.5));
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
RecordAbilityBattle(battlerDef, defAbility);
|
||||
}
|
||||
break;
|
||||
@ -9463,12 +9483,16 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
|
||||
return uq4_12_multiply_by_int_half_down(modifier, basePower);
|
||||
}
|
||||
|
||||
static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 isCrit, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectAtk, u32 weather)
|
||||
static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, u32 holdEffectAtk, u32 weather)
|
||||
{
|
||||
u8 atkStage;
|
||||
u32 atkStat;
|
||||
uq4_12_t modifier;
|
||||
u16 atkBaseSpeciesId;
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
atkBaseSpeciesId = GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species);
|
||||
|
||||
@ -9517,7 +9541,7 @@ static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 m
|
||||
}
|
||||
|
||||
// critical hits ignore attack stat's stage drops
|
||||
if (isCrit && atkStage < DEFAULT_STAT_STAGE)
|
||||
if (damageCalcData->isCrit && atkStage < DEFAULT_STAT_STAGE)
|
||||
atkStage = DEFAULT_STAT_STAGE;
|
||||
// pokemon with unaware ignore attack stat changes while taking damage
|
||||
if (defAbility == ABILITY_UNAWARE)
|
||||
@ -9663,7 +9687,7 @@ static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 m
|
||||
if (moveType == TYPE_FIRE || moveType == TYPE_ICE)
|
||||
{
|
||||
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5));
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
RecordAbilityBattle(battlerDef, ABILITY_THICK_FAT);
|
||||
}
|
||||
break;
|
||||
@ -9740,12 +9764,15 @@ static bool32 CanEvolve(u32 species)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, bool32 isCrit, bool32 updateFlags, u32 atkAbility, u32 defAbility, u32 holdEffectDef, u32 weather)
|
||||
static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, u32 holdEffectDef, u32 weather)
|
||||
{
|
||||
bool32 usesDefStat;
|
||||
u8 defStage;
|
||||
u32 defStat, def, spDef;
|
||||
uq4_12_t modifier;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped
|
||||
{
|
||||
@ -9776,7 +9803,7 @@ static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32
|
||||
defStat /= 2;
|
||||
|
||||
// critical hits ignore positive stat changes
|
||||
if (isCrit && defStage > DEFAULT_STAT_STAGE)
|
||||
if (damageCalcData->isCrit && defStage > DEFAULT_STAT_STAGE)
|
||||
defStage = DEFAULT_STAT_STAGE;
|
||||
// pokemon with unaware ignore defense stat changes while dealing damage
|
||||
if (atkAbility == ABILITY_UNAWARE)
|
||||
@ -9798,7 +9825,7 @@ static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat)
|
||||
{
|
||||
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
RecordAbilityBattle(battlerDef, ABILITY_MARVEL_SCALE);
|
||||
}
|
||||
break;
|
||||
@ -9806,7 +9833,7 @@ static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32
|
||||
if (usesDefStat)
|
||||
{
|
||||
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
RecordAbilityBattle(battlerDef, ABILITY_FUR_COAT);
|
||||
}
|
||||
break;
|
||||
@ -9814,7 +9841,7 @@ static inline u32 CalcDefenseStat(u32 move, u32 battlerAtk, u32 battlerDef, u32
|
||||
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat)
|
||||
{
|
||||
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
RecordAbilityBattle(battlerDef, ABILITY_GRASS_PELT);
|
||||
}
|
||||
break;
|
||||
@ -9898,9 +9925,9 @@ static inline s32 CalculateBaseDamage(u32 power, u32 userFinalAttack, u32 level,
|
||||
return power * userFinalAttack * (2 * level / 5 + 2) / targetFinalDefense / 50 + 2;
|
||||
}
|
||||
|
||||
static inline uq4_12_t GetTargetDamageModifier(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
static inline uq4_12_t GetTargetDamageModifier(struct DamageCalculationData *damageCalcData)
|
||||
{
|
||||
if (IsDoubleBattle() && GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2)
|
||||
if (IsDoubleBattle() && GetMoveTargetCount(damageCalcData) >= 2)
|
||||
return B_MULTIPLE_TARGETS_DMG >= GEN_4 ? UQ_4_12(0.75) : UQ_4_12(0.5);
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
@ -9912,8 +9939,12 @@ static inline uq4_12_t GetParentalBondModifier(u32 battlerAtk)
|
||||
return B_PARENTAL_BOND_DMG >= GEN_7 ? UQ_4_12(0.25) : UQ_4_12(0.5);
|
||||
}
|
||||
|
||||
static inline uq4_12_t GetSameTypeAttackBonusModifier(u32 battlerAtk, u32 moveType, u32 move, u32 abilityAtk)
|
||||
static inline uq4_12_t GetSameTypeAttackBonusModifier(struct DamageCalculationData *damageCalcData, u32 abilityAtk)
|
||||
{
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
if (moveType == TYPE_MYSTERY)
|
||||
return UQ_4_12(1.0);
|
||||
else if (gBattleStruct->pledgeMove && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), moveType))
|
||||
@ -9924,8 +9955,11 @@ static inline uq4_12_t GetSameTypeAttackBonusModifier(u32 battlerAtk, u32 moveTy
|
||||
}
|
||||
|
||||
// Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks.
|
||||
static uq4_12_t GetWeatherDamageModifier(u32 battlerAtk, u32 move, u32 moveType, u32 holdEffectAtk, u32 holdEffectDef, u32 weather)
|
||||
static uq4_12_t GetWeatherDamageModifier(struct DamageCalculationData *damageCalcData, u32 holdEffectAtk, u32 holdEffectDef, u32 weather)
|
||||
{
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
if (weather == B_WEATHER_NONE)
|
||||
return UQ_4_12(1.0);
|
||||
if (gMovesInfo[move].effect == EFFECT_HYDRO_STEAM && (weather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA)
|
||||
@ -9948,8 +9982,11 @@ static uq4_12_t GetWeatherDamageModifier(u32 battlerAtk, u32 move, u32 moveType,
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
static inline uq4_12_t GetBurnOrFrostBiteModifier(u32 battlerAtk, u32 move, u32 abilityAtk)
|
||||
static inline uq4_12_t GetBurnOrFrostBiteModifier(struct DamageCalculationData *damageCalcData, u32 abilityAtk)
|
||||
{
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 move = damageCalcData->move;
|
||||
|
||||
if (gBattleMons[battlerAtk].status1 & STATUS1_BURN
|
||||
&& IS_MOVE_PHYSICAL(move)
|
||||
&& (B_BURN_FACADE_DMG < GEN_6 || gMovesInfo[move].effect != EFFECT_FACADE)
|
||||
@ -9976,9 +10013,9 @@ static inline uq4_12_t GetGlaiveRushModifier(u32 battlerDef)
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(u32 battlerDef, u32 move)
|
||||
static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(struct DamageCalculationData *damageCalcData)
|
||||
{
|
||||
if ((IsZMove(move) || IsMaxMove(move)) && IS_BATTLER_PROTECTED(battlerDef))
|
||||
if ((IsZMove(damageCalcData->move) || IsMaxMove(damageCalcData->move)) && IS_BATTLER_PROTECTED(damageCalcData->battlerDef))
|
||||
return UQ_4_12(0.25);
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
@ -10119,8 +10156,11 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
|
||||
static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq4_12_t typeEffectivenessModifier, bool32 updateFlags, u32 abilityDef, u32 holdEffectDef)
|
||||
static inline uq4_12_t GetDefenderItemsModifier(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 abilityDef, u32 holdEffectDef)
|
||||
{
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
u32 holdEffectDefParam = GetBattlerHoldEffectParam(battlerDef);
|
||||
u32 itemDef = gBattleMons[battlerDef].item;
|
||||
|
||||
@ -10131,7 +10171,7 @@ static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq
|
||||
return UQ_4_12(1.0);
|
||||
if (moveType == holdEffectDefParam && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0)))
|
||||
{
|
||||
if (updateFlags)
|
||||
if (damageCalcData->updateFlags)
|
||||
gSpecialStatuses[battlerDef].berryReduced = TRUE;
|
||||
return (abilityDef == ABILITY_RIPEN) ? UQ_4_12(0.25) : UQ_4_12(0.5);
|
||||
}
|
||||
@ -10150,9 +10190,15 @@ static inline uq4_12_t GetDefenderItemsModifier(u32 moveType, u32 battlerDef, uq
|
||||
// https://bulbapedia.bulbagarden.net/wiki/Damage#Generation_V_onward
|
||||
// Please Note: Fixed Point Multiplication is not associative.
|
||||
// The order of operations is relevant.
|
||||
static inline uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, bool32 isCrit, uq4_12_t typeEffectivenessModifier, bool32 updateFlags,
|
||||
static inline uq4_12_t GetOtherModifiers(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier,
|
||||
u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk, u32 holdEffectDef)
|
||||
{
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
u32 isCrit = damageCalcData->isCrit;
|
||||
|
||||
uq4_12_t finalModifier = UQ_4_12(1.0);
|
||||
u32 battlerDefPartner = BATTLE_PARTNER(battlerDef);
|
||||
u32 unmodifiedAttackerSpeed = gBattleMons[battlerAtk].speed;
|
||||
@ -10171,14 +10217,14 @@ static inline uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk,
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags, abilityDef, holdEffectDef));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(damageCalcData, typeEffectivenessModifier, abilityDef, holdEffectDef));
|
||||
}
|
||||
else
|
||||
{
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit, abilityAtk));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(moveType, battlerDef, typeEffectivenessModifier, updateFlags, abilityDef, holdEffectDef));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(damageCalcData, typeEffectivenessModifier, abilityDef, holdEffectDef));
|
||||
DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk));
|
||||
}
|
||||
return finalModifier;
|
||||
@ -10190,73 +10236,75 @@ static inline uq4_12_t GetOtherModifiers(u32 move, u32 moveType, u32 battlerAtk,
|
||||
dmg = uq4_12_multiply_by_int_half_down(modifier, dmg); \
|
||||
} while (0)
|
||||
|
||||
static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower,
|
||||
bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather,
|
||||
u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef)
|
||||
static inline s32 DoMoveDamageCalcVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, u32 weather,
|
||||
u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef)
|
||||
{
|
||||
s32 dmg;
|
||||
u32 userFinalAttack;
|
||||
u32 targetFinalDefense;
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
|
||||
if (fixedBasePower)
|
||||
gBattleMovePower = fixedBasePower;
|
||||
else
|
||||
gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType, updateFlags, abilityAtk, abilityDef, holdEffectAtk, weather);
|
||||
gBattleMovePower = CalcMoveBasePowerAfterModifiers(damageCalcData, abilityAtk, abilityDef, holdEffectAtk, weather);
|
||||
|
||||
userFinalAttack = CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags, abilityAtk, abilityDef, holdEffectAtk, weather);
|
||||
targetFinalDefense = CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags, abilityAtk, abilityDef, holdEffectDef, weather);
|
||||
userFinalAttack = CalcAttackStat(damageCalcData, abilityAtk, abilityDef, holdEffectAtk, weather);
|
||||
targetFinalDefense = CalcDefenseStat(damageCalcData, abilityAtk, abilityDef, holdEffectDef, weather);
|
||||
|
||||
dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, gBattleMons[battlerAtk].level, targetFinalDefense);
|
||||
DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(move, battlerAtk, battlerDef));
|
||||
DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(damageCalcData));
|
||||
DAMAGE_APPLY_MODIFIER(GetParentalBondModifier(battlerAtk));
|
||||
DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(battlerAtk, move, moveType, holdEffectAtk, holdEffectDef, weather));
|
||||
DAMAGE_APPLY_MODIFIER(GetCriticalModifier(isCrit));
|
||||
DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(damageCalcData, holdEffectAtk, holdEffectDef, weather));
|
||||
DAMAGE_APPLY_MODIFIER(GetCriticalModifier(damageCalcData->isCrit));
|
||||
DAMAGE_APPLY_MODIFIER(GetGlaiveRushModifier(battlerDef));
|
||||
|
||||
if (randomFactor)
|
||||
if (damageCalcData->randomFactor)
|
||||
{
|
||||
dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO);
|
||||
dmg /= 100;
|
||||
}
|
||||
|
||||
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
|
||||
DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType));
|
||||
DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, damageCalcData->moveType));
|
||||
else
|
||||
DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk));
|
||||
DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(damageCalcData, abilityAtk));
|
||||
DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier);
|
||||
DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk));
|
||||
DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(battlerDef, move));
|
||||
DAMAGE_APPLY_MODIFIER(GetOtherModifiers(move, moveType, battlerAtk, battlerDef, isCrit, typeEffectivenessModifier, updateFlags, abilityAtk, abilityDef, holdEffectAtk, holdEffectDef));
|
||||
DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(damageCalcData, abilityAtk));
|
||||
DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(damageCalcData));
|
||||
DAMAGE_APPLY_MODIFIER(GetOtherModifiers(damageCalcData, typeEffectivenessModifier, abilityAtk, abilityDef, holdEffectAtk, holdEffectDef));
|
||||
|
||||
if (dmg == 0)
|
||||
dmg = 1;
|
||||
return dmg;
|
||||
}
|
||||
|
||||
static inline s32 DoMoveDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower,
|
||||
bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather)
|
||||
static inline s32 DoMoveDamageCalc(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, u32 weather)
|
||||
{
|
||||
u32 holdEffectAtk, holdEffectDef, abilityAtk, abilityDef;
|
||||
|
||||
if (typeEffectivenessModifier == UQ_4_12(0.0))
|
||||
return 0;
|
||||
|
||||
holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||||
holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
abilityAtk = GetBattlerAbility(battlerAtk);
|
||||
abilityDef = GetBattlerAbility(battlerDef);
|
||||
holdEffectAtk = GetBattlerHoldEffect(damageCalcData->battlerAtk, TRUE);
|
||||
holdEffectDef = GetBattlerHoldEffect(damageCalcData->battlerDef, TRUE);
|
||||
abilityAtk = GetBattlerAbility(damageCalcData->battlerAtk);
|
||||
abilityDef = GetBattlerAbility(damageCalcData->battlerDef);
|
||||
|
||||
return DoMoveDamageCalcVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor,
|
||||
updateFlags, typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef);
|
||||
return DoMoveDamageCalcVars(damageCalcData, fixedBasePower, typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef);
|
||||
}
|
||||
|
||||
static inline s32 DoFutureSightAttackDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType,
|
||||
bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather,
|
||||
u32 holdEffectDef, u32 abilityDef)
|
||||
static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, u32 holdEffectDef, u32 abilityDef)
|
||||
{
|
||||
s32 dmg;
|
||||
u32 userFinalAttack;
|
||||
u32 targetFinalDefense;
|
||||
u32 battlerAtk = damageCalcData->battlerAtk;
|
||||
u32 battlerDef = damageCalcData->battlerDef;
|
||||
u32 move = damageCalcData->move;
|
||||
u32 moveType = damageCalcData->moveType;
|
||||
|
||||
struct Pokemon *party = GetSideParty(GetBattlerSide(battlerAtk));
|
||||
struct Pokemon *partyMon = &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]];
|
||||
@ -10269,12 +10317,12 @@ static inline s32 DoFutureSightAttackDamageCalcVars(u32 move, u32 battlerAtk, u3
|
||||
else
|
||||
userFinalAttack = GetMonData(partyMon, MON_DATA_SPATK, NULL);
|
||||
|
||||
targetFinalDefense = CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags, ABILITY_NONE, abilityDef, holdEffectDef, weather);
|
||||
targetFinalDefense = CalcDefenseStat(damageCalcData, ABILITY_NONE, abilityDef, holdEffectDef, weather);
|
||||
dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, partyMonLevel, targetFinalDefense);
|
||||
|
||||
DAMAGE_APPLY_MODIFIER(GetCriticalModifier(isCrit));
|
||||
DAMAGE_APPLY_MODIFIER(GetCriticalModifier(damageCalcData->isCrit));
|
||||
|
||||
if (randomFactor)
|
||||
if (damageCalcData->randomFactor)
|
||||
{
|
||||
dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO);
|
||||
dmg /= 100;
|
||||
@ -10295,19 +10343,17 @@ static inline s32 DoFutureSightAttackDamageCalcVars(u32 move, u32 battlerAtk, u3
|
||||
return dmg;
|
||||
}
|
||||
|
||||
static inline s32 DoFutureSightAttackDamageCalc(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType,
|
||||
bool32 isCrit, bool32 randomFactor, bool32 updateFlags, uq4_12_t typeEffectivenessModifier, u32 weather)
|
||||
static inline s32 DoFutureSightAttackDamageCalc(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 weather)
|
||||
{
|
||||
u32 holdEffectDef, abilityDef;
|
||||
|
||||
if (typeEffectivenessModifier == UQ_4_12(0.0))
|
||||
return 0;
|
||||
|
||||
holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE);
|
||||
abilityDef = GetBattlerAbility(battlerDef);
|
||||
holdEffectDef = GetBattlerHoldEffect(damageCalcData->battlerDef, TRUE);
|
||||
abilityDef = GetBattlerAbility(damageCalcData->battlerDef);
|
||||
|
||||
return DoFutureSightAttackDamageCalcVars(move, battlerAtk, battlerDef, moveType, isCrit, randomFactor,
|
||||
updateFlags, typeEffectivenessModifier, weather, holdEffectDef, abilityDef);
|
||||
return DoFutureSightAttackDamageCalcVars(damageCalcData, typeEffectivenessModifier, weather, holdEffectDef, abilityDef);
|
||||
}
|
||||
|
||||
#undef DAMAGE_APPLY_MODIFIER
|
||||
@ -10320,31 +10366,37 @@ static u32 GetWeather(void)
|
||||
return gBattleWeather;
|
||||
}
|
||||
|
||||
s32 CalculateMoveDamage(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags)
|
||||
static inline bool32 IsFutureSightAttackerInParty(struct DamageCalculationData *damageCalcData)
|
||||
{
|
||||
struct Pokemon *party = GetSideParty(GetBattlerSide(gBattlerAttacker));
|
||||
if (gMovesInfo[damageCalcData->move].effect != EFFECT_FUTURE_SIGHT)
|
||||
return FALSE;
|
||||
|
||||
if (gMovesInfo[move].effect == EFFECT_FUTURE_SIGHT
|
||||
&& (&party[gWishFutureKnock.futureSightPartyIndex[battlerDef]] != &party[gBattlerPartyIndexes[battlerAtk]]) )
|
||||
{
|
||||
return DoFutureSightAttackDamageCalc(move, battlerAtk, battlerDef, moveType, isCrit, randomFactor,
|
||||
updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), updateFlags),
|
||||
GetWeather());
|
||||
}
|
||||
else
|
||||
{
|
||||
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor,
|
||||
updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, GetBattlerAbility(battlerDef), updateFlags),
|
||||
GetWeather());
|
||||
}
|
||||
struct Pokemon *party = GetSideParty(GetBattlerSide(gBattlerAttacker));
|
||||
return &party[gWishFutureKnock.futureSightPartyIndex[damageCalcData->battlerDef]]
|
||||
!= &party[gBattlerPartyIndexes[damageCalcData->battlerAtk]];
|
||||
}
|
||||
|
||||
s32 CalculateMoveDamage(struct DamageCalculationData *damageCalcData, u32 fixedBasePower)
|
||||
{
|
||||
u32 typeEffectivenessMultiplier = CalcTypeEffectivenessMultiplier(damageCalcData->move,
|
||||
damageCalcData->moveType,
|
||||
damageCalcData->battlerAtk,
|
||||
damageCalcData->battlerDef,
|
||||
GetBattlerAbility(damageCalcData->battlerDef),
|
||||
damageCalcData->updateFlags);
|
||||
|
||||
if (IsFutureSightAttackerInParty(damageCalcData))
|
||||
return DoFutureSightAttackDamageCalc(damageCalcData, typeEffectivenessMultiplier, GetWeather());
|
||||
|
||||
return DoMoveDamageCalc(damageCalcData, fixedBasePower, typeEffectivenessMultiplier, GetWeather());
|
||||
}
|
||||
|
||||
// for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once
|
||||
s32 CalculateMoveDamageVars(u32 move, u32 battlerAtk, u32 battlerDef, u32 moveType, s32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, bool32 isCrit, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef)
|
||||
s32 CalculateMoveDamageVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier,
|
||||
u32 weather, u32 holdEffectAtk, u32 holdEffectDef, u32 abilityAtk, u32 abilityDef)
|
||||
{
|
||||
return DoMoveDamageCalcVars(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, FALSE, FALSE,
|
||||
typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef);
|
||||
return DoMoveDamageCalcVars(damageCalcData, fixedBasePower, typeEffectivenessModifier, weather,
|
||||
holdEffectAtk, holdEffectDef, abilityAtk, abilityDef);
|
||||
}
|
||||
|
||||
static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defType, u32 battlerAtk, bool32 recordAbilities)
|
||||
|
||||
@ -464,6 +464,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would
|
||||
ASSUME(gSpeciesInfo[SPECIES_PELIPPER].types[1] == TYPE_FLYING);
|
||||
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].type == TYPE_GROUND);
|
||||
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
|
||||
PLAYER(SPECIES_ELECTRODE) { Moves(MOVE_THUNDERBOLT, MOVE_THUNDER_WAVE, MOVE_THUNDER_SHOCK); }
|
||||
OPPONENT(SPECIES_PELIPPER) { Moves(MOVE_EARTHQUAKE); };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user