Skip to content
Open
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
57 changes: 57 additions & 0 deletions spec/System/TestTreeTab_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
describe("TreeTab", function()
before_each(function()
newBuild()
end)

teardown(function()
-- newBuild() resets the shared build state for the next test.
end)

it("adds separate power report entries for mastery effects", function()
local treeTab = build.treeTab
local parentNode = { id = 2 }
local masteryNode = {
id = 1,
type = "Mastery",
dn = "Two Hand Mastery",
power = {
masteryEffects = {
[101] = { singleStat = 10, pathPower = 10 },
[102] = { singleStat = 20, pathPower = 20 },
},
},
masteryEffects = {
{ effect = 101 },
{ effect = 102 },
},
path = { parentNode, false },
x = 10,
y = 20,
}
masteryNode.path[2] = masteryNode

treeTab.build.displayStats = {
{ stat = "Damage", label = "Damage", fmt = ".1f" },
}
treeTab.build.spec.nodes = {
[masteryNode.id] = masteryNode,
}
treeTab.build.spec.masterySelections = { }
treeTab.build.spec.tree.clusterNodeMap = { }
treeTab.build.spec.tree.masteryEffects = {
[101] = { id = 101, sd = { "Gain 10 Damage" }, stats = { "Gain 10 Damage" } },
[102] = { id = 102, sd = { "Gain 20 Damage" }, stats = { "Gain 20 Damage" } },
}
treeTab.build.calcsTab.mainEnv = { grantedPassives = { } }

local report = treeTab:BuildPowerReportList({ stat = "Damage", label = "Damage" })

assert.are.same(2, #report)
assert.are.same("Mastery", report[1].type)
assert.are.same("Two Hand Mastery: Gain 20 Damage", report[1].name)
assert.are.same(20, report[1].power)
assert.are.same(2, report[1].pathDist)
assert.are.same(10, report[2].power)
assert.are.same("Two Hand Mastery: Gain 10 Damage", report[2].name)
end)
end)
110 changes: 106 additions & 4 deletions src/Classes/CalcsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -492,18 +492,46 @@ function CalcsTabClass:PowerBuilder()
if coroutine.running() then
coroutine.yield()
end

local function buildMasteryEffectNode(node, effect)
local effectNode = {
id = node.id,
type = node.type,
name = node.name,
sd = { },
}
for i, sd in ipairs(effect.sd or { }) do
effectNode.sd[i] = sd
end
self.build.spec.tree:ProcessStats(effectNode)
return effectNode
end

local start = GetTime()
local nodeIndex = 0
local total = 0

for nodeId, node in pairs(self.build.spec.nodes) do
wipeTable(node.power)
if node.type == "Mastery" then
node.power.masteryEffects = { }
end
if node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
distanceMap[node.pathDist or 1000][nodeId] = node
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
total = total + 1
if node.type == "Mastery" and node.allMasteryOptions then
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
local assignedNodeId = isValueInTable(self.build.spec.masterySelections, masteryEffect.effect)
if not assignedNodeId or assignedNodeId == node.id then
total = total + 1
end
end
end
else
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
distanceMap[node.pathDist or 1000][nodeId] = node
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
total = total + 1
end
end
end
end
Expand Down Expand Up @@ -572,6 +600,17 @@ function CalcsTabClass:PowerBuilder()
end
end
end
if node.type == "Mastery" then
local selectedEffectId = self.build.spec.masterySelections[node.id]
if selectedEffectId then
node.power.masteryEffects[selectedEffectId] = {
singleStat = node.power.singleStat,
pathPower = node.power.pathPower,
offence = node.power.offence,
defence = node.power.defence,
}
end
end
nodeIndex = nodeIndex + 1
if coroutine.running() and GetTime() - start > 100 then
if self.build.powerBuilderProgressCallback then
Expand All @@ -583,6 +622,69 @@ function CalcsTabClass:PowerBuilder()
end
end

for nodeId, node in pairs(self.build.spec.nodes) do
if node.type == "Mastery" and node.allMasteryOptions and node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
local assignedNodeId = isValueInTable(self.build.spec.masterySelections, masteryEffect.effect)
if not assignedNodeId or assignedNodeId == node.id then
local effect = self.build.spec.tree.masteryEffects[masteryEffect.effect]
if effect then
local effectNode = buildMasteryEffectNode(node, effect)
if effectNode.modKey ~= "" then
if not cache[effectNode.modKey] then
cache[effectNode.modKey] = calcFunc({ addNodes = { [effectNode] = true } }, useFullDPS)
end
local output = cache[effectNode.modKey]
node.power.masteryEffects[effect.id] = { }
if self.powerStat and self.powerStat.stat and not self.powerStat.ignoreForNodes then
node.power.masteryEffects[effect.id].singleStat = self:CalculatePowerStat(self.powerStat, output, calcBase)
node.power.masteryEffects[effect.id].pathPower = node.power.masteryEffects[effect.id].singleStat
if node.path and not node.ascendancyName then
newPowerMax.singleStat = m_max(newPowerMax.singleStat, node.power.masteryEffects[effect.id].singleStat)
local pathNodes = {
[effectNode] = true
}
for _, pathNode in pairs(node.path) do
if pathNode ~= node then
pathNodes[pathNode] = true
end
end
if node.pathDist > 1 then
node.power.masteryEffects[effect.id].pathPower = self:CalculatePowerStat(self.powerStat, calcFunc({ addNodes = pathNodes }, useFullDPS), calcBase)
end
end
node.power.singleStat = m_max(node.power.singleStat or 0, node.power.masteryEffects[effect.id].singleStat)
node.power.pathPower = m_max(node.power.pathPower or 0, node.power.masteryEffects[effect.id].pathPower)
elseif not self.powerStat or not self.powerStat.ignoreForNodes then
node.power.masteryEffects[effect.id].offence, node.power.masteryEffects[effect.id].defence = self:CalculateCombinedOffDefStat(output, calcBase)
node.power.masteryEffects[effect.id].singleStat = node.power.masteryEffects[effect.id].offence
if node.path and not node.ascendancyName then
newPowerMax.offence = m_max(newPowerMax.offence, node.power.masteryEffects[effect.id].offence)
newPowerMax.defence = m_max(newPowerMax.defence, node.power.masteryEffects[effect.id].defence)
newPowerMax.offencePerPoint = m_max(newPowerMax.offencePerPoint, node.power.masteryEffects[effect.id].offence / node.pathDist)
newPowerMax.defencePerPoint = m_max(newPowerMax.defencePerPoint, node.power.masteryEffects[effect.id].defence / node.pathDist)
end
node.power.offence = m_max(node.power.offence or 0, node.power.masteryEffects[effect.id].offence)
node.power.defence = m_max(node.power.defence or 0, node.power.masteryEffects[effect.id].defence)
node.power.singleStat = m_max(node.power.singleStat or 0, node.power.masteryEffects[effect.id].singleStat)
end
end
nodeIndex = nodeIndex + 1
if coroutine.running() and GetTime() - start > 100 then
if self.build.powerBuilderProgressCallback then
self.build.powerBuilderProgressCallback(m_floor(nodeIndex/total*100))
end
coroutine.yield()
start = GetTime()
end
end
end
end
end
end
end

-- Calculate the impact of every cluster notable
-- used for the power report screen
for nodeName, node in pairs(self.build.spec.tree.clusterNodeMap) do
Expand Down
9 changes: 9 additions & 0 deletions src/Classes/PowerReportListControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ local PowerReportListClass = newClass("PowerReportListControl", "ListControl", f
self.colLabels = true
self.nodeSelectCallback = nodeSelectCallback
self.showClusters = false
self.showMasteries = true
self.allocated = false
self.label = "Building Tree..."

Expand All @@ -34,6 +35,11 @@ local PowerReportListClass = newClass("PowerReportListControl", "ListControl", f
self:ReList()
self:ReSort(3) -- Sort by power
end)
self.controls.masteryCheck = new("CheckBoxControl", {"RIGHT", self.controls.filterSelect, "LEFT"}, {-120, 0, 18}, "Show Masteries:", function(state)
self.showMasteries = state
self:ReList()
self:ReSort(3) -- Sort by power
end, nil, true)
end)

function PowerReportListClass:SetReport(stat, report)
Expand Down Expand Up @@ -103,6 +109,9 @@ function PowerReportListClass:ReList()
if self.allocated then
insert = item.allocated
end
if not self.showMasteries and item.type == "Mastery" then
insert = false
end

if insert then
t_insert(self.list, item)
Expand Down
47 changes: 47 additions & 0 deletions src/Classes/TreeTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,53 @@ function TreeTabClass:BuildPowerReportList(currentStat)
type = node.type,
pathDist = pathDist
})
elseif node.type == "Mastery" and node.power.masteryEffects and not node.ascendancyName then
local pathDist
if isAlloc then
pathDist = #(node.depends or { }) == 0 and 1 or #node.depends
else
pathDist = #(node.path or { }) == 0 and 1 or #node.path
end

