annotate WorldQuests.lua @ 37:78cf1f19856a

WorldPlan: - Quest pins are now placed on the flight map. Their visibility rules will mirror the filter options from the world map. - Filter controls polish: - First click negates other reward type filters. Subsequent clicks will then toggle individual reward types until the filters are reset via Right-click. - Adheres to the Blizzard CVars added in patch 7.1 - Numerous optimizations to how data and visual updates are handled; should see an even better load time, and snappier world map interaction. ClassPlan: - The 'Available Missions' list is now recorded. It can be reviewed by clicking on the mission list heading. - Information filtering by character and realm.
author Nenue
date Fri, 04 Nov 2016 02:47:17 -0400
parents 21bcff08b0f4
children 589c444d4837
rev   line source
Nenue@33 1 -- WorldPlan
Nenue@33 2 -- WorldQuests.lua
Nenue@33 3 -- Created: 11/2/2016 3:40 PM
Nenue@33 4 -- %file-revision%
Nenue@33 5
Nenue@33 6 local WorldQuests = WorldPlanQuestsMixin
Nenue@33 7
Nenue@33 8 local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo
Nenue@33 9 local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented
Nenue@33 10 local TQ_GetQuestZoneID = C_TaskQuest.GetQuestZoneID
Nenue@33 11 local GetMapInfo = GetMapInfo
Nenue@33 12 local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
Nenue@33 13 local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end
Nenue@33 14 local wqprint = DEVIAN_WORKSPACE and function(...) _G.print('WorldQuests', ...) end or function() end
Nenue@33 15 local wprint = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
Nenue@35 16 local mprint = DEVIAN_WORKSPACE and function(...) _G.print('Canvas', ...) end or function() end
Nenue@33 17
Nenue@33 18
Nenue@37 19 local PinBaseIndex = 1000
Nenue@33 20 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 21 local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70', [AZSUNA_ID] = 'Azsuna', [VALSHARAH_ID] = "Val'sharah",
Nenue@33 22 [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim', [SURAMAR_ID] = 'Suramar', [EOA_ID] = 'EyeOfAszhara', }
Nenue@33 23
Nenue@33 24 local REWARD_CASH = WORLD_QUEST_REWARD_TYPE_FLAG_GOLD
Nenue@33 25 local REWARD_ARTIFACT_POWER = WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER
Nenue@33 26 local REWARD_GEAR = WORLD_QUEST_REWARD_TYPE_FLAG_EQUIPMENT
Nenue@33 27 local REWARD_CURRENCY = WORLD_QUEST_REWARD_TYPE_FLAG_ORDER_RESOURCES
Nenue@33 28 local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS
Nenue@33 29
Nenue@33 30
Nenue@33 31 local numPins = 0
Nenue@33 32 local ZoneInfo = {}
Nenue@33 33 local NumPinFrames = 1
Nenue@33 34
Nenue@33 35
Nenue@33 36 --%debug%
Nenue@33 37 local SetTimedCallbackForAllPins = function(seconds, callback)
Nenue@33 38 C_Timer.After(seconds, function()
Nenue@33 39 for id, pin in pairs(WorldPlanQuests.QuestsByID) do
Nenue@33 40 callback(pin)
Nenue@33 41 end
Nenue@33 42 end)
Nenue@33 43 end
Nenue@33 44
Nenue@33 45 function WorldQuests:Setup()
Nenue@33 46
Nenue@33 47
Nenue@33 48 for mapID, mapName in pairs(WORLD_QUEST_MAPS) do
Nenue@33 49 self.QuestsByZone[mapID] = {}
Nenue@33 50 end
Nenue@33 51
Nenue@33 52
Nenue@33 53 -- refresh positions any time blizzard does so (i.e. mousewheel zoom)
Nenue@33 54 hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function()
Nenue@33 55 self:Refresh(true)
Nenue@33 56 end)
Nenue@33 57
Nenue@33 58 -- hide the original world quest POIs
Nenue@33 59 hooksecurefunc("WorldMap_UpdateQuestBonusObjectives", function()
Nenue@33 60 for i = 1, NUM_WORLDMAP_TASK_POIS do
Nenue@33 61 local button = _G['WorldMapFrameTaskPOI'..i]
Nenue@33 62 if button and button.worldQuest then
Nenue@33 63 button:Hide()
Nenue@33 64 end
Nenue@33 65 end
Nenue@33 66 end)
Nenue@33 67 end
Nenue@33 68 local WorldMapPOIFrame
Nenue@33 69 local defaults = {}
Nenue@33 70 function WorldQuests:OnLoad()
Nenue@33 71 print('|cFF00FF88'..self:GetName()..':OnLoad')
Nenue@33 72
Nenue@33 73 WorldPlan:AddHandler(self, defaults)
Nenue@33 74
Nenue@33 75 local rgbWhite = {1, 1, 1}
Nenue@33 76 WorldPlan:AddTypeInfo(self, REWARD_REAGENT, { r = 0, g = 1, b = 1 })
Nenue@33 77 WorldPlan:AddTypeInfo(self, REWARD_ARTIFACT_POWER, { r = 1, g = .25, b = .5, hasNumeric = true, numberRGB = rgbWhite })
Nenue@33 78 WorldPlan:AddTypeInfo(self, REWARD_GEAR, { r = .1, g = .2, b = 1 })
Nenue@33 79 WorldPlan:AddTypeInfo(self, REWARD_CURRENCY, { r = 1, g = 1, b = 0, hasNumeric = true, numberRGB = {1,1,0}, })
Nenue@36 80 WorldPlan:AddTypeInfo(self, REWARD_CASH, { r = .7, g = .6, b = .32, pinMask = false, rewardMask = false })
Nenue@33 81
Nenue@33 82 for areaID, fileName in pairs(WORLD_QUEST_MAPS) do
Nenue@33 83 self.QuestsByZone[areaID] = {}
Nenue@33 84 end
Nenue@33 85
Nenue@33 86 self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL')
Nenue@33 87 self:RegisterEvent('SKILL_LINES_CHANGED')
Nenue@33 88
Nenue@33 89 WorldMapPOIFrame = _G.WorldMapPOIFrame
Nenue@33 90
Nenue@33 91 end
Nenue@33 92
Nenue@33 93 function WorldQuests:OnEvent (event, ...)
Nenue@33 94 local print = wqprint
Nenue@33 95 print('|cFFFFFF00'..self:GetName()..':OnEvent()'..event..'|r', GetTime(), ...)
Nenue@33 96 if event == 'QUEST_LOG_UPDATE' then
Nenue@33 97 local questID, added = ...
Nenue@33 98 if questID and added then
Nenue@33 99 local questPOI = self:AcquirePin(questID)
Nenue@33 100 self.isStale, self.isPending = questPOI:RefreshData()
Nenue@33 101 else
Nenue@33 102 self:RefreshData()
Nenue@33 103 end
Nenue@33 104 print('WorldMapFrame', WorldMapFrame:IsVisible(), 'hasUpdates:', self.isStale)
Nenue@34 105 elseif event == 'WORLD_MAP_UPDATE' or event == 'PLAYER_ENTERING_WORLD' then
Nenue@33 106 self.isStale = true
Nenue@33 107 elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then
Nenue@33 108 local questID = ...
Nenue@33 109 if questID and self.QuestsByID[questID] then
Nenue@33 110 self:ReleasePin(self.QuestsByID[questID])
Nenue@33 111 end
Nenue@33 112 elseif event == 'SKILL_LINES_CHANGED' then
Nenue@33 113 self.isStale = true
Nenue@33 114 end
Nenue@33 115 end
Nenue@33 116
Nenue@33 117 local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation
Nenue@33 118 function WorldQuests:AcquirePin (questID, mapID)
Nenue@33 119 local pin = self.QuestsByID[questID]
Nenue@33 120 local isNew = false
Nenue@33 121 if not pin then
Nenue@33 122 isNew = true
Nenue@33 123 local numFree = #self.freePins
Nenue@33 124 if numFree >= 1 then
Nenue@33 125 pin = tremove(self.freePins, numFree)
Nenue@33 126 --print('|cFF00FF00Re-using', pin:GetName())
Nenue@33 127 else
Nenue@33 128 local name = 'WorldPlanQuestMarker' .. NumPinFrames
Nenue@33 129 --print('|cFF00FF00Creating', name)
Nenue@33 130 pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin')
Nenue@33 131
Nenue@33 132 pin:SetFrameStrata('HIGH')
Nenue@33 133 pin.GetTypeInfo = function(frame, typeID)
Nenue@33 134 return self:GetTypeInfo(typeID)
Nenue@33 135 end
Nenue@33 136 NumPinFrames = NumPinFrames + 1
Nenue@33 137 --pin.iconBorder:SetVertexColor(0,0,0,1)
Nenue@33 138 end
Nenue@33 139 pin:SetID(questID)
Nenue@33 140 pin.isNew = true
Nenue@33 141 pin.currentWidth = nil
Nenue@33 142
Nenue@33 143 -- used by TaskPOI_x scripts
Nenue@33 144 pin.questID = questID
Nenue@33 145 pin.worldQuest = true
Nenue@33 146
Nenue@33 147 self.QuestsByID[questID] = pin
Nenue@33 148 else
Nenue@33 149 --print('|cFF00FF00Using', pin:GetName())
Nenue@33 150 end
Nenue@33 151 mapID = mapID or TQ_GetQuestZoneID(questID)
Nenue@33 152 self.QuestsByZone[mapID][questID] = pin
Nenue@33 153
Nenue@33 154 return pin, isNew
Nenue@33 155 end
Nenue@33 156
Nenue@33 157 -- remove from index and add it to the recycling heap
Nenue@33 158 function WorldQuests:ReleasePin (pin)
Nenue@33 159
Nenue@33 160 local id = pin.questId
Nenue@33 161 if id then
Nenue@33 162 self.QuestsByID[id] = nil
Nenue@33 163 for i, zone in pairs(self.QuestsByZone) do
Nenue@33 164 print('-', i, zone[i])
Nenue@33 165 zone[id] = nil
Nenue@33 166 end
Nenue@33 167 end
Nenue@33 168 pin:Hide()
Nenue@33 169 pin:ClearAllPoints()
Nenue@33 170 tinsert(self.freePins, pin)
Nenue@33 171 print('|cFFFF4400Clearing out', pin:GetName(),id)
Nenue@33 172 end
Nenue@33 173
Nenue@33 174 -- create of update quest pins for a map and its underlying zones
Nenue@33 175 function WorldQuests:RefreshData (mapID)
Nenue@33 176 local print = wqprint
Nenue@35 177 mapID = mapID or WorldPlan.currentMapID
Nenue@33 178 if not mapID then
Nenue@33 179 -- info not available yet
Nenue@33 180 return
Nenue@33 181 end
Nenue@33 182
Nenue@34 183
Nenue@34 184 print('|cFF00FF88'..self:GetName()..':RefreshData()|r', 'map:', mapID, 'realMap:', GetCurrentMapAreaID())
Nenue@33 185
Nenue@33 186 if mapID == BROKEN_ISLES_ID then
Nenue@33 187 self.isStale = false
Nenue@33 188 print('|cFF00FFFFContinent:|r', mapID, GetMapNameByID(mapID), superTrackedID)
Nenue@33 189 self.fullSearch = true
Nenue@33 190 for i = 1, MC_GetNumZones(mapID) do
Nenue@33 191 local submapID, name, depth = MC_GetZoneInfo(mapID, i)
Nenue@33 192 self:RefreshData(submapID)
Nenue@33 193 end
Nenue@33 194 self.fullSearch = nil
Nenue@33 195 elseif self.QuestsByZone[mapID] then
Nenue@35 196 local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID, WorldPlan.currentMapID)
Nenue@33 197 local numQuests = 0
Nenue@33 198 if taskInfo and #taskInfo >= 1 then
Nenue@33 199 print('|cFF00FFFF Zone:|r', mapID, GetMapNameByID(mapID), #taskInfo)
Nenue@33 200 wipe(self.QuestsByZone[mapID])
Nenue@33 201 ZoneInfo[mapID] = taskInfo
Nenue@33 202 qprint('|cFFFF4400START of', GetMapNameByID(mapID))
Nenue@33 203 for taskID, info in pairs(taskInfo) do
Nenue@33 204 local questID = info.questId
Nenue@33 205 info.mapID = mapID
Nenue@33 206 local questPOI = self:AcquirePin(questID, mapID)
Nenue@33 207 local hasUpdate, isPending = questPOI:RefreshData(info)
Nenue@34 208 -- WorldPlan:print('|cFF0088FF'..questPOI.title..'|r', hasUpdate)
Nenue@33 209 self.isStale = (self.isStale or hasUpdate)
Nenue@33 210 self.isPending = (self.isPending or isPending)
Nenue@33 211 numQuests = numQuests + 1
Nenue@33 212 end
Nenue@33 213 qprint('|cFFFF4400END of', GetMapNameByID(mapID))
Nenue@33 214 end
Nenue@33 215 end
Nenue@33 216
Nenue@33 217 if not self.fullSearch then
Nenue@33 218 print(' hasUpdate:', self.isStale, 'isPending:', self.isPending, 'timer:', (self.OnNext and 'waiting' or ''))
Nenue@33 219 --WorldPlan.isStale = (self.isStale or WorldPlan.isStale)
Nenue@33 220 end
Nenue@33 221
Nenue@33 222 end
Nenue@33 223
Nenue@35 224 function WorldQuests:Refresh(forced)
Nenue@33 225 local print = wqprint
Nenue@33 226 print('|cFF00FF88'..self:GetName()..':Refresh()|r')
Nenue@34 227 if not self:IsVisible() then
Nenue@34 228 self.isStale = true
Nenue@34 229 print('frame closed, do it later')
Nenue@34 230 return
Nenue@34 231 end
Nenue@34 232
Nenue@33 233 self:Reset()
Nenue@33 234 self:UpdateAnchors()
Nenue@33 235 self:Cleanup ()
Nenue@33 236 end
Nenue@33 237
Nenue@33 238 -- prepares elements for a map update
Nenue@33 239 function WorldQuests:Reset ()
Nenue@33 240 local print = wqprint
Nenue@33 241 print('|cFF00FF88'..self:GetName()..':Reset()|r')
Nenue@33 242 for questID, pin in pairs(self.QuestsByID) do
Nenue@33 243 pin.used = nil
Nenue@33 244 end
Nenue@33 245 end
Nenue@33 246
Nenue@33 247 -- update visibility states of all pins
Nenue@33 248 function WorldQuests:UpdateAnchors (submapID)
Nenue@33 249
Nenue@33 250 local print = wqprint
Nenue@33 251 local db = WorldPlan.db
Nenue@33 252 local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo()
Nenue@33 253 if isMicroDungeon then
Nenue@33 254 return
Nenue@33 255 end
Nenue@33 256
Nenue@33 257 local currentMap = GetCurrentMapAreaID()
Nenue@33 258 local submapID = submapID or currentMap
Nenue@33 259
Nenue@33 260 if submapID == BROKEN_ISLES_ID and (not db.DisplayContinentPins) then
Nenue@33 261 print('not updating map for reasons')
Nenue@33 262 return
Nenue@33 263 end
Nenue@33 264 print('|cFF88FF00'..self:GetName()..':UpdateAnchors|r', submapID, GetMapNameByID(submapID), 'pin count:', numPins)
Nenue@33 265 local numZones = MC_GetNumZones(submapID)
Nenue@33 266 if numZones then
Nenue@33 267 for i = 1, numZones do
Nenue@33 268 local subMapID = MC_GetZoneInfo(submapID, i)
Nenue@33 269 self:UpdateAnchors(subMapID)
Nenue@33 270 end
Nenue@33 271 end
Nenue@33 272 local pins = self.QuestsByZone[submapID]
Nenue@33 273
Nenue@33 274 if pins then
Nenue@33 275 local hostFrame = WorldMapPOIFrame
Nenue@33 276 local mapWidth, mapHeight = hostFrame:GetSize()
Nenue@33 277 for questID, pin in pairs(pins) do
Nenue@33 278 pin:IsShowable()
Nenue@33 279 if pin.used then
Nenue@35 280 pin.isStale = true
Nenue@35 281 pin:SetFrameLevel(PinBaseIndex+ numPins)
Nenue@35 282 print('level', PinBaseIndex+ numPins)
Nenue@34 283 pin:SetAnchor(_G.WorldMapPOIFrame, currentMap, mapWidth, mapHeight)
Nenue@33 284 numPins = numPins + 1
Nenue@33 285 end
Nenue@33 286 end
Nenue@33 287 end
Nenue@33 288 end
Nenue@33 289
Nenue@33 290 -- shows, animates, or hides pins based on their current visibility flags
Nenue@33 291 local debug_show = {}
Nenue@33 292 local debug_animate = {}
Nenue@33 293 local debug_hide = {}
Nenue@33 294 function WorldQuests:Cleanup ()
Nenue@33 295 local print = wqprint
Nenue@34 296 local showQuestPOI = WorldPlan.db.EnablePins
Nenue@35 297 print('|cFFFFFF00'..self:GetName()..':Cleanup()|r')
Nenue@34 298 -- continent or zone sizing
Nenue@33 299
Nenue@33 300
Nenue@33 301 numPins = 0
Nenue@33 302 for questID, pin in pairs(self.QuestsByID) do
Nenue@34 303 pin:SetShown((showQuestPOI and pin.used))
Nenue@33 304 end
Nenue@33 305 self.isStale = nil
Nenue@33 306 end
Nenue@33 307
Nenue@33 308 function WorldQuests:FilterCheckByID(questID)
Nenue@33 309 local pin = WorldQuests:GetPinByQuestID(questID)
Nenue@33 310 return pin:IsShowable()
Nenue@33 311 end
Nenue@35 312
Nenue@35 313