Combines CalculateMoveDamage arguments into a struct (#5570)

This commit is contained in:
Alex 2024-10-31 07:59:28 +01:00 committed by GitHub
parent 13a26c462e
commit d5f8e9cc0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 243 additions and 137 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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); };