clean up some shit
[QuestHelper.git] / tracker.lua
blobc80c10c3691b3badf59033d182cd0c1e1218dd7a
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:SetWidth(200)
47 tracker:SetHeight(100)
48 tracker.dw, tracker.dh = 200, 100
50 local in_tracker = 0
52 minbutton:SetFrameStrata("DIALOG")
53 minbutton:Hide()
54 minbutton:SetPoint("TOPRIGHT", WatchFrame) -- We default to a different location to make it more likely to display the right item.
55 minbutton:SetMovable(true)
56 minbutton:SetUserPlaced(true)
57 minbutton:SetWidth(32 / 1.6)
58 minbutton:SetHeight(32)
59 local minbutton_tex = minbutton:CreateTexture()
60 minbutton_tex:SetAllPoints()
61 minbutton_tex:SetTexture(.6, .6, .6)
63 local sigil = minbutton:CreateTexture("BACKGROUND", minbutton_tex)
64 sigil:SetHeight(32)
65 sigil:SetWidth(32)
66 --sigil:SetPoint("CENTER", 0, 0)
67 sigil:SetTexture("Interface\\AddOns\\QuestHelper\\sigil")
68 sigil:SetPoint("CENTER", minbutton, "CENTER")
71 tracker:SetPoint("CENTER", minbutton)
73 function minbutton:moved()
74 local x, y = self:GetCenter()
75 local w, h = UIParent:GetWidth(), UIParent:GetHeight()
76 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 "")
78 tracker:ClearAllPoints()
79 tracker:SetPoint("CENTER", self)
81 if anchor ~= "" then
82 tracker:SetPoint(anchor, self)
83 end
84 end
86 function QuestHelper:ResetTrackerPosition(cmd)
87 minbutton:ClearAllPoints()
88 if cmd and string.find(cmd, "center") then
89 minbutton:SetPoint("CENTER", nil, "CENTER", 100, 100)
90 else
91 minbutton:SetPoint("RIGHT", nil, "RIGHT", -20, 230)
92 end
93 minbutton:moved()
94 QuestHelper_Pref.track_minimized = false
95 tracker:Show()
96 self:TextOut("Quest tracker postion reset.")
97 end
99 QH_Event({"DISPLAY_SIZE_CHANGED", "PLAYER_ENTERING_WORLD"}, function () minbutton:moved() end)
101 QH_Hook(minbutton, "OnClick", function ()
102 QuestHelper_Pref.track_minimized = not QuestHelper_Pref.track_minimized
103 if QuestHelper_Pref.track_minimized then
104 tracker:Hide()
105 else
106 tracker:Show()
108 end)
110 minbutton:RegisterForDrag("LeftButton")
112 QH_Hook(minbutton, "OnDragStart", function(self)
113 if self:IsVisible() then
114 self:StartMoving()
115 QH_Hook(self, "OnUpdate", self.moved)
117 end)
119 QH_Hook(minbutton, "OnDragStop", function(self)
120 QH_Hook(self, "OnUpdate", nil)
121 self:StopMovingOrSizing()
122 self:moved()
123 end)
125 QH_Hook(minbutton, "OnEnter", function (self)
126 self:SetAlpha(1)
127 end)
129 QH_Hook(minbutton, "OnLeave", function (self)
130 self:SetAlpha(QuestHelper_Pref.track_minimized and .3 or .5)
131 end)
133 -- used_items[objective][index]
134 -- used_count[objective] is incremented as the last valid index
135 -- so, therefore, used_items[objective][used_count[objective]] is not nil
136 local used_items = {}
137 local used_count = {}
139 -- it's possible for an item to be in neither used_items nor recycled_items, if it's in the process of fading out
140 local recycled_items = {}
142 -- These two functions are basically identical. Combine them.
143 local function itemupdate(item, delta)
144 local done = true
146 local a = item:GetAlpha()
147 a = a + delta
149 if a < 1 then
150 item:SetAlpha(a)
151 done = false
152 else
153 item:SetAlpha(1)
156 local t = item.t + delta
158 if t < 1 then
159 item.t = t
160 local it = 1-t
161 local sp = math.sqrt(t-t*t)
162 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
163 done = false
164 else
165 item.t = 1
166 item.x, item.y = item.ex, item.ey
169 item:ClearAllPoints()
170 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
172 if done then
173 QH_Hook(item, "OnUpdate", nil)
177 local function itemfadeout(item, delta)
178 local a = item:GetAlpha()
179 a = a - delta
181 if a > 0 then
182 item:SetAlpha(a)
183 else
184 item:SetAlpha(1)
185 item:Hide()
186 QH_Hook(item, "OnUpdate", nil)
187 table.insert(recycled_items, item)
188 return
191 local t = item.t + delta
193 if t < 1 then
194 item.t = t
195 local it = 1-t
196 local sp = math.sqrt(t-t*t)
197 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
198 else
199 item.t = 1
200 item.x, item.y = item.ex, item.ey
203 item:ClearAllPoints()
204 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
207 --[[function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
208 if (QuestLogFrame:IsShown()) then
209 HideUIPanel(QuestLogFrame);
210 else
211 ShowUIPanel(QuestLogFrame);
215 -- Grim stuff with uberquest, I need a better way to handle this
216 local function itemclick(item, button)
217 if button == "RightButton" then
218 local quest = item.quest
219 local index = 1
220 while true do
221 local title = GetQuestLogTitle(index)
222 if not title then break end
224 if title == quest then
225 if UberQuest then
226 -- UberQuest needs a little extra effort to work properly.
228 if UberQuest_List:IsShown() and GetQuestLogSelection() == index then
229 QH_ToggleQuestLog()
230 else
231 QuestLog_SetSelection(index)
233 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
234 -- and in the process update the frames to reflect the selected quest.
235 UberQuest_List:Hide()
236 UberQuest_Details:Show()
237 QH_ToggleQuestLog()
239 else
240 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
242 if QuestLogFrame:IsShown() and GetQuestLogSelection() == index then
243 -- If the selected quest is already being shown, hide it.
244 QH_ToggleQuestLog()
245 else
246 -- Otherwise, select it and show it.
247 QuestLog_SetSelection(index)
249 if not QuestLogFrame:IsShown() then
250 QH_ToggleQuestLog()
255 return
258 index = index + 1
261 end]]
263 local function allocateItem()
264 local item
266 item = table.remove(recycled_items)
267 if item then return item end
269 item = CreateFrame("Frame", nil, tracker)
270 item.text = item:CreateFontString()
271 item.text:SetShadowColor(0, 0, 0, .8)
272 item.text:SetShadowOffset(1, -1)
273 item.text:SetPoint("TOPLEFT", item)
274 return item
277 local specitem_max = 1
278 local specitem_unused = {}
280 -- 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
281 local function addItem(objective, y, meta)
282 local obj_key = objective
283 if obj_key.cluster then obj_key = obj_key.cluster end
284 used_count[obj_key] = (used_count[obj_key] or 0) + 1
285 if not used_items[obj_key] then used_items[obj_key] = QuestHelper:CreateTable("additem used_items") end
286 local item = used_items[obj_key][used_count[obj_key]]
288 local x = meta and 4 or 20
290 if not item then
291 used_items[obj_key][used_count[obj_key]] = allocateItem()
292 item = used_items[obj_key][used_count[obj_key]]
294 if meta then
295 item.text:SetFont(QuestHelper.font.serif, 12)
296 item.text:SetTextColor(.82, .65, 0)
297 else
298 item.text:SetFont(QuestHelper.font.sans, 12)
299 item.text:SetTextColor(.82, .82, .82)
302 item.obj = objective
304 item.sx, item.sy, item.x, item.y, item.ex, item.ey, item.t = x+30, y, x, y, x, y, 0
305 QH_Hook(item, "OnUpdate", itemupdate)
306 item:SetAlpha(0)
307 item:Show()
310 item.text:SetText(item.obj.tracker_desc or "(no description)")
312 local w, h = item.text:GetWidth(), item.text:GetHeight()
313 item:SetWidth(w)
314 item:SetHeight(h)
316 if objective.tracker_clicked then
317 QH_Hook(item, "OnMouseDown", function (self, button) if button == "RightButton" then objective.tracker_clicked() end end)
318 item:EnableMouse(true)
321 if item.ex ~= x or item.ey ~= y then
322 item.sx, item.sy, item.ex, item.ey = item.x, item.y, x, y
323 item.t = 0
324 QH_Hook(item, "OnUpdate", itemupdate)
327 -- we're just going to recycle this each time
328 if item.specitem then
329 item.specitem:Hide()
330 table.insert(specitem_unused, item.specitem)
331 item.specitem = nil
334 local spacer = 0
335 -- hacky - progress only shows up if we're not on a metaobjective. wheee
336 if objective.type_quest and objective.type_quest.index and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
337 item.specitem = table.remove(specitem_unused)
338 if not item.specitem then
339 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
340 QuestHelper: Assert(item.specitem)
342 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
343 QuestHelper: Assert(rangey)
344 local fn, fh, ff = rangey:GetFont()
345 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
346 rangey:SetText(RANGE_INDICATOR)
347 rangey:ClearAllPoints()
348 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
350 specitem_max = specitem_max + 1
353 item.specitem:SetScale(0.9)
354 item.specitem:ClearAllPoints()
355 item.specitem:SetParent(item)
356 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
358 local _, tex, charges = GetQuestLogSpecialItemInfo(objective.type_quest.index)
359 item.specitem:SetID(objective.type_quest.index)
360 SetItemButtonTexture(item.specitem, tex)
361 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
362 item.specitem.charges = charges
364 item.specitem:Show()
366 spacer = h
369 return w+x+4, y+h, y+h+spacer
372 local function addMetaObjective(metaobj, items, y, depth)
373 local x, spacer
374 x, y, spacer = addItem(metaobj, y, true)
375 for _, v in ipairs(items) do
376 x, y = addItem(v, y, false)
378 return math.max(y, spacer), depth + #items + 1
381 --[[ -- these will be plugged in later one way or another
382 local function ccode(r1, g1, b1, r2, g2, b2, p)
383 local ip
384 p, ip = p*255, 255-p*255
385 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
388 local function qname(title, level)
389 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
390 title = string.format("[%d] %s", level, title)
393 if level == 7778 then
394 level = -7778
397 if QuestHelper_Pref.track_qcolour then
398 local player_level = QuestHelper.player_level
399 local delta = level - player_level
401 local colour
403 if delta >= 5 then
404 colour = "|cffff0000"
405 elseif delta >= 0 then
406 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
407 else
408 local grey
410 if player_level >= 60 then grey = player_level - 9
411 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
412 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
413 else grey = 0 end
415 if level == -7778 then
416 colour = "|cff808080"
417 elseif level > grey then
418 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
419 else
420 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
424 title = string.format("%s%s", colour, title)
427 return title
430 local function oname(text, pct)
431 if QuestHelper_Pref.track_ocolour then
432 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)
435 return text
439 local function removeUnusedItem(item)
440 item.t = 0
441 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
442 QH_Hook(item, "OnMouseDown", nil)
443 item:EnableMouse(false)
444 QH_Hook(item, "OnUpdate", itemfadeout)
446 if item.specitem then
447 item.specitem:Hide()
448 table.insert(specitem_unused, item.specitem)
449 item.specitem = nil
453 --[=[
454 local was_inside = false
456 -- :ughh:
458 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
459 local count = 0
460 local quest
462 if objective.cat == "quest" then
463 quest = objective
464 else
465 quest = objective.quest
468 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
469 seen[quest] = true
471 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
473 if not level then
474 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
475 if not level then
476 level, name = 1, quest.obj
480 level = tonumber(level) or 1
482 count = count + 1
483 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
484 x = math.max(x, w)
485 y = y + h + gap
486 gap = 2
488 for obj in pairs(quest.swap_after or quest.after) do
489 if obj.progress then
490 table.insert(obj_list, obj)
494 table.sort(obj_list, objlist_sort)
496 for i, obj in ipairs(obj_list) do
497 local pct, text = 0, obj.obj
498 local seen_sum, seen_max = 0, 0
500 if obj.progress then
501 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
503 for user, progress in pairs(obj.progress) do
504 seen_sum = seen_sum + progress[3]
505 seen_max = seen_max + 1
506 seen_have[progress[1]] = true
507 seen_need[progress[2]] = true
510 if seen_max > 0 then
511 pct = seen_sum / seen_max
512 local list = QuestHelper:CreateTable()
514 for val in pairs(seen_have) do
515 table.insert(list, val)
518 table.sort(list)
520 local have = table.concat(list, ", ")
522 for i = #list,1,-1 do
523 list[i] = nil
526 for val in pairs(seen_need) do
527 table.insert(list, val)
530 if #list ~= 1 or list[1] ~= 1 then
531 -- If there is only one thing needed, ignore the progress, it's redundant.
532 -- It's either shown or it isn't.
534 table.sort(list)
536 local need = table.concat(list, ", ")
538 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
539 text, have, need)
542 QuestHelper:ReleaseTable(list)
545 QuestHelper:ReleaseTable(seen_have)
546 QuestHelper:ReleaseTable(seen_need)
549 if seen_sum ~= seen_max then
550 count = count + 1
551 w, h = addItem(oname(text, pct), quest, obj, -y)
552 x = math.max(x, w)
553 y = y + h
557 for i = #obj_list, 1, -1 do obj_list[i] = nil end
560 return x, y, gap, count
564 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
565 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
568 local hidden_vquest1 = { tracker_desc = QHText("QUESTS_HIDDEN_1"), tracker_clicked = QH_Hidden_Menu }
569 local hidden_vquest2 = { tracker_desc = " " .. QHText("QUESTS_HIDDEN_2"), tracker_clicked = QH_Hidden_Menu }
571 local route = {}
572 local pinned = {}
574 -- This is actually called surprisingly often.
575 function QH_Tracker_Rescan()
576 used_count = QuestHelper:CreateTable("tracker rescan used_count")
578 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
579 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
581 local y, depth = 0, 0
584 local had_pinned = false
586 local objs = QuestHelper:CreateTable("tracker objs")
587 for k, v in pairs(pinned) do
588 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
589 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
590 obj_done[k.cluster] = true
593 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
594 for k, v in pairs(objs) do
595 v.cluster = k
596 v.trackkey = k
597 table.insert(sort_objs, v)
600 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
602 for _, v in ipairs(sort_objs) do
603 y, depth = addMetaObjective(v.cluster, v, y, depth)
604 had_pinned = true
605 QuestHelper:ReleaseTable(v)
607 QuestHelper:ReleaseTable(sort_objs)
608 QuestHelper:ReleaseTable(objs)
610 if had_pinned then y = y + 10 end
613 if QuestHelper.loading_main then
614 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
615 local x, ty = addItem(loading_vquest, y)
616 y = ty + 10
618 if QuestHelper.flightpathing then
619 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
620 local x, ty = addItem(flightpath_vquest, y)
621 y = ty + 10
624 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
625 for k, v in ipairs(route) do
626 if not v.ignore then
627 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
628 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
633 local current_mo
634 local current_mo_cluster
635 for k, v in ipairs(route) do
636 if depth > QuestHelper_Pref.track_size and not debug_output then break end
637 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
638 if current_mo and v.why ~= current_mo then
639 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
640 QuestHelper:ReleaseTable(current_mo_cluster)
641 current_mo, current_mo_cluster = nil, nil
644 if not v.why.tracker_split then
645 if not mo_done[v.why] then
646 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
647 mo_done[v.why] = true
649 else
650 if not current_mo then
651 current_mo = v.why
652 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
654 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
657 obj_done[v] = true
660 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
661 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
663 if current_mo_cluster then
664 QuestHelper:ReleaseTable(current_mo_cluster)
668 -- now we check to see if we need a hidden display
669 if (debug_output or depth < QuestHelper_Pref.track_size) and not QuestHelper.loading_main and not QuestHelper_Pref.filter_done and not QuestHelper_Pref.filter_zone and not QuestHelper_Pref.filter_watched then
670 local show = false
672 QH_Route_TraverseClusters(
673 function (clust)
674 if not show then
675 QH_Route_IgnoredReasons_Cluster(clust, function (reason)
676 show = true
677 end)
679 for _, v in ipairs(clust) do
680 QH_Route_IgnoredReasons_Node(v, function (reason)
681 show = true
682 end)
688 if show then
689 y = y + 10
690 _, y = addItem(hidden_vquest1, y)
691 _, y = addItem(hidden_vquest2, y)
696 -- any manipulations of the tracker should be done by now, everything after this is bookkeeping
698 for k, v in pairs(used_items) do
699 if not used_count[k] or used_count[k] < #v then
700 local ttp = QuestHelper:CreateTable("used_items ttp")
701 for m = 1, (used_count[k] or 0) do
702 table.insert(ttp, v[m])
704 for m = (used_count[k] or 0) + 1, #v do
705 removeUnusedItem(v[m])
708 if used_items[k] then
709 QuestHelper:ReleaseTable(used_items[k])
712 if #ttp > 0 then
713 used_items[k] = ttp
714 else
715 used_items[k] = nil
716 QuestHelper:ReleaseTable(ttp)
721 QuestHelper:ReleaseTable(mo_done)
722 QuestHelper:ReleaseTable(obj_done)
723 for k, v in pairs(metalookup) do
724 QuestHelper:ReleaseTable(v)
726 QuestHelper:ReleaseTable(metalookup)
728 QuestHelper:ReleaseTable(used_count)
729 used_count = nil
731 if y ~= tracker.dh then
732 tracker.t = 0
733 tracker.sh = tracker:GetHeight()
734 tracker.dh = y
735 tracker.sw = tracker.dw
736 resizing = true
744 --[[
745 if x ~= tracker.dw or y ~= tracker.dy then
746 tracker.t = 0
747 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
748 tracker.dw, tracker.dh = x, y
749 resizing = true
750 end]]
752 --[[
754 local quests = QuestHelper.quest_log
755 local added = 0
756 local x, y = 4, 4
757 local gap = 0
758 local track_size = QuestHelper_Pref.track_size
759 local quests_added = {}
761 for quest, objs in pairs(used_items) do
762 for obj, item in pairs(objs) do
763 item.used = false
767 for i, objective in pairs(QuestHelper.route) do
768 if objective.watched then
769 obj_index_lookup[objective] = i
773 for q, data in pairs(QuestHelper.quest_log) do
774 quest_lookup[data.index] = q
777 -- Add our little "not yet loaded" notification
778 local loadedshow = false
779 if not QuestHelper.Routing.map_walker then
780 local count
781 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
782 added = added + count
783 loadedshow = true
785 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
786 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
790 -- Add an extra large gap to seperate the notification from everything else
791 gap = gap * 5
793 -- Add Quests that are watched but not in the route.
794 if UberQuest then
795 local uq_settings = UberQuest_Config[UnitName("player")]
796 if uq_settings then
797 local list = uq_settings.selected
798 if list then
799 local i = 1
800 while true do
801 local name = GetQuestLogTitle(i)
802 if not name then break end
803 quest_lookup[name] = quest_lookup[i]
804 i = i + 1
806 for name in pairs(list) do
807 local q = quest_lookup[name]
808 if q and not obj_index_lookup[q] then
809 local count
810 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
811 added = added + count
812 quests_added[q] = true
817 else
818 for i = 1,GetNumQuestWatches() do
819 local q = quest_lookup[GetQuestIndexForWatch(i)]
820 if q and not obj_index_lookup[q] then
821 local count
822 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
823 added = added + count
828 -- Add Quests that are watched and are in the route.
829 for i, objective in ipairs(QuestHelper.route) do
830 local count
831 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
832 added = added + count
833 quests_added[objective] = true
836 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
837 gap = gap * 5
839 -- Add Quests that aren't watched and are in the route.
840 if added <= track_size then
841 for i, objective in ipairs(QuestHelper.route) do
842 local count
843 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
844 added = added + count
845 quests_added[objective] = true
847 if added > track_size then
848 break
853 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
854 local added = 0
855 local notadded = 0
856 for k, v in pairs(quest_lookup) do
857 if not quests_added[v] then
858 notadded = notadded + 1
859 else
860 added = added + 1
864 if notadded > 0 then
865 gap = gap * 10
866 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
867 added = added + count
868 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
869 added = added + count
873 for obj in pairs(obj_index_lookup) do
874 obj_index_lookup[obj] = nil
877 for key in pairs(quest_lookup) do
878 quest_lookup[key] = nil
881 for quest, objs in pairs(used_items) do
882 for obj, item in pairs(objs) do
883 if not item.used then
884 removeUnusedItem(quest, obj, item)
889 for key in pairs(seen) do
890 seen[key] = nil
893 y = y+4
895 if x ~= tracker.dw or y ~= tracker.dy then
896 tracker.t = 0
897 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
898 tracker.dw, tracker.dh = x, y
899 resizing = true
902 added = 0
906 function QH_Tracker_UpdateRoute(new_route)
907 route = new_route
908 QH_Tracker_Rescan()
911 function QH_Tracker_Pin(metaobjective, suppress)
912 if not pinned[metaobjective] then
913 pinned[metaobjective] = true
915 if not suppress then
916 QH_Tracker_Rescan()
921 function QH_Tracker_Unpin(metaobjective, suppress)
922 if pinned[metaobjective] then
923 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
925 if not suppress then
926 QH_Tracker_Rescan()
931 function QH_Tracker_SetPin(metaobjective, flag, suppress)
932 if flag then
933 QH_Tracker_Pin(metaobjective, suppress)
934 else
935 QH_Tracker_Unpin(metaobjective, suppress)
940 local check_delay = 4
942 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
943 function tracker:update(delta)
944 if not delta then
945 -- This is called without a value when the questlog is updated.
946 -- We'll make sure we update the display on the next update.
947 check_delay = 1e99
948 return
951 if resizing then
952 local t = self.t+delta
954 if t > 1 then
955 self:SetWidth(self.dw)
956 self:SetHeight(self.dh)
957 resizing = false
958 else
959 self.t = t
960 local it = 1-t
961 self:SetWidth(self.sw*it+self.dw*t)
962 self:SetHeight(self.sh*it+self.dh*t)
966 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
967 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
968 -- the tracker.
969 local x, y = GetCursorPosition()
970 local s = 1/self:GetEffectiveScale()
971 x, y = x*s, y*s
973 QuestHelper: Assert(x)
974 QuestHelper: Assert(y)
975 --[[ QuestHelper: Assert(self:GetLeft())
976 QuestHelper: Assert(self:GetBottom())
977 QuestHelper: Assert(self:GetRight())
978 QuestHelper: Assert(self:GetTop())]]
980 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
981 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
982 if inside ~= was_inside then
983 was_inside = inside
984 if inside then
985 minbutton:SetAlpha(.5)
986 elseif not QuestHelper_Pref.track_minimized then
987 minbutton:SetAlpha(0)
991 check_delay = check_delay + delta
992 if check_delay > 1 then
993 check_delay = 0
995 QH_Tracker_Rescan()
999 QH_Hook(tracker, "OnUpdate", tracker.update)
1001 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
1002 --[[
1003 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
1005 function AddQuestWatch(...)
1006 tracker:update()
1007 return orig_AddQuestWatch(...)
1010 function RemoveQuestWatch(...)
1011 tracker:update()
1012 return orig_RemoveQuestWatch(...)
1013 end]]
1015 -------------------------------------------------------------------------------------------------
1016 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
1018 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
1019 local TrackerBackdropFound = false
1021 local function TrackerBackdropOnShow(self, ...)
1022 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1023 TrackerBackdropFound:Hide()
1026 if orig_TrackerBackdropOnShow then
1027 return orig_TrackerBackdropOnShow(self, ...)
1031 function tracker:HideDefaultTracker()
1032 -- The easy part: hide the original tracker
1033 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1034 WatchFrame_ClearDisplay()
1035 WatchFrame_Update()
1037 -- The harder part: hide all those little buttons
1039 local index = 1
1040 while true do
1041 local orig = _G["WatchFrameItem" .. tostring(index)]
1042 if orig then orig:Hide() else break end
1043 index = index + 1
1047 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1048 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1049 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1050 -- to keep checking. Hopefully, this won't happen too often.
1051 if not TrackerBackdropFound then
1052 if QuestWatchFrameBackdrop then
1053 -- Found bEQL's QuestWatchFrameBackdrop...
1054 TrackerBackdropFound = QuestWatchFrameBackdrop
1057 if TrackerBackdropFound then
1058 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1059 TrackerBackdropFound:Hide()
1061 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1062 QH_Hook(TrackerBackdropFound, "OnShow", TrackerBackdropOnShow)
1067 function tracker:ShowDefaultTracker()
1068 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1069 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1070 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1071 WatchFrame_Update()
1073 if TrackerBackdropFound then
1074 TrackerBackdropFound:Show()
1078 function QuestHelper:ShowTracker()
1079 tracker:HideDefaultTracker()
1080 minbutton:Show()
1082 if QuestHelper_Pref.track_minimized then
1083 minbutton:SetAlpha(.3)
1084 else
1085 minbutton:SetAlpha(0)
1086 tracker:Show()
1090 function QuestHelper:HideTracker()
1091 tracker:ShowDefaultTracker()
1092 tracker:Hide()
1093 minbutton:Hide()