1 QuestHelper_File
["tracker.lua"] = "Development Version"
2 QuestHelper_Loadtime
["tracker.lua"] = GetTime()
4 local tracker
= CreateFrame("Frame", "QuestHelperQuestWatchFrame", UIParent
)
5 local minbutton
= CreateFrame("Button", "QuestHelperQuestWatchFrameMinimizeButton", UIParent
)
7 QuestHelper
.tracker
= tracker
11 tracker
:SetHeight(100)
12 tracker
.dw
, tracker
.dh
= 200, 100
16 minbutton
:SetFrameStrata("DIALOG")
18 minbutton
:SetPoint("CENTER", QuestWatchFrame
) -- We default to a different location to make it more likely to display the right item.
19 minbutton
:SetMovable(true)
20 minbutton
:SetUserPlaced(true)
21 minbutton
:SetWidth(10)
22 minbutton
:SetHeight(5)
23 local minbutton_tex
= minbutton
:CreateTexture()
24 minbutton_tex
:SetAllPoints()
25 minbutton_tex
:SetTexture(.8, .8, .8)
27 tracker
:SetPoint("CENTER", minbutton
)
29 function minbutton
:moved()
30 local x
, y
= self
:GetCenter()
31 local w
, h
= UIParent
:GetWidth(), UIParent
:GetHeight()
32 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 "")
34 tracker
:ClearAllPoints()
35 tracker
:SetPoint("CENTER", self
)
38 tracker
:SetPoint(anchor
, self
)
42 function QuestHelper
:ResetTrackerPosition(cmd
)
43 minbutton
:ClearAllPoints()
44 if cmd
and string.find(cmd
, "center") then
45 minbutton
:SetPoint("CENTER", nil, "CENTER", 100, 100)
47 minbutton
:SetPoint("RIGHT", nil, "RIGHT", -20, 230)
50 QuestHelper_Pref
.track_minimized
= false
52 self
:TextOut("Quest tracker postion reset.")
55 minbutton
:SetScript("OnEvent", minbutton
.moved
)
56 minbutton
:RegisterEvent("DISPLAY_SIZE_CHANGED")
57 minbutton
:RegisterEvent("PLAYER_ENTERING_WORLD")
59 minbutton
:SetScript("OnClick", function ()
60 QuestHelper_Pref
.track_minimized
= not QuestHelper_Pref
.track_minimized
61 if QuestHelper_Pref
.track_minimized
then
68 minbutton
:RegisterForDrag("LeftButton")
70 minbutton
:SetScript("OnDragStart", function(self
)
71 if self
:IsVisible() then
73 self
:SetScript("OnUpdate", self
.moved
)
77 minbutton
:SetScript("OnDragStop", function(self
)
78 self
:SetScript("OnUpdate", nil)
79 self
:StopMovingOrSizing()
83 minbutton
:SetScript("OnEnter", function (self
)
87 minbutton
:SetScript("OnLeave", function (self
)
88 self
:SetAlpha(QuestHelper_Pref
.track_minimized
and .3 or .5)
91 local unused_items
= {}
94 local function itemupdate(item
, delta
)
97 local a
= item
:GetAlpha()
107 local t
= item
.t
+ delta
112 local sp
= math
.sqrt(t
-t
*t
)
113 item
.x
, item
.y
= item
.sx
*it
+item
.dx
*t
+(item
.sy
-item
.dy
)*sp
, item
.sy
*it
+item
.dy
*t
+(item
.dx
-item
.sx
)*sp
117 item
.x
, item
.y
= item
.dx
, item
.dy
120 item
:ClearAllPoints()
121 item
:SetPoint("TOPLEFT", tracker
, "TOPLEFT", item
.x
, item
.y
)
124 item
:SetScript("OnUpdate", nil)
128 local function itemfadeout(item
, delta
)
129 local a
= item
:GetAlpha()
137 item
:SetScript("OnUpdate", nil)
141 local t
= item
.t
+ delta
146 local sp
= math
.sqrt(t
-t
*t
)
147 item
.x
, item
.y
= item
.sx
*it
+item
.dx
*t
+(item
.sy
-item
.dy
)*sp
, item
.sy
*it
+item
.dy
*t
+(item
.dx
-item
.sx
)*sp
150 item
.x
, item
.y
= item
.dx
, item
.dy
153 item
:ClearAllPoints()
154 item
:SetPoint("TOPLEFT", tracker
, "TOPLEFT", item
.x
, item
.y
)
157 function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
158 if (QuestLogFrame
:IsShown()) then
159 HideUIPanel(QuestLogFrame
);
161 ShowUIPanel(QuestLogFrame
);
166 local function itemclick(item
, button
)
167 if button
== "RightButton" then
168 local quest
= item
.quest
171 local title
= GetQuestLogTitle(index
)
172 if not title
then break end
174 if title
== quest
then
176 -- UberQuest needs a little extra effort to work properly.
178 if UberQuest_List
:IsShown() and GetQuestLogSelection() == index
then
181 QuestLog_SetSelection(index
)
183 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
184 -- and in the process update the frames to reflect the selected quest.
185 UberQuest_List
:Hide()
186 UberQuest_Details
:Show()
190 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
192 if QuestLogFrame
:IsShown() and GetQuestLogSelection() == index
then
193 -- If the selected quest is already being shown, hide it.
196 -- Otherwise, select it and show it.
197 QuestLog_SetSelection(index
)
199 if not QuestLogFrame
:IsShown() then
213 local specitem_max
= 1
214 local specitem_unused
= {}
216 local function addItem(name
, quest
, obj
, y
, qname
, qindex
)
217 local x
= qname
and 4 or 20
218 local item
= used_items
[quest
] and used_items
[quest
][obj
]
220 item
= next(unused_items
)
222 unused_items
[item
] = nil
224 item
= CreateFrame("Frame", nil, tracker
)
225 item
.text
= item
:CreateFontString()
226 item
.text
:SetShadowColor(0, 0, 0, .8)
227 item
.text
:SetShadowOffset(1, -1)
228 item
.text
:SetPoint("TOPLEFT", item
)
232 item
.text
:SetFont(QuestHelper
.font
.serif
, 12)
233 item
.text
:SetTextColor(.82, .65, 0)
235 item
.text
:SetFont(QuestHelper
.font
.sans
, 12)
236 item
.text
:SetTextColor(.82, .82, .82)
239 if not used_items
[quest
] then used_items
[quest
] = {} end
241 used_items
[quest
][obj
] = item
242 item
.sx
, item
.sy
, item
.x
, item
.y
, item
.dx
, item
.dy
, item
.t
= x
+30, y
, x
, y
, x
, y
, 0
243 item
:SetScript("OnUpdate", itemupdate
)
251 item
.text
:SetText(name
)
252 local w
, h
= item
.text
:GetWidth(), item
.text
:GetHeight()
257 item
:SetScript("OnMouseDown", itemclick
)
258 item
:EnableMouse(true)
261 if item
.dx
~= x
or item
.dy
~= y
then
262 item
.sx
, item
.sy
, item
.dx
, item
.dy
= item
.x
, item
.y
, x
, y
264 item
:SetScript("OnUpdate", itemupdate
)
267 if GetQuestLogSpecialItemInfo
and qindex
and GetQuestLogSpecialItemInfo(qindex
) and not item
.specitem
then -- 3.1 hackery
268 item
.specitem
= table.remove(specitem_unused
)
269 if not item
.specitem
then
270 item
.specitem
= CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max
), item
, "WatchFrameItemButtonTemplate")
271 specitem_max
= specitem_max
+ 1
272 QuestHelper
: Assert(item
.specitem
)
275 item
.specitem
:SetScale(0.9)
276 item
.specitem
:ClearAllPoints()
277 item
.specitem
:SetPoint("TOPRIGHT", item
, "TOPLEFT", 0, 0)
279 item
.specitem
:SetID(obj
.index
)
281 local _
, tex
= GetQuestLogSpecialItemInfo(obj
.index
)
282 SetItemButtonTexture(item
.specitem
, tex
)
283 item
.specitem
.rangeTimer
= -1 -- This makes the little dot go away. Why does it do that?
291 local function ccode(r1
, g1
, b1
, r2
, g2
, b2
, p
)
293 p
, ip
= p
*255, 255-p
*255
294 return string.format("|cff%02x%02x%02x", r1
*ip
+r2
*p
, g1
*ip
+g2
*p
, b1
*ip
+b2
*p
)
297 local function qname(title
, level
)
298 if QuestHelper_Pref
.track_level
and level
~= 7777 and level
~= 7778 then
299 title
= string.format("[%d] %s", level
, title
)
302 if level
== 7778 then
306 if QuestHelper_Pref
.track_qcolour
then
307 local player_level
= QuestHelper
.player_level
308 local delta
= level
- player_level
313 colour
= "|cffff0000"
314 elseif delta
>= 0 then
315 colour
= ccode(1, 1, 0, 1, 0, 0, delta
/5)
319 if player_level
>= 60 then grey
= player_level
- 9
320 elseif player_level
>= 40 then grey
= player_level
- math
.floor(player_level
/5) - 1
321 elseif player_level
>= 6 then grey
= player_level
- math
.floor(player_level
/10) - 5
324 if level
== -7778 then
325 colour
= "|cff808080"
326 elseif level
> grey
then
327 colour
= ccode(0, 1, 0, 1, 1, 0, (grey
-level
)/(grey
-player_level
))
329 colour
= ccode(.4, .4, .4, .2, .8, .2, (1-level
)/(1-grey
))
333 title
= string.format("%s%s", colour
, title
)
339 local function oname(text
, pct
)
340 if QuestHelper_Pref
.track_ocolour
then
341 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
)
347 local function removeUnusedItem(quest
, obj
, item
)
348 unused_items
[item
] = true
349 used_items
[quest
][obj
] = nil
350 if not next(used_items
[quest
]) then used_items
[quest
] = nil end
353 item
.sx
, item
.sy
= item
.x
, item
.y
354 item
.dx
, item
.dy
= item
.x
+30, item
.y
355 item
:SetScript("OnMouseDown", nil)
356 item
:EnableMouse(false)
357 item
:SetScript("OnUpdate", itemfadeout
)
359 if item
.specitem
then
361 table.insert(specitem_unused
, item
.specitem
)
366 local resizing
= false
367 local check_delay
= 4
370 local obj_index_lookup
= {}
371 local quest_lookup
= {}
372 local was_inside
= false
374 local function watched_filter(obj
)
375 return obj
:IsWatched()
378 local function objlist_sort(a
, b
)
379 return (obj_index_lookup
[a
] or 0) < (obj_index_lookup
[b
] or 0)
382 function tracker
:reset()
383 for quest
, objs
in pairs(used_items
) do
384 for obj
, item
in pairs(objs
) do
385 removeUnusedItem(quest
, obj
, item
)
391 local function addobj(objective
, seen
, obj_index_lookup
, filter
, x
, y
, gap
)
395 if objective
.cat
== "quest" then
398 quest
= objective
.quest
401 if quest
and quest
.watched
and not seen
[quest
] and (not filter
or filter(quest
)) then
404 local level
, name
= string.match(quest
.obj
, "^(%d+)/%d*/(.*)$")
407 level
, name
= string.match(quest
.obj
, "^(%d+)/(.*)$")
409 level
, name
= 1, quest
.obj
413 level
= tonumber(level
) or 1
416 local w
, h
= addItem(qname(name
, level
), true, quest
, -(y
+gap
), name
, quest
.index
)
421 for obj
in pairs(quest
.swap_after
or quest
.after
) do
423 table.insert(obj_list
, obj
)
427 table.sort(obj_list
, objlist_sort
)
429 for i
, obj
in ipairs(obj_list
) do
430 local pct
, text
= 0, obj
.obj
431 local seen_sum
, seen_max
= 0, 0
434 local seen_have
, seen_need
= QuestHelper
:CreateTable(), QuestHelper
:CreateTable()
436 for user
, progress
in pairs(obj
.progress
) do
437 seen_sum
= seen_sum
+ progress
[3]
438 seen_max
= seen_max
+ 1
439 seen_have
[progress
[1]]
= true
440 seen_need
[progress
[2]]
= true
444 pct
= seen_sum
/ seen_max
445 local list
= QuestHelper
:CreateTable()
447 for val
in pairs(seen_have
) do
448 table.insert(list
, val
)
453 local have
= table.concat(list
, ", ")
455 for i
= #list
,1,-1 do
459 for val
in pairs(seen_need
) do
460 table.insert(list
, val
)
463 if #list
~= 1 or list
[1] ~= 1 then
464 -- If there is only one thing needed, ignore the progress, it's redundant.
465 -- It's either shown or it isn't.
469 local need
= table.concat(list
, ", ")
471 text
= string.format((tonumber(have
) and tonumber(need
) and QUEST_ITEMS_NEEDED
) or QUEST_FACTION_NEEDED
,
475 QuestHelper
:ReleaseTable(list
)
478 QuestHelper
:ReleaseTable(seen_have
)
479 QuestHelper
:ReleaseTable(seen_need
)
482 if seen_sum
~= seen_max
then
484 w
, h
= addItem(oname(text
, pct
), quest
, obj
, -y
)
490 for i
= #obj_list
, 1, -1 do obj_list
[i
] = nil end
493 return x
, y
, gap
, count
496 local loading_vquest
= { cat
= "quest", obj
= "7777/" .. QHFormat("QH_LOADING", "0"), after
= {}, watched
= true }
497 local hidden_vquest1
= { cat
= "quest", obj
= "7778/" .. QHText("QUESTS_HIDDEN_1"), after
= {}, watched
= true }
498 local hidden_vquest2
= { cat
= "quest", obj
= "7778/ " .. QHText("QUESTS_HIDDEN_2"), after
= {}, watched
= true }
500 function tracker
:update(delta
)
502 -- This is called without a value when the questlog is updated.
503 -- We'll make sure we update the display on the next update.
509 local t
= self
.t
+delta
512 self
:SetWidth(self
.dw
)
513 self
:SetHeight(self
.dh
)
518 self
:SetWidth(self
.sw
*it
+self
.dw
*t
)
519 self
:SetHeight(self
.sh
*it
+self
.dh
*t
)
523 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
524 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
526 local x
, y
= GetCursorPosition()
527 local s
= 1/self
:GetEffectiveScale()
530 QuestHelper
: Assert(x
)
531 QuestHelper
: Assert(y
)
532 --[[ QuestHelper: Assert(self:GetLeft())
533 QuestHelper: Assert(self:GetBottom())
534 QuestHelper: Assert(self:GetRight())
535 QuestHelper: Assert(self:GetTop())]]
537 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
538 local inside
= (self
:GetLeft() and (x
>= self
:GetLeft() and y
>= self
:GetBottom() and x
< self
:GetRight() and y
< self
:GetTop()))
539 if inside
~= was_inside
then
542 minbutton
:SetAlpha(.7)
543 elseif not QuestHelper_Pref
.track_minimized
then
544 minbutton
:SetAlpha(0)
548 check_delay
= check_delay
+ delta
549 if check_delay
> 5 or (not QuestHelper
.Routing
.map_walker
and check_delay
> 0.5) then
552 local quests
= QuestHelper
.quest_log
556 local track_size
= QuestHelper_Pref
.track_size
557 local quests_added
= {}
559 for quest
, objs
in pairs(used_items
) do
560 for obj
, item
in pairs(objs
) do
565 for i
, objective
in pairs(QuestHelper
.route
) do
566 if objective
.watched
then
567 obj_index_lookup
[objective
] = i
571 for q
, data
in pairs(QuestHelper
.quest_log
) do
572 quest_lookup
[data
.index
] = q
575 -- Add our little "not yet loaded" notification
576 local loadedshow
= false
577 if not QuestHelper
.Routing
.map_walker
then
579 x
, y
, gap
, count
= addobj(loading_vquest
, seen
, nil, nil, x
, y
, gap
)
580 added
= added
+ count
583 if QuestHelper_Flight_Updates
and QuestHelper_Flight_Updates_Current
and QuestHelper_Flight_Updates
> 0 and QuestHelper_Flight_Updates_Current
< QuestHelper_Flight_Updates
then
584 loading_vquest
.obj
= string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current
* 100 / QuestHelper_Flight_Updates
)))
588 -- Add an extra large gap to seperate the notification from everything else
591 -- Add Quests that are watched but not in the route.
593 local uq_settings
= UberQuest_Config
[UnitName("player")]
595 local list
= uq_settings
.selected
599 local name
= GetQuestLogTitle(i
)
600 if not name
then break end
601 quest_lookup
[name
] = quest_lookup
[i
]
604 for name
in pairs(list
) do
605 local q
= quest_lookup
[name
]
606 if q
and not obj_index_lookup
[q
] then
608 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
609 added
= added
+ count
610 quests_added
[q
] = true
616 for i
= 1,GetNumQuestWatches() do
617 local q
= quest_lookup
[GetQuestIndexForWatch(i
)]
618 if q
and not obj_index_lookup
[q
] then
620 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
621 added
= added
+ count
626 -- Add Quests that are watched and are in the route.
627 for i
, objective
in ipairs(QuestHelper
.route
) do
629 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, watched_filter
, x
, y
, gap
)
630 added
= added
+ count
631 quests_added
[objective
] = true
634 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
637 -- Add Quests that aren't watched and are in the route.
638 if added
<= track_size
then
639 for i
, objective
in ipairs(QuestHelper
.route
) do
641 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
642 added
= added
+ count
643 quests_added
[objective
] = true
645 if added
> track_size
then
651 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
654 for k
, v
in pairs(quest_lookup
) do
655 if not quests_added
[v
] then
656 notadded
= notadded
+ 1
664 x
, y
, gap
, count
= addobj(hidden_vquest1
, seen
, nil, nil, x
, y
, gap
)
665 added
= added
+ count
666 x
, y
, gap
, count
= addobj(hidden_vquest2
, seen
, nil, nil, x
, y
, gap
)
667 added
= added
+ count
671 for obj
in pairs(obj_index_lookup
) do
672 obj_index_lookup
[obj
] = nil
675 for key
in pairs(quest_lookup
) do
676 quest_lookup
[key
] = nil
679 for quest
, objs
in pairs(used_items
) do
680 for obj
, item
in pairs(objs
) do
681 if not item
.used
then
682 removeUnusedItem(quest
, obj
, item
)
687 for key
in pairs(seen
) do
693 if x
~= tracker
.dw
or y
~= tracker
.dy
then
695 tracker
.sw
, tracker
.sh
= tracker
:GetWidth(), tracker
:GetHeight()
696 tracker
.dw
, tracker
.dh
= x
, y
704 tracker
:SetScript("OnUpdate", tracker
.update
)
706 -- Some hooks to update the tracker when quests are added or removed.
707 local orig_AddQuestWatch
, orig_RemoveQuestWatch
= AddQuestWatch
, RemoveQuestWatch
709 function AddQuestWatch(...)
711 return orig_AddQuestWatch(...)
714 function RemoveQuestWatch(...)
716 return orig_RemoveQuestWatch(...)
719 -------------------------------------------------------------------------------------------------
720 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
722 local orig_TrackerOnShow
723 if QuestWatchFrame
then -- 3.1 hackery
724 orig_TrackerOnShow
= QuestWatchFrame
:GetScript("OnShow")
726 local orig_TrackerBackdropOnShow
-- bEQL (and perhaps other mods) add a backdrop to the tracker
727 local TrackerBackdropFound
= false
729 local function TrackerBackdropOnShow(self
, ...)
730 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
731 TrackerBackdropFound
:Hide()
734 if orig_TrackerBackdropOnShow
then
735 return orig_TrackerBackdropOnShow(self
, ...)
739 function tracker
:HideDefaultTracker()
740 -- The easy part: hide the original tracker
741 if QuestWatchFrame
then -- 3.1 hackery
742 QuestWatchFrame
:Hide()
744 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
745 WatchFrame_ClearDisplay()
749 -- The harder part: check if a known backdrop is present (but we don't already know about it).
750 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
751 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
752 -- to keep checking. Hopefully, this won't happen too often.
753 if not TrackerBackdropFound
then
754 if QuestWatchFrameBackdrop
then
755 -- Found bEQL's QuestWatchFrameBackdrop...
756 TrackerBackdropFound
= QuestWatchFrameBackdrop
759 if TrackerBackdropFound
then
760 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
761 TrackerBackdropFound
:Hide()
763 orig_TrackerBackdropOnShow
= TrackerBackdropFound
:GetScript("OnShow")
764 TrackerBackdropFound
:SetScript("OnShow", TrackerBackdropOnShow
)
769 function tracker
:ShowDefaultTracker()
770 if QuestWatchFrame
then -- 3.1 hackery
771 QuestWatchFrame
:Show()
772 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
775 -- I like how there's code explicitly to allow us to do this without checking if it's already added
776 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
777 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
781 if TrackerBackdropFound
then
782 TrackerBackdropFound
:Show()
786 if QuestWatchFrame
then -- 3.1 hackery
787 local function QuestWatchFrameOnShow(self
, ...)
788 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
789 tracker
:HideDefaultTracker()
792 if orig_TrackerOnShow
then
793 return orig_TrackerOnShow(self
, ...)
797 QuestWatchFrame
:SetScript("OnShow", QuestWatchFrameOnShow
)
800 function QuestHelper
:ShowTracker()
801 tracker
:HideDefaultTracker()
804 if QuestHelper_Pref
.track_minimized
then
805 minbutton
:SetAlpha(.3)
807 minbutton
:SetAlpha(0)
812 function QuestHelper
:HideTracker()
813 tracker
:ShowDefaultTracker()