Mercurial > wow > worldplan
view WorldPlan.lua @ 3:c006ce87a147
prototype structure
author | Nenue |
---|---|
date | Sat, 15 Oct 2016 09:54:56 -0400 |
parents | 232617b8bcd5 |
children | 34d9fbf7af20 |
line wrap: on
line source
-- Veneer -- WorldPlan.lua -- Created: 8/16/2016 8:19 AM -- %file-revision% --[[ Summary: Adds reward icons to the world quest POI markers, and adds said markers to the continent map. Issues: Dalaran quests aren't visible until that map has been specifically viewed by the player. --]] WorldPlanCore = {} WorldPlanPOIMixin = {} WorldPlanFilterPinMixin = {} local WorldPlanFlightMapMixin = setmetatable({}, {__tostring = function() return 'FlightMapHandler' end}) local WorldQuests = setmetatable({ QuestsByID = {}, freePins = {} }, {__tostring = function() return 'QuestHandler' end}) local FilterBar = setmetatable({ SummaryHeaders = {} }, {__tostring = function() return 'FilterBar' end}) local WorldPlan = WorldPlanCore local QuestPOI = WorldPlanPOIMixin local FilterPin = WorldPlanFilterPinMixin local WP_VERSION = "1.0" local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end local wipe, tremove, tinsert, pairs, floor, tContains = table.wipe, table.remove, table.insert, pairs, floor, tContains local TQ_GetQuestInfoByQuestID = C_TaskQuest.GetQuestInfoByQuestID -- Return the name of a quest with a given ID local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented local TQ_GetQuestTimeLeftMinutes = C_TaskQuest.GetQuestTimeLeftMinutes local TQ_RequestPreloadRewardData = C_TaskQuest.RequestPreloadRewardData local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation local TQ_IsActive = C_TaskQuest.IsActive local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS local WorldMap_DoesWorldQuestInfoPassFilters = WorldMap_DoesWorldQuestInfoPassFilters local QuestMapFrame_IsQuestWorldQuest = QuestMapFrame_IsQuestWorldQuest local GameTooltip = GameTooltip local GetItemIcon = GetItemIcon local GetMapInfo, QuestPOIGetIconInfo = GetMapInfo, QuestPOIGetIconInfo local GetQuestTagInfo, HaveQuestData = GetQuestTagInfo, HaveQuestData local GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney = GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney local GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString = GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString local GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID = GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end local iprint = DEVIAN_WORKSPACE and function(...) _G.print('ItemScan', ...) end or function() end local SearchFaction local PinBaseIndex = 1600 local ShowAllProfessionQuests = false local DisplayContinentSummary = true local DisplayContinentPins = true local NotifyWhenNewQuests = true local PinRewardFilter, PinTagFilter local BROKEN_ISLES_ID, DALARAN_ID, AZSUNA_ID, VALSHARAH_ID, HIGHMOUNTAIN_ID, STORMHEIM_ID, SURAMAR_ID, EOA_ID = 1007, 1014, 1015,1018, 1024, 1017, 1033, 1096 -- maps where we do our own anchors local CONTINENT_MAPS = { [BROKEN_ISLES_ID] = BROKEN_ISLES_ID, } local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70', [AZSUNA_ID] = 'Azsuna', [VALSHARAH_ID] = "Val'sharah", [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim', [SURAMAR_ID] = 'Suramar', [EOA_ID] = 'EyeOfAszhara', } -- default color templates local ARTIFACT_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_ARTIFACT] local MONEY_COLOR = {hex ='|cFFFFFF00', r=1, g=1, b=0} local COMMON_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_COMMON] -- operating flags local superTrackedID local currentMapName local hasNewQuestPins local isContinentMap local ICON_UNKNOWN = "Interface\\ICONS\\inv_misc_questionmark" local ICON_MONEY = "Interface\\Buttons\\UI-GroupLoot-Coin-Up" local POI_BORDER_MASK = "Interface\\Minimap\\UI-Minimap-Background" local POI_BORDER_FILL = "Interface\\BUTTONS\\YELLOWORANGE64" local POI_BORDER_BLUE = "Interface\\BUTTONS\\GRADBLUE" local POI_BORDER_RED = "Interface\\BUTTONS\\RedGrad64" local POI_BORDER_YELLOW = "Interface\\BUTTONS\\YELLOWORANGE64" local POI_BORDER_GREEN = "Interface\\BUTTONS\\GREENGRAD64" local REWARD_CASH = 1001 local REWARD_ARTIFACT_POWER = 1002 local REWARD_GEAR = 1003 local REWARD_CURRENCY = 1004 local REWARD_ITEM = 1005 local REWARD_REAGENT = 1006 local POI_DEFAULT_TYPE = { a = 1, r = 1, g = 1, b = 1, x = 0, y = 0, desaturated = true, mask = POI_BORDER_MASK, texture = POI_BORDER_FILL, continent = { PinSize = 18, Border = 3, TrackingBorder = 2, TagSize = 6, TimeleftStage = 3, }, zone = { PinSize = 22, Border = 3, TrackingBorder = 2, TagSize = 12, TimeleftStage = 3, }, minimized = { PinSize = 4, Border = 1, TrackingBorder = 2, NoIcon = true, TimeleftStage = 1, } } local POI_REWARD_TYPE = setmetatable({}, { __newindex = function(t, k, v) if type(v) == 'table' then setmetatable(v, {__index = POI_DEFAULT_TYPE}) end rawset(t,k,v) end }) local POI_FILTER_STYLE = setmetatable({ continentBorder = 2, zoneBorder = 2, }, {__index = POI_DEFAULT_TYPE}) local LE_QUEST_TAG_TYPE_PVP = LE_QUEST_TAG_TYPE_PVP local LE_QUEST_TAG_TYPE_PET_BATTLE = LE_QUEST_TAG_TYPE_PET_BATTLE local LE_QUEST_TAG_TYPE_DUNGEON = LE_QUEST_TAG_TYPE_DUNGEON local LE_QUEST_TAG_TYPE_PROFESSION = LE_QUEST_TAG_TYPE_PROFESSION local LE_QUEST_TAG_TYPE_NORMAL = LE_QUEST_TAG_TYPE_NORMAL -- Pin color/display variables POI_REWARD_TYPE[REWARD_ITEM] = { r = 1, g = 1, b = 1, } POI_REWARD_TYPE[REWARD_REAGENT] = { r = 0, g = 1, b = 1, } POI_REWARD_TYPE[REWARD_ARTIFACT_POWER] = { r = 1, g = .25, b = .5, showNumber = true, } POI_REWARD_TYPE[REWARD_GEAR] = { r = .1, g = .2, b = 1, } POI_REWARD_TYPE[REWARD_CURRENCY] = { r = 1, g = 1, b = 0, } POI_REWARD_TYPE[REWARD_CASH] = { r = 0, g = 0, b = 0, --x = 0, y = -1, --mask = ICON_MONEY, --continentBorder = 1, --zoneBorder = 1, } -- Summary header structure local POI_FILTER_OPTIONS = { { label = 'Filters', texture = "Interface\\WorldMap\\WorldMap-Icon" }, { filterKey= 'rewardType', filterValue = REWARD_ARTIFACT_POWER, label = 'Artifact Power', texture = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" }, { filterKey= 'rewardType', filterValue = REWARD_CURRENCY,label = 'Currency', texture = "Interface\\ICONS\\inv_misc_elvencoins" }, { filterKey= 'rewardType', filterValue = REWARD_ITEM, label = 'Item', texture = "Interface\\ICONS\\inv_crate_01" }, { filterKey= 'rewardType', filterValue = REWARD_GEAR, label = 'Equipment', texture = "Interface\\ICONS\\garrison_bluearmorupgrade" }, { filterKey= 'rewardType', filterValue = REWARD_REAGENT, label = 'Reagents', texture = 1417744 }, { filterKey= 'rewardType', filterValue = REWARD_CASH, label = 'Reagents', texture = ICON_MONEY }, { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PVP, label = 'PvP', texture = "Interface\\Icons\\Ability_PVP_GladiatorMedallion", spacing = 10 }, { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PET_BATTLE, label = 'Pet Battle', texture = "Interface\\Icons\\PetJournalPortrait", }, { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", }, } local PIN_TIME_CONTEXT = { {max = 60, r=1, g=0.25, b =0, format = function (minutes) return '|cFFFF4400'.. minutes .. 'm' end, continentAlpha = 1, swipeTime = 1440, }, {max = 240, r=1, g=.5, b=0, format = function(minutes) return '|cFFFF4400'.. floor(minutes/60) .. 'h' end, continentAlpha = 1, swipeTime = 1440, }, {max = 1440, r=1, g=1, b=0, format = function(minutes) return '|cFFFFFF00'.. floor(minutes/60) .. 'h' end, continentAlpha = .55, swipeTime = 1440 }, {max = 10081, r=0, g=1, b=0, continentAlpha = .3, }, -- 7 days + 1 minute } local numPins = 0 local QuestsByZone = {} local QuestsByFaction = {} local QuestsByReward = {} local QuestsByTag = {} local QuestsByID = {} local QuestPositions = {} local FilterInclusions = {rewardType = {}, worldQuestType = {}} local FilterExclusions = {rewardType = {}, worldQuestType = {} } local NotificationTypes = {} local ZoneInfo = {} local SummaryHeaders = {} local FreePins = {} local NumPinFrames = 1 local hasPendingQuestData local notifyPlayed local scanner, wmtt, WorldMapPOIFrame local tasksQueue = {} local function OnNext (func) if #tasksQueue == 0 then _G.WorldPlan:SetScript('OnUpdate', function() local func = tremove(tasksQueue, 1) if func then func() end if #tasksQueue == 0 then _G.WorldPlan:SetScript('OnUpdate', nil) end end) end tinsert(tasksQueue, func) end -- combines templates local function DoMixins(frame,...) for i = 1, select('#', ...) do for k,v in pairs(select(i,...)) do frame[k] = v end end return frame end -- use tooltip object to extract item details local ParseItemReward = function(questID) local rewardType = REWARD_ITEM local name, icon, quantity, quality, _, itemID = GetQuestLogRewardInfo(1, questID) if not itemID then return REWARD_ITEM end scanner:SetOwner(WorldPlan, "ANCHOR_NONE") scanner:SetItemByID(itemID) local ttl1 = _G['WorldPlanTooltipTextLeft1'] local ttl2 = _G['WorldPlanTooltipTextLeft2'] local ttl3 = _G['WorldPlanTooltipTextLeft3'] local ttl4 = _G['WorldPlanTooltipTextLeft4'] if ttl2 then local text = ttl2:GetText() -- Artifact Power if text and text:match("|cFFE6CC80") then --print('AP token!', text) local power if ttl4 then local text = ttl4:GetText() --print('tip line 4', text) if text then power = text:gsub("%p", ""):match("%d+") power = tonumber(power) end end rewardType = REWARD_ARTIFACT_POWER icon = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" quantity = power elseif text and text:match("Item Level") then --print('equipment!', text) rewardType = REWARD_GEAR quantity = text:match("Item Level ([%d\+]+)") end end if ttl3 then local text = ttl3:GetText() -- Crafting Reagent if text and text:match("Crafting Reagent") then --print('reagent', text) rewardType = REWARD_REAGENT end end iprint(' item:', name, rewardType, icon, quantity) return rewardType, icon, quantity, name, itemID end -- update a masked texture without messing up its blending mask local SetMaskedTexture = function(region, file, mask) mask = mask or POI_BORDER_MASK region:SetMask(nil) region:SetTexture(file) region:SetMask(mask) end -- tracking menu toggler local DropDown_OnClick = function(self) local key = self.value if key then if WorldPlanData[key] then WorldPlanData[key] = nil else WorldPlanData[key] = true end end end function WorldPlan:print(...) local msg for i = 1, select('#', ...) do msg = (msg and (msg .. ' ') or '') .. tostring(select(i, ...)) end DEFAULT_CHAT_FRAME:AddMessage("|cFF0088FFWorldPlan|r: " .. msg) end function WorldPlan:OnLoad () WorldPlan = self scanner = _G.WorldPlanTooltip wmtt = _G.WorldMapTooltip WorldMapPOIFrame = _G.WorldMapPOIFrame WorldPlan:print('v'..WP_VERSION) self:RegisterEvent("QUESTLINE_UPDATE") self:RegisterEvent("QUEST_LOG_UPDATE") self:RegisterEvent("WORLD_MAP_UPDATE") self:RegisterEvent("WORLD_QUEST_COMPLETED_BY_SPELL") self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED") self:RegisterEvent("SKILL_LINES_CHANGED") self:RegisterEvent("ARTIFACT_XP_UPDATE") self:RegisterEvent("ADDON_LOADED") WorldPlan.modules = { WorldQuests, FilterBar, WorldPlanFlightMapMixin, } hooksecurefunc("UIDropDownMenu_Initialize", self.OnDropDownInitialize) end function WorldPlan:OnEvent (event, ...) print() print(event, ...) if event == 'ADDON_LOADED' then local addon = ... if addon == "Blizzard_FlightMap" then print('do mixin junk') self.OnFlightMapLoaded() end if IsLoggedIn() and not self.initialized then self:Setup() end elseif event == 'WORLD_MAP_UPDATE' then self:RefreshAll() end for i, module in ipairs(self.modules) do if module.OnEvent then print('forwarding to', tostring(module)) module:OnEvent(event, ...) end end end function WorldPlan:Setup () if not WorldPlanData then WorldPlanData = {key = 0 } end WorldPlanData.key = (WorldPlanData.key or 0) + 1 self.db = WorldPlanData for i, module in ipairs(self.modules) do if module.Setup then module:Setup() end if not module.RegisterEvent then module.RegisterEvent = self.RegisterEvent end end self.initialized = true end function WorldPlan:RefreshAll (forced) for i, module in ipairs(self.modules) do if module.Refresh then module:Refresh() end end end function WorldPlan:UpdateAnchors () for i, module in ipairs(self.modules) do if module.UpdateAnchors then module:UpdateAnchors() end end end -- insert visual options into the tracking button menu WorldPlan.OnDropDownInitialize = function (self, callback, dropType) if self ~= WorldMapFrameDropDown then return end local info = UIDropDownMenu_CreateInfo() info.text = "" info.isTitle = true UIDropDownMenu_AddButton(info) info.text = "|cFF00AAFFWorldPlan|r" info.isTitle = true UIDropDownMenu_AddButton(info) info.isTitle = nil info.disabled = nil info.keepShownOnClick = true info.tooltipOnButton = 1 info.text = "Hidden World Quests" info.isNotRadio = true info.value = "ShowAllProfessionQuests" info.checked = ShowAllProfessionQuests info.tooltipTitle = "Hidden Quests" info.tooltipText = "Display work order and profession-related quests that are skipped by the default UI." info.func = DropDown_OnClick UIDropDownMenu_AddButton(info) info.text = "Show Continent Pins" info.isNotRadio = true info.value = "DisplayContinentPins" info.checked = DisplayContinentPins info.tooltipTitle = "Continent Pins" info.tooltipText = "Display quest pins on the continent map (may get cramped)." info.func = DropDown_OnClick UIDropDownMenu_AddButton(info) info.text = "Show Summary" info.isNotRadio = true info.value = "DisplayContinentSummary" info.tooltipTitle = "Summary Bar" info.tooltipText = "Display a summary of active world quests. Note: requires directly viewing Broken Isle and Dalaran maps to gain complete info." info.checked = DisplayContinentSummary info.func = DropDown_OnClick UIDropDownMenu_AddButton(info) end function WorldQuests:Setup() -- refresh positions any time blizzard does so (i.e. mousewheel zoom) hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function() self:Refresh() end) -- hide the original world quest POIs hooksecurefunc("WorldMap_UpdateQuestBonusObjectives", function() for i = 1, NUM_WORLDMAP_TASK_POIS do local button = _G['WorldMapFrameTaskPOI'..i] if button and button.worldQuest then button:Hide() end end end) end function WorldQuests:OnEvent (event, ...) print('|cFFFFFF00'..tostring(self)..':OnEvent()'..event..'|r', ...) if event == 'QUEST_LOG_UPDATE' then local questID, added = ... if questID and added then self:GetPinByQuestID(questID) else self:GetPinsForMap() end elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then local questID = ... if questID and QuestsByID[questID] then self:ReleasePin(QuestsByID[questID]) end elseif event == 'SKILL_LINES_CHANGED' then self:Refresh() end end function WorldQuests:AcquirePin (questID, pinTable) local pin = QuestsByID[questID] local isNew = false if not pin then isNew = true local numFree = #self.freePins if numFree >= 1 then pin = tremove(self.freePins, numFree) --print('|cFF00FF00Re-using', pin:GetName()) else local name = 'WorldPlanQuestMarker' .. NumPinFrames --print('|cFF00FF00Creating', name) pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin') pin:SetFrameStrata('HIGH') pin:SetScript('OnEnter', function(self) TaskPOI_OnEnter(self) end) pin:SetScript('OnLeave', function(self) TaskPOI_OnLeave(self) end) pin:SetScript('OnMouseDown', TaskPOI_OnClick) NumPinFrames = NumPinFrames + 1 --pin.iconBorder:SetVertexColor(0,0,0,1) end QuestsByID[questID] = pin pin.isNew = true pin.currentWidth = nil -- used by TaskPOI_x scripts pin.questID = questID pin.worldQuest = true else --print('|cFF00FF00Using', pin:GetName()) end -- set display flags accordingly if pinTable then for k,v in pairs(pinTable) do pin[k] = v end end pin.throttle = nil pin.timeThreschold = nil return pin, isNew end -- remove from index and add it to the recycling heap function WorldQuests:ReleasePin (pin) local id = pin.questId if id then QuestsByID[id] = nil for i, zone in pairs(QuestsByZone) do print('-', i, zone[i]) zone[id] = nil end end if pin.factionID then QuestsByFaction[pin.factionID][id] = nil end pin:Hide() pin:ClearAllPoints() tinsert(self.freePins, pin) print('|cFFFF4400Clearing out', pin:GetName(),id) end -- create of update quest pins for a map and its underlying zones function WorldQuests:GetPinsForMap (mapID) mapID = mapID or GetCurrentMapAreaID() superTrackedID = GetSuperTrackedQuestID() if not mapID then -- info not available yet return end if mapID == BROKEN_ISLES_ID then hasPendingQuestData = nil print('|cFF00FFFFRefreshQuestsForMap|r', mapID, GetMapNameByID(mapID), superTrackedID) self.fullSearch = true for i = 1, MC_GetNumZones(mapID) do local submapID, name, depth = MC_GetZoneInfo(mapID, i) self:GetPinsForMap(submapID) end self.fullSearch = nil elseif QuestsByZone[mapID] then local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID) local quest = QuestsByZone[mapID] local numQuests = 0 if taskInfo and #taskInfo >= 1 then print('|cFF00FFFFRefreshQuestsForMap|r', mapID, GetMapNameByID(mapID), #taskInfo) wipe(QuestsByZone[mapID]) ZoneInfo[mapID] = taskInfo for taskID, info in pairs(taskInfo) do local questID = info.questId info.mapID = mapID QuestsByZone[mapID][questID] = self:GetPinByQuestID(questID, info) numQuests = numQuests + 1 end end end if hasNewQuestPins and not self.fullSearch then print('|cFF00FF00NEW PINS DO ANCHOR THINGS') self:Refresh () hasNewQuestPins = nil end end -- create or update the pin using the given questID and C_TaskQuest results function WorldQuests:GetPinByQuestID (questID, taskInfo) if (QuestMapFrame_IsQuestWorldQuest (questID)) then local questTitle, rewardIcon, rewardName, rewardCount, rewardStyle, rewardType, itemID, quantity, quality, _ local pin = self:AcquirePin(questID, taskInfo) if pin.isNew then if not hasNewQuestPins then print('triggering new quest pins event') end hasNewQuestPins = true end if not HaveQuestData(questID) then print('|cFFFF4400Retrieval failed.') TQ_RequestPreloadRewardData(questID) hasPendingQuestData = true else print('|cFF00FF88HaveQuestData|r') pin.mapID = pin.mapID or C_TaskQuest.GetQuestZoneID(questID) -- set reward category local numRewards = GetNumQuestLogRewards(questID) local numCurrency = GetNumQuestLogRewardCurrencies(questID) local money = GetQuestLogRewardMoney(questID) if numRewards >= 1 then rewardType, rewardIcon, rewardCount, rewardName, itemID = ParseItemReward(questID) elseif numCurrency >= 1 then rewardName, rewardIcon, rewardCount = GetQuestLogRewardCurrencyInfo(1, questID) rewardType = REWARD_CURRENCY elseif money >= 1 then rewardIcon = ICON_MONEY rewardName = GetMoneyString(money) rewardType = REWARD_CASH end rewardStyle = POI_REWARD_TYPE[rewardType] or POI_DEFAULT_TYPE pin.itemNumber = rewardCount or pin.itemNumber pin.rewardType = rewardType or REWARD_ITEM pin.style = rewardStyle QuestsByID[questID] = pin -- title, faction, capped state local questTitle, factionID, capped = TQ_GetQuestInfoByQuestID(questID) if factionID then QuestsByFaction[factionID] = QuestsByFaction[factionID] or {} QuestsByFaction[factionID][questID] = pin end pin.title = questTitle or "|cFFFF0000Retrieving..." pin.factionID = factionID pin.capped = capped -- set tag details local tagID, tagName, worldQuestType, rarity, isElite, tradeskillLineIndex = GetQuestTagInfo(questID); local tagAtlas if worldQuestType == LE_QUEST_TAG_TYPE_PET_BATTLE then tagAtlas = "worldquest-icon-petbattle" elseif worldQuestType == LE_QUEST_TAG_TYPE_PVP then tagAtlas = "worldquest-icon-pvp-ffa" elseif worldQuestType == LE_QUEST_TAG_TYPE_PROFESSION then local id = tradeskillLineIndex and select(7, GetProfessionInfo(tradeskillLineIndex)) if id then tagAtlas = WORLD_QUEST_ICONS_BY_PROFESSION[id] end elseif worldQuestType == LE_QUEST_TAG_TYPE_DUNGEON then tagAtlas = "worldquest-icon-dungeon" end pin.tagID = tagID pin.tagName = tagName pin.worldQuestType = worldQuestType pin.isElite = isElite pin.tradeskillLineIndex = tradeskillLineIndex pin.rarity = rarity pin.tagAtlas = tagAtlas end -- flag unresolved info if not (rewardIcon and rewardName) then if not pin.isPending then pin.isPending = true TQ_RequestPreloadRewardData (questID) pin.rewardType = pin.rewardType or REWARD_ITEM pin.style = pin.style or POI_REWARD_TYPE[REWARD_ITEM] if not hasPendingQuestData then hasPendingQuestData = true PlaySoundKitID(229) end --WorldPlan:print('|cFFFFFF00'..tostring(pin.title)..'|r waiting on texture info') end else if (rewardIcon and rewardName) then --WorldPlan:print('|cFF00FF00'..tostring(pin.title)..'|r has info', rewardIcon, rewardName) pin.hasUpdate = true end pin.isPending = nil end pin.itemTexture = rewardIcon or pin.itemTexture pin.itemName = rewardName or pin.itemName qprint(' |cFF00FFFF'..questID..'|r:->', (HaveQuestData(questID) and "|cFF00FF00HaveQuestData" or "-"), (C_TaskQuest.IsActive(questID) and "|cFF88FF00IsActive|r" or "")) qprint(' ', pin.title, pin.itemTexture, 'rewardType:', pin.rewardType, 'tag:', pin.tagID, 'style', pin.style ) end return QuestsByID[questID] end function WorldQuests:Refresh(forced) print('pushing |cFF00FF88'..tostring(self)..':Refresh()|r') if not WorldMapPOIFrame:IsVisible() then return end self:Reset() self:UpdateAnchors() self:Cleanup () end -- prepares elements for a map update function WorldQuests:Reset () wipe(QuestPositions) wipe(QuestsByReward) wipe(QuestsByTag) for questID, pin in pairs(QuestsByID) do pin.used = nil end qprint(tostring(self)..':|cFFFFFF00Reset()|r') end -- update visibility states of all pins function WorldQuests:UpdateAnchors (submapID) local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo() if isMicroDungeon then return end local currentMap = GetCurrentMapAreaID() local submapID = submapID or currentMap if submapID == BROKEN_ISLES_ID and (not DisplayContinentPins) then print('not updating map for reasons') return end print('|cFF88FF00'..tostring(self)..':UpdateAnchors|r', submapID, GetMapNameByID(submapID), 'pin count:', numPins) local numZones = MC_GetNumZones(submapID) if numZones then for i = 1, numZones do local subMapID = MC_GetZoneInfo(submapID, i) self:UpdateAnchors(subMapID) end end local pins = QuestsByZone[submapID] if pins then local hostFrame = WorldMapPOIFrame local mapWidth, mapHeight = hostFrame:GetSize() for questID, pin in pairs(pins) do if pin:IsShowable(true) then pin:SetFrameLevel(PinBaseIndex+numPins) pin:SetAnchor(WorldMapPOIFrame, currentMap, mapWidth, mapHeight) pin.used = true numPins = numPins + 1 end end end end -- shows, animates, or hides pins based on their current visibility flags local debug_show = {} local debug_animate = {} local debug_hide = {} function WorldQuests:Cleanup () local showQuestPOI = GetCVarBool("questPOI") print('|cFFFFFF00'..tostring(self)..':Cleanup()|r') wipe(QuestsByReward) wipe(QuestsByTag) wipe(debug_show) wipe(debug_animate) wipe(debug_hide) local mapID = GetCurrentMapAreaID() isContinentMap = (mapID == BROKEN_ISLES_ID) -- continent or zone sizing local subStyle = isContinentMap and 'continent' or 'zone' numPins = 0 for questID, pin in pairs(QuestsByID) do -- can we show it? if showQuestPOI and (pin.used) then pin.subStyle = pin.filtered and 'minimized' or subStyle pin:GetSizeMetrics() -- is it a new quest? if pin.isNew then if not pin.isAnimating then pin.isAnimating = true OnNext(function() pin.isNew = nil pin:Show() pin.FadeIn:Play() end) if not notifyPlayed then for k,v in pairs(NotificationTypes) do if v[pin[k]] then notifyPlayed = true PlaySoundKitID(23404) end end end tinsert(debug_animate,questID) end -- trap new but animating pins here else -- hard show existing pin pin:Show() tinsert(debug_show,questID) end else pin:Hide() tinsert(debug_hide,questID) end -- is it part of the current map? if pin.used then local rewardType = pin.rewardType local tagType = pin.worldQuestType if rewardType then QuestsByReward[rewardType] = QuestsByReward[rewardType] or {} tinsert(QuestsByReward[rewardType], pin) end if tagType then QuestsByTag[tagType] = QuestsByTag[tagType] or {} tinsert(QuestsByTag[tagType], pin) end end end print(' adding:', table.concat(debug_animate, ',' )) print(' showing:', table.concat(debug_show, ',' )) hasNewQuestPins = nil notifyPlayed = nil end -- data provider manipulations for the taxi map WorldPlan.OnFlightMapLoaded = function() if true then return end -- todo: figure out how to layer inside the map canvas local res = {} local t = {} for k,v in pairs(FlightMapFrame) do tinsert(res, tostring(k)) end table.sort(res) for i, k in ipairs(res) do print(k) end hooksecurefunc(FlightMapFrame, 'RefreshAll', function(self) print('|cFF0088FFWQDP RefreshAllData ', GetTime()) WorldPlan:GetPinsForMap(self:GetMapID()) for pin in self:EnumerateAllPins() do if pin.worldQuest then --print('got pin #', pin.questID) local wp = QuestsByID[pin.questID] if wp then wp:ClearAllPoints() wp:SetParent(FlightMapFrame.ScrollContainer) wp:SetFrameStrata('MEDIUM') wp:SetPoint('CENTER', pin, 'CENTER') wp:Show() end end end end) end local throttle = 0 local tooltip = CreateFrame ("GameTooltip", "VeneerWorldQuestsScanner", nil, "GameTooltipTemplate") local tooltipLine1 = _G['VeneerWorldQuestsScannerTextLeft1'] local tooltipLine3 = _G['VeneerWorldQuestsScannerTextLeft3'] local GetTime, mod = GetTime, mod function QuestPOI:OnLoad() self:RegisterEvent('SUPER_TRACKED_QUEST_CHANGED') end function QuestPOI:OnEvent(event, ...) if event == 'SUPER_TRACKED_QUEST_CHANGED' then if self:IsVisible() then self:Refresh() end end end local PIN_UPDATE_DELAY = .016 local TOP_PIN_ID function QuestPOI:OnUpdate (sinceLast) -- control update check intervals self.throttle = (self.throttle or PIN_UPDATE_DELAY) - sinceLast if self.throttle <= 0 then -- factor overtime into the throttle timer self.throttle = PIN_UPDATE_DELAY - self.throttle else return end -- query for reward data if it wasn't found in the original scan local questID = self.questID if self.isPending then WorldQuests:GetPinByQuestID(questID) if not (self.PendingFade:IsPlaying() or self.isAnimating) then self.PendingFade:Play() end return else if self.PendingFade:IsPlaying() then self.PendingFade:Stop() end end if self.hasUpdate then self:Refresh() self.hasUpdate = nil end -- update time elements local tl = self.timeThreschold local timeLeft = TQ_GetQuestTimeLeftMinutes(questID) if timeLeft > 0 then for i, context in ipairs(PIN_TIME_CONTEXT) do if i > self.TimeleftStage then self.timeLabel:SetText(nil) break end if timeLeft <= context.max then if tl ~= i then tl = i end if context.format then self.timeLabel:SetText(context.format(timeLeft)) else self.timeLabel:SetText(nil) end break end end else -- remove self in a timely manner if not self.isPending then self:Hide() end end self.timeThreschold = tl if self:IsMouseOver() then self.MouseGlow:Show() else self.MouseGlow:Hide() end end function QuestPOI:OnShow () qprint('|cFFFFFF00["'..tostring(self.title)..'"]|r:OnShow()') -- pop this on principle self:Refresh() end function QuestPOI:OnHide() --qprint('|cFFFFFF00["'..tostring(self.title)..'"]|r:OnHide()') end function QuestPOI:Refresh () print('|cFF00FF88["'..tostring(self.title)..'"]|r:Refresh()', tostring(self.title), "|T"..tostring(self.itemTexture)..":12:12|t", tostring(self.itemName)) qprint(self.style) local questID = self.questId local style = self.style local borderMask = style.mask local borderFill = style.texture local iconBorder = self.iconBorder local icon = self.icon local count = self.count --WorldPlan:print(tostring(self.title), "|T"..tostring(self.itemTexture)..":16:16|t", tostring(self.itemName)) SetMaskedTexture(icon, self.itemTexture or ICON_UNKNOWN, borderMask) icon:SetAllPoints(self) if self.itemName then local color = self.rewardColor or COMMON_COLOR if self.itemNumber and self.target then self.count:SetText(color.hex .. tostring(self.itemNumber)) else self.count:SetText(nil) end end SetMaskedTexture(iconBorder, borderFill, borderMask) local border = (self.rewardType and POI_REWARD_TYPE[self.rewardType]) or (WORLD_QUEST_QUALITY_COLORS[self.rarity] or COMMON_COLOR) iconBorder:SetVertexColor(border.r, border.g, border.b, border.a) iconBorder:SetDesaturated(true) local trackingBorder = self.supertrackBorder self.highlight:SetMask(nil) if questID == GetSuperTrackedQuestID() then trackingBorder:SetVertexColor(0,0,0,1) else trackingBorder:SetVertexColor(0,0,0,0.5) end self.highlight:SetAllPoints(trackingBorder) SetMaskedTexture(trackingBorder, borderFill, borderMask) self.highlight:SetMask(borderMask) local qType = self.worldQuestType self.tagIcon:SetAtlas(self.tagAtlas) self.tagIcon:SetTexCoord(0,1,0,1) if self.isElite then self.EliteDecal:Show() else self.EliteDecal:Hide() end if style.showNumber then self.label:SetText(self.itemNumber) else self.label:SetText(nil) end qprint('|cFF88FF00updated', questID, self.title, self.rewardType, (style.showNumber and self.itemNumber) or '') end function QuestPOI:SetAnchor(frame, mapID, mapWidth, mapHeight) self:ClearAllPoints() local dX, dY = TQ_GetQuestLocation(self.questID, mapID) if not dX or dX == 0 then local _, x, y = QuestPOIGetIconInfo(self.questID) if x and floor(x) ~= 0 then dX, dY = x, y else dX, dY = self.x, self.y end end self.x = dX self.y = dY print(' |cFF00FF00'..self.questID..':|r', format("%0.2f %0.2f", dX, dY)) local pX = (dX * mapWidth) local pY = (-dY * mapHeight) self:SetParent(WorldMapPOIFrame) self:SetPoint('CENTER', frame, 'TOPLEFT', pX, pY) end function QuestPOI:IsShowable (ignoreFilters) local qType = self.worldQuestType local rType = self.rewardType self.filtered = nil print(' |cFFFF4400IsShowable()|r', self.tradeskillLineIndex, self.title) local whiteListed, blackListed for filterKey, includes in pairs(FilterInclusions) do local controlValue = self[filterKey] if FilterInclusions[filterKey][controlValue] then whiteListed = true break end if FilterExclusions[filterKey][controlValue] then blackListed = true end end print(blackListed, whiteListed) self.filtered = (blackListed and (not whiteListed)) if not TQ_IsActive(self.questID) then print(' quest is dead') return false end if qType == LE_QUEST_TAG_TYPE_PROFESSION then if not (ShowAllProfessionQuests or (self.tradeskillLineIndex and GetProfessionInfo(self.tradeskillLineIndex))) then print(' non-profession') return false end end return true end function QuestPOI:UpdateTimer (timeLeft, timeType) print('|cFF0088FFUpdatePinTimer()|r') end --- Re-acquires size information and triggers things function QuestPOI:GetSizeMetrics (style, subStyle) self.style = self.style or POI_DEFAULT_TYPE self.subStyle = self.subStyle or 'continent' style = style or self.style subStyle = style[subStyle or self.subStyle] self.currentWidth = subStyle.PinSize self.borderSize = subStyle.Border self.trackingBorderSize = subStyle.TrackingBorder self.tagSize = subStyle.TagSize self.TimeleftStage = subStyle.TimeleftStage self.NoIcon = subStyle.NoIcon self:SetSize(self.currentWidth, self.currentWidth) end -- triggered by OnSizeChanged script function QuestPOI:OnSizeChanged () local iconBorder = self.iconBorder local trackingBorder = self.supertrackBorder local tag = self.tagIcon local style = self.style or POI_DEFAULT_TYPE local mask = style.mask or POI_BORDER_FILL self.icon:SetMask(nil) self.iconBorder:SetMask(nil) self.supertrackBorder:SetMask(nil) local borderWidth = self.borderSize local trackingWidth = self.trackingBorderSize iconBorder:ClearAllPoints() iconBorder:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', -borderWidth + (style.x or 0), -borderWidth + (style.y or 0)) iconBorder:SetPoint('TOPRIGHT', self, 'TOPRIGHT', borderWidth + (style.x or 0), borderWidth + (style.y or 0)) trackingBorder:ClearAllPoints() trackingBorder:SetPoint('BOTTOMLEFT', iconBorder, 'BOTTOMLEFT', -trackingWidth, -trackingWidth) trackingBorder:SetPoint('TOPRIGHT', iconBorder, 'TOPRIGHT', trackingWidth, trackingWidth) if self.tagSize then tag:Show() tag:ClearAllPoints() tag:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', borderWidth, -borderWidth) else tag:Hide() end self.icon:SetMask(mask) self.iconBorder:SetMask(mask) self.supertrackBorder:SetMask(mask) if self.NoIcon then self.icon:Hide() else self.icon:Show() end end function FilterBar:OnEvent(event) if event == 'QUEST_LOG_UPDATE' then self:Refresh() end end function FilterBar:PassesFilterSet(filterKey, pin) local passesFilter = true for filterKey, filters in pairs(QuestFilters) do for rewardType, value in pairs(QuestFilters[filterKey]) do if value == 1 and rewardType == pin[filterKey] then passesFilter = true elseif value == -1 and rewardType == pin[filterKey] then passesFilter = false end end end return passesFilter end local debug_headers = {} function FilterBar:Refresh() local mapID = GetCurrentMapAreaID() local blocks = self.SummaryHeaders local numHeaders = 0 local lastButton local pinSize, borderSize, trackingSize = 20, 2, 2 print('|cFF00FF88'..tostring(self)..':Refresh()|r') for index, info in pairs(POI_FILTER_OPTIONS) do local numQuests = 0 local quests = QuestsByZone[mapID] or QuestsByID for questID, pin in pairs(quests) do if pin.used then if not info.filterKey then numQuests = numQuests + 1 elseif pin[info.filterKey] == info.filterValue then numQuests = numQuests + 1 end end end print(tostring(index).. ' ("'..tostring(info.label)..'"'.. tostring(rewardFilter).. ', '..tostring(tagFilter) .. ', '..tostring(numQuests)..')') if numQuests >= 1 then numHeaders = numHeaders + 1 local button = blocks[numHeaders] if not blocks[numHeaders] then button = CreateFrame('Button', 'WorldPlanFilterButton'..numHeaders, WorldMapScrollFrame, 'WorldPlanFilterPin') button.iconBorder:SetTexture(info.fill or POI_BORDER_FILL) button.iconBorder:SetMask(info.mask or POI_BORDER_MASK) button.iconBorder:SetDesaturated(info.desaturated) button.supertrackBorder:SetTexture(info.fill or POI_BORDER_FILL) button.supertrackBorder:SetMask(info.mask or POI_BORDER_MASK) button.supertrackBorder:SetDesaturated(true) blocks[numHeaders] = button end button:SetID(index) button.lastButton = lastButton button:Refresh(info, (numHeaders == 1), numQuests) button:Show() lastButton = button end end -- hide trailing buttons for i = numHeaders + 1, #POI_FILTER_OPTIONS do if blocks[i] then blocks[i]:Hide() end end end function FilterPin:Refresh(info, isFirst, numQuests) info = info or POI_FILTER_OPTIONS[self:GetID()] isFirst = isFirst or self.isFirst numQuests = numQuests or self.numQuests self.isFirst = isFirst self.numQuests = numQuests self.filterKey = info.filterKey self.filterValue = info.filterValue self.tagID = info.tagID self.icon:ClearAllPoints() self.icon:SetTexture(info.texture) self.icon:SetAllPoints(self) self.supertrackBorder:Hide() self.label:SetText(numQuests) self:Show() if isFirst then self:SetPoint('TOP', WorldMapFrame.UIElementsFrame.TrackingOptionsButton, 'BOTTOM', 0, -5) else self:SetPoint('TOPRIGHT', self.lastButton, 'BOTTOMRIGHT', 0, -(3*2 + 1 + (info.spacing or 0))) end print(self.filterKey, self.filterValue) local r, g, b, a = 1,1,1,1 if self.filterKey then if FilterInclusions[self.filterKey][self.filterValue] == true then r, g, b = 0, 1, 0 elseif FilterExclusions[self.filterKey][self.filterValue] then r, g, b = 1, 0, 0 end end self.iconBorder:SetVertexColor(r, g, b, a) self:GetSizeMetrics() end function FilterPin:OnLoad() self:RegisterForClicks('AnyUp') self:SetFrameStrata('HIGH') self:SetFrameLevel(151) self:SetScript('OnUpdate', nil) end function FilterPin:OnUpdate () end function FilterPin:OnEnter () local filter = POI_FILTER_OPTIONS[self:GetID()] local mapID = GetCurrentMapAreaID() local quests = (mapID == BROKEN_ISLES_ID) and QuestsByID or QuestsByZone[mapID] if quests then GameTooltip:SetOwner(self, 'ANCHOR_RIGHT') GameTooltip:AddLine(filter.label) local filterKey = self.filterKey local filterValue = self.filterValue if filterKey then for questID, pin in pairs(quests) do if pin.used and not pin.filtered then if pin[filterKey] == filterValue then local style = pin.style or POI_FILTER_STYLE GameTooltip:AddLine(pin.title) end end end else GameTooltip:AddLine('Reset all filters') end GameTooltip:Show() end end function FilterPin:OnLeave () if GameTooltip:IsOwned(self) then GameTooltip:Hide() end end -- shift-click: reset filter -- click: rotate through include(1), exclude(-1), ignore(nil) function FilterPin:OnClick (button) local filterKey = self.filterKey local filterValue = self.filterValue print('click', filterKey, filterValue) if not filterKey then -- resetting for k,v in pairs(FilterInclusions) do wipe(v) end for k,v in pairs(FilterExclusions) do wipe(v) end else local setExclude = (button == 'RightButton') if IsControlKeyDown() then if setExclude then if FilterExclusions[filterKey][filterValue] then FilterExclusions[filterKey][filterValue] = nil else FilterExclusions[filterKey][filterValue] = true end FilterInclusions[filterKey][filterValue] = nil else if FilterInclusions[filterKey][filterValue] == true then FilterInclusions[filterKey][filterValue] = nil else FilterInclusions[filterKey][filterValue] = true end FilterExclusions[filterKey][filterValue] = nil end elseif IsShiftKeyDown() then FilterInclusions[filterKey][filterValue] = nil FilterExclusions[filterKey][filterValue] = nil else print('limit to', filterKey, filterValue) if setExclude then FilterExclusions[filterKey][filterValue] = true FilterInclusions[filterKey][filterValue] = nil else FilterInclusions[filterKey][filterValue] = true FilterExclusions[filterKey][filterValue] = nil end for k, info in ipairs(POI_FILTER_OPTIONS) do if info.filterKey and ((info.filterKey ~= filterKey) or (info.filterValue ~= filterValue)) then if setExclude then FilterExclusions[info.filterKey][info.filterValue] = nil FilterInclusions[info.filterKey][info.filterValue] = true else FilterExclusions[info.filterKey][info.filterValue] = true FilterInclusions[info.filterKey][info.filterValue] = nil end end end end end WorldPlan:RefreshAll() end --%debug% local SetTimedCallbackForAllPins = function(seconds, callback) C_Timer.After(seconds, function() for id, pin in pairs(QuestsByID) do callback(pin) end end) end SLASH_WORLDPLAN1 = "/worldplan" SLASH_WORLDPLAN2 = "/wp" SlashCmdList.WORLDPLAN = function() print('command pop') WorldPlan:GetPinsForMap() WorldPlan:RefreshPins() SetTimedCallbackForAllPins(0, function(self) self.FadeIn:Play() self.FlashIn:Play() end) SetTimedCallbackForAllPins(5, function(self) self.PendingFade:Play() end) SetTimedCallbackForAllPins(8, function(self) self.PendingFade:Stop() end) end --%end-debug% for mapID, mapName in pairs(WORLD_QUEST_MAPS) do QuestsByZone[mapID] = {} end for index, color in pairs(ITEM_QUALITY_COLORS) do POI_REWARD_TYPE[index] = { r = color.r, g = color.g, b = color.b, hex = color.hex, } end