diff --git a/spec/System/TestSkills_spec.lua b/spec/System/TestSkills_spec.lua index d8fb3f90a2..a3f75fb8ef 100644 --- a/spec/System/TestSkills_spec.lua +++ b/spec/System/TestSkills_spec.lua @@ -1204,6 +1204,46 @@ describe("TestSkills", function() assert.truthy(breakdownText:match("weighted average")) end) + it("ignores non-negative elemental resistance after inversion", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.enemyFireResist = -50 + build.configTab.input.conditionEnemyFrozen = true + build.configTab.input.customMods = "Hits have 100% chance to treat Enemy Monster Elemental Resistance values as inverted\nHits ignore non-negative Elemental Resistances of Frozen Enemies" + build.configTab:BuildModList() + runCallback("OnFrame") + + assert.are.equals(1, build.calcsTab.calcsOutput.FireEffMult) + end) + + it("inverts the selected lowest elemental resistance", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.enemyFireResist = 50 + build.configTab.input.enemyColdResist = 20 + build.configTab.input.enemyLightningResist = 30 + build.configTab.input.customMods = "Hits have 100% chance to treat Enemy Monster Elemental Resistance values as inverted\nElemental Damage you Deal with Hits is Resisted by Lowest Elemental Resistance instead" + build.configTab:BuildModList() + runCallback("OnFrame") + + assert.are.equals(1.2, build.calcsTab.calcsOutput.FireEffMult) + end) + + it("shows the resistance used for each inversion outcome", function() + build.skillsTab:PasteSocketGroup("Fireball 20/0 1") + build.configTab.input.enemyIsBoss = "None" + build.configTab.input.enemyFireResist = 50 + build.configTab.input.conditionEnemyFrozen = true + build.configTab.input.customMods = "Hits have 50% chance to treat Enemy Monster Elemental Resistance values as inverted\nHits ignore non-negative Elemental Resistances of Frozen Enemies" + 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("0%% ^8%(non%-inverted hit after penetration%)")) + assert.truthy(breakdownText:match("%-50%% ^8%(inverted hit after penetration%)")) + 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 e5b079eb6c..193db6af9a 100644 --- a/src/Modules/CalcBreakdown.lua +++ b/src/Modules/CalcBreakdown.lua @@ -114,7 +114,7 @@ return function(modDB, output, actor) return out end - function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sourceRes, useRes, invertChance, minPen, effectiveResist) + function breakdown.effMult(damageType, resist, pen, taken, mult, takenMore, sourceRes, useRes, invertChance, minPen, effectiveResist, normalHitResist, invertedHitResist) local out = { } local resistForm = (damageType == "Physical") and "physical damage reduction" or "resistance" local resistLabel = resistForm @@ -130,11 +130,11 @@ return function(modDB, output, actor) t_insert(out, s_format("Enemy %s: %d%%", resistLabel, resist)) end if invertChance and invertChance ~= 0 and useRes then - local normalResist = calcPenResist(resist) - local invertedResist = calcPenResist(-resist) + normalHitResist = normalHitResist or calcPenResist(resist) + invertedHitResist = invertedHitResist or 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(non-inverted hit after penetration)", normalHitResist)) + t_insert(out, s_format("%g%% ^8(inverted hit after penetration)", invertedHitResist)) 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:") @@ -263,4 +263,4 @@ return function(modDB, output, actor) end return breakdown -end \ No newline at end of file +end diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 180823177a..8eaee760aa 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -4244,9 +4244,6 @@ function calcs.offence(env, actor, activeSkill) end sourceRes = elementUsed elseif isElemental[damageType] then - if resist > 0 and modDB:Flag(cfg, "IgnoreNonNegativeEleRes") then - resist = 0 - end pen = skillModList:Sum("BASE", cfg, damageType.."Penetration", "ElementalPenetration") minPen = skillModList:Sum("BASE", cfg, damageType.."PenetrationMinimum", "ElementalPenetrationMinimum") takenInc = takenInc + enemyDB:Sum("INC", cfg, "ElementalDamageTaken") @@ -4272,14 +4269,22 @@ function calcs.offence(env, actor, activeSkill) 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 - effectiveResist = (isElemental[damageType] and invertChance > 0) and (resist - 2 * invertChance * resist) or resist - effMult = effMult * (1 - effectiveResist / 100) - elseif useRes then + local cannotElePenIgnore = isElemental[damageType] and skillModList:Flag(cfg, "CannotElePenIgnore") + local ignoreNonNegativeEleRes = isElemental[damageType] and modDB:Flag(cfg, "IgnoreNonNegativeEleRes") + local calcHitResist = function(hitResist) + if not cannotElePenIgnore and ignoreNonNegativeEleRes and hitResist >= 0 then + return 0 + end + return cannotElePenIgnore and hitResist or calcPenResist(hitResist) + end + local normalHitResist = calcHitResist(resist) + local invertedHitResist = calcHitResist(-resist) + local usesResistance = cannotElePenIgnore or useRes + if usesResistance then if isElemental[damageType] and invertChance > 0 then - effectiveResist = calcPenResist(resist) * (1 - invertChance) + calcPenResist(-resist) * invertChance + effectiveResist = normalHitResist * (1 - invertChance) + invertedHitResist * invertChance else - effectiveResist = calcPenResist(resist) + effectiveResist = normalHitResist end effMult = effMult * (1 - effectiveResist / 100) end @@ -4289,12 +4294,13 @@ 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 or invertChance > 0) and skillModList:Flag(cfg, isElemental[damageType] and "CannotElePenIgnore" or nil) then + local breakdownResist = invertChance > 0 and resist or normalHitResist + if pass == 2 and breakdown and (effMult ~= 1 or sourceRes ~= damageType or invertChance > 0) and cannotElePenIgnore 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, effectiveResist) + breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, breakdownResist, 0, takenInc, effMult, takenMore, sourceRes, usesResistance, invertChance, minPen, effectiveResist, normalHitResist, invertedHitResist) 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, effectiveResist) + breakdown[damageType.."EffMult"] = breakdown.effMult(damageType, breakdownResist, pen, takenInc, effMult, takenMore, sourceRes, usesResistance, invertChance, minPen, effectiveResist, normalHitResist, invertedHitResist) end end if pass == 2 and breakdown then @@ -6454,4 +6460,4 @@ function calcs.offence(env, actor, activeSkill) output.CullingDPS = output.CombinedDPS * (bestCull - 1) output.ReservationDPS = output.CombinedDPS * (output.ReservationDpsMultiplier - 1) output.CombinedDPS = output.CombinedDPS * bestCull * output.ReservationDpsMultiplier -end \ No newline at end of file +end