Skip to content
Closed
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
52 changes: 52 additions & 0 deletions spec/System/TestAilments_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,56 @@ describe("TestAilments", function()

assert.are.equals(1.1, build.calcsTab.mainOutput.PoisonEffMult)
end)

-- Pseudo-hits: a skill flagged with skillData.pseudoHitAilment inflicts that one
-- ailment as though dealing its hit damage, but the hit itself deals no damage
-- (e.g. Infernal Legion's burn). No in-tree skill sets the flag yet, so run the
-- calcs directly on an env with the flag injected.
describe("pseudo-hit ailment sources", function()
local function setupFireball(customMods)
build.skillsTab:PasteSocketGroup("Fireball 20/0 1\n")
runCallback("OnFrame")
build.configTab.input.customMods = customMods
build.configTab:BuildModList()
runCallback("OnFrame")
end

local function performWith(pseudoHitAilment)
local calcs = build.calcsTab.calcs
local env = calcs.initEnv(build, "MAIN")
env.player.mainSkill.skillData.pseudoHitAilment = pseudoHitAilment
calcs.perform(env)
return env.player.output
end

it("deals no hit damage but still applies its ailment", function()
setupFireball("100% chance to Ignite")
local real = performWith(nil)
assert.True(real.TotalDPS and real.TotalDPS > 0)
assert.True(real.IgniteDPS and real.IgniteDPS > 0)
local pseudo = performWith("Ignite")
assert.are.equals(0, pseudo.TotalDPS)
assert.are.equals(0, pseudo.AverageDamage)
-- the ailment still sees the full stored hit damage
assert.are.equals(real.IgniteDPS, pseudo.IgniteDPS)
end)

it("does not let other on-hit ailments ride on the notional damage", function()
setupFireball("100% chance to Ignite\n100% chance to Poison on Hit\nAdds 50 to 70 Chaos Damage to Spells")
local real = performWith(nil)
assert.True(real.PoisonDPS and real.PoisonDPS > 0)
local pseudo = performWith("Ignite")
assert.True(not pseudo.PoisonDPS or pseudo.PoisonDPS == 0, "pseudo-hit must not produce poison DPS")
assert.are.equals(real.IgniteDPS, pseudo.IgniteDPS)
end)

it("still scales with ailment magnitude", function()
setupFireball("100% chance to Ignite")
local base = performWith("Ignite").IgniteDPS
assert.True(base and base > 0)
setupFireball("100% chance to Ignite\n50% increased Magnitude of Ailments")
local scaled = performWith("Ignite").IgniteDPS
assert.True(math.abs(scaled / base - 1.5) < 1e-6, string.format("expected 1.5x, got %.6fx", scaled / base))
end)
end)
end)
21 changes: 19 additions & 2 deletions src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4836,6 +4836,12 @@ function calcs.offence(env, actor, activeSkill)
if not canDeal[damageType] then
return false
end
-- A pseudo-hit (e.g. Infernal Legion "ignites as though dealing X") is not a
-- real hit, so it only inflicts the one ailment it represents -- on-hit ailment
-- sources (a Poison support, etc.) can't ride along on its notional damage.
if skillData.pseudoHitAilment and ailmentType ~= skillData.pseudoHitAilment then
return false
end
-- check against input valid types
if ((defaultDamageTypes and defaultDamageTypes[damageType])
or (ailmentData[ailmentType] and damageType == ailmentData[ailmentType].associatedType)) then
Expand Down Expand Up @@ -5488,8 +5494,9 @@ function calcs.offence(env, actor, activeSkill)
end
end

-- Calculate damaging ailment values
for _, damagingAilment in ipairs({"Bleed", "Poison", "Ignite"}) do
-- Calculate damaging ailment values. A pseudo-hit only seeds the ailment it
-- represents; that restriction lives in canDoAilment, so the loop stays general.
for _, damagingAilment in ipairs({ "Bleed", "Poison", "Ignite" }) do
local damageType = data.defaultAilmentDamageTypes[damagingAilment]["DamageType"]
if not canDeal[damageType] then
for _, type in ipairs(dmgTypeList) do
Expand Down Expand Up @@ -6133,6 +6140,16 @@ function calcs.offence(env, actor, activeSkill)
end
end

-- pseudoHitAilment: the hit deals no damage of its own (e.g. Infernal Legion
-- "ignites as though dealing X"), it only seeds its ailment, which was already
-- derived from the stored hit damage above. Zero the hit outputs so only the
-- ailment/DoT counts toward DPS.
if skillData.pseudoHitAilment then
output.AverageDamage = 0
output.AverageHit = 0
output.TotalDPS = 0
end

-- Calculate combined DPS estimate, including DoTs
local baseDPS = output[(skillData.showAverage and "AverageDamage") or "TotalDPS"]
output.CombinedDPS = baseDPS
Expand Down