@@ -102,59 +102,129 @@ do
102102
103103 local emptyTable = {}
104104
105- function TalentViewer_ClassTalentTalentsTabMixin :GetAndCacheNodeInfo (nodeID )
106- local nodeInfo = LibTalentTree :GetLibNodeInfo (TalentViewer .treeId , nodeID )
107- if not nodeInfo then nodeInfo = LibTalentTree :GetNodeInfo (TalentViewer .treeId , nodeID ) end
108- if nodeInfo .ID ~= nodeID then return nil end
109- local isGranted = LibTalentTree :IsNodeGrantedForSpec (TalentViewer .selectedSpecId , nodeID )
110- local isChoiceNode = # nodeInfo .entryIDs > 1
111- local selectedEntryId = isChoiceNode and TalentViewer :GetSelectedEntryId (nodeID ) or nil
112-
113- local meetsEdgeRequirements = true
114- local meetsGateRequirements = true
115- if not TalentViewer .db .ignoreRestrictions then
116- for _ , conditionId in ipairs (nodeInfo .conditionIDs ) do
117- local condInfo = self :GetAndCacheCondInfo (conditionId )
118- if condInfo .isGate and not condInfo .isMet then meetsGateRequirements = false end
105+ local nodeEdgesCache = {}
106+ local function getNodeEdges (nodeID )
107+ if not nodeEdgesCache [nodeID ] then
108+ nodeEdgesCache [nodeID ] = LibTalentTree :GetNodeEdges (TalentViewer .treeId , nodeID ) or emptyTable
109+ end
110+ return nodeEdgesCache [nodeID ]
111+ end
112+
113+ local incomingNodeEdgesCache = {}
114+ local function getIncomingNodeEdges (nodeID )
115+ local function getIncomingNodeEdgesCallback (nodeID )
116+ local incomingEdges = {}
117+ for _ , treeNodeId in ipairs (C_Traits .GetTreeNodes (TalentViewer .treeId )) do
118+ local edges = getNodeEdges (treeNodeId )
119+ for _ , edge in ipairs (edges ) do
120+ if edge .targetNode == nodeID then
121+ table.insert (incomingEdges , treeNodeId )
122+ end
123+ end
119124 end
125+ return incomingEdges
120126 end
121127
122- local isAvailable = meetsEdgeRequirements and meetsGateRequirements
128+ return GetOrCreateTableEntryByCallback (incomingNodeEdgesCache , nodeID , getIncomingNodeEdgesCallback )
129+ end
123130
124- nodeInfo .activeRank = isGranted
125- and nodeInfo .maxRanks
126- or ((isChoiceNode and selectedEntryId and 1 ) or TalentViewer :GetActiveRank (nodeID ))
127- nodeInfo .currentRank = nodeInfo .activeRank
128- nodeInfo .ranksPurchased = not isGranted and nodeInfo .currentRank or 0
129- nodeInfo .isAvailable = isAvailable
130- nodeInfo .canPurchaseRank = isAvailable and not isGranted and ((TalentViewer .purchasedRanks [nodeID ] or 0 ) < nodeInfo .maxRanks )
131- nodeInfo .canRefundRank = not isGranted and ((TalentViewer .purchasedRanks [nodeID ] or 0 ) > 0 )
132- nodeInfo .meetsEdgeRequirements = meetsEdgeRequirements
133-
134- for _ , edge in ipairs (nodeInfo .visibleEdges ) do
135- edge .isActive = nodeInfo .activeRank == nodeInfo .maxRanks
131+ function TalentViewer_ClassTalentTalentsTabMixin :MarkEdgeRequirementCacheDirty (nodeID )
132+ local edges = getNodeEdges (nodeID )
133+ for _ , edge in ipairs (edges ) do
134+ self .edgeRequirementsCache [edge .targetNode ] = nil
136135 end
136+ end
137137
138- if # nodeInfo .entryIDs > 1 then
139- local entryIndex
140- for i , entryId in ipairs (nodeInfo .entryIDs ) do
141- if entryId == selectedEntryId then
142- entryIndex = i
143- break
138+ function TalentViewer_ClassTalentTalentsTabMixin :MeetsEdgeRequirements (nodeID )
139+ local function EdgeRequirementCallback (nodeID )
140+ local incomingEdges = getIncomingNodeEdges (nodeID )
141+ local hasActiveIncomingEdge = false
142+ local hasInactiveIncomingEdge = false
143+ for _ , incomingNodeId in ipairs (incomingEdges ) do
144+ local nodeInfo = LibTalentTree :GetLibNodeInfo (TalentViewer .treeId , incomingNodeId )
145+ if not nodeInfo then nodeInfo = LibTalentTree :GetNodeInfo (TalentViewer .treeId , incomingNodeId ) end
146+ if nodeInfo and LibTalentTree :IsNodeVisibleForSpec (TalentViewer .selectedSpecId , incomingNodeId ) then
147+ local isGranted = LibTalentTree :IsNodeGrantedForSpec (TalentViewer .selectedSpecId , incomingNodeId )
148+ local isChoiceNode = # nodeInfo .entryIDs > 1
149+ local selectedEntryId = isChoiceNode and TalentViewer :GetSelectedEntryId (incomingNodeId ) or nil
150+ local activeRank = isGranted
151+ and nodeInfo .maxRanks
152+ or ((isChoiceNode and selectedEntryId and 1 ) or TalentViewer :GetActiveRank (incomingNodeId ))
153+ local isEdgeActive = activeRank == nodeInfo .maxRanks
154+
155+ if not isEdgeActive then
156+ hasInactiveIncomingEdge = true
157+ else
158+ hasActiveIncomingEdge = true
159+ end
144160 end
145161 end
146- nodeInfo .activeEntry = entryIndex and { entryID = nodeInfo .entryIDs [entryIndex ], rank = nodeInfo .activeRank } or emptyTable
147- else
148- nodeInfo .activeEntry = { entryID = nodeInfo .entryIDs [1 ], rank = nodeInfo .activeRank }
162+
163+ return not hasInactiveIncomingEdge or hasActiveIncomingEdge
149164 end
150165
151- nodeInfo .isVisible = LibTalentTree :IsNodeVisibleForSpec (TalentViewer .selectedSpecId , nodeID )
166+ return GetOrCreateTableEntryByCallback (self .edgeRequirementsCache , nodeID , EdgeRequirementCallback )
167+ end
168+
169+ function TalentViewer_ClassTalentTalentsTabMixin :GetAndCacheNodeInfo (nodeID )
170+ local function GetNodeInfoCallback (nodeID )
171+ local nodeInfo = LibTalentTree :GetLibNodeInfo (TalentViewer .treeId , nodeID )
172+ if not nodeInfo then nodeInfo = LibTalentTree :GetNodeInfo (TalentViewer .treeId , nodeID ) end
173+ if nodeInfo .ID ~= nodeID then return nil end
174+ local isGranted = LibTalentTree :IsNodeGrantedForSpec (TalentViewer .selectedSpecId , nodeID )
175+ local isChoiceNode = # nodeInfo .entryIDs > 1
176+ local selectedEntryId = isChoiceNode and TalentViewer :GetSelectedEntryId (nodeID ) or nil
177+
178+ local meetsEdgeRequirements = TalentViewer .db .ignoreRestrictions or self :MeetsEdgeRequirements (nodeID )
179+ local meetsGateRequirements = true
180+ if not TalentViewer .db .ignoreRestrictions then
181+ for _ , conditionId in ipairs (nodeInfo .conditionIDs ) do
182+ local condInfo = self :GetAndCacheCondInfo (conditionId )
183+ if condInfo .isGate and not condInfo .isMet then meetsGateRequirements = false end
184+ end
185+ end
186+
187+ local isAvailable = meetsGateRequirements
152188
153- return nodeInfo
189+ nodeInfo .activeRank = isGranted
190+ and nodeInfo .maxRanks
191+ or ((isChoiceNode and selectedEntryId and 1 ) or TalentViewer :GetActiveRank (nodeID ))
192+ nodeInfo .currentRank = nodeInfo .activeRank
193+ nodeInfo .ranksPurchased = not isGranted and nodeInfo .currentRank or 0
194+ nodeInfo .isAvailable = isAvailable
195+ nodeInfo .canPurchaseRank = isAvailable and meetsEdgeRequirements and not isGranted and ((TalentViewer .purchasedRanks [nodeID ] or 0 ) < nodeInfo .maxRanks )
196+ nodeInfo .canRefundRank = not isGranted
197+ nodeInfo .meetsEdgeRequirements = meetsEdgeRequirements
198+
199+ for _ , edge in ipairs (nodeInfo .visibleEdges ) do
200+ edge .isActive = nodeInfo .activeRank == nodeInfo .maxRanks
201+ end
202+
203+ if # nodeInfo .entryIDs > 1 then
204+ local entryIndex
205+ for i , entryId in ipairs (nodeInfo .entryIDs ) do
206+ if entryId == selectedEntryId then
207+ entryIndex = i
208+ break
209+ end
210+ end
211+ nodeInfo .activeEntry = entryIndex and { entryID = nodeInfo .entryIDs [entryIndex ], rank = nodeInfo .activeRank } or nil
212+ else
213+ nodeInfo .activeEntry = { entryID = nodeInfo .entryIDs [1 ], rank = nodeInfo .activeRank }
214+ end
215+ if not isChoiceNode and nodeInfo .activeRank ~= nodeInfo .maxRanks then
216+ nodeInfo .nextEntry = { entryID = nodeInfo .entryIDs [1 ], rank = nodeInfo .activeRank + 1 }
217+ end
218+
219+ nodeInfo .isVisible = LibTalentTree :IsNodeVisibleForSpec (TalentViewer .selectedSpecId , nodeID )
220+
221+ return nodeInfo
222+ end
223+ return GetOrCreateTableEntryByCallback (self .nodeInfoCache , nodeID , GetNodeInfoCallback );
154224 end
155225
156226 function TalentViewer_ClassTalentTalentsTabMixin :GetAndCacheCondInfo (condID )
157- local function GetCondInfoCallback ()
227+ local function GetCondInfoCallback (condID )
158228 local condInfo = C_Traits .GetConditionInfo (C_ClassTalents .GetActiveConfigID (), condID )
159229 if condInfo .isGate then
160230 local gates = LibTalentTree :GetGates (self :GetSpecID ())
203273 end
204274 end
205275 end
276+ function talentButton :CanRefundRank ()
277+ -- remove this method override if/when "cascaded refunds" are implemented
278+ return self .nodeInfo .canRefundRank and self .nodeInfo .ranksPurchased and (self .nodeInfo .ranksPurchased > 0 );
279+ end
206280
207281 function talentButton :PurchaseRank ()
208282 self :PlaySelectSound ();
209283 TalentViewer :PurchaseRank (self :GetNodeID ());
284+ talentFrame :MarkEdgeRequirementCacheDirty (self :GetNodeID ());
210285 talentFrame :MarkNodeInfoCacheDirty (self :GetNodeID ())
211286 talentFrame :UpdateTreeCurrencyInfo ()
212287 -- self:CheckTooltip();
215290 function talentButton :RefundRank ()
216291 self :PlayDeselectSound ();
217292 TalentViewer :RefundRank (self :GetNodeID ());
293+ talentFrame :MarkEdgeRequirementCacheDirty (self :GetNodeID ());
218294 talentFrame :MarkNodeInfoCacheDirty (self :GetNodeID ())
219295 talentFrame :UpdateTreeCurrencyInfo ()
220296 -- self:CheckTooltip();
225301
226302 function TalentViewer_ClassTalentTalentsTabMixin :SetSelection (nodeID , entryID )
227303 TalentViewer :SetSelection (nodeID , entryID )
304+ self :MarkEdgeRequirementCacheDirty (nodeID );
228305 self :MarkNodeInfoCacheDirty (nodeID )
229306 self :UpdateTreeCurrencyInfo ()
230307 end
307384 function TalentViewer_ClassTalentTalentsTabMixin :OnLoad ()
308385 ClassTalentTalentsTabMixin .OnLoad (self )
309386
387+ self .edgeRequirementsCache = {}
388+
310389 local setAmountOverride = function (self , amount )
311390 local requiredLevel = self .isClassCurrency and 8 or 9 ;
312391 local spent = (self .isClassCurrency and MAX_LEVEL_CLASS_CURRENCY_CAP or MAX_LEVEL_SPEC_CURRENCY_CAP ) - amount ;
@@ -447,6 +526,7 @@ function TalentViewer:ResetTree()
447526 wipe (self .purchasedRanks )
448527 wipe (self .selectedEntries )
449528 wipe (self .currencySpending )
529+ wipe (self :GetTalentFrame ().edgeRequirementsCache )
450530 TalentViewer_DF .Talents :SetTalentTreeID (self .treeId , true );
451531 TalentViewer_DF .Talents :UpdateClassVisuals ()
452532 TalentViewer_DF .Talents :UpdateSpecBackground ();
526606function TalentViewer :OnInitialize ()
527607 local defaults = {
528608 ldbOptions = { hide = false },
529- ignoreRestrictions = true ,
609+ ignoreRestrictions = false ,
530610 }
531611
532612 TalentTreeViewerDB = TalentTreeViewerDB or {}
@@ -725,12 +805,19 @@ function TalentViewer:InitCheckbox()
725805 self .ignoreRestrictionsCheckbox = TalentViewer_DF .Talents .IgnoreRestrictions
726806 local checkbox = self .ignoreRestrictionsCheckbox
727807 checkbox .Text :SetText (' Ignore Restrictions' )
728- checkbox .tooltip = ' Ignore restrictions when selecting talents'
729808 if self .db then
730809 checkbox :SetChecked (self .db .ignoreRestrictions )
731810 end
732- checkbox :SetScript (' OnClick' , function (checkbox )
733- self .db .ignoreRestrictions = checkbox :GetChecked ()
811+ checkbox :SetScript (' OnEnter' , function (self )
812+ GameTooltip :SetOwner (self , " ANCHOR_RIGHT" );
813+ GameTooltip_AddNormalLine (GameTooltip , ' Ignore restrictions when selecting talents' );
814+ GameTooltip :Show ();
815+ end )
816+ checkbox :SetScript (' OnLeave' , function (self )
817+ GameTooltip :Hide ();
818+ end )
819+ checkbox :SetScript (' OnClick' , function (button )
820+ self .db .ignoreRestrictions = button :GetChecked ()
734821 self :GetTalentFrame ():UpdateTreeCurrencyInfo ()
735822 end )
736823end
0 commit comments