Skip to content

Commit b6b954d

Browse files
committed
add masteries to power report
1 parent 57185ad commit b6b954d

4 files changed

Lines changed: 219 additions & 4 deletions

File tree

spec/System/TestTreeTab_spec.lua

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
describe("TreeTab", function()
2+
before_each(function()
3+
newBuild()
4+
end)
5+
6+
teardown(function()
7+
-- newBuild() resets the shared build state for the next test.
8+
end)
9+
10+
it("adds separate power report entries for mastery effects", function()
11+
local treeTab = build.treeTab
12+
local parentNode = { id = 2 }
13+
local masteryNode = {
14+
id = 1,
15+
type = "Mastery",
16+
dn = "Two Hand Mastery",
17+
power = {
18+
masteryEffects = {
19+
[101] = { singleStat = 10, pathPower = 10 },
20+
[102] = { singleStat = 20, pathPower = 20 },
21+
},
22+
},
23+
masteryEffects = {
24+
{ effect = 101 },
25+
{ effect = 102 },
26+
},
27+
path = { parentNode, false },
28+
x = 10,
29+
y = 20,
30+
}
31+
masteryNode.path[2] = masteryNode
32+
33+
treeTab.build.displayStats = {
34+
{ stat = "Damage", label = "Damage", fmt = ".1f" },
35+
}
36+
treeTab.build.spec.nodes = {
37+
[masteryNode.id] = masteryNode,
38+
}
39+
treeTab.build.spec.masterySelections = { }
40+
treeTab.build.spec.tree.clusterNodeMap = { }
41+
treeTab.build.spec.tree.masteryEffects = {
42+
[101] = { id = 101, sd = { "Gain 10 Damage" }, stats = { "Gain 10 Damage" } },
43+
[102] = { id = 102, sd = { "Gain 20 Damage" }, stats = { "Gain 20 Damage" } },
44+
}
45+
treeTab.build.calcsTab.mainEnv = { grantedPassives = { } }
46+
47+
local report = treeTab:BuildPowerReportList({ stat = "Damage", label = "Damage" })
48+
49+
assert.are.same(2, #report)
50+
assert.are.same("Mastery", report[1].type)
51+
assert.are.same("Two Hand Mastery: Gain 20 Damage", report[1].name)
52+
assert.are.same(20, report[1].power)
53+
assert.are.same(2, report[1].pathDist)
54+
assert.are.same(10, report[2].power)
55+
assert.are.same("Two Hand Mastery: Gain 10 Damage", report[2].name)
56+
end)
57+
end)

src/Classes/CalcsTab.lua

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,18 +492,46 @@ function CalcsTabClass:PowerBuilder()
492492
if coroutine.running() then
493493
coroutine.yield()
494494
end
495+
496+
local function buildMasteryEffectNode(node, effect)
497+
local effectNode = {
498+
id = node.id,
499+
type = node.type,
500+
name = node.name,
501+
sd = { },
502+
}
503+
for i, sd in ipairs(effect.sd or { }) do
504+
effectNode.sd[i] = sd
505+
end
506+
self.build.spec.tree:ProcessStats(effectNode)
507+
return effectNode
508+
end
495509

496510
local start = GetTime()
497511
local nodeIndex = 0
498512
local total = 0
499513

500514
for nodeId, node in pairs(self.build.spec.nodes) do
501515
wipeTable(node.power)
516+
if node.type == "Mastery" then
517+
node.power.masteryEffects = { }
518+
end
502519
if node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
503-
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
504-
distanceMap[node.pathDist or 1000][nodeId] = node
505-
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
506-
total = total + 1
520+
if node.type == "Mastery" and node.allMasteryOptions then
521+
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
522+
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
523+
local assignedNodeId = isValueInTable(self.build.spec.masterySelections, masteryEffect.effect)
524+
if not assignedNodeId or assignedNodeId == node.id then
525+
total = total + 1
526+
end
527+
end
528+
end
529+
else
530+
distanceMap[node.pathDist or 1000] = distanceMap[node.pathDist or 1000] or { }
531+
distanceMap[node.pathDist or 1000][nodeId] = node
532+
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
533+
total = total + 1
534+
end
507535
end
508536
end
509537
end
@@ -572,6 +600,17 @@ function CalcsTabClass:PowerBuilder()
572600
end
573601
end
574602
end
603+
if node.type == "Mastery" then
604+
local selectedEffectId = self.build.spec.masterySelections[node.id]
605+
if selectedEffectId then
606+
node.power.masteryEffects[selectedEffectId] = {
607+
singleStat = node.power.singleStat,
608+
pathPower = node.power.pathPower,
609+
offence = node.power.offence,
610+
defence = node.power.defence,
611+
}
612+
end
613+
end
575614
nodeIndex = nodeIndex + 1
576615
if coroutine.running() and GetTime() - start > 100 then
577616
if self.build.powerBuilderProgressCallback then
@@ -583,6 +622,69 @@ function CalcsTabClass:PowerBuilder()
583622
end
584623
end
585624

625+
for nodeId, node in pairs(self.build.spec.nodes) do
626+
if node.type == "Mastery" and node.allMasteryOptions and node.modKey ~= "" and not self.mainEnv.grantedPassives[nodeId] then
627+
if not (self.nodePowerMaxDepth and self.nodePowerMaxDepth < node.pathDist) then
628+
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
629+
local assignedNodeId = isValueInTable(self.build.spec.masterySelections, masteryEffect.effect)
630+
if not assignedNodeId or assignedNodeId == node.id then
631+
local effect = self.build.spec.tree.masteryEffects[masteryEffect.effect]
632+
if effect then
633+
local effectNode = buildMasteryEffectNode(node, effect)
634+
if effectNode.modKey ~= "" then
635+
if not cache[effectNode.modKey] then
636+
cache[effectNode.modKey] = calcFunc({ addNodes = { [effectNode] = true } }, useFullDPS)
637+
end
638+
local output = cache[effectNode.modKey]
639+
node.power.masteryEffects[effect.id] = { }
640+
if self.powerStat and self.powerStat.stat and not self.powerStat.ignoreForNodes then
641+
node.power.masteryEffects[effect.id].singleStat = self:CalculatePowerStat(self.powerStat, output, calcBase)
642+
node.power.masteryEffects[effect.id].pathPower = node.power.masteryEffects[effect.id].singleStat
643+
if node.path and not node.ascendancyName then
644+
newPowerMax.singleStat = m_max(newPowerMax.singleStat, node.power.masteryEffects[effect.id].singleStat)
645+
local pathNodes = {
646+
[effectNode] = true
647+
}
648+
for _, pathNode in pairs(node.path) do
649+
if pathNode ~= node then
650+
pathNodes[pathNode] = true
651+
end
652+
end
653+
if node.pathDist > 1 then
654+
node.power.masteryEffects[effect.id].pathPower = self:CalculatePowerStat(self.powerStat, calcFunc({ addNodes = pathNodes }, useFullDPS), calcBase)
655+
end
656+
end
657+
node.power.singleStat = m_max(node.power.singleStat or 0, node.power.masteryEffects[effect.id].singleStat)
658+
node.power.pathPower = m_max(node.power.pathPower or 0, node.power.masteryEffects[effect.id].pathPower)
659+
elseif not self.powerStat or not self.powerStat.ignoreForNodes then
660+
node.power.masteryEffects[effect.id].offence, node.power.masteryEffects[effect.id].defence = self:CalculateCombinedOffDefStat(output, calcBase)
661+
node.power.masteryEffects[effect.id].singleStat = node.power.masteryEffects[effect.id].offence
662+
if node.path and not node.ascendancyName then
663+
newPowerMax.offence = m_max(newPowerMax.offence, node.power.masteryEffects[effect.id].offence)
664+
newPowerMax.defence = m_max(newPowerMax.defence, node.power.masteryEffects[effect.id].defence)
665+
newPowerMax.offencePerPoint = m_max(newPowerMax.offencePerPoint, node.power.masteryEffects[effect.id].offence / node.pathDist)
666+
newPowerMax.defencePerPoint = m_max(newPowerMax.defencePerPoint, node.power.masteryEffects[effect.id].defence / node.pathDist)
667+
end
668+
node.power.offence = m_max(node.power.offence or 0, node.power.masteryEffects[effect.id].offence)
669+
node.power.defence = m_max(node.power.defence or 0, node.power.masteryEffects[effect.id].defence)
670+
node.power.singleStat = m_max(node.power.singleStat or 0, node.power.masteryEffects[effect.id].singleStat)
671+
end
672+
end
673+
nodeIndex = nodeIndex + 1
674+
if coroutine.running() and GetTime() - start > 100 then
675+
if self.build.powerBuilderProgressCallback then
676+
self.build.powerBuilderProgressCallback(m_floor(nodeIndex/total*100))
677+
end
678+
coroutine.yield()
679+
start = GetTime()
680+
end
681+
end
682+
end
683+
end
684+
end
685+
end
686+
end
687+
586688
-- Calculate the impact of every cluster notable
587689
-- used for the power report screen
588690
for nodeName, node in pairs(self.build.spec.tree.clusterNodeMap) do

src/Classes/PowerReportListControl.lua

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ local PowerReportListClass = newClass("PowerReportListControl", "ListControl", f
2323
self.colLabels = true
2424
self.nodeSelectCallback = nodeSelectCallback
2525
self.showClusters = false
26+
self.showMasteries = true
2627
self.allocated = false
2728
self.label = "Building Tree..."
2829

@@ -34,6 +35,11 @@ local PowerReportListClass = newClass("PowerReportListControl", "ListControl", f
3435
self:ReList()
3536
self:ReSort(3) -- Sort by power
3637
end)
38+
self.controls.masteryCheck = new("CheckBoxControl", {"RIGHT", self.controls.filterSelect, "LEFT"}, {-120, 0, 18}, "Show Masteries:", function(state)
39+
self.showMasteries = state
40+
self:ReList()
41+
self:ReSort(3) -- Sort by power
42+
end, nil, true)
3743
end)
3844

3945
function PowerReportListClass:SetReport(stat, report)
@@ -103,6 +109,9 @@ function PowerReportListClass:ReList()
103109
if self.allocated then
104110
insert = item.allocated
105111
end
112+
if not self.showMasteries and item.type == "Mastery" then
113+
insert = false
114+
end
106115

107116
if insert then
108117
t_insert(self.list, item)

src/Classes/TreeTab.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,53 @@ function TreeTabClass:BuildPowerReportList(currentStat)
10931093
type = node.type,
10941094
pathDist = pathDist
10951095
})
1096+
elseif node.type == "Mastery" and node.power.masteryEffects and not node.ascendancyName then
1097+
local pathDist
1098+
if isAlloc then
1099+
pathDist = #(node.depends or { }) == 0 and 1 or #node.depends
1100+
else
1101+
pathDist = #(node.path or { }) == 0 and 1 or #node.path
1102+
end
1103+
1104+
for _, masteryEffect in ipairs(node.masteryEffects or { }) do
1105+
local effect = self.build.spec.tree.masteryEffects[masteryEffect.effect]
1106+
local effectPower = node.power.masteryEffects[masteryEffect.effect]
1107+
if effect and effectPower then
1108+
local nodePower = (effectPower.singleStat or 0) * ((displayStat.pc or displayStat.mod) and 100 or 1)
1109+
local pathPower = ((effectPower.pathPower or effectPower.singleStat or 0) / pathDist) * ((displayStat.pc or displayStat.mod) and 100 or 1)
1110+
local nodePowerStr = s_format("%"..displayStat.fmt, nodePower)
1111+
local pathPowerStr = s_format("%"..displayStat.fmt, pathPower)
1112+
1113+
nodePowerStr = formatNumSep(nodePowerStr)
1114+
pathPowerStr = formatNumSep(pathPowerStr)
1115+
1116+
if (nodePower > 0 and not displayStat.lowerIsBetter) or (nodePower < 0 and displayStat.lowerIsBetter) then
1117+
nodePowerStr = colorCodes.POSITIVE .. nodePowerStr
1118+
elseif (nodePower < 0 and not displayStat.lowerIsBetter) or (nodePower > 0 and displayStat.lowerIsBetter) then
1119+
nodePowerStr = colorCodes.NEGATIVE .. nodePowerStr
1120+
end
1121+
if (pathPower > 0 and not displayStat.lowerIsBetter) or (pathPower < 0 and displayStat.lowerIsBetter) then
1122+
pathPowerStr = colorCodes.POSITIVE .. pathPowerStr
1123+
elseif (pathPower < 0 and not displayStat.lowerIsBetter) or (pathPower > 0 and displayStat.lowerIsBetter) then
1124+
pathPowerStr = colorCodes.NEGATIVE .. pathPowerStr
1125+
end
1126+
1127+
local effectLabelParts = isAlloc and not node.allMasteryOptions and node.sd or effect.stats or effect.sd
1128+
t_insert(report, {
1129+
name = effectLabelParts and node.dn..": "..t_concat(effectLabelParts, " / ") or node.dn,
1130+
power = nodePower,
1131+
powerStr = nodePowerStr,
1132+
pathPower = pathPower,
1133+
pathPowerStr = pathPowerStr,
1134+
allocated = isAlloc,
1135+
id = node.id,
1136+
x = node.x,
1137+
y = node.y,
1138+
type = node.type,
1139+
pathDist = pathDist
1140+
})
1141+
end
1142+
end
10961143
end
10971144
end
10981145

0 commit comments

Comments
 (0)