From d5f8e9cc0e34c8248886d155558d7338e8184bb6 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:59:28 +0100 Subject: [PATCH] Combines CalculateMoveDamage arguments into a struct (#5570) --- include/battle_ai_util.h | 7 +- include/battle_util.h | 18 ++- src/battle_ai_main.c | 32 +++-- src/battle_ai_switch_items.c | 15 ++- src/battle_ai_util.c | 63 ++++++---- src/battle_script_commands.c | 22 +++- src/battle_tv.c | 10 +- src/battle_util.c | 212 +++++++++++++++++++++------------- test/battle/ai/ai_switching.c | 1 + 9 files changed, 243 insertions(+), 137 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index a3cd38293b..63991ed980 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -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); diff --git a/include/battle_util.h b/include/battle_util.h index 504c3be3ad..e2dd9ac826 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -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); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a1cf751034..bdd0aef150 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -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; } diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 03f5378a64..5e606b6a38 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -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; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 01adcfffd9..4f52873f9f 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -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 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5424d1b499..805be74e4e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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; } diff --git a/src/battle_tv.c b/src/battle_tv.c index 6134b492c4..ccea3551cd 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -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; diff --git a/src/battle_util.c b/src/battle_util.c index 74c7fd62aa..1e5f809c52 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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) diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 787c5d505d..5610689f49 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -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); };