will this help? maybe
[QuestHelper.git] / tracker.lua
blobc1c56514624985204eb4257131b7cae60ecf089f
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("BACKGROUND")
49 tracker.dw, tracker.dh = 200, 100
51 local in_tracker = 0
53 minbutton:SetFrameStrata("LOW")
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(24 / 1.6)
59 minbutton:SetHeight(24)
60 minbutton:SetFrameLevel(3)
61 local minbutton_tex = minbutton:CreateTexture()
62 minbutton_tex:SetAllPoints()
63 minbutton_tex:SetTexture(.6, .6, .6)
64 minbutton_tex:SetParent(minbutton)
66 local sigargh = CreateFrame("Frame", minbutton)
67 sigargh:SetFrameStrata("LOW")
68 sigargh:SetFrameLevel(4)
70 local sigil = sigargh:CreateTexture("BACKGROUND")
71 sigil:SetHeight(24)
72 sigil:SetWidth(24)
73 --sigil:SetPoint("CENTER", 0, 0)
74 sigil:SetTexture("Interface\\AddOns\\QuestHelper\\sigil")
75 sigil:SetPoint("CENTER", minbutton_tex, "CENTER")
78 tracker:SetPoint("CENTER", minbutton)
80 function minbutton:moved()
81 local x, y = self:GetCenter()
82 local w, h = UIParent:GetWidth(), UIParent:GetHeight()
83 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 "")
85 tracker:ClearAllPoints()
86 tracker:SetPoint("CENTER", self)
88 if anchor ~= "" then
89 tracker:SetPoint(anchor, self)
90 end
91 end
93 function QuestHelper:ResetTrackerPosition(cmd)
94 minbutton:ClearAllPoints()
95 if cmd and string.find(cmd, "center") then
96 minbutton:SetPoint("CENTER", nil, "CENTER", 100, 100)
97 else
98 minbutton:SetPoint("RIGHT", nil, "RIGHT", -20, 230)
99 end
100 minbutton:moved()
101 QuestHelper_Pref.track_minimized = false
102 tracker:Show()
103 self:TextOut("Quest tracker postion reset.")
106 QH_Event({"DISPLAY_SIZE_CHANGED", "PLAYER_ENTERING_WORLD"}, function () minbutton:moved() end)
108 QH_Hook(minbutton, "OnClick", function ()
109 QuestHelper_Pref.track_minimized = not QuestHelper_Pref.track_minimized
110 if QuestHelper_Pref.track_minimized then
111 tracker:Hide()
112 else
113 tracker:Show()
115 end)
117 minbutton:RegisterForDrag("LeftButton")
119 QH_Hook(minbutton, "OnDragStart", function(self)
120 if self:IsVisible() then
121 self:StartMoving()
122 QH_Hook(self, "OnUpdate", self.moved)
124 end)
126 QH_Hook(minbutton, "OnDragStop", function(self)
127 QH_Hook(self, "OnUpdate", nil)
128 self:StopMovingOrSizing()
129 self:moved()
130 end)
132 local entered_main = false
133 local entered_tracker = false
134 local function RefreshColor()
135 if entered_main then
136 minbutton:SetAlpha(1)
137 sigargh:SetAlpha(1)
138 elseif entered_tracker then
139 minbutton:SetAlpha(.3)
140 sigargh:SetAlpha(.3)
141 elseif QuestHelper_Pref.track_minimized then
142 minbutton:SetAlpha(.3)
143 sigargh:SetAlpha(.3)
144 else
145 minbutton:SetAlpha(0)
146 sigargh:SetAlpha(0)
149 local function SetEnteredMain(x)
150 entered_main = x
151 RefreshColor()
153 local function SetEnteredTracker(x)
154 entered_tracker = x
155 RefreshColor()
158 QH_Hook(minbutton, "OnEnter", function (self)
159 SetEnteredMain(true)
160 end)
162 QH_Hook(minbutton, "OnLeave", function (self)
163 SetEnteredMain(false)
164 end)
166 -- used_items[objective][index]
167 -- used_count[objective] is incremented as the last valid index
168 -- so, therefore, used_items[objective][used_count[objective]] is not nil
169 local used_items = {}
170 local used_count = {}
172 -- it's possible for an item to be in neither used_items nor recycled_items, if it's in the process of fading out
173 local recycled_items = {}
175 -- These two functions are basically identical. Combine them.
176 local function itemupdate(item, delta)
177 local done = true
179 local a = item:GetAlpha()
180 a = a + delta
182 if a < 1 then
183 item:SetAlpha(a)
184 done = false
185 else
186 item:SetAlpha(1)
189 local t = item.t + delta
191 if t < 1 then
192 item.t = t
193 local it = 1-t
194 local sp = math.sqrt(t-t*t)
195 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
196 done = false
197 else
198 item.t = 1
199 item.x, item.y = item.ex, item.ey
202 item:ClearAllPoints()
203 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
205 if done then
206 QH_Hook(item, "OnUpdate", nil)
210 local function itemfadeout(item, delta)
211 local a = item:GetAlpha()
212 a = a - delta
214 if a > 0 then
215 item:SetAlpha(a)
216 else
217 item:SetAlpha(1)
218 item:Hide()
219 QH_Hook(item, "OnUpdate", nil)
220 table.insert(recycled_items, item)
221 return
224 local t = item.t + delta
226 if t < 1 then
227 item.t = t
228 local it = 1-t
229 local sp = math.sqrt(t-t*t)
230 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
231 else
232 item.t = 1
233 item.x, item.y = item.ex, item.ey
236 item:ClearAllPoints()
237 item:SetPoint("TOPLEFT", tracker, "TOPLEFT", item.x, -item.y)
240 --[[function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
241 if (QuestLogFrame:IsShown()) then
242 HideUIPanel(QuestLogFrame);
243 else
244 ShowUIPanel(QuestLogFrame);
248 -- Grim stuff with uberquest, I need a better way to handle this
249 local function itemclick(item, button)
250 if button == "RightButton" then
251 local quest = item.quest
252 local index = 1
253 while true do
254 local title = GetQuestLogTitle(index)
255 if not title then break end
257 if title == quest then
258 if UberQuest then
259 -- UberQuest needs a little extra effort to work properly.
261 if UberQuest_List:IsShown() and GetQuestLogSelection() == index then
262 QH_ToggleQuestLog()
263 else
264 QuestLog_SetSelection(index)
266 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
267 -- and in the process update the frames to reflect the selected quest.
268 UberQuest_List:Hide()
269 UberQuest_Details:Show()
270 QH_ToggleQuestLog()
272 else
273 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
275 if QuestLogFrame:IsShown() and GetQuestLogSelection() == index then
276 -- If the selected quest is already being shown, hide it.
277 QH_ToggleQuestLog()
278 else
279 -- Otherwise, select it and show it.
280 QuestLog_SetSelection(index)
282 if not QuestLogFrame:IsShown() then
283 QH_ToggleQuestLog()
288 return
291 index = index + 1
294 end]]
296 local function allocateItem()
297 local item
299 item = table.remove(recycled_items)
300 if item then return item end
302 item = CreateFrame("Frame", nil, tracker)
303 item.text = item:CreateFontString()
304 item.text:SetShadowColor(0, 0, 0, .8)
305 item.text:SetShadowOffset(1, -1)
306 item.text:SetPoint("TOPLEFT", item)
307 return item
310 local specitem_max = 1
311 local specitem_unused = {}
313 -- 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
314 local function addItem(objective, y, meta)
315 local obj_key = objective
316 if obj_key.cluster then obj_key = obj_key.cluster end
317 used_count[obj_key] = (used_count[obj_key] or 0) + 1
318 if not used_items[obj_key] then used_items[obj_key] = QuestHelper:CreateTable("additem used_items") end
319 local item = used_items[obj_key][used_count[obj_key]]
321 local x = meta and 4 or 20
323 if not item then
324 used_items[obj_key][used_count[obj_key]] = allocateItem()
325 item = used_items[obj_key][used_count[obj_key]]
327 if meta then
328 item.text:SetFont(QuestHelper.font.serif, 12)
329 item.text:SetTextColor(.82, .65, 0)
330 else
331 item.text:SetFont(QuestHelper.font.sans, 12)
332 item.text:SetTextColor(.82, .82, .82)
335 item.obj = objective
337 item.sx, item.sy, item.x, item.y, item.ex, item.ey, item.t = x+30, y, x, y, x, y, 0
338 QH_Hook(item, "OnUpdate", itemupdate)
339 item:SetAlpha(0)
340 item:Show()
343 item.text:SetText(item.obj.tracker_desc or "(no description)")
345 local w, h = item.text:GetWidth(), item.text:GetHeight()
346 item:SetWidth(w)
347 item:SetHeight(h)
349 if objective.tracker_clicked then
350 QH_Hook(item, "OnMouseDown", function (self, button) if button == "RightButton" then objective.tracker_clicked() end end)
351 item:EnableMouse(true)
354 if item.ex ~= x or item.ey ~= y then
355 item.sx, item.sy, item.ex, item.ey = item.x, item.y, x, y
356 item.t = 0
357 QH_Hook(item, "OnUpdate", itemupdate)
360 -- we're just going to recycle this each time
361 if item.specitem then
362 item.specitem:Hide()
363 table.insert(specitem_unused, item.specitem)
364 item.specitem = nil
367 local spacer = 0
368 -- hacky - progress only shows up if we're not on a metaobjective. wheee
369 if objective.type_quest and objective.type_quest.index and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
370 item.specitem = table.remove(specitem_unused)
371 if not item.specitem then
372 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
373 QuestHelper: Assert(item.specitem)
375 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
376 QuestHelper: Assert(rangey)
377 local fn, fh, ff = rangey:GetFont()
378 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
379 rangey:SetText(RANGE_INDICATOR)
380 rangey:ClearAllPoints()
381 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
383 specitem_max = specitem_max + 1
386 item.specitem:SetScale(0.9)
387 item.specitem:ClearAllPoints()
388 item.specitem:SetParent(item)
389 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
391 local _, tex, charges = GetQuestLogSpecialItemInfo(objective.type_quest.index)
392 item.specitem:SetID(objective.type_quest.index)
393 SetItemButtonTexture(item.specitem, tex)
394 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
395 item.specitem.charges = charges
397 item.specitem:Show()
399 spacer = h
402 return w+x+4, y+h, y+h+spacer
405 local function addMetaObjective(metaobj, items, y, depth)
406 local seen_texts = QuestHelper:CreateTable("amo_seen_texts")
408 local x, spacer
409 x, y, spacer = addItem(metaobj, y, true)
410 for _, v in ipairs(items) do
411 if not v.tracker_hide_dupes or not seen_texts[v.tracker_desc] then
412 x, y = addItem(v, y, false)
413 seen_texts[v.tracker_desc] = true
416 return math.max(y, spacer), depth + #items + 1
419 --[[ -- these will be plugged in later one way or another
420 local function ccode(r1, g1, b1, r2, g2, b2, p)
421 local ip
422 p, ip = p*255, 255-p*255
423 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
426 local function qname(title, level)
427 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
428 title = string.format("[%d] %s", level, title)
431 if level == 7778 then
432 level = -7778
435 if QuestHelper_Pref.track_qcolour then
436 local player_level = QuestHelper.player_level
437 local delta = level - player_level
439 local colour
441 if delta >= 5 then
442 colour = "|cffff0000"
443 elseif delta >= 0 then
444 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
445 else
446 local grey
448 if player_level >= 60 then grey = player_level - 9
449 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
450 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
451 else grey = 0 end
453 if level == -7778 then
454 colour = "|cff808080"
455 elseif level > grey then
456 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
457 else
458 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
462 title = string.format("%s%s", colour, title)
465 return title
468 local function oname(text, pct)
469 if QuestHelper_Pref.track_ocolour then
470 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)
473 return text
477 local function removeUnusedItem(item)
478 item.t = 0
479 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
480 QH_Hook(item, "OnMouseDown", nil)
481 item:EnableMouse(false)
482 QH_Hook(item, "OnUpdate", itemfadeout)
484 if item.specitem then
485 item.specitem:Hide()
486 table.insert(specitem_unused, item.specitem)
487 item.specitem = nil
491 --[=[
492 local was_inside = false
494 -- :ughh:
496 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
497 local count = 0
498 local quest
500 if objective.cat == "quest" then
501 quest = objective
502 else
503 quest = objective.quest
506 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
507 seen[quest] = true
509 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
511 if not level then
512 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
513 if not level then
514 level, name = 1, quest.obj
518 level = tonumber(level) or 1
520 count = count + 1
521 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
522 x = math.max(x, w)
523 y = y + h + gap
524 gap = 2
526 for obj in pairs(quest.swap_after or quest.after) do
527 if obj.progress then
528 table.insert(obj_list, obj)
532 table.sort(obj_list, objlist_sort)
534 for i, obj in ipairs(obj_list) do
535 local pct, text = 0, obj.obj
536 local seen_sum, seen_max = 0, 0
538 if obj.progress then
539 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
541 for user, progress in pairs(obj.progress) do
542 seen_sum = seen_sum + progress[3]
543 seen_max = seen_max + 1
544 seen_have[progress[1]] = true
545 seen_need[progress[2]] = true
548 if seen_max > 0 then
549 pct = seen_sum / seen_max
550 local list = QuestHelper:CreateTable()
552 for val in pairs(seen_have) do
553 table.insert(list, val)
556 table.sort(list)
558 local have = table.concat(list, ", ")
560 for i = #list,1,-1 do
561 list[i] = nil
564 for val in pairs(seen_need) do
565 table.insert(list, val)
568 if #list ~= 1 or list[1] ~= 1 then
569 -- If there is only one thing needed, ignore the progress, it's redundant.
570 -- It's either shown or it isn't.
572 table.sort(list)
574 local need = table.concat(list, ", ")
576 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
577 text, have, need)
580 QuestHelper:ReleaseTable(list)
583 QuestHelper:ReleaseTable(seen_have)
584 QuestHelper:ReleaseTable(seen_need)
587 if seen_sum ~= seen_max then
588 count = count + 1
589 w, h = addItem(oname(text, pct), quest, obj, -y)
590 x = math.max(x, w)
591 y = y + h
595 for i = #obj_list, 1, -1 do obj_list[i] = nil end
598 return x, y, gap, count
602 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
603 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
604 local recalculating_vquest = {tracker_desc = QHFormat("QH_RECALCULATING", "0")}
606 local recalculating_start = nil
609 local hidden_vquest1 = { tracker_desc = QHText("QUESTS_HIDDEN_1"), tracker_clicked = QH_Hidden_Menu }
610 local hidden_vquest2 = { tracker_desc = " " .. QHText("QUESTS_HIDDEN_2"), tracker_clicked = QH_Hidden_Menu }
612 local route = {}
613 local pinned = {}
615 -- This is actually called surprisingly often.
616 function QH_Tracker_Rescan()
617 used_count = QuestHelper:CreateTable("tracker rescan used_count")
619 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
620 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
622 local y, depth = 0, 0
625 local had_pinned = false
627 local objs = QuestHelper:CreateTable("tracker objs")
628 for k, v in pairs(pinned) do
629 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
630 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
631 obj_done[k.cluster] = true
634 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
635 for k, v in pairs(objs) do
636 v.cluster = k
637 v.trackkey = k
638 table.insert(sort_objs, v)
641 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
643 for _, v in ipairs(sort_objs) do
644 y, depth = addMetaObjective(v.cluster, v, y, depth)
645 had_pinned = true
646 QuestHelper:ReleaseTable(v)
648 QuestHelper:ReleaseTable(sort_objs)
649 QuestHelper:ReleaseTable(objs)
651 if had_pinned then y = y + 10 end
654 if QuestHelper.loading_main then
655 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
656 local x, ty = addItem(loading_vquest, y)
657 y = ty + 10
659 if QuestHelper.flightpathing then
660 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
661 local x, ty = addItem(flightpath_vquest, y)
662 y = ty + 10
664 if not QuestHelper.loading_main and not QuestHelper.flightpathing and QuestHelper.route_change_progress then
665 if recalculating_start then
666 if recalculating_start + 5 < GetTime() then
667 recalculating_vquest.tracker_desc = QHFormat("QH_RECALCULATING", string.format("%d", QuestHelper.route_change_progress:GetPercentage() * 100))
668 local x, ty = addItem(recalculating_vquest, y)
669 y = ty + 10
671 else
672 recalculating_start = GetTime()
674 else
675 recalculating_start = nil
678 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
679 for k, v in ipairs(route) do
680 if not v.ignore then
681 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
682 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
687 local current_mo
688 local current_mo_cluster
689 for k, v in ipairs(route) do
690 if depth > QuestHelper_Pref.track_size and not debug_output then break end
691 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
692 if current_mo and v.why ~= current_mo and (v.why.tracker_split or not mo_done[v.why]) then
693 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
694 QuestHelper:ReleaseTable(current_mo_cluster)
695 current_mo, current_mo_cluster = nil, nil
698 if not v.why.tracker_split then
699 if not mo_done[v.why] then
700 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
701 mo_done[v.why] = true
703 else
704 if not current_mo then
705 current_mo = v.why
706 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
708 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
711 obj_done[v] = true
714 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
715 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
717 if current_mo_cluster then
718 QuestHelper:ReleaseTable(current_mo_cluster)
722 -- now we check to see if we need a hidden display
723 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
724 local show = false
726 QH_Route_TraverseClusters(
727 function (clust)
728 if not show then
729 QH_Route_IgnoredReasons_Cluster(clust, function (reason)
730 show = true
731 end)
733 for _, v in ipairs(clust) do
734 QH_Route_IgnoredReasons_Node(v, function (reason)
735 show = true
736 end)
742 if show then
743 y = y + 10
744 _, y = addItem(hidden_vquest1, y)
745 _, y = addItem(hidden_vquest2, y)
750 -- any manipulations of the tracker should be done by now, everything after this is bookkeeping
752 for k, v in pairs(used_items) do
753 if not used_count[k] or used_count[k] < #v then
754 local ttp = QuestHelper:CreateTable("used_items ttp")
755 for m = 1, (used_count[k] or 0) do
756 table.insert(ttp, v[m])
758 for m = (used_count[k] or 0) + 1, #v do
759 removeUnusedItem(v[m])
762 if used_items[k] then
763 QuestHelper:ReleaseTable(used_items[k])
766 if #ttp > 0 then
767 used_items[k] = ttp
768 else
769 used_items[k] = nil
770 QuestHelper:ReleaseTable(ttp)
775 QuestHelper:ReleaseTable(mo_done)
776 QuestHelper:ReleaseTable(obj_done)
777 for k, v in pairs(metalookup) do
778 QuestHelper:ReleaseTable(v)
780 QuestHelper:ReleaseTable(metalookup)
782 QuestHelper:ReleaseTable(used_count)
783 used_count = nil
785 if y ~= tracker.dh then
786 tracker.t = 0
787 tracker.sh = tracker:GetHeight()
788 tracker.dh = y
789 tracker.sw = tracker.dw
790 resizing = true
798 --[[
799 if x ~= tracker.dw or y ~= tracker.dy then
800 tracker.t = 0
801 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
802 tracker.dw, tracker.dh = x, y
803 resizing = true
804 end]]
806 --[[
808 local quests = QuestHelper.quest_log
809 local added = 0
810 local x, y = 4, 4
811 local gap = 0
812 local track_size = QuestHelper_Pref.track_size
813 local quests_added = {}
815 for quest, objs in pairs(used_items) do
816 for obj, item in pairs(objs) do
817 item.used = false
821 for i, objective in pairs(QuestHelper.route) do
822 if objective.watched then
823 obj_index_lookup[objective] = i
827 for q, data in pairs(QuestHelper.quest_log) do
828 quest_lookup[data.index] = q
831 -- Add our little "not yet loaded" notification
832 local loadedshow = false
833 if not QuestHelper.Routing.map_walker then
834 local count
835 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
836 added = added + count
837 loadedshow = true
839 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
840 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
844 -- Add an extra large gap to seperate the notification from everything else
845 gap = gap * 5
847 -- Add Quests that are watched but not in the route.
848 if UberQuest then
849 local uq_settings = UberQuest_Config[UnitName("player")]
850 if uq_settings then
851 local list = uq_settings.selected
852 if list then
853 local i = 1
854 while true do
855 local name = GetQuestLogTitle(i)
856 if not name then break end
857 quest_lookup[name] = quest_lookup[i]
858 i = i + 1
860 for name in pairs(list) do
861 local q = quest_lookup[name]
862 if q and not obj_index_lookup[q] then
863 local count
864 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
865 added = added + count
866 quests_added[q] = true
871 else
872 for i = 1,GetNumQuestWatches() do
873 local q = quest_lookup[GetQuestIndexForWatch(i)]
874 if q and not obj_index_lookup[q] then
875 local count
876 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
877 added = added + count
882 -- Add Quests that are watched and are in the route.
883 for i, objective in ipairs(QuestHelper.route) do
884 local count
885 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
886 added = added + count
887 quests_added[objective] = true
890 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
891 gap = gap * 5
893 -- Add Quests that aren't watched and are in the route.
894 if added <= track_size then
895 for i, objective in ipairs(QuestHelper.route) do
896 local count
897 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
898 added = added + count
899 quests_added[objective] = true
901 if added > track_size then
902 break
907 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
908 local added = 0
909 local notadded = 0
910 for k, v in pairs(quest_lookup) do
911 if not quests_added[v] then
912 notadded = notadded + 1
913 else
914 added = added + 1
918 if notadded > 0 then
919 gap = gap * 10
920 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
921 added = added + count
922 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
923 added = added + count
927 for obj in pairs(obj_index_lookup) do
928 obj_index_lookup[obj] = nil
931 for key in pairs(quest_lookup) do
932 quest_lookup[key] = nil
935 for quest, objs in pairs(used_items) do
936 for obj, item in pairs(objs) do
937 if not item.used then
938 removeUnusedItem(quest, obj, item)
943 for key in pairs(seen) do
944 seen[key] = nil
947 y = y+4
949 if x ~= tracker.dw or y ~= tracker.dy then
950 tracker.t = 0
951 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
952 tracker.dw, tracker.dh = x, y
953 resizing = true
956 added = 0
960 function QH_Tracker_UpdateRoute(new_route)
961 route = new_route
962 QH_Tracker_Rescan()
965 function QH_Tracker_Pin(metaobjective, suppress)
966 if not pinned[metaobjective] then
967 pinned[metaobjective] = true
969 if not suppress then
970 QH_Tracker_Rescan()
975 function QH_Tracker_Unpin(metaobjective, suppress)
976 if pinned[metaobjective] then
977 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
979 if not suppress then
980 QH_Tracker_Rescan()
985 function QH_Tracker_SetPin(metaobjective, flag, suppress)
986 if flag then
987 QH_Tracker_Pin(metaobjective, suppress)
988 else
989 QH_Tracker_Unpin(metaobjective, suppress)
994 local check_delay = 4
996 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
997 function tracker:update(delta)
998 if not delta then
999 -- This is called without a value when the questlog is updated.
1000 -- We'll make sure we update the display on the next update.
1001 check_delay = 1e99
1002 return
1005 if resizing then
1006 local t = self.t+delta
1008 if t > 1 then
1009 self:SetWidth(self.dw)
1010 self:SetHeight(self.dh)
1011 resizing = false
1012 else
1013 self.t = t
1014 local it = 1-t
1015 self:SetWidth(self.sw*it+self.dw*t)
1016 self:SetHeight(self.sh*it+self.dh*t)
1020 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
1021 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
1022 -- the tracker.
1023 local x, y = GetCursorPosition()
1024 local s = 1/self:GetEffectiveScale()
1025 x, y = x*s, y*s
1027 QuestHelper: Assert(x)
1028 QuestHelper: Assert(y)
1029 --[[ QuestHelper: Assert(self:GetLeft())
1030 QuestHelper: Assert(self:GetBottom())
1031 QuestHelper: Assert(self:GetRight())
1032 QuestHelper: Assert(self:GetTop())]]
1034 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
1035 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
1036 if inside ~= was_inside then
1037 was_inside = inside
1038 if inside then
1039 SetEnteredTracker(true)
1040 else
1041 SetEnteredTracker(false)
1045 check_delay = check_delay + delta
1046 if check_delay > 1 then
1047 check_delay = 0
1049 QH_Tracker_Rescan()
1053 QH_Hook(tracker, "OnUpdate", tracker.update)
1055 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
1056 --[[
1057 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
1059 function AddQuestWatch(...)
1060 tracker:update()
1061 return orig_AddQuestWatch(...)
1064 function RemoveQuestWatch(...)
1065 tracker:update()
1066 return orig_RemoveQuestWatch(...)
1067 end]]
1069 -------------------------------------------------------------------------------------------------
1070 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
1072 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
1073 local TrackerBackdropFound = false
1075 local function TrackerBackdropOnShow(self, ...)
1076 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1077 TrackerBackdropFound:Hide()
1080 if orig_TrackerBackdropOnShow then
1081 return orig_TrackerBackdropOnShow(self, ...)
1085 function tracker:HideDefaultTracker()
1086 -- The easy part: hide the original tracker
1087 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1088 WatchFrame_ClearDisplay()
1089 WatchFrame_Update()
1091 -- The harder part: hide all those little buttons
1093 local index = 1
1094 while true do
1095 local orig = _G["WatchFrameItem" .. tostring(index)]
1096 if orig then orig:Hide() else break end
1097 index = index + 1
1101 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1102 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1103 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1104 -- to keep checking. Hopefully, this won't happen too often.
1105 if not TrackerBackdropFound then
1106 if QuestWatchFrameBackdrop then
1107 -- Found bEQL's QuestWatchFrameBackdrop...
1108 TrackerBackdropFound = QuestWatchFrameBackdrop
1111 if TrackerBackdropFound then
1112 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1113 TrackerBackdropFound:Hide()
1115 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1116 QH_Hook(TrackerBackdropFound, "OnShow", TrackerBackdropOnShow)
1121 function tracker:ShowDefaultTracker()
1122 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1123 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1124 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1125 WatchFrame_Update()
1127 if TrackerBackdropFound then
1128 TrackerBackdropFound:Show()
1132 function QuestHelper:ShowTracker()
1133 tracker:HideDefaultTracker()
1134 minbutton:Show()
1136 RefreshColor()
1137 if not QuestHelper_Pref.track_minimized then
1138 tracker:Show()
1142 function QuestHelper:HideTracker()
1143 tracker:ShowDefaultTracker()
1144 tracker:Hide()
1145 minbutton:Hide()