this is a little more elegant
[QuestHelper.git] / tracker.lua
blobe1e51c79b2b96ac8b51845603334f5c4be6103e0
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:SetFrameStrata("LOW")
49 tracker.dw, tracker.dh = 200, 100
51 local in_tracker = 0
53 --minbutton:SetFrameStrata("HIGH")
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(32 / 1.6)
59 minbutton:SetHeight(32)
60 local minbutton_tex = minbutton:CreateTexture()
61 minbutton_tex:SetAllPoints()
62 minbutton_tex:SetTexture(.6, .6, .6)
63 minbutton_tex:SetParent(minbutton)
65 local sigil = minbutton:CreateTexture("BACKGROUND", minbutton_tex)
66 sigil:SetHeight(32)
67 sigil:SetWidth(32)
68 --sigil:SetPoint("CENTER", 0, 0)
69 sigil:SetTexture("Interface\\AddOns\\QuestHelper\\sigil")
70 sigil:SetPoint("CENTER", minbutton_tex, "CENTER")
73 tracker:SetPoint("CENTER", minbutton)
75 function minbutton:moved()
76 local x, y = self:GetCenter()
77 local w, h = UIParent:GetWidth(), UIParent:GetHeight()
78 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 "")
80 tracker:ClearAllPoints()
81 tracker:SetPoint("CENTER", self)
83 if anchor ~= "" then
84 tracker:SetPoint(anchor, self)
85 end
86 end
88 function QuestHelper:ResetTrackerPosition(cmd)
89 minbutton:ClearAllPoints()
90 if cmd and string.find(cmd, "center") then
91 minbutton:SetPoint("CENTER", nil, "CENTER", 100, 100)
92 else
93 minbutton:SetPoint("RIGHT", nil, "RIGHT", -20, 230)
94 end
95 minbutton:moved()
96 QuestHelper_Pref.track_minimized = false
97 tracker:Show()
98 self:TextOut("Quest tracker postion reset.")
99 end
101 QH_Event({"DISPLAY_SIZE_CHANGED", "PLAYER_ENTERING_WORLD"}, function () minbutton:moved() end)
103 QH_Hook(minbutton, "OnClick", function ()
104 QuestHelper_Pref.track_minimized = not QuestHelper_Pref.track_minimized
105 if QuestHelper_Pref.track_minimized then
106 tracker:Hide()
107 else
108 tracker:Show()
110 end)
112 minbutton:RegisterForDrag("LeftButton")
114 QH_Hook(minbutton, "OnDragStart", function(self)
115 if self:IsVisible() then
116 self:StartMoving()
117 QH_Hook(self, "OnUpdate", self.moved)
119 end)
121 QH_Hook(minbutton, "OnDragStop", function(self)
122 QH_Hook(self, "OnUpdate", nil)
123 self:StopMovingOrSizing()
124 self:moved()
125 end)
127 QH_Hook(minbutton, "OnEnter", function (self)
128 self:SetAlpha(1)
129 end)
131 QH_Hook(minbutton, "OnLeave", function (self)
132 self:SetAlpha(QuestHelper_Pref.track_minimized and .3 or .5)
133 end)
135 -- used_items[objective][index]
136 -- used_count[objective] is incremented as the last valid index
137 -- so, therefore, used_items[objective][used_count[objective]] is not nil
138 local used_items = {}
139 local used_count = {}
141 -- it's possible for an item to be in neither used_items nor recycled_items, if it's in the process of fading out
142 local recycled_items = {}
144 -- These two functions are basically identical. Combine them.
145 local function itemupdate(item, delta)
146 local done = true
148 local a = item:GetAlpha()
149 a = a + delta
151 if a < 1 then
152 item:SetAlpha(a)
153 done = false
154 else
155 item:SetAlpha(1)
158 local t = item.t + delta
160 if t < 1 then
161 item.t = t
162 local it = 1-t
163 local sp = math.sqrt(t-t*t)
164 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
165 done = false
166 else
167 item.t = 1
168 item.x, item.y = item.ex, item.ey
171 item:ClearAllPoints()
172 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
174 if done then
175 QH_Hook(item, "OnUpdate", nil)
179 local function itemfadeout(item, delta)
180 local a = item:GetAlpha()
181 a = a - delta
183 if a > 0 then
184 item:SetAlpha(a)
185 else
186 item:SetAlpha(1)
187 item:Hide()
188 QH_Hook(item, "OnUpdate", nil)
189 table.insert(recycled_items, item)
190 return
193 local t = item.t + delta
195 if t < 1 then
196 item.t = t
197 local it = 1-t
198 local sp = math.sqrt(t-t*t)
199 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
200 else
201 item.t = 1
202 item.x, item.y = item.ex, item.ey
205 item:ClearAllPoints()
206 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
209 --[[function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
210 if (QuestLogFrame:IsShown()) then
211 HideUIPanel(QuestLogFrame);
212 else
213 ShowUIPanel(QuestLogFrame);
217 -- Grim stuff with uberquest, I need a better way to handle this
218 local function itemclick(item, button)
219 if button == "RightButton" then
220 local quest = item.quest
221 local index = 1
222 while true do
223 local title = GetQuestLogTitle(index)
224 if not title then break end
226 if title == quest then
227 if UberQuest then
228 -- UberQuest needs a little extra effort to work properly.
230 if UberQuest_List:IsShown() and GetQuestLogSelection() == index then
231 QH_ToggleQuestLog()
232 else
233 QuestLog_SetSelection(index)
235 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
236 -- and in the process update the frames to reflect the selected quest.
237 UberQuest_List:Hide()
238 UberQuest_Details:Show()
239 QH_ToggleQuestLog()
241 else
242 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
244 if QuestLogFrame:IsShown() and GetQuestLogSelection() == index then
245 -- If the selected quest is already being shown, hide it.
246 QH_ToggleQuestLog()
247 else
248 -- Otherwise, select it and show it.
249 QuestLog_SetSelection(index)
251 if not QuestLogFrame:IsShown() then
252 QH_ToggleQuestLog()
257 return
260 index = index + 1
263 end]]
265 local function allocateItem()
266 local item
268 item = table.remove(recycled_items)
269 if item then return item end
271 item = CreateFrame("Frame", nil, tracker)
272 item.text = item:CreateFontString()
273 item.text:SetShadowColor(0, 0, 0, .8)
274 item.text:SetShadowOffset(1, -1)
275 item.text:SetPoint("TOPLEFT", item)
276 return item
279 local specitem_max = 1
280 local specitem_unused = {}
282 -- 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
283 local function addItem(objective, y, meta)
284 local obj_key = objective
285 if obj_key.cluster then obj_key = obj_key.cluster end
286 used_count[obj_key] = (used_count[obj_key] or 0) + 1
287 if not used_items[obj_key] then used_items[obj_key] = QuestHelper:CreateTable("additem used_items") end
288 local item = used_items[obj_key][used_count[obj_key]]
290 local x = meta and 4 or 20
292 if not item then
293 used_items[obj_key][used_count[obj_key]] = allocateItem()
294 item = used_items[obj_key][used_count[obj_key]]
296 if meta then
297 item.text:SetFont(QuestHelper.font.serif, 12)
298 item.text:SetTextColor(.82, .65, 0)
299 else
300 item.text:SetFont(QuestHelper.font.sans, 12)
301 item.text:SetTextColor(.82, .82, .82)
304 item.obj = objective
306 item.sx, item.sy, item.x, item.y, item.ex, item.ey, item.t = x+30, y, x, y, x, y, 0
307 QH_Hook(item, "OnUpdate", itemupdate)
308 item:SetAlpha(0)
309 item:Show()
312 item.text:SetText(item.obj.tracker_desc or "(no description)")
314 local w, h = item.text:GetWidth(), item.text:GetHeight()
315 item:SetWidth(w)
316 item:SetHeight(h)
318 if objective.tracker_clicked then
319 QH_Hook(item, "OnMouseDown", function (self, button) if button == "RightButton" then objective.tracker_clicked() end end)
320 item:EnableMouse(true)
323 if item.ex ~= x or item.ey ~= y then
324 item.sx, item.sy, item.ex, item.ey = item.x, item.y, x, y
325 item.t = 0
326 QH_Hook(item, "OnUpdate", itemupdate)
329 -- we're just going to recycle this each time
330 if item.specitem then
331 item.specitem:Hide()
332 table.insert(specitem_unused, item.specitem)
333 item.specitem = nil
336 local spacer = 0
337 -- hacky - progress only shows up if we're not on a metaobjective. wheee
338 if objective.type_quest and objective.type_quest.index and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
339 item.specitem = table.remove(specitem_unused)
340 if not item.specitem then
341 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
342 QuestHelper: Assert(item.specitem)
344 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
345 QuestHelper: Assert(rangey)
346 local fn, fh, ff = rangey:GetFont()
347 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
348 rangey:SetText(RANGE_INDICATOR)
349 rangey:ClearAllPoints()
350 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
352 specitem_max = specitem_max + 1
355 item.specitem:SetScale(0.9)
356 item.specitem:ClearAllPoints()
357 item.specitem:SetParent(item)
358 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
360 local _, tex, charges = GetQuestLogSpecialItemInfo(objective.type_quest.index)
361 item.specitem:SetID(objective.type_quest.index)
362 SetItemButtonTexture(item.specitem, tex)
363 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
364 item.specitem.charges = charges
366 item.specitem:Show()
368 spacer = h
371 return w+x+4, y+h, y+h+spacer
374 local function addMetaObjective(metaobj, items, y, depth)
375 local x, spacer
376 x, y, spacer = addItem(metaobj, y, true)
377 for _, v in ipairs(items) do
378 x, y = addItem(v, y, false)
380 return math.max(y, spacer), depth + #items + 1
383 --[[ -- these will be plugged in later one way or another
384 local function ccode(r1, g1, b1, r2, g2, b2, p)
385 local ip
386 p, ip = p*255, 255-p*255
387 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
390 local function qname(title, level)
391 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
392 title = string.format("[%d] %s", level, title)
395 if level == 7778 then
396 level = -7778
399 if QuestHelper_Pref.track_qcolour then
400 local player_level = QuestHelper.player_level
401 local delta = level - player_level
403 local colour
405 if delta >= 5 then
406 colour = "|cffff0000"
407 elseif delta >= 0 then
408 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
409 else
410 local grey
412 if player_level >= 60 then grey = player_level - 9
413 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
414 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
415 else grey = 0 end
417 if level == -7778 then
418 colour = "|cff808080"
419 elseif level > grey then
420 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
421 else
422 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
426 title = string.format("%s%s", colour, title)
429 return title
432 local function oname(text, pct)
433 if QuestHelper_Pref.track_ocolour then
434 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)
437 return text
441 local function removeUnusedItem(item)
442 item.t = 0
443 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
444 QH_Hook(item, "OnMouseDown", nil)
445 item:EnableMouse(false)
446 QH_Hook(item, "OnUpdate", itemfadeout)
448 if item.specitem then
449 item.specitem:Hide()
450 table.insert(specitem_unused, item.specitem)
451 item.specitem = nil
455 --[=[
456 local was_inside = false
458 -- :ughh:
460 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
461 local count = 0
462 local quest
464 if objective.cat == "quest" then
465 quest = objective
466 else
467 quest = objective.quest
470 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
471 seen[quest] = true
473 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
475 if not level then
476 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
477 if not level then
478 level, name = 1, quest.obj
482 level = tonumber(level) or 1
484 count = count + 1
485 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
486 x = math.max(x, w)
487 y = y + h + gap
488 gap = 2
490 for obj in pairs(quest.swap_after or quest.after) do
491 if obj.progress then
492 table.insert(obj_list, obj)
496 table.sort(obj_list, objlist_sort)
498 for i, obj in ipairs(obj_list) do
499 local pct, text = 0, obj.obj
500 local seen_sum, seen_max = 0, 0
502 if obj.progress then
503 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
505 for user, progress in pairs(obj.progress) do
506 seen_sum = seen_sum + progress[3]
507 seen_max = seen_max + 1
508 seen_have[progress[1]] = true
509 seen_need[progress[2]] = true
512 if seen_max > 0 then
513 pct = seen_sum / seen_max
514 local list = QuestHelper:CreateTable()
516 for val in pairs(seen_have) do
517 table.insert(list, val)
520 table.sort(list)
522 local have = table.concat(list, ", ")
524 for i = #list,1,-1 do
525 list[i] = nil
528 for val in pairs(seen_need) do
529 table.insert(list, val)
532 if #list ~= 1 or list[1] ~= 1 then
533 -- If there is only one thing needed, ignore the progress, it's redundant.
534 -- It's either shown or it isn't.
536 table.sort(list)
538 local need = table.concat(list, ", ")
540 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
541 text, have, need)
544 QuestHelper:ReleaseTable(list)
547 QuestHelper:ReleaseTable(seen_have)
548 QuestHelper:ReleaseTable(seen_need)
551 if seen_sum ~= seen_max then
552 count = count + 1
553 w, h = addItem(oname(text, pct), quest, obj, -y)
554 x = math.max(x, w)
555 y = y + h
559 for i = #obj_list, 1, -1 do obj_list[i] = nil end
562 return x, y, gap, count
566 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
567 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
570 local hidden_vquest1 = { tracker_desc = QHText("QUESTS_HIDDEN_1"), tracker_clicked = QH_Hidden_Menu }
571 local hidden_vquest2 = { tracker_desc = " " .. QHText("QUESTS_HIDDEN_2"), tracker_clicked = QH_Hidden_Menu }
573 local route = {}
574 local pinned = {}
576 -- This is actually called surprisingly often.
577 function QH_Tracker_Rescan()
578 used_count = QuestHelper:CreateTable("tracker rescan used_count")
580 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
581 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
583 local y, depth = 0, 0
586 local had_pinned = false
588 local objs = QuestHelper:CreateTable("tracker objs")
589 for k, v in pairs(pinned) do
590 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
591 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
592 obj_done[k.cluster] = true
595 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
596 for k, v in pairs(objs) do
597 v.cluster = k
598 v.trackkey = k
599 table.insert(sort_objs, v)
602 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
604 for _, v in ipairs(sort_objs) do
605 y, depth = addMetaObjective(v.cluster, v, y, depth)
606 had_pinned = true
607 QuestHelper:ReleaseTable(v)
609 QuestHelper:ReleaseTable(sort_objs)
610 QuestHelper:ReleaseTable(objs)
612 if had_pinned then y = y + 10 end
615 if QuestHelper.loading_main then
616 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
617 local x, ty = addItem(loading_vquest, y)
618 y = ty + 10
620 if QuestHelper.flightpathing then
621 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
622 local x, ty = addItem(flightpath_vquest, y)
623 y = ty + 10
626 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
627 for k, v in ipairs(route) do
628 if not v.ignore then
629 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
630 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
635 local current_mo
636 local current_mo_cluster
637 for k, v in ipairs(route) do
638 if depth > QuestHelper_Pref.track_size and not debug_output then break end
639 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
640 if current_mo and v.why ~= current_mo then
641 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
642 QuestHelper:ReleaseTable(current_mo_cluster)
643 current_mo, current_mo_cluster = nil, nil
646 if not v.why.tracker_split then
647 if not mo_done[v.why] then
648 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
649 mo_done[v.why] = true
651 else
652 if not current_mo then
653 current_mo = v.why
654 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
656 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
659 obj_done[v] = true
662 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
663 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
665 if current_mo_cluster then
666 QuestHelper:ReleaseTable(current_mo_cluster)
670 -- now we check to see if we need a hidden display
671 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
672 local show = false
674 QH_Route_TraverseClusters(
675 function (clust)
676 if not show then
677 QH_Route_IgnoredReasons_Cluster(clust, function (reason)
678 show = true
679 end)
681 for _, v in ipairs(clust) do
682 QH_Route_IgnoredReasons_Node(v, function (reason)
683 show = true
684 end)
690 if show then
691 y = y + 10
692 _, y = addItem(hidden_vquest1, y)
693 _, y = addItem(hidden_vquest2, y)
698 -- any manipulations of the tracker should be done by now, everything after this is bookkeeping
700 for k, v in pairs(used_items) do
701 if not used_count[k] or used_count[k] < #v then
702 local ttp = QuestHelper:CreateTable("used_items ttp")
703 for m = 1, (used_count[k] or 0) do
704 table.insert(ttp, v[m])
706 for m = (used_count[k] or 0) + 1, #v do
707 removeUnusedItem(v[m])
710 if used_items[k] then
711 QuestHelper:ReleaseTable(used_items[k])
714 if #ttp > 0 then
715 used_items[k] = ttp
716 else
717 used_items[k] = nil
718 QuestHelper:ReleaseTable(ttp)
723 QuestHelper:ReleaseTable(mo_done)
724 QuestHelper:ReleaseTable(obj_done)
725 for k, v in pairs(metalookup) do
726 QuestHelper:ReleaseTable(v)
728 QuestHelper:ReleaseTable(metalookup)
730 QuestHelper:ReleaseTable(used_count)
731 used_count = nil
733 if y ~= tracker.dh then
734 tracker.t = 0
735 tracker.sh = tracker:GetHeight()
736 tracker.dh = y
737 tracker.sw = tracker.dw
738 resizing = true
746 --[[
747 if x ~= tracker.dw or y ~= tracker.dy then
748 tracker.t = 0
749 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
750 tracker.dw, tracker.dh = x, y
751 resizing = true
752 end]]
754 --[[
756 local quests = QuestHelper.quest_log
757 local added = 0
758 local x, y = 4, 4
759 local gap = 0
760 local track_size = QuestHelper_Pref.track_size
761 local quests_added = {}
763 for quest, objs in pairs(used_items) do
764 for obj, item in pairs(objs) do
765 item.used = false
769 for i, objective in pairs(QuestHelper.route) do
770 if objective.watched then
771 obj_index_lookup[objective] = i
775 for q, data in pairs(QuestHelper.quest_log) do
776 quest_lookup[data.index] = q
779 -- Add our little "not yet loaded" notification
780 local loadedshow = false
781 if not QuestHelper.Routing.map_walker then
782 local count
783 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
784 added = added + count
785 loadedshow = true
787 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
788 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
792 -- Add an extra large gap to seperate the notification from everything else
793 gap = gap * 5
795 -- Add Quests that are watched but not in the route.
796 if UberQuest then
797 local uq_settings = UberQuest_Config[UnitName("player")]
798 if uq_settings then
799 local list = uq_settings.selected
800 if list then
801 local i = 1
802 while true do
803 local name = GetQuestLogTitle(i)
804 if not name then break end
805 quest_lookup[name] = quest_lookup[i]
806 i = i + 1
808 for name in pairs(list) do
809 local q = quest_lookup[name]
810 if q and not obj_index_lookup[q] then
811 local count
812 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
813 added = added + count
814 quests_added[q] = true
819 else
820 for i = 1,GetNumQuestWatches() do
821 local q = quest_lookup[GetQuestIndexForWatch(i)]
822 if q and not obj_index_lookup[q] then
823 local count
824 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
825 added = added + count
830 -- Add Quests that are watched and are in the route.
831 for i, objective in ipairs(QuestHelper.route) do
832 local count
833 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
834 added = added + count
835 quests_added[objective] = true
838 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
839 gap = gap * 5
841 -- Add Quests that aren't watched and are in the route.
842 if added <= track_size then
843 for i, objective in ipairs(QuestHelper.route) do
844 local count
845 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
846 added = added + count
847 quests_added[objective] = true
849 if added > track_size then
850 break
855 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
856 local added = 0
857 local notadded = 0
858 for k, v in pairs(quest_lookup) do
859 if not quests_added[v] then
860 notadded = notadded + 1
861 else
862 added = added + 1
866 if notadded > 0 then
867 gap = gap * 10
868 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
869 added = added + count
870 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
871 added = added + count
875 for obj in pairs(obj_index_lookup) do
876 obj_index_lookup[obj] = nil
879 for key in pairs(quest_lookup) do
880 quest_lookup[key] = nil
883 for quest, objs in pairs(used_items) do
884 for obj, item in pairs(objs) do
885 if not item.used then
886 removeUnusedItem(quest, obj, item)
891 for key in pairs(seen) do
892 seen[key] = nil
895 y = y+4
897 if x ~= tracker.dw or y ~= tracker.dy then
898 tracker.t = 0
899 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
900 tracker.dw, tracker.dh = x, y
901 resizing = true
904 added = 0
908 function QH_Tracker_UpdateRoute(new_route)
909 route = new_route
910 QH_Tracker_Rescan()
913 function QH_Tracker_Pin(metaobjective, suppress)
914 if not pinned[metaobjective] then
915 pinned[metaobjective] = true
917 if not suppress then
918 QH_Tracker_Rescan()
923 function QH_Tracker_Unpin(metaobjective, suppress)
924 if pinned[metaobjective] then
925 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
927 if not suppress then
928 QH_Tracker_Rescan()
933 function QH_Tracker_SetPin(metaobjective, flag, suppress)
934 if flag then
935 QH_Tracker_Pin(metaobjective, suppress)
936 else
937 QH_Tracker_Unpin(metaobjective, suppress)
942 local check_delay = 4
944 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
945 function tracker:update(delta)
946 if not delta then
947 -- This is called without a value when the questlog is updated.
948 -- We'll make sure we update the display on the next update.
949 check_delay = 1e99
950 return
953 if resizing then
954 local t = self.t+delta
956 if t > 1 then
957 self:SetWidth(self.dw)
958 self:SetHeight(self.dh)
959 resizing = false
960 else
961 self.t = t
962 local it = 1-t
963 self:SetWidth(self.sw*it+self.dw*t)
964 self:SetHeight(self.sh*it+self.dh*t)
968 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
969 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
970 -- the tracker.
971 local x, y = GetCursorPosition()
972 local s = 1/self:GetEffectiveScale()
973 x, y = x*s, y*s
975 QuestHelper: Assert(x)
976 QuestHelper: Assert(y)
977 --[[ QuestHelper: Assert(self:GetLeft())
978 QuestHelper: Assert(self:GetBottom())
979 QuestHelper: Assert(self:GetRight())
980 QuestHelper: Assert(self:GetTop())]]
982 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
983 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
984 if inside ~= was_inside then
985 was_inside = inside
986 if inside then
987 minbutton:SetAlpha(.5)
988 elseif not QuestHelper_Pref.track_minimized then
989 minbutton:SetAlpha(0)
993 check_delay = check_delay + delta
994 if check_delay > 1 then
995 check_delay = 0
997 QH_Tracker_Rescan()
1001 QH_Hook(tracker, "OnUpdate", tracker.update)
1003 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
1004 --[[
1005 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
1007 function AddQuestWatch(...)
1008 tracker:update()
1009 return orig_AddQuestWatch(...)
1012 function RemoveQuestWatch(...)
1013 tracker:update()
1014 return orig_RemoveQuestWatch(...)
1015 end]]
1017 -------------------------------------------------------------------------------------------------
1018 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
1020 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
1021 local TrackerBackdropFound = false
1023 local function TrackerBackdropOnShow(self, ...)
1024 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1025 TrackerBackdropFound:Hide()
1028 if orig_TrackerBackdropOnShow then
1029 return orig_TrackerBackdropOnShow(self, ...)
1033 function tracker:HideDefaultTracker()
1034 -- The easy part: hide the original tracker
1035 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1036 WatchFrame_ClearDisplay()
1037 WatchFrame_Update()
1039 -- The harder part: hide all those little buttons
1041 local index = 1
1042 while true do
1043 local orig = _G["WatchFrameItem" .. tostring(index)]
1044 if orig then orig:Hide() else break end
1045 index = index + 1
1049 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1050 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1051 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1052 -- to keep checking. Hopefully, this won't happen too often.
1053 if not TrackerBackdropFound then
1054 if QuestWatchFrameBackdrop then
1055 -- Found bEQL's QuestWatchFrameBackdrop...
1056 TrackerBackdropFound = QuestWatchFrameBackdrop
1059 if TrackerBackdropFound then
1060 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1061 TrackerBackdropFound:Hide()
1063 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1064 QH_Hook(TrackerBackdropFound, "OnShow", TrackerBackdropOnShow)
1069 function tracker:ShowDefaultTracker()
1070 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1071 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1072 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1073 WatchFrame_Update()
1075 if TrackerBackdropFound then
1076 TrackerBackdropFound:Show()
1080 function QuestHelper:ShowTracker()
1081 tracker:HideDefaultTracker()
1082 minbutton:Show()
1084 if QuestHelper_Pref.track_minimized then
1085 minbutton:SetAlpha(.3)
1086 else
1087 minbutton:SetAlpha(0)
1088 tracker:Show()
1092 function QuestHelper:HideTracker()
1093 tracker:ShowDefaultTracker()
1094 tracker:Hide()
1095 minbutton:Hide()