Mercurial > wow > buffalo2
comparison Veneer_Objectives/ObjectiveTracker.lua @ 77:51f248dc0276
setup working copy dingaling
author | Nenue |
---|---|
date | Sat, 27 Aug 2016 10:51:08 -0400 |
parents | |
children | 0784b87f9722 |
comparison
equal
deleted
inserted
replaced
76:83b3cdaae6a5 | 77:51f248dc0276 |
---|---|
1 -- Veneer | |
2 -- ObjectiveTracker.lua | |
3 -- Created: 8/16/2016 8:19 AM | |
4 -- %file-revision% | |
5 -- This is more or less a copy of the blizzard code with some customization stuff added | |
6 | |
7 local plugin = VeneerObjectives | |
8 local vn, print = LibStub("LibKraken").register(Veneer, VeneerObjectives) | |
9 local ot | |
10 local otvn | |
11 | |
12 local band, floor, min = bit.band, math.floor, math.min | |
13 | |
14 | |
15 local X_QUEST = 0x00001; | |
16 local X_QUEST_ADDED = 0x00002; | |
17 local X_TASK_ADDED = 0x00004; | |
18 local X_WORLD_QUEST_ADDED = 0x00008; | |
19 local X_SCENARIO = 0x00010; | |
20 local X_SCENARIO_NEW_STAGE = 0x00020; | |
21 local X_ACHIEVEMENT = 0x00040; | |
22 local X_ACHIEVEMENT_ADDED = 0x00080; | |
23 local X_SCENARIO_BONUS_DELAYED = 0x00100; | |
24 local X_SUPER_TRACK_CHANGED = 0x00200; | |
25 -- these are for the specific module ONLY! | |
26 local X_MODULE_QUEST = 0x00400; | |
27 local X_MODULE_AUTO_QUEST_POPUP = 0x00800; | |
28 local X_MODULE_BONUS_OBJECTIVE = 0x01000; | |
29 local X_MODULE_WORLD_QUEST = 0x02000; | |
30 local X_MODULE_SCENARIO = 0x04000; | |
31 local X_MODULE_ACHIEVEMENT = 0x08000; | |
32 local X_SCENARIO_SPELLS = 0x10000; | |
33 -- special updates | |
34 local X_STATIC = 0x0000; | |
35 local X_ALL = 0xFFFF; | |
36 | |
37 -- state information | |
38 local X_REASON = X_ALL; -- default | |
39 local X_MONEY = 0 | |
40 local X_ANIMATION = 0 | |
41 local X_ID = 0; | |
42 | |
43 local INSET_V = -20 | |
44 | |
45 | |
46 | |
47 local trackerWidth = 240 | |
48 local blockIndent = 12 | |
49 local blockSpacing = 3 | |
50 | |
51 local tracker = { | |
52 freeBlocks = {}, | |
53 usedBlocks = {}, | |
54 freeProgressBars = {}, | |
55 usedProgressBars = {}, | |
56 freeTimers = {}, | |
57 usedTimers = {}, | |
58 freeLines = {}, | |
59 | |
60 headerText = 'Default Header', | |
61 contentHeight = 0, | |
62 animationHeight = 0, | |
63 updateReasonModule = 0, | |
64 updateReasonEvents = 0, | |
65 } | |
66 local GetTrackerHandler = function(handler) | |
67 handler = handler or {} | |
68 setmetatable(handler, {__index=tracker}) | |
69 return handler | |
70 end | |
71 | |
72 local Header_OnAnimationStart = function (self, forced) | |
73 local header = self:GetParent() | |
74 header.animating = true | |
75 plugin:UpdateAnimation(header) | |
76 end | |
77 | |
78 local Header_OnAnimationFinished = function (self, forced) | |
79 local header = self:GetParent() | |
80 header.animating = nil | |
81 plugin:Update(X_ANIMATION) | |
82 end | |
83 | |
84 -- reset generic flags for layout compilation | |
85 function tracker:InitLayout (isStatic) | |
86 print('|cFF00FFFFlayout|r', self.headerText) | |
87 self.firstBlock = nil | |
88 self.lastBlock = nil | |
89 self.currentBlock = nil | |
90 self.oldContentHeight = self.contentHeight | |
91 self.oldAnimationHeight = self.animationHeight | |
92 self.numBlocks = 0 | |
93 for id, block in pairs(self.usedBlocks) do | |
94 block.used = nil | |
95 end | |
96 if not isStatic then | |
97 self.hasSkippedBlocks = false | |
98 end | |
99 | |
100 if not self.header then | |
101 self:SetHeader(self.headerText, X_REASON) | |
102 self.header:SetPoint('TOPLEFT', plugin, 'TOPLEFT', 0, -plugin.contentHeight) | |
103 plugin.contentHeight = plugin.contentHeight + self.header:GetHeight() | |
104 end | |
105 | |
106 | |
107 self.contentHeight = 0 | |
108 | |
109 end | |
110 | |
111 -- clear out generic flags and set aside block frames for re-use | |
112 tracker.EndLayout = function(self) | |
113 self.lastBlock = self.currentBlock | |
114 for id, block in pairs(self.usedBlocks) do | |
115 if not block.used then | |
116 self:FreeBlock(block) | |
117 end | |
118 end | |
119 end | |
120 | |
121 function tracker:SetHeader (text, animateReason) | |
122 if self.header then | |
123 return self.header | |
124 end | |
125 | |
126 local block = CreateFrame('Button', nil, plugin, 'VeneerObjectiveHeader') | |
127 block.handler = self | |
128 block.text:SetText(self.headerText) | |
129 | |
130 self.currentBlock = block | |
131 self.header = block | |
132 return block | |
133 end | |
134 | |
135 function tracker:FreeBlock(block) | |
136 self.usedBlocks[block.id] = nil | |
137 print('|cFF0088FFfree|r', block:GetName()) | |
138 tinsert(self.freeBlocks, block) | |
139 | |
140 block.numLines = 0 | |
141 block:SetHeight(0) | |
142 block:Hide() | |
143 end | |
144 | |
145 -- should only be used in an N-lastN loop | |
146 function tracker:FreeLine(line) | |
147 if line.block then | |
148 line.block.lines[line.index] = nil | |
149 end | |
150 print('|cFF0088FFfree|r', line:GetName()) | |
151 local freeLines = line.type or self.freeLines | |
152 tinsert(freeLines, line) | |
153 end | |
154 | |
155 local blocksn = 0 | |
156 tracker.GetBlock = function(self, id) | |
157 local block = self.usedBlocks[id] | |
158 if not block then | |
159 local numFree = #self.freeBlocks | |
160 if numFree >= 1 then | |
161 block = tremove(self.freeBlocks, numFree) | |
162 else | |
163 blocksn = blocksn + 1 | |
164 block = CreateFrame('Button', 'OTVNBlock'..blocksn, plugin, 'VeneerObjectiveBlock') | |
165 block.lines = {} | |
166 end | |
167 self.usedBlocks[id] = block | |
168 block.id = id | |
169 block.handler = self | |
170 end | |
171 | |
172 block.used = true | |
173 block.currentLine = nil | |
174 block.numLines = 0 | |
175 | |
176 if block.lines then | |
177 for i, line in ipairs(block.lines) do | |
178 line.used = nil | |
179 end | |
180 end | |
181 | |
182 block.contentHeight = blockSpacing | |
183 block:SetWidth(trackerWidth) | |
184 block:SetHeight(0) | |
185 | |
186 return block | |
187 end | |
188 | |
189 -- obtain line | |
190 local linesn = 0 | |
191 function tracker:GetLine(block, index, lineType) | |
192 local line = block.lines[index] | |
193 if line and line.lineType ~= lineType then | |
194 | |
195 tinsert(self.freeLines, line) | |
196 line = nil | |
197 end | |
198 | |
199 if not line then | |
200 local freeLines = (lineType and lineType.freeLines) or self.freeLines | |
201 local numFreeLines = #freeLines | |
202 if numFreeLines >= 1 then | |
203 line = tremove(freeLines, numFreeLines) | |
204 else | |
205 linesn = linesn + 1 | |
206 line = CreateFrame('Frame', 'OTVNLine'.. linesn, block, (lineType and lineType.template) or 'VeneerObjectiveLine') | |
207 end | |
208 line:SetParent(block) | |
209 end | |
210 | |
211 line.type = lineType | |
212 line.index = index | |
213 line.used = true | |
214 line.block = block | |
215 line.text:SetPoint('TOPLEFT', line, 'TOPLEFT', blockIndent, 0) | |
216 line:SetHeight(0) -- in order for GetStringHeight to be useful | |
217 | |
218 block.lines[index] = line | |
219 | |
220 return line | |
221 end | |
222 | |
223 function tracker:AddProgressBar(block, line, questID) | |
224 end | |
225 | |
226 function tracker:FreeProgressBar(block, line) | |
227 end | |
228 | |
229 function tracker:AddTimerBar(block, line, duration, startTime) | |
230 end | |
231 | |
232 function tracker:FreeTimerBar(block, line) | |
233 end | |
234 | |
235 -- Checks for space and anchors or frees the block accordingly | |
236 function tracker:AddBlock (block, force) | |
237 local anchor = self.currentBlock or self.header | |
238 self.numBlocks = self.numBlocks + 1 | |
239 | |
240 if block.header then | |
241 print('header!', floor(block.contentHeight), '+', floor(block.header:GetStringHeight())) | |
242 block.contentHeight = block.contentHeight + block.header:GetStringHeight() | |
243 self.used = true | |
244 end | |
245 | |
246 | |
247 if self.currentBlock then | |
248 self.currentBlock.nextBlock = block | |
249 block.prevBlock = self.currentBlock | |
250 else | |
251 self.firstBlock = block | |
252 end | |
253 | |
254 self.contentHeight = self.contentHeight + block.contentHeight | |
255 self.currentBlock = block | |
256 | |
257 if not plugin.currentBlock then | |
258 plugin.firstBlock = block | |
259 end | |
260 plugin.currentBlock = block | |
261 | |
262 | |
263 | |
264 block:SetHeight(block.contentHeight) | |
265 print('block|cFF88FF00', self.numBlocks, block, '|rto', anchor, 'size', block.contentHeight) | |
266 block:ClearAllPoints() | |
267 block:SetPoint('TOPLEFT', anchor, 'BOTTOMLEFT', 0, -blockSpacing) | |
268 block:Show() | |
269 | |
270 -- free unused lines | |
271 local numLines = #block.lines | |
272 for i = 1, numLines do | |
273 local line = block.lines[i] | |
274 if not line.used or block.collapsed then | |
275 print('|cFFFF4400!|r') | |
276 self:FreeLine(line) | |
277 else | |
278 if not line:IsShown() then | |
279 line:Show() | |
280 end | |
281 end | |
282 end | |
283 | |
284 return true | |
285 end | |
286 | |
287 function tracker:SetLine(block, index, lineType, textOrFunc) | |
288 local anchor = block.currentLine or block.header | |
289 local line = self:GetLine(block, index, lineType) | |
290 | |
291 if line.ticker then | |
292 line.ticker:Cancel() | |
293 line.ticker = nil | |
294 end | |
295 | |
296 local text = textOrFunc | |
297 if type(textOrFunc) == 'function' then | |
298 text = textOrFunc() | |
299 | |
300 line.ticker = C_Timer.NewTicker(10, function() | |
301 line.height = tracker:SetLineText(line.text, textOrTextFunc()) | |
302 line:SetHeight(line.height) | |
303 end) | |
304 end | |
305 line.height = tracker:SetLineText(line.text, text) | |
306 line:SetHeight(line.height) | |
307 | |
308 print('line|cFFFFFF00', line:GetName(), '|rto', anchor:GetName(), 'size', line.height) | |
309 line:SetPoint('TOPLEFT', anchor, 'BOTTOMLEFT', 0, 0) | |
310 | |
311 block.contentHeight = block.contentHeight + line.height | |
312 block.numLines = block.numLines + 1 | |
313 | |
314 if block.currentLine then | |
315 line.prevLine = block.currentLine | |
316 block.currentLine.nextLine = line | |
317 else | |
318 block.firstLine = line | |
319 end | |
320 | |
321 block.currentLine = line | |
322 plugin.currentLine = line | |
323 | |
324 end | |
325 | |
326 function tracker:SetLineText(fontString, text) | |
327 fontString:SetText(text) | |
328 return fontString:GetStringHeight() | |
329 end | |
330 | |
331 -- Update lite | |
332 function tracker:ResetAnchors () | |
333 self:InitLayout() | |
334 | |
335 self:EndLayout() | |
336 end | |
337 | |
338 | |
339 local UpdateQuestTracker = function(watchIndex, logIndex) | |
340 return true | |
341 end | |
342 | |
343 -- top-down updaters | |
344 | |
345 local questTracker = GetTrackerHandler({ | |
346 headerText = 'Quests', | |
347 updateReasonModule = X_MODULE_QUEST, | |
348 updateReasonEvents = (X_QUEST + X_QUEST_ADDED), | |
349 }) | |
350 function questTracker:Update () | |
351 self:InitLayout() | |
352 for watchIndex = 1, GetNumQuestWatches() do | |
353 local questID, title, questLogIndex, numObjectives, requiredMoney, isComplete, startEvent, isAutoComplete, failureTime, timeElapsed, questType, isTask, isBounty, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(watchIndex) | |
354 if not questID then | |
355 -- stop parsing; end of data or variables haven't completely loaded | |
356 break | |
357 end | |
358 local showQuest = true | |
359 if isTask or (isBounty and not IsQuestComplete(questID)) then | |
360 -- do nothing | |
361 else | |
362 -- obtain a block | |
363 local block = self:GetBlock(questID) | |
364 | |
365 block.header:SetText(title) | |
366 | |
367 for i = 1, numObjectives do | |
368 local text, objectiveType, finished = GetQuestLogLeaderBoard(i, questLogIndex) | |
369 if text then | |
370 self:SetLine(block, i, nil, text) | |
371 end | |
372 end | |
373 | |
374 block:SetScript('OnClick', self.OnClick) | |
375 block:RegisterForClicks('AnyDown') | |
376 | |
377 self:AddBlock(block) | |
378 end | |
379 end | |
380 | |
381 self:EndLayout() | |
382 end | |
383 questTracker.OnClick = function(block, button) | |
384 if button == 'RightButton' then | |
385 ObjectiveTracker_ToggleDropDown(block, QuestObjectiveTracker_OnOpenDropDown) | |
386 else | |
387 CloseDropDownMenus() | |
388 | |
389 local questLogIndex = GetQuestLogIndexByID(block.id) | |
390 if ( IsModifiedClick("QUESTWATCHTOGGLE") ) then | |
391 QuestObjectiveTracker_UntrackQuest(nil, block.id); | |
392 else | |
393 if ( IsQuestComplete(block.id) and GetQuestLogIsAutoComplete(questLogIndex) ) then | |
394 AutoQuestPopupTracker_RemovePopUp(block.id); | |
395 ShowQuestComplete(questLogIndex); | |
396 else | |
397 QuestLogPopupDetailFrame_Show(questLogIndex); | |
398 end | |
399 end | |
400 end | |
401 end | |
402 | |
403 local UpdateAchievementTracker = function() end | |
404 local UpdateTaskTracker = function() end | |
405 local UpdateScenarioTracker = function() end | |
406 | |
407 function plugin:Update(reason, id) | |
408 | |
409 print('update', reason, #plugin.trackers) | |
410 | |
411 if plugin.minimized then | |
412 for i, tracker in ipairs(plugin.trackers) do | |
413 tracker:Hide() | |
414 end | |
415 return | |
416 end | |
417 | |
418 | |
419 local X_REASON = reason or X_ALL | |
420 local X_ID = id | |
421 | |
422 plugin.maxHeight = plugin:GetHeight() | |
423 plugin.oldContentsHeight = plugin.contentHeight | |
424 plugin.contentHeight = 0 | |
425 plugin.currentBlock = nil | |
426 plugin.firstBlock = nil | |
427 plugin:SetWidth(trackerWidth) | |
428 | |
429 -- pad for header | |
430 plugin.contentHeight = plugin.header:GetHeight() | |
431 | |
432 for i, header in ipairs(plugin.trackers) do | |
433 header.used = nil | |
434 end | |
435 | |
436 | |
437 local addedSpace = false | |
438 for i, tracker in ipairs(plugin.trackers) do | |
439 if ( band(X_REASON, tracker.updateReasonModule + tracker.updateReasonEvents ) > 0 ) then | |
440 print('->', tracker.headerText) | |
441 tracker:Update() | |
442 if tracker.oldContentHeight - tracker.contentHeight >= 1 then | |
443 addedSpace = true | |
444 end | |
445 else | |
446 -- if we can fit another header or an animation handler popped, contents need to be generated | |
447 if addedSpace or (tracker.header and tracker.header.animating) then | |
448 print('->', tracker.headerText, 'added space') | |
449 tracker:Update() | |
450 else | |
451 print('->', tracker.headerText, 'lite update') | |
452 -- otherwise, measure contents and hide anything that won't fit | |
453 tracker:ResetAnchors() | |
454 end | |
455 end | |
456 end | |
457 | |
458 for i, tracker in ipairs(plugin.trackers) do | |
459 if tracker.currentBlock then | |
460 if not tracker.header:IsShown() then | |
461 tracker.header:Show() | |
462 end | |
463 else | |
464 if tracker.header:IsShown() then | |
465 tracker.header:Hide() | |
466 end | |
467 end | |
468 end | |
469 | |
470 if VeneerBuffFrame and VeneerBuffFrame.lastBuff then | |
471 plugin:ClearAllPoints() | |
472 plugin:SetPoint('TOP', VeneerBuffFrame.lastBuff, 'BOTTOM', 0, -4) | |
473 plugin:SetPoint('RIGHT', UIParent, 'RIGHT', -6, 0) | |
474 else | |
475 plugin:ClearAllPoints() | |
476 plugin:SetPoint('TOPRIGHT', UIParent, 'TOPRIGHT', -6, -120) | |
477 end | |
478 | |
479 plugin:Show() | |
480 end | |
481 | |
482 plugin.init = function() | |
483 | |
484 plugin:RegisterEvent("QUEST_LOG_UPDATE") | |
485 plugin:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED") | |
486 plugin:RegisterEvent("QUEST_WATCH_LIST_CHANGED") | |
487 plugin:RegisterEvent("QUEST_AUTOCOMPLETE") | |
488 plugin:RegisterEvent("QUEST_ACCEPTED") | |
489 plugin:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED") | |
490 plugin:RegisterEvent("SCENARIO_UPDATE") | |
491 plugin:RegisterEvent("SCENARIO_CRITERIA_UPDATE") | |
492 plugin:RegisterEvent("SCENARIO_SPELL_UPDATE") | |
493 plugin:RegisterEvent("TRACKED_ACHIEVEMENT_UPDATE") | |
494 plugin:RegisterEvent("ZONE_CHANGED_NEW_AREA"); | |
495 plugin:RegisterEvent("ZONE_CHANGED"); | |
496 plugin:RegisterEvent("QUEST_POI_UPDATE"); | |
497 plugin:RegisterEvent("VARIABLES_LOADED"); | |
498 plugin:RegisterEvent("QUEST_TURNED_IN"); | |
499 plugin:RegisterEvent("PLAYER_MONEY"); | |
500 | |
501 plugin.trackers = { | |
502 questTracker, | |
503 } | |
504 print('bub') | |
505 end | |
506 function plugin:event(event, ...) | |
507 print('|cFFFF0088' .. event, ...) | |
508 if event == 'QUEST_WATCH_LIST_CHANGED' then | |
509 local questID, added = ... | |
510 if added then | |
511 if not IsQuestBounty(questID) or IsQuestComplete(questId) then | |
512 print(questID, added) | |
513 plugin:Update(X_QUEST_ADDED, questID) | |
514 end | |
515 else | |
516 print() | |
517 plugin:Update(X_QUEST) | |
518 end | |
519 elseif event == 'QUEST_LOG_UPDATE' then | |
520 plugin:Update(X_MODULE_QUEST) | |
521 elseif event == 'QUEST_POI_UPDATE' then | |
522 | |
523 if GetCVar("trackQuestSorting") == "proximity" then | |
524 -- todo: sort blocks | |
525 end | |
526 plugin:Update(X_MODULE_QUEST) | |
527 elseif event == 'PLAYER_MONEY' then | |
528 plugin:Update(X_MONEY) | |
529 end | |
530 | |
531 end | |
532 | |
533 -- adjusts money events bitcode when a tracker is displaying money data | |
534 function plugin:UpdateMoneyFlag(watchMoney, reason) | |
535 if watchMoney then | |
536 if (band(X_MONEY, reason) == 0) then | |
537 X_MONEY = X_MONEY - reason | |
538 end | |
539 else | |
540 if (band(X_MONEY, reason) > 0) then | |
541 X_MONEY = X_MONEY + reason | |
542 end | |
543 end | |
544 end | |
545 | |
546 -- used by framescripts to poke the updater when an animation has ended | |
547 function plugin:UpdateAnimation(block) | |
548 | |
549 local reason = block.handler.updateReasonEvents | |
550 if block.animating then | |
551 if (band(X_ANIMATION, reason) == 0) then | |
552 X_ANIMATION = X_ANIMATION - reason | |
553 end | |
554 else | |
555 if (band(X_ANIMATION, reason) > 0) then | |
556 X_ANIMATION = X_ANIMATION + reason | |
557 end | |
558 end | |
559 end |