diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index 87c0873e..95d89474 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -134,7 +134,8 @@ function calcs.takenHitFromDamage(rawDamage, damageType, actor) local takenFlat = output[damageConvertedType.."takenFlat"] if convertPercent > 0 or takenFlat ~= 0 then local convertedDamage = rawDamage * convertPercent / 100 - local reducedDamage = round(m_max(convertedDamage * damageMitigationMultiplierForType(convertedDamage, damageConvertedType) + takenFlat, 0) * output[damageConvertedType .."AfterReductionTakenHitMulti"]) * (1 - output["VaalArcticArmourMitigation"]) + local vaalArctic = m_min(-modDB:Sum("MORE", nil, "VaalArcticArmourMitigation") / 100, 1) + local reducedDamage = round(m_max(convertedDamage * damageMitigationMultiplierForType(convertedDamage, damageConvertedType) + takenFlat, 0) * output[damageConvertedType .."AfterReductionTakenHitMulti"]) * (1 - vaalArctic) receivedDamageSum = receivedDamageSum + reducedDamage damages[damageConvertedType] = (reducedDamage > 0 or convertPercent > 0) and reducedDamage or nil end @@ -169,7 +170,7 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) alliesTakenBeforeYou["frostShield"] = { remaining = output.FrostShieldLife, percent = output.FrostShieldDamageMitigation / 100 } end if output.TotalSpectreLife then - alliesTakenBeforeYou["specters"] = { remaining = output.TotalSpectreLife, percent = output.SpectreAllyDamageMitigation / 100 } + alliesTakenBeforeYou["spectres"] = { remaining = output.TotalSpectreLife, percent = output.SpectreAllyDamageMitigation / 100 } end if output.TotalTotemLife then alliesTakenBeforeYou["totems"] = { remaining = output.TotalTotemLife, percent = output.TotemAllyDamageMitigation / 100 } @@ -214,121 +215,141 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) local LifeLossLostOverTime = poolTbl.LifeLossLostOverTime or 0 local LifeBelowHalfLossLostOverTime = poolTbl.LifeBelowHalfLossLostOverTime or 0 local overkillDamage = 0 + local resourcesLostToTypeDamage = { Physical = { }, Lightning = { }, Cold = { }, Fire = { }, Chaos = { } } local MoMPoolRemaining = m_huge local esPoolRemaining = m_huge - for damageType, damage in pairs(damageTable) do - local damageRemainder = damage - for _, allyValues in pairs(alliesTakenBeforeYou) do - if not allyValues.damageType or allyValues.damageType == damageType then - if allyValues.remaining > 0 then - local tempDamage = m_min(damageRemainder * allyValues.percent, allyValues.remaining) - allyValues.remaining = m_floor(allyValues.remaining - tempDamage) - damageRemainder = damageRemainder - tempDamage + for _, damageType in ipairs(dmgTypeList) do + local damageRemainder = damageTable[damageType] + if damageRemainder then + for ally, allyValues in pairs(alliesTakenBeforeYou) do + if not allyValues.damageType or allyValues.damageType == damageType then + if allyValues.remaining > 0 then + local tempDamage = m_min(damageRemainder * allyValues.percent, allyValues.remaining) + allyValues.remaining = m_floor(allyValues.remaining - tempDamage) + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType][ally] = tempDamage >= 1 and tempDamage or nil + end end end - end - -- frost shield / soul link / other taken before you does not count as you taking damage - damageTakenThatCanBeRecouped[damageType] = (damageTakenThatCanBeRecouped[damageType] or 0) + damageRemainder - if aegis[damageType] > 0 then - local tempDamage = m_min(damageRemainder, aegis[damageType]) - aegis[damageType] = aegis[damageType] - tempDamage - damageRemainder = damageRemainder - tempDamage - end - if isElemental[damageType] and aegis.sharedElemental > 0 then - local tempDamage = m_min(damageRemainder, aegis.sharedElemental) - aegis.sharedElemental = aegis.sharedElemental - tempDamage - damageRemainder = damageRemainder - tempDamage - end - if aegis.shared > 0 then - local tempDamage = m_min(damageRemainder, aegis.shared) - aegis.shared = aegis.shared - tempDamage - damageRemainder = damageRemainder - tempDamage - end - if guard[damageType] > 0 then - local tempDamage = m_min(damageRemainder * output[damageType.."GuardAbsorbRate"] / 100, guard[damageType]) - guard[damageType] = m_floor(guard[damageType] - tempDamage) - damageRemainder = damageRemainder - tempDamage - end - if guard.shared > 0 then - local tempDamage = m_min(damageRemainder * output.sharedGuardAbsorbRate / 100, guard.shared) - guard.shared = m_floor(guard.shared - tempDamage) - damageRemainder = damageRemainder - tempDamage - end - if ward > 0 then - local tempDamage = m_min(damageRemainder * (1 - (modDB:Sum("BASE", nil, "WardBypass") or 0) / 100), ward) - ward = ward - tempDamage - damageRemainder = damageRemainder - tempDamage - end - local esBypass = output[damageType.."EnergyShieldBypass"] / 100 or 0 - local lifeHitPool = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented) - local MoMEffect = m_min(output.sharedMindOverMatter + output[damageType.."MindOverMatter"], 100) / 100 - local MoMPool = MoMEffect < 1 and m_min(lifeHitPool / (1 - MoMEffect) - lifeHitPool, mana) or mana - local lifePlusMoMHitPool = lifeHitPool + MoMPool - if energyShield > 0 and not modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 then - local esPool = esBypass > 0 and m_min(lifePlusMoMHitPool / esBypass - lifePlusMoMHitPool, energyShield) or energyShield - local tempDamage = m_min(damageRemainder * (1 - esBypass), esPool) - esPoolRemaining = m_min(esPoolRemaining, esPool - tempDamage) - energyShield = energyShield - tempDamage - damageRemainder = damageRemainder - tempDamage - elseif esBypass == 1 then - esPoolRemaining = 0 - end - if MoMEffect > 0 and mana > 0 then - local MoMDamage = damageRemainder * MoMEffect - if modDB:Flag(nil, "EnergyShieldProtectsMana") and energyShield > 0 and esBypass < 1 then - local MoMEBPool = esBypass > 0 and m_min(MoMPool / esBypass - MoMPool, energyShield) or energyShield - local tempDamage = m_min(MoMDamage * (1 - esBypass), MoMEBPool) - esPoolRemaining = m_min(esPoolRemaining, MoMEBPool - tempDamage) + -- frost shield / soul link / other taken before you does not count as you taking damage + damageTakenThatCanBeRecouped[damageType] = (damageTakenThatCanBeRecouped[damageType] or 0) + damageRemainder + if aegis[damageType] > 0 then + local tempDamage = m_min(damageRemainder, aegis[damageType]) + aegis[damageType] = aegis[damageType] - tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].aegis = tempDamage >= 1 and tempDamage or nil + end + if isElemental[damageType] and aegis.sharedElemental > 0 then + local tempDamage = m_min(damageRemainder, aegis.sharedElemental) + aegis.sharedElemental = aegis.sharedElemental - tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].sharedElementalAegis = tempDamage >= 1 and tempDamage or nil + end + if aegis.shared > 0 then + local tempDamage = m_min(damageRemainder, aegis.shared) + aegis.shared = aegis.shared - tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].sharedAegis = tempDamage >= 1 and tempDamage or nil + end + if guard[damageType] > 0 then + local tempDamage = m_min(damageRemainder * output[damageType.."GuardAbsorbRate"] / 100, guard[damageType]) + guard[damageType] = m_floor(guard[damageType] - tempDamage) + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].guard = tempDamage >= 1 and tempDamage or nil + end + if guard.shared > 0 then + local tempDamage = m_min(damageRemainder * output.sharedGuardAbsorbRate / 100, guard.shared) + guard.shared = m_floor(guard.shared - tempDamage) + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].sharedGuard = tempDamage >= 1 and tempDamage or nil + end + if ward > 0 then + local tempDamage = m_min(damageRemainder * (1 - (modDB:Sum("BASE", nil, "WardBypass") or 0) / 100), ward) + ward = ward - tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].ward = tempDamage >= 1 and tempDamage or nil + end + local esBypass = output[damageType.."EnergyShieldBypass"] / 100 or 0 + local lifeHitPool = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented) + local MoMEffect = m_min(output.sharedMindOverMatter + output[damageType.."MindOverMatter"], 100) / 100 + local MoMPool = MoMEffect < 1 and m_min(lifeHitPool / (1 - MoMEffect) - lifeHitPool, mana) or mana + local lifePlusMoMHitPool = lifeHitPool + MoMPool + if energyShield > 0 and not modDB:Flag(nil, "EnergyShieldProtectsMana") and esBypass < 1 then + local esPool = esBypass > 0 and m_min(lifePlusMoMHitPool / esBypass - lifePlusMoMHitPool, energyShield) or energyShield + local tempDamage = m_min(damageRemainder * (1 - esBypass), esPool) + esPoolRemaining = m_min(esPoolRemaining, esPool - tempDamage) energyShield = energyShield - tempDamage damageRemainder = damageRemainder - tempDamage - MoMDamage = MoMDamage - tempDamage + resourcesLostToTypeDamage[damageType].energyShield = tempDamage >= 1 and tempDamage or nil + elseif esBypass == 1 then + esPoolRemaining = 0 end - local tempDamage = m_min(MoMDamage, MoMPool) - MoMPoolRemaining = m_min(MoMPoolRemaining, MoMPool - tempDamage) - mana = mana - tempDamage - damageRemainder = damageRemainder - tempDamage - else - MoMPoolRemaining = 0 - end - if output.preventedLifeLossTotal > 0 then - local halfLife = output.Life * 0.5 - local lifeOverHalfLife = m_max(life - halfLife, 0) - local preventPercent = output.preventedLifeLoss / 100 - local poolAboveLow = lifeOverHalfLife / (1 - preventPercent) - local preventBelowHalfPercent = lifeLossBelowHalfPrevented / 100 - local damageThatLifeCanStillTake = poolAboveLow + m_max(m_min(life, halfLife), 0) / (1 - preventBelowHalfPercent) / (1 - output.preventedLifeLoss / 100) - if damageThatLifeCanStillTake < damageRemainder then - overkillDamage = overkillDamage + damageRemainder - damageThatLifeCanStillTake - damageRemainder = damageThatLifeCanStillTake - end - if output.preventedLifeLossBelowHalf ~= 0 then - local damageToSplit = m_min(damageRemainder, poolAboveLow) - local lostLife = damageToSplit * (1 - preventPercent) - local preventedLoss = damageToSplit * preventPercent - damageRemainder = damageRemainder - damageToSplit - LifeLossLostOverTime = LifeLossLostOverTime + preventedLoss - life = life - lostLife - if life <= halfLife then - local unspecificallyLowLifePreventedDamage = damageRemainder * preventPercent - LifeLossLostOverTime = LifeLossLostOverTime + unspecificallyLowLifePreventedDamage - damageRemainder = damageRemainder - unspecificallyLowLifePreventedDamage - local specificallyLowLifePreventedDamage = damageRemainder * preventBelowHalfPercent - LifeBelowHalfLossLostOverTime = LifeBelowHalfLossLostOverTime + specificallyLowLifePreventedDamage - damageRemainder = damageRemainder - specificallyLowLifePreventedDamage + if MoMEffect > 0 and mana > 0 then + local MoMDamage = damageRemainder * MoMEffect + if modDB:Flag(nil, "EnergyShieldProtectsMana") and energyShield > 0 and esBypass < 1 then + local MoMEBPool = esBypass > 0 and m_min(MoMPool / esBypass - MoMPool, energyShield) or energyShield + local tempDamage = m_min(MoMDamage * (1 - esBypass), MoMEBPool) + esPoolRemaining = m_min(esPoolRemaining, MoMEBPool - tempDamage) + energyShield = energyShield - tempDamage + damageRemainder = damageRemainder - tempDamage + MoMDamage = MoMDamage - tempDamage + resourcesLostToTypeDamage[damageType].energyShield = tempDamage >= 1 and tempDamage or nil end - else - local tempDamage = damageRemainder * output.preventedLifeLoss / 100 - LifeLossLostOverTime = LifeLossLostOverTime + tempDamage + local tempDamage = m_min(MoMDamage, MoMPool) + MoMPoolRemaining = m_min(MoMPoolRemaining, MoMPool - tempDamage) + mana = mana - tempDamage damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].mana = tempDamage >= 1 and tempDamage or nil + else + MoMPoolRemaining = 0 end + if output.preventedLifeLossTotal > 0 then + local halfLife = output.Life * 0.5 + local lifeOverHalfLife = m_max(life - halfLife, 0) + local preventPercent = output.preventedLifeLoss / 100 + local poolAboveLow = lifeOverHalfLife / (1 - preventPercent) + local preventBelowHalfPercent = lifeLossBelowHalfPrevented / 100 + local damageThatLifeCanStillTake = poolAboveLow + m_max(m_min(life, halfLife), 0) / (1 - preventBelowHalfPercent) / (1 - output.preventedLifeLoss / 100) + if damageThatLifeCanStillTake < damageRemainder then + overkillDamage = overkillDamage + damageRemainder - damageThatLifeCanStillTake + damageRemainder = damageThatLifeCanStillTake + end + if output.preventedLifeLossBelowHalf ~= 0 then + local damageToSplit = m_min(damageRemainder, poolAboveLow) + local lostLife = damageToSplit * (1 - preventPercent) + local preventedLoss = damageToSplit * preventPercent + damageRemainder = damageRemainder - damageToSplit + LifeLossLostOverTime = LifeLossLostOverTime + preventedLoss + life = life - lostLife + resourcesLostToTypeDamage[damageType].life = lostLife + resourcesLostToTypeDamage[damageType].lifeLossPrevented = preventedLoss + if life <= halfLife then + local unspecificallyLowLifePreventedDamage = damageRemainder * preventPercent + LifeLossLostOverTime = LifeLossLostOverTime + unspecificallyLowLifePreventedDamage + damageRemainder = damageRemainder - unspecificallyLowLifePreventedDamage + local specificallyLowLifePreventedDamage = damageRemainder * preventBelowHalfPercent + LifeBelowHalfLossLostOverTime = LifeBelowHalfLossLostOverTime + specificallyLowLifePreventedDamage + damageRemainder = damageRemainder - specificallyLowLifePreventedDamage + resourcesLostToTypeDamage[damageType].lifeLossPrevented = resourcesLostToTypeDamage[damageType].lifeLossPrevented + unspecificallyLowLifePreventedDamage + specificallyLowLifePreventedDamage + end + resourcesLostToTypeDamage[damageType].lifeLossPrevented = resourcesLostToTypeDamage[damageType].lifeLossPrevented >= 1 and resourcesLostToTypeDamage[damageType].lifeLossPrevented or nil + else + local tempDamage = damageRemainder * output.preventedLifeLoss / 100 + LifeLossLostOverTime = LifeLossLostOverTime + tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].lifeLossPrevented = tempDamage >= 1 and tempDamage or nil + end + end + if life > 0 then + local tempDamage = m_min(damageRemainder, life) + life = life - tempDamage + damageRemainder = damageRemainder - tempDamage + resourcesLostToTypeDamage[damageType].life = (resourcesLostToTypeDamage[damageType].life or 0) + (tempDamage > 0 and tempDamage or 0) + end + overkillDamage = overkillDamage + damageRemainder + resourcesLostToTypeDamage[damageType].overkill = damageRemainder >= 1 and damageRemainder or nil end - if life > 0 then - local tempDamage = m_min(damageRemainder, life) - life = life - tempDamage - damageRemainder = damageRemainder - tempDamage - end - overkillDamage = overkillDamage + damageRemainder end local hitPoolRemaining = calcLifeHitPoolWithLossPrevention(life, output.Life, output.preventedLifeLoss, lifeLossBelowHalfPrevented) + (MoMPoolRemaining ~= m_huge and MoMPoolRemaining or 0) + (esPoolRemaining ~= m_huge and esPoolRemaining or 0) @@ -345,10 +366,76 @@ function calcs.reducePoolsByDamage(poolTable, damageTable, actor) LifeLossLostOverTime = m_ceil(LifeLossLostOverTime), LifeBelowHalfLossLostOverTime = m_ceil(LifeBelowHalfLossLostOverTime), OverkillDamage = m_ceil(overkillDamage), - hitPoolRemaining = m_floor(hitPoolRemaining) + hitPoolRemaining = m_floor(hitPoolRemaining), + resourcesLostToTypeDamage = resourcesLostToTypeDamage } end +---Inserts breakdown of resources drained by a hit +---@param breakdownTable table table to insert items to +---@param poolsRemaining table remaining pools after running reducePoolsByDamage on damage to beak down +---@param output table must already contain things like guard, aegis, ally life... +---@return table breakdownTable with drained resource list +local function incomingDamageBreakdown(breakdownTable, poolsRemaining, output) + --region Breakdown inserts + if output.FrostShieldLife and output.FrostShieldLife > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Frost Shield Life ^7(%d remaining)", output.FrostShieldLife - poolsRemaining.AlliesTakenBeforeYou["frostShield"].remaining, poolsRemaining.AlliesTakenBeforeYou["frostShield"].remaining)) + end + if output.TotalSpectreLife and output.TotalSpectreLife > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Spectre Life ^7(%d remaining)", output.TotalSpectreLife - poolsRemaining.AlliesTakenBeforeYou["spectres"].remaining, poolsRemaining.AlliesTakenBeforeYou["spectres"].remaining)) + end + if output.TotalTotemLife and output.TotalTotemLife > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Totem Life ^7(%d remaining)", output.TotalTotemLife - poolsRemaining.AlliesTakenBeforeYou["totems"].remaining, poolsRemaining.AlliesTakenBeforeYou["totems"].remaining)) + end + if output.TotalVaalRejuvenationTotemLife and output.TotalVaalRejuvenationTotemLife > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Vaal Rejuvenation Totem Life ^7(%d remaining)", output.TotalVaalRejuvenationTotemLife - poolsRemaining.AlliesTakenBeforeYou["vaalRejuvenationTotems"].remaining, poolsRemaining.AlliesTakenBeforeYou["vaalRejuvenationTotems"].remaining)) + end + if output.TotalRadianceSentinelLife and output.TotalRadianceSentinelLife > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Sentinel of Radiance Life ^7(%d remaining)", output.TotalRadianceSentinelLife - poolsRemaining.AlliesTakenBeforeYou["radianceSentinel"].remaining, poolsRemaining.AlliesTakenBeforeYou["radianceSentinel"].remaining)) + end + if output.AlliedEnergyShield and output.AlliedEnergyShield > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Allied Energy shield ^7(%d remaining)", output.AlliedEnergyShield - poolsRemaining.AlliesTakenBeforeYou["soulLink"].remaining, poolsRemaining.AlliesTakenBeforeYou["soulLink"].remaining)) + end + for _, damageType in ipairs(dmgTypeList) do + if poolsRemaining.resourcesLostToTypeDamage[damageType].aegis then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."%s Aegis charge ^7(%d remaining)", poolsRemaining.resourcesLostToTypeDamage[damageType].aegis, damageType, poolsRemaining.Aegis[damageType])) + end + end + if output.sharedElementalAegis and output.sharedElementalAegis ~= poolsRemaining.Aegis.sharedElemental then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Elemental Aegis charge ^7(%d remaining)", output.sharedElementalAegis - poolsRemaining.Aegis.sharedElemental, poolsRemaining.Aegis.sharedElemental)) + end + if output.sharedAegis and output.sharedAegis > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Shared Aegis charge ^7(%d remaining)", output.sharedAegis - poolsRemaining.Aegis.shared, poolsRemaining.Aegis.shared)) + end + for _, damageType in ipairs(dmgTypeList) do + if poolsRemaining.resourcesLostToTypeDamage[damageType].guard then + t_insert(breakdownTable, s_format("\n\t%d "..colorCodes.SCOURGE.."%s Guard charge ^7(%d remaining)", poolsRemaining.resourcesLostToTypeDamage[damageType].guard, damageType, poolsRemaining.Guard[damageType])) + end + end + if output.sharedGuardAbsorb and output.sharedGuardAbsorb > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.SCOURGE.."Shared Guard charge ^7(%d remaining)", output.sharedGuardAbsorb - poolsRemaining.Guard.shared, poolsRemaining.Guard.shared)) + end + if output.Ward and output.Ward > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.WARD.."Ward", output.Ward)) + end + if output.EnergyShieldRecoveryCap ~= poolsRemaining.EnergyShield and output.EnergyShieldRecoveryCap and output.EnergyShieldRecoveryCap > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.ES.."Energy Shield ^7(%d remaining)", output.EnergyShieldRecoveryCap - poolsRemaining.EnergyShield, poolsRemaining.EnergyShield)) + end + if output.ManaUnreserved ~= poolsRemaining.Mana and output.ManaUnreserved and output.ManaUnreserved > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.MANA.."Mana ^7(%d remaining)", output.ManaUnreserved - poolsRemaining.Mana, poolsRemaining.Mana)) + end + if poolsRemaining.LifeLossLostOverTime + poolsRemaining.LifeBelowHalfLossLostOverTime > 0 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.LIFE.."Life ^7Loss Prevented", poolsRemaining.LifeLossLostOverTime + poolsRemaining.LifeBelowHalfLossLostOverTime)) + end + t_insert(breakdownTable, s_format("\t%d "..colorCodes.LIFE.."Life ^7(%d remaining)", output.LifeRecoverable - poolsRemaining.Life, poolsRemaining.Life)) + if poolsRemaining.OverkillDamage >= 1 then + t_insert(breakdownTable, s_format("\t%d "..colorCodes.NEGATIVE.."Overkill damage", poolsRemaining.OverkillDamage)) + end + --endregion + + return breakdownTable +end + -- Performs all ingame and related defensive calculations function calcs.defence(env, actor) local modDB = actor.modDB @@ -1905,18 +1992,6 @@ function calcs.buildDefenceEstimations(env, actor) if takenMult ~= 1 or takenFlat ~= 0 or spellSuppressMult ~= 1 or impaleDamage ~= 0 then t_insert(breakdown[damageType.."TakenHitMult"], s_format("= %.3f", output[damageType.."TakenHitMult"])) end - breakdown[damageType.."TakenHit"] = { - s_format("Final %s Damage taken:", damageType), - s_format("%.1f incoming damage", damage), - s_format("x %.3f damage mult", output[damageType.."TakenHitMult"]), - s_format("= %.1f", output[damageType.."TakenHit"]), - } - t_insert(breakdown["totalTakenHit"].rowList, { - type = s_format("%s", damageType), - incoming = s_format("%.1f incoming damage", damage), - mult = s_format("x %.3f damage mult", output[damageType.."TakenHitMult"] ), - value = s_format("%d", output[damageType.."TakenHit"]), - }) if output.AnyTakenReflect then breakdown[damageType.."TakenReflectMult"] = { s_format("Resistance: %.3f", 1 - resist / 100), @@ -2299,7 +2374,7 @@ function calcs.buildDefenceEstimations(env, actor) } end - -- from specters + -- from spectres output["SpectreAllyDamageMitigation"] = modDB:Sum("BASE", nil, "takenFromSpectresBeforeYou") if output["SpectreAllyDamageMitigation"] ~= 0 then output["TotalSpectreLife"] = modDB:Sum("BASE", nil, "TotalSpectreLife") @@ -2411,7 +2486,7 @@ function calcs.buildDefenceEstimations(env, actor) alliesTakenBeforeYou["frostShield"] = { remaining = output.FrostShieldLife, percent = output.FrostShieldDamageMitigation / 100 } end if output.TotalSpectreLife then - alliesTakenBeforeYou["specters"] = { remaining = output.TotalSpectreLife, percent = output.SpectreAllyDamageMitigation / 100 } + alliesTakenBeforeYou["spectres"] = { remaining = output.TotalSpectreLife, percent = output.SpectreAllyDamageMitigation / 100 } end if output.TotalTotemLife then alliesTakenBeforeYou["totems"] = { remaining = output.TotalTotemLife, percent = output.TotemAllyDamageMitigation / 100 } @@ -3103,65 +3178,9 @@ function calcs.buildDefenceEstimations(env, actor) t_insert(breakdown[maxHitCurType], s_format("/ %.2f ^8(modifiers to enemy damage)", enemyDamageMult)) end t_insert(breakdown[maxHitCurType], s_format("= %.0f ^8maximum survivable enemy damage%s", finalMaxHit, useConversionSmoothing and " (approximate)" or "")) - - local poolsRemaining = calcs.reducePoolsByDamage(nil, takenDamages, actor) - - t_insert(breakdown[maxHitCurType], s_format("^8Such a hit would drain the following:")) - if output.FrostShieldLife and output.FrostShieldLife > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Frost Shield Life ^7(%d remaining)", output.FrostShieldLife - poolsRemaining.AlliesTakenBeforeYou["frostShield"].remaining, poolsRemaining.AlliesTakenBeforeYou["frostShield"].remaining)) - end - if output.TotalSpectreLife and output.TotalSpectreLife > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Total Spectre Life ^7(%d remaining)", output.TotalSpectreLife - poolsRemaining.AlliesTakenBeforeYou["specters"].remaining, poolsRemaining.AlliesTakenBeforeYou["specters"].remaining)) - end - if output.TotalTotemLife and output.TotalTotemLife > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Total Totem Life ^7(%d remaining)", output.TotalTotemLife - poolsRemaining.AlliesTakenBeforeYou["totems"].remaining, poolsRemaining.AlliesTakenBeforeYou["totems"].remaining)) - end - if output.TotalVaalRejuvenationTotemLife and output.TotalVaalRejuvenationTotemLife > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Total Vaal Rejuvenation Totem Life ^7(%d remaining)", output.TotalVaalRejuvenationTotemLife - poolsRemaining.AlliesTakenBeforeYou["vaalRejuvenationTotems"].remaining, poolsRemaining.AlliesTakenBeforeYou["vaalRejuvenationTotems"].remaining)) - end - if output.TotalRadianceSentinelLife and output.TotalRadianceSentinelLife > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Total Sentinel of Radiance Life ^7(%d remaining)", output.TotalRadianceSentinelLife - poolsRemaining.AlliesTakenBeforeYou["radianceSentinel"].remaining, poolsRemaining.AlliesTakenBeforeYou["radianceSentinel"].remaining)) - end - if output.AlliedEnergyShield and output.AlliedEnergyShield > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Total Allied Energy shield ^7(%d remaining)", output.AlliedEnergyShield - poolsRemaining.AlliesTakenBeforeYou["soulLink"].remaining, poolsRemaining.AlliesTakenBeforeYou["soulLink"].remaining)) - end - if output.sharedAegis and output.sharedAegis > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Shared Aegis charge ^7(%d remaining)", output.sharedAegis - poolsRemaining.Aegis.shared, poolsRemaining.Aegis.shared)) - end - local receivedElemental = false - for takenType in pairs(takenDamages) do - receivedElemental = receivedElemental or isElemental[takenType] - if output[takenType.."Aegis"] and output[takenType.."Aegis"] > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."%s Aegis charge ^7(%d remaining)", output[takenType.."Aegis"] - poolsRemaining.Aegis[takenType], takenType, poolsRemaining.Aegis[takenType])) - end - end - if receivedElemental and output.sharedElementalAegis and output.sharedElementalAegis > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.GEM.."Elemental Aegis charge ^7(%d remaining)", output.sharedElementalAegis - poolsRemaining.Aegis.sharedElemental, poolsRemaining.Aegis.sharedElemental)) - end - if output.sharedGuardAbsorb and output.sharedGuardAbsorb > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.SCOURGE.."Shared Guard charge ^7(%d remaining)", output.sharedGuardAbsorb - poolsRemaining.Guard.shared, poolsRemaining.Guard.shared)) - end - for takenType in pairs(takenDamages) do - if output[takenType.."GuardAbsorb"] and output[takenType.."GuardAbsorb"] > 0 then - t_insert(breakdown[maxHitCurType], s_format("\n\t%d "..colorCodes.SCOURGE.."%s Guard charge ^7(%d remaining)", output[takenType.."GuardAbsorb"] - poolsRemaining.Guard[takenType], takenType, poolsRemaining.Guard[takenType])) - end - end - if output.Ward and output.Ward > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.WARD.."Ward", output.Ward)) - end - if output.EnergyShieldRecoveryCap ~= poolsRemaining.EnergyShield and output.EnergyShieldRecoveryCap and output.EnergyShieldRecoveryCap > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.ES.."Energy Shield ^7(%d remaining)", output.EnergyShieldRecoveryCap - poolsRemaining.EnergyShield, poolsRemaining.EnergyShield)) - end - if output.ManaUnreserved ~= poolsRemaining.Mana and output.ManaUnreserved and output.ManaUnreserved > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.MANA.."Mana ^7(%d remaining)", output.ManaUnreserved - poolsRemaining.Mana, poolsRemaining.Mana)) - end - if poolsRemaining.LifeLossLostOverTime + poolsRemaining.LifeBelowHalfLossLostOverTime > 0 then - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.LIFE.."Life ^7Loss Prevented", poolsRemaining.LifeLossLostOverTime + poolsRemaining.LifeBelowHalfLossLostOverTime)) - end - t_insert(breakdown[maxHitCurType], s_format("\t%d "..colorCodes.LIFE.."Life ^7(%d remaining)", output.LifeRecoverable - poolsRemaining.Life, poolsRemaining.Life)) - if poolsRemaining.OverkillDamage >= 1 then - t_insert(breakdown[maxHitCurType], s_format("\t%d Overkill damage", poolsRemaining.OverkillDamage)) - end + + t_insert(breakdown[maxHitCurType], "^8Such a hit would drain the following resources:") + breakdown[maxHitCurType] = incomingDamageBreakdown(breakdown[maxHitCurType], calcs.reducePoolsByDamage(nil, takenDamages, actor), output) end end @@ -3595,4 +3614,106 @@ function calcs.buildDefenceEstimations(env, actor) end end end + + --region Add breakdown to enemy incoming damage + if breakdown then + local takenHit = { } + for _, damageType in ipairs(dmgTypeList) do + takenHit[damageType] = output[damageType.."TakenHit"] > 0 and output[damageType.."TakenHit"] or nil + t_insert(breakdown["totalTakenHit"].rowList, { + type = s_format("%s", damageType), + incoming = s_format("%.1f incoming damage", output[damageType.."TakenDamage"]), + mult = s_format("x %.3f damage mult", output[damageType.."TakenHitMult"] ), + value = s_format("%d", m_ceil(output[damageType.."TakenHit"])), + }) + end + local poolsRemaining = calcs.reducePoolsByDamage(nil, takenHit, actor) + + t_insert(breakdown["totalTakenHit"], "Such a hit would drain the following resources:") + breakdown["totalTakenHit"] = incomingDamageBreakdown(breakdown["totalTakenHit"], poolsRemaining, output) + + for _, damageType in ipairs(dmgTypeList) do + local breakdownTable = {} + local resourcesLost = poolsRemaining.resourcesLostToTypeDamage[damageType] + local resourcesLostSum = 0 + + t_insert(breakdownTable, s_format("Final %s Damage taken:", damageType)) + t_insert(breakdownTable, s_format("%.1f incoming damage", output[damageType.."TakenDamage"])) + t_insert(breakdownTable, s_format("x %.3f damage mult", output[damageType.."TakenHitMult"])) + t_insert(breakdownTable, s_format("= %.1f", output[damageType.."TakenHit"])) + + t_insert(breakdownTable, "This part of the hit drains the following resources:") + if resourcesLost.frostShield then + resourcesLostSum = resourcesLostSum + resourcesLost.frostShield + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Frost Shield Life", resourcesLost.frostShield)) + end + if resourcesLost.spectres then + resourcesLostSum = resourcesLostSum + resourcesLost.spectres + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Spectre Life", resourcesLost.spectres)) + end + if resourcesLost.totems then + resourcesLostSum = resourcesLostSum + resourcesLost.totems + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Totem Life", resourcesLost.totems)) + end + if resourcesLost.vaalRejuvenationTotems then + resourcesLostSum = resourcesLostSum + resourcesLost.vaalRejuvenationTotems + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Vaal Rejuvenation Totem Life", resourcesLost.vaalRejuvenationTotems)) + end + if resourcesLost.radianceSentinel then + resourcesLostSum = resourcesLostSum + resourcesLost.radianceSentinel + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Sentinel of Radiance Life", resourcesLost.radianceSentinel)) + end + if resourcesLost.soulLink then + resourcesLostSum = resourcesLostSum + resourcesLost.soulLink + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Total Allied Energy shield", resourcesLost.soulLink)) + end + if resourcesLost.aegis then + resourcesLostSum = resourcesLostSum + resourcesLost.aegis + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."%s Aegis charge", resourcesLost.aegis, damageType)) + end + if resourcesLost.sharedElementalAegis then + resourcesLostSum = resourcesLostSum + resourcesLost.sharedElementalAegis + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Elemental Aegis charge", resourcesLost.sharedElementalAegis)) + end + if resourcesLost.sharedAegis then + resourcesLostSum = resourcesLostSum + resourcesLost.sharedAegis + t_insert(breakdownTable, s_format("\t%d "..colorCodes.GEM.."Shared Aegis charge", resourcesLost.sharedAegis)) + end + if resourcesLost.guard then + resourcesLostSum = resourcesLostSum + resourcesLost.guard + t_insert(breakdownTable, s_format("\n\t%d "..colorCodes.SCOURGE.."%s Guard charge", resourcesLost.guard, damageType)) + end + if resourcesLost.sharedGuard then + resourcesLostSum = resourcesLostSum + resourcesLost.sharedGuard + t_insert(breakdownTable, s_format("\t%d "..colorCodes.SCOURGE.."Shared Guard charge", resourcesLost.sharedGuard)) + end + if resourcesLost.ward then + resourcesLostSum = resourcesLostSum + resourcesLost.ward + t_insert(breakdownTable, s_format("\t%d "..colorCodes.WARD.."Ward", resourcesLost.ward)) + end + if resourcesLost.energyShield then + resourcesLostSum = resourcesLostSum + resourcesLost.energyShield + t_insert(breakdownTable, s_format("\t%d "..colorCodes.ES.."Energy Shield", resourcesLost.energyShield)) + end + if resourcesLost.mana then + resourcesLostSum = resourcesLostSum + resourcesLost.mana + t_insert(breakdownTable, s_format("\t%d "..colorCodes.MANA.."Mana", resourcesLost.mana)) + end + if resourcesLost.lifeLossPrevented and resourcesLost.lifeLossPrevented > 0 then + resourcesLostSum = resourcesLostSum + resourcesLost.lifeLossPrevented + t_insert(breakdownTable, s_format("\t%d "..colorCodes.LIFE.."Life ^7Loss Prevented", resourcesLost.lifeLossPrevented)) + end + if resourcesLost.life and resourcesLost.life > 0 then + resourcesLostSum = resourcesLostSum + resourcesLost.life + t_insert(breakdownTable, s_format("\t%d "..colorCodes.LIFE.."Life", resourcesLost.life)) + end + if resourcesLost.overkill then + resourcesLostSum = resourcesLostSum + resourcesLost.overkill + t_insert(breakdownTable, s_format("\t%d "..colorCodes.NEGATIVE.."Overkill damage", resourcesLost.overkill)) + end + t_insert(breakdownTable, s_format("\t^8(%d total)", resourcesLostSum)) + breakdown[damageType.."TakenHit"] = breakdownTable + end + end + --endregion end