annotate WorldPlan.lua @ 34:0100d923d8c3

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