Skip to content

Commit 0030663

Browse files
committed
Add support for level 80+ apex talents in icy-veins calculator link imports
1 parent 086a66d commit 0030663

File tree

2 files changed

+91
-18
lines changed

2 files changed

+91
-18
lines changed

TalentTreeViewer_TWW/IcyVeinsImport.lua

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,16 @@ local LibTT = LibStub('LibTalentTree-1.0');
1313

1414
local L = LibStub('AceLocale-3.0'):GetLocale(name)
1515

16+
IcyVeinsImport.TREE_TYPE_CLASS = 'class';
17+
IcyVeinsImport.TREE_TYPE_SPEC = 'spec';
18+
IcyVeinsImport.TREE_TYPE_SUB_TREE = 'subTree';
19+
1620
IcyVeinsImport.bitWidthSpecID = 12;
1721
IcyVeinsImport.bitWidthNodeIndex = 6;
22+
--- @private
23+
IcyVeinsImport.classAndSpecNodeCache = {};
24+
25+
local apexTalentLevels = { 81, 84, 87, 90 };
1826

1927
--- @param text string
2028
--- @return boolean
@@ -62,8 +70,10 @@ function IcyVeinsImport:ParseUrl(url)
6270
dataSection = dataSection:gsub(':', '/'); -- IcyVeins uses base64 with `:`, whereas wow uses `/`
6371

