objectives work, I think
[QuestHelper.git] / tracker.lua
blobcb3edfd6f0244a429f2607991e2c0dda8a56b1b0
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 local spacer = 0
330 -- hacky - progress only shows up if we're not on a metaobjective. wheee
331 if objective.type_quest and objective.type_quest.index and not objective.progress and GetQuestLogSpecialItemInfo(objective.type_quest.index) then
332 item.specitem = table.remove(specitem_unused)
333 if not item.specitem then
334 item.specitem = CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max), item, "WatchFrameItemButtonTemplate")
335 QuestHelper: Assert(item.specitem)
337 local rangey = _G["QH_SpecItem_" .. tostring(specitem_max) .. "HotKey"]
338 QuestHelper: Assert(rangey)
339 local fn, fh, ff = rangey:GetFont()
340 rangey:SetFont("Fonts\\ARIALN.TTF", fh, ff)
341 rangey:SetText(RANGE_INDICATOR)
342 rangey:ClearAllPoints()
343 rangey:SetPoint("BOTTOMRIGHT", item.specitem, "BOTTOMRIGHT", 0, 2)
345 specitem_max = specitem_max + 1
348 item.specitem:SetScale(0.9)
349 item.specitem:ClearAllPoints()
350 item.specitem:SetParent(item)
351 item.specitem:SetPoint("TOPRIGHT", item, "TOPLEFT", 0, 0)
353 local _, tex = GetQuestLogSpecialItemInfo(objective.type_quest.index)
354 item.specitem:SetID(objective.type_quest.index)
355 SetItemButtonTexture(item.specitem, tex)
356 item.specitem.rangeTimer = -1 -- This makes the little dot go away. Why does it do that?
358 item.specitem:Show()
360 spacer = h
363 return w+x+4, y+h, y+h+spacer
366 local function addMetaObjective(metaobj, items, y, depth)
367 local x, spacer
368 x, y, spacer = addItem(metaobj, y, true)
369 for _, v in ipairs(items) do
370 x, y = addItem(v, y, false)
372 return math.max(y, spacer), depth + #items + 1
375 --[[ -- these will be plugged in later one way or another
376 local function ccode(r1, g1, b1, r2, g2, b2, p)
377 local ip
378 p, ip = p*255, 255-p*255
379 return string.format("|cff%02x%02x%02x", r1*ip+r2*p, g1*ip+g2*p, b1*ip+b2*p)
382 local function qname(title, level)
383 if QuestHelper_Pref.track_level and level ~= 7777 and level ~= 7778 then
384 title = string.format("[%d] %s", level, title)
387 if level == 7778 then
388 level = -7778
391 if QuestHelper_Pref.track_qcolour then
392 local player_level = QuestHelper.player_level
393 local delta = level - player_level
395 local colour
397 if delta >= 5 then
398 colour = "|cffff0000"
399 elseif delta >= 0 then
400 colour = ccode(1, 1, 0, 1, 0, 0, delta/5)
401 else
402 local grey
404 if player_level >= 60 then grey = player_level - 9
405 elseif player_level >= 40 then grey = player_level - math.floor(player_level/5) - 1
406 elseif player_level >= 6 then grey = player_level - math.floor(player_level/10) - 5
407 else grey = 0 end
409 if level == -7778 then
410 colour = "|cff808080"
411 elseif level > grey then
412 colour = ccode(0, 1, 0, 1, 1, 0, (grey-level)/(grey-player_level))
413 else
414 colour = ccode(.4, .4, .4, .2, .8, .2, (1-level)/(1-grey))
418 title = string.format("%s%s", colour, title)
421 return title
424 local function oname(text, pct)
425 if QuestHelper_Pref.track_ocolour then
426 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)
429 return text
433 local function removeUnusedItem(item)
434 item.t = 0
435 item.sx, item.sy, item.dx, item.dy = item.x, item.y, item.x+30, item.y
436 item:SetScript("OnMouseDown", nil)
437 item:EnableMouse(false)
438 item:SetScript("OnUpdate", itemfadeout)
440 if item.specitem then
441 item.specitem:Hide()
442 table.insert(specitem_unused, item.specitem)
443 item.specitem = nil
447 --[=[
448 local was_inside = false
450 -- :ughh:
452 local function addobj(objective, seen, obj_index_lookup, filter, x, y, gap)
453 local count = 0
454 local quest
456 if objective.cat == "quest" then
457 quest = objective
458 else
459 quest = objective.quest
462 if quest and quest.watched and not seen[quest] and (not filter or filter(quest)) then
463 seen[quest] = true
465 local level, name = string.match(quest.obj, "^(%d+)/%d*/(.*)$")
467 if not level then
468 level, name = string.match(quest.obj, "^(%d+)/(.*)$")
469 if not level then
470 level, name = 1, quest.obj
474 level = tonumber(level) or 1
476 count = count + 1
477 local w, h = addItem(qname(name, level), true, quest, -(y+gap), name, quest.index)
478 x = math.max(x, w)
479 y = y + h + gap
480 gap = 2
482 for obj in pairs(quest.swap_after or quest.after) do
483 if obj.progress then
484 table.insert(obj_list, obj)
488 table.sort(obj_list, objlist_sort)
490 for i, obj in ipairs(obj_list) do
491 local pct, text = 0, obj.obj
492 local seen_sum, seen_max = 0, 0
494 if obj.progress then
495 local seen_have, seen_need = QuestHelper:CreateTable(), QuestHelper:CreateTable()
497 for user, progress in pairs(obj.progress) do
498 seen_sum = seen_sum + progress[3]
499 seen_max = seen_max + 1
500 seen_have[progress[1]] = true
501 seen_need[progress[2]] = true
504 if seen_max > 0 then
505 pct = seen_sum / seen_max
506 local list = QuestHelper:CreateTable()
508 for val in pairs(seen_have) do
509 table.insert(list, val)
512 table.sort(list)
514 local have = table.concat(list, ", ")
516 for i = #list,1,-1 do
517 list[i] = nil
520 for val in pairs(seen_need) do
521 table.insert(list, val)
524 if #list ~= 1 or list[1] ~= 1 then
525 -- If there is only one thing needed, ignore the progress, it's redundant.
526 -- It's either shown or it isn't.
528 table.sort(list)
530 local need = table.concat(list, ", ")
532 text = string.format((tonumber(have) and tonumber(need) and QUEST_ITEMS_NEEDED) or QUEST_FACTION_NEEDED,
533 text, have, need)
536 QuestHelper:ReleaseTable(list)
539 QuestHelper:ReleaseTable(seen_have)
540 QuestHelper:ReleaseTable(seen_need)
543 if seen_sum ~= seen_max then
544 count = count + 1
545 w, h = addItem(oname(text, pct), quest, obj, -y)
546 x = math.max(x, w)
547 y = y + h
551 for i = #obj_list, 1, -1 do obj_list[i] = nil end
554 return x, y, gap, count
558 local loading_vquest = {tracker_desc = QHFormat("QH_LOADING", "0")}
559 local flightpath_vquest = {tracker_desc = QHFormat("QH_FLIGHTPATH", "0")}
561 --local hidden_vquest1 = { cat = "quest", obj = "7778/" .. QHText("QUESTS_HIDDEN_1"), after = {}, watched = true }
562 --local hidden_vquest2 = { cat = "quest", obj = "7778/ " .. QHText("QUESTS_HIDDEN_2"), after = {}, watched = true }
564 local route = {}
565 local pinned = {}
567 -- This is actually called surprisingly often.
568 function QH_Tracker_Rescan()
569 used_count = QuestHelper:CreateTable("tracker rescan used_count")
571 local mo_done = QuestHelper:CreateTable("tracker rescan mo_done")
572 local obj_done = QuestHelper:CreateTable("tracker rescan obj_done")
574 local y, depth = 0, 0
577 local had_pinned = false
579 local objs = QuestHelper:CreateTable("tracker objs")
580 for k, v in pairs(pinned) do
581 if not objs[k.why] then objs[k.why] = QuestHelper:CreateTable("tracker objs sub") end
582 if not k.ignore and not k.tracker_hidden then table.insert(objs[k.why], k) end
583 obj_done[k.cluster] = true
586 local sort_objs = QuestHelper:CreateTable("tracker sobjs")
587 for k, v in pairs(objs) do
588 v.cluster = k
589 v.trackkey = k
590 table.insert(sort_objs, v)
593 table.sort(sort_objs, function (a, b) return tostring(a.trackkey) < tostring(b.trackkey) end)
595 for _, v in ipairs(sort_objs) do
596 y, depth = addMetaObjective(v.cluster, v, y, depth)
597 had_pinned = true
598 QuestHelper:ReleaseTable(v)
600 QuestHelper:ReleaseTable(sort_objs)
601 QuestHelper:ReleaseTable(objs)
603 if had_pinned then y = y + 10 end
606 if QuestHelper.loading_main then
607 loading_vquest.tracker_desc = QHFormat("QH_LOADING", string.format("%d", QuestHelper.loading_main:GetPercentage() * 100))
608 local x, ty = addItem(loading_vquest, y)
609 y = ty + 10
611 if QuestHelper.flightpathing then
612 flightpath_vquest.tracker_desc = QHFormat("QH_FLIGHTPATH", string.format("%d", QuestHelper.flightpathing:GetPercentage() * 100))
613 local x, ty = addItem(flightpath_vquest, y)
614 y = ty + 10
617 local metalookup = QuestHelper:CreateTable("tracker rescan metalookup")
618 for k, v in ipairs(route) do
619 if not v.ignore then
620 if not metalookup[v.why] then metalookup[v.why] = QuestHelper:CreateTable("tracker rescan metalookup item") end
621 if not v.tracker_hidden then table.insert(metalookup[v.why], v) end
626 local current_mo
627 local current_mo_cluster
628 for k, v in ipairs(route) do
629 if depth > QuestHelper_Pref.track_size and not debug_output then break end
630 if not v.ignore and not v.why.tracker_hidden and not obj_done[v.cluster] then
631 if current_mo and v.why ~= current_mo then
632 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
633 QuestHelper:ReleaseTable(current_mo_cluster)
634 current_mo, current_mo_cluster = nil, nil
637 if not v.why.tracker_split then
638 if not mo_done[v.why] then
639 y, depth = addMetaObjective(v.why, metalookup[v.why], y, depth)
640 mo_done[v.why] = true
642 else
643 if not current_mo then
644 current_mo = v.why
645 current_mo_cluster = QuestHelper:CreateTable("tracker current cluster")
647 if not v.tracker_hidden then table.insert(current_mo_cluster, v) end
650 obj_done[v] = true
653 if current_mo and not (depth > QuestHelper_Pref.track_size and not debug_output) then
654 y, depth = addMetaObjective(current_mo, current_mo_cluster, y, depth)
656 if current_mo_cluster then
657 QuestHelper:ReleaseTable(current_mo_cluster)
661 for k, v in pairs(used_items) do
662 if not used_count[k] or used_count[k] < #v then
663 local ttp = QuestHelper:CreateTable("used_items ttp")
664 for m = 1, (used_count[k] or 0) do
665 table.insert(ttp, v[m])
667 for m = (used_count[k] or 0) + 1, #v do
668 removeUnusedItem(v[m])
671 if used_items[k] then
672 QuestHelper:ReleaseTable(used_items[k])
675 if #ttp > 0 then
676 used_items[k] = ttp
677 else
678 used_items[k] = nil
679 QuestHelper:ReleaseTable(ttp)
684 QuestHelper:ReleaseTable(mo_done)
685 QuestHelper:ReleaseTable(obj_done)
686 for k, v in pairs(metalookup) do
687 QuestHelper:ReleaseTable(v)
689 QuestHelper:ReleaseTable(metalookup)
691 QuestHelper:ReleaseTable(used_count)
692 used_count = nil
694 if y ~= tracker.dh then
695 tracker.t = 0
696 tracker.sh = tracker:GetHeight()
697 tracker.dh = y
698 tracker.sw = tracker.dw
699 resizing = true
702 --[[
703 if x ~= tracker.dw or y ~= tracker.dy then
704 tracker.t = 0
705 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
706 tracker.dw, tracker.dh = x, y
707 resizing = true
708 end]]
710 --[[
712 local quests = QuestHelper.quest_log
713 local added = 0
714 local x, y = 4, 4
715 local gap = 0
716 local track_size = QuestHelper_Pref.track_size
717 local quests_added = {}
719 for quest, objs in pairs(used_items) do
720 for obj, item in pairs(objs) do
721 item.used = false
725 for i, objective in pairs(QuestHelper.route) do
726 if objective.watched then
727 obj_index_lookup[objective] = i
731 for q, data in pairs(QuestHelper.quest_log) do
732 quest_lookup[data.index] = q
735 -- Add our little "not yet loaded" notification
736 local loadedshow = false
737 if not QuestHelper.Routing.map_walker then
738 local count
739 x, y, gap, count = addobj(loading_vquest, seen, nil, nil, x, y, gap)
740 added = added + count
741 loadedshow = true
743 if QuestHelper_Flight_Updates and QuestHelper_Flight_Updates_Current and QuestHelper_Flight_Updates > 0 and QuestHelper_Flight_Updates_Current < QuestHelper_Flight_Updates then
744 loading_vquest.obj = string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current * 100 / QuestHelper_Flight_Updates)))
748 -- Add an extra large gap to seperate the notification from everything else
749 gap = gap * 5
751 -- Add Quests that are watched but not in the route.
752 if UberQuest then
753 local uq_settings = UberQuest_Config[UnitName("player")]
754 if uq_settings then
755 local list = uq_settings.selected
756 if list then
757 local i = 1
758 while true do
759 local name = GetQuestLogTitle(i)
760 if not name then break end
761 quest_lookup[name] = quest_lookup[i]
762 i = i + 1
764 for name in pairs(list) do
765 local q = quest_lookup[name]
766 if q and not obj_index_lookup[q] then
767 local count
768 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
769 added = added + count
770 quests_added[q] = true
775 else
776 for i = 1,GetNumQuestWatches() do
777 local q = quest_lookup[GetQuestIndexForWatch(i)]
778 if q and not obj_index_lookup[q] then
779 local count
780 x, y, gap, count = addobj(q, seen, obj_index_lookup, nil, x, y, gap)
781 added = added + count
786 -- Add Quests that are watched and are in the route.
787 for i, objective in ipairs(QuestHelper.route) do
788 local count
789 x, y, gap, count = addobj(objective, seen, obj_index_lookup, watched_filter, x, y, gap)
790 added = added + count
791 quests_added[objective] = true
794 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
795 gap = gap * 5
797 -- Add Quests that aren't watched and are in the route.
798 if added <= track_size then
799 for i, objective in ipairs(QuestHelper.route) do
800 local count
801 x, y, gap, count = addobj(objective, seen, obj_index_lookup, nil, x, y, gap)
802 added = added + count
803 quests_added[objective] = true
805 if added > track_size then
806 break
811 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
812 local added = 0
813 local notadded = 0
814 for k, v in pairs(quest_lookup) do
815 if not quests_added[v] then
816 notadded = notadded + 1
817 else
818 added = added + 1
822 if notadded > 0 then
823 gap = gap * 10
824 x, y, gap, count = addobj(hidden_vquest1, seen, nil, nil, x, y, gap)
825 added = added + count
826 x, y, gap, count = addobj(hidden_vquest2, seen, nil, nil, x, y, gap)
827 added = added + count
831 for obj in pairs(obj_index_lookup) do
832 obj_index_lookup[obj] = nil
835 for key in pairs(quest_lookup) do
836 quest_lookup[key] = nil
839 for quest, objs in pairs(used_items) do
840 for obj, item in pairs(objs) do
841 if not item.used then
842 removeUnusedItem(quest, obj, item)
847 for key in pairs(seen) do
848 seen[key] = nil
851 y = y+4
853 if x ~= tracker.dw or y ~= tracker.dy then
854 tracker.t = 0
855 tracker.sw, tracker.sh = tracker:GetWidth(), tracker:GetHeight()
856 tracker.dw, tracker.dh = x, y
857 resizing = true
860 added = 0
864 function QH_Tracker_UpdateRoute(new_route)
865 route = new_route
866 QH_Tracker_Rescan()
869 function QH_Tracker_Pin(metaobjective)
870 if not pinned[metaobjective] then
871 pinned[metaobjective] = true
872 QH_Tracker_Rescan()
876 function QH_Tracker_Unpin(metaobjective)
877 if pinned[metaobjective] then
878 pinned[metaobjective] = nil -- nil, not false, so it'll be garbage-collected appropriately
879 QH_Tracker_Rescan()
883 function QH_Tracker_SetPin(metaobjective, flag)
884 if flag then
885 QH_Tracker_Pin(metaobjective)
886 else
887 QH_Tracker_Unpin(metaobjective)
892 local check_delay = 4
894 -- This function does the grunt work of cursor positioning and rescaling. It does not actually reorganize items.
895 function tracker:update(delta)
896 if not delta then
897 -- This is called without a value when the questlog is updated.
898 -- We'll make sure we update the display on the next update.
899 check_delay = 1e99
900 return
903 if resizing then
904 local t = self.t+delta
906 if t > 1 then
907 self:SetWidth(self.dw)
908 self:SetHeight(self.dh)
909 resizing = false
910 else
911 self.t = t
912 local it = 1-t
913 self:SetWidth(self.sw*it+self.dw*t)
914 self:SetHeight(self.sh*it+self.dh*t)
918 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
919 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
920 -- the tracker.
921 local x, y = GetCursorPosition()
922 local s = 1/self:GetEffectiveScale()
923 x, y = x*s, y*s
925 QuestHelper: Assert(x)
926 QuestHelper: Assert(y)
927 --[[ QuestHelper: Assert(self:GetLeft())
928 QuestHelper: Assert(self:GetBottom())
929 QuestHelper: Assert(self:GetRight())
930 QuestHelper: Assert(self:GetTop())]]
932 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
933 local inside = (self:GetLeft() and (x >= self:GetLeft() and y >= self:GetBottom() and x < self:GetRight() and y < self:GetTop()))
934 if inside ~= was_inside then
935 was_inside = inside
936 if inside then
937 minbutton:SetAlpha(.7)
938 elseif not QuestHelper_Pref.track_minimized then
939 minbutton:SetAlpha(0)
943 check_delay = check_delay + delta
944 if check_delay > 1 then
945 check_delay = 0
947 QH_Tracker_Rescan()
951 tracker:SetScript("OnUpdate", tracker.update)
953 -- Some hooks to update the tracker when quests are added or removed. These should be moved into the quest director.
954 --[[
955 local orig_AddQuestWatch, orig_RemoveQuestWatch = AddQuestWatch, RemoveQuestWatch
957 function AddQuestWatch(...)
958 tracker:update()
959 return orig_AddQuestWatch(...)
962 function RemoveQuestWatch(...)
963 tracker:update()
964 return orig_RemoveQuestWatch(...)
965 end]]
967 -------------------------------------------------------------------------------------------------
968 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
970 local orig_TrackerOnShow
971 if QuestWatchFrame then -- 3.1 hackery
972 orig_TrackerOnShow = QuestWatchFrame:GetScript("OnShow")
974 local orig_TrackerBackdropOnShow -- bEQL (and perhaps other mods) add a backdrop to the tracker
975 local TrackerBackdropFound = false
977 local function TrackerBackdropOnShow(self, ...)
978 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
979 TrackerBackdropFound:Hide()
982 if orig_TrackerBackdropOnShow then
983 return orig_TrackerBackdropOnShow(self, ...)
987 function tracker:HideDefaultTracker()
988 -- The easy part: hide the original tracker
989 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests)
990 WatchFrame_ClearDisplay()
991 WatchFrame_Update()
993 -- The harder part: hide all those little buttons
995 local index = 1
996 while true do
997 local orig = _G["WatchFrameItem" .. tostring(index)]
998 if orig then orig:Hide() else break end
999 index = index + 1
1003 -- The harder part: check if a known backdrop is present (but we don't already know about it).
1004 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
1005 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
1006 -- to keep checking. Hopefully, this won't happen too often.
1007 if not TrackerBackdropFound then
1008 if QuestWatchFrameBackdrop then
1009 -- Found bEQL's QuestWatchFrameBackdrop...
1010 TrackerBackdropFound = QuestWatchFrameBackdrop
1013 if TrackerBackdropFound then
1014 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
1015 TrackerBackdropFound:Hide()
1017 orig_TrackerBackdropOnShow = TrackerBackdropFound:GetScript("OnShow")
1018 TrackerBackdropFound:SetScript("OnShow", TrackerBackdropOnShow)
1023 function tracker:ShowDefaultTracker()
1024 if QuestWatchFrame then -- 3.1 hackery
1025 QuestWatchFrame:Show()
1026 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1027 QuestWatch_Update()
1028 else
1029 -- I like how there's code explicitly to allow us to do this without checking if it's already added
1030 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests)
1031 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
1032 WatchFrame_Update()
1035 if TrackerBackdropFound then
1036 TrackerBackdropFound:Show()
1040 if QuestWatchFrame then -- 3.1 hackery
1041 local function QuestWatchFrameOnShow(self, ...)
1042 if QuestHelper_Pref.track and not QuestHelper_Pref.hide then
1043 tracker:HideDefaultTracker()
1046 if orig_TrackerOnShow then
1047 return orig_TrackerOnShow(self, ...)
1051 QuestWatchFrame:SetScript("OnShow", QuestWatchFrameOnShow)
1054 function QuestHelper:ShowTracker()
1055 tracker:HideDefaultTracker()
1056 minbutton:Show()
1058 if QuestHelper_Pref.track_minimized then
1059 minbutton:SetAlpha(.3)
1060 else
1061 minbutton:SetAlpha(0)
1062 tracker:Show()
1066 function QuestHelper:HideTracker()
1067 tracker:ShowDefaultTracker()
1068 tracker:Hide()
1069 minbutton:Hide()