@@ -13,8 +13,16 @@ local LibTT = LibStub('LibTalentTree-1.0');
1313
1414local 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+
1620IcyVeinsImport .bitWidthSpecID = 12 ;
1721IcyVeinsImport .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 ;
95121end
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 ;
142169end
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
151217function 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 ;
188259end
189260
0 commit comments