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
63 changes: 63 additions & 0 deletions spec/System/TestTreeTab_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
describe("TreeTab", function()
local originalClusterNodeMap
local originalMasteryEffects

before_each(function()
newBuild()
originalClusterNodeMap = build.spec.tree.clusterNodeMap
originalMasteryEffects = build.spec.tree.masteryEffects
end)

after_each(function()
build.spec.tree.clusterNodeMap = originalClusterNodeMap
build.spec.tree.masteryEffects = originalMasteryEffects
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)
150 changes: 124 additions & 26 deletions src/Classes/CalcsTab.lua
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ function CalcsTabClass:PowerBuilder()
local cache = { }
local distanceMap = { }
local distanceList = { }
local masteryNodeList = { }
local newPowerMax = {
singleStat = 0,
offence = 0,
Expand All @@ -492,18 +493,73 @@ 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 function masteryEffectCanBeAssignedToNode(node, masteryEffect)
local assignedNodeId = isValueInTable(self.build.spec.masterySelections, masteryEffect.effect)
return not assignedNodeId or assignedNodeId == node.id
end

local function calculateAddNodePower(power, node, output, buildPathNodes)
if self.powerStat and self.powerStat.stat and not self.powerStat.ignoreForNodes then
power.singleStat = self:CalculatePowerStat(self.powerStat, output, calcBase)
if node.path and not node.ascendancyName then
newPowerMax.singleStat = m_max(newPowerMax.singleStat, power.singleStat)
power.pathPower = power.singleStat
if node.pathDist > 1 then
power.pathPower = self:CalculatePowerStat(self.powerStat, calcFunc({ addNodes = buildPathNodes() }, useFullDPS), calcBase)
end
end
elseif not self.powerStat or not self.powerStat.ignoreForNodes then
power.offence, power.defence = self:CalculateCombinedOffDefStat(output, calcBase)
power.singleStat = power.offence
if node.path and not node.ascendancyName then
newPowerMax.offence = m_max(newPowerMax.offence, power.offence)
newPowerMax.defence = m_max(newPowerMax.defence, power.defence)
newPowerMax.offencePerPoint = m_max(newPowerMax.offencePerPoint, power.offence / node.pathDist)
newPowerMax.defencePerPoint = m_max(newPowerMax.defencePerPoint, power.defence / node.pathDist)
end
end
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 = { }
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NB: adding a new table to node.power since a single power per node can't represent unallocated mastery options

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
t_insert(masteryNodeList, node)
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
if masteryEffectCanBeAssignedToNode(node, masteryEffect) 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 All @@ -530,29 +586,13 @@ function CalcsTabClass:PowerBuilder()
cache[node.modKey] = calcFunc({ addNodes = { [node] = true } }, useFullDPS)
end
local output = cache[node.modKey]
if self.powerStat and self.powerStat.stat and not self.powerStat.ignoreForNodes then
node.power.singleStat = self:CalculatePowerStat(self.powerStat, output, calcBase)
if node.path and not node.ascendancyName then
newPowerMax.singleStat = m_max(newPowerMax.singleStat, node.power.singleStat)
node.power.pathPower = node.power.singleStat
local pathNodes = { }
for _, node in pairs(node.path) do
pathNodes[node] = true
end
if node.pathDist > 1 then
node.power.pathPower = self:CalculatePowerStat(self.powerStat, calcFunc({ addNodes = pathNodes }, useFullDPS), calcBase)
end
end
elseif not self.powerStat or not self.powerStat.ignoreForNodes then
node.power.offence, node.power.defence = self:CalculateCombinedOffDefStat(output, calcBase)
node.power.singleStat = node.power.offence
if node.path and not node.ascendancyName then
newPowerMax.offence = m_max(newPowerMax.offence, node.power.offence)
newPowerMax.defence = m_max(newPowerMax.defence, node.power.defence)
newPowerMax.offencePerPoint = m_max(newPowerMax.offencePerPoint, node.power.offence / node.pathDist)
newPowerMax.defencePerPoint = m_max(newPowerMax.defencePerPoint, node.power.defence / node.pathDist)
calculateAddNodePower(node.power, node, output, function()
local pathNodes = { }
for _, pathNode in pairs(node.path) do
pathNodes[pathNode] = true
end
end
return pathNodes
end)
elseif node.alloc and node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
if not cache[node.modKey.."_remove"] then
cache[node.modKey.."_remove"] = calcFunc({ removeNodes = { [node] = true } }, useFullDPS)
Expand All @@ -572,6 +612,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 +634,53 @@ function CalcsTabClass:PowerBuilder()
end
end

for _, node in ipairs(masteryNodeList) do
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
if masteryEffectCanBeAssignedToNode(node, masteryEffect) 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] = { }
local effectPower = node.power.masteryEffects[effect.id]
calculateAddNodePower(effectPower, node, output, function()
local pathNodes = {
[effectNode] = true
}
for _, pathNode in pairs(node.path) do
if pathNode ~= node then
pathNodes[pathNode] = true
end
end
return pathNodes
end)
if self.powerStat and self.powerStat.stat and not self.powerStat.ignoreForNodes then
effectPower.pathPower = effectPower.pathPower or effectPower.singleStat
node.power.singleStat = m_max(node.power.singleStat or 0, effectPower.singleStat)
node.power.pathPower = m_max(node.power.pathPower or 0, effectPower.pathPower)
elseif not self.powerStat or not self.powerStat.ignoreForNodes then
node.power.offence = m_max(node.power.offence or 0, effectPower.offence)
node.power.defence = m_max(node.power.defence or 0, effectPower.defence)
node.power.singleStat = m_max(node.power.singleStat or 0, effectPower.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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opted to duplicate the power calc in the loop here instead of a bigger refactor for readability

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to not duplicate this logic, actually.

-- 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
Loading
Loading