--[[ ########################################################## S V U I By: Failcoder ########################################################## LOCALIZED LUA FUNCTIONS ########################################################## ]]-- --[[ GLOBALS ]]-- local _G = _G; local unpack = _G.unpack; local select = _G.select; local pairs = _G.pairs; local ipairs = _G.ipairs; local type = _G.type; local error = _G.error; local pcall = _G.pcall; local tostring = _G.tostring; local tonumber = _G.tonumber; local tinsert = _G.tinsert; local string = _G.string; local math = _G.math; local table = _G.table; local tinsert = _G.tinsert; local tremove = _G.tremove; local wipe = _G.wipe; --[[ STRING METHODS ]]-- local format = string.format; --[[ MATH METHODS ]]-- local abs, ceil, floor, round, maxNum = math.abs, math.ceil, math.floor, math.round, math.max; --[[ TABLE METHODS ]]-- local tsort, tcopy = table.sort, table.copy; --BLIZZARD API local CreateFrame = _G.CreateFrame; local InCombatLockdown = _G.InCombatLockdown; local GameTooltip = _G.GameTooltip; local hooksecurefunc = _G.hooksecurefunc; --[[ ########################################################## GET ADDON DATA ########################################################## ]]-- local SV = _G['SVUI'] local L = SV.L local LSM = _G.LibStub("LibSharedMedia-3.0") local MOD = SV.QuestTracker; --[[ ########################################################## LOCALS ########################################################## ]]-- local ITEM_BUTTON_SIZE = 28; local ITEMS_PER_ROW = 5; local ROW_WIDTH = 300; local ROW_HEIGHT = 20; local QUEST_ROW_HEIGHT = ROW_HEIGHT + 2; local INNER_HEIGHT = ROW_HEIGHT - 4; local LARGE_ROW_HEIGHT = ROW_HEIGHT * 2; local LARGE_INNER_HEIGHT = LARGE_ROW_HEIGHT - 4; local OBJ_ICON_ACTIVE = [[Interface\COMMON\Indicator-Yellow]]; local OBJ_ICON_COMPLETE = [[Interface\COMMON\Indicator-Green]]; local OBJ_ICON_INCOMPLETE = [[Interface\COMMON\Indicator-Gray]]; local CACHED_QUESTS = {}; local QUESTS_BY_LOCATION = {}; local QUEST_HEADER_MAP = {}; local USED_QUESTIDS = {}; local TICKERS, ACTIVE_ITEMS, SWAP_ITEMS = {}, {}, {}; local CURRENT_MAP_ID = 0; local WORLDMAP_UPDATE = false; local DEFAULT_COLOR = {r = 1, g = 0.68, b = 0.1} local QuestInZone = { [14108] = 541, [13998] = 11, [25798] = 61, [25799] = 61, [25112] = 161, [25111] = 161, [24735] = 201, }; local ItemBlacklist = { [113191] = true, [110799] = true, [109164] = true, }; --[[ ########################################################## ITEM BAR/BUTTON CONSTRUCT ########################################################## ]]-- local ItemBar = _G["SVUI_QuestItemBar"]; ItemBar.Buttons = {}; local ItemBar_OnEvent = function(self, event) if(event == 'PLAYER_REGEN_ENABLED' and self.needsUpdate) then self:Update() self:UnregisterEvent("PLAYER_REGEN_ENABLED") end end ItemBar:SetScript('OnEvent', ItemBar_OnEvent); local CreateQuestItemButton; do --[[ HANDLERS ]]-- local Button_UpdateCooldown = function(self) if(self:IsShown() and self.itemID) then local start, duration, enable = GetItemCooldown(self.itemID) if((start and start > 0) and (duration and duration > 0)) then self.Cooldown:SetCooldown(start, duration) self.Cooldown:Show() else self.Cooldown:Hide() end end end local Button_OnEnter = function(self) if(self.itemID and (not MOD.DOCK_IS_FADED)) then GameTooltip:SetOwner(self, 'ANCHOR_LEFT') GameTooltip:SetHyperlink(self.itemLink) end end local Button_OnLeave = function(self) GameTooltip_Hide() end local Button_OnEvent = function(self, event) if(event == 'BAG_UPDATE_COOLDOWN') then self:UpdateCooldown() elseif(event == 'PLAYER_REGEN_ENABLED') then self:SetAttribute('item', self.attribute) self:UnregisterEvent(event) self:UpdateCooldown() --else --self:Update() end end local Button_SetItem = function(self, itemLink, texture, completed) if(completed) then self:ClearUsage() return end if(itemLink) then if(ACTIVE_ITEMS[itemLink] or ((itemLink == self.itemLink) and self:IsShown())) then return end ACTIVE_ITEMS[itemLink] = self:GetID(); self.Icon:SetTexture(texture) self.itemID, self.itemName = string.match(itemLink, '|Hitem:(.-):.-|h%[(.+)%]|h') self.itemLink = itemLink if(ItemBlacklist[self.itemID]) then return end self:FadeIn() end if(InCombatLockdown()) then self.attribute = self.itemName self:RegisterEvent('PLAYER_REGEN_ENABLED') else self:SetAttribute('item', self.itemName) self:UpdateCooldown() end return true end local Button_ClearItem = function(self) self.attribute = nil; if(InCombatLockdown()) then self:RegisterEvent('PLAYER_REGEN_ENABLED'); else self:SetAttribute('item', nil) end end local Button_UpdateItem = function(self) local numItems = 0 local shortestDistance = 62500 local closestQuestLink, closestQuestTexture local activeQuestLink, activeQuestTexture for index = 1, GetNumQuestWatches() do local questID, _, questIndex, _, _, isComplete = GetQuestWatchInfo(index) if(questID and QuestHasPOIInfo(questID)) then local link, texture, _, showCompleted = GetQuestLogSpecialItemInfo(questIndex) if(link) then local areaID = QuestInZone[questID] if questIndex == MOD.CurrentQuest then activeQuestLink = link activeQuestTexture = texture end if(areaID and areaID == GetCurrentMapAreaID()) then closestQuestLink = link closestQuestTexture = texture elseif(not isComplete or (isComplete and showCompleted)) then local distanceSq, onContinent = GetDistanceSqToQuest(questIndex) if(onContinent and distanceSq < shortestDistance) then shortestDistance = distanceSq closestQuestLink = link closestQuestTexture = texture end end numItems = numItems + 1 end end end if(closestQuestLink) then self:SetUsage(closestQuestLink, closestQuestTexture); elseif(activeQuestLink) then self:SetUsage(activeQuestLink, activeQuestTexture); end local name = self:GetName(); if(numItems > 0 and not TICKERS[name]) then TICKERS[name] = C_Timer.NewTicker(30, function() self:Update() end) elseif(numItems == 0 and TICKERS[name]) then TICKERS[name]:Cancel() TICKERS[name] = nil end end --[[ METHOD ]]-- CreateQuestItemButton = function(index) local buttonName = "SVUI_QuestButton" .. index local itembutton = CreateFrame('Button', buttonName, UIParent, 'SecureActionButtonTemplate, SecureHandlerStateTemplate, SecureHandlerAttributeTemplate'); itembutton:SetAlpha(0); itembutton:SetStyle("!_ActionSlot"); --itembutton:SetBackdropBorderColor(0.1, 0.1, 0.1) itembutton:SetSize(ITEM_BUTTON_SIZE, ITEM_BUTTON_SIZE); itembutton:SetID(index); itembutton.___overflow = false; itembutton.SetUsage = Button_SetItem; itembutton.ClearUsage = Button_ClearItem; itembutton.Update = Button_UpdateItem; itembutton.UpdateCooldown = Button_UpdateCooldown; local Icon = itembutton:CreateTexture('$parentIcon', 'BACKGROUND') Icon:SetTexCoord(unpack(_G.SVUI_ICON_COORDS)) Icon:SetAllPoints() itembutton.Icon = Icon local Cooldown = CreateFrame('Cooldown', '$parentCooldown', itembutton, 'CooldownFrameTemplate') Cooldown:ClearAllPoints() Cooldown:SetPoint('TOPRIGHT', -2, -3) Cooldown:SetPoint('BOTTOMLEFT', 2, 1) Cooldown:Hide() itembutton.Cooldown = Cooldown --RegisterStateDriver(itembutton, 'visible', '[petbattle] hide; show') itembutton:SetAttribute('type', 'item'); itembutton:SetAttribute('_onattributechanged', [[ if(name == 'item') then if(value and not self:IsShown()) then self:Show() self:SetAlpha(1) elseif(not value) then self:SetAlpha(0) self:Hide() end end ]]); itembutton:SetScript('OnEnter', Button_OnEnter); itembutton:SetScript('OnLeave', Button_OnLeave); itembutton:SetScript('OnEvent', Button_OnEvent); itembutton:RegisterEvent('UPDATE_EXTRA_ACTIONBAR'); itembutton:RegisterEvent('BAG_UPDATE_COOLDOWN'); itembutton:RegisterEvent('BAG_UPDATE_DELAYED'); itembutton:RegisterEvent('WORLD_MAP_UPDATE'); itembutton:RegisterEvent('QUEST_LOG_UPDATE'); itembutton:RegisterEvent('QUEST_POI_UPDATE'); SV:ManageVisibility(itembutton) return itembutton end function ItemBar:SetQuestItem(itemLink, texture, completed) if(not itemLink) then return end local savedIndex = ACTIVE_ITEMS[itemLink] if(savedIndex and self.Buttons[savedIndex]) then self.Buttons[savedIndex]:SetUsage(itemLink, texture, completed) else local maxIndex = #self.Buttons; for i = 1, maxIndex do if(not self.Buttons[i]:GetAttribute('item')) then self.Buttons[i]:SetUsage(itemLink, texture, completed) return end end local index = maxIndex + 1 self.Buttons[index] = CreateQuestItemButton(index) self.Buttons[index]:SetUsage(itemLink, texture, completed) end end end function ItemBar:Reset() local maxIndex = #self.Buttons; for i = 1, maxIndex do local button = self.Buttons[i]; button:ClearUsage(); end end local function HideItemBarButtons() local maxIndex = #ItemBar.Buttons; for i = 1, maxIndex do local button = ItemBar.Buttons[i]; button:FadeOut(0.1, 1, 0, true); end ItemBar:FadeOut(0.1, 1, 0, true); end local function ShowItemBarButtons() ItemBar:FadeIn(); local maxIndex = #ItemBar.Buttons; for link, index in pairs(ACTIVE_ITEMS) do local button = ItemBar.Buttons[index]; button:FadeIn(); end MOD.Headers["Quests"]:Reset(); MOD.Headers["Quests"]:Refresh(); end function ItemBar:Update() if(InCombatLockdown()) then self:RegisterEvent("PLAYER_REGEN_ENABLED") self.needsUpdate = true return end wipe(ACTIVE_ITEMS); local maxIndex = #self.Buttons; local firstButton = self.Buttons[1]; local itemLink = firstButton.itemLink; if(itemLink) then ACTIVE_ITEMS[itemLink] = 1 end local dockletLocation = MOD.Docklet.Parent.Bar.Data.Location; local isHorizontal = (SV.db.QuestTracker.itemBarDirection == 'HORIZONTAL'); local anchor1 = isHorizontal and "LEFT" or "TOP"; local anchor2 = isHorizontal and "RIGHT" or "BOTTOM"; local switch1 = isHorizontal and "BOTTOM" or "RIGHT"; local switch2 = isHorizontal and "TOP" or "LEFT"; local xOff = isHorizontal and 2 or 0; local yOff = isHorizontal and 0 or -2; local xChange = isHorizontal and 0 or -2; local yChange = isHorizontal and 2 or 0; local itemScale = ITEM_BUTTON_SIZE + 2; if(self.Grip and (not self.Grip:HasMoved())) then local dockWidth,dockHeight = MOD.Docklet:GetSize() ITEMS_PER_ROW = isHorizontal and (dockWidth / itemScale) or (dockHeight / itemScale); end firstButton:ClearAllPoints(); firstButton:SetSize(ITEM_BUTTON_SIZE, ITEM_BUTTON_SIZE); if(dockletLocation:find('Left')) then anchor1 = isHorizontal and "RIGHT" or "TOP"; anchor2 = isHorizontal and "LEFT" or "BOTTOM"; switch1 = isHorizontal and "BOTTOM" or "LEFT"; switch2 = isHorizontal and "TOP" or "RIGHT"; xOff = isHorizontal and -2 or 0; xChange = isHorizontal and 0 or 2; end if(dockletLocation:find('Top')) then if(not isHorizontal) then anchor1 = "BOTTOM"; anchor2 = "TOP"; yOff = 2; else switch1 = "TOP"; switch2 = "BOTTOM"; yChange = -2; end end if(isHorizontal) then firstButton:SetPoint(anchor1, self, anchor1, 0, 0); if(SV.Tooltip and (not self.tipanchorchecked) and SV.Tooltip.Holder and SV.Tooltip.Holder.Grip and (not SV.Tooltip.Holder.Grip:HasMoved())) then SV.Tooltip.DefaultPadding = 56 self.tipanchorchecked = true end else firstButton:SetPoint(anchor2, self, anchor2, 0, 0); end local lastButton, totalShown, button = firstButton, 1; for i = 2, maxIndex do button = self.Buttons[i]; itemLink = button.itemLink; button:ClearAllPoints(); button:SetSize(ITEM_BUTTON_SIZE, ITEM_BUTTON_SIZE); if(button:IsShown()) then totalShown = totalShown + 1; if(totalShown == (ITEMS_PER_ROW + 1)) then totalShown = 1; local lastRowButton = self.Buttons[i - ITEMS_PER_ROW]; if(lastRowButton) then button:SetPoint(switch1, lastRowButton, switch2, xChange, yChange) else button:SetPoint(switch1, firstButton, switch2, xChange, yChange) end else button:SetPoint(anchor2, lastButton, anchor1, xOff, -yOff) end button:FadeIn(); lastButton = button if(itemLink) then ACTIVE_ITEMS[itemLink] = i else button:ClearUsage() end end end self.needsUpdate = nil end --[[ ########################################################## QUEST CACHING ########################################################## ]]-- local function CacheQuestHeaders() wipe(QUEST_HEADER_MAP) local currentHeader = "Misc"; local numEntries, numQuests = GetNumQuestLogEntries(); for i = 1, numEntries do local title, _, _, isHeader, _, _, _, questID = GetQuestLogTitle(i); if(isHeader) then currentHeader = title; else QUEST_HEADER_MAP[questID] = currentHeader end end end local function UpdateCachedQuests(needsSorting) local s = 62500; local c = 0; local li = 0; local HeadersCached = false; wipe(QUESTS_BY_LOCATION) for i = 1, GetNumQuestWatches() do local questID, _, questLogIndex, numObjectives, _, completed, _, _, duration, elapsed, questType, isTask, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(i); if(questID) then -- and (not USED_QUESTIDS[questID]) local distanceSq, onContinent = GetDistanceSqToQuest(questLogIndex) local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questLogIndex) local link, texture, _, showCompleted = GetQuestLogSpecialItemInfo(questLogIndex) if(not CACHED_QUESTS[questID]) then CACHED_QUESTS[questID] = {i, title, level, texture, questID, questLogIndex, numObjectives, duration, elapsed, completed, questType, link}; else CACHED_QUESTS[questID][1] = i; -- args: quest watch index CACHED_QUESTS[questID][2] = title; -- args: quest title CACHED_QUESTS[questID][3] = level; -- args: quest level CACHED_QUESTS[questID][4] = texture; -- args: quest item icon CACHED_QUESTS[questID][5] = questID; -- args: quest id CACHED_QUESTS[questID][6] = questLogIndex; -- args: quest log index CACHED_QUESTS[questID][7] = numObjectives; -- args: quest objective count CACHED_QUESTS[questID][8] = duration; -- args: quest timer duration CACHED_QUESTS[questID][9] = elapsed; -- args: quest timer elapsed CACHED_QUESTS[questID][10] = completed; -- args: quest is completed CACHED_QUESTS[questID][11] = questType; -- args: quest type CACHED_QUESTS[questID][12] = link; -- args: quest item link end if(questID == MOD.ActiveQuestID) then MOD:UpdateActiveObjective('FORCED_UPDATE') end if(not QUEST_HEADER_MAP[questID] and (not HeadersCached)) then CacheQuestHeaders() HeadersCached = true end local header = QUEST_HEADER_MAP[questID] or "Misc" tinsert(QUESTS_BY_LOCATION, {distanceSq, header, questID}); end end tsort(QUESTS_BY_LOCATION, function(a,b) if(a[2] and b[2]) then return a[2] < b[2] else return false end end); tsort(QUESTS_BY_LOCATION, function(a,b) if(a[1] and b[1]) then return a[1] < b[1] else return false end end); end local function UpdateCachedDistance() local s = 62500; wipe(QUESTS_BY_LOCATION) local HeadersCached = false; for questID,questData in pairs(CACHED_QUESTS) do local questLogIndex = questData[6]; local distanceSq, onContinent = GetDistanceSqToQuest(questLogIndex) if(not QUEST_HEADER_MAP[questID] and (not HeadersCached)) then CacheQuestHeaders() HeadersCached = true end local header = QUEST_HEADER_MAP[questID] or "Misc" tinsert(QUESTS_BY_LOCATION, {distanceSq, header, questID}); end tsort(QUESTS_BY_LOCATION, function(a,b) if(a[2] and b[2]) then return a[2] < b[2] else return false end end); tsort(QUESTS_BY_LOCATION, function(a,b) if(a[1] and b[1]) then return a[1] < b[1] else return false end end); end local function AddCachedQuest(questLogIndex) local HeadersCached = false; if(questLogIndex) then -- and (not USED_QUESTIDS[questID]) local i = GetQuestWatchIndex(questLogIndex) if(i) then local distanceSq, onContinent = GetDistanceSqToQuest(questLogIndex) local questID, _, _, numObjectives, _, completed, _, _, duration, elapsed, questType, isTask, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(i); if(not CACHED_QUESTS[questID]) then local title, level, suggestedGroup = GetQuestLogTitle(questLogIndex) local link, texture, _, showCompleted = GetQuestLogSpecialItemInfo(questLogIndex) local mapID, floorNumber = 0,0 if(not WorldMapFrame:IsShown()) then mapID, floorNumber = GetQuestWorldMapAreaID(questID) else WORLDMAP_UPDATE = true; end CACHED_QUESTS[questID] = {i, title, level, texture, questID, questLogIndex, numObjectives, duration, elapsed, completed, questType, link}; if(not QUEST_HEADER_MAP[questID] and (not HeadersCached)) then CacheQuestHeaders() HeadersCached = true end local header = QUEST_HEADER_MAP[questID] or "Misc" tinsert(QUESTS_BY_LOCATION, {distanceSq, header, questID}); tsort(QUESTS_BY_LOCATION, function(a,b) if(a[2] and b[2]) then return a[2] < b[2] else return false end end); tsort(QUESTS_BY_LOCATION, function(a,b) if(a[1] and b[1]) then return a[1] < b[1] else return false end end); end return questID; end end return false; end --[[ ########################################################## SCRIPT HANDLERS ########################################################## ]]-- local BadgeButton_OnEnter = function(self, ...) if(MOD.DOCK_IS_FADED) then return end GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT", 0, ROW_HEIGHT) GameTooltip:ClearLines() GameTooltip:AddLine("Click to track this quest.") GameTooltip:Show() end local RowButton_OnEnter = function(self, ...) if(MOD.DOCK_IS_FADED) then return end GameTooltip:SetOwner(self, "ANCHOR_BOTTOMLEFT", 0, ROW_HEIGHT) GameTooltip:ClearLines() GameTooltip:AddDoubleLine("[Left-Click]", "View the log entry for this quest.", 0, 1, 0, 1, 1, 1) GameTooltip:AddLine(" ") GameTooltip:AddDoubleLine("[Right-Click]", "Remove this quest from the tracker.", 0, 1, 0, 1, 1, 1) GameTooltip:AddLine(" ") GameTooltip:AddDoubleLine("[SHIFT+Click]", "Show this quest on the map.", 0, 1, 0, 1, 1, 1) GameTooltip:Show() end local AnyButton_OnLeave = function(self, ...) GameTooltip:Hide() end local TimerBar_OnUpdate = function(self, elapsed) local statusbar = self.Timer.Bar local timeNow = GetTime(); local timeRemaining = statusbar.duration - (timeNow - statusbar.startTime); statusbar:SetValue(timeRemaining); if(timeRemaining < 0) then -- hold at 0 for a moment if(timeRemaining > -1) then timeRemaining = 0; else self:StopTimer(); end end local r,g,b = MOD:GetTimerTextColor(statusbar.duration, statusbar.duration - timeRemaining) self.Timer.TimeLeft:SetText(GetTimeStringFromSeconds(timeRemaining, nil, true)); self.Timer.TimeLeft:SetTextColor(r,g,b); end local ActiveButton_OnClick = function(self, button) local rowIndex = self:GetID(); if(rowIndex and (rowIndex ~= 0)) then local questID, _, questLogIndex, numObjectives, requiredMoney, completed, startEvent, isAutoComplete, duration, elapsed, questType, isTask, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(rowIndex); if(questID) then local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questLogIndex) local icon = self.Icon:GetTexture() local itemLink, texture, _, showCompleted = GetQuestLogSpecialItemInfo(questLogIndex) SetSuperTrackedQuestID(questID); MOD.Headers["Active"]:Set(title, level, icon, questID, questLogIndex, numObjectives, duration, elapsed, isComplete, itemLink); end end end local ViewButton_OnClick = function(self, button) local questIndex = self:GetID(); if(questIndex and (questIndex ~= 0)) then local questID = select(8, GetQuestLogTitle(questIndex)); if(IsModifiedClick("CHATLINK") and ChatEdit_GetActiveWindow()) then local questLink = GetQuestLink(questIndex); if(questLink) then ChatEdit_InsertLink(questLink); end elseif(questID and IsShiftKeyDown()) then QuestMapFrame_OpenToQuestDetails(questID); elseif(questID and button ~= "RightButton") then CloseDropDownMenus(); if(IsQuestComplete(questID) and GetQuestLogIsAutoComplete(questIndex)) then AutoQuestPopupTracker_RemovePopUp(questID); ShowQuestComplete(questIndex); else QuestLogPopupDetailFrame_Show(questIndex); end elseif(questID) then local superTrackedQuestID = GetSuperTrackedQuestID(); RemoveQuestWatch(questIndex); if(questID == superTrackedQuestID) then QuestSuperTracking_OnQuestUntracked(); end end end end --[[ ########################################################## TRACKER FUNCTIONS ########################################################## ]]-- local StartTimer = function(self, duration, elapsed) local timeNow = GetTime(); local startTime = timeNow - elapsed; local timeRemaining = duration - startTime; self.Timer:SetHeight(INNER_HEIGHT); self.Timer:FadeIn(); self.Timer.Bar.duration = duration or 1; self.Timer.Bar.startTime = startTime; self.Timer.Bar:SetMinMaxValues(0, self.Timer.Bar.duration); self.Timer.Bar:SetValue(timeRemaining); self.Timer.TimeLeft:SetText(GetTimeStringFromSeconds(duration, nil, true)); self.Timer.TimeLeft:SetTextColor(MOD:GetTimerTextColor(duration, duration - timeRemaining)); self:SetScript("OnUpdate", TimerBar_OnUpdate); end local StopTimer = function(self) self.Timer:SetHeight(1); self.Timer:SetAlpha(0); self.Timer.Bar.duration = 1; self.Timer.Bar.startTime = 0; self.Timer.Bar:SetMinMaxValues(0, self.Timer.Bar.duration); self.Timer.Bar:SetValue(0); self.Timer.TimeLeft:SetText(''); self.Timer.TimeLeft:SetTextColor(1,1,1); self:SetScript("OnUpdate", nil); end local GetQuestRow = function(self, index) if(not self.Rows[index]) then local previousFrame = self.Rows[#self.Rows] local index = #self.Rows + 1; local yOffset = -3; local anchorFrame; if(previousFrame and previousFrame.Objectives) then anchorFrame = previousFrame.Objectives; yOffset = -6; else anchorFrame = self.Header; end local row = CreateFrame("Frame", nil, self) row:SetPoint("TOPLEFT", anchorFrame, "BOTTOMLEFT", 0, yOffset); row:SetPoint("TOPRIGHT", anchorFrame, "BOTTOMRIGHT", 0, yOffset); row:SetHeight(QUEST_ROW_HEIGHT); row.Badge = CreateFrame("Frame", nil, row) row.Badge:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0); row.Badge:SetSize(QUEST_ROW_HEIGHT, QUEST_ROW_HEIGHT); --row.Badge:SetStyle("Frame", "Lite") row.Badge.Icon = row.Badge:CreateTexture(nil,"OVERLAY") row.Badge.Icon:SetAllPoints(row.Badge); row.Badge.Icon:SetTexture(MOD.media.incompleteIcon) row.Badge.Icon:SetTexCoord(0.05, 0.95, 0.05, 0.95) row.Badge.Button = CreateFrame("Button", nil, row.Badge) row.Badge.Button:SetAllPoints(row.Badge); row.Badge.Button:SetStyle("LiteButton") row.Badge.Button:SetID(0) row.Badge.Button.Icon = row.Badge.Icon; row.Badge.Button:RegisterForClicks("LeftButtonUp", "RightButtonUp") row.Badge.Button:SetScript("OnClick", ActiveButton_OnClick) row.Badge.Button:SetScript("OnEnter", BadgeButton_OnEnter) row.Badge.Button:SetScript("OnLeave", AnyButton_OnLeave) row.Header = CreateFrame("Frame", nil, row) row.Header:SetPoint("TOPLEFT", row, "TOPLEFT", (QUEST_ROW_HEIGHT + 6), 0); row.Header:SetPoint("TOPRIGHT", row, "TOPRIGHT", -2, 0); row.Header:SetHeight(INNER_HEIGHT); row.Header.Level = row.Header:CreateFontString(nil,"OVERLAY") row.Header.Level:SetFontObject(SVUI_Font_Quest_Number); row.Header.Level:SetJustifyH('RIGHT') row.Header.Level:SetText('') row.Header.Level:SetPoint("TOPRIGHT", row.Header, "TOPRIGHT", -4, 0); row.Header.Level:SetPoint("BOTTOMRIGHT", row.Header, "BOTTOMRIGHT", -4, 0); row.Header.Text = row.Header:CreateFontString(nil,"OVERLAY") row.Header.Text:SetFontObject(SVUI_Font_Quest); row.Header.Text:SetJustifyH('LEFT') row.Header.Text:SetTextColor(1,1,0) row.Header.Text:SetText('') row.Header.Text:SetPoint("TOPLEFT", row.Header, "TOPLEFT", 4, 0); row.Header.Text:SetPoint("BOTTOMRIGHT", row.Header.Level, "BOTTOMLEFT", 0, 0); row.Header.Zone = row:CreateFontString(nil,"OVERLAY") row.Header.Zone:SetAllPoints(row); row.Header.Zone:SetFontObject(SVUI_Font_Quest); row.Header.Zone:SetJustifyH('LEFT') row.Header.Zone:SetTextColor(0.75,0.25,1) row.Header.Zone:SetText("") row.Button = CreateFrame("Button", nil, row.Header) row.Button:SetPoint("TOPLEFT", row, "TOPLEFT", (QUEST_ROW_HEIGHT + 6), 0); row.Button:SetPoint("TOPRIGHT", row, "TOPRIGHT", -2, 0); row.Button:SetHeight(INNER_HEIGHT + 2); row.Button:SetStyle("LiteButton") row.Button:SetID(0) row.Button:RegisterForClicks("LeftButtonUp", "RightButtonUp") row.Button:SetScript("OnClick", ViewButton_OnClick); row.Button:SetScript("OnEnter", RowButton_OnEnter) row.Button:SetScript("OnLeave", AnyButton_OnLeave) row.Timer = CreateFrame("Frame", nil, row) row.Timer:SetPoint("TOPLEFT", row, "BOTTOMLEFT", 0, 4); row.Timer:SetPoint("TOPRIGHT", row, "BOTTOMRIGHT", 0, 4); row.Timer:SetHeight(INNER_HEIGHT); row.Timer.Bar = CreateFrame("StatusBar", nil, row.Timer); row.Timer.Bar:SetPoint("TOPLEFT", row.Timer, "TOPLEFT", 4, -2); row.Timer.Bar:SetPoint("BOTTOMRIGHT", row.Timer, "BOTTOMRIGHT", -4, 2); row.Timer.Bar:SetStatusBarTexture(SV.media.statusbar.default) row.Timer.Bar:SetStatusBarColor(0.5,0,1) --1,0.15,0.08 row.Timer.Bar:SetMinMaxValues(0, 1) row.Timer.Bar:SetValue(0) local bgFrame = CreateFrame("Frame", nil, row.Timer.Bar) bgFrame:InsetPoints(row.Timer.Bar, -2, -2) bgFrame:SetFrameLevel(bgFrame:GetFrameLevel() - 1) bgFrame.bg = bgFrame:CreateTexture(nil, "BACKGROUND") bgFrame.bg:SetAllPoints(bgFrame) bgFrame.bg:SetTexture(SV.media.statusbar.default) bgFrame.bg:SetVertexColor(0,0,0,0.5) local borderB = bgFrame:CreateTexture(nil,"OVERLAY") borderB:SetColorTexture(0,0,0) borderB:SetPoint("BOTTOMLEFT") borderB:SetPoint("BOTTOMRIGHT") borderB:SetHeight(2) local borderT = bgFrame:CreateTexture(nil,"OVERLAY") borderT:SetColorTexture(0,0,0) borderT:SetPoint("TOPLEFT") borderT:SetPoint("TOPRIGHT") borderT:SetHeight(2) local borderL = bgFrame:CreateTexture(nil,"OVERLAY") borderL:SetColorTexture(0,0,0) borderL:SetPoint("TOPLEFT") borderL:SetPoint("BOTTOMLEFT") borderL:SetWidth(2) local borderR = bgFrame:CreateTexture(nil,"OVERLAY") borderR:SetColorTexture(0,0,0) borderR:SetPoint("TOPRIGHT") borderR:SetPoint("BOTTOMRIGHT") borderR:SetWidth(2) row.Timer.TimeLeft = row.Timer.Bar:CreateFontString(nil,"OVERLAY"); row.Timer.TimeLeft:InsetPoints(row.Timer.Bar); row.Timer.TimeLeft:SetFontObject(SVUI_Font_Quest_Number); row.Timer.TimeLeft:SetTextColor(1,1,1) row.Timer.TimeLeft:SetText('') row.Timer:SetHeight(1); row.Timer:SetAlpha(0); row.StartTimer = StartTimer; row.StopTimer = StopTimer; row.Objectives = MOD.NewObjectiveHeader(row); row.Objectives:SetPoint("TOPLEFT", row.Timer, "BOTTOMLEFT", 0, 0); row.Objectives:SetPoint("TOPRIGHT", row.Timer, "BOTTOMRIGHT", 0, 0); row.Objectives:SetHeight(1); row.RowID = 0; self.Rows[index] = row; return row; end return self.Rows[index]; end local SetQuestRow = function(self, index, watchIndex, title, level, icon, questID, questLogIndex, subCount, duration, elapsed, completed, questType) if(not watchIndex) then return index,0 end index = index or #self.Rows index = index + 1; local fill_height = 0; local iscomplete = true; local objective_rows = 0; local row = self:Get(index); if(not icon) then icon = completed and MOD.media.completeIcon or MOD.media.incompleteIcon end local color = DEFAULT_COLOR if(level and type(level) == 'number') then color = GetQuestDifficultyColor(level); end row.Header:SetAlpha(1); row.Header.Zone:SetText('') row.Header.Level:SetTextColor(color.r, color.g, color.b) row.Header.Level:SetText(level) row.Header.Text:SetTextColor(color.r, color.g, color.b) row.Header.Text:SetText(title) row.Badge.Icon:SetTexture(icon); row.Badge.Button:Enable(); row.Badge.Button:SetID(watchIndex); row.Badge:SetAlpha(1); row.Button:SetAlpha(1); row.Button:Enable(); row.Button:SetID(questLogIndex); row:SetHeight(QUEST_ROW_HEIGHT); row:FadeIn(); local objective_block = row.Objectives; objective_block:Reset(); for i = 1, subCount do local description, category, objective_completed = GetQuestLogLeaderBoard(i, questLogIndex); -- local description, category, objective_completed = GetQuestObjectiveInfo(questID, i, false); if not objective_completed then iscomplete = false end if(description) then fill_height = fill_height + (INNER_HEIGHT + 4); objective_rows = objective_block:SetInfo(objective_rows, description, objective_completed); end end if(duration) then if(elapsed and elapsed < duration) then fill_height = fill_height + (INNER_HEIGHT + 4); row:StartTimer(duration, elapsed) end end if(objective_rows > 0) then objective_block:SetHeight(fill_height); objective_block:FadeIn(); end fill_height = fill_height + (QUEST_ROW_HEIGHT + 6); return index, fill_height; end local SetZoneHeader = function(self, index, zoneName) index = index + 1; local row = self:Get(index); row.Header.Level:SetText(''); row.Header.Text:SetText(''); row.Badge.Icon:SetTexture(""); row.Badge.Button:SetID(0); row.Badge:SetAlpha(0); row.Button:SetID(0); row.Button:Disable(); row.Button:SetAlpha(0); row.Badge.Button:Disable(); row.Header.Zone:SetTextColor(1,0.31,0.1) row.Header.Zone:SetText(zoneName); row:SetHeight(ROW_HEIGHT); row:SetAlpha(1); local objective_block = row.Objectives; objective_block:Reset(); return index, zoneName; end local RefreshQuests = function(self, event, ...) local rows = 0; local fill_height = 0; local zone = 0; for i = 1, #QUESTS_BY_LOCATION do local zoneName = QUESTS_BY_LOCATION[i][2] local questID = QUESTS_BY_LOCATION[i][3] local quest = CACHED_QUESTS[questID] if(quest) then if(quest[2] and quest[2] ~= '') then local add_height = 0; if(zone ~= zoneName) then rows, zone = self:SetZone(rows, zoneName); fill_height = fill_height + QUEST_ROW_HEIGHT; end rows, add_height = self:Set(rows, unpack(quest)) fill_height = fill_height + add_height; if(quest[12]) then ItemBar:SetQuestItem(quest[12], quest[4], quest[10]) end end end end if(rows == 0 or (fill_height <= 1)) then self:SetHeight(1); self:SetAlpha(0); else self:SetHeight(fill_height + 2); self:FadeIn(); end ItemBar:Update() end local AddOneQuest = function(self, questID) local rows = 0; if(questID) then local fill_height = self:GetHeight(); local quest = CACHED_QUESTS[questID]; if(quest[2] and quest[2] ~= '') then local add_height = 0; rows, add_height = self:Set(rows, unpack(quest)) fill_height = fill_height + add_height; if(quest[12]) then ItemBar:SetQuestItem(quest[12], quest[4], quest[10]) end end self:SetHeight(fill_height + 2); end ItemBar:Update() end local ResetQuestBlock = function(self) for x = 1, #self.Rows do local row = self.Rows[x] if(row) then row.Header.Text:SetText(''); row.Header:SetAlpha(0); row.Header.Zone:SetText(''); row.Button:SetID(0); row.Button:Disable(); row.Badge.Button:SetID(0); row.Badge.Icon:SetTexture(""); row.Badge:SetAlpha(0); row.Badge.Button:Disable(); row:SetHeight(1); row:SetAlpha(0); row.Objectives:Reset(); end end UpdateCachedQuests(); end local LiteResetQuestBlock = function(self) for x = 1, #self.Rows do local row = self.Rows[x] if(row) then row.Objectives:Reset(true); end end end local _hook_WorldMapFrameOnHide = function() if(not WORLDMAP_UPDATE) then return end MOD.Headers["Quests"]:Reset() MOD.Headers["Quests"]:Refresh() MOD:UpdateDimensions(); end --[[ ########################################################## CORE FUNCTIONS ########################################################## ]]-- function MOD:UpdateObjectives(event, ...) if(event == "ZONE_CHANGED_NEW_AREA") then if(not WorldMapFrame:IsShown() and GetCVarBool("questPOI")) then SetMapToCurrentZone(); CURRENT_MAP_ID = GetCurrentMapAreaID(); UpdateCachedDistance(); self.Headers["Quests"]:LiteReset() self.Headers["Quests"]:Refresh(event, ...) end elseif(event == "ZONE_CHANGED") then local inMicroDungeon = IsPlayerInMicroDungeon(); if(inMicroDungeon ~= self.inMicroDungeon) then if(not WorldMapFrame:IsShown() and GetCVarBool("questPOI")) then SetMapToCurrentZone(); CURRENT_MAP_ID = GetCurrentMapAreaID(); UpdateCachedDistance(); self.Headers["Quests"]:LiteReset() self.Headers["Quests"]:Refresh(event, ...) end self.inMicroDungeon = inMicroDungeon; end else if(event == "QUEST_ACCEPTED" or event == "QUEST_WATCH_LIST_CHANGED") then local questLogIndex, questID, isTracked; if(event == "QUEST_ACCEPTED") then questLogIndex, questID = ...; if(AUTO_QUEST_WATCH == "1") then AddQuestWatch(questLogIndex); QuestSuperTracking_OnQuestTracked(questID); end local addedQuest = AddCachedQuest(questLogIndex) if(addedQuest) then self.Headers["Quests"]:AddQuest(addedQuest) self:UpdateDimensions(); end elseif(event == "QUEST_WATCH_LIST_CHANGED") then questID, isTracked = ...; if(questID) then local questLogIndex = GetQuestLogIndexByID(questID) if(isTracked) then local addedQuest = AddCachedQuest(questLogIndex) self.Headers["Quests"]:AddQuest(addedQuest) else CACHED_QUESTS[questID] = nil; self:CheckActiveQuest(questID); self.Headers["Quests"]:Reset(); self.Headers["Quests"]:Refresh(event, ...) end self:UpdateDimensions(); end end elseif(event == "QUEST_TURNED_IN") then local questID, XP, Money = ... if(IsQuestTask(questID)) then self:CacheBonusObjective(event, ...); end self:CheckActiveQuest(questID); if(CACHED_QUESTS[questID]) then CACHED_QUESTS[questID] = nil; ItemBar:Reset(); self.Headers["Quests"]:Reset(); self.Headers["Quests"]:Refresh(event, ...); self:UpdateDimensions(); end elseif(event == "QUEST_LOG_UPDATE") then self.Headers["Quests"]:Reset(); self.Headers["Quests"]:Refresh(event, ...) self:UpdateBonusObjective(event, ...); self:UpdateDimensions(); else self:UpdateBonusObjective(event, ...) end end end local function ReAnchorItemBar() if(InCombatLockdown()) then return end local dockletLocation = MOD.Docklet.Parent.Bar.Data.Location; local isHorizontal = (SV.db.QuestTracker.itemBarDirection == 'HORIZONTAL'); local anchor1 = isHorizontal and "LEFT" or "RIGHT"; local anchor2 = "LEFT"; local xOff = isHorizontal and 0 or -4; local yOff = isHorizontal and 4 or 0; if(dockletLocation:find('Left')) then anchor1 = isHorizontal and "RIGHT" or "LEFT"; anchor2 = "RIGHT"; xOff = isHorizontal and 0 or 4; end local prefix1 = isHorizontal and "BOTTOM" or "TOP"; local prefix2 = "TOP"; if(dockletLocation:find('Top')) then prefix1 = isHorizontal and "TOP" or "BOTTOM"; prefix2 = "BOTTOM"; yOff = -4; end anchor1 = prefix1 .. anchor1; anchor2 = prefix2 .. anchor2; local parentWindow = MOD.Docklet.Parent.Window; ItemBar:ClearAllPoints(); ItemBar:SetSecurePoint(anchor1, parentWindow, anchor2, xOff, yOff); if(isHorizontal) then ItemBar:SetWidth(parentWindow:GetWidth()); ItemBar:SetHeight(32); else ItemBar:SetWidth(32); ItemBar:SetHeight(parentWindow:GetHeight()); end end local function UpdateQuestLocals(...) ROW_WIDTH, ROW_HEIGHT, INNER_HEIGHT, LARGE_ROW_HEIGHT, LARGE_INNER_HEIGHT = ...; QUEST_ROW_HEIGHT = ROW_HEIGHT + 2; ITEM_BUTTON_SIZE = SV.db.QuestTracker.itemButtonSize; ITEMS_PER_ROW = SV.db.QuestTracker.itemButtonsPerRow; ReAnchorItemBar(); ItemBar:Update() end local function PostMoveCallback(buttonName) if(not buttonName or (buttonName ~= MOD.Docklet.Button:GetName())) then return end if(ItemBar.Grip and (not ItemBar.Grip:HasMoved())) then ReAnchorItemBar() end MOD.QuestItemTimer = SV.Timers:ExecuteTimer(ShowItemBarButtons, 1.2); ShowItemBarButtons(); end local function PostShowCallback(location, windowName) if(not location or (location ~= MOD.Docklet.Parent.Bar.Data.Location)) then return end if(not windowName or (windowName ~= MOD.Docklet:GetName())) then return end MOD.QuestItemTimer = SV.Timers:ExecuteTimer(ShowItemBarButtons, 1.2); ShowItemBarButtons(); end local function PostHideCallback(location, windowName) if(not location or (location ~= MOD.Docklet.Parent.Bar.Data.Location)) then return end if(not windowName or (windowName ~= MOD.Docklet:GetName())) then return end if(MOD.QuestItemTimer) then SV.Timers:RemoveTimer(MOD.QuestItemTimer) MOD.QuestItemTimer = nil end HideItemBarButtons(); end function MOD:InitializeQuests() ReAnchorItemBar() SV:NewAnchor(ItemBar, L["Quest Items"]); ITEM_BUTTON_SIZE = SV.db.QuestTracker.itemButtonSize; ITEMS_PER_ROW = SV.db.QuestTracker.itemButtonsPerRow; for i = 1, 5 do ItemBar.Buttons[i] = CreateQuestItemButton(i) end SV.Events:On("DOCKLET_MOVED", PostMoveCallback, true); SV.Events:On("DOCKLET_SHOWN", PostShowCallback, true); SV.Events:On("DOCKLET_HIDDEN", PostHideCallback, true); local scrollChild = self.Docklet.ScrollFrame.ScrollChild; local quests = CreateFrame("Frame", nil, scrollChild) quests:SetWidth(ROW_WIDTH); quests:SetHeight(ROW_HEIGHT); quests:SetPoint("TOPLEFT", self.Headers["Bonus"], "BOTTOMLEFT", 0, -4); --quests:SetStyle() quests.Header = CreateFrame("Frame", nil, quests) quests.Header:SetPoint("TOPLEFT", quests, "TOPLEFT", 2, -2); quests.Header:SetPoint("TOPRIGHT", quests, "TOPRIGHT", -2, -2); quests.Header:SetHeight(INNER_HEIGHT); quests.Header.Text = quests.Header:CreateFontString(nil,"OVERLAY") quests.Header.Text:SetPoint("TOPLEFT", quests.Header, "TOPLEFT", 2, 0); quests.Header.Text:SetPoint("BOTTOMLEFT", quests.Header, "BOTTOMLEFT", 2, 0); quests.Header.Text:SetFontObject(SVUI_Font_Quest_Header); quests.Header.Text:SetJustifyH('LEFT') quests.Header.Text:SetTextColor(0.28,0.75,1) quests.Header.Text:SetText(TRACKER_HEADER_QUESTS) quests.Header.Divider = quests.Header:CreateTexture(nil, 'BACKGROUND'); quests.Header.Divider:SetPoint("TOPLEFT", quests.Header.Text, "TOPRIGHT", -10, 0); quests.Header.Divider:SetPoint("BOTTOMRIGHT", quests.Header, "BOTTOMRIGHT", 0, 0); quests.Header.Divider:SetTexture([[Interface\AddOns\SVUI_!Core\assets\textures\DROPDOWN-DIVIDER]]); quests.Rows = {}; quests.Get = GetQuestRow; quests.Set = SetQuestRow; quests.SetZone = SetZoneHeader; quests.Refresh = RefreshQuests; quests.AddQuest = AddOneQuest; quests.Reset = ResetQuestBlock; quests.LiteReset = LiteResetQuestBlock; self.Headers["Quests"] = quests; self:RegisterEvent("QUEST_LOG_UPDATE", self.UpdateObjectives); self:RegisterEvent("QUEST_WATCH_LIST_CHANGED", self.UpdateObjectives); self:RegisterEvent("QUEST_ACCEPTED", self.UpdateObjectives); self:RegisterEvent("QUEST_POI_UPDATE", self.UpdateObjectives); self:RegisterEvent("QUEST_TURNED_IN", self.UpdateObjectives); self:RegisterEvent("ZONE_CHANGED_NEW_AREA", self.UpdateObjectives); self:RegisterEvent("ZONE_CHANGED", self.UpdateObjectives); --ItemBar:Reset(); CacheQuestHeaders() self.Headers["Quests"]:Reset() self.Headers["Quests"]:Refresh() WorldMapFrame:HookScript("OnHide", _hook_WorldMapFrameOnHide) ItemBar:Show() SV.Events:On("QUEST_LAYOUT_UPDATED", ReAnchorItemBar, true); SV.Events:On("QUEST_UPVALUES_UPDATED", UpdateQuestLocals, true); end