Fix memory leak/performance issue with the map open
[QuestHelper.git] / tracker.lua
blob7ba2260a4c035a30f377a32aeb874525e1f72228
1 QuestHelper_File["tracker.lua"] = "Development Version"
2 QuestHelper_Loadtime["tracker.lua"] = GetTime()
4 local debug_output = false
5 if QuestHelper_File["tracker.lua"] == "Development Version" then debug_output = true end
7 --[[ NOTES TO SELF
9 So here's what we want to do.
11 We want a "refresh notification" where we send it the current route.
13 If things aren't in the route, we don't care about them . . . unless they're pinned.
15 So we have "refresh notification" and "pin toggles". In both cases, the objective and only the objective is passed in. Right now there's no concept of priority within the pins.
17 We're also not bothering with the whole metaobjective tree, we're just going one step up.
19 So, our algorithm:
21 Note that "add" means "add iff it hasn't already been added."
23 Iff we haven't loaded yet, add a loaded message and a gap.
25 For each pinned objective, add the metaobjective and all objectives in its internal order. Iff we added things, add a gap.
27 For each route objective, add the metaobjective and all objectives in route order.
29 Later on we'll add an option for "splitting" metaobjectives, or possibly following the metaobjective tree all the way up.
33 So, "add" is complicated, due to the two requirements. We have to both add everything, and not add everything *yet*. I think the goal here is to make an Add function for adding a metaobjective that takes a list of objectives to be children, then doublecheck inside the function that we have all objectives.
35 We don't actually want "all objectives" long-term, note, we want only unfinished objectives. I sort of don't like the idea of keeping "finished objectives" around, however. Maybe we should only toss objectives in if they're in the routing system? Then how do I handle unknown objectives? (Simple - you pin them, and point out that we don't know how to do them.)
37 Also, to handle the "moving things around", we need to be a little clever. One line might be either a metaobjective or an objective, but in either case, we need a counter for which case it is. If everything shuffles, and we had two copies of metaobjective, MO1 moves to MO1 and MO2 moves to MO2. Easy.
40 local tracker = CreateFrame("Frame", "QuestHelperQuestWatchFrame", UIParent)
41 local minbutton = CreateFrame("Button", "QuestHelperQuestWatchFrameMinimizeButton", UIParent)
43 QuestHelper.tracker = tracker
45 local resizing = false
46 tracker:Hide()
47 tracker:SetWidth(200)
48 tracker:SetHeight(100)
49 tracker.dw, tracker.dh = 200, 100
51 local in_tracker = 0
53 minbutton:SetFrameStrata("DIALOG")
54 minbutton:Hide()
55 minbutton:SetPoint("TOPRIGHT", WatchFrame) -- We default to a different location to make it more likely to display the right item.
56 minbutton:SetMovable(true)
57 minbutton:SetUserPlaced(true)
58 minbutton:SetWidth(10)
59 minbutton:SetHeight(5)
60 local minbutton_tex = minbutton:CreateTexture()
61 minbutton_tex:SetAllPoints()
62 minbutton_tex:SetTexture(.8, .8, .8)
64 tracker:SetPoint("CENTER", minbutton)
66 function minbutton:moved()
67 local x, y = self:GetCenter()
68 local w, h = UIParent:GetWidth(), UIParent:GetHeight()
69 local anchor = (y < h*.45 and "BOTTOM" or y > h*.55 and "TOP" or "")..(x < w*.45 and "LEFT" or x > w*.55 and "RIGHT" or "")
71 tracker:ClearAllPoints()
72 tracker:SetPoint("CENTER", self)
74 if anchor ~= "" then
75 tracker:SetPoint(anchor, self)
76 end
77 end
79 function QuestHelper:ResetTrackerPosition(cmd)
80 minbutton:ClearAllPoints()
81 if cmd and string.find(cmd, "center") then
82 minbutton:SetPoint("CENTER", nil, "CENTER", 100, 100)
83 else
84 minbutton:SetPoint("RIGHT", nil, "RIGHT", -20, 230)
85 end
86 minbutton:moved()
87 QuestHelper_Pref.track_minimized = false
88 tracker:Show()
89 self:TextOut("Quest tracker postion reset.")
90 end
92 QH_Event({"DISPLAY_SIZE_CHANGED", "PLAYER_ENTERING_WORLD"}, function () minbutton:moved() end)
94 QH_Hook(minbutton, "OnClick", function ()
95 QuestHelper_Pref.track_minimized = not QuestHelper_Pref.track_minimized
96 if QuestHelper_Pref.track_minimized then
97 tracker:Hide()
98 else
99 tracker:Show()
101 end)
103 minbutton:RegisterForDrag("LeftButton")
105 QH_Hook(minbutton, "OnDragStart", function(self)
106 if self:IsVisible() then
107 self:StartMoving()
108 QH_Hook(self, "OnUpdate", self.moved)
110 end)
112 QH_Hook(minbutton, "OnDragStop", function(self)
113 QH_Hook(self, "OnUpdate", nil)
114 self:StopMovingOrSizing()
115 self:moved()
116 end)
118 QH_Hook(minbutton, "OnEnter", function (self)
119 self:SetAlpha(1)
120 end)
122 QH_Hook(minbutton, "OnLeave", function (self)
123 self:SetAlpha(QuestHelper_Pref.track_minimized and .3 or .5)
124 end)
126 -- used_items[objective][index]
127 -- used_count[objective] is incremented as the last valid index
128 -- so, therefore, used_items[objective][used_count[objective]] is not nil
129 local used_items = {}
130 local used_count = {}
132 -- it's possible for an item to be in neither used_items nor recycled_items, if it's in the process of fading out
133 local recycled_items = {}
135 -- These two functions are basically identical. Combine them.
136 local function itemupdate(item, delta)
137 local done = true
139 local a = item:GetAlpha()
140 a = a + delta
142 if a < 1 then
143 item:SetAlpha(a)
144 done = false
145 else
146 item:SetAlpha(1)
149 local t = item.t + delta
151 if t < 1 then
152 item.t = t
153 local it = 1-t
154 local sp = math.sqrt(t-t*t)
155 item.x, item.y = item.sx*it+item.ex*t+(item.sy-item.ey)*sp, item.sy*it+item.ey*t+(item.ex-item.sx)*sp
156 done = false
157 else
158 item.t = 1
159 item.x, item.y = item.ex, item.ey
162 item:ClearAllPoints()
163 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
165 if done then
166 QH_Hook(item, "OnUpdate", nil)
170 local function itemfadeout(item, delta)
171 local a = item:GetAlpha()
172 a = a - delta
174 if a > 0 then
175 item:SetAlpha(a)
176 else
177 item:SetAlpha(1)
178 item:Hide()
179 QH_Hook(item, "OnUpdate", nil)
180 table.insert(recycled_items, item)
181 return
184 local t = item.t + delta
186 if t < 1 then
187 item.t = t
188 local it = 1-t
189 local sp = math.sqrt(t-t*t)
190 item.x, item.y = item.sx*it+item.ex*t+(item.sy-item.ey)*sp, item.sy*it+item.ey*t+(item.ex-item.sx)*sp
191 else
192 item.t = 1
193 item.x, item.y = item.ex, item.ey
196 item:ClearAllPoints()
197 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
200 --[[function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
201 if (QuestLogFrame:IsShown()) then
202 HideUIPanel(QuestLogFrame);
203 else
204 ShowUIPanel(QuestLogFrame);
208 -- Grim stuff with uberquest, I need a better way to handle this
209 local function itemclick(item, button)
210 if button == "RightButton" then
211 local quest = item.quest
212 local index = 1
213 while true do
214 local title = GetQuestLogTitle(index)
215 if not title then break end
217 if title == quest then
218 if UberQuest then
219 -- UberQuest needs a little extra effort to work properly.
221 if UberQuest_List:IsShown() and GetQuestLogSelection() == index then
222 QH_ToggleQuestLog()
223 else
224 QuestLog_SetSelection(index)
226 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
227 -- and in the process update the frames to reflect the selected quest.
228 UberQuest_List:Hide()
229 UberQuest_Details:Show()
230 QH_ToggleQuestLog()
232 else
233 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
235 if QuestLogFrame:IsShown() and GetQuestLogSelection() == index then
236 -- If the selected quest is already being shown, hide it.
237 QH_ToggleQuestLog()
238 else
239 -- Otherwise, select it and show it.
240 QuestLog_SetSelection(index)
242 if not QuestLogFrame:IsShown() then
243 QH_ToggleQuestLog()
248 return
251 index = index + 1
254 end]]
256 local function allocateItem()
257 local item
259 item = table.remove(recycled_items)
260 if item then return item end
262 item = CreateFrame("Frame", nil, tracker)
263 item.text = item:CreateFontString()
264 item.text:SetShadowColor(0, 0, 0, .8)
265 item.text:SetShadowOffset(1, -1)
266 item.text:SetPoint("TOPLEFT", item)
267 return item
270 local specitem_max = 1
271 local specitem_unused = {}
273 -- This is adding a *single item*. This won't be called by the main parsing loop, but it does need some serious hackery. Let's see now
274 local function addItem(objective, y, meta)
275 local obj_key = objective
276 if obj_key.cluster then obj_key = obj_key.cluster end
277 used_count[obj_key] = (used_count[obj_key] or 0) + 1
278 if not used_items[obj_key] then used_items[obj_key] = QuestHelper:CreateTable("additem used_items") end
279 local item = used_items[obj_key][used_count[obj_key]]
281 local x = meta and 4 or 20
283 if not item then
284 used_items[obj_key][used_count[obj_key]] = allocateItem()
285 item = used_items[obj_key][used_count[obj_key]]
287 if meta then
288 item.text:SetFont(QuestHelper.font.serif, 12)
289 item.text:SetTextColor(.82, .65, 0)
290 else
291 item.text:SetFont(QuestHelper.font.sans, 12)
292 item.text:SetTextColor(.82, .82, .82)
295 item.obj = objective
297 item.sx, item.sy, item.x, item.y, item.ex, item.ey, item.t = x+30, y, x, y, x, y, 0
298 QH_Hook(item, "OnUpdate", itemupdate)
299 item:SetAlpha(0)
300 item:Show()
303 item.text:SetText(item.obj.tracker_desc or "(no description)")
305 local w, h = item.text:GetWidth(), item.text:GetHeight()
306 item:SetWidth(w)
307 item:SetHeight(h)
309 if objective.tracker_clicked then
310 QH_Hook(item, "OnMouseDown", function (self, button) if button == "RightButton" then objective.tracker_clicked() end end)
311 item:EnableMouse(true)
314 if item.ex ~= x or item.ey ~= y then
315 item.sx, item.sy, item.ex, item.ey = item.x, item.y, x, y
316 item.t = 0
317 QH_Hook(item, "OnUpdate", itemupdate)
320 -- we're just going to recycle this each time
321 if item.specitem then
322 item.specitem:Hide()
323 table.insert(specitem_unused, item.specitem)
324 item.specitem = nil
327 local spacer = 0
328 -- hacky - progress only shows up if we're not on a metaobjective. wheee
329 if objective.type_quest and objective.type_quest.index and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
330 item.specitem = table.remove(specitem_unused)
331 if not item.specitem then
332 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
333 QuestHelper: Assert(item.specitem)
335 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
336 QuestHelper: Assert(rangey)
337 local fn, fh, ff = rangey:GetFont()
338 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
339 rangey:SetText(RANGE_INDICATOR)
340 rangey:ClearAllPoints()
341 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
343 specitem_max = specitem_max + 1
346 item.specitem:SetScale(0.9)
347 item.specitem:ClearAllPoints()
348 item.specitem:SetParent(item)
349 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
351 local _, tex = GetQuestLogSpecialItemInfo(objective.type_quest.index)
352 item.specitem:SetID(objective.type_quest.index)
353 SetItemButtonTexture(item.specitem, tex)
354 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
356 item.specitem:Show()
358 spacer = h
361 return w+x+4, y+h, y+h+spacer
364 local function addMetaObjective(metaobj, items, y, depth)
365 local x, spacer
366 x, y, spacer = addItem(metaobj, y, true)
367 for _, v in ipairs(items) do
368 x, y = addItem(v, y, false)
370 return math.max(y, spacer), depth + #items + 1
373 --[[ -- these will be plugged in later one way or another
374 local function ccode(r1, g1, b1, r2, g2, b2, p)
375 local ip
376 p, ip = p*255, 255-p*255
377 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
380 local function qname(title, level)
381 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
382 title = string.format("[%d] %s", level, title)
385 if level == 7778 then
386 level = -7778
389 if QuestHelper_Pref.track_qcolour then
390 local player_level = QuestHelper.player_level
391 local delta = level - player_level
393 local colour
395 if delta >= 5 then
396 colour = "|cffff0000"
397 elseif delta >= 0 then
398 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
399 else
400 local grey
402 if player_level >= 60 then grey = player_level - 9
403 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
404 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
405 else grey = 0 end
407 if level == -7778 then
408 colour = "|cff808080"
409 elseif level > grey then
410 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
411 else
412 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
416 title = string.format("%s%s", colour, title)
419 return title
422 local function oname(text, pct)
423 if QuestHelper_Pref.track_ocolour then
424 text = string.format("%s%s", pct < 0.5 and ccode(1, 0, 0, 1, 1, 0, pct*2) or ccode(1, 1, 0, 0, 1, 0, pct*2-1), text)
427 return text
431 local function removeUnusedItem(item)
432 item.t = 0
433 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
434 QH_Hook(item, "OnMouseDown", nil)
435 item:EnableMouse(false)
436 QH_Hook(item, "OnUpdate", itemfadeout)
438 if item.specitem then
439 item.specitem:Hide()
440 table.insert(specitem_unused, item.specitem)
441 item.specitem = nil
445 --[=[
446 local was_inside = false
448 -- :ughh:
450 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
451 local count = 0
452 local quest
454 if objective.cat == "quest" then
455 quest = objective
456 else
457 quest = objective.quest
460 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
461 seen[quest] = true
463 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
465 if not level then
466 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
467 if not level then
468 level, name = 1, quest.obj
472 level = tonumber(level) or 1
474 count = count + 1
475 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
476 x = math.max(x, w)
477 y = y + h + gap
478 gap = 2
480 for obj in pairs(quest.swap_after or quest.after) do
481 if obj.progress then
482 table.insert(obj_list, obj)
486 table.sort(obj_list, objlist_sort)
488 for i, obj in ipairs(obj_list) do
489 local pct, text = 0, obj.obj
490 local seen_sum, seen_max = 0, 0
492 if obj.progress then
493 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
495 for user, progress in pairs(obj.progress) do
496 seen_sum = seen_sum + progress[3]
497 seen_max = seen_max + 1
498 seen_have[progress[1]] = true
499 seen_need[progress[2]] = true
502 if seen_max > 0 then
503 pct = seen_sum / seen_max
504 local list = QuestHelper:CreateTable()
506 for val in pairs(seen_have) do
507 table.insert(list, val)
510 table.sort(list)
512 local have = table.concat(list, ", ")
514 for i = #list,1,-1 do
515 list[i] = nil
518 for val in pairs(seen_need) do
519 table.insert(list, val)
522 if #list ~= 1 or list[1] ~= 1 then
523 -- If there is only one thing needed, ignore the progress, it's redundant.
524 -- It's either shown or it isn't.
526 table.sort(list)
528 local need = table.concat(list, ", ")
530 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
531 text, have, need)
534 QuestHelper:ReleaseTable(list)
537 QuestHelper:ReleaseTable(seen_have)
538 QuestHelper:ReleaseTable(seen_need)
541 if seen_sum ~= seen_max then
542 count = count + 1
543 w, h = addItem(oname(text, pct), quest, obj, -y)
544 x = math.max(x, w)
545 y = y + h
549 for i = #obj_list, 1, -1 do obj_list[i] = nil end
552 return x, y, gap, count
556 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
557 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
559 --local hidden_vquest1 = { cat = "quest", obj = "7778/" .. QHText("QUESTS_HIDDEN_1"), after = {}, watched = true }
560 --local hidden_vquest2 = { cat = "quest", obj = "7778/ " .. QHText("QUESTS_HIDDEN_2"), after = {}, watched = true }
562 local route = {}
563 local pinned = {}
565 -- This is actually called surprisingly often.
566 function QH_Tracker_Rescan()
567 used_count = QuestHelper:CreateTable("tracker rescan used_count")
569 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
570 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
572 local y, depth = 0, 0
575 local had_pinned = false
577 local objs = QuestHelper:CreateTable("tracker objs")
578 for k, v in pairs(pinned) do
579 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
580 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
581 obj_done[k.cluster] = true
584 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
585 for k, v in pairs(objs) do
586 v.cluster = k
587 v.trackkey = k
588 table.insert(sort_objs, v)
591 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
593 for _, v in ipairs(sort_objs) do
594 y, depth = addMetaObjective(v.cluster, v, y, depth)
595 had_pinned = true
596 QuestHelper:ReleaseTable(v)
598 QuestHelper:ReleaseTable(sort_objs)
599 QuestHelper:ReleaseTable(objs)
601 if had_pinned then y = y + 10 end
604 if QuestHelper.loading_main then
605 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
606 local x, ty = addItem(loading_vquest, y)
607 y = ty + 10
609 if QuestHelper.flightpathing then
610 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
611 local x, ty = addItem(flightpath_vquest, y)
612 y = ty + 10
615 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
616 for k, v in ipairs(route) do
617 if not v.ignore then
618 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
619 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
624 local current_mo
625 local current_mo_cluster
626 for k, v in ipairs(route) do
627 if depth > QuestHelper_Pref.track_size and not debug_output then break end
628 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
629 if current_mo and v.why ~= current_mo then
630 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
631 QuestHelper:ReleaseTable(current_mo_cluster)
632 current_mo, current_mo_cluster = nil, nil
635 if not v.why.tracker_split then
636 if not mo_done[v.why] then
637 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
638 mo_done[v.why] = true
640 else
641 if not current_mo then
642 current_mo = v.why
643 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
645 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
648 obj_done[v] = true
651 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
652 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
654 if current_mo_cluster then
655 QuestHelper:ReleaseTable(current_mo_cluster)
659 for k, v in pairs(used_items) do
660 if not used_count[k] or used_count[k] < #v then
661 local ttp = QuestHelper:CreateTable("used_items ttp")
662 for m = 1, (used_count[k] or 0) do
663 table.insert(ttp, v[m])
665 for m = (used_count[k] or 0) + 1, #v do
666 removeUnusedItem(v[m])
669 if used_items[k] then
670 QuestHelper:ReleaseTable(used_items[k])
673 if #ttp > 0 then
674 used_items[k] = ttp
675 else
676 used_items[k] = nil
677 QuestHelper:ReleaseTable(ttp)
682 QuestHelper:ReleaseTable(mo_done)
683 QuestHelper:ReleaseTable(obj_done)
684 for k, v in pairs(metalookup) do
685 QuestHelper:ReleaseTable(v)
687 QuestHelper:ReleaseTable(metalookup)
689 QuestHelper:ReleaseTable(used_count)
690 used_count = nil
692 if y ~= tracker.dh then
693 tracker.t = 0
694 tracker.sh = tracker:GetHeight()
695 tracker.dh = y
696 tracker.sw = tracker.dw
697 resizing = true
700 --[[
701 if x ~= tracker.dw or y ~= tracker.dy then
702 tracker.t = 0
703 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
704 tracker.dw, tracker.dh = x, y
705 resizing = true
706 end]]
708 --[[
710 local quests = QuestHelper.quest_log
711 local added = 0
712 local x, y = 4, 4
713 local gap = 0
714 local track_size = QuestHelper_Pref.track_size
715 local quests_added = {}
717 for quest, objs in pairs(used_items) do
718 for obj, item in pairs(objs) do
719 item.used = false
723 for i, objective in pairs(QuestHelper.route) do
724 if objective.watched then
725 obj_index_lookup[objective] = i
729 for q, data in pairs(QuestHelper.quest_log) do
730 quest_lookup[data.index] = q
733 -- Add our little "not yet loaded" notification
734 local loadedshow = false
735 if not QuestHelper.Routing.map_walker then
736 local count
737 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
738 added = added + count
739 loadedshow = true
741 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
742 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
746 -- Add an extra large gap to seperate the notification from everything else
747 gap = gap * 5
749 -- Add Quests that are watched but not in the route.
750 if UberQuest then
751 local uq_settings = UberQuest_Config[UnitName("player")]
752 if uq_settings then
753 local list = uq_settings.selected
754 if list then
755 local i = 1
756 while true do
757 local name = GetQuestLogTitle(i)
758 if not name then break end
759 quest_lookup[name] = quest_lookup[i]
760 i = i + 1
762 for name in pairs(list) do
763 local q = quest_lookup[name]
764 if q and not obj_index_lookup[q] then
765 local count
766 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
767 added = added + count
768 quests_added[q] = true
773 else
774 for i = 1,GetNumQuestWatches() do
775 local q = quest_lookup[GetQuestIndexForWatch(i)]
776 if q and not obj_index_lookup[q] then
777 local count
778 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
779 added = added + count
784 -- Add Quests that are watched and are in the route.
785 for i, objective in ipairs(QuestHelper.route) do
786 local count
787 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
788 added = added + count
789 quests_added[objective] = true
792 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
793 gap = gap * 5
795 -- Add Quests that aren't watched and are in the route.
796 if added <= track_size then
797 for i, objective in ipairs(QuestHelper.route) do
798 local count
799 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
800 added = added + count
801 quests_added[objective] = true
803 if added > track_size then
804 break
809 if not loadedshow and added < track_size and not QuestHelper_Pref.filter_done and not QuestHelper_Pref.filter_zone and not QuestHelper_Pref.filter_watched then
810 local added = 0
811 local notadded = 0
812 for k, v in pairs(quest_lookup) do
813 if not quests_added[v] then
814 notadded = notadded + 1
815 else
816 added = added + 1
820 if notadded > 0 then
821 gap = gap * 10
822 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
823 added = added + count
824 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
825 added = added + count
829 for obj in pairs(obj_index_lookup) do
830 obj_index_lookup[obj] = nil
833 for key in pairs(quest_lookup) do
834 quest_lookup[key] = nil
837 for quest, objs in pairs(used_items) do
838 for obj, item in pairs(objs) do
839 if not item.used then
840 removeUnusedItem(quest, obj, item)
845 for key in pairs(seen) do
846 seen[key] = nil
849 y = y+4
851 if x ~= tracker.dw or y ~= tracker.dy then
852 tracker.t = 0
853 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
854 tracker.dw, tracker.dh = x, y
855 resizing = true
858 added = 0
862 function QH_Tracker_UpdateRoute(new_route)
863 route = new_route
864 QH_Tracker_Rescan()
867 function QH_Tracker_Pin(metaobjective)
868 if not pinned[metaobjective] then
869 pinned[metaobjective] = true
870 QH_Tracker_Rescan()
874 function QH_Tracker_Unpin(metaobjective)
875 if pinned[metaobjective] then
876 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
877 QH_Tracker_Rescan()
881 function QH_Tracker_SetPin(metaobjective, flag)
882 if flag then
883 QH_Tracker_Pin(metaobjective)
884 else
885 QH_Tracker_Unpin(metaobjective)
890 local check_delay = 4
892 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
893 function tracker:update(delta)
894 if not delta then
895 -- This is called without a value when the questlog is updated.
896 -- We'll make sure we update the display on the next update.
897 check_delay = 1e99
898 return
901 if resizing then
902 local t = self.t+delta
904 if t > 1 then
905 self:SetWidth(self.dw)
906 self:SetHeight(self.dh)
907 resizing = false
908 else
909 self.t = t
910 local it = 1-t
911 self:SetWidth(self.sw*it+self.dw*t)
912 self:SetHeight(self.sh*it+self.dh*t)
916 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
917 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
918 -- the tracker.
919 local x, y = GetCursorPosition()
920 local s = 1/self:GetEffectiveScale()
921 x, y = x*s, y*s
923 QuestHelper: Assert(x)
924 QuestHelper: Assert(y)
925 --[[ QuestHelper: Assert(self:GetLeft())
926 QuestHelper: Assert(self:GetBottom())
927 QuestHelper: Assert(self:GetRight())
928 QuestHelper: Assert(self:GetTop())]]
930 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
931 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
932 if inside ~= was_inside then
933 was_inside = inside
934 if inside then
935 minbutton:SetAlpha(.7)
936 elseif not QuestHelper_Pref.track_minimized then
937 minbutton:SetAlpha(0)
941 check_delay = check_delay + delta
942 if check_delay > 1 then
943 check_delay = 0
945 QH_Tracker_Rescan()
949 QH_Hook(tracker, "OnUpdate", tracker.update)
951 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
952 --[[
953 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
955 function AddQuestWatch(...)
956 tracker:update()
957 return orig_AddQuestWatch(...)
960 function RemoveQuestWatch(...)
961 tracker:update()
962 return orig_RemoveQuestWatch(...)
963 end]]
965 -------------------------------------------------------------------------------------------------
966 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
968 local orig_TrackerOnShow
969 if QuestWatchFrame then -- 3.1 hackery
970 orig_TrackerOnShow = QuestWatchFrame:GetScript("OnShow")
972 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
973 local TrackerBackdropFound = false
975 local function TrackerBackdropOnShow(self, ...)
976 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
977 TrackerBackdropFound:Hide()
980 if orig_TrackerBackdropOnShow then
981 return orig_TrackerBackdropOnShow(self, ...)
985 function tracker:HideDefaultTracker()
986 -- The easy part: hide the original tracker
987 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
988 WatchFrame_ClearDisplay()
989 WatchFrame_Update()
991 -- The harder part: hide all those little buttons
993 local index = 1
994 while true do
995 local orig = _G["WatchFrameItem" .. tostring(index)]
996 if orig then orig:Hide() else break end
997 index = index + 1
1001 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1002 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1003 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1004 -- to keep checking. Hopefully, this won't happen too often.
1005 if not TrackerBackdropFound then
1006 if QuestWatchFrameBackdrop then
1007 -- Found bEQL's QuestWatchFrameBackdrop...
1008 TrackerBackdropFound = QuestWatchFrameBackdrop
1011 if TrackerBackdropFound then
1012 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1013 TrackerBackdropFound:Hide()
1015 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1016 QH_Hook(TrackerBackdropFound, "OnShow", TrackerBackdropOnShow)
1021 function tracker:ShowDefaultTracker()
1022 if QuestWatchFrame then -- 3.1 hackery
1023 QuestWatchFrame:Show()
1024 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1025 QuestWatch_Update()
1026 else
1027 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1028 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1029 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1030 WatchFrame_Update()
1033 if TrackerBackdropFound then
1034 TrackerBackdropFound:Show()
1038 if QuestWatchFrame then -- 3.1 hackery
1039 local function QuestWatchFrameOnShow(self, ...)
1040 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1041 tracker:HideDefaultTracker()
1044 if orig_TrackerOnShow then
1045 return orig_TrackerOnShow(self, ...)
1049 QH_Hook(QuestWatchFrame, "OnShow", QuestWatchFrameOnShow)
1052 function QuestHelper:ShowTracker()
1053 tracker:HideDefaultTracker()
1054 minbutton:Show()
1056 if QuestHelper_Pref.track_minimized then
1057 minbutton:SetAlpha(.3)
1058 else
1059 minbutton:SetAlpha(0)
1060 tracker:Show()
1064 function QuestHelper:HideTracker()
1065 tracker:ShowDefaultTracker()
1066 tracker:Hide()
1067 minbutton:Hide()