Skip to content

Commit 468911e

Browse files
LocalIdentityLocalIdentity
andauthored
Fix rounding of scaled damage conversion percentages (#10263)
* Fix rounding of scaled conversion percentages The game rounds every scaled conversion destination to a whole percentage point before summing the converted amount. e.g. Three 40% conversions become 33%, 33%, and 33% instead of the 33.33% PoB currently uses * Fix rounding for damage conversion values Only round converted damage at the end instead of the start of the calculation path. This prevents each recursive conversion step from creating or losing damage values. --------- Co-authored-by: LocalIdentity <localidentity2@gmail.com>
1 parent 0346b91 commit 468911e

2 files changed

Lines changed: 58 additions & 15 deletions

File tree

‎spec/System/TestOffence_spec.lua‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,42 @@ describe("TestOffence", function()
8787
assert.are.equals(19, build.calcsTab.mainEnv.minion.output.AreaOfEffectRadius)
8888
end)
8989

90+
it("rounds each scaled damage conversion to a whole percent", function()
91+
build.skillsTab:PasteSocketGroup("Fireball 20/0 1")
92+
build.configTab.input.customMods = [[
93+
40% of Physical Damage Converted to Lightning Damage
94+
40% of Physical Damage Converted to Cold Damage
95+
40% of Physical Damage Converted to Fire Damage
96+
]]
97+
build.configTab:BuildModList()
98+
runCallback("OnFrame")
99+
100+
local conversion = build.calcsTab.mainEnv.player.mainSkill.conversionTable.Physical
101+
assert.are.equals(0.33, conversion.conversion.Lightning)
102+
assert.are.equals(0.33, conversion.conversion.Cold)
103+
assert.are.equals(0.33, conversion.conversion.Fire)
104+
assert.is_true(math.abs(conversion.mult - 0.01) < 0.000001)
105+
end)
106+
107+
it("rounds damage after the final step of a conversion path", function()
108+
build.itemsTab:CreateDisplayItemFromRaw([[
109+
New Item
110+
Imbued Wand
111+
Adds 2 to 2 Physical Damage to Spells
112+
]])
113+
build.itemsTab:AddDisplayItem()
114+
build.skillsTab:PasteSocketGroup("Fireball 20/0 1")
115+
build.configTab.input.customMods = [[
116+
50% of Physical Damage Converted to Lightning Damage
117+
50% of Lightning Damage Converted to Cold Damage
118+
]]
119+
build.configTab:BuildModList()
120+
runCallback("OnFrame")
121+
122+
assert.are.equals(9.6, build.calcsTab.calcsOutput.PhysicalMinBase)
123+
assert.are.equals(1, build.calcsTab.calcsOutput.ColdMin)
124+
end)
125+
90126
it("parses more/less/increased/reduced minimum and maximum damage of every type", function()
91127
build.itemsTab:CreateDisplayItemFromRaw([[
92128
New Item

‎src/Modules/CalcOffence.lua‎

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,25 +87,23 @@ local function calcDamage(activeSkill, output, cfg, breakdown, damageType, typeF
8787
addMax = addMax + max * convMult
8888
end
8989
end
90-
if addMin ~= 0 and addMax ~= 0 then
91-
addMin = round(addMin)
92-
addMax = round(addMax)
93-
end
94-
9590
local baseMin = output[damageType.."MinBase"]
9691
local baseMax = output[damageType.."MaxBase"]
9792
if baseMin == 0 and baseMax == 0 then
9893
-- No base damage for this type, don't need to calculate modifiers
9994
if breakdown and (addMin ~= 0 or addMax ~= 0) then
10095
t_insert(breakdown.damageTypes, {
10196
source = damageType,
102-
convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
103-
total = addMin .. " to " .. addMax,
97+
convSrc = (addMin ~= 0 or addMax ~= 0) and (round(addMin) .. " to " .. round(addMax)),
98+
total = round(addMin) .. " to " .. round(addMax),
10499
convDst = convDst and s_format("%d%% to %s", conversionTable[damageType].conversion[convDst] * 100, convDst),
105100
gainDst = convDst and s_format("%d%% gained as %s", conversionTable[damageType].gain[convDst] * 100, convDst),
106101
})
107102
end
108-
return addMin, addMax
103+
if convDst then
104+
return addMin, addMax
105+
end
106+
return round(addMin), round(addMax)
109107
end
110108

111109
-- Combine modifiers
@@ -125,15 +123,20 @@ local function calcDamage(activeSkill, output, cfg, breakdown, damageType, typeF
125123
base = baseMin .. " to " .. baseMax,
126124
inc = (inc ~= 1 and "x "..inc),
127125
more = (more ~= 1 and "x "..more),
128-
convSrc = (addMin ~= 0 or addMax ~= 0) and (addMin .. " to " .. addMax),
129-
total = (round(baseMin * inc * more) + addMin) .. " to " .. (round(baseMax * inc * more) + addMax),
126+
convSrc = (addMin ~= 0 or addMax ~= 0) and (round(addMin) .. " to " .. round(addMax)),
127+
total = round(baseMin * inc * more + addMin) .. " to " .. round(baseMax * inc * more + addMax),
130128
convDst = convDst and conversionTable[damageType].conversion[convDst] > 0 and s_format("%d%% to %s", conversionTable[damageType].conversion[convDst] * 100, convDst),
131129
gainDst = convDst and conversionTable[damageType].gain[convDst] > 0 and s_format("%d%% gained as %s", conversionTable[damageType].gain[convDst] * 100, convDst),
132130
})
133131
end
134132

135-
return round(((baseMin * inc * more) * genericMoreMinDamage + addMin) * moreMinDamage * incMinDamage),
136-
round(((baseMax * inc * more) * genericMoreMaxDamage + addMax) * moreMaxDamage * incMaxDamage)
133+
local min = ((baseMin * inc * more) * genericMoreMinDamage + addMin) * moreMinDamage * incMinDamage
134+
local max = ((baseMax * inc * more) * genericMoreMaxDamage + addMax) * moreMaxDamage * incMaxDamage
135+
if convDst then
136+
-- Converted paths stay fractional until they reach their final damage type.
137+
return min, max
138+
end
139+
return round(min), round(max)
137140
end
138141

139142
local function calcAilmentSourceDamage(activeSkill, output, cfg, breakdown, damageType, typeFlags)
@@ -1956,12 +1959,16 @@ function calcs.offence(env, actor, activeSkill)
19561959
globalTotal = globalTotal * factor
19571960
end
19581961
local dmgTable = { conversion = { }, gain = { } }
1962+
local convertedTotal = 0
19591963
for type in pairs(globalConv) do
1960-
dmgTable.conversion[type] = (globalConv[type] + skillConv[type]) / 100
1964+
-- The game stores each scaled conversion destination as a whole percent.
1965+
local conversion = round(globalConv[type] + skillConv[type])
1966+
dmgTable.conversion[type] = conversion / 100
19611967
dmgTable.gain[type] = add[type] / 100
1962-
dmgTable[type] = (globalConv[type] + skillConv[type] + add[type]) / 100
1968+
dmgTable[type] = (conversion + add[type]) / 100
1969+
convertedTotal = convertedTotal + conversion
19631970
end
1964-
dmgTable.mult = 1 - m_min((globalTotal + skillTotal) / 100, 1)
1971+
dmgTable.mult = 1 - m_min(convertedTotal / 100, 1)
19651972
conversionTable[damageType] = dmgTable
19661973
end
19671974
conversionTable["Chaos"] = { mult = 1 }

0 commit comments

Comments
 (0)