Nenue@33: -- WorldPlan Nenue@33: -- WorldQuests.lua Nenue@33: -- Created: 11/2/2016 3:40 PM Nenue@33: -- %file-revision% Nenue@33: Nenue@33: WorldPlanQuestsMixin = { Nenue@33: QuestsByZone = {}, Nenue@33: QuestsByID = {}, Nenue@33: freePins = {}, Nenue@33: } Nenue@33: local WorldQuests = WorldPlanQuestsMixin Nenue@33: Nenue@33: local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo Nenue@33: local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented Nenue@33: local TQ_GetQuestZoneID = C_TaskQuest.GetQuestZoneID Nenue@33: local GetMapInfo = GetMapInfo Nenue@33: local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end Nenue@33: local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end Nenue@33: local wqprint = DEVIAN_WORKSPACE and function(...) _G.print('WorldQuests', ...) end or function() end Nenue@33: local wprint = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end Nenue@33: Nenue@33: Nenue@33: local PinBaseIndex = 1600 Nenue@33: 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 Nenue@33: local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70', [AZSUNA_ID] = 'Azsuna', [VALSHARAH_ID] = "Val'sharah", Nenue@33: [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim', [SURAMAR_ID] = 'Suramar', [EOA_ID] = 'EyeOfAszhara', } Nenue@33: Nenue@33: local REWARD_CASH = WORLD_QUEST_REWARD_TYPE_FLAG_GOLD Nenue@33: local REWARD_ARTIFACT_POWER = WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER Nenue@33: local REWARD_GEAR = WORLD_QUEST_REWARD_TYPE_FLAG_EQUIPMENT Nenue@33: local REWARD_CURRENCY = WORLD_QUEST_REWARD_TYPE_FLAG_ORDER_RESOURCES Nenue@33: local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS Nenue@33: Nenue@33: Nenue@33: local numPins = 0 Nenue@33: local ZoneInfo = {} Nenue@33: local NumPinFrames = 1 Nenue@33: Nenue@33: Nenue@33: --%debug% Nenue@33: local SetTimedCallbackForAllPins = function(seconds, callback) Nenue@33: C_Timer.After(seconds, function() Nenue@33: for id, pin in pairs(WorldPlanQuests.QuestsByID) do Nenue@33: callback(pin) Nenue@33: end Nenue@33: end) Nenue@33: end Nenue@33: Nenue@33: function WorldQuests:Setup() Nenue@33: Nenue@33: Nenue@33: for mapID, mapName in pairs(WORLD_QUEST_MAPS) do Nenue@33: self.QuestsByZone[mapID] = {} Nenue@33: end Nenue@33: Nenue@33: Nenue@33: -- refresh positions any time blizzard does so (i.e. mousewheel zoom) Nenue@33: hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function() Nenue@33: self:Refresh(true) Nenue@33: end) Nenue@33: Nenue@33: -- hide the original world quest POIs Nenue@33: hooksecurefunc("WorldMap_UpdateQuestBonusObjectives", function() Nenue@33: for i = 1, NUM_WORLDMAP_TASK_POIS do Nenue@33: local button = _G['WorldMapFrameTaskPOI'..i] Nenue@33: if button and button.worldQuest then Nenue@33: button:Hide() Nenue@33: end Nenue@33: end Nenue@33: end) Nenue@33: end Nenue@33: local WorldMapPOIFrame Nenue@33: local defaults = {} Nenue@33: function WorldQuests:OnLoad() Nenue@33: print('|cFF00FF88'..self:GetName()..':OnLoad') Nenue@33: Nenue@33: WorldPlan:AddHandler(self, defaults) Nenue@33: Nenue@33: local rgbWhite = {1, 1, 1} Nenue@33: WorldPlan:AddTypeInfo(self, REWARD_REAGENT, { r = 0, g = 1, b = 1 }) Nenue@33: WorldPlan:AddTypeInfo(self, REWARD_ARTIFACT_POWER, { r = 1, g = .25, b = .5, hasNumeric = true, numberRGB = rgbWhite }) Nenue@33: WorldPlan:AddTypeInfo(self, REWARD_GEAR, { r = .1, g = .2, b = 1 }) Nenue@33: WorldPlan:AddTypeInfo(self, REWARD_CURRENCY, { r = 1, g = 1, b = 0, hasNumeric = true, numberRGB = {1,1,0}, }) Nenue@33: WorldPlan:AddTypeInfo(self, REWARD_CASH, { r = 0, g = 0, b = 0, }) Nenue@33: Nenue@33: for areaID, fileName in pairs(WORLD_QUEST_MAPS) do Nenue@33: self.QuestsByZone[areaID] = {} Nenue@33: end Nenue@33: Nenue@33: self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL') Nenue@33: self:RegisterEvent('SKILL_LINES_CHANGED') Nenue@33: Nenue@33: WorldMapPOIFrame = _G.WorldMapPOIFrame Nenue@33: Nenue@33: end Nenue@33: Nenue@33: function WorldQuests:OnEvent (event, ...) Nenue@33: local print = wqprint Nenue@33: print('|cFFFFFF00'..self:GetName()..':OnEvent()'..event..'|r', GetTime(), ...) Nenue@33: if event == 'QUEST_LOG_UPDATE' then Nenue@33: local questID, added = ... Nenue@33: if questID and added then Nenue@33: local questPOI = self:AcquirePin(questID) Nenue@33: self.isStale, self.isPending = questPOI:RefreshData() Nenue@33: else Nenue@33: self:RefreshData() Nenue@33: end Nenue@33: print('WorldMapFrame', WorldMapFrame:IsVisible(), 'hasUpdates:', self.isStale) Nenue@34: elseif event == 'WORLD_MAP_UPDATE' or event == 'PLAYER_ENTERING_WORLD' then Nenue@33: self.isStale = true Nenue@33: elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then Nenue@33: local questID = ... Nenue@33: if questID and self.QuestsByID[questID] then Nenue@33: self:ReleasePin(self.QuestsByID[questID]) Nenue@33: end Nenue@33: elseif event == 'SKILL_LINES_CHANGED' then Nenue@33: self.isStale = true Nenue@33: end Nenue@33: end Nenue@33: Nenue@33: local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation Nenue@33: function WorldQuests:AcquirePin (questID, mapID) Nenue@33: local pin = self.QuestsByID[questID] Nenue@33: local isNew = false Nenue@33: if not pin then Nenue@33: isNew = true Nenue@33: local numFree = #self.freePins Nenue@33: if numFree >= 1 then Nenue@33: pin = tremove(self.freePins, numFree) Nenue@33: --print('|cFF00FF00Re-using', pin:GetName()) Nenue@33: else Nenue@33: local name = 'WorldPlanQuestMarker' .. NumPinFrames Nenue@33: --print('|cFF00FF00Creating', name) Nenue@33: pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin') Nenue@33: Nenue@33: pin:SetFrameStrata('HIGH') Nenue@33: pin.GetTypeInfo = function(frame, typeID) Nenue@33: return self:GetTypeInfo(typeID) Nenue@33: end Nenue@33: NumPinFrames = NumPinFrames + 1 Nenue@33: --pin.iconBorder:SetVertexColor(0,0,0,1) Nenue@33: end Nenue@33: pin:SetID(questID) Nenue@33: pin.isNew = true Nenue@33: pin.currentWidth = nil Nenue@33: Nenue@33: -- used by TaskPOI_x scripts Nenue@33: pin.questID = questID Nenue@33: pin.worldQuest = true Nenue@33: Nenue@33: self.QuestsByID[questID] = pin Nenue@33: else Nenue@33: --print('|cFF00FF00Using', pin:GetName()) Nenue@33: end Nenue@33: mapID = mapID or TQ_GetQuestZoneID(questID) Nenue@33: self.QuestsByZone[mapID][questID] = pin Nenue@33: Nenue@33: return pin, isNew Nenue@33: end Nenue@33: Nenue@33: -- remove from index and add it to the recycling heap Nenue@33: function WorldQuests:ReleasePin (pin) Nenue@33: Nenue@33: local id = pin.questId Nenue@33: if id then Nenue@33: self.QuestsByID[id] = nil Nenue@33: for i, zone in pairs(self.QuestsByZone) do Nenue@33: print('-', i, zone[i]) Nenue@33: zone[id] = nil Nenue@33: end Nenue@33: end Nenue@33: pin:Hide() Nenue@33: pin:ClearAllPoints() Nenue@33: tinsert(self.freePins, pin) Nenue@33: print('|cFFFF4400Clearing out', pin:GetName(),id) Nenue@33: end Nenue@33: Nenue@33: -- create of update quest pins for a map and its underlying zones Nenue@33: function WorldQuests:RefreshData (mapID) Nenue@33: local print = wqprint Nenue@33: mapID = mapID or GetCurrentMapAreaID() Nenue@33: superTrackedID = GetSuperTrackedQuestID() Nenue@33: if not mapID then Nenue@33: -- info not available yet Nenue@33: return Nenue@33: end Nenue@33: Nenue@34: if not self:IsVisible() then Nenue@34: self.isStale = true Nenue@34: print('frame closed, do it later') Nenue@34: return Nenue@34: end Nenue@34: Nenue@34: Nenue@34: print('|cFF00FF88'..self:GetName()..':RefreshData()|r', 'map:', mapID, 'realMap:', GetCurrentMapAreaID()) Nenue@33: Nenue@33: if mapID == BROKEN_ISLES_ID then Nenue@33: self.isStale = false Nenue@33: print('|cFF00FFFFContinent:|r', mapID, GetMapNameByID(mapID), superTrackedID) Nenue@33: self.fullSearch = true Nenue@33: for i = 1, MC_GetNumZones(mapID) do Nenue@33: local submapID, name, depth = MC_GetZoneInfo(mapID, i) Nenue@33: self:RefreshData(submapID) Nenue@33: end Nenue@33: self.fullSearch = nil Nenue@33: elseif self.QuestsByZone[mapID] then Nenue@33: local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID) Nenue@33: local numQuests = 0 Nenue@33: if taskInfo and #taskInfo >= 1 then Nenue@33: print('|cFF00FFFF Zone:|r', mapID, GetMapNameByID(mapID), #taskInfo) Nenue@33: wipe(self.QuestsByZone[mapID]) Nenue@33: ZoneInfo[mapID] = taskInfo Nenue@33: qprint('|cFFFF4400START of', GetMapNameByID(mapID)) Nenue@33: for taskID, info in pairs(taskInfo) do Nenue@33: local questID = info.questId Nenue@33: info.mapID = mapID Nenue@33: local questPOI = self:AcquirePin(questID, mapID) Nenue@33: local hasUpdate, isPending = questPOI:RefreshData(info) Nenue@34: -- WorldPlan:print('|cFF0088FF'..questPOI.title..'|r', hasUpdate) Nenue@33: self.isStale = (self.isStale or hasUpdate) Nenue@33: self.isPending = (self.isPending or isPending) Nenue@33: numQuests = numQuests + 1 Nenue@33: end Nenue@33: qprint('|cFFFF4400END of', GetMapNameByID(mapID)) Nenue@33: end Nenue@33: end Nenue@33: Nenue@33: if not self.fullSearch then Nenue@33: print(' hasUpdate:', self.isStale, 'isPending:', self.isPending, 'timer:', (self.OnNext and 'waiting' or '')) Nenue@33: --WorldPlan.isStale = (self.isStale or WorldPlan.isStale) Nenue@33: end Nenue@33: Nenue@33: end Nenue@33: Nenue@33: function WorldQuests:Refresh() Nenue@33: local print = wqprint Nenue@33: print('|cFF00FF88'..self:GetName()..':Refresh()|r') Nenue@34: if not self:IsVisible() then Nenue@34: self.isStale = true Nenue@34: print('frame closed, do it later') Nenue@34: return Nenue@34: end Nenue@34: Nenue@33: self:Reset() Nenue@33: self:UpdateAnchors() Nenue@33: self:Cleanup () Nenue@33: end Nenue@33: Nenue@33: -- prepares elements for a map update Nenue@33: function WorldQuests:Reset () Nenue@33: local print = wqprint Nenue@33: print('|cFF00FF88'..self:GetName()..':Reset()|r') Nenue@33: for questID, pin in pairs(self.QuestsByID) do Nenue@33: pin.used = nil Nenue@33: end Nenue@33: end Nenue@33: Nenue@33: -- update visibility states of all pins Nenue@33: function WorldQuests:UpdateAnchors (submapID) Nenue@33: Nenue@33: local print = wqprint Nenue@33: local db = WorldPlan.db Nenue@33: local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo() Nenue@33: if isMicroDungeon then Nenue@33: return Nenue@33: end Nenue@33: Nenue@33: local currentMap = GetCurrentMapAreaID() Nenue@33: local submapID = submapID or currentMap Nenue@33: Nenue@33: if submapID == BROKEN_ISLES_ID and (not db.DisplayContinentPins) then Nenue@33: print('not updating map for reasons') Nenue@33: return Nenue@33: end Nenue@33: print('|cFF88FF00'..self:GetName()..':UpdateAnchors|r', submapID, GetMapNameByID(submapID), 'pin count:', numPins) Nenue@33: local numZones = MC_GetNumZones(submapID) Nenue@33: if numZones then Nenue@33: for i = 1, numZones do Nenue@33: local subMapID = MC_GetZoneInfo(submapID, i) Nenue@33: self:UpdateAnchors(subMapID) Nenue@33: end Nenue@33: end Nenue@33: local pins = self.QuestsByZone[submapID] Nenue@33: Nenue@33: if pins then Nenue@33: local hostFrame = WorldMapPOIFrame Nenue@33: local mapWidth, mapHeight = hostFrame:GetSize() Nenue@33: for questID, pin in pairs(pins) do Nenue@33: pin:IsShowable() Nenue@33: if pin.used then Nenue@33: pin.hasUpdate = true Nenue@33: pin:SetFrameLevel(PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins) Nenue@33: print('level', PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins) Nenue@34: pin:SetAnchor(_G.WorldMapPOIFrame, currentMap, mapWidth, mapHeight) Nenue@33: numPins = numPins + 1 Nenue@33: end Nenue@33: end Nenue@33: end Nenue@33: end Nenue@33: Nenue@33: -- shows, animates, or hides pins based on their current visibility flags Nenue@33: local debug_show = {} Nenue@33: local debug_animate = {} Nenue@33: local debug_hide = {} Nenue@33: function WorldQuests:Cleanup () Nenue@33: local print = wqprint Nenue@34: local showQuestPOI = WorldPlan.db.EnablePins Nenue@34: WorldPlan:print('|cFFFFFF00'..self:GetName()..':Cleanup()|r') Nenue@34: -- continent or zone sizing Nenue@33: Nenue@33: Nenue@33: numPins = 0 Nenue@33: for questID, pin in pairs(self.QuestsByID) do Nenue@33: -- can we show it? Nenue@34: pin:SetShown((showQuestPOI and pin.used)) Nenue@33: end Nenue@33: print(' adding:', table.concat(debug_animate, ',' )) Nenue@33: print(' refresh:', table.concat(debug_show, ',' )) Nenue@33: print(' hiding:', table.concat(debug_hide, ',' )) Nenue@33: self.isStale = nil Nenue@33: end Nenue@33: Nenue@33: function WorldQuests:FilterCheckByID(questID) Nenue@33: local pin = WorldQuests:GetPinByQuestID(questID) Nenue@33: return pin:IsShowable() Nenue@33: end