Refactor damage calculations aruguments by using a struct context (#7108)

This commit is contained in:
Alex 2025-06-13 14:16:40 +02:00 committed by GitHub
parent 75982721cd
commit e1b542944f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 285 additions and 320 deletions

View File

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

View File

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

View File

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

View File

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

View File

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