Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions spec/System/TestSkills_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions src/Modules/CalcBreakdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:")
Expand Down Expand Up @@ -263,4 +263,4 @@ return function(modDB, output, actor)
end

return breakdown
end
end
32 changes: 19 additions & 13 deletions src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
end