diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index 21877e1383..6b7d664ee0 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -798,6 +798,20 @@ describe("TestSkills", function() assert.True(build.calcsTab.mainEnv.enemyDB:Sum("BASE", nil, "FireResist") < fireResistWithoutPotentExposure) end) + it("averages inverted elemental resistance after penetration", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.enemyFireResist = 50 + build.configTab.input.customMods = "Hits have 50% chance to treat Enemy Monster Elemental Resistance values as inverted\nDamage Penetrates 50% of Enemy Fire Resistance" + build.configTab:BuildModList() + runCallback("OnFrame") + + assert.are.equals(1.25, build.calcsTab.calcsOutput.FireEffMult) + local breakdownText = table.concat(build.calcsTab.calcsEnv.player.breakdown.FireEffMult, "\n") + assert.truthy(breakdownText:match("inverted hit")) + assert.truthy(breakdownText:match("weighted average")) + end) + it("Test granted skills with exposure stats make exposure configurable", function() build.skillsTab:PasteSocketGroup("Fireball 20/0 1") local spec = build.spec diff --git a/src/Modules/CalcBreakdown.lua b/src/Modules/CalcBreakdown.lua index c2740d5dcd..a2ab6b7738 100644 --- a/src/Modules/CalcBreakdown.lua +++ b/src/Modules/CalcBreakdown.lua @@ -110,20 +110,29 @@ function breakdown.area(base, areaMod, total, incBreakpoint, moreBreakpoint, red return out end -function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sourceRes, useRes, invertChance, minPen) +function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sourceRes, useRes, invertChance, minPen, effectiveResist) local out = { } local resistForm = (damageType == "Physical") and "physical damage reduction" or "resistance" local resistLabel = resistForm - - if invertChance and invertChance ~= 0 then - resistLabel = "average inverted "..resistForm + minPen = minPen or 0 + local calcPenResist = function(resist) + return resist > minPen and m_max(resist - pen, minPen) or resist end + effectiveResist = effectiveResist or calcPenResist(resist) + if sourceRes and sourceRes ~= damageType then t_insert(out, s_format("Enemy %s: %d%% ^8(%s)", resistLabel, resist, sourceRes)) elseif resist ~= 0 then t_insert(out, s_format("Enemy %s: %d%%", resistLabel, resist)) end - if pen ~= 0 or not useRes then + if invertChance and invertChance ~= 0 and useRes then + local normalResist = calcPenResist(resist) + local invertedResist = calcPenResist(-resist) + t_insert(out, "Effective resistance:") + t_insert(out, s_format("%g%% ^8(non-inverted hit after penetration)", normalResist)) + t_insert(out, s_format("%g%% ^8(inverted hit after penetration)", invertedResist)) + t_insert(out, s_format("= %g%% ^8(weighted average from %.0f%% inversion chance)", effectiveResist, invertChance * 100)) + elseif pen ~= 0 or not useRes then t_insert(out, "Effective resistance:") t_insert(out, s_format("%d%% ^8(resistance)", resist)) if pen < 0 then @@ -145,7 +154,7 @@ function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sour if useRes then breakdown.multiChain(out, { label = "Effective DPS modifier:", - { "%.2f ^8(%s)", 1 - (math.max(resist - pen,0)) / 100, resistForm }, + { "%.2f ^8(%s)", 1 - effectiveResist / 100, resistForm }, { "%.2f ^8(increased/reduced damage taken)", 1 + taken / 100 }, { "%.2f ^8(more/less damage taken)", takenMore }, total = s_format("= %.3f", mult), diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index c314de83f0..a750c61c45 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4157,11 +4157,7 @@ function calcs.offence(env, actor, activeSkill) end end - local invertChance = m_max(m_min(skillModList:Sum("CHANCE", cfg, "HitsInvertEleResChance"), 1), 0) - if isElemental[damageType] and invertChance > 0 then - -- resist = (1 - invertChance) * resist + invertChance * (-1 * resist) - resist = resist - 2 * invertChance * resist - end + local invertChance = isElemental[damageType] and m_max(m_min(skillModList:Sum("CHANCE", cfg, "HitsInvertEleResChance"), 1), 0) or 0 sourceRes = env.modDB:Flag(nil, "Enemy"..sourceRes.."ResistEqualToYours") and "Your "..sourceRes.." Resistance" or (env.partyMembers.modDB:Flag(nil, "Enemy"..sourceRes.."ResistEqualToYours") and "Party Member "..sourceRes.." Resistance" or sourceRes) if skillFlags.projectile then takenInc = takenInc + enemyDB:Sum("INC", nil, "ProjectileDamageTaken") @@ -4174,10 +4170,20 @@ function calcs.offence(env, actor, activeSkill) end local effMult = (1 + takenInc / 100) * takenMore local useRes = useThisResist(damageType) + local effectiveResist = resist + local calcPenResist = function(resist) + return resist > minPen and m_max(resist - pen, minPen) or resist + end if skillModList:Flag(cfg, isElemental[damageType] and "CannotElePenIgnore" or nil) then - effMult = effMult * (1 - resist / 100) + effectiveResist = (isElemental[damageType] and invertChance > 0) and (resist - 2 * invertChance * resist) or resist + effMult = effMult * (1 - effectiveResist / 100) elseif useRes then - effMult = effMult * (1 - (resist > minPen and m_max(resist - pen, minPen) or resist) / 100) + if isElemental[damageType] and invertChance > 0 then + effectiveResist = calcPenResist(resist) * (1 - invertChance) + calcPenResist(-resist) * invertChance + else + effectiveResist = calcPenResist(resist) + end + effMult = effMult * (1 - effectiveResist / 100) end damageTypeHitMin = damageTypeHitMin * effMult damageTypeHitMax = damageTypeHitMax * effMult @@ -4185,12 +4191,12 @@ function calcs.offence(env, actor, activeSkill) if env.mode == "CALCS" then output[damageType.."EffMult"] = effMult end - if pass == 2 and breakdown and (effMult ~= 1 or sourceRes ~= damageType) and skillModList:Flag(cfg, isElemental[damageType] and "CannotElePenIgnore" or nil) then + if pass == 2 and breakdown and (effMult ~= 1 or sourceRes ~= damageType or invertChance > 0) and skillModList:Flag(cfg, isElemental[damageType] and "CannotElePenIgnore" or nil) then t_insert(breakdown[damageType], s_format("x %.3f ^8(effective DPS modifier)", effMult)) - breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, 0, takenInc, effMult, takenMore, sourceRes, useRes, invertChance, minPen) - elseif pass == 2 and breakdown and (effMult ~= 1 or (resist - pen) < minPen or sourceRes ~= damageType) then + breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, 0, takenInc, effMult, takenMore, sourceRes, useRes, invertChance, minPen, effectiveResist) + elseif pass == 2 and breakdown and (effMult ~= 1 or (resist - pen) < minPen or sourceRes ~= damageType or invertChance > 0) then t_insert(breakdown[damageType], s_format("x %.3f ^8(effective DPS modifier)", effMult)) - breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, pen, takenInc, effMult, takenMore, sourceRes, useRes, invertChance, minPen) + breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, resist, pen, takenInc, effMult, takenMore, sourceRes, useRes, invertChance, minPen, effectiveResist) end end if pass == 2 and breakdown then