loud in debug mode
[QuestHelper.git] / tracker.lua
blobb952d5ae8b9490e6a4ad133ab3b963c594d8830a
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 minbutton:SetScript("OnEvent", minbutton.moved)
93 minbutton:RegisterEvent("DISPLAY_SIZE_CHANGED")
94 minbutton:RegisterEvent("PLAYER_ENTERING_WORLD")
96 minbutton:SetScript("OnClick", function ()
97 QuestHelper_Pref.track_minimized = not QuestHelper_Pref.track_minimized
98 if QuestHelper_Pref.track_minimized then
99 tracker:Hide()
100 else
101 tracker:Show()
103 end)
105 minbutton:RegisterForDrag("LeftButton")
107 minbutton:SetScript("OnDragStart", function(self)
108 if self:IsVisible() then
109 self:StartMoving()
110 self:SetScript("OnUpdate", self.moved)
112 end)
114 minbutton:SetScript("OnDragStop", function(self)
115 self:SetScript("OnUpdate", nil)
116 self:StopMovingOrSizing()
117 self:moved()
118 end)
120 minbutton:SetScript("OnEnter", function (self)
121 self:SetAlpha(1)
122 end)
124 minbutton:SetScript("OnLeave", function (self)
125 self:SetAlpha(QuestHelper_Pref.track_minimized and .3 or .5)
126 end)
128 -- used_items[objective][index]
129 -- used_count[objective] is incremented as the last valid index
130 -- so, therefore, used_items[objective][used_count[objective]] is not nil
131 local used_items = {}
132 local used_count = {}
134 -- it's possible for an item to be in neither used_items nor recycled_items, if it's in the process of fading out
135 local recycled_items = {}
137 -- These two functions are basically identical. Combine them.
138 local function itemupdate(item, delta)
139 local done = true
141 local a = item:GetAlpha()
142 a = a + delta
144 if a < 1 then
145 item:SetAlpha(a)
146 done = false
147 else
148 item:SetAlpha(1)
151 local t = item.t + delta
153 if t < 1 then
154 item.t = t
155 local it = 1-t
156 local sp = math.sqrt(t-t*t)
157 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
158 done = false
159 else
160 item.t = 1
161 item.x, item.y = item.ex, item.ey
164 item:ClearAllPoints()
165 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
167 if done then
168 item:SetScript("OnUpdate", nil)
172 local function itemfadeout(item, delta)
173 local a = item:GetAlpha()
174 a = a - delta
176 if a > 0 then
177 item:SetAlpha(a)
178 else
179 item:SetAlpha(1)
180 item:Hide()
181 item:SetScript("OnUpdate", nil)
182 table.insert(recycled_items, item)
183 return
186 local t = item.t + delta
188 if t < 1 then
189 item.t = t
190 local it = 1-t
191 local sp = math.sqrt(t-t*t)
192 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
193 else
194 item.t = 1
195 item.x, item.y = item.ex, item.ey
198 item:ClearAllPoints()
199 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
202 --[[function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
203 if (QuestLogFrame:IsShown()) then
204 HideUIPanel(QuestLogFrame);
205 else
206 ShowUIPanel(QuestLogFrame);
210 -- Grim stuff with uberquest, I need a better way to handle this
211 local function itemclick(item, button)
212 if button == "RightButton" then
213 local quest = item.quest
214 local index = 1
215 while true do
216 local title = GetQuestLogTitle(index)
217 if not title then break end
219 if title == quest then
220 if UberQuest then
221 -- UberQuest needs a little extra effort to work properly.
223 if UberQuest_List:IsShown() and GetQuestLogSelection() == index then
224 QH_ToggleQuestLog()
225 else
226 QuestLog_SetSelection(index)
228 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
229 -- and in the process update the frames to reflect the selected quest.
230 UberQuest_List:Hide()
231 UberQuest_Details:Show()
232 QH_ToggleQuestLog()
234 else
235 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
237 if QuestLogFrame:IsShown() and GetQuestLogSelection() == index then
238 -- If the selected quest is already being shown, hide it.
239 QH_ToggleQuestLog()
240 else
241 -- Otherwise, select it and show it.
242 QuestLog_SetSelection(index)
244 if not QuestLogFrame:IsShown() then
245 QH_ToggleQuestLog()
250 return
253 index = index + 1
256 end]]
258 local function allocateItem()
259 local item
261 item = table.remove(recycled_items)
262 if item then return item end
264 item = CreateFrame("Frame", nil, tracker)
265 item.text = item:CreateFontString()
266 item.text:SetShadowColor(0, 0, 0, .8)
267 item.text:SetShadowOffset(1, -1)
268 item.text:SetPoint("TOPLEFT", item)
269 return item
272 local specitem_max = 1
273 local specitem_unused = {}
275 -- 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
276 local function addItem(objective, y, meta)
277 local obj_key = objective
278 if obj_key.cluster then obj_key = obj_key.cluster end
279 used_count[obj_key] = (used_count[obj_key] or 0) + 1
280 if not used_items[obj_key] then used_items[obj_key] = QuestHelper:CreateTable("additem used_items") end
281 local item = used_items[obj_key][used_count[obj_key]]
283 local x = meta and 4 or 20
285 if not item then
286 used_items[obj_key][used_count[obj_key]] = allocateItem()
287 item = used_items[obj_key][used_count[obj_key]]
289 if meta then
290 item.text:SetFont(QuestHelper.font.serif, 12)
291 item.text:SetTextColor(.82, .65, 0)
292 else
293 item.text:SetFont(QuestHelper.font.sans, 12)
294 item.text:SetTextColor(.82, .82, .82)
297 item.obj = objective
299 item.sx, item.sy, item.x, item.y, item.ex, item.ey, item.t = x+30, y, x, y, x, y, 0
300 item:SetScript("OnUpdate", itemupdate)
301 item:SetAlpha(0)
302 item:Show()
305 item.text:SetText(item.obj.tracker_desc)
307 local w, h = item.text:GetWidth(), item.text:GetHeight()
308 item:SetWidth(w)
309 item:SetHeight(h)
311 if objective.tracker_clicked then
312 item:SetScript("OnMouseDown", function (self, button) if button == "RightButton" then objective.tracker_clicked() end end)
313 item:EnableMouse(true)
316 if item.ex ~= x or item.ey ~= y then
317 item.sx, item.sy, item.ex, item.ey = item.x, item.y, x, y
318 item.t = 0
319 item:SetScript("OnUpdate", itemupdate)
322 -- we're just going to recycle this each time
323 if item.specitem then
324 item.specitem:Hide()
325 table.insert(specitem_unused, item.specitem)
326 item.specitem = nil
329 if objective.type_quest then
330 QuestHelper: Assert(objective.type_quest.index)
333 local spacer = 0
334 -- hacky - progress only shows up if we're not on a metaobjective. wheee
335 if objective.type_quest and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
336 item.specitem = table.remove(specitem_unused)
337 if not item.specitem then
338 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
339 QuestHelper: Assert(item.specitem)
341 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
342 QuestHelper: Assert(rangey)
343 local fn, fh, ff = rangey:GetFont()
344 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
345 rangey:SetText(RANGE_INDICATOR)
346 rangey:ClearAllPoints()
347 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
349 specitem_max = specitem_max + 1
352 item.specitem:SetScale(0.9)
353 item.specitem:ClearAllPoints()
354 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
356 local _, tex = GetQuestLogSpecialItemInfo(objective.type_quest.index)
357 item.specitem:SetID(objective.type_quest.index)
358 SetItemButtonTexture(item.specitem, tex)
359 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
361 item.specitem:Show()
363 spacer = h
366 return w+x+4, y+h, y+h+spacer
369 local function addMetaObjective(metaobj, items, y, depth)
370 local x, spacer
371 x, y, spacer = addItem(metaobj, y, true)
372 for _, v in ipairs(items) do
373 x, y = addItem(v, y, false)
375 return math.max(y, spacer), depth + #items + 1
378 --[[ -- these will be plugged in later one way or another
379 local function ccode(r1, g1, b1, r2, g2, b2, p)
380 local ip
381 p, ip = p*255, 255-p*255
382 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
385 local function qname(title, level)
386 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
387 title = string.format("[%d] %s", level, title)
390 if level == 7778 then
391 level = -7778
394 if QuestHelper_Pref.track_qcolour then
395 local player_level = QuestHelper.player_level
396 local delta = level - player_level
398 local colour
400 if delta >= 5 then
401 colour = "|cffff0000"
402 elseif delta >= 0 then
403 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
404 else
405 local grey
407 if player_level >= 60 then grey = player_level - 9
408 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
409 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
410 else grey = 0 end
412 if level == -7778 then
413 colour = "|cff808080"
414 elseif level > grey then
415 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
416 else
417 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
421 title = string.format("%s%s", colour, title)
424 return title
427 local function oname(text, pct)
428 if QuestHelper_Pref.track_ocolour then
429 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)
432 return text
436 local function removeUnusedItem(item)
437 item.t = 0
438 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
439 item:SetScript("OnMouseDown", nil)
440 item:EnableMouse(false)
441 item:SetScript("OnUpdate", itemfadeout)
443 if item.specitem then
444 item.specitem:Hide()
445 table.insert(specitem_unused, item.specitem)
446 item.specitem = nil
450 --[=[
451 local was_inside = false
453 -- :ughh:
455 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
456 local count = 0
457 local quest
459 if objective.cat == "quest" then
460 quest = objective
461 else
462 quest = objective.quest
465 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
466 seen[quest] = true
468 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
470 if not level then
471 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
472 if not level then
473 level, name = 1, quest.obj
477 level = tonumber(level) or 1
479 count = count + 1
480 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
481 x = math.max(x, w)
482 y = y + h + gap
483 gap = 2
485 for obj in pairs(quest.swap_after or quest.after) do
486 if obj.progress then
487 table.insert(obj_list, obj)
491 table.sort(obj_list, objlist_sort)
493 for i, obj in ipairs(obj_list) do
494 local pct, text = 0, obj.obj
495 local seen_sum, seen_max = 0, 0
497 if obj.progress then
498 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
500 for user, progress in pairs(obj.progress) do
501 seen_sum = seen_sum + progress[3]
502 seen_max = seen_max + 1
503 seen_have[progress[1]] = true
504 seen_need[progress[2]] = true
507 if seen_max > 0 then
508 pct = seen_sum / seen_max
509 local list = QuestHelper:CreateTable()
511 for val in pairs(seen_have) do
512 table.insert(list, val)
515 table.sort(list)
517 local have = table.concat(list, ", ")
519 for i = #list,1,-1 do
520 list[i] = nil
523 for val in pairs(seen_need) do
524 table.insert(list, val)
527 if #list ~= 1 or list[1] ~= 1 then
528 -- If there is only one thing needed, ignore the progress, it's redundant.
529 -- It's either shown or it isn't.
531 table.sort(list)
533 local need = table.concat(list, ", ")
535 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
536 text, have, need)
539 QuestHelper:ReleaseTable(list)
542 QuestHelper:ReleaseTable(seen_have)
543 QuestHelper:ReleaseTable(seen_need)
546 if seen_sum ~= seen_max then
547 count = count + 1
548 w, h = addItem(oname(text, pct), quest, obj, -y)
549 x = math.max(x, w)
550 y = y + h
554 for i = #obj_list, 1, -1 do obj_list[i] = nil end
557 return x, y, gap, count
561 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
562 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
564 --local hidden_vquest1 = { cat = "quest", obj = "7778/" .. QHText("QUESTS_HIDDEN_1"), after = {}, watched = true }
565 --local hidden_vquest2 = { cat = "quest", obj = "7778/ " .. QHText("QUESTS_HIDDEN_2"), after = {}, watched = true }
567 local route = {}
568 local pinned = {}
570 -- This is actually called surprisingly often.
571 function QH_Tracker_Rescan()
572 used_count = QuestHelper:CreateTable("tracker rescan used_count")
574 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
575 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
577 local y, depth = 0, 0
580 local had_pinned = false
582 local objs = QuestHelper:CreateTable("tracker objs")
583 for k, v in pairs(pinned) do
584 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
585 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
586 obj_done[k.cluster] = true
589 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
590 for k, v in pairs(objs) do
591 v.cluster = k
592 v.trackkey = k
593 table.insert(sort_objs, v)
596 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
598 for _, v in ipairs(sort_objs) do
599 y, depth = addMetaObjective(v.cluster, v, y, depth)
600 had_pinned = true
601 QuestHelper:ReleaseTable(v)
603 QuestHelper:ReleaseTable(sort_objs)
604 QuestHelper:ReleaseTable(objs)
606 if had_pinned then y = y + 10 end
609 if QuestHelper.loading_main then
610 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
611 local x, ty = addItem(loading_vquest, y)
612 y = ty + 10
614 if QuestHelper.flightpathing then
615 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
616 local x, ty = addItem(flightpath_vquest, y)
617 y = ty + 10
620 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
621 for k, v in ipairs(route) do
622 if not v.ignore then
623 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
624 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
629 local current_mo
630 local current_mo_cluster
631 for k, v in ipairs(route) do
632 if depth > QuestHelper_Pref.track_size and not debug_output then break end
633 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
634 if current_mo and v.why ~= current_mo then
635 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
636 QuestHelper:ReleaseTable(current_mo_cluster)
637 current_mo, current_mo_cluster = nil, nil
640 if not v.why.tracker_split then
641 if not mo_done[v.why] then
642 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
643 mo_done[v.why] = true
645 else
646 if not current_mo then
647 current_mo = v.why
648 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
650 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
653 obj_done[v] = true
656 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
657 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
659 if current_mo_cluster then
660 QuestHelper:ReleaseTable(current_mo_cluster)
664 for k, v in pairs(used_items) do
665 if not used_count[k] or used_count[k] < #v then
666 local ttp = QuestHelper:CreateTable("used_items ttp")
667 for m = 1, (used_count[k] or 0) do
668 table.insert(ttp, v[m])
670 for m = (used_count[k] or 0) + 1, #v do
671 removeUnusedItem(v[m])
674 if used_items[k] then
675 QuestHelper:ReleaseTable(used_items[k])
678 if #ttp > 0 then
679 used_items[k] = ttp
680 else
681 used_items[k] = nil
682 QuestHelper:ReleaseTable(ttp)
687 QuestHelper:ReleaseTable(mo_done)
688 QuestHelper:ReleaseTable(obj_done)
689 for k, v in pairs(metalookup) do
690 QuestHelper:ReleaseTable(v)
692 QuestHelper:ReleaseTable(metalookup)
694 QuestHelper:ReleaseTable(used_count)
695 used_count = nil
697 if y ~= tracker.dh then
698 tracker.t = 0
699 tracker.sh = tracker:GetHeight()
700 tracker.dh = y
701 tracker.sw = tracker.dw
702 resizing = true
705 --[[
706 if x ~= tracker.dw or y ~= tracker.dy then
707 tracker.t = 0
708 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
709 tracker.dw, tracker.dh = x, y
710 resizing = true
711 end]]
713 --[[
715 local quests = QuestHelper.quest_log
716 local added = 0
717 local x, y = 4, 4
718 local gap = 0
719 local track_size = QuestHelper_Pref.track_size
720 local quests_added = {}
722 for quest, objs in pairs(used_items) do
723 for obj, item in pairs(objs) do
724 item.used = false
728 for i, objective in pairs(QuestHelper.route) do
729 if objective.watched then
730 obj_index_lookup[objective] = i
734 for q, data in pairs(QuestHelper.quest_log) do
735 quest_lookup[data.index] = q
738 -- Add our little "not yet loaded" notification
739 local loadedshow = false
740 if not QuestHelper.Routing.map_walker then
741 local count
742 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
743 added = added + count
744 loadedshow = true
746 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
747 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
751 -- Add an extra large gap to seperate the notification from everything else
752 gap = gap * 5
754 -- Add Quests that are watched but not in the route.
755 if UberQuest then
756 local uq_settings = UberQuest_Config[UnitName("player")]
757 if uq_settings then
758 local list = uq_settings.selected
759 if list then
760 local i = 1
761 while true do
762 local name = GetQuestLogTitle(i)
763 if not name then break end
764 quest_lookup[name] = quest_lookup[i]
765 i = i + 1
767 for name in pairs(list) do
768 local q = quest_lookup[name]
769 if q and not obj_index_lookup[q] then
770 local count
771 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
772 added = added + count
773 quests_added[q] = true
778 else
779 for i = 1,GetNumQuestWatches() do
780 local q = quest_lookup[GetQuestIndexForWatch(i)]
781 if q and not obj_index_lookup[q] then
782 local count
783 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
784 added = added + count
789 -- Add Quests that are watched and are in the route.
790 for i, objective in ipairs(QuestHelper.route) do
791 local count
792 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
793 added = added + count
794 quests_added[objective] = true
797 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
798 gap = gap * 5
800 -- Add Quests that aren't watched and are in the route.
801 if added <= track_size then
802 for i, objective in ipairs(QuestHelper.route) do
803 local count
804 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
805 added = added + count
806 quests_added[objective] = true
808 if added > track_size then
809 break
814 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
815 local added = 0
816 local notadded = 0
817 for k, v in pairs(quest_lookup) do
818 if not quests_added[v] then
819 notadded = notadded + 1
820 else
821 added = added + 1
825 if notadded > 0 then
826 gap = gap * 10
827 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
828 added = added + count
829 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
830 added = added + count
834 for obj in pairs(obj_index_lookup) do
835 obj_index_lookup[obj] = nil
838 for key in pairs(quest_lookup) do
839 quest_lookup[key] = nil
842 for quest, objs in pairs(used_items) do
843 for obj, item in pairs(objs) do
844 if not item.used then
845 removeUnusedItem(quest, obj, item)
850 for key in pairs(seen) do
851 seen[key] = nil
854 y = y+4
856 if x ~= tracker.dw or y ~= tracker.dy then
857 tracker.t = 0
858 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
859 tracker.dw, tracker.dh = x, y
860 resizing = true
863 added = 0
867 function QH_Tracker_UpdateRoute(new_route)
868 route = new_route
869 QH_Tracker_Rescan()
872 function QH_Tracker_Pin(metaobjective)
873 if not pinned[metaobjective] then
874 pinned[metaobjective] = true
875 QH_Tracker_Rescan()
879 function QH_Tracker_Unpin(metaobjective)
880 if pinned[metaobjective] then
881 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
882 QH_Tracker_Rescan()
886 function QH_Tracker_SetPin(metaobjective, flag)
887 if flag then
888 QH_Tracker_Pin(metaobjective)
889 else
890 QH_Tracker_Unpin(metaobjective)
895 local check_delay = 4
897 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
898 function tracker:update(delta)
899 if not delta then
900 -- This is called without a value when the questlog is updated.
901 -- We'll make sure we update the display on the next update.
902 check_delay = 1e99
903 return
906 if resizing then
907 local t = self.t+delta
909 if t > 1 then
910 self:SetWidth(self.dw)
911 self:SetHeight(self.dh)
912 resizing = false
913 else
914 self.t = t
915 local it = 1-t
916 self:SetWidth(self.sw*it+self.dw*t)
917 self:SetHeight(self.sh*it+self.dh*t)
921 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
922 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
923 -- the tracker.
924 local x, y = GetCursorPosition()
925 local s = 1/self:GetEffectiveScale()
926 x, y = x*s, y*s
928 QuestHelper: Assert(x)
929 QuestHelper: Assert(y)
930 --[[ QuestHelper: Assert(self:GetLeft())
931 QuestHelper: Assert(self:GetBottom())
932 QuestHelper: Assert(self:GetRight())
933 QuestHelper: Assert(self:GetTop())]]
935 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
936 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
937 if inside ~= was_inside then
938 was_inside = inside
939 if inside then
940 minbutton:SetAlpha(.7)
941 elseif not QuestHelper_Pref.track_minimized then
942 minbutton:SetAlpha(0)
946 check_delay = check_delay + delta
947 if check_delay > 1 then
948 check_delay = 0
950 QH_Tracker_Rescan()
954 tracker:SetScript("OnUpdate", tracker.update)
956 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
957 --[[
958 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
960 function AddQuestWatch(...)
961 tracker:update()
962 return orig_AddQuestWatch(...)
965 function RemoveQuestWatch(...)
966 tracker:update()
967 return orig_RemoveQuestWatch(...)
968 end]]
970 -------------------------------------------------------------------------------------------------
971 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
973 local orig_TrackerOnShow
974 if QuestWatchFrame then -- 3.1 hackery
975 orig_TrackerOnShow = QuestWatchFrame:GetScript("OnShow")
977 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
978 local TrackerBackdropFound = false
980 local function TrackerBackdropOnShow(self, ...)
981 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
982 TrackerBackdropFound:Hide()
985 if orig_TrackerBackdropOnShow then
986 return orig_TrackerBackdropOnShow(self, ...)
990 function tracker:HideDefaultTracker()
991 -- The easy part: hide the original tracker
992 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
993 WatchFrame_ClearDisplay()
994 WatchFrame_Update()
996 -- The harder part: hide all those little buttons
998 local index = 1
999 while true do
1000 local orig = _G["WatchFrameItem" .. tostring(index)]
1001 if orig then orig:Hide() else break end
1002 index = index + 1
1006 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1007 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1008 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1009 -- to keep checking. Hopefully, this won't happen too often.
1010 if not TrackerBackdropFound then
1011 if QuestWatchFrameBackdrop then
1012 -- Found bEQL's QuestWatchFrameBackdrop...
1013 TrackerBackdropFound = QuestWatchFrameBackdrop
1016 if TrackerBackdropFound then
1017 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1018 TrackerBackdropFound:Hide()
1020 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1021 TrackerBackdropFound:SetScript("OnShow", TrackerBackdropOnShow)
1026 function tracker:ShowDefaultTracker()
1027 if QuestWatchFrame then -- 3.1 hackery
1028 QuestWatchFrame:Show()
1029 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1030 QuestWatch_Update()
1031 else
1032 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1033 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1034 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1035 WatchFrame_Update()
1038 if TrackerBackdropFound then
1039 TrackerBackdropFound:Show()
1043 if QuestWatchFrame then -- 3.1 hackery
1044 local function QuestWatchFrameOnShow(self, ...)
1045 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1046 tracker:HideDefaultTracker()
1049 if orig_TrackerOnShow then
1050 return orig_TrackerOnShow(self, ...)
1054 QuestWatchFrame:SetScript("OnShow", QuestWatchFrameOnShow)
1057 function QuestHelper:ShowTracker()
1058 tracker:HideDefaultTracker()
1059 minbutton:Show()
1061 if QuestHelper_Pref.track_minimized then
1062 minbutton:SetAlpha(.3)
1063 else
1064 minbutton:SetAlpha(0)
1065 tracker:Show()
1069 function QuestHelper:HideTracker()
1070 tracker:ShowDefaultTracker()
1071 tracker:Hide()
1072 minbutton:Hide()