annotate WorldPlan.lua @ 35:26dfa661daa7

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