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 self
:TextOut("Quest tracker postion reset.")
53 minbutton
:SetScript("OnEvent", minbutton
.moved
)
54 minbutton
:RegisterEvent("DISPLAY_SIZE_CHANGED")
55 minbutton
:RegisterEvent("PLAYER_ENTERING_WORLD")
57 minbutton
:SetScript("OnClick", function ()
58 QuestHelper_Pref
.track_minimized
= not QuestHelper_Pref
.track_minimized
59 if QuestHelper_Pref
.track_minimized
then
66 minbutton
:RegisterForDrag("LeftButton")
68 minbutton
:SetScript("OnDragStart", function(self
)
69 if self
:IsVisible() then
71 self
:SetScript("OnUpdate", self
.moved
)
75 minbutton
:SetScript("OnDragStop", function(self
)
76 self
:SetScript("OnUpdate", nil)
77 self
:StopMovingOrSizing()
81 minbutton
:SetScript("OnEnter", function (self
)
85 minbutton
:SetScript("OnLeave", function (self
)
86 self
:SetAlpha(QuestHelper_Pref
.track_minimized
and .3 or .5)
89 local unused_items
= {}
92 local function itemupdate(item
, delta
)
95 local a
= item
:GetAlpha()
105 local t
= item
.t
+ delta
110 local sp
= math
.sqrt(t
-t
*t
)
111 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
115 item
.x
, item
.y
= item
.dx
, item
.dy
118 item
:ClearAllPoints()
119 item
:SetPoint("TOPLEFT", tracker
, "TOPLEFT", item
.x
, item
.y
)
122 item
:SetScript("OnUpdate", nil)
126 local function itemfadeout(item
, delta
)
127 local a
= item
:GetAlpha()
135 item
:SetScript("OnUpdate", nil)
139 local t
= item
.t
+ delta
144 local sp
= math
.sqrt(t
-t
*t
)
145 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
148 item
.x
, item
.y
= item
.dx
, item
.dy
151 item
:ClearAllPoints()
152 item
:SetPoint("TOPLEFT", tracker
, "TOPLEFT", item
.x
, item
.y
)
155 function QH_ToggleQuestLog() -- This seems to be gone in 3.0, so I'm adding it here.
156 if (QuestLogFrame
:IsShown()) then
157 HideUIPanel(QuestLogFrame
);
159 ShowUIPanel(QuestLogFrame
);
164 local function itemclick(item
, button
)
165 if button
== "RightButton" then
166 local quest
= item
.quest
169 local title
= GetQuestLogTitle(index
)
170 if not title
then break end
172 if title
== quest
then
174 -- UberQuest needs a little extra effort to work properly.
176 if UberQuest_List
:IsShown() and GetQuestLogSelection() == index
then
179 QuestLog_SetSelection(index
)
181 -- By hiding the list, the replaced ToggleQuestLog function should try to reshow it
182 -- and in the process update the frames to reflect the selected quest.
183 UberQuest_List
:Hide()
184 UberQuest_Details
:Show()
188 -- This code seems to work properly with the builtin questlog, as well as bEQL and DoubleWide.
190 if QuestLogFrame
:IsShown() and GetQuestLogSelection() == index
then
191 -- If the selected quest is already being shown, hide it.
194 -- Otherwise, select it and show it.
195 QuestLog_SetSelection(index
)
197 if not QuestLogFrame
:IsShown() then
211 local function addItem(name
, quest
, obj
, y
, qname
)
212 local x
= qname
and 4 or 20
213 local item
= used_items
[quest
] and used_items
[quest
][obj
]
215 item
= next(unused_items
)
217 unused_items
[item
] = nil
219 item
= CreateFrame("Frame", nil, tracker
)
220 item
.text
= item
:CreateFontString()
221 item
.text
:SetShadowColor(0, 0, 0, .8)
222 item
.text
:SetShadowOffset(1, -1)
223 item
.text
:SetPoint("TOPLEFT", item
)
227 item
.text
:SetFont(QuestHelper
.font
.serif
, 12)
228 item
.text
:SetTextColor(.82, .65, 0)
230 item
.text
:SetFont(QuestHelper
.font
.sans
, 12)
231 item
.text
:SetTextColor(.82, .82, .82)
234 if not used_items
[quest
] then used_items
[quest
] = {} end
236 used_items
[quest
][obj
] = item
237 item
.sx
, item
.sy
, item
.x
, item
.y
, item
.dx
, item
.dy
, item
.t
= x
+30, y
, x
, y
, x
, y
, 0
238 item
:SetScript("OnUpdate", itemupdate
)
246 item
.text
:SetText(name
)
247 local w
, h
= item
.text
:GetWidth(), item
.text
:GetHeight()
252 item
:SetScript("OnMouseDown", itemclick
)
253 item
:EnableMouse(true)
256 if item
.dx
~= x
or item
.dy
~= y
then
257 item
.sx
, item
.sy
, item
.dx
, item
.dy
= item
.x
, item
.y
, x
, y
259 item
:SetScript("OnUpdate", itemupdate
)
265 local function ccode(r1
, g1
, b1
, r2
, g2
, b2
, p
)
267 p
, ip
= p
*255, 255-p
*255
268 return string.format("|cff%02x%02x%02x", r1
*ip
+r2
*p
, g1
*ip
+g2
*p
, b1
*ip
+b2
*p
)
271 local function qname(title
, level
)
272 if QuestHelper_Pref
.track_level
and level
~= 7777 and level
~= 7778 then
273 title
= string.format("[%d] %s", level
, title
)
276 if level
== 7778 then
280 if QuestHelper_Pref
.track_qcolour
then
281 local player_level
= QuestHelper
.player_level
282 local delta
= level
- player_level
287 colour
= "|cffff0000"
288 elseif delta
>= 0 then
289 colour
= ccode(1, 1, 0, 1, 0, 0, delta
/5)
293 if player_level
>= 60 then grey
= player_level
- 9
294 elseif player_level
>= 40 then grey
= player_level
- math
.floor(player_level
/5) - 1
295 elseif player_level
>= 6 then grey
= player_level
- math
.floor(player_level
/10) - 5
298 if level
== -7778 then
299 colour
= "|cff808080"
300 elseif level
> grey
then
301 colour
= ccode(0, 1, 0, 1, 1, 0, (grey
-level
)/(grey
-player_level
))
303 colour
= ccode(.4, .4, .4, .2, .8, .2, (1-level
)/(1-grey
))
307 title
= string.format("%s%s", colour
, title
)
313 local function oname(text
, pct
)
314 if QuestHelper_Pref
.track_ocolour
then
315 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
)
321 local function removeUnusedItem(quest
, obj
, item
)
322 unused_items
[item
] = true
323 used_items
[quest
][obj
] = nil
324 if not next(used_items
[quest
]) then used_items
[quest
] = nil end
327 item
.sx
, item
.sy
= item
.x
, item
.y
328 item
.dx
, item
.dy
= item
.x
+30, item
.y
329 item
:SetScript("OnMouseDown", nil)
330 item
:EnableMouse(false)
331 item
:SetScript("OnUpdate", itemfadeout
)
334 local resizing
= false
335 local check_delay
= 4
338 local obj_index_lookup
= {}
339 local quest_lookup
= {}
340 local was_inside
= false
342 local function watched_filter(obj
)
343 return obj
:IsWatched()
346 local function objlist_sort(a
, b
)
347 return (obj_index_lookup
[a
] or 0) < (obj_index_lookup
[b
] or 0)
350 function tracker
:reset()
351 for quest
, objs
in pairs(used_items
) do
352 for obj
, item
in pairs(objs
) do
353 removeUnusedItem(quest
, obj
, item
)
359 local function addobj(objective
, seen
, obj_index_lookup
, filter
, x
, y
, gap
)
363 if objective
.cat
== "quest" then
366 quest
= objective
.quest
369 if quest
and quest
.watched
and not seen
[quest
] and (not filter
or filter(quest
)) then
372 local level
, name
= string.match(quest
.obj
, "^(%d+)/%d*/(.*)$")
375 level
, name
= string.match(quest
.obj
, "^(%d+)/(.*)$")
377 level
, name
= 1, quest
.obj
381 level
= tonumber(level
) or 1
384 local w
, h
= addItem(qname(name
, level
), true, quest
, -(y
+gap
), name
)
389 for obj
in pairs(quest
.swap_after
or quest
.after
) do
391 table.insert(obj_list
, obj
)
395 table.sort(obj_list
, objlist_sort
)
397 for i
, obj
in ipairs(obj_list
) do
398 local pct
, text
= 0, obj
.obj
399 local seen_sum
, seen_max
= 0, 0
402 local seen_have
, seen_need
= QuestHelper
:CreateTable(), QuestHelper
:CreateTable()
404 for user
, progress
in pairs(obj
.progress
) do
405 seen_sum
= seen_sum
+ progress
[3]
406 seen_max
= seen_max
+ 1
407 seen_have
[progress
[1]]
= true
408 seen_need
[progress
[2]]
= true
412 pct
= seen_sum
/ seen_max
413 local list
= QuestHelper
:CreateTable()
415 for val
in pairs(seen_have
) do
416 table.insert(list
, val
)
421 local have
= table.concat(list
, ", ")
423 for i
= #list
,1,-1 do
427 for val
in pairs(seen_need
) do
428 table.insert(list
, val
)
431 if #list
~= 1 or list
[1] ~= 1 then
432 -- If there is only one thing needed, ignore the progress, it's redundant.
433 -- It's either shown or it isn't.
437 local need
= table.concat(list
, ", ")
439 text
= string.format((tonumber(have
) and tonumber(need
) and QUEST_ITEMS_NEEDED
) or QUEST_FACTION_NEEDED
,
443 QuestHelper
:ReleaseTable(list
)
446 QuestHelper
:ReleaseTable(seen_have
)
447 QuestHelper
:ReleaseTable(seen_need
)
450 if seen_sum
~= seen_max
then
452 w
, h
= addItem(oname(text
, pct
), quest
, obj
, -y
)
458 for i
= #obj_list
, 1, -1 do obj_list
[i
] = nil end
461 return x
, y
, gap
, count
464 local loading_vquest
= { cat
= "quest", obj
= "7777/" .. QHFormat("QH_LOADING", "0"), after
= {}, watched
= true }
465 local hidden_vquest1
= { cat
= "quest", obj
= "7778/" .. QHText("QUESTS_HIDDEN_1"), after
= {}, watched
= true }
466 local hidden_vquest2
= { cat
= "quest", obj
= "7778/ " .. QHText("QUESTS_HIDDEN_2"), after
= {}, watched
= true }
468 function tracker
:update(delta
)
470 -- This is called without a value when the questlog is updated.
471 -- We'll make sure we update the display on the next update.
477 local t
= self
.t
+delta
480 self
:SetWidth(self
.dw
)
481 self
:SetHeight(self
.dh
)
486 self
:SetWidth(self
.sw
*it
+self
.dw
*t
)
487 self
:SetHeight(self
.sh
*it
+self
.dh
*t
)
491 -- Manually checking if the mouse is in the frame, because if I used on OnEnter, i'd have to enable mouse input,
492 -- and if I did that, it would prevent the player from using the mouse to change the view if they clicked inside
494 local x
, y
= GetCursorPosition()
495 local s
= 1/self
:GetEffectiveScale()
498 QuestHelper
: Assert(x
)
499 QuestHelper
: Assert(y
)
500 --[[ QuestHelper: Assert(self:GetLeft())
501 QuestHelper: Assert(self:GetBottom())
502 QuestHelper: Assert(self:GetRight())
503 QuestHelper: Assert(self:GetTop())]]
505 -- Sometimes it just doesn't know its own coordinates. Not sure why. Maybe this will fix it.
506 local inside
= (self
:GetLeft() and (x
>= self
:GetLeft() and y
>= self
:GetBottom() and x
< self
:GetRight() and y
< self
:GetTop()))
507 if inside
~= was_inside
then
510 minbutton
:SetAlpha(.7)
511 elseif not QuestHelper_Pref
.track_minimized
then
512 minbutton
:SetAlpha(0)
516 check_delay
= check_delay
+ delta
517 if check_delay
> 5 or (not QuestHelper
.Routing
.map_walker
and check_delay
> 0.5) then
520 local quests
= QuestHelper
.quest_log
524 local track_size
= QuestHelper_Pref
.track_size
525 local quests_added
= {}
527 for quest
, objs
in pairs(used_items
) do
528 for obj
, item
in pairs(objs
) do
533 for i
, objective
in pairs(QuestHelper
.route
) do
534 if objective
.watched
then
535 obj_index_lookup
[objective
] = i
539 for q
, data
in pairs(QuestHelper
.quest_log
) do
540 quest_lookup
[data
.index
] = q
543 -- Add our little "not yet loaded" notification
544 local loadedshow
= false
545 if not QuestHelper
.Routing
.map_walker
then
547 x
, y
, gap
, count
= addobj(loading_vquest
, seen
, nil, nil, x
, y
, gap
)
548 added
= added
+ count
551 if QuestHelper_Flight_Updates
and QuestHelper_Flight_Updates_Current
and QuestHelper_Flight_Updates
> 0 and QuestHelper_Flight_Updates_Current
< QuestHelper_Flight_Updates
then
552 loading_vquest
.obj
= string.format("7777/" .. QHFormat("QH_LOADING", string.format("%d", QuestHelper_Flight_Updates_Current
* 100 / QuestHelper_Flight_Updates
)))
556 -- Add an extra large gap to seperate the notification from everything else
559 -- Add Quests that are watched but not in the route.
561 local uq_settings
= UberQuest_Config
[UnitName("player")]
563 local list
= uq_settings
.selected
567 local name
= GetQuestLogTitle(i
)
568 if not name
then break end
569 quest_lookup
[name
] = quest_lookup
[i
]
572 for name
in pairs(list
) do
573 local q
= quest_lookup
[name
]
574 if q
and not obj_index_lookup
[q
] then
576 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
577 added
= added
+ count
578 quests_added
[q
] = true
584 for i
= 1,GetNumQuestWatches() do
585 local q
= quest_lookup
[GetQuestIndexForWatch(i
)]
586 if q
and not obj_index_lookup
[q
] then
588 x
, y
, gap
, count
= addobj(q
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
589 added
= added
+ count
594 -- Add Quests that are watched and are in the route.
595 for i
, objective
in ipairs(QuestHelper
.route
) do
597 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, watched_filter
, x
, y
, gap
)
598 added
= added
+ count
599 quests_added
[objective
] = true
602 -- Add an extra large gap to seperate the watched objectives from the automatic objectives.
605 -- Add Quests that aren't watched and are in the route.
606 if added
<= track_size
then
607 for i
, objective
in ipairs(QuestHelper
.route
) do
609 x
, y
, gap
, count
= addobj(objective
, seen
, obj_index_lookup
, nil, x
, y
, gap
)
610 added
= added
+ count
611 quests_added
[objective
] = true
613 if added
> track_size
then
619 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
622 for k
, v
in pairs(quest_lookup
) do
623 if not quests_added
[v
] then
624 notadded
= notadded
+ 1
632 x
, y
, gap
, count
= addobj(hidden_vquest1
, seen
, nil, nil, x
, y
, gap
)
633 added
= added
+ count
634 x
, y
, gap
, count
= addobj(hidden_vquest2
, seen
, nil, nil, x
, y
, gap
)
635 added
= added
+ count
639 for obj
in pairs(obj_index_lookup
) do
640 obj_index_lookup
[obj
] = nil
643 for key
in pairs(quest_lookup
) do
644 quest_lookup
[key
] = nil
647 for quest
, objs
in pairs(used_items
) do
648 for obj
, item
in pairs(objs
) do
649 if not item
.used
then
650 removeUnusedItem(quest
, obj
, item
)
655 for key
in pairs(seen
) do
661 if x
~= tracker
.dw
or y
~= tracker
.dy
then
663 tracker
.sw
, tracker
.sh
= tracker
:GetWidth(), tracker
:GetHeight()
664 tracker
.dw
, tracker
.dh
= x
, y
672 tracker
:SetScript("OnUpdate", tracker
.update
)
674 -- Some hooks to update the tracker when quests are added or removed.
675 local orig_AddQuestWatch
, orig_RemoveQuestWatch
= AddQuestWatch
, RemoveQuestWatch
677 function AddQuestWatch(...)
679 return orig_AddQuestWatch(...)
682 function RemoveQuestWatch(...)
684 return orig_RemoveQuestWatch(...)
687 -------------------------------------------------------------------------------------------------
688 -- This batch of stuff is to make sure the original tracker (and any modifications) stay hidden
690 local orig_TrackerOnShow
= QuestWatchFrame
:GetScript("OnShow")
691 local orig_TrackerBackdropOnShow
-- bEQL (and perhaps other mods) add a backdrop to the tracker
692 local TrackerBackdropFound
= false
694 local function TrackerBackdropOnShow(self
, ...)
695 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
696 TrackerBackdropFound
:Hide()
699 if orig_TrackerBackdropOnShow
then
700 return orig_TrackerBackdropOnShow(self
, ...)
704 function tracker
:HideDefaultTracker()
705 -- The easy part: hide the original tracker
706 QuestWatchFrame
:Hide()
708 -- The harder part: check if a known backdrop is present (but we don't already know about it).
709 -- If it is, make sure it's hidden, and hook its OnShow to make sure it stays that way.
710 -- Unfortunately, I can't figure out a good time to check for this once, so we'll just have
711 -- to keep checking. Hopefully, this won't happen too often.
712 if not TrackerBackdropFound
then
713 if QuestWatchFrameBackdrop
then
714 -- Found bEQL's QuestWatchFrameBackdrop...
715 TrackerBackdropFound
= QuestWatchFrameBackdrop
718 if TrackerBackdropFound
then
719 -- OK, we found something - so hide it, and make sure it doesn't rear its ugly head again
720 TrackerBackdropFound
:Hide()
722 orig_TrackerBackdropOnShow
= TrackerBackdropFound
:GetScript("OnShow")
723 TrackerBackdropFound
:SetScript("OnShow", TrackerBackdropOnShow
)
728 function tracker
:ShowDefaultTracker()
729 QuestWatchFrame
:Show()
731 -- Make sure the default tracker is up to date on what what's being watched and what isn't.
734 if TrackerBackdropFound
then
735 TrackerBackdropFound
:Show()
739 local function QuestWatchFrameOnShow(self
, ...)
740 if QuestHelper_Pref
.track
and not QuestHelper_Pref
.hide
then
741 tracker
:HideDefaultTracker()
744 if orig_TrackerOnShow
then
745 return orig_TrackerOnShow(self
, ...)
749 QuestWatchFrame
:SetScript("OnShow", QuestWatchFrameOnShow
)
751 function QuestHelper
:ShowTracker()
752 tracker
:HideDefaultTracker()
755 if QuestHelper_Pref
.track_minimized
then
756 minbutton
:SetAlpha(.3)
758 minbutton
:SetAlpha(0)
763 function QuestHelper
:HideTracker()
764 tracker
:ShowDefaultTracker()