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
36 changes: 36 additions & 0 deletions spec/System/TestSkills_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,42 @@ describe("TestAttacks", function()
assert.True(math.abs(finalCost - 12) < 0.1) -- floor(12 * 1.5) / 1.5
end)

it("Test flat cost is added before cost efficiency", function()
-- In-game order is ((base cost * multipliers) + flat cost) / (1 + cost efficiency)
build.skillsTab:PasteSocketGroup("Hydrosphere 1/0 1\n")

-- Hydrosphere 12 base mana cost
build.configTab.input.customMods = "+10 to Total Mana Cost\n50% increased Mana Cost Efficiency"
build.configTab:BuildModList()
runCallback("OnFrame")

local finalCost = build.calcsTab.mainOutput.ManaCost
-- (12 + 10) / 1.5 = 14.667
assert.True(math.abs(finalCost - 22 / 1.5) < 0.001)
end)
it("Test flat cost is added before cost efficiency for life costs", function()
build.skillsTab:PasteSocketGroup("Hydrosphere 1/0 1\n")

-- Convert Hydrosphere's 12 base cost to life, then add +10 flat and 50% efficiency
build.configTab.input.customMods = "Skills Cost Life instead of Mana\n+10 to Total Cost\n50% increased Cost Efficiency"
build.configTab:BuildModList()
runCallback("OnFrame")

-- (12 + 10) / 1.5 = 14.667
assert.True(math.abs(build.calcsTab.mainOutput.LifeCost - 22 / 1.5) < 0.001)
end)