6472
local sections = { string.split('-', dataSection) };
73+
local sectionCount = #sections;
6574
local specIDString, classString, specString = sections[1], sections[2], sections[3];
66-
local heroString = sections[#sections - 1]; -- there seems to sometimes be an additional segment in the middle? not sure what that is
75+
local apexTalentString = sectionCount == 6 and sections[4] or "";
76+
local heroString = sectionCount == 6 and sections[5] or sections[4];
6777
local specIDStream = ExportUtil.MakeImportDataStream(specIDString);
6878
local specID = tonumber(specIDStream:ExtractValue(self.bitWidthSpecID));
6979
local classID = specID and C_SpecializationInfo.GetClassIDFromSpecID(specID);
@@ -75,35 +85,50 @@ function IcyVeinsImport:ParseUrl(url)
7585
end
7686
local classStream = ExportUtil.MakeImportDataStream(classString);
7787
local specStream = ExportUtil.MakeImportDataStream(specString);
88+
local apexTalentStream = ExportUtil.MakeImportDataStream(apexTalentString);
7889
local heroStream = ExportUtil.MakeImportDataStream(heroString);
7990
local selectedSubTreeID;
8091
if heroStream:GetNumberOfBits() > 0 then
8192
local heroTreeIndex = heroStream:ExtractValue(1) + 1;
8293
selectedSubTreeID = LibTT:GetSubTreeIDsForSpecID(specID)[heroTreeIndex];
8394
end
8495

85-
local classNodes, specNodes, heroNodes = self:GetClassAndSpecNodeIDs(specID, treeID, selectedSubTreeID);
96+
local classNodes, specNodes, apexNodes, heroNodes = self:GetClassAndSpecNodeIDs(specID, treeID, selectedSubTreeID);
8697

8798
local levelingBuild = { entries = {}, selectedSubTreeID = selectedSubTreeID };
88-
levelingBuild.entries[1] = self:ParseDataSegment(10, 2, classStream, classNodes);
89-
levelingBuild.entries[2] = self:ParseDataSegment(11, 2, specStream, specNodes);
99+
levelingBuild.entries[1] = self:ParseDataSegment(classStream, classNodes, self.TREE_TYPE_CLASS);
100+
levelingBuild.entries[2] = self:ParseDataSegment(specStream, specNodes, self.TREE_TYPE_SPEC);
101+
local apexEntries = apexTalentString ~= "" and self:ParseDataSegment(apexTalentStream, apexNodes, self.TREE_TYPE_SPEC) or nil;
102+
if apexEntries and next(apexEntries) then
103+
local count = table.count(apexEntries);
104+
local nodeID = apexNodes[1];
105+
for _, level in ipairs_reverse(apexTalentLevels) do
106+
if not levelingBuild.entries[2][level] then
107+
levelingBuild.entries[2][level] = {
108+
nodeID = nodeID,
109+
targetRank = count,
110+
};
111+
count = count - 1;
112+
if count == 0 then break; end
113+
end
114+
end
115+
end
90116
if heroNodes and selectedSubTreeID then
91-
levelingBuild.entries[selectedSubTreeID] = self:ParseDataSegment(71, 1, heroStream, heroNodes);
117+
levelingBuild.entries[selectedSubTreeID] = self:ParseDataSegment(heroStream, heroNodes, self.TREE_TYPE_SUB_TREE);
92118
end
93119

94120
return classID, specID, levelingBuild;
95121
end
96122

97-
--- @param startingLevel number
98-
--- @param levelMultiplier number
99123
--- @param dataStream ImportDataStreamMixin
100124
--- @param nodes number[]
125+
--- @param treeType 'class'|'spec'|'subTree'
101126
--- @return table<number, TalentViewer_LevelingBuildEntry> # [level] = entry
102127
--- @private
103-
function IcyVeinsImport:ParseDataSegment(startingLevel, levelMultiplier, dataStream, nodes)
104-
local level = startingLevel;
128+
function IcyVeinsImport:ParseDataSegment(dataStream, nodes, treeType)
105129
local rankByNodeID = {};
106130
local levelingOrder = {};
131+
local currencySpent = 0;
107132

108133
while (dataStream:GetNumberOfBits() - dataStream.currentExtractedBits) > self.bitWidthNodeIndex do
109134
local success, nodeIndex = pcall(function() return dataStream:ExtractValue(self.bitWidthNodeIndex); end);
@@ -112,11 +137,12 @@ function IcyVeinsImport:ParseDataSegment(startingLevel, levelMultiplier, dataStr
112137
nodeIndex = nodeIndex + 1; -- 0-based to 1-based
113138
local nodeID = nodes[nodeIndex];
114139
if not nodeID then
115-
print(L['Error while importing IcyVeins URL: Could not find node for index'], nodeIndex);
140+
print(L['Error while importing IcyVeins URL: Could not find node for index'], nodeIndex, '-', treeType);
116141
if DevTool and DevTool.AddData then
117142
DevTool:AddData({
118143
nodeIndex = nodeIndex,
119144
nodes = nodes,
145+
treeType = treeType,
120146
}, 'Error while importing IcyVeins URL: Could not find node for index')
121147
end
122148
else
@@ -128,43 +154,87 @@ function IcyVeinsImport:ParseDataSegment(startingLevel, levelMultiplier, dataStr
128154
entry = nodeInfo.entryIDs and nodeInfo.entryIDs[choiceIndex] or nil;
129155
end
130156
rankByNodeID[nodeID] = (rankByNodeID[nodeID] or 0) + 1;
157+
currencySpent = currencySpent + 1;
158+
local level = self:GetRequiredLevelForCurrencySpent(currencySpent, treeType);
131159

132160
levelingOrder[level] = {
133161
nodeID = nodeID,
134162
entryID = entry,
135163
targetRank = rankByNodeID[nodeID],
136164
};
137-
level = level + levelMultiplier;
138165
end
139166
end
140167

141168
return levelingOrder;
142169
end
143170

144-
--- @private
145-
IcyVeinsImport.classAndSpecNodeCache = {};
171+
--- @param spent number
172+
--- @param treeType 'class'|'spec'|'subTree'
173+
--- @return number requiredLevel
174+
function IcyVeinsImport:GetRequiredLevelForCurrencySpent(spent, treeType)
175+
local requiredLevel;
176+
if self.TREE_TYPE_CLASS == treeType then
177+
-- starts at 8 (so that first talent point results in level 10)
178+
-- 10-70 = spendingUnderOrEqual31 * 2
179+
-- 81-90 = spendingOver31 * 3
180+
-- ignore apex talents for now
181+
if spent > 31 then
182+
requiredLevel = 79 + ((spent - 31) * 3);
183+
else
184+
requiredLevel = 8 + (spent * 2);
185+
end
186+
elseif self.TREE_TYPE_SUB_TREE == treeType then
187+
-- starts at 70 (so that first talent point results in level 71)
188+
-- 71-80 = spendingUnderOrEqual10 * 1
189+
-- 81-90 = spendingOver10 * 3
190+
if spent > 10 then
191+
requiredLevel = 80 + ((spent - 10) * 3);
192+
else
193+
requiredLevel = 70 + spent;
194+
end
195+
elseif self.TREE_TYPE_SPEC == treeType then
196+
-- if apex talent selected: minimum level is 80 regardless of spending
197+
-- starts at 9 (so that first talent point results in level 11)
198+
-- 11-70 = spendingUnderOrEqual30 * 2
199+
-- 81-90 = spendingOver30 * 3
200+
if spent > 30 then
201+
requiredLevel = 78 + ((spent - 30) * 3);
202+
else
203+
requiredLevel = 9 + (spent * 2);
204+
end
205+
else
206+
error('Invalid currency type: ' .. tostring(treeType));
207+
end
208+
209+
return math.max(10, requiredLevel);
210+
end
211+
146212
--- @param specID number
147213
--- @param treeID number
148214
--- @param selectedSubTreeID number?
149-
--- @return number[], number[], nil|number[] # classNodes, specNodes, heroNodes (if applicable)
215+
--- @return number[], number[], number[], number[]|nil # classNodes, specNodes, apexNodes, heroNodes (if applicable)
150216
--- @private
151217
function IcyVeinsImport:GetClassAndSpecNodeIDs(specID, treeID, selectedSubTreeID)
152218
if self.classAndSpecNodeCache[specID] then
153-
local classNodes, specNodes, heroNodesByTree = unpack(self.classAndSpecNodeCache[specID]);
154-
return classNodes, specNodes, heroNodesByTree[selectedSubTreeID] or nil;
219+
local classNodes, specNodes, apexNodes, heroNodesByTree = unpack(self.classAndSpecNodeCache[specID]);
220+
221+
return classNodes, specNodes, apexNodes, heroNodesByTree[selectedSubTreeID] or nil;
155222
end
156223

157224
local nodes = C_Traits.GetTreeNodes(treeID);
158225

159226
local classNodes = {};
160227
local specNodes = {};
228+
local apexNodes = {};
161229
local heroNodesByTree = {};
162230

163231
for _, nodeID in ipairs(nodes or {}) do
164232
local nodeInfo = LibTT:GetNodeInfo(nodeID);
165233
if LibTT:IsNodeVisibleForSpec(specID, nodeID) and nodeInfo.maxRanks > 0 then
166234
if nodeInfo.isSubTreeSelection then
167235
-- skip
236+
elseif nodeInfo.isApexTalent then
237+
table.insert(apexNodes, nodeID);
168238
elseif nodeInfo.subTreeID then
169239
heroNodesByTree[nodeInfo.subTreeID] = heroNodesByTree[nodeInfo.subTreeID] or {};
170240
table.insert(heroNodesByTree[nodeInfo.subTreeID], nodeID);
@@ -181,9 +251,10 @@ function IcyVeinsImport:GetClassAndSpecNodeIDs(specID, treeID, selectedSubTreeID
181251

182252
table.sort(classNodes);
183253
table.sort(specNodes);
254+
table.sort(apexNodes);
184255

185-
self.classAndSpecNodeCache[specID] = {classNodes, specNodes, heroNodesByTree};
256+
self.classAndSpecNodeCache[specID] = { classNodes, specNodes, apexNodes, heroNodesByTree };
186257

187-
return classNodes, specNodes, heroNodesByTree[selectedSubTreeID] or nil;
258+
return classNodes, specNodes, apexNodes, heroNodesByTree[selectedSubTreeID] or nil;
188259
end
189260

TalentTreeViewer_TWW/mixins/LevelingOrderMixin.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ ns.mixins.LevelingOrderMixin = LevelingOrderMixin;
1111
--- @param order number[]
1212
function LevelingOrderMixin:SetOrder(order)
1313
self.order = CopyTable(order);
14+
table.sort(self.order);
1415
self:UpdateText();
1516
end
1617
--- @param level number
1718
function LevelingOrderMixin:AppendToOrder(level)
1819
table.insert(self.order, level);
20+
table.sort(self.order);
1921
self:UpdateText();
2022
end
2123
function LevelingOrderMixin:RemoveLastOrder()

0 commit comments

Comments
 (0)