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", WatchFrame
) -- 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 -- we're just going to recycle this each time
268 if item
.specitem
then
270 table.insert(specitem_unused
, item
.specitem
)
275 -- ughhh just want this to work
278 if GetQuestLogTitle(qindex
) == qname
then break end
279 if GetQuestLogTitle(qindex
) == nil then qindex
= nil break end
284 local _
, _
, _
, _
, _
, _
, complete
= GetQuestLogTitle(qindex
)
285 if complete
== nil and GetQuestLogTitle(qindex
) and GetQuestLogTitle(qindex
) == qname
then
286 local noitem
= not GetQuestLogSpecialItemInfo(qindex
)
289 item
.specitem
= table.remove(specitem_unused
)
290 if not item
.specitem
then
291 item
.specitem
= CreateFrame("BUTTON", "QH_SpecItem_" .. tostring(specitem_max
), item
, "WatchFrameItemButtonTemplate")
292 QuestHelper
: Assert(item
.specitem
)
294 local rangey
= _G
["QH_SpecItem_" .. tostring(specitem_max
) .. "HotKey"]
295 QuestHelper
: Assert(rangey
)
296 local fn
, fh
, ff
= rangey
:GetFont()
297 rangey
:SetFont("Fonts\\ARIALN.TTF", fh
, ff
)
298 rangey
:SetText(RANGE_INDICATOR
)
299 rangey
:ClearAllPoints()
300 rangey
:SetPoint("BOTTOMRIGHT", item
.specitem
, "BOTTOMRIGHT", 0, 2)
302 specitem_max
= specitem_max
+ 1
305 item
.specitem
:SetScale(0.9)
306 item
.specitem
:ClearAllPoints()
307 item
.specitem
:SetPoint("TOPRIGHT", item
, "TOPLEFT", 0, 0)
309 item
.specitem
:SetID(obj
.index
)
311 local _
, tex
= GetQuestLogSpecialItemInfo(obj
.index
)
312 SetItemButtonTexture(item
.specitem
, tex
)
313 item
.specitem
.rangeTimer
= -1 -- This makes the little dot go away. Why does it do that?
324 local function ccode(r1
, g1
, b1
, r2
, g2
, b2
, p
)
326 p
, ip
= p
*255, 255-p
*255
327 return string.format("|cff%02x%02x%02x", r1
*ip
+r2
*p
, g1
*ip
+g2
*p
, b1
*ip
+b2
*p
)
330 local function qname(title
, level
)
331 if QuestHelper_Pref
.track_level
and level
~= 7777 and level
~= 7778 then
332 title
= string.format("[%d] %s", level
, title
)
335 if level
== 7778 then
339 if QuestHelper_Pref
.track_qcolour
then
340 local player_level
= QuestHelper
.player_level
341 local delta
= level
- player_level
346 colour
= "|cffff0000"
347 elseif delta
>= 0 then
348 colour
= ccode(1, 1, 0, 1, 0, 0, delta
/5)
352 if player_level
>= 60 then grey
= player_level
- 9
353 elseif player_level
>= 40 then grey
= player_level
- math
.floor(player_level
/5) - 1
354 elseif player_level
>= 6 then grey
= player_level
- math
.floor(player_level
/10) - 5
357 if level
== -7778 then
358 colour
= "|cff808080"
359 elseif level
> grey
then
360 colour
= ccode(0, 1, 0, 1, 1, 0, (grey
-level
)/(grey
-player_level
))
362 colour
= ccode(.4, .4, .4, .2, .8, .2, (1-level
)/(1-grey
))
366 title
= string.format("%s%s", colour
, title
)
372 local function oname(text
, pct
)
373 if QuestHelper_Pref
.track_ocolour
then
374 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
)
380 local function removeUnusedItem(quest
, obj
, item
)
381 unused_items
[item
] = true
382 used_items
[quest
][obj
] = nil
383 if not next(used_items
[quest
]) then used_items
[quest
] = nil end
386 item
.sx
, item
.sy
= item
.x
, item
.y
387 item
.dx
, item
.dy
= item
.x
+30, item
.y
388 item
:SetScript("OnMouseDown", nil)
389 item
:EnableMouse(false)
390 item
:SetScript("OnUpdate", itemfadeout
)
392 if item
.specitem
then
394 table.insert(specitem_unused
, item
.specitem
)
399 local resizing
= false
400 local check_delay
= 4
403 local obj_index_lookup
= {}
404 local quest_lookup
= {}
405 local was_inside
= false
407 local function watched_filter(obj
)
408 return obj
:IsWatched()
411 local function objlist_sort(a
, b
)
412 return (obj_index_lookup
[a
] or 0) < (obj_index_lookup
[b
] or 0)
415 function tracker
:reset()
416 for quest
, objs
in pairs(used_items
) do
417 for obj
, item
in pairs(objs
) do
418 removeUnusedItem(quest
, obj
, item
)
424 local function addobj(objective
, seen
, obj_index_lookup
, filter
, x
, y
, gap
)
428 if objective
.cat
== "quest" then
431 quest
= objective
.quest
434 if quest
and quest
.watched
and not seen
[quest
] and (not filter
or filter(quest
)) then
437 local level
, name
= string.match(quest
.obj
, "^(%d+)/%d*/(.*)$")
440 level
, name
= string.match(quest
.obj
, "^(%d+)/(.*)$")
442 level
, name
= 1, quest
.obj
446 level
= tonumber(level
) or 1
449 local w
, h
= addItem(qname(name
, level
), true, quest
, -(y
+gap
), name
, quest
.index
)
454 for obj
in pairs(quest
.swap_after
or quest
.after
) do
456 table.insert(obj_list
, obj
)
460 table.sort(obj_list
, objlist_sort
)
462 for i
, obj
in ipairs(obj_list
) do
463 local pct
, text
= 0, obj
.obj
464 local seen_sum
, seen_max
= 0, 0
467 local seen_have
, seen_need
= QuestHelper
:CreateTable(), QuestHelper
:CreateTable()
469 for user
, progress
in pairs(obj
.progress
) do
470 seen_sum
= seen_sum
+ progress
[3]
471 seen_max
= seen_max
+ 1
472 seen_have
[progress
[1]]
= true
473 seen_need
[progress
[2]]
= true
477 pct
= seen_sum
/ seen_max
478 local list
= QuestHelper
:CreateTable()
480 for val
in pairs(seen_have
) do
481 table.insert(list
, val
)
486 local have
= table.concat(list
, ", ")
488 for i
= #list
,1,-1 do
492 for val
in pairs(seen_need
) do
493 table.insert(list
, val
)
496 if #list
~= 1 or list
[1] ~= 1 then
497 -- If there is only one thing needed, ignore the progress, it's redundant.
498 -- It's either shown or it isn't.
502 local need
= table.concat(list
, ", ")
504 text
= string.format((tonumber(have
) and tonumber(need
) and QUEST_ITEMS_NEEDED
) or QUEST_FACTION_NEEDED
,
508 QuestHelper
:ReleaseTable(list
)
511 QuestHelper
:ReleaseTable(seen_have
)
512 QuestHelper
:ReleaseTable(seen_need
)
515 if seen_sum
~= seen_max
then
517 w
, h
= addItem(oname(text
, pct
), quest
, obj
, -y
)
523 for i
= #obj_list
, 1, -1 do obj_list
[i
] = nil end
526 return x
, y
, gap
, count
529 local loading_vquest
= { cat
= "quest", obj
= "7777/" .. QHFormat("QH_LOADING", "0"), after
= {}, watched
= true }
530 local hidden_vquest1
= { cat
= "quest", obj
= "7778/" .. QHText("QUESTS_HIDDEN_1"), after
= {}, watched
= true }
531 local hidden_vquest2
= { cat
= "quest", obj
= "7778/ " .. QHText("QUESTS_HIDDEN_2"), after
= {}, watched
= true }
533 function tracker
:update(delta
)
535 -- This is called without a value when the questlog is updated.
536 -- We'll make sure we update the display on the next update.
542 local t
= self
.t
+delta
545 self
:SetWidth(self
.dw
)
546 self
:SetHeight(self
.dh
)
551 self
:SetWidth(self
.sw
*it
+self
.dw
*t
)
552 self
:SetHeight(self
.sh
*it
+self
.dh
*t
)
556 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
557 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
559 local x
, y
= GetCursorPosition()
560 local s
= 1/self
:GetEffectiveScale()
563 QuestHelper
: Assert(x
)
564 QuestHelper
: Assert(y
)
565 --[[ QuestHelper: Assert(self:GetLeft())
566 QuestHelper: Assert(self:GetBottom())
567 QuestHelper: Assert(self:GetRight())
568 QuestHelper: Assert(self:GetTop())]]
570 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
571 local inside
= (self
:GetLeft() and (x
>= self
:GetLeft() and y
>= self
:GetBottom() and x
< self
:GetRight() and y
< self
:GetTop()))
572 if inside
~= was_inside
then
575 minbutton
:SetAlpha(.7)
576 elseif not QuestHelper_Pref
.track_minimized
then
577 minbutton
:SetAlpha(0)
581 check_delay
= check_delay
+ delta
582 if check_delay
> 5 or (not QuestHelper
.Routing
.map_walker
and check_delay
> 0.5) then
585 local quests
= QuestHelper
.quest_log
589 local track_size
= QuestHelper_Pref
.track_size
590 local quests_added
= {}
592 for quest
, objs
in pairs(used_items
) do
593 for obj
, item
in pairs(objs
) do
598 for i
, objective
in pairs(QuestHelper
.route
) do
599 if objective
.watched
then
600 obj_index_lookup
[objective
] = i
604 for q
, data
in pairs(QuestHelper
.quest_log
) do
605 quest_lookup
[data
.index
] = q
608 -- Add our little "not yet loaded" notification
609 local loadedshow
= false
610 if not QuestHelper
.Routing
.map_walker
then
612 x
, y
, gap
, count
= addobj(loading_vquest
, seen
, nil, nil, x
, y
, gap
)
613 added
= added
+ count
616 if QuestHelper_Flight_Updates
and QuestHelper_Flight_Updates_Current
and QuestHelper_Flight_Updates
> 0 and QuestHelper_Flight_Updates_Current
< QuestHelper_Flight_Updates
then
617 loading_vquest
.obj
= string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current
* 100 / QuestHelper_Flight_Updates
)))
621 -- Add an extra large gap to seperate the notification from everything else
624 -- Add Quests that are watched but not in the route.
626 local uq_settings
= UberQuest_Config
[UnitName("player")]
628 local list
= uq_settings
.selected
632 local name
= GetQuestLogTitle(i
)
633 if not name
then break end
634 quest_lookup
[name
] = quest_lookup
[i
]
637 for name
in pairs(list
) do
638 local q
= quest_lookup
[name
]
639 if q
and not obj_index_lookup
[q
] then
641 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
642 added
= added
+ count
643 quests_added
[q
] = true
649 for i
= 1,GetNumQuestWatches() do
650 local q
= quest_lookup
[GetQuestIndexForWatch(i
)]
651 if q
and not obj_index_lookup
[q
] then
653 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
654 added
= added
+ count
659 -- Add Quests that are watched and are in the route.
660 for i
, objective
in ipairs(QuestHelper
.route
) do
662 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, watched_filter
, x
, y
, gap
)
663 added
= added
+ count
664 quests_added
[objective
] = true
667 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
670 -- Add Quests that aren't watched and are in the route.
671 if added
<= track_size
then
672 for i
, objective
in ipairs(QuestHelper
.route
) do
674 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
675 added
= added
+ count
676 quests_added
[objective
] = true
678 if added
> track_size
then
684 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
687 for k
, v
in pairs(quest_lookup
) do
688 if not quests_added
[v
] then
689 notadded
= notadded
+ 1
697 x
, y
, gap
, count
= addobj(hidden_vquest1
, seen
, nil, nil, x
, y
, gap
)
698 added
= added
+ count
699 x
, y
, gap
, count
= addobj(hidden_vquest2
, seen
, nil, nil, x
, y
, gap
)
700 added
= added
+ count
704 for obj
in pairs(obj_index_lookup
) do
705 obj_index_lookup
[obj
] = nil
708 for key
in pairs(quest_lookup
) do
709 quest_lookup
[key
] = nil
712 for quest
, objs
in pairs(used_items
) do
713 for obj
, item
in pairs(objs
) do
714 if not item
.used
then
715 removeUnusedItem(quest
, obj
, item
)
720 for key
in pairs(seen
) do
726 if x
~= tracker
.dw
or y
~= tracker
.dy
then
728 tracker
.sw
, tracker
.sh
= tracker
:GetWidth(), tracker
:GetHeight()
729 tracker
.dw
, tracker
.dh
= x
, y
737 tracker
:SetScript("OnUpdate", tracker
.update
)
739 -- Some hooks to update the tracker when quests are added or removed.
740 local orig_AddQuestWatch
, orig_RemoveQuestWatch
= AddQuestWatch
, RemoveQuestWatch
742 function AddQuestWatch(...)
744 return orig_AddQuestWatch(...)
747 function RemoveQuestWatch(...)
749 return orig_RemoveQuestWatch(...)
752 -------------------------------------------------------------------------------------------------
753 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
755 local orig_TrackerOnShow
756 if QuestWatchFrame
then -- 3.1 hackery
757 orig_TrackerOnShow
= QuestWatchFrame
:GetScript("OnShow")
759 local orig_TrackerBackdropOnShow
-- bEQL (and perhaps other mods) add a backdrop to the tracker
760 local TrackerBackdropFound
= false
762 local function TrackerBackdropOnShow(self
, ...)
763 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
764 TrackerBackdropFound
:Hide()
767 if orig_TrackerBackdropOnShow
then
768 return orig_TrackerBackdropOnShow(self
, ...)
772 function tracker
:HideDefaultTracker()
773 -- The easy part: hide the original tracker
774 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
775 WatchFrame_ClearDisplay()
778 -- The harder part: hide all those little buttons
782 local orig
= _G
["WatchFrameItem" .. tostring(index
)]
783 print("WatchFrameItem" .. tostring(index
), orig
)
784 if orig
then orig
:Hide() else break end
789 -- The harder part: check if a known backdrop is present (but we don't already know about it).
790 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
791 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
792 -- to keep checking. Hopefully, this won't happen too often.
793 if not TrackerBackdropFound
then
794 if QuestWatchFrameBackdrop
then
795 -- Found bEQL's QuestWatchFrameBackdrop...
796 TrackerBackdropFound
= QuestWatchFrameBackdrop
799 if TrackerBackdropFound
then
800 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
801 TrackerBackdropFound
:Hide()
803 orig_TrackerBackdropOnShow
= TrackerBackdropFound
:GetScript("OnShow")
804 TrackerBackdropFound
:SetScript("OnShow", TrackerBackdropOnShow
)
809 function tracker
:ShowDefaultTracker()
810 if QuestWatchFrame
then -- 3.1 hackery
811 QuestWatchFrame
:Show()
812 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
815 -- I like how there's code explicitly to allow us to do this without checking if it's already added
816 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
817 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
821 if TrackerBackdropFound
then
822 TrackerBackdropFound
:Show()
826 if QuestWatchFrame
then -- 3.1 hackery
827 local function QuestWatchFrameOnShow(self
, ...)
828 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
829 tracker
:HideDefaultTracker()
832 if orig_TrackerOnShow
then
833 return orig_TrackerOnShow(self
, ...)
837 QuestWatchFrame
:SetScript("OnShow", QuestWatchFrameOnShow
)
840 function QuestHelper
:ShowTracker()
841 tracker
:HideDefaultTracker()
844 if QuestHelper_Pref
.track_minimized
then
845 minbutton
:SetAlpha(.3)
847 minbutton
:SetAlpha(0)
852 function QuestHelper
:HideTracker()
853 tracker
:ShowDefaultTracker()