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 function addItem(name
, quest
, obj
, y
, qname
)
214 local x
= qname
and 4 or 20
215 local item
= used_items
[quest
] and used_items
[quest
][obj
]
217 item
= next(unused_items
)
219 unused_items
[item
] = nil
221 item
= CreateFrame("Frame", nil, tracker
)
222 item
.text
= item
:CreateFontString()
223 item
.text
:SetShadowColor(0, 0, 0, .8)
224 item
.text
:SetShadowOffset(1, -1)
225 item
.text
:SetPoint("TOPLEFT", item
)
229 item
.text
:SetFont(QuestHelper
.font
.serif
, 12)
230 item
.text
:SetTextColor(.82, .65, 0)
232 item
.text
:SetFont(QuestHelper
.font
.sans
, 12)
233 item
.text
:SetTextColor(.82, .82, .82)
236 if not used_items
[quest
] then used_items
[quest
] = {} end
238 used_items
[quest
][obj
] = item
239 item
.sx
, item
.sy
, item
.x
, item
.y
, item
.dx
, item
.dy
, item
.t
= x
+30, y
, x
, y
, x
, y
, 0
240 item
:SetScript("OnUpdate", itemupdate
)
248 item
.text
:SetText(name
)
249 local w
, h
= item
.text
:GetWidth(), item
.text
:GetHeight()
254 item
:SetScript("OnMouseDown", itemclick
)
255 item
:EnableMouse(true)
258 if item
.dx
~= x
or item
.dy
~= y
then
259 item
.sx
, item
.sy
, item
.dx
, item
.dy
= item
.x
, item
.y
, x
, y
261 item
:SetScript("OnUpdate", itemupdate
)
267 local function ccode(r1
, g1
, b1
, r2
, g2
, b2
, p
)
269 p
, ip
= p
*255, 255-p
*255
270 return string.format("|cff%02x%02x%02x", r1
*ip
+r2
*p
, g1
*ip
+g2
*p
, b1
*ip
+b2
*p
)
273 local function qname(title
, level
)
274 if QuestHelper_Pref
.track_level
and level
~= 7777 and level
~= 7778 then
275 title
= string.format("[%d] %s", level
, title
)
278 if level
== 7778 then
282 if QuestHelper_Pref
.track_qcolour
then
283 local player_level
= QuestHelper
.player_level
284 local delta
= level
- player_level
289 colour
= "|cffff0000"
290 elseif delta
>= 0 then
291 colour
= ccode(1, 1, 0, 1, 0, 0, delta
/5)
295 if player_level
>= 60 then grey
= player_level
- 9
296 elseif player_level
>= 40 then grey
= player_level
- math
.floor(player_level
/5) - 1
297 elseif player_level
>= 6 then grey
= player_level
- math
.floor(player_level
/10) - 5
300 if level
== -7778 then
301 colour
= "|cff808080"
302 elseif level
> grey
then
303 colour
= ccode(0, 1, 0, 1, 1, 0, (grey
-level
)/(grey
-player_level
))
305 colour
= ccode(.4, .4, .4, .2, .8, .2, (1-level
)/(1-grey
))
309 title
= string.format("%s%s", colour
, title
)
315 local function oname(text
, pct
)
316 if QuestHelper_Pref
.track_ocolour
then
317 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
)
323 local function removeUnusedItem(quest
, obj
, item
)
324 unused_items
[item
] = true
325 used_items
[quest
][obj
] = nil
326 if not next(used_items
[quest
]) then used_items
[quest
] = nil end
329 item
.sx
, item
.sy
= item
.x
, item
.y
330 item
.dx
, item
.dy
= item
.x
+30, item
.y
331 item
:SetScript("OnMouseDown", nil)
332 item
:EnableMouse(false)
333 item
:SetScript("OnUpdate", itemfadeout
)
336 local resizing
= false
337 local check_delay
= 4
340 local obj_index_lookup
= {}
341 local quest_lookup
= {}
342 local was_inside
= false
344 local function watched_filter(obj
)
345 return obj
:IsWatched()
348 local function objlist_sort(a
, b
)
349 return (obj_index_lookup
[a
] or 0) < (obj_index_lookup
[b
] or 0)
352 function tracker
:reset()
353 for quest
, objs
in pairs(used_items
) do
354 for obj
, item
in pairs(objs
) do
355 removeUnusedItem(quest
, obj
, item
)
361 local function addobj(objective
, seen
, obj_index_lookup
, filter
, x
, y
, gap
)
365 if objective
.cat
== "quest" then
368 quest
= objective
.quest
371 if quest
and quest
.watched
and not seen
[quest
] and (not filter
or filter(quest
)) then
374 local level
, name
= string.match(quest
.obj
, "^(%d+)/%d*/(.*)$")
377 level
, name
= string.match(quest
.obj
, "^(%d+)/(.*)$")
379 level
, name
= 1, quest
.obj
383 level
= tonumber(level
) or 1
386 local w
, h
= addItem(qname(name
, level
), true, quest
, -(y
+gap
), name
)
391 for obj
in pairs(quest
.swap_after
or quest
.after
) do
393 table.insert(obj_list
, obj
)
397 table.sort(obj_list
, objlist_sort
)
399 for i
, obj
in ipairs(obj_list
) do
400 local pct
, text
= 0, obj
.obj
401 local seen_sum
, seen_max
= 0, 0
404 local seen_have
, seen_need
= QuestHelper
:CreateTable(), QuestHelper
:CreateTable()
406 for user
, progress
in pairs(obj
.progress
) do
407 seen_sum
= seen_sum
+ progress
[3]
408 seen_max
= seen_max
+ 1
409 seen_have
[progress
[1]]
= true
410 seen_need
[progress
[2]]
= true
414 pct
= seen_sum
/ seen_max
415 local list
= QuestHelper
:CreateTable()
417 for val
in pairs(seen_have
) do
418 table.insert(list
, val
)
423 local have
= table.concat(list
, ", ")
425 for i
= #list
,1,-1 do
429 for val
in pairs(seen_need
) do
430 table.insert(list
, val
)
433 if #list
~= 1 or list
[1] ~= 1 then
434 -- If there is only one thing needed, ignore the progress, it's redundant.
435 -- It's either shown or it isn't.
439 local need
= table.concat(list
, ", ")
441 text
= string.format((tonumber(have
) and tonumber(need
) and QUEST_ITEMS_NEEDED
) or QUEST_FACTION_NEEDED
,
445 QuestHelper
:ReleaseTable(list
)
448 QuestHelper
:ReleaseTable(seen_have
)
449 QuestHelper
:ReleaseTable(seen_need
)
452 if seen_sum
~= seen_max
then
454 w
, h
= addItem(oname(text
, pct
), quest
, obj
, -y
)
460 for i
= #obj_list
, 1, -1 do obj_list
[i
] = nil end
463 return x
, y
, gap
, count
466 local loading_vquest
= { cat
= "quest", obj
= "7777/" .. QHFormat("QH_LOADING", "0"), after
= {}, watched
= true }
467 local hidden_vquest1
= { cat
= "quest", obj
= "7778/" .. QHText("QUESTS_HIDDEN_1"), after
= {}, watched
= true }
468 local hidden_vquest2
= { cat
= "quest", obj
= "7778/ " .. QHText("QUESTS_HIDDEN_2"), after
= {}, watched
= true }
470 function tracker
:update(delta
)
472 -- This is called without a value when the questlog is updated.
473 -- We'll make sure we update the display on the next update.
479 local t
= self
.t
+delta
482 self
:SetWidth(self
.dw
)
483 self
:SetHeight(self
.dh
)
488 self
:SetWidth(self
.sw
*it
+self
.dw
*t
)
489 self
:SetHeight(self
.sh
*it
+self
.dh
*t
)
493 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
494 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
496 local x
, y
= GetCursorPosition()
497 local s
= 1/self
:GetEffectiveScale()
500 QuestHelper
: Assert(x
)
501 QuestHelper
: Assert(y
)
502 --[[ QuestHelper: Assert(self:GetLeft())
503 QuestHelper: Assert(self:GetBottom())
504 QuestHelper: Assert(self:GetRight())
505 QuestHelper: Assert(self:GetTop())]]
507 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
508 local inside
= (self
:GetLeft() and (x
>= self
:GetLeft() and y
>= self
:GetBottom() and x
< self
:GetRight() and y
< self
:GetTop()))
509 if inside
~= was_inside
then
512 minbutton
:SetAlpha(.7)
513 elseif not QuestHelper_Pref
.track_minimized
then
514 minbutton
:SetAlpha(0)
518 check_delay
= check_delay
+ delta
519 if check_delay
> 5 or (not QuestHelper
.Routing
.map_walker
and check_delay
> 0.5) then
522 local quests
= QuestHelper
.quest_log
526 local track_size
= QuestHelper_Pref
.track_size
527 local quests_added
= {}
529 for quest
, objs
in pairs(used_items
) do
530 for obj
, item
in pairs(objs
) do
535 for i
, objective
in pairs(QuestHelper
.route
) do
536 if objective
.watched
then
537 obj_index_lookup
[objective
] = i
541 for q
, data
in pairs(QuestHelper
.quest_log
) do
542 quest_lookup
[data
.index
] = q
545 -- Add our little "not yet loaded" notification
546 local loadedshow
= false
547 if not QuestHelper
.Routing
.map_walker
then
549 x
, y
, gap
, count
= addobj(loading_vquest
, seen
, nil, nil, x
, y
, gap
)
550 added
= added
+ count
553 if QuestHelper_Flight_Updates
and QuestHelper_Flight_Updates_Current
and QuestHelper_Flight_Updates
> 0 and QuestHelper_Flight_Updates_Current
< QuestHelper_Flight_Updates
then
554 loading_vquest
.obj
= string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current
* 100 / QuestHelper_Flight_Updates
)))
558 -- Add an extra large gap to seperate the notification from everything else
561 -- Add Quests that are watched but not in the route.
563 local uq_settings
= UberQuest_Config
[UnitName("player")]
565 local list
= uq_settings
.selected
569 local name
= GetQuestLogTitle(i
)
570 if not name
then break end
571 quest_lookup
[name
] = quest_lookup
[i
]
574 for name
in pairs(list
) do
575 local q
= quest_lookup
[name
]
576 if q
and not obj_index_lookup
[q
] then
578 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
579 added
= added
+ count
580 quests_added
[q
] = true
586 for i
= 1,GetNumQuestWatches() do
587 local q
= quest_lookup
[GetQuestIndexForWatch(i
)]
588 if q
and not obj_index_lookup
[q
] then
590 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
591 added
= added
+ count
596 -- Add Quests that are watched and are in the route.
597 for i
, objective
in ipairs(QuestHelper
.route
) do
599 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, watched_filter
, x
, y
, gap
)
600 added
= added
+ count
601 quests_added
[objective
] = true
604 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
607 -- Add Quests that aren't watched and are in the route.
608 if added
<= track_size
then
609 for i
, objective
in ipairs(QuestHelper
.route
) do
611 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
612 added
= added
+ count
613 quests_added
[objective
] = true
615 if added
> track_size
then
621 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
624 for k
, v
in pairs(quest_lookup
) do
625 if not quests_added
[v
] then
626 notadded
= notadded
+ 1
634 x
, y
, gap
, count
= addobj(hidden_vquest1
, seen
, nil, nil, x
, y
, gap
)
635 added
= added
+ count
636 x
, y
, gap
, count
= addobj(hidden_vquest2
, seen
, nil, nil, x
, y
, gap
)
637 added
= added
+ count
641 for obj
in pairs(obj_index_lookup
) do
642 obj_index_lookup
[obj
] = nil
645 for key
in pairs(quest_lookup
) do
646 quest_lookup
[key
] = nil
649 for quest
, objs
in pairs(used_items
) do
650 for obj
, item
in pairs(objs
) do
651 if not item
.used
then
652 removeUnusedItem(quest
, obj
, item
)
657 for key
in pairs(seen
) do
663 if x
~= tracker
.dw
or y
~= tracker
.dy
then
665 tracker
.sw
, tracker
.sh
= tracker
:GetWidth(), tracker
:GetHeight()
666 tracker
.dw
, tracker
.dh
= x
, y
674 tracker
:SetScript("OnUpdate", tracker
.update
)
676 -- Some hooks to update the tracker when quests are added or removed.
677 local orig_AddQuestWatch
, orig_RemoveQuestWatch
= AddQuestWatch
, RemoveQuestWatch
679 function AddQuestWatch(...)
681 return orig_AddQuestWatch(...)
684 function RemoveQuestWatch(...)
686 return orig_RemoveQuestWatch(...)
689 -------------------------------------------------------------------------------------------------
690 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
692 local orig_TrackerOnShow
693 if QuestWatchFrame
then -- 3.1 hackery
694 orig_TrackerOnShow
= QuestWatchFrame
:GetScript("OnShow")
696 local orig_TrackerBackdropOnShow
-- bEQL (and perhaps other mods) add a backdrop to the tracker
697 local TrackerBackdropFound
= false
699 local function TrackerBackdropOnShow(self
, ...)
700 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
701 TrackerBackdropFound
:Hide()
704 if orig_TrackerBackdropOnShow
then
705 return orig_TrackerBackdropOnShow(self
, ...)
709 function tracker
:HideDefaultTracker()
710 -- The easy part: hide the original tracker
711 if QuestWatchFrame
then -- 3.1 hackery
712 QuestWatchFrame
:Hide()
714 WatchFrame_RemoveObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
715 WatchFrame_ClearDisplay()
719 -- The harder part: check if a known backdrop is present (but we don't already know about it).
720 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
721 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
722 -- to keep checking. Hopefully, this won't happen too often.
723 if not TrackerBackdropFound
then
724 if QuestWatchFrameBackdrop
then
725 -- Found bEQL's QuestWatchFrameBackdrop...
726 TrackerBackdropFound
= QuestWatchFrameBackdrop
729 if TrackerBackdropFound
then
730 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
731 TrackerBackdropFound
:Hide()
733 orig_TrackerBackdropOnShow
= TrackerBackdropFound
:GetScript("OnShow")
734 TrackerBackdropFound
:SetScript("OnShow", TrackerBackdropOnShow
)
739 function tracker
:ShowDefaultTracker()
740 if QuestWatchFrame
then -- 3.1 hackery
741 QuestWatchFrame
:Show()
742 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
745 -- I like how there's code explicitly to allow us to do this without checking if it's already added
746 WatchFrame_AddObjectiveHandler(WatchFrame_DisplayTrackedQuests
)
747 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
751 if TrackerBackdropFound
then
752 TrackerBackdropFound
:Show()
756 if QuestWatchFrame
then -- 3.1 hackery
757 local function QuestWatchFrameOnShow(self
, ...)
758 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
759 tracker
:HideDefaultTracker()
762 if orig_TrackerOnShow
then
763 return orig_TrackerOnShow(self
, ...)
767 QuestWatchFrame
:SetScript("OnShow", QuestWatchFrameOnShow
)
770 function QuestHelper
:ShowTracker()
771 tracker
:HideDefaultTracker()
774 if QuestHelper_Pref
.track_minimized
then
775 minbutton
:SetAlpha(.3)
777 minbutton
:SetAlpha(0)
782 function QuestHelper
:HideTracker()
783 tracker
:ShowDefaultTracker()