annotate FilterBar.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 be4db60219ca
children 589c444d4837
rev   line source
Nenue@30 1 -- WorldPlan
Nenue@30 2 -- FilterBar.lua
Nenue@30 3 -- Created: 10/27/2016 8:55 PM
Nenue@30 4 -- %file-revision%
Nenue@30 5 --
Nenue@30 6
Nenue@30 7 local print = DEVIAN_WORKSPACE and function(...) _G.print('FilterBar', ...) end or nop
Nenue@33 8 local wprint = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
Nenue@31 9 local wipe, ipairs, pairs = table.wipe, ipairs, pairs
Nenue@30 10
Nenue@30 11 local REWARD_CASH = WORLD_QUEST_REWARD_TYPE_FLAG_GOLD
Nenue@30 12 local REWARD_ARTIFACT_POWER = WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER
Nenue@30 13 local REWARD_GEAR = WORLD_QUEST_REWARD_TYPE_FLAG_EQUIPMENT
Nenue@30 14 local REWARD_CURRENCY = WORLD_QUEST_REWARD_TYPE_FLAG_ORDER_RESOURCES
Nenue@30 15 local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS
Nenue@30 16
Nenue@30 17
Nenue@30 18 local LE_QUEST_TAG_TYPE_PVP = LE_QUEST_TAG_TYPE_PVP
Nenue@30 19 local LE_QUEST_TAG_TYPE_PET_BATTLE = LE_QUEST_TAG_TYPE_PET_BATTLE
Nenue@30 20 local LE_QUEST_TAG_TYPE_DUNGEON = LE_QUEST_TAG_TYPE_DUNGEON
Nenue@30 21 local LE_QUEST_TAG_TYPE_PROFESSION = LE_QUEST_TAG_TYPE_PROFESSION
Nenue@33 22
Nenue@33 23 local familiars = {
Nenue@33 24 [42159] = {npc = 106552, name = 'Nightwatcher Merayl'},
Nenue@33 25 [40277] = {npc = 97804, name = 'Tiffany Nelson'},
Nenue@33 26 [40298] = {npc = 99182, name = 'Sir Galveston'},
Nenue@33 27 [40282] = {npc= 99150, name = 'Grixis Tinypop'},
Nenue@33 28 [40278] = {npc = 98270, name = 'Robert Craig'},
Nenue@33 29 [48195] = {npc = 105250, name = 'Aulier'},
Nenue@33 30 [41990] = {npc = 105674, name = 'Varenne'},
Nenue@33 31 [41860] = {npc = 104970, name = 'Xorvasc'},
Nenue@33 32 [40299] = {npc = 99210, name = 'Bodhi Sunwayver'},
Nenue@33 33 [42442] = {npc = 107489, name = 'Amalia'},
Nenue@33 34 [40280] = {npc = 99077, name = 'Bredda Tenderhide'},
Nenue@33 35 [41687] = {npc = 104553, name = 'Odrogg'},
Nenue@33 36 [41944] = {npc = 105455, name = 'Trapper Jarrun'},
Nenue@33 37 [40337] = {npc = 97709, name = 'Master Tamer Flummox'},
Nenue@33 38 [40279] = {npc = 99035, name = 'Durian Strongfruit'}
Nenue@33 39 }
Nenue@33 40 local familiars_id = 9696
Nenue@33 41
Nenue@30 42 local DEFAULT_FILTER_LAYOUT = {
Nenue@36 43 iconWidth = 24,
Nenue@36 44 borderWidth = 3,
Nenue@36 45 highlightWidth = 2,
Nenue@30 46 TagSize = 12,
Nenue@30 47 TimeleftStage = 3,
Nenue@30 48 showNumber = true,
Nenue@30 49 numberFontObject = 'WorldPlanNumberFontThin'
Nenue@30 50 }
Nenue@30 51 local DEFAULT_FILTER_LIST = {
Nenue@30 52 { label = 'Filters', texture = "Interface\\WorldMap\\WorldMap-Icon" },
Nenue@30 53 { filterKey= 'rewardType', cVar = 'worldQuestFilterArtifactPower', filterValue = REWARD_ARTIFACT_POWER, label = 'Artifact Power', texture = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" },
Nenue@31 54 { filterKey= 'rewardType', cVar = 'worldQuestFilterOrderResources', filterValue = REWARD_CURRENCY,label = 'Order Resources', texture = "Interface\\Icons\\inv_orderhall_orderresources" },
Nenue@30 55 { filterKey= 'rewardType', cVar = 'worldQuestFilterEquipment', filterValue = REWARD_GEAR, label = 'Equipment', texture = "Interface\\ICONS\\garrison_bluearmorupgrade" },
Nenue@30 56 { filterKey= 'rewardType', cVar = 'worldQuestFilterProfessionMaterials', filterValue = REWARD_REAGENT, label = 'Materials', texture = 1417744 },
Nenue@30 57 { filterKey= 'rewardType', cVar = 'worldQuestFilterGold', filterValue = REWARD_CASH, label = 'Gold', texture = "Interface\\Buttons\\UI-GroupLoot-Coin-Up" },
Nenue@30 58 { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PVP, label = 'PvP', texture = "Interface\\Icons\\Ability_PVP_GladiatorMedallion", spacing = 10 },
Nenue@30 59 { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PET_BATTLE, label = 'Pet Battle', texture = "Interface\\Icons\\PetJournalPortrait", },
Nenue@30 60 { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
Nenue@30 61 { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\ICONS\\70_professions_scroll_02", },
Nenue@30 62 }
Nenue@30 63 local defaults = {}
Nenue@30 64
Nenue@30 65 WorldPlanSummaryMixin = {
Nenue@30 66 selectedBountyIndex = {},
Nenue@30 67 bounties = {},
Nenue@30 68 filterList = {},
Nenue@30 69 buttons = {},
Nenue@30 70 }
Nenue@30 71 WorldPlanFilterPinMixin = {}
Nenue@30 72
Nenue@30 73 function WorldPlanSummaryMixin:OnLoad()
Nenue@30 74 WorldPlan:AddHandler(self, defaults)
Nenue@30 75 for index, info in ipairs(DEFAULT_FILTER_LIST) do
Nenue@30 76 info.zone = DEFAULT_FILTER_LAYOUT
Nenue@30 77 info.continent = DEFAULT_FILTER_LAYOUT
Nenue@30 78 info.pinMask = "Interface\\Minimap\\UI-Minimap-Background"
Nenue@30 79
Nenue@30 80 WorldPlan:AddTypeInfo(self,index, info)
Nenue@30 81 end
Nenue@30 82
Nenue@30 83 end
Nenue@30 84
Nenue@30 85
Nenue@30 86 function WorldPlanSummaryMixin:OnEvent(event)
Nenue@33 87 print('|cFF00FF88'..self:GetName()..':OnEvent()', event)
Nenue@33 88 self.isStale = true
Nenue@30 89 end
Nenue@30 90
Nenue@30 91 local bountyIndex
Nenue@30 92 local debug_headers = {}
Nenue@30 93
Nenue@30 94 function WorldPlanSummaryMixin:Setup()
Nenue@33 95 print('|cFF00FF88'..self:GetName()..':Setup()')
Nenue@30 96 self:GetFilters()
Nenue@30 97 end
Nenue@30 98
Nenue@30 99
Nenue@33 100 function WorldPlanSummaryMixin:OnUpdate()
Nenue@33 101 if self.isStale then
Nenue@33 102 wprint('|cFF00FF00pushing update')
Nenue@33 103 self:Refresh()
Nenue@33 104 end
Nenue@30 105 end
Nenue@33 106
Nenue@33 107 function WorldPlanSummaryMixin:OnShow()
Nenue@33 108 print('|cFF00FF88'..self:GetName()..':OnShow()')
Nenue@32 109 if self.isStale then
Nenue@32 110 self:Refresh()
Nenue@32 111 end
Nenue@32 112 end
Nenue@30 113
Nenue@30 114 function WorldPlanSummaryMixin:GetFilters()
Nenue@30 115
Nenue@33 116 print('|cFF00FFFF'..self:GetName()..':GetFilters()')
Nenue@30 117
Nenue@30 118 wipe(self.filterList)
Nenue@30 119
Nenue@30 120 for index, info in ipairs(DEFAULT_FILTER_LIST) do
Nenue@30 121 tinsert(self.filterList, info)
Nenue@30 122 end
Nenue@30 123 self.bounties, self.numBounties = GetQuestBountyInfoForMapID(WorldPlan.currentMapID)
Nenue@30 124 self.BountyFilters = {}
Nenue@30 125 for index, data in ipairs(self.bounties) do
Nenue@30 126 local info = self.BountyFilters[index]
Nenue@30 127 if not info then
Nenue@30 128 info = {}
Nenue@30 129 self.BountyFilters[index] = info
Nenue@30 130 end
Nenue@30 131
Nenue@30 132 local questTitle = GetQuestLogTitle(GetQuestLogIndexByID(data.questID))
Nenue@30 133
Nenue@30 134 info.filterKey = 'factionID'
Nenue@30 135 info.filterValue = data.factionID
Nenue@30 136 info.label = questTitle
Nenue@30 137 info.texture = data.icon
Nenue@30 138 print('loading emissary', questTitle)
Nenue@30 139
Nenue@30 140 tinsert(self.filterList, info)
Nenue@30 141 --{ filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
Nenue@30 142 end
Nenue@30 143 end
Nenue@30 144
Nenue@30 145 function WorldPlanSummaryMixin:Reset()
Nenue@30 146 self.pinLayout = self:GetTypeInfo(255)
Nenue@30 147 self:GetFilters()
Nenue@30 148 end
Nenue@30 149
Nenue@33 150 function WorldPlanSummaryMixin:Refresh()
Nenue@33 151 self:Update()
Nenue@30 152 end
Nenue@30 153
Nenue@30 154 local filterFill = "Interface\\BUTTONS\\YELLOWORANGE64"
Nenue@30 155 local filterMask = "Interface\\Minimap\\UI-Minimap-Background"
Nenue@31 156
Nenue@31 157 local questResults = {{}}
Nenue@33 158 function WorldPlanSummaryMixin:Update()
Nenue@30 159 local blocks = self.buttons
Nenue@30 160 local relativeFrame = WorldMapFrame.UIElementsFrame.TrackingOptionsButton
Nenue@30 161 local numHeaders = 0
Nenue@33 162 print('|cFF00FF88'..self:GetName()..':Update()|r', 'currentMap=',WorldPlan.currentMapID)
Nenue@30 163
Nenue@36 164 local layout = DEFAULT_FILTER_LAYOUT
Nenue@36 165 local borderWidth = layout.iconWidth + (layout.borderWidth * 2)
Nenue@36 166 local highlightWidth = borderWidth + (layout.highlightWidth * 2)
Nenue@33 167 local quests = WorldPlanQuests.QuestsByZone[WorldPlan.currentMapID] or WorldPlanQuests.QuestsByID
Nenue@31 168 local foundQuests = questResults[1]
Nenue@30 169 for index, info in ipairs(self.filterList) do
Nenue@30 170 local numQuests = 0
Nenue@31 171 local resultIndex = numHeaders + 1
Nenue@31 172 questResults[resultIndex] = questResults[resultIndex] or {}
Nenue@31 173 wipe(questResults[resultIndex])
Nenue@30 174 for questID, pin in pairs(quests) do
Nenue@30 175 if pin.used then
Nenue@30 176 if not info.filterKey then
Nenue@30 177 numQuests = numQuests + 1
Nenue@30 178 elseif pin[info.filterKey] == info.filterValue then
Nenue@30 179 numQuests = numQuests + 1
Nenue@31 180 tinsert(questResults[resultIndex], pin)
Nenue@30 181 end
Nenue@30 182 end
Nenue@30 183 end
Nenue@30 184 print(tostring(index).. ' ("'..tostring(info.label)..'" f('.. tostring(info.filterKey).. '='..tostring(info.filterValue) .. '), '..tostring(numQuests)..')')
Nenue@30 185
Nenue@30 186 if numQuests >= 1 then
Nenue@30 187 numHeaders = numHeaders + 1
Nenue@30 188 local button = blocks[numHeaders]
Nenue@30 189 if not blocks[numHeaders] then
Nenue@30 190 button = CreateFrame('Button', 'WorldPlanFilterButton'..numHeaders, WorldMapScrollFrame, 'WorldPlanFilterPin')
Nenue@36 191 button:SetSize(borderWidth, borderWidth)
Nenue@30 192
Nenue@36 193 button.icon:SetSize(layout.iconWidth, layout.iconWidth)
Nenue@36 194 button.RewardBorder:SetSize(borderWidth, borderWidth)
Nenue@36 195 button.HighlightBorder:SetSize(highlightWidth, highlightWidth)
Nenue@36 196 button.RewardBorder:SetMask(filterMask)
Nenue@36 197 button.RewardBorder:SetDesaturated(true)
Nenue@30 198
Nenue@36 199 button.HighlightBorder:Hide()
Nenue@30 200 blocks[numHeaders] = button
Nenue@30 201 end
Nenue@30 202
Nenue@30 203 button.info = info
Nenue@31 204 button.questList = questResults[resultIndex]
Nenue@30 205 button:SetID(index)
Nenue@36 206 button.spacing = ((relativeFrame.cVar and (not info.cVar)) or (relativeFrame.filterKey ~= info.filterKey)) and 5 or 0
Nenue@30 207 button.relativeFrame = relativeFrame
Nenue@30 208 button:Refresh((numHeaders == 1), numQuests)
Nenue@30 209 button:Show()
Nenue@30 210 relativeFrame = button
Nenue@30 211 end
Nenue@30 212
Nenue@30 213 end
Nenue@30 214
Nenue@30 215 self.numHeaders = numHeaders
Nenue@30 216 for i = numHeaders + 1, #blocks do
Nenue@30 217 if blocks[i] then
Nenue@30 218 blocks[i]:Hide()
Nenue@30 219 end
Nenue@31 220 if questResults[i] then
Nenue@31 221 wipe(questResults[i])
Nenue@31 222 end
Nenue@30 223 end
Nenue@32 224 self.isStale = nil
Nenue@30 225 end
Nenue@30 226
Nenue@30 227 function WorldPlanSummaryMixin:Cleanup()
Nenue@30 228 -- hide trailing buttons
Nenue@30 229 end
Nenue@30 230
Nenue@31 231 local rgbWhite = {r = 1, g= 1, b= 1, hex = '|cFFFFFFFF'}
Nenue@30 232 function WorldPlanFilterPinMixin:OnEnter()
Nenue@31 233 if self.questList and #self.questList >= 1 then
Nenue@31 234 GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
Nenue@31 235 GameTooltip:AddLine(self.info.label)
Nenue@31 236 for index, pin in ipairs(self.questList) do
Nenue@31 237 local colorInfo = (pin.quality and ITEM_QUALITY_COLORS[pin.quality]) or rgbWhite
Nenue@36 238 GameTooltip:AddLine('|T'.. tostring(pin.itemTexture)..':16:16|t ' .. pin.title ..(pin.cheevos and " |cFFFFFF00!|R" or ''), colorInfo.r, colorInfo.g, colorInfo.b)
Nenue@31 239 end
Nenue@36 240 GameTooltip:Show()
Nenue@31 241 end
Nenue@30 242 end
Nenue@30 243
Nenue@31 244 function WorldPlanFilterPinMixin:OnLeave()
Nenue@31 245 if GameTooltip:IsOwned(self) then
Nenue@31 246 GameTooltip:Hide()
Nenue@31 247 end
Nenue@31 248 end
Nenue@30 249
Nenue@30 250 function WorldPlanFilterPinMixin:Refresh(isFirst, numQuests)
Nenue@30 251 isFirst = isFirst or self.isFirst
Nenue@30 252 numQuests = numQuests or self.numQuests
Nenue@30 253
Nenue@30 254 local info = self.info
Nenue@30 255
Nenue@30 256 self.isFirst = isFirst
Nenue@30 257 self.numQuests = numQuests
Nenue@30 258 self.filterKey = info.filterKey
Nenue@30 259 self.filterValue = info.filterValue
Nenue@30 260 self.tagID = info.tagID
Nenue@30 261
Nenue@30 262 self.icon:SetMask(filterMask)
Nenue@30 263 self.icon:SetTexture(info.texture)
Nenue@30 264 self.count:SetText(numQuests)
Nenue@30 265 self.cVar = info.cVar
Nenue@30 266
Nenue@30 267 self.itemTexture = self.texture
Nenue@30 268
Nenue@30 269 if isFirst then
Nenue@30 270 self:SetPoint('TOP', self.relativeFrame, 'BOTTOM', 0, -5)
Nenue@30 271 else
Nenue@36 272 self:SetPoint('TOPRIGHT', self.relativeFrame, 'BOTTOMRIGHT', 0, -(self.spacing or 0))
Nenue@30 273 end
Nenue@30 274 print('anchor to', self.relativeFrame:GetName(), info.mask)
Nenue@30 275
Nenue@30 276 local r, g, b, a = 1,1,1,1
Nenue@31 277 local desaturated = false
Nenue@31 278 if self.cVar then
Nenue@36 279 self.RewardBorder:SetVertexColor(1, 1, 1, 1)
Nenue@33 280 if GetCVarBool(self.cVar) then
Nenue@31 281 self.icon:SetVertexColor(1,1,1,1)
Nenue@31 282 self:SetAlpha(1)
Nenue@31 283 else
Nenue@31 284 self.icon:SetVertexColor(.5, .5, .5, 1)
Nenue@31 285 self:SetAlpha(0.5)
Nenue@30 286 end
Nenue@30 287 else
Nenue@31 288 self:SetAlpha(1)
Nenue@31 289 if WorldPlan.UsedFilters[self.filterKey] then
Nenue@31 290 if WorldPlan.UsedFilters[self.filterKey] == self.filterValue then
Nenue@36 291 self.RewardBorder:SetVertexColor(0, 1, 0)
Nenue@31 292 else
Nenue@36 293 self.RewardBorder:SetVertexColor(1, 0, 0)
Nenue@31 294 end
Nenue@31 295 else
Nenue@36 296 if self.filterKey == 'worldQuestType' then
Nenue@36 297 self.RewardBorder:SetVertexColor(0, 1, 1)
Nenue@36 298 elseif self.filterKey == 'factionID' then
Nenue@36 299 self.RewardBorder:SetVertexColor(1, 1, 0)
Nenue@36 300 else
Nenue@36 301
Nenue@36 302 self.RewardBorder:SetVertexColor(0.5, 0.5, 0.5)
Nenue@36 303 end
Nenue@36 304
Nenue@30 305 end
Nenue@30 306 end
Nenue@31 307
Nenue@30 308 --self:UpdateSize()
Nenue@30 309 end
Nenue@30 310
Nenue@30 311 function WorldPlanFilterPinMixin:OnLoad()
Nenue@30 312 self:RegisterForClicks('AnyUp')
Nenue@30 313 self:SetFrameStrata('HIGH')
Nenue@30 314 self:SetFrameLevel(151)
Nenue@30 315 self:SetScript('OnUpdate', nil)
Nenue@36 316 WorldPlanPOIMixin.OnLoad(self)
Nenue@30 317 end
Nenue@30 318
Nenue@30 319 function WorldPlanFilterPinMixin:OnUpdate ()
Nenue@30 320 end
Nenue@30 321
Nenue@33 322
Nenue@30 323 function WorldPlanFilterPinMixin:OnLeave ()
Nenue@30 324 if GameTooltip:IsOwned(self) then
Nenue@30 325 GameTooltip:Hide()
Nenue@30 326 end
Nenue@30 327 end
Nenue@30 328
Nenue@30 329 -- shift-click: reset filter
Nenue@30 330 -- click: rotate through include(1), exclude(-1), ignore(nil)
Nenue@36 331 local filtered_report = {}
Nenue@30 332 function WorldPlanFilterPinMixin:OnClick (button)
Nenue@30 333
Nenue@31 334 print('|cFF00FF88'..self:GetName()..':OnClick()|r', filterKey, filterValue, operation)
Nenue@30 335 local filterKey = self.filterKey
Nenue@30 336 local filterValue = self.filterValue
Nenue@36 337 local cVar = self.cVar
Nenue@36 338 local parent = self:GetParent()
Nenue@36 339 local operation = opPrefix
Nenue@36 340 local setDirty = false
Nenue@30 341
Nenue@36 342 local resetMode = (button == 'RightButton')
Nenue@36 343 wipe(filtered_report)
Nenue@36 344 if not (filterKey or cVar) then
Nenue@30 345 wipe(WorldPlan.UsedFilters)
Nenue@33 346 for i, info in ipairs(DEFAULT_FILTER_LIST) do
Nenue@33 347 if info.cVar then
Nenue@36 348 if GetCVar(info.cVar) ~= 1 then
Nenue@36 349 tinsert(filtered_report, '|cFF888888'.. tostring(info.label) ..'|r')
Nenue@36 350 end
Nenue@33 351 SetCVar(info.cVar, 1)
Nenue@33 352 end
Nenue@33 353 end
Nenue@33 354
Nenue@36 355 elseif cVar then
Nenue@36 356 WorldPlan:print('Toggling cvar filter:', cVar)
Nenue@36 357 if (not parent.isDirty) or resetMode then
Nenue@33 358 for i, info in ipairs(DEFAULT_FILTER_LIST) do
Nenue@33 359 if info.cVar then
Nenue@36 360 local value = GetCVar(info.cVar)
Nenue@36 361 if resetMode then
Nenue@36 362 value = 1
Nenue@36 363 parent.isDirty = nil
Nenue@33 364 else
Nenue@36 365
Nenue@36 366 if (cVar ~= info.cVar) then
Nenue@36 367 value = 0
Nenue@36 368 else
Nenue@36 369 value = 1
Nenue@36 370 end
Nenue@36 371 setDirty = true
Nenue@36 372
Nenue@33 373 end
Nenue@36 374 SetCVar(info.cVar, value)
Nenue@33 375 end
Nenue@33 376 end
Nenue@36 377 if setDirty then
Nenue@36 378 parent.isDirty = true
Nenue@36 379 end
Nenue@36 380 else
Nenue@36 381 SetCVar(cVar, (GetCVarBool(cVar) and 0) or 1)
Nenue@36 382 end
Nenue@30 383 else
Nenue@31 384 local flushValue
Nenue@31 385 print('')
Nenue@31 386 if WorldPlan.UsedFilters[filterKey] == filterValue then
Nenue@31 387 WorldPlan.UsedFilters[filterKey] = nil
Nenue@36 388 tinsert(filtered_report, '|cFFFF0000'.. tostring(filterKey) ..'|r')
Nenue@31 389 else
Nenue@31 390 WorldPlan.UsedFilters[filterKey] = filterValue
Nenue@36 391 tinsert(filtered_report, '|cFF00FF00'.. tostring(filterKey) ..'|r')
Nenue@30 392 end
Nenue@30 393 end
Nenue@36 394 WorldPlan:print('Changed:', table.concat(filtered_report, ', '))
Nenue@33 395 WorldPlan:Refresh(true)
Nenue@30 396 end