annotate WorldPlan.lua @ 36:21bcff08b0f4

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 01:40:39 -0400
parents 26dfa661daa7
children 589c444d4837
rev   line source
Nenue@0 1 -- WorldPlan.lua
Nenue@0 2 -- Created: 8/16/2016 8:19 AM
Nenue@0 3 -- %file-revision%
Nenue@0 4
Nenue@29 5 WorldPlanCore = {
Nenue@29 6 defaults = {},
Nenue@30 7 modules = {},
Nenue@33 8 FilterOptions = {},
Nenue@33 9 UsedFilters = {},
Nenue@30 10 QuestsByZone = {},
Nenue@30 11 QuestsByID = {},
Nenue@33 12 TaskQueue = {},
Nenue@29 13 }
Nenue@36 14 WorldPlanQuestsMixin = {
Nenue@36 15 QuestsByZone = {},
Nenue@36 16 QuestsByID = {},
Nenue@36 17 freePins = {},
Nenue@36 18 }
Nenue@35 19 WorldPlanPOIMixin = {}
Nenue@33 20 local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
Nenue@0 21 local WP_VERSION = "1.0"
Nenue@33 22 local tinsert, pairs, floor = table.insert, pairs, floor
Nenue@0 23 local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS
Nenue@33 24 local BROKEN_ISLES_ID = 1007
Nenue@0 25 local GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID = GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID
Nenue@0 26
Nenue@0 27 -- default color templates
Nenue@29 28 local DEFAULT_TYPE = {
Nenue@0 29 a = 1,
Nenue@0 30 r = 1, g = 1, b = 1,
Nenue@0 31 x = 0, y = 0,
Nenue@0 32 desaturated = true,
Nenue@33 33 pinMask = "Interface\\Minimap\\UI-Minimap-Background",
Nenue@33 34 rewardMask = "Interface\\Minimap\\UI-Minimap-Background",
Nenue@33 35 texture = "Interface\\BUTTONS\\YELLOWORANGE64",
Nenue@0 36 continent = {
Nenue@30 37 PinSize = 14,
Nenue@30 38 Border = 2,
Nenue@36 39 highlightWidth = 1,
Nenue@0 40 TagSize = 6,
Nenue@36 41 maxAlertLevel = 0,
Nenue@27 42 showNumber = true,
Nenue@30 43 numberFontObject = 'WorldPlanFont'
Nenue@0 44 },
Nenue@0 45 zone = {
Nenue@36 46 iconWidth = 22,
Nenue@36 47 borderWidth = 3,
Nenue@36 48 highlightWidth = 2,
Nenue@0 49 TagSize = 12,
Nenue@36 50 maxAlertLevel = 3,
Nenue@27 51 showNumber = true,
Nenue@30 52 numberFontObject = 'WorldPlanNumberFontThin'
Nenue@0 53 },
Nenue@0 54 minimized = {
Nenue@36 55 iconWidth = 6,
Nenue@36 56 borderWidth = 0,
Nenue@36 57 highlightWidth = 0,
Nenue@36 58 maxAlertLevel = 1,
Nenue@0 59 NoIcon = true,
Nenue@0 60 TimeleftStage = 1,
Nenue@27 61 showNumber = false,
Nenue@0 62 }
Nenue@0 63 }
Nenue@29 64
Nenue@29 65
Nenue@0 66
Nenue@9 67
Nenue@9 68 local defaults = {
Nenue@27 69 ShowAllProfessionQuests = false,
Nenue@9 70 DisplayContinentSummary = true,
Nenue@9 71 DisplayContinentPins = true,
Nenue@9 72 NotifyWhenNewQuests = true,
Nenue@9 73 EnablePins = true,
Nenue@34 74 FadeWhileGrouped = false,
Nenue@9 75 }
Nenue@9 76
Nenue@9 77 -- operating flags
Nenue@9 78 local superTrackedID
Nenue@9 79 local currentMapName
Nenue@9 80 local hasNewQuestPins
Nenue@9 81 local isContinentMap
Nenue@0 82 local hasPendingQuestData
Nenue@0 83 local notifyPlayed
Nenue@0 84 local scanner, wmtt, WorldMapPOIFrame
Nenue@0 85
Nenue@0 86
Nenue@0 87 -- tracking menu toggler
Nenue@0 88 local DropDown_OnClick = function(self)
Nenue@0 89 local key = self.value
Nenue@0 90 if key then
Nenue@0 91 if WorldPlanData[key] then
Nenue@0 92 WorldPlanData[key] = nil
Nenue@0 93 else
Nenue@0 94 WorldPlanData[key] = true
Nenue@0 95 end
Nenue@0 96 end
Nenue@33 97 _G.WorldPlan:Refresh()
Nenue@0 98 end
Nenue@0 99
Nenue@36 100 function WorldPlanCore:print(...)
Nenue@0 101 local msg
Nenue@0 102 for i = 1, select('#', ...) do
Nenue@0 103 msg = (msg and (msg .. ' ') or '') .. tostring(select(i, ...))
Nenue@0 104 end
Nenue@0 105 DEFAULT_CHAT_FRAME:AddMessage("|cFF0088FFWorldPlan|r: " .. msg)
Nenue@0 106 end
Nenue@0 107
Nenue@30 108 local current_type_owner
Nenue@36 109 function WorldPlanCore:AddHandler (frame, defaults)
Nenue@30 110 print('|cFFFFFF00'..self:GetName()..':AddHandler()', frame:GetName())
Nenue@30 111 tinsert(self.modules, frame)
Nenue@30 112 self.defaults[frame] = defaults
Nenue@30 113 frame.GetTypeInfo = function(frame, typeID)
Nenue@30 114 return self:GetTypeInfo(frame, typeID)
Nenue@30 115 end
Nenue@30 116 end
Nenue@30 117
Nenue@36 118 function WorldPlanCore:OnLoad ()
Nenue@29 119
Nenue@29 120 self.Types = setmetatable({}, {
Nenue@29 121 __newindex = function(t, k, v)
Nenue@29 122 if type(v) == 'table' then
Nenue@30 123 print('adding owner', k)
Nenue@30 124 v = setmetatable(v, {
Nenue@30 125 __newindex = function(t2,k2,v2)
Nenue@30 126 if type(v2) == 'table' then
Nenue@30 127 print('adding type', k2)
Nenue@30 128 v2 = setmetatable(v2, {__index = function(t3,k3)
Nenue@30 129 --print('##deferring to default key', k3)
Nenue@30 130 return DEFAULT_TYPE[k3]
Nenue@30 131 end})
Nenue@30 132 end
Nenue@30 133 rawset(t2,k2,v2)
Nenue@29 134 end})
Nenue@29 135 end
Nenue@29 136 rawset(t,k,v)
Nenue@29 137 end
Nenue@29 138 })
Nenue@29 139
Nenue@30 140 self.Types[self] = {}
Nenue@29 141
Nenue@29 142 for index, color in pairs(ITEM_QUALITY_COLORS) do
Nenue@30 143 self:AddTypeInfo(self, index, { r = color.r, g = color.g, b = color.b, hex = color.hex, })
Nenue@29 144 end
Nenue@29 145
Nenue@0 146
Nenue@36 147 WorldPlanCore:print('v'..WP_VERSION)
Nenue@0 148
Nenue@0 149 self:RegisterEvent("QUESTLINE_UPDATE")
Nenue@0 150 self:RegisterEvent("QUEST_LOG_UPDATE")
Nenue@0 151 self:RegisterEvent("WORLD_MAP_UPDATE")
Nenue@0 152 self:RegisterEvent("WORLD_QUEST_COMPLETED_BY_SPELL")
Nenue@0 153 self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED")
Nenue@0 154 self:RegisterEvent("SKILL_LINES_CHANGED")
Nenue@0 155 self:RegisterEvent("ARTIFACT_XP_UPDATE")
Nenue@0 156 self:RegisterEvent("ADDON_LOADED")
Nenue@27 157 self:SetParent(WorldMapFrame)
Nenue@0 158 end
Nenue@0 159
Nenue@36 160 function WorldPlanCore:OnShow()
Nenue@27 161 print(self:GetName()..':OnShow()')
Nenue@27 162 if self.isStale then
Nenue@30 163 self:Refresh()
Nenue@27 164 end
Nenue@34 165
Nenue@34 166 hooksecurefunc(self, 'SetScript', function(...) self:print('|cFFFFFF00'..self:GetName()..':SetScript()|r', ...) end)
Nenue@27 167 end
Nenue@27 168
Nenue@36 169 function WorldPlanCore:OnEvent (event, ...)
Nenue@0 170 print()
Nenue@33 171 print(event, 'init:', self.initialized)
Nenue@0 172 if event == 'ADDON_LOADED' then
Nenue@0 173
Nenue@0 174 if IsLoggedIn() and not self.initialized then
Nenue@0 175 self:Setup()
Nenue@0 176 end
Nenue@27 177 else
Nenue@33 178 if event == 'WORLD_MAP_UPDATE' then
Nenue@33 179 self.currentMapID = GetCurrentMapAreaID()
Nenue@33 180 self.isContinentMap = (self.currentMapID == BROKEN_ISLES_ID)
Nenue@35 181 --self:print('|cFFFF4400currentMapID =', self.currentMapID)
Nenue@35 182 self.isStale = true
Nenue@33 183 end
Nenue@33 184
Nenue@27 185 for i, module in ipairs(self.modules) do
Nenue@27 186 if module.OnEvent then
Nenue@33 187 print(' |cFF0088FF'..module:GetName() .. ':OnEvent()|r')
Nenue@27 188 module:OnEvent(event, ...)
Nenue@27 189 end
Nenue@0 190 end
Nenue@0 191 end
Nenue@0 192 end
Nenue@0 193
Nenue@33 194 function WorldPlanCore:OnNext(func)
Nenue@34 195
Nenue@34 196
Nenue@33 197 tinsert(self.TaskQueue, func)
Nenue@35 198 --self:print('|cFF00FF00adding scheduled task #', #self.TaskQueue)
Nenue@33 199 end
Nenue@33 200
Nenue@33 201 function WorldPlanCore:OnUpdate()
Nenue@33 202 if #self.TaskQueue >= 1 then
Nenue@34 203 local func = tremove(self.TaskQueue, 1)
Nenue@34 204 --self:print('|cFF00FF00running scheduled task #', #self.TaskQueue)
Nenue@33 205 func()
Nenue@33 206 end
Nenue@33 207
Nenue@33 208 if self.isStale then
Nenue@33 209 print('|cFF00FF00pushing global update')
Nenue@33 210 self.isStale = nil
Nenue@33 211 self:Refresh()
Nenue@33 212 else
Nenue@33 213 for i, module in ipairs(self.modules) do
Nenue@33 214 if module.isStale then
Nenue@33 215 print('|cFF00FF00internal '..module:GetName()..':Refresh()|r')
Nenue@33 216 module:Refresh()
Nenue@33 217 end
Nenue@33 218 end
Nenue@33 219 end
Nenue@33 220 end
Nenue@0 221
Nenue@36 222 function WorldPlanCore:Setup ()
Nenue@0 223 if not WorldPlanData then
Nenue@0 224 WorldPlanData = {key = 0 }
Nenue@0 225 end
Nenue@0 226 WorldPlanData.key = (WorldPlanData.key or 0) + 1
Nenue@0 227 self.db = WorldPlanData
Nenue@9 228 self.db.WorldQuests = self.db.WorldQuests or {}
Nenue@9 229 db = self.db
Nenue@9 230 for k,v in pairs(defaults) do
Nenue@18 231 --[===[@non-debug@
Nenue@18 232 if not db[k] then
Nenue@18 233 db[k] = v
Nenue@18 234 end
Nenue@18 235
Nenue@18 236 --@end-non-debug@]===]
Nenue@18 237 --@debug@
Nenue@9 238 db[k] = v
Nenue@18 239 --@end-debug@
Nenue@9 240 end
Nenue@9 241
Nenue@9 242 self.currentMapID = GetCurrentMapAreaID()
Nenue@0 243
Nenue@0 244 for i, module in ipairs(self.modules) do
Nenue@9 245 module.db = self.db
Nenue@0 246 if module.Setup then module:Setup() end
Nenue@0 247 if not module.RegisterEvent then
Nenue@0 248 module.RegisterEvent = self.RegisterEvent
Nenue@0 249 end
Nenue@0 250 end
Nenue@0 251 self.initialized = true
Nenue@0 252
Nenue@9 253 hooksecurefunc("UIDropDownMenu_Initialize", self.OnDropDownInitialize)
Nenue@33 254
Nenue@33 255 hooksecurefunc("WorldMapTrackingOptionsDropDown_OnClick", function(button)
Nenue@33 256 print("|cFF0088FFWorldMapTrackingOptionsDropDown_OnClick|r")
Nenue@33 257 local value = button.value
Nenue@33 258 if (value == "worldQuestFilterOrderResources" or value == "worldQuestFilterArtifactPower" or
Nenue@33 259 value == "worldQuestFilterProfessionMaterials" or value == "worldQuestFilterGold" or
Nenue@33 260 value == "worldQuestFilterEquipment") then
Nenue@33 261 self:Refresh(true)
Nenue@33 262 end
Nenue@33 263 end)
Nenue@0 264 end
Nenue@0 265
Nenue@36 266 function WorldPlanCore:AddTypeInfo(owner, id, info)
Nenue@30 267 self.Types[owner] = self.Types[owner] or {}
Nenue@30 268 self.Types[owner][id] = info
Nenue@30 269 print('Type('..owner:GetName()..')('..id..') = '.. tostring(info))
Nenue@30 270 end
Nenue@30 271
Nenue@36 272 function WorldPlanCore:GetTypeInfo(owner, typeID)
Nenue@29 273 local info, extraInfo
Nenue@30 274 if not owner then
Nenue@30 275 --print('## deferring to default type list')
Nenue@30 276 else
Nenue@30 277 --print('## pulling for', owner:GetName(), 'id =', typeID)
Nenue@30 278 end
Nenue@30 279
Nenue@30 280 owner = owner or self
Nenue@30 281 if (not typeID) or (not self.Types[owner][typeID]) then
Nenue@30 282 --print('## sending list default')
Nenue@29 283 info = DEFAULT_TYPE
Nenue@29 284 else
Nenue@30 285 --print('## sent list definition', typeID)
Nenue@30 286 info = self.Types[owner][typeID]
Nenue@29 287 end
Nenue@29 288
Nenue@29 289 if isContinentMap then
Nenue@29 290 extraInfo = info.continent
Nenue@30 291 --print('### continent subtype', extraInfo)
Nenue@29 292 else
Nenue@29 293 extraInfo = info.zone
Nenue@29 294
Nenue@30 295 --print('### zone subtype', extraInfo)
Nenue@29 296 end
Nenue@29 297 return info, extraInfo
Nenue@29 298 end
Nenue@29 299
Nenue@29 300 do
Nenue@29 301 local timeStates = {
Nenue@29 302 {maxSeconds = 60,
Nenue@29 303 r=1, g=0.25, b =0, format = function (minutes) return '|cFFFF4400'.. minutes .. 'm' end,
Nenue@29 304 },
Nenue@29 305 {maxSeconds = 240,
Nenue@29 306 r=1, g=.5, b=0, format = function(minutes) return '|cFFFF4400'.. floor(minutes/60) .. 'h' end,
Nenue@29 307 },
Nenue@29 308 {maxSeconds = 1440,
Nenue@29 309 r=1, g=1, b=0, format = function(minutes) return '|cFFFFFF00'.. floor(minutes/60) .. 'h' end,
Nenue@29 310 },
Nenue@29 311 {maxSeconds = 10081,
Nenue@29 312 r=0, g=1, b=0,
Nenue@29 313 }, -- 7 days + 1 minute
Nenue@29 314 }
Nenue@29 315 -- Generates a timeleft string
Nenue@36 316 function WorldPlanCore:GetTimeInfo(timeLeft, limit)
Nenue@29 317 limit = limit or #timeStates
Nenue@29 318 for index = 1, limit do
Nenue@29 319 local state = timeStates[index]
Nenue@29 320 if timeLeft <= state.maxSeconds then
Nenue@29 321 local text
Nenue@29 322 if state.format then
Nenue@29 323 text = state.format(timeLeft)
Nenue@29 324 end
Nenue@29 325 return text, index
Nenue@29 326 end
Nenue@29 327 end
Nenue@29 328 return nil, nil
Nenue@29 329 end
Nenue@29 330 end
Nenue@29 331
Nenue@36 332 function WorldPlanCore:Refresh (forced)
Nenue@30 333 print('|cFFFFFF00'..self:GetName()..':Refresh()|r forced:', forced, 'init:', self.initialized)
Nenue@9 334 if not self.initialized then
Nenue@9 335 return
Nenue@9 336 end
Nenue@9 337
Nenue@9 338 for i, module in ipairs(self.modules) do
Nenue@0 339 if module.Refresh then
Nenue@33 340 print('|cFF00FF00external '..module:GetName()..':Refresh()|r')
Nenue@33 341 module:Refresh(forced)
Nenue@0 342 end
Nenue@0 343 end
Nenue@0 344 end
Nenue@0 345
Nenue@0 346 -- insert visual options into the tracking button menu
Nenue@36 347 WorldPlanCore.OnDropDownInitialize = function (self, callback, dropType)
Nenue@0 348 if self ~= WorldMapFrameDropDown then
Nenue@0 349 return
Nenue@0 350 end
Nenue@9 351 local db = WorldPlan.db
Nenue@0 352
Nenue@0 353 local info = UIDropDownMenu_CreateInfo()
Nenue@0 354 info.text = ""
Nenue@0 355 info.isTitle = true
Nenue@0 356 UIDropDownMenu_AddButton(info)
Nenue@0 357 info.text = "|cFF00AAFFWorldPlan|r"
Nenue@0 358 info.isTitle = true
Nenue@0 359 UIDropDownMenu_AddButton(info)
Nenue@0 360 info.isTitle = nil
Nenue@0 361 info.disabled = nil
Nenue@0 362 info.keepShownOnClick = true
Nenue@0 363 info.tooltipOnButton = 1
Nenue@0 364
Nenue@9 365 info.text = "Enable"
Nenue@9 366 info.isNotRadio = true
Nenue@9 367 info.value = "EnablePins"
Nenue@9 368 info.checked = db.EnablePins
Nenue@9 369 info.tooltipTitle = "Enable World Quest Overlays"
Nenue@9 370 info.tooltipText = "Toggle the detail layers here."
Nenue@9 371 info.func = DropDown_OnClick
Nenue@9 372 UIDropDownMenu_AddButton(info)
Nenue@9 373
Nenue@9 374 info.text = "Display All Profession Quests"
Nenue@0 375 info.isNotRadio = true
Nenue@0 376 info.value = "ShowAllProfessionQuests"
Nenue@9 377 info.checked = db.ShowAllProfessionQuests
Nenue@0 378 info.tooltipTitle = "Hidden Quests"
Nenue@0 379 info.tooltipText = "Display work order and profession-related quests that are skipped by the default UI."
Nenue@0 380 info.func = DropDown_OnClick
Nenue@0 381 UIDropDownMenu_AddButton(info)
Nenue@0 382
Nenue@0 383 info.text = "Show Continent Pins"
Nenue@0 384 info.isNotRadio = true
Nenue@0 385 info.value = "DisplayContinentPins"
Nenue@9 386 info.checked = db.DisplayContinentPins
Nenue@0 387 info.tooltipTitle = "Continent Pins"
Nenue@0 388 info.tooltipText = "Display quest pins on the continent map (may get cramped)."
Nenue@0 389 info.func = DropDown_OnClick
Nenue@0 390 UIDropDownMenu_AddButton(info)
Nenue@0 391
Nenue@0 392 info.text = "Show Summary"
Nenue@0 393 info.isNotRadio = true
Nenue@0 394 info.value = "DisplayContinentSummary"
Nenue@0 395 info.tooltipTitle = "Summary Bar"
Nenue@0 396 info.tooltipText = "Display a summary of active world quests. Note: requires directly viewing Broken Isle and Dalaran maps to gain complete info."
Nenue@9 397 info.checked = db.DisplayContinentSummary
Nenue@9 398 info.func = DropDown_OnClick
Nenue@9 399 UIDropDownMenu_AddButton(info)
Nenue@9 400
Nenue@9 401 info.text = "Fade In Groups"
Nenue@9 402 info.isNotRadio = true
Nenue@9 403 info.value = "FadeWhileGrouped"
Nenue@9 404 info.tooltipTitle = "Group Fade"
Nenue@9 405 info.tooltipText = "Reduce pin alpha when grouped, so player dots are easier to see."
Nenue@34 406 info.checked = db.FadeWhileGrouped
Nenue@0 407 info.func = DropDown_OnClick
Nenue@0 408 UIDropDownMenu_AddButton(info)
Nenue@0 409 end
Nenue@0 410
Nenue@30 411 --------------------------------------------------------------------------------------------------------------------
Nenue@35 412 -------------------
Nenue@0 413
Nenue@0 414
Nenue@0 415
Nenue@0 416
Nenue@0 417
Nenue@0 418
Nenue@0 419
Nenue@0 420 SLASH_WORLDPLAN1 = "/worldplan"
Nenue@0 421 SLASH_WORLDPLAN2 = "/wp"
Nenue@0 422 SlashCmdList.WORLDPLAN = function()
Nenue@0 423 print('command pop')
Nenue@36 424 WorldPlanCore:GetPinsForMap()
Nenue@36 425 WorldPlanCore:RefreshPins()
Nenue@0 426
Nenue@0 427 SetTimedCallbackForAllPins(0, function(self) self.FadeIn:Play() self.FlashIn:Play() end)
Nenue@0 428 SetTimedCallbackForAllPins(5, function(self) self.PendingFade:Play() end)
Nenue@0 429 SetTimedCallbackForAllPins(8, function(self) self.PendingFade:Stop() end)
Nenue@0 430 end
Nenue@35 431 --%end-debug%