Skip to content

Commit 90fa139

Browse files
committed
Fix mana cost efficiency order of operations and add mods to breakdown
1 parent 7b10380 commit 90fa139

3 files changed

Lines changed: 47 additions & 22 deletions

File tree

spec/System/TestSkills_spec.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,19 @@ describe("TestAttacks", function()
216216
assert.True(math.abs(finalCost - 12) < 0.1) -- floor(12 * 1.5) / 1.5
217217
end)
218218

219+
it("Test flat cost is added before cost efficiency (#10003)", function()
220+
-- In-game order is ((base cost * multipliers) + flat cost) / (1 + cost efficiency)
221+
build.skillsTab:PasteSocketGroup("Hydrosphere 1/0 1\n")
222+
223+
-- Hydrosphere 12 base mana cost
224+
build.configTab.input.customMods = "+10 to Total Mana Cost\n50% increased Mana Cost Efficiency"
225+
build.configTab:BuildModList()
226+
runCallback("OnFrame")
227+
228+
local finalCost = build.calcsTab.mainOutput.ManaCost
229+
-- (12 + 10) / 1.5 = 14.667
230+
assert.True(math.abs(finalCost - 22 / 1.5) < 0.001)
231+
end)
219232
it("Test mana cost efficiency with support gems", function()
220233
-- Test interaction between cost efficiency and cost multipliers
221234
build.skillsTab:PasteSocketGroup("Contagion 6/0 1\nMagnified Area I 1/0 1")

src/Modules/CalcOffence.lua

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,7 +1756,7 @@ function calcs.offence(env, actor, activeSkill)
17561756
moreType = skillModList:More(skillCfg, val.type.."Cost")
17571757
moreCost = skillModList:More(skillCfg, "Cost")
17581758
inc = skillModList:Sum("INC", skillCfg, val.type.."Cost", "Cost")
1759-
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * val.baseCostRaw * moreType * moreCost / costEfficiency) + val.totalCost)
1759+
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * val.baseCostRaw * moreType * moreCost) + val.totalCost) / costEfficiency
17601760
if inc < 0 then
17611761
output[costName] = m_max(0, m_ceil((1 + inc / 100) * output[costName]))
17621762
else
@@ -1772,9 +1772,9 @@ function calcs.offence(env, actor, activeSkill)
17721772
else
17731773
output[costName] = m_max(0, m_floor(moreCost * output[costName]))
17741774
end
1775+
output[costName] = m_max(0, output[costName] + val.totalCost)
17751776
-- Apply cost efficiency (similar to reservation efficiency)
17761777
output[costName] = m_max(0, output[costName] / costEfficiency)
1777-
output[costName] = m_max(0, output[costName] + val.totalCost)
17781778
if val.type == "Mana" and hybridLifeCost > 0 then -- Life/Mana Mastery
17791779
output[costName] = m_max(0, m_floor((1 - hybridLifeCost) * output[costName]))
17801780
output[costNameRaw] = output[costNameRaw] and m_max(0, (1 - hybridLifeCost) * output[costNameRaw])
@@ -1785,10 +1785,10 @@ function calcs.offence(env, actor, activeSkill)
17851785
output[costName] = m_floor(val.baseCost + val.baseCostNoMult)
17861786
output[costName] = m_max(0, (1 + inc / 100) * output[costName])
17871787
output[costName] = m_max(0, moreType * output[costName])
1788+
output[costName] = m_max(0, output[costName] + val.totalCost)
17881789
-- Apply cost efficiency for unaffected costs too
17891790
output[costName] = m_max(0, output[costName] / costEfficiency)
1790-
output[costName] = m_max(0, output[costName] + val.totalCost)
1791-
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * (val.baseCostRaw + val.baseCostNoMult) * moreType / costEfficiency) + val.totalCost)
1791+
output[costNameRaw] = val.baseCostRaw and m_max(0, m_max(0, (1 + inc / 100) * (val.baseCostRaw + val.baseCostNoMult) * moreType) + val.totalCost) / costEfficiency
17921792
end
17931793
if breakdown and hasCost then
17941794
breakdown[costName] = {
@@ -1815,16 +1815,16 @@ function calcs.offence(env, actor, activeSkill)
18151815
if moreType ~= 1 then
18161816
t_insert(breakdown[costName], s_format("x %.2f ^8(more/less "..val.text.." cost)", moreType))
18171817
end
1818+
if val.totalCost ~= 0 then
1819+
t_insert(breakdown[costName], s_format("%+d ^8(total " .. val.text .. " cost)", val.totalCost))
1820+
end
18181821
if costEfficiency ~= 1 then
18191822
t_insert(breakdown[costName], s_format("/ %.2f ^8(" .. val.text .. " cost efficiency)", costEfficiency))
18201823
end
1821-
if val.totalCost ~= 0 then
1822-
t_insert(breakdown[costName], s_format("%+d ^8(total "..val.text.." cost)", val.totalCost))
1823-
end
18241824
if val.type == "Mana" and hybridLifeCost > 0 then
18251825
t_insert(breakdown[costName], s_format("x %.2f ^8(%d%% paid for with life)", (1-hybridLifeCost), hybridLifeCost*100))
18261826
end
1827-
t_insert(breakdown[costName], s_format("= %"..(val.upfront and "d" or ".2f")..(val.percent and "%%" or ""), output[costName]))
1827+
t_insert(breakdown[costName], s_format("= %" .. (val.upfront and "d" or ".2f") .. (val.percent and "%%" or ""), m_ceil(output[costName])))
18281828
end
18291829
end
18301830
end

src/Modules/CalcSections.lua

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ local fireConvert = {
4040
"FireDamageConvertToChaos", "ElementalDamageConvertToChaos", "NonChaosDamageConvertToChaos",
4141
"FireDamageGainAsChaos", "ElementalDamageGainAsChaos", "NonChaosDamageGainAsChaos"
4242
}
43+
local manaCost = {
44+
"ManaCost", "Cost", "ManaCostNoMult", "ManaCostEfficiency", "CostEfficiency"
45+
}
46+
local lifeCost = {
47+
"LifeCost", "Cost", "LifeCostNoMult", "LifeCostEfficiency", "CostEfficiency"
48+
}
49+
local ESCost = {
50+
"ESCost", "Cost", "ESCostNoMult", "ESCostEfficiency", "CostEfficiency"
51+
}
52+
local rageCost = {
53+
"RageCost", "Cost", "RageNoMult", "RageCostEfficiency", "CostEfficiency"
54+
}
4355

4456
-- format {width, id, group, color, subsection:{default hidden, label, data:{}}}
4557
return {
@@ -619,20 +631,20 @@ return {
619631
{ 1, "SkillTypeStats", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill type-specific Stats", data = {
620632
{ label = "Gem Level", haveOutput = "GemHasLevel", { format = "{0:output:GemLevel}", { breakdown = "GemLevel" }, { modName = { "GemLevel" }, cfg = "skill" },{ modName = { "GemSupportLevel" }, cfg = "skill" }, { modName = { "GemItemLevel" }, cfg = "skill" }, }, },
621633
{ label = "Gem Quality", haveOutput = "GemHasQuality", { format = "{0:output:GemQuality}", { breakdown = "GemQuality" }, { modName = { "GemQuality" }, cfg = "skill" },{ modName = { "GemSupportQuality" }, cfg = "skill" }, { modName = { "GemItemQuality" }, cfg = "skill" }, }, },
622-
{ label = "Mana Cost", color = colorCodes.MANA, haveOutput = "ManaHasCost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
623-
{ label = "Mana % Cost", color = colorCodes.MANA, haveOutput = "ManaPercentHasCost", { format = "{0:output:ManaPercentCost}", { breakdown = "ManaPercentCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
624-
{ label = "Mana per second", color = colorCodes.MANA, haveOutput = "ManaPerSecondHasCost", { format = "{2:output:ManaPerSecondCost}", { breakdown = "ManaPerSecondCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
625-
{ label = "Mana % per second", color = colorCodes.MANA, haveOutput = "ManaPercentPerSecondHasCost", { format = "{2:output:ManaPercentPerSecondCost}", { breakdown = "ManaPercentPerSecondCost" }, { modName = { "ManaCost", "Cost", "ManaCostNoMult" }, cfg = "skill" }, }, },
626-
{ label = "Life Cost", color = colorCodes.LIFE, haveOutput = "LifeHasCost", { format = "{0:output:LifeCost}", { breakdown = "LifeCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
627-
{ label = "Life % Cost", color = colorCodes.LIFE, haveOutput = "LifePercentHasCost", { format = "{0:output:LifePercentCost}", { breakdown = "LifePercentCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
628-
{ label = "Life per second", color = colorCodes.LIFE, haveOutput = "LifePerSecondHasCost", { format = "{2:output:LifePerSecondCost}", { breakdown = "LifePerSecondCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
629-
{ label = "Life % per second", color = colorCodes.LIFE, haveOutput = "LifePercentPerSecondHasCost", { format = "{2:output:LifePercentPerSecondCost}", { breakdown = "LifePercentPerSecondCost" }, { modName = { "LifeCost", "Cost", "LifeCostNoMult" }, cfg = "skill" }, }, },
630-
{ label = "ES Cost", color = colorCodes.ES, haveOutput = "ESHasCost", { format = "{0:output:ESCost}", { breakdown = "ESCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
631-
{ label = "ES per second", color = colorCodes.ES, haveOutput = "ESPerSecondHasCost", { format = "{2:output:ESPerSecondCost}", { breakdown = "ESPerSecondCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
632-
{ label = "ES % per second", color = colorCodes.ES, haveOutput = "ESPercentPerSecondHasCost", { format = "{2:output:ESPercentPerSecondCost}", { breakdown = "ESPercentPerSecondCost" }, { modName = { "ESCost", "Cost", "ESCostNoMult" }, cfg = "skill" }, }, },
633-
{ label = "Rage Cost", color = colorCodes.RAGE, haveOutput = "RageHasCost", { format = "{0:output:RageCost}", { breakdown = "RageCost" }, { modName = { "RageCost", "Cost", "RageNoMult" }, cfg = "skill" }, }, },
634-
{ label = "Rage per second", color = colorCodes.RAGE, haveOutput = "RagePerSecondHasCost", { format = "{2:output:RagePerSecondCost}", { breakdown = "RagePerSecondCost" }, { modName = { "RageCost", "Cost", "RageNoMult" }, cfg = "skill" }, }, },
635-
{ label = "Soul Cost", color = colorCodes.RAGE, haveOutput = "SoulHasCost", { format = "{0:output:SoulCost}", { breakdown = "SoulCost" }, { modName = { "SoulCost" }, cfg = "skill" }, }, },
634+
{ label = "Mana Cost", color = colorCodes.MANA, haveOutput = "ManaHasCost", { format = "{0:output:ManaCost}", { breakdown = "ManaCost" }, { modName = manaCost, cfg = "skill" }, }, },
635+
{ label = "Mana % Cost", color = colorCodes.MANA, haveOutput = "ManaPercentHasCost", { format = "{0:output:ManaPercentCost}", { breakdown = "ManaPercentCost" }, { modName = manaCost, cfg = "skill" }, }, },
636+
{ label = "Mana per second", color = colorCodes.MANA, haveOutput = "ManaPerSecondHasCost", { format = "{2:output:ManaPerSecondCost}", { breakdown = "ManaPerSecondCost" }, { modName = manaCost, cfg = "skill" }, }, },
637+
{ label = "Mana % per second", color = colorCodes.MANA, haveOutput = "ManaPercentPerSecondHasCost", { format = "{2:output:ManaPercentPerSecondCost}", { breakdown = "ManaPercentPerSecondCost" }, { modName = manaCost, cfg = "skill" }, }, },
638+
{ label = "Life Cost", color = colorCodes.LIFE, haveOutput = "LifeHasCost", { format = "{0:output:LifeCost}", { breakdown = "LifeCost" }, { modName = lifeCost, cfg = "skill" }, }, },
639+
{ label = "Life % Cost", color = colorCodes.LIFE, haveOutput = "LifePercentHasCost", { format = "{0:output:LifePercentCost}", { breakdown = "LifePercentCost" }, { modName = lifeCost, cfg = "skill" }, }, },
640+
{ label = "Life per second", color = colorCodes.LIFE, haveOutput = "LifePerSecondHasCost", { format = "{2:output:LifePerSecondCost}", { breakdown = "LifePerSecondCost" }, { modName = lifeCost, cfg = "skill" }, }, },
641+
{ label = "Life % per second", color = colorCodes.LIFE, haveOutput = "LifePercentPerSecondHasCost", { format = "{2:output:LifePercentPerSecondCost}", { breakdown = "LifePercentPerSecondCost" }, { modName = lifeCost, cfg = "skill" }, }, },
642+
{ label = "ES Cost", color = colorCodes.ES, haveOutput = "ESHasCost", { format = "{0:output:ESCost}", { breakdown = "ESCost" }, { modName = ESCost, cfg = "skill" }, }, },
643+
{ label = "ES per second", color = colorCodes.ES, haveOutput = "ESPerSecondHasCost", { format = "{2:output:ESPerSecondCost}", { breakdown = "ESPerSecondCost" }, { modName = ESCost, cfg = "skill" }, }, },
644+
{ label = "ES % per second", color = colorCodes.ES, haveOutput = "ESPercentPerSecondHasCost", { format = "{2:output:ESPercentPerSecondCost}", { breakdown = "ESPercentPerSecondCost" }, { modName = ESCost, cfg = "skill" }, }, },
645+
{ label = "Rage Cost", color = colorCodes.RAGE, haveOutput = "RageHasCost", { format = "{0:output:RageCost}", { breakdown = "RageCost" }, { modName = rageCost, cfg = "skill" }, }, },
646+
{ label = "Rage per second", color = colorCodes.RAGE, haveOutput = "RagePerSecondHasCost", { format = "{2:output:RagePerSecondCost}", { breakdown = "RagePerSecondCost" }, { modName = rageCost, cfg = "skill" }, }, },
647+
{ label = "Soul Cost", color = colorCodes.RAGE, haveOutput = "SoulHasCost", { format = "{0:output:SoulCost}", { breakdown = "SoulCost" }, { modName = { "SoulCost", "SoulCostEfficiency" }, cfg = "skill" }, }, },
636648
{ label = "Active Minion Limit", haveOutput = "ActiveMinionLimit", { format = "{0:output:ActiveMinionLimit}" } },
637649
{ label = "Quantity Multiplier", haveOutput = "QuantityMultiplier", { format = "{0:output:QuantityMultiplier}",
638650
{ breakdown = "QuantityMultiplier" },

0 commit comments

Comments
 (0)