for _, masteryEffect in ipairs(node.masteryEffects or { }) do
local effect = self.build.spec.tree.masteryEffects[masteryEffect.effect]
local effectPower = node.power.masteryEffects[masteryEffect.effect]
if effect and effectPower then
local nodePower = (effectPower.singleStat or 0) * ((displayStat.pc or displayStat.mod) and 100 or 1)
local pathPower = ((effectPower.pathPower or effectPower.singleStat or 0) / pathDist) * ((displayStat.pc or displayStat.mod) and 100 or 1)
local nodePowerStr = s_format("%"..displayStat.fmt, nodePower)
local pathPowerStr = s_format("%"..displayStat.fmt, pathPower)

nodePowerStr = formatNumSep(nodePowerStr)
pathPowerStr = formatNumSep(pathPowerStr)

if (nodePower > 0 and not displayStat.lowerIsBetter) or (nodePower < 0 and displayStat.lowerIsBetter) then
nodePowerStr = colorCodes.POSITIVE .. nodePowerStr
elseif (nodePower < 0 and not displayStat.lowerIsBetter) or (nodePower > 0 and displayStat.lowerIsBetter) then
nodePowerStr = colorCodes.NEGATIVE .. nodePowerStr
end
if (pathPower > 0 and not displayStat.lowerIsBetter) or (pathPower < 0 and displayStat.lowerIsBetter) then
pathPowerStr = colorCodes.POSITIVE .. pathPowerStr
elseif (pathPower < 0 and not displayStat.lowerIsBetter) or (pathPower > 0 and displayStat.lowerIsBetter) then
pathPowerStr = colorCodes.NEGATIVE .. pathPowerStr
end

local effectLabelParts = isAlloc and not node.allMasteryOptions and node.sd or effect.stats or effect.sd
t_insert(report, {
name = effectLabelParts and node.dn..": "..t_concat(effectLabelParts, " / ") or node.dn,
power = nodePower,
powerStr = nodePowerStr,
pathPower = pathPower,
pathPowerStr = pathPowerStr,
allocated = isAlloc,
id = node.id,
x = node.x,
y = node.y,
type = node.type,
pathDist = pathDist
})
end
end
end
end

Expand Down