it("Test flat cost is added before cost efficiency for energy shield costs (#10003)", function()
build.skillsTab:PasteSocketGroup("Hydrosphere 1/0 1\n")

-- Convert Hydrosphere's 12 base cost to ES, then add +10 flat and 50% efficiency
build.configTab.input.customMods = "Skills Cost Energy Shield instead of Mana or Life\n+10 to Total Cost\n50% increased Cost Efficiency"
build.configTab:BuildModList()
runCallback("OnFrame")

-- (12 + 10) / 1.5 = 14.667
assert.True(math.abs(build.calcsTab.mainOutput.ESCost - 22 / 1.5) < 0.001)
end)
it("Test mana cost efficiency with support gems", function()
-- Test interaction between cost efficiency and cost multipliers
build.skillsTab:PasteSocketGroup("Contagion 6/0 1\nMagnified Area I 1/0 1")
Expand Down
16 changes: 8 additions & 8 deletions src/Modules/CalcOffence.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1756,7 +1756,7 @@ function calcs.offence(env, actor, activeSkill)
moreType = skillModList:More(skillCfg, val.type.."Cost")
moreCost = skillModList:More(skillCfg, "Cost")
inc = skillModList:Sum("INC", skillCfg, val.type.."Cost", "Cost")
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * val.baseCostRaw * moreType * moreCost / costEfficiency) + val.totalCost)
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * val.baseCostRaw * moreType * moreCost) + val.totalCost) / costEfficiency
if inc < 0 then
output[costName] = m_max(0, m_ceil((1 + inc / 100) * output[costName]))
else
Expand All @@ -1772,9 +1772,9 @@ function calcs.offence(env, actor, activeSkill)
else
output[costName] = m_max(0, m_floor(moreCost * output[costName]))
end
output[costName] = m_max(0, output[costName] + val.totalCost)
-- Apply cost efficiency (similar to reservation efficiency)
output[costName] = m_max(0, output[costName] / costEfficiency)
output[costName] = m_max(0, output[costName] + val.totalCost)
if val.type == "Mana" and hybridLifeCost > 0 then -- Life/Mana Mastery
output[costName] = m_max(0, m_floor((1 - hybridLifeCost) * output[costName]))
output[costNameRaw] = output[costNameRaw] and m_max(0, (1 - hybridLifeCost) * output[costNameRaw])
Expand All @@ -1785,10 +1785,10 @@ function calcs.offence(env, actor, activeSkill)
output[costName] = m_floor(val.baseCost + val.baseCostNoMult)
output[costName] = m_max(0, (1 + inc / 100) * output[costName])
output[costName] = m_max(0, moreType * output[costName])
output[costName] = m_max(0, output[costName] + val.totalCost)
-- Apply cost efficiency for unaffected costs too
output[costName] = m_max(0, output[costName] / costEfficiency)
output[costName] = m_max(0, output[costName] + val.totalCost)
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * (val.baseCostRaw + val.baseCostNoMult) * moreType / costEfficiency) + val.totalCost)
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * (val.baseCostRaw + val.baseCostNoMult) * moreType) + val.totalCost) / costEfficiency
end
if breakdown and hasCost then
breakdown[costName] = {
Expand All @@ -1815,16 +1815,16 @@ function calcs.offence(env, actor, activeSkill)
if moreType ~= 1 then
t_insert(breakdown[costName], s_format("x %.2f ^8(more/less "..val.text.." cost)", moreType))
end
if val.totalCost ~= 0 then
t_insert(breakdown[costName], s_format("%+d ^8(total " .. val.text .. " cost)", val.totalCost))
end
if costEfficiency ~= 1 then
t_insert(breakdown[costName], s_format("/ %.2f ^8(" .. val.text .. " cost efficiency)", costEfficiency))
end
if val.totalCost ~= 0 then
t_insert(breakdown[costName], s_format("%+d ^8(total "..val.text.." cost)", val.totalCost))
end
if val.type == "Mana" and hybridLifeCost > 0 then
t_insert(breakdown[costName], s_format("x %.2f ^8(%d%% paid for with life)", (1-hybridLifeCost), hybridLifeCost*100))
end
t_insert(breakdown[costName], s_format("= %"..(val.upfront and "d" or ".2f")..(val.percent and "%%" or ""), output[costName]))
t_insert(breakdown[costName], s_format("= %" .. (val.upfront and "d" or ".2f") .. (val.percent and "%%" or ""), m_ceil(output[costName])))
end
end
end
Expand Down
40 changes: 26 additions & 14 deletions src/Modules/CalcSections.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ local fireConvert = {
"FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
"FireDamageGainAsChaos", "ElementalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
}
local manaCost = {
"ManaCost", "Cost", "ManaCostNoMult", "ManaCostEfficiency", "CostEfficiency"
}
local lifeCost = {
"LifeCost", "Cost", "LifeCostNoMult", "LifeCostEfficiency", "CostEfficiency"
}
local ESCost = {
"ESCost", "Cost", "ESCostNoMult", "ESCostEfficiency", "CostEfficiency"
}
local rageCost = {
"RageCost", "Cost", "RageCostNoMult", "RageCostEfficiency", "CostEfficiency"
}

-- format {width, id, group, color, subsection:{default hidden, label, data:{}}}
return {
Expand Down Expand Up @@ -619,20 +631,20 @@ return {
{ 1, "SkillTypeStats", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill type-specific Stats", data = {
{ label = "Gem Level", haveOutput = "GemHasLevel", { format = "{0:output:GemLevel}", { breakdown = "GemLevel" }, { modName = { "GemLevel" }, cfg = "skill" },{ modName = { "GemSupportLevel" }, cfg = "skill" }, { modName = { "GemItemLevel" }, cfg = "skill" }, }, },
{ label = "Gem Quality", haveOutput = "GemHasQuality", { format = "{0:output:GemQuality}", { breakdown = "GemQuality" }, { modName = { "GemQuality" }, cfg = "skill" },{ modName = { "GemSupportQuality" }, cfg = "skill" }, { modName = { "GemItemQuality" }, cfg = "skill" }, }, },
{ label = "Mana Cost", color = colorCodes.MANA, haveOutput = "ManaHasCost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
{ label = "Mana % Cost", color = colorCodes.MANA, haveOutput = "ManaPercentHasCost", { format = "{0:output:ManaPercentCost}", { breakdown = "ManaPercentCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
{ label = "Mana per second", color = colorCodes.MANA, haveOutput = "ManaPerSecondHasCost", { format = "{2:output:ManaPerSecondCost}", { breakdown = "ManaPerSecondCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
{ label = "Mana % per second", color = colorCodes.MANA, haveOutput = "ManaPercentPerSecondHasCost", { format = "{2:output:ManaPercentPerSecondCost}", { breakdown = "ManaPercentPerSecondCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
{ label = "Life Cost", color = colorCodes.LIFE, haveOutput = "LifeHasCost", { format = "{0:output:LifeCost}", { breakdown = "LifeCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
{ label = "Life % Cost", color = colorCodes.LIFE, haveOutput = "LifePercentHasCost", { format = "{0:output:LifePercentCost}", { breakdown = "LifePercentCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
{ label = "Life per second", color = colorCodes.LIFE, haveOutput = "LifePerSecondHasCost", { format = "{2:output:LifePerSecondCost}", { breakdown = "LifePerSecondCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
{ label = "Life % per second", color = colorCodes.LIFE, haveOutput = "LifePercentPerSecondHasCost", { format = "{2:output:LifePercentPerSecondCost}", { breakdown = "LifePercentPerSecondCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
{ label = "ES Cost", color = colorCodes.ES, haveOutput = "ESHasCost", { format = "{0:output:ESCost}", { breakdown = "ESCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
{ label = "ES per second", color = colorCodes.ES, haveOutput = "ESPerSecondHasCost", { format = "{2:output:ESPerSecondCost}", { breakdown = "ESPerSecondCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
{ label = "ES % per second", color = colorCodes.ES, haveOutput = "ESPercentPerSecondHasCost", { format = "{2:output:ESPercentPerSecondCost}", { breakdown = "ESPercentPerSecondCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
{ label = "Rage Cost", color = colorCodes.RAGE, haveOutput = "RageHasCost", { format = "{0:output:RageCost}", { breakdown = "RageCost" }, { modName = { "RageCost", "Cost", "RageNoMult" }, cfg = "skill" }, }, },
{ label = "Rage per second", color = colorCodes.RAGE, haveOutput = "RagePerSecondHasCost", { format = "{2:output:RagePerSecondCost}", { breakdown = "RagePerSecondCost" }, { modName = { "RageCost", "Cost", "RageNoMult" }, cfg = "skill" }, }, },
{ label = "Soul Cost", color = colorCodes.RAGE, haveOutput = "SoulHasCost", { format = "{0:output:SoulCost}", { breakdown = "SoulCost" }, { modName = { "SoulCost" }, cfg = "skill" }, }, },
{ label = "Mana Cost", color = colorCodes.MANA, haveOutput = "ManaHasCost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = manaCost, cfg = "skill" }, }, },
{ label = "Mana % Cost", color = colorCodes.MANA, haveOutput = "ManaPercentHasCost", { format = "{0:output:ManaPercentCost}", { breakdown = "ManaPercentCost" }, { modName = manaCost, cfg = "skill" }, }, },
{ label = "Mana per second", color = colorCodes.MANA, haveOutput = "ManaPerSecondHasCost", { format = "{2:output:ManaPerSecondCost}", { breakdown = "ManaPerSecondCost" }, { modName = manaCost, cfg = "skill" }, }, },
{ label = "Mana % per second", color = colorCodes.MANA, haveOutput = "ManaPercentPerSecondHasCost", { format = "{2:output:ManaPercentPerSecondCost}", { breakdown = "ManaPercentPerSecondCost" }, { modName = manaCost, cfg = "skill" }, }, },
{ label = "Life Cost", color = colorCodes.LIFE, haveOutput = "LifeHasCost", { format = "{0:output:LifeCost}", { breakdown = "LifeCost" }, { modName = lifeCost, cfg = "skill" }, }, },
{ label = "Life % Cost", color = colorCodes.LIFE, haveOutput = "LifePercentHasCost", { format = "{0:output:LifePercentCost}", { breakdown = "LifePercentCost" }, { modName = lifeCost, cfg = "skill" }, }, },
{ label = "Life per second", color = colorCodes.LIFE, haveOutput = "LifePerSecondHasCost", { format = "{2:output:LifePerSecondCost}", { breakdown = "LifePerSecondCost" }, { modName = lifeCost, cfg = "skill" }, }, },
{ label = "Life % per second", color = colorCodes.LIFE, haveOutput = "LifePercentPerSecondHasCost", { format = "{2:output:LifePercentPerSecondCost}", { breakdown = "LifePercentPerSecondCost" }, { modName = lifeCost, cfg = "skill" }, }, },
{ label = "ES Cost", color = colorCodes.ES, haveOutput = "ESHasCost", { format = "{0:output:ESCost}", { breakdown = "ESCost" }, { modName = ESCost, cfg = "skill" }, }, },
{ label = "ES per second", color = colorCodes.ES, haveOutput = "ESPerSecondHasCost", { format = "{2:output:ESPerSecondCost}", { breakdown = "ESPerSecondCost" }, { modName = ESCost, cfg = "skill" }, }, },
{ label = "ES % per second", color = colorCodes.ES, haveOutput = "ESPercentPerSecondHasCost", { format = "{2:output:ESPercentPerSecondCost}", { breakdown = "ESPercentPerSecondCost" }, { modName = ESCost, cfg = "skill" }, }, },
{ label = "Rage Cost", color = colorCodes.RAGE, haveOutput = "RageHasCost", { format = "{0:output:RageCost}", { breakdown = "RageCost" }, { modName = rageCost, cfg = "skill" }, }, },
{ label = "Rage per second", color = colorCodes.RAGE, haveOutput = "RagePerSecondHasCost", { format = "{2:output:RagePerSecondCost}", { breakdown = "RagePerSecondCost" }, { modName = rageCost, cfg = "skill" }, }, },
{ label = "Soul Cost", color = colorCodes.RAGE, haveOutput = "SoulHasCost", { format = "{0:output:SoulCost}", { breakdown = "SoulCost" }, { modName = { "SoulCost", "SoulCostEfficiency" }, cfg = "skill" }, }, },
{ label = "Active Minion Limit", haveOutput = "ActiveMinionLimit", { format = "{0:output:ActiveMinionLimit}" } },
{ label = "Quantity Multiplier", haveOutput = "QuantityMultiplier", { format = "{0:output:QuantityMultiplier}",
{ breakdown = "QuantityMultiplier" },
Expand Down
Loading