From e1b542944fb29fca947c975f363d36190026abfc Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:16:40 +0200 Subject: [PATCH] Refactor damage calculations aruguments by using a struct context (#7108) --- include/battle_util.h | 20 +- src/battle_ai_util.c | 92 +++----- src/battle_script_commands.c | 34 +-- src/battle_tv.c | 19 +- src/battle_util.c | 440 ++++++++++++++++------------------- 5 files changed, 285 insertions(+), 320 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index 53de080665..71f468996d 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -159,7 +159,7 @@ enum { extern const struct TypePower gNaturalGiftTable[]; -struct DamageCalculationData +struct DamageContext { u32 battlerAtk:3; u32 battlerDef:3; @@ -168,9 +168,16 @@ struct DamageCalculationData u32 isCrit:1; u32 randomFactor:1; u32 updateFlags:1; - u32 padding:2; + u32 padding1:2; + u32 weather:16; + u32 fixedBasePower:8; + u32 padding2:8; + uq4_12_t typeEffectivenessModifier; + u32 abilityAtk:16; + u32 abilityDef:16; + enum ItemHoldEffect holdEffectAtk:16; + enum ItemHoldEffect holdEffectDef:16; }; -STATIC_ASSERT(sizeof(struct DamageCalculationData) <= 4, StructExceedsFourBytes); enum SleepClauseBlock { @@ -272,10 +279,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(struct DamageCalculationData *damageCalcData, u32 fixedBasePower); -s32 CalculateMoveDamageVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, - u32 weather, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef, u32 abilityAtk, u32 abilityDef); -s32 ApplyModifiersAfterDmgRoll(s32 dmg, struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 abilityAtk, u32 abilityDef, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef); +s32 CalculateMoveDamage(struct DamageContext *ctx); +s32 CalculateMoveDamageVars(struct DamageContext *ctx); +s32 ApplyModifiersAfterDmgRoll(struct DamageContext *ctx, s32 dmg); 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_util.c b/src/battle_ai_util.c index d84a7b6343..31807e74ab 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -616,10 +616,9 @@ static inline void AI_RestoreBattlerTypes(u32 battlerAtk, u32 *types) gBattleMons[battlerAtk].types[2] = types[2]; } -static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCalcData, u16 *medianDamage, u16 *minimumDamage, u16 *maximumDamage, enum ItemHoldEffect holdEffectAtk, u32 abilityAtk) +static inline void CalcDynamicMoveDamage(struct DamageContext *ctx, u16 *medianDamage, u16 *minimumDamage, u16 *maximumDamage) { - u32 move = damageCalcData->move; - enum BattleMoveEffects effect = GetMoveEffect(move); + enum BattleMoveEffects effect = GetMoveEffect(ctx->move); u16 median = *medianDamage; u16 minimum = *minimumDamage; u16 maximum = *maximumDamage; @@ -627,19 +626,19 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal switch (effect) { case EFFECT_MULTI_HIT: - if (move == MOVE_WATER_SHURIKEN && gBattleMons[damageCalcData->battlerAtk].species == SPECIES_GRENINJA_ASH) + if (ctx->move == MOVE_WATER_SHURIKEN && gBattleMons[ctx->battlerAtk].species == SPECIES_GRENINJA_ASH) { median *= 3; minimum *= 3; maximum *= 3; } - else if (abilityAtk == ABILITY_SKILL_LINK) + else if (ctx->abilityAtk == ABILITY_SKILL_LINK) { median *= 5; minimum *= 5; maximum *= 5; } - else if (holdEffectAtk == HOLD_EFFECT_LOADED_DICE) + else if (ctx->holdEffectAtk == HOLD_EFFECT_LOADED_DICE) { median *= 9; median /= 2; @@ -655,18 +654,19 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal break; case EFFECT_ENDEAVOR: // If target has less HP than user, Endeavor does no damage - median = maximum = minimum = max(0, gBattleMons[damageCalcData->battlerDef].hp - gBattleMons[damageCalcData->battlerAtk].hp); + median = maximum = minimum = max(0, gBattleMons[ctx->battlerDef].hp - gBattleMons[ctx->battlerAtk].hp); break; case EFFECT_BEAT_UP: if (B_BEAT_UP >= GEN_5) { - u32 partyCount = CalculatePartyCount(GetBattlerParty(damageCalcData->battlerAtk)); + u32 partyCount = CalculatePartyCount(GetBattlerParty(ctx->battlerAtk)); u32 i; gBattleStruct->beatUpSlot = 0; - damageCalcData->isCrit = FALSE; + ctx->isCrit = FALSE; + ctx->fixedBasePower = 0; median = 0; for (i = 0; i < partyCount; i++) - median += CalculateMoveDamage(damageCalcData, 0); + median += CalculateMoveDamage(ctx); maximum = minimum = median; gBattleStruct->beatUpSlot = 0; } @@ -676,7 +676,7 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal } // Handle other multi-strike moves - u32 strikeCount = GetMoveStrikeCount(move); + u32 strikeCount = GetMoveStrikeCount(ctx->move); if (strikeCount > 1 && effect != EFFECT_TRIPLE_KICK) { median *= strikeCount; @@ -684,11 +684,11 @@ static inline void CalcDynamicMoveDamage(struct DamageCalculationData *damageCal maximum *= strikeCount; } - if (abilityAtk == ABILITY_PARENTAL_BOND + if (ctx->abilityAtk == ABILITY_PARENTAL_BOND && !strikeCount && effect != EFFECT_TRIPLE_KICK && effect != EFFECT_MULTI_HIT - && !AI_IsDoubleSpreadMove(damageCalcData->battlerAtk, move)) + && !AI_IsDoubleSpreadMove(ctx->battlerAtk, ctx->move)) { median += median / (B_PARENTAL_BOND_DMG >= GEN_7 ? 4 : 2); minimum += minimum / (B_PARENTAL_BOND_DMG >= GEN_7 ? 4 : 2); @@ -781,14 +781,21 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType); s32 fixedBasePower = SetFixedMoveBasePower(battlerAtk, move); - struct DamageCalculationData damageCalcData; - damageCalcData.battlerAtk = battlerAtk; - damageCalcData.battlerDef = battlerDef; - damageCalcData.move = move; - damageCalcData.moveType = moveType; - damageCalcData.isCrit = ShouldCalcCritDamage(battlerAtk, battlerDef, move, aiData); - damageCalcData.randomFactor = FALSE; - damageCalcData.updateFlags = FALSE; + struct DamageContext ctx; + ctx.battlerAtk = battlerAtk; + ctx.battlerDef = battlerDef; + ctx.move = move; + ctx.moveType = moveType; + ctx.isCrit = ShouldCalcCritDamage(battlerAtk, battlerDef, move, aiData); + ctx.randomFactor = FALSE; + ctx.updateFlags = FALSE; + ctx.weather = weather; + ctx.fixedBasePower = fixedBasePower; + ctx.typeEffectivenessModifier = effectivenessMultiplier; + ctx.abilityAtk = aiData->abilities[battlerAtk]; + ctx.abilityDef = aiData->abilities[battlerDef]; + ctx.holdEffectAtk = aiData->holdEffects[battlerAtk]; + ctx.holdEffectDef = aiData->holdEffects[battlerDef]; if (moveEffect == EFFECT_TRIPLE_KICK) { @@ -796,59 +803,34 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u { s32 damageByRollType = 0; - s32 oneTripleKickHit = CalculateMoveDamageVars(&damageCalcData, fixedBasePower, - effectivenessMultiplier, weather, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + s32 oneTripleKickHit = CalculateMoveDamageVars(&ctx); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_LOWEST); - simDamage.minimum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.minimum += ApplyModifiersAfterDmgRoll(&ctx, damageByRollType); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_DEFAULT); - simDamage.median += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.median += ApplyModifiersAfterDmgRoll(&ctx, damageByRollType); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_HIGHEST); - simDamage.maximum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.maximum += ApplyModifiersAfterDmgRoll(&ctx, damageByRollType); } } else { - u32 damage = CalculateMoveDamageVars(&damageCalcData, fixedBasePower, - effectivenessMultiplier, weather, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + u32 damage = CalculateMoveDamageVars(&ctx); simDamage.minimum = GetDamageByRollType(damage, DMG_ROLL_LOWEST); - simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.minimum = ApplyModifiersAfterDmgRoll(&ctx, simDamage.minimum); simDamage.median = GetDamageByRollType(damage, DMG_ROLL_DEFAULT); - simDamage.median = ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.median = ApplyModifiersAfterDmgRoll(&ctx, simDamage.median); simDamage.maximum = GetDamageByRollType(damage, DMG_ROLL_HIGHEST); - simDamage.maximum = ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + simDamage.maximum = ApplyModifiersAfterDmgRoll(&ctx, simDamage.maximum); } if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE) - { - CalcDynamicMoveDamage(&damageCalcData, - &simDamage.median, - &simDamage.minimum, - &simDamage.maximum, - aiData->holdEffects[battlerAtk], - aiData->abilities[battlerAtk]); - } + CalcDynamicMoveDamage(&ctx, &simDamage.median, &simDamage.minimum, &simDamage.maximum); AI_RestoreBattlerTypes(battlerAtk, types); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a371a076ea..048c4d13de 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2063,18 +2063,18 @@ static void Cmd_critcalc(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static inline void CalculateAndSetMoveDamage(struct DamageCalculationData *damageCalcData, u32 battlerDef) +static inline void CalculateAndSetMoveDamage(struct DamageContext *ctx) { - SetDynamicMoveCategory(gBattlerAttacker, battlerDef, gCurrentMove); - damageCalcData->battlerDef = battlerDef; - damageCalcData->isCrit = gSpecialStatuses[battlerDef].criticalHit; - gBattleStruct->moveDamage[battlerDef] = CalculateMoveDamage(damageCalcData, 0); + SetDynamicMoveCategory(gBattlerAttacker, ctx->battlerDef, gCurrentMove); + ctx->isCrit = gSpecialStatuses[ctx->battlerDef].criticalHit; + ctx->fixedBasePower = 0; + gBattleStruct->moveDamage[ctx->battlerDef] = CalculateMoveDamage(ctx); // Slighly hacky but we need to check move result flags for distortion match-up as well but it can only be done after damage calcs - if (gSpecialStatuses[battlerDef].distortedTypeMatchups && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) + if (gSpecialStatuses[ctx->battlerDef].distortedTypeMatchups && gBattleStruct->moveResultFlags[ctx->battlerDef] & MOVE_RESULT_NO_EFFECT) { - gSpecialStatuses[battlerDef].distortedTypeMatchups = FALSE; - gSpecialStatuses[battlerDef].teraShellAbilityDone = FALSE; + gSpecialStatuses[ctx->battlerDef].distortedTypeMatchups = FALSE; + gSpecialStatuses[ctx->battlerDef].teraShellAbilityDone = FALSE; } } @@ -2090,12 +2090,12 @@ static void Cmd_damagecalc(void) u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); - struct DamageCalculationData damageCalcData; - damageCalcData.battlerAtk = gBattlerAttacker; - damageCalcData.move = gCurrentMove; - damageCalcData.moveType = GetBattleMoveType(gCurrentMove); - damageCalcData.randomFactor = TRUE; - damageCalcData.updateFlags = TRUE; + struct DamageContext ctx; + ctx.battlerAtk = gBattlerAttacker; + ctx.move = gCurrentMove; + ctx.moveType = GetBattleMoveType(gCurrentMove); + ctx.randomFactor = TRUE; + ctx.updateFlags = TRUE; if (IsSpreadMove(moveTarget)) { @@ -2107,12 +2107,14 @@ static void Cmd_damagecalc(void) || gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT) continue; - CalculateAndSetMoveDamage(&damageCalcData, battlerDef); + ctx.battlerDef = battlerDef; + CalculateAndSetMoveDamage(&ctx); } } else { - CalculateAndSetMoveDamage(&damageCalcData, gBattlerTarget); + ctx.battlerDef = gBattlerTarget; + CalculateAndSetMoveDamage(&ctx); } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_tv.c b/src/battle_tv.c index e7c182e537..4215d3ead3 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -1261,15 +1261,16 @@ static void TrySetBattleSeminarShow(void) powerOverride = 0; if (ShouldCalculateDamage(gCurrentMove, &dmgByMove[i], &powerOverride)) { - struct DamageCalculationData damageCalcData; - damageCalcData.battlerAtk = gBattlerAttacker; - damageCalcData.battlerDef = gBattlerTarget; - damageCalcData.move = gCurrentMove; - damageCalcData.moveType = GetMoveType(gCurrentMove); - damageCalcData.isCrit = FALSE; - damageCalcData.randomFactor = FALSE; - damageCalcData.updateFlags = FALSE; - gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&damageCalcData, powerOverride); + struct DamageContext ctx; + ctx.battlerAtk = gBattlerAttacker; + ctx.battlerDef = gBattlerTarget; + ctx.move = gCurrentMove; + ctx.moveType = GetMoveType(gCurrentMove); + ctx.isCrit = FALSE; + ctx.randomFactor = FALSE; + ctx.updateFlags = FALSE; + ctx.fixedBasePower = powerOverride; + gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&ctx); dmgByMove[i] = gBattleStruct->moveDamage[gBattlerTarget]; if (dmgByMove[i] == 0 && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) dmgByMove[i] = 1; diff --git a/src/battle_util.c b/src/battle_util.c index 9bd2e80017..b18288763f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1969,14 +1969,15 @@ static void CancellerObedience(u32 *effect) break; case DISOBEYS_HITS_SELF: gBattlerTarget = gBattlerAttacker; - 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; - gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&damageCalcData, 40); + struct DamageContext ctx; + ctx.battlerAtk = ctx.battlerDef = gBattlerAttacker; + ctx.move = MOVE_NONE; + ctx.moveType = TYPE_MYSTERY; + ctx.isCrit = FALSE; + ctx.randomFactor = FALSE; + ctx.updateFlags = TRUE; + ctx.fixedBasePower = 40; + gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx); gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gHitMarker |= HITMARKER_OBEYS; @@ -2145,14 +2146,15 @@ static void CancellerConfused(u32 *effect) { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; - 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; - gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&damageCalcData, 40); + struct DamageContext ctx; + ctx.battlerAtk = ctx.battlerDef = gBattlerAttacker; + ctx.move = MOVE_NONE; + ctx.moveType = TYPE_MYSTERY; + ctx.isCrit = FALSE; + ctx.randomFactor = FALSE; + ctx.updateFlags = TRUE; + ctx.fixedBasePower = 40; + gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx); gProtectStructs[gBattlerAttacker].confusionSelfDmg = TRUE; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; @@ -7776,11 +7778,11 @@ u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc) return count; } -u32 GetMoveTargetCount(struct DamageCalculationData *damageCalcData) +u32 GetMoveTargetCount(struct DamageContext *ctx) { - u32 battlerAtk = damageCalcData->battlerAtk; - u32 battlerDef = damageCalcData->battlerDef; - u32 move = damageCalcData->move; + u32 battlerAtk = ctx->battlerAtk; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; switch (GetBattlerMoveTargetType(battlerAtk, move)) { @@ -7919,11 +7921,11 @@ u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter) return basePower; } -static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData, u32 abilityDef, u32 weather) +static inline u32 CalcMoveBasePower(struct DamageContext *ctx) { - u32 battlerAtk = damageCalcData->battlerAtk; - u32 battlerDef = damageCalcData->battlerDef; - u32 move = damageCalcData->move; + u32 battlerAtk = ctx->battlerAtk; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; u32 i; u32 basePower = GetMovePower(move); @@ -7988,7 +7990,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData basePower *= 2; break; case EFFECT_WEATHER_BALL: - if (weather & B_WEATHER_ANY) + if (ctx->weather & B_WEATHER_ANY) basePower *= 2; break; case EFFECT_PURSUIT: @@ -8000,7 +8002,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData break; case EFFECT_DOUBLE_POWER_ON_ARG_STATUS: // Comatose targets treated as if asleep - if ((gBattleMons[battlerDef].status1 | (STATUS1_SLEEP * (abilityDef == ABILITY_COMATOSE))) & GetMoveEffectArg_Status(move) + if ((gBattleMons[battlerDef].status1 | (STATUS1_SLEEP * (ctx->abilityDef == ABILITY_COMATOSE))) & GetMoveEffectArg_Status(move) && !((GetMoveAdditionalEffectById(move, 0)->moveEffect == MOVE_EFFECT_REMOVE_STATUS) && DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) basePower *= 2; break; @@ -8185,14 +8187,14 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData return basePower; } -static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, enum ItemHoldEffect holdEffectAtk, u32 weather) +static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) { u32 holdEffectParamAtk; - u32 basePower = CalcMoveBasePower(damageCalcData, defAbility, weather); - u32 battlerAtk = damageCalcData->battlerAtk; - u32 battlerDef = damageCalcData->battlerDef; - u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; + u32 basePower = CalcMoveBasePower(ctx); + u32 battlerAtk = ctx->battlerAtk; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; + u32 moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); uq4_12_t holdEffectModifier; @@ -8263,7 +8265,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * modifier = uq4_12_multiply(modifier, UQ_4_12(B_SPORT_DMG_REDUCTION >= GEN_5 ? 0.33 : 0.5)); // attacker's abilities - switch (atkAbility) + switch (ctx->abilityAtk) { case ABILITY_TECHNICIAN: if (basePower <= 60) @@ -8291,7 +8293,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * break; case ABILITY_SAND_FORCE: if ((moveType == TYPE_STEEL || moveType == TYPE_ROCK || moveType == TYPE_GROUND) - && weather & B_WEATHER_SANDSTORM) + && ctx->weather & B_WEATHER_SANDSTORM) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); break; case ABILITY_RIVALRY: @@ -8391,15 +8393,15 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * } // target's abilities - switch (defAbility) + switch (ctx->abilityDef) { case ABILITY_HEATPROOF: case ABILITY_WATER_BUBBLE: if (moveType == TYPE_FIRE) { modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); - if (damageCalcData->updateFlags) - RecordAbilityBattle(battlerDef, defAbility); + if (ctx->updateFlags) + RecordAbilityBattle(battlerDef, ctx->abilityDef); } break; case ABILITY_DRY_SKIN: @@ -8409,7 +8411,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); - if (((weather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battlerDef].boosterEnergyActivates) + if (((ctx->weather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battlerDef].boosterEnergyActivates) && ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF)) && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); @@ -8433,7 +8435,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * holdEffectModifier = uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(holdEffectParamAtk)); // attacker's hold effect - switch (holdEffectAtk) + switch (ctx->holdEffectAtk) { case HOLD_EFFECT_MUSCLE_BAND: if (IsBattleMovePhysical(move)) @@ -8496,16 +8498,16 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData * return uq4_12_multiply_by_int_half_down(modifier, basePower); } -static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, enum ItemHoldEffect holdEffectAtk, u32 weather) +static inline u32 CalcAttackStat(struct DamageContext *ctx) { 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; + u32 battlerAtk = ctx->battlerAtk; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; + u32 moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); atkBaseSpeciesId = GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species); @@ -8555,10 +8557,10 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u } // critical hits ignore attack stat's stage drops - if (damageCalcData->isCrit && atkStage < DEFAULT_STAT_STAGE) + if (ctx->isCrit && atkStage < DEFAULT_STAT_STAGE) atkStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore attack stat changes while taking damage - if (defAbility == ABILITY_UNAWARE) + if (ctx->abilityDef == ABILITY_UNAWARE) atkStage = DEFAULT_STAT_STAGE; atkStat *= gStatStageRatios[atkStage][0]; @@ -8568,7 +8570,7 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u modifier = UQ_4_12(1.0); // attacker's abilities - switch (atkAbility) + switch (ctx->abilityAtk) { case ABILITY_HUGE_POWER: case ABILITY_PURE_POWER: @@ -8666,7 +8668,7 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u if (!(gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED)) { u32 atkHighestStat = GetHighestStatId(battlerAtk); - if (((weather & B_WEATHER_SUN) && HasWeatherEffect()) || gDisableStructs[battlerAtk].boosterEnergyActivates) + if (((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect()) || gDisableStructs[battlerAtk].boosterEnergyActivates) { if ((IsBattleMovePhysical(move) && atkHighestStat == STAT_ATK) || (IsBattleMoveSpecial(move) && atkHighestStat == STAT_SPATK)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); @@ -8685,7 +8687,7 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u } break; case ABILITY_ORICHALCUM_PULSE: - if ((weather & B_WEATHER_SUN) && HasWeatherEffect() && IsBattleMovePhysical(move)) + if ((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect() && IsBattleMovePhysical(move)) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3333)); break; case ABILITY_HADRON_ENGINE: @@ -8695,13 +8697,13 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u } // target's abilities - switch (defAbility) + switch (ctx->abilityDef) { case ABILITY_THICK_FAT: if (moveType == TYPE_FIRE || moveType == TYPE_ICE) { modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); - if (damageCalcData->updateFlags) + if (ctx->updateFlags) RecordAbilityBattle(battlerDef, ABILITY_THICK_FAT); } break; @@ -8720,14 +8722,14 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u } // field abilities - if (IsAbilityOnField(ABILITY_VESSEL_OF_RUIN) && atkAbility != ABILITY_VESSEL_OF_RUIN && IsBattleMoveSpecial(move)) + if (IsAbilityOnField(ABILITY_VESSEL_OF_RUIN) && ctx->abilityAtk != ABILITY_VESSEL_OF_RUIN && IsBattleMoveSpecial(move)) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.75)); - if (IsAbilityOnField(ABILITY_TABLETS_OF_RUIN) && atkAbility != ABILITY_TABLETS_OF_RUIN && IsBattleMovePhysical(move)) + if (IsAbilityOnField(ABILITY_TABLETS_OF_RUIN) && ctx->abilityAtk != ABILITY_TABLETS_OF_RUIN && IsBattleMovePhysical(move)) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.75)); // attacker's hold effect - switch (holdEffectAtk) + switch (ctx->holdEffectAtk) { case HOLD_EFFECT_THICK_CLUB: if ((atkBaseSpeciesId == SPECIES_CUBONE || atkBaseSpeciesId == SPECIES_MAROWAK) && IsBattleMovePhysical(move)) @@ -8779,15 +8781,15 @@ static bool32 CanEvolve(u32 species) return FALSE; } -static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, u32 atkAbility, u32 defAbility, enum ItemHoldEffect holdEffectDef, u32 weather) +static inline u32 CalcDefenseStat(struct DamageContext *ctx) { bool32 usesDefStat; u8 defStage; u32 defStat, def, spDef; uq4_12_t modifier; - u32 battlerDef = damageCalcData->battlerDef; - u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; + u32 moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped @@ -8820,10 +8822,10 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, defStat /= 2; // critical hits ignore positive stat changes - if (damageCalcData->isCrit && defStage > DEFAULT_STAT_STAGE) + if (ctx->isCrit && defStage > DEFAULT_STAT_STAGE) defStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore defense stat changes while dealing damage - if (atkAbility == ABILITY_UNAWARE) + if (ctx->abilityAtk == ABILITY_UNAWARE) defStage = DEFAULT_STAT_STAGE; // certain moves also ignore stat changes if (MoveIgnoresDefenseEvasionStages(move)) @@ -8836,13 +8838,13 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, modifier = UQ_4_12(1.0); // target's abilities - switch (defAbility) + switch (ctx->abilityDef) { case ABILITY_MARVEL_SCALE: if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat) { modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); - if (damageCalcData->updateFlags) + if (ctx->updateFlags) RecordAbilityBattle(battlerDef, ABILITY_MARVEL_SCALE); } break; @@ -8850,7 +8852,7 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, if (usesDefStat) { modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); - if (damageCalcData->updateFlags) + if (ctx->updateFlags) RecordAbilityBattle(battlerDef, ABILITY_FUR_COAT); } break; @@ -8858,7 +8860,7 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat) { modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); - if (damageCalcData->updateFlags) + if (ctx->updateFlags) RecordAbilityBattle(battlerDef, ABILITY_GRASS_PELT); } break; @@ -8885,14 +8887,14 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, } // field abilities - if (IsAbilityOnField(ABILITY_SWORD_OF_RUIN) && defAbility != ABILITY_SWORD_OF_RUIN && usesDefStat) + if (IsAbilityOnField(ABILITY_SWORD_OF_RUIN) && ctx->abilityDef != ABILITY_SWORD_OF_RUIN && usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.75)); - if (IsAbilityOnField(ABILITY_BEADS_OF_RUIN) && defAbility != ABILITY_BEADS_OF_RUIN && !usesDefStat) + if (IsAbilityOnField(ABILITY_BEADS_OF_RUIN) && ctx->abilityDef != ABILITY_BEADS_OF_RUIN && !usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.75)); // target's hold effects - switch (holdEffectDef) + switch (ctx->holdEffectDef) { case HOLD_EFFECT_DEEP_SEA_SCALE: if (gBattleMons[battlerDef].species == SPECIES_CLAMPERL && !usesDefStat) @@ -8943,9 +8945,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(struct DamageCalculationData *damageCalcData) +static inline uq4_12_t GetTargetDamageModifier(struct DamageContext *ctx) { - if (IsDoubleBattle() && GetMoveTargetCount(damageCalcData) >= 2) + if (IsDoubleBattle() && GetMoveTargetCount(ctx) >= 2) return B_MULTIPLE_TARGETS_DMG >= GEN_4 ? UQ_4_12(0.75) : UQ_4_12(0.5); return UQ_4_12(1.0); } @@ -8957,62 +8959,53 @@ 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(struct DamageCalculationData *damageCalcData, u32 abilityAtk) +static inline uq4_12_t GetSameTypeAttackBonusModifier(struct DamageContext *ctx) { - u32 battlerAtk = damageCalcData->battlerAtk; - u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; - - if (moveType == TYPE_MYSTERY) + if (ctx->moveType == TYPE_MYSTERY) return UQ_4_12(1.0); - else if (gBattleStruct->pledgeMove && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), moveType)) - return (abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); - else if (!IS_BATTLER_OF_TYPE(battlerAtk, moveType) || move == MOVE_STRUGGLE || move == MOVE_NONE) + else if (gBattleStruct->pledgeMove && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(ctx->battlerAtk), ctx->moveType)) + return (ctx->abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); + else if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, ctx->moveType) || ctx->move == MOVE_STRUGGLE || ctx->move == MOVE_NONE) return UQ_4_12(1.0); - return (abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); + return (ctx->abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); } // Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. -static uq4_12_t GetWeatherDamageModifier(struct DamageCalculationData *damageCalcData, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef, u32 weather) +static uq4_12_t GetWeatherDamageModifier(struct DamageContext *ctx) { - u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; - - if (weather == B_WEATHER_NONE) + if (ctx->weather == B_WEATHER_NONE) return UQ_4_12(1.0); - if (GetMoveEffect(move) == EFFECT_HYDRO_STEAM && (weather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) + if (GetMoveEffect(ctx->move) == EFFECT_HYDRO_STEAM && (ctx->weather & B_WEATHER_SUN) && ctx->holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) return UQ_4_12(1.5); - if (holdEffectDef == HOLD_EFFECT_UTILITY_UMBRELLA) + if (ctx->holdEffectDef == HOLD_EFFECT_UTILITY_UMBRELLA) return UQ_4_12(1.0); - if (weather & B_WEATHER_RAIN) + if (ctx->weather & B_WEATHER_RAIN) { - if (moveType != TYPE_FIRE && moveType != TYPE_WATER) + if (ctx->moveType != TYPE_FIRE && ctx->moveType != TYPE_WATER) return UQ_4_12(1.0); - return (moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); + return (ctx->moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); } - if (weather & B_WEATHER_SUN) + if (ctx->weather & B_WEATHER_SUN) { - if (moveType != TYPE_FIRE && moveType != TYPE_WATER) + if (ctx->moveType != TYPE_FIRE && ctx->moveType != TYPE_WATER) return UQ_4_12(1.0); - return (moveType == TYPE_WATER) ? UQ_4_12(0.5) : UQ_4_12(1.5); + return (ctx->moveType == TYPE_WATER) ? UQ_4_12(0.5) : UQ_4_12(1.5); } return UQ_4_12(1.0); } -static inline uq4_12_t GetBurnOrFrostBiteModifier(struct DamageCalculationData *damageCalcData, u32 abilityAtk) +static inline uq4_12_t GetBurnOrFrostBiteModifier(struct DamageContext *ctx) { - u32 battlerAtk = damageCalcData->battlerAtk; - u32 move = damageCalcData->move; - enum BattleMoveEffects moveEffect = GetMoveEffect(move); + enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->move); - if (gBattleMons[battlerAtk].status1 & STATUS1_BURN - && IsBattleMovePhysical(move) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_BURN + && IsBattleMovePhysical(ctx->move) && (B_BURN_FACADE_DMG < GEN_6 || moveEffect != EFFECT_FACADE) - && abilityAtk != ABILITY_GUTS) + && ctx->abilityAtk != ABILITY_GUTS) return UQ_4_12(0.5); - if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE - && IsBattleMoveSpecial(move) + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE + && IsBattleMoveSpecial(ctx->move) && (B_BURN_FACADE_DMG < GEN_6 || moveEffect != EFFECT_FACADE)) return UQ_4_12(0.5); return UQ_4_12(1.0); @@ -9032,12 +9025,12 @@ static inline uq4_12_t GetGlaiveRushModifier(u32 battlerDef) return UQ_4_12(1.0); } -static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(struct DamageCalculationData *damageCalcData) +static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(struct DamageContext *ctx) { - if (!IsZMove(damageCalcData->move) && !IsMaxMove(damageCalcData->move)) + if (!IsZMove(ctx->move) && !IsMaxMove(ctx->move)) return UQ_4_12(1.0); - u32 protected = gProtectStructs[damageCalcData->battlerDef].protected; + u32 protected = gProtectStructs[ctx->battlerDef].protected; if (GetProtectType(protected) == PROTECT_TYPE_SINGLE && protected != PROTECT_MAX_GUARD) return UQ_4_12(0.25); return UQ_4_12(1.0); @@ -9071,14 +9064,14 @@ static inline uq4_12_t GetAirborneModifier(u32 move, u32 battlerDef) return UQ_4_12(1.0); } -static inline uq4_12_t GetScreensModifier(u32 move, u32 battlerAtk, u32 battlerDef, bool32 isCrit, u32 abilityAtk) +static inline uq4_12_t GetScreensModifier(struct DamageContext *ctx) { - u32 sideStatus = gSideStatuses[GetBattlerSide(battlerDef)]; - bool32 lightScreen = (sideStatus & SIDE_STATUS_LIGHTSCREEN) && IsBattleMoveSpecial(move); - bool32 reflect = (sideStatus & SIDE_STATUS_REFLECT) && IsBattleMovePhysical(move); + u32 sideStatus = gSideStatuses[GetBattlerSide(ctx->battlerDef)]; + bool32 lightScreen = (sideStatus & SIDE_STATUS_LIGHTSCREEN) && IsBattleMoveSpecial(ctx->move); + bool32 reflect = (sideStatus & SIDE_STATUS_REFLECT) && IsBattleMovePhysical(ctx->move); bool32 auroraVeil = sideStatus & SIDE_STATUS_AURORA_VEIL; - if (isCrit || abilityAtk == ABILITY_INFILTRATOR || gProtectStructs[battlerAtk].confusionSelfDmg) + if (ctx->isCrit || ctx->abilityAtk == ABILITY_INFILTRATOR || gProtectStructs[ctx->battlerAtk].confusionSelfDmg) return UQ_4_12(1.0); if (reflect || lightScreen || auroraVeil) return (IsDoubleBattle()) ? UQ_4_12(0.667) : UQ_4_12(0.5); @@ -9112,33 +9105,33 @@ static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typ return UQ_4_12(1.0); } -static inline uq4_12_t GetDefenderAbilitiesModifier(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t typeEffectivenessModifier, u32 abilityDef) +static inline uq4_12_t GetDefenderAbilitiesModifier(struct DamageContext *ctx) { - switch (abilityDef) + switch (ctx->abilityDef) { case ABILITY_MULTISCALE: case ABILITY_SHADOW_SHIELD: - if (IsBattlerAtMaxHp(battlerDef)) + if (IsBattlerAtMaxHp(ctx->battlerDef)) return UQ_4_12(0.5); break; case ABILITY_FILTER: case ABILITY_SOLID_ROCK: case ABILITY_PRISM_ARMOR: - if (typeEffectivenessModifier >= UQ_4_12(2.0)) + if (ctx->typeEffectivenessModifier >= UQ_4_12(2.0)) return UQ_4_12(0.75); break; case ABILITY_FLUFFY: - if (!IsMoveMakingContact(move, battlerAtk) && moveType == TYPE_FIRE) + if (!IsMoveMakingContact(ctx->move, ctx->battlerAtk) && ctx->moveType == TYPE_FIRE) return UQ_4_12(2.0); - if (IsMoveMakingContact(move, battlerAtk) && moveType != TYPE_FIRE) + if (IsMoveMakingContact(ctx->move, ctx->battlerAtk) && ctx->moveType != TYPE_FIRE) return UQ_4_12(0.5); break; case ABILITY_PUNK_ROCK: - if (IsSoundMove(move)) + if (IsSoundMove(ctx->move)) return UQ_4_12(0.5); break; case ABILITY_ICE_SCALES: - if (IsBattleMoveSpecial(move)) + if (IsBattleMoveSpecial(ctx->move)) return UQ_4_12(0.5); break; } @@ -9185,24 +9178,18 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff return UQ_4_12(1.0); } -static inline uq4_12_t GetDefenderItemsModifier(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 abilityDef, enum ItemHoldEffect holdEffectDef) +static inline uq4_12_t GetDefenderItemsModifier(struct DamageContext *ctx) { - u32 battlerDef = damageCalcData->battlerDef; - u32 moveType = damageCalcData->moveType; - - u32 holdEffectDefParam = GetBattlerHoldEffectParam(battlerDef); - u32 itemDef = gBattleMons[battlerDef].item; - - switch (holdEffectDef) + switch (ctx->holdEffectDef) { case HOLD_EFFECT_RESIST_BERRY: - if (UnnerveOn(battlerDef, itemDef)) + if (UnnerveOn(ctx->battlerDef, gBattleMons[ctx->battlerDef].item)) return UQ_4_12(1.0); - if (moveType == holdEffectDefParam && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0))) + if (ctx->moveType == GetBattlerHoldEffectParam(ctx->battlerDef) && (ctx->moveType == TYPE_NORMAL || ctx->typeEffectivenessModifier >= UQ_4_12(2.0))) { - if (damageCalcData->updateFlags) - gSpecialStatuses[battlerDef].berryReduced = TRUE; - return (abilityDef == ABILITY_RIPEN) ? UQ_4_12(0.25) : UQ_4_12(0.5); + if (ctx->updateFlags) + gSpecialStatuses[ctx->battlerDef].berryReduced = TRUE; + return (ctx->abilityDef == ABILITY_RIPEN) ? UQ_4_12(0.25) : UQ_4_12(0.5); } break; default: @@ -9221,42 +9208,36 @@ static inline uq4_12_t GetDefenderItemsModifier(struct DamageCalculationData *da // 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(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, - u32 abilityAtk, u32 abilityDef, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef) +static inline uq4_12_t GetOtherModifiers(struct DamageContext *ctx) { - 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; - u32 unmodifiedDefenderSpeed = gBattleMons[battlerDef].speed; + u32 battlerDefPartner = BATTLE_PARTNER(ctx->battlerDef); + u32 unmodifiedAttackerSpeed = gBattleMons[ctx->battlerAtk].speed; + u32 unmodifiedDefenderSpeed = gBattleMons[ctx->battlerDef].speed; + //TODO: Behemoth Blade, Behemoth Bash, Dynamax Cannon (Dynamax) - DAMAGE_MULTIPLY_MODIFIER(GetMinimizeModifier(move, battlerDef)); - DAMAGE_MULTIPLY_MODIFIER(GetUndergroundModifier(move, battlerDef)); - DAMAGE_MULTIPLY_MODIFIER(GetDiveModifier(move, battlerDef)); - DAMAGE_MULTIPLY_MODIFIER(GetAirborneModifier(move, battlerDef)); - DAMAGE_MULTIPLY_MODIFIER(GetScreensModifier(move, battlerAtk, battlerDef, isCrit, abilityAtk)); - DAMAGE_MULTIPLY_MODIFIER(GetCollisionCourseElectroDriftModifier(move, typeEffectivenessModifier)); + DAMAGE_MULTIPLY_MODIFIER(GetMinimizeModifier(ctx->move, ctx->battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetUndergroundModifier(ctx->move, ctx->battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetDiveModifier(ctx->move, ctx->battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetAirborneModifier(ctx->move, ctx->battlerDef)); + DAMAGE_MULTIPLY_MODIFIER(GetScreensModifier(ctx)); + DAMAGE_MULTIPLY_MODIFIER(GetCollisionCourseElectroDriftModifier(ctx->move, ctx->typeEffectivenessModifier)); if (unmodifiedAttackerSpeed >= unmodifiedDefenderSpeed) { - DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit, abilityAtk)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(ctx->battlerAtk, ctx->typeEffectivenessModifier, ctx->isCrit, ctx->abilityAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(ctx)); DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(damageCalcData, typeEffectivenessModifier, abilityDef, holdEffectDef)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(ctx->battlerAtk, ctx->typeEffectivenessModifier, ctx->holdEffectAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(ctx)); } else { - DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(move, moveType, battlerAtk, battlerDef, typeEffectivenessModifier, abilityDef)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderAbilitiesModifier(ctx)); DAMAGE_MULTIPLY_MODIFIER(GetDefenderPartnerAbilitiesModifier(battlerDefPartner)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(battlerAtk, typeEffectivenessModifier, isCrit, abilityAtk)); - DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(damageCalcData, typeEffectivenessModifier, abilityDef, holdEffectDef)); - DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(battlerAtk, typeEffectivenessModifier, holdEffectAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerAbilitiesModifier(ctx->battlerAtk, ctx->typeEffectivenessModifier, ctx->isCrit, ctx->abilityAtk)); + DAMAGE_MULTIPLY_MODIFIER(GetDefenderItemsModifier(ctx)); + DAMAGE_MULTIPLY_MODIFIER(GetAttackerItemsModifier(ctx->battlerAtk, ctx->typeEffectivenessModifier, ctx->holdEffectAtk)); } return finalModifier; } @@ -9267,31 +9248,28 @@ static inline uq4_12_t GetOtherModifiers(struct DamageCalculationData *damageCal dmg = uq4_12_multiply_by_int_half_down(modifier, dmg); \ } while (0) -static inline s32 DoMoveDamageCalcVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, u32 weather, - enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef, u32 abilityAtk, u32 abilityDef) +static inline s32 DoMoveDamageCalcVars(struct DamageContext *ctx) { s32 dmg; u32 userFinalAttack; u32 targetFinalDefense; - u32 battlerAtk = damageCalcData->battlerAtk; - u32 battlerDef = damageCalcData->battlerDef; - if (fixedBasePower) - gBattleMovePower = fixedBasePower; + if (ctx->fixedBasePower) + gBattleMovePower = ctx->fixedBasePower; else - gBattleMovePower = CalcMoveBasePowerAfterModifiers(damageCalcData, abilityAtk, abilityDef, holdEffectAtk, weather); + gBattleMovePower = CalcMoveBasePowerAfterModifiers(ctx); - userFinalAttack = CalcAttackStat(damageCalcData, abilityAtk, abilityDef, holdEffectAtk, weather); - targetFinalDefense = CalcDefenseStat(damageCalcData, abilityAtk, abilityDef, holdEffectDef, weather); + userFinalAttack = CalcAttackStat(ctx); + targetFinalDefense = CalcDefenseStat(ctx); - dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, gBattleMons[battlerAtk].level, targetFinalDefense); - DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(damageCalcData)); - DAMAGE_APPLY_MODIFIER(GetParentalBondModifier(battlerAtk)); - DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(damageCalcData, holdEffectAtk, holdEffectDef, weather)); - DAMAGE_APPLY_MODIFIER(GetCriticalModifier(damageCalcData->isCrit)); - DAMAGE_APPLY_MODIFIER(GetGlaiveRushModifier(battlerDef)); + dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, gBattleMons[ctx->battlerAtk].level, targetFinalDefense); + DAMAGE_APPLY_MODIFIER(GetTargetDamageModifier(ctx)); + DAMAGE_APPLY_MODIFIER(GetParentalBondModifier(ctx->battlerAtk)); + DAMAGE_APPLY_MODIFIER(GetWeatherDamageModifier(ctx)); + DAMAGE_APPLY_MODIFIER(GetCriticalModifier(ctx->isCrit)); + DAMAGE_APPLY_MODIFIER(GetGlaiveRushModifier(ctx->battlerDef)); - if (damageCalcData->randomFactor) + if (ctx->randomFactor) { dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO); dmg /= 100; @@ -9303,55 +9281,55 @@ static inline s32 DoMoveDamageCalcVars(struct DamageCalculationData *damageCalcD return dmg; } - dmg = ApplyModifiersAfterDmgRoll(dmg, damageCalcData, typeEffectivenessModifier, abilityAtk, abilityDef, holdEffectAtk, holdEffectDef); + dmg = ApplyModifiersAfterDmgRoll(ctx, dmg); if (dmg == 0) dmg = 1; return dmg; } -s32 ApplyModifiersAfterDmgRoll(s32 dmg, struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 abilityAtk, u32 abilityDef, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef) +s32 ApplyModifiersAfterDmgRoll(struct DamageContext *ctx, s32 dmg) { - if (GetActiveGimmick(damageCalcData->battlerAtk) == GIMMICK_TERA) - DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(damageCalcData->battlerAtk, damageCalcData->moveType)); + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_TERA) + DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(ctx->battlerAtk, ctx->moveType)); else - DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(damageCalcData, abilityAtk)); - DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); - DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(damageCalcData, abilityAtk)); - DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(damageCalcData)); - DAMAGE_APPLY_MODIFIER(GetOtherModifiers(damageCalcData, typeEffectivenessModifier, abilityAtk, abilityDef, holdEffectAtk, holdEffectDef)); + DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(ctx)); + DAMAGE_APPLY_MODIFIER(ctx->typeEffectivenessModifier); + DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(ctx)); + DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(ctx)); + DAMAGE_APPLY_MODIFIER(GetOtherModifiers(ctx)); return dmg; } -static inline s32 DoFixedDamageMoveCalc(struct DamageCalculationData *damageCalcData) +static inline s32 DoFixedDamageMoveCalc(struct DamageContext *ctx) { s32 dmg = 0; s32 randDamage; - switch (GetMoveEffect(damageCalcData->move)) + switch (GetMoveEffect(ctx->move)) { case EFFECT_LEVEL_DAMAGE: - dmg = gBattleMons[damageCalcData->battlerAtk].level; + dmg = gBattleMons[ctx->battlerAtk].level; break; case EFFECT_PSYWAVE: randDamage = B_PSYWAVE_DMG >= GEN_6 ? (Random() % 101) : ((Random() % 11) * 10); - dmg = gBattleMons[damageCalcData->battlerAtk].level * (randDamage + 50) / 100; + dmg = gBattleMons[ctx->battlerAtk].level * (randDamage + 50) / 100; break; case EFFECT_FIXED_HP_DAMAGE: - dmg = GetMoveFixedHPDamage(damageCalcData->move); + dmg = GetMoveFixedHPDamage(ctx->move); break; case EFFECT_FIXED_PERCENT_DAMAGE: - dmg = GetNonDynamaxHP(damageCalcData->battlerDef) * GetMoveDamagePercentage(damageCalcData->move) / 100; + dmg = GetNonDynamaxHP(ctx->battlerDef) * GetMoveDamagePercentage(ctx->move) / 100; break; case EFFECT_FINAL_GAMBIT: - dmg = GetNonDynamaxHP(damageCalcData->battlerAtk); + dmg = GetNonDynamaxHP(ctx->battlerAtk); break; default: return INT32_MAX; } - gBattleStruct->moveResultFlags[damageCalcData->battlerDef] &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE); + gBattleStruct->moveResultFlags[ctx->battlerDef] &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE); if (dmg == 0) dmg = 1; @@ -9359,36 +9337,32 @@ static inline s32 DoFixedDamageMoveCalc(struct DamageCalculationData *damageCalc return dmg; } -static inline s32 DoMoveDamageCalc(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, u32 weather) +static inline s32 DoMoveDamageCalc(struct DamageContext *ctx) { - enum ItemHoldEffect holdEffectAtk, holdEffectDef; - u32 abilityAtk, abilityDef; - - if (typeEffectivenessModifier == UQ_4_12(0.0)) + if (ctx->typeEffectivenessModifier == UQ_4_12(0.0)) return 0; - s32 dmg = DoFixedDamageMoveCalc(damageCalcData); + s32 dmg = DoFixedDamageMoveCalc(ctx); if (dmg != INT32_MAX) return dmg; - holdEffectAtk = GetBattlerHoldEffect(damageCalcData->battlerAtk, TRUE); - holdEffectDef = GetBattlerHoldEffect(damageCalcData->battlerDef, TRUE); - abilityAtk = GetBattlerAbility(damageCalcData->battlerAtk); - abilityDef = GetBattlerAbility(damageCalcData->battlerDef); + ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk, TRUE); + ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef, TRUE); + ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk); + ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); - return DoMoveDamageCalcVars(damageCalcData, fixedBasePower, typeEffectivenessModifier, weather, holdEffectAtk, holdEffectDef, abilityAtk, abilityDef); + return DoMoveDamageCalcVars(ctx); } -static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, - u32 weather, enum ItemHoldEffect holdEffectDef, u32 abilityDef) +static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageContext *ctx) { s32 dmg; u32 userFinalAttack; u32 targetFinalDefense; - u32 battlerAtk = damageCalcData->battlerAtk; - u32 battlerDef = damageCalcData->battlerDef; - u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; + u32 battlerAtk = ctx->battlerAtk; + u32 battlerDef = ctx->battlerDef; + u32 move = ctx->move; + u32 moveType = ctx->moveType; struct Pokemon *party = GetBattlerParty(battlerAtk); struct Pokemon *partyMon = &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]]; @@ -9401,12 +9375,12 @@ static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData else userFinalAttack = GetMonData(partyMon, MON_DATA_SPATK, NULL); - targetFinalDefense = CalcDefenseStat(damageCalcData, ABILITY_NONE, abilityDef, holdEffectDef, weather); + targetFinalDefense = CalcDefenseStat(ctx); dmg = CalculateBaseDamage(gBattleMovePower, userFinalAttack, partyMonLevel, targetFinalDefense); - DAMAGE_APPLY_MODIFIER(GetCriticalModifier(damageCalcData->isCrit)); + DAMAGE_APPLY_MODIFIER(GetCriticalModifier(ctx->isCrit)); - if (damageCalcData->randomFactor) + if (ctx->randomFactor) { dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO); dmg /= 100; @@ -9417,7 +9391,7 @@ static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData DAMAGE_APPLY_MODIFIER(UQ_4_12(1.5)); else DAMAGE_APPLY_MODIFIER(UQ_4_12(1.0)); - DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); + DAMAGE_APPLY_MODIFIER(ctx->typeEffectivenessModifier); if (dmg == 0) dmg = 1; @@ -9425,18 +9399,12 @@ static inline s32 DoFutureSightAttackDamageCalcVars(struct DamageCalculationData return dmg; } -static inline s32 DoFutureSightAttackDamageCalc(struct DamageCalculationData *damageCalcData, uq4_12_t typeEffectivenessModifier, u32 weather) +static inline s32 DoFutureSightAttackDamageCalc(struct DamageContext *ctx) { - enum ItemHoldEffect holdEffectDef; - u32 abilityDef; - - if (typeEffectivenessModifier == UQ_4_12(0.0)) + if (ctx->typeEffectivenessModifier == UQ_4_12(0.0)) return 0; - holdEffectDef = GetBattlerHoldEffect(damageCalcData->battlerDef, TRUE); - abilityDef = GetBattlerAbility(damageCalcData->battlerDef); - - return DoFutureSightAttackDamageCalcVars(damageCalcData, typeEffectivenessModifier, weather, holdEffectDef, abilityDef); + return DoFutureSightAttackDamageCalcVars(ctx); } #undef DAMAGE_APPLY_MODIFIER @@ -9459,31 +9427,36 @@ bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, u32 move) && &party[gWishFutureKnock.futureSightPartyIndex[battlerDef]] != &party[BATTLE_PARTNER(gBattlerPartyIndexes[battlerAtk])]; } -s32 CalculateMoveDamage(struct DamageCalculationData *damageCalcData, u32 fixedBasePower) +s32 CalculateMoveDamage(struct DamageContext *ctx) { - u32 typeEffectivenessMultiplier = CalcTypeEffectivenessMultiplier(damageCalcData->move, - damageCalcData->moveType, - damageCalcData->battlerAtk, - damageCalcData->battlerDef, - GetBattlerAbility(damageCalcData->battlerDef), - damageCalcData->updateFlags); + ctx->weather = GetWeather(); + ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk); + ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); + ctx->holdEffectAtk = GetItemHoldEffect(ctx->battlerAtk); + ctx->holdEffectDef = GetItemHoldEffect(ctx->battlerDef); - if (IsFutureSightAttackerInParty(damageCalcData->battlerAtk, damageCalcData->battlerDef, damageCalcData->move)) - return DoFutureSightAttackDamageCalc(damageCalcData, typeEffectivenessMultiplier, GetWeather()); + ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier( + ctx->move, + ctx->moveType, + ctx->battlerAtk, + ctx->battlerDef, + ctx->abilityDef, + ctx->updateFlags); - return DoMoveDamageCalc(damageCalcData, fixedBasePower, typeEffectivenessMultiplier, GetWeather()); + if (IsFutureSightAttackerInParty(ctx->battlerAtk, ctx->battlerDef, ctx->move)) + return DoFutureSightAttackDamageCalc(ctx); + + return DoMoveDamageCalc(ctx); } // for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once -s32 CalculateMoveDamageVars(struct DamageCalculationData *damageCalcData, u32 fixedBasePower, uq4_12_t typeEffectivenessModifier, - u32 weather, enum ItemHoldEffect holdEffectAtk, enum ItemHoldEffect holdEffectDef, u32 abilityAtk, u32 abilityDef) +s32 CalculateMoveDamageVars(struct DamageContext *ctx) { - s32 dmg = DoFixedDamageMoveCalc(damageCalcData); + s32 dmg = DoFixedDamageMoveCalc(ctx); if (dmg != INT32_MAX) return dmg; - return DoMoveDamageCalcVars(damageCalcData, fixedBasePower, typeEffectivenessModifier, weather, - holdEffectAtk, holdEffectDef, abilityAtk, abilityDef); + return DoMoveDamageCalcVars(ctx); } static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 moveType, u32 battlerDef, u32 defAbility, u32 defType, u32 battlerAtk, bool32 recordAbilities) @@ -10715,6 +10688,7 @@ bool32 PickupHasValidTarget(u32 battler) return FALSE; } +// TODO: Pass down weather as an arg bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) { if (gBattleWeather & weatherFlags && HasWeatherEffect())