view WorldPlan.lua @ 5:4e1883842abf

- use of SetShown() for combat visibility toggle
author Nenue
date Tue, 18 Oct 2016 01:50:06 -0400
parents 232617b8bcd5
children 34d9fbf7af20
line wrap: on
line source
-- Veneer
-- WorldPlan.lua
-- Created: 8/16/2016 8:19 AM
-- %file-revision%
--[[
 Summary:
 Adds reward icons to the world quest POI markers, and adds said markers to the continent map.

 Issues:
 Dalaran quests aren't visible until that map has been specifically viewed by the player.
--]]

WorldPlanCore = {}
WorldPlanPOIMixin = {}
WorldPlanFilterPinMixin = {}
local WorldPlanFlightMapMixin = setmetatable({}, {__tostring = function() return 'FlightMapHandler' end})
local WorldQuests = setmetatable({ QuestsByID = {}, freePins = {} }, {__tostring = function() return 'QuestHandler' end})
local FilterBar = setmetatable({ SummaryHeaders = {} },  {__tostring = function() return 'FilterBar' end})

local WorldPlan = WorldPlanCore
local QuestPOI = WorldPlanPOIMixin
local FilterPin = WorldPlanFilterPinMixin
local WP_VERSION = "1.0"

local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
local wipe, tremove, tinsert, pairs, floor, tContains = table.wipe, table.remove, table.insert, pairs, floor, tContains
local TQ_GetQuestInfoByQuestID = C_TaskQuest.GetQuestInfoByQuestID -- Return the name of a quest with a given ID
local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented
local TQ_GetQuestTimeLeftMinutes = C_TaskQuest.GetQuestTimeLeftMinutes
local TQ_RequestPreloadRewardData = C_TaskQuest.RequestPreloadRewardData
local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation
local TQ_IsActive = C_TaskQuest.IsActive
local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS
local WorldMap_DoesWorldQuestInfoPassFilters = WorldMap_DoesWorldQuestInfoPassFilters
local QuestMapFrame_IsQuestWorldQuest = QuestMapFrame_IsQuestWorldQuest
local GameTooltip = GameTooltip
local GetItemIcon = GetItemIcon


local GetMapInfo, QuestPOIGetIconInfo = GetMapInfo, QuestPOIGetIconInfo
local GetQuestTagInfo, HaveQuestData =  GetQuestTagInfo, HaveQuestData
local GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney = GetNumQuestLogRewards, GetNumQuestLogRewardCurrencies, GetQuestLogRewardMoney
local GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString = GetQuestLogRewardInfo, GetQuestLogRewardCurrencyInfo, GetMoneyString

local GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID = GetCurrentMapAreaID, GetMapNameByID, GetSuperTrackedQuestID
local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo

local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end
local iprint = DEVIAN_WORKSPACE and function(...) _G.print('ItemScan', ...) end or function() end

local SearchFaction
local PinBaseIndex = 1600
local ShowAllProfessionQuests = false
local DisplayContinentSummary = true
local DisplayContinentPins = true
local NotifyWhenNewQuests = true
local PinRewardFilter, PinTagFilter
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

-- maps where we do our own anchors
local CONTINENT_MAPS = { [BROKEN_ISLES_ID] = BROKEN_ISLES_ID, }
local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70',  [AZSUNA_ID] = 'Azsuna',  [VALSHARAH_ID] = "Val'sharah",
  [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim',  [SURAMAR_ID] = 'Suramar',  [EOA_ID] = 'EyeOfAszhara', }
-- default color templates
local ARTIFACT_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_ARTIFACT]
local MONEY_COLOR = {hex ='|cFFFFFF00', r=1, g=1, b=0}
local COMMON_COLOR = ITEM_QUALITY_COLORS[LE_ITEM_QUALITY_COMMON]

-- operating flags
local superTrackedID
local currentMapName
local hasNewQuestPins
local isContinentMap

local ICON_UNKNOWN = "Interface\\ICONS\\inv_misc_questionmark"
local ICON_MONEY = "Interface\\Buttons\\UI-GroupLoot-Coin-Up"

local POI_BORDER_MASK = "Interface\\Minimap\\UI-Minimap-Background"
local POI_BORDER_FILL = "Interface\\BUTTONS\\YELLOWORANGE64"
local POI_BORDER_BLUE = "Interface\\BUTTONS\\GRADBLUE"
local POI_BORDER_RED = "Interface\\BUTTONS\\RedGrad64"
local POI_BORDER_YELLOW = "Interface\\BUTTONS\\YELLOWORANGE64"
local POI_BORDER_GREEN = "Interface\\BUTTONS\\GREENGRAD64"

local REWARD_CASH = 1001
local REWARD_ARTIFACT_POWER = 1002
local REWARD_GEAR = 1003
local REWARD_CURRENCY = 1004
local REWARD_ITEM = 1005
local REWARD_REAGENT = 1006

local POI_DEFAULT_TYPE = {
  a = 1,
  r = 1, g = 1, b = 1,
  x = 0, y = 0,
  desaturated = true,
  mask = POI_BORDER_MASK,
  texture = POI_BORDER_FILL,
  continent = {
    PinSize = 18,
    Border = 3,
    TrackingBorder = 2,
    TagSize = 6,
    TimeleftStage = 3,
  },
  zone = {
    PinSize = 22,
    Border = 3,
    TrackingBorder = 2,
    TagSize = 12,
    TimeleftStage = 3,
  },
  minimized = {
    PinSize = 4,
    Border = 1,
    TrackingBorder = 2,
    NoIcon = true,
    TimeleftStage = 1,
  }
}
local POI_REWARD_TYPE =  setmetatable({}, {
  __newindex = function(t, k, v)
    if type(v) == 'table' then
      setmetatable(v, {__index = POI_DEFAULT_TYPE})
    end
    rawset(t,k,v)
  end
})
local POI_FILTER_STYLE = setmetatable({
  continentBorder = 2,
  zoneBorder = 2,
}, {__index = POI_DEFAULT_TYPE})

local LE_QUEST_TAG_TYPE_PVP = LE_QUEST_TAG_TYPE_PVP
local LE_QUEST_TAG_TYPE_PET_BATTLE = LE_QUEST_TAG_TYPE_PET_BATTLE
local LE_QUEST_TAG_TYPE_DUNGEON = LE_QUEST_TAG_TYPE_DUNGEON
local LE_QUEST_TAG_TYPE_PROFESSION = LE_QUEST_TAG_TYPE_PROFESSION
local LE_QUEST_TAG_TYPE_NORMAL = LE_QUEST_TAG_TYPE_NORMAL

-- Pin color/display variables
POI_REWARD_TYPE[REWARD_ITEM] = {
  r = 1, g = 1, b = 1,
}
POI_REWARD_TYPE[REWARD_REAGENT] = {
  r = 0, g = 1, b = 1,
}
POI_REWARD_TYPE[REWARD_ARTIFACT_POWER] = {
  r = 1, g = .25, b = .5,
  showNumber = true,
}
POI_REWARD_TYPE[REWARD_GEAR] = {
  r = .1, g = .2, b = 1,
}
POI_REWARD_TYPE[REWARD_CURRENCY] = {
  r = 1, g = 1, b = 0,
}
POI_REWARD_TYPE[REWARD_CASH] = {
  r = 0, g = 0, b = 0,
  --x = 0, y = -1,
  --mask = ICON_MONEY,
  --continentBorder = 1,
  --zoneBorder = 1,
}


-- Summary header structure
local POI_FILTER_OPTIONS = {
  { label = 'Filters', texture = "Interface\\WorldMap\\WorldMap-Icon" },
  { filterKey= 'rewardType', filterValue = REWARD_ARTIFACT_POWER, label = 'Artifact Power', texture = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" },
  { filterKey= 'rewardType', filterValue = REWARD_CURRENCY,label = 'Currency', texture = "Interface\\ICONS\\inv_misc_elvencoins" },
  { filterKey= 'rewardType', filterValue = REWARD_ITEM, label = 'Item', texture = "Interface\\ICONS\\inv_crate_01" },
  { filterKey= 'rewardType', filterValue = REWARD_GEAR, label = 'Equipment', texture = "Interface\\ICONS\\garrison_bluearmorupgrade" },
  { filterKey= 'rewardType', filterValue = REWARD_REAGENT, label = 'Reagents', texture = 1417744 },
  { filterKey= 'rewardType', filterValue = REWARD_CASH, label = 'Reagents', texture = ICON_MONEY },
  { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PVP, label = 'PvP', texture = "Interface\\Icons\\Ability_PVP_GladiatorMedallion", spacing = 10 },
  { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PET_BATTLE, label = 'Pet Battle', texture = "Interface\\Icons\\PetJournalPortrait", },
  { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
}

local PIN_TIME_CONTEXT = {
  {max = 60,
    r=1, g=0.25, b =0, format = function (minutes) return '|cFFFF4400'.. minutes .. 'm' end,
    continentAlpha = 1, swipeTime = 1440,
  },
  {max = 240,
    r=1, g=.5, b=0, format = function(minutes) return '|cFFFF4400'.. floor(minutes/60) .. 'h' end,
    continentAlpha = 1, swipeTime = 1440,
  },
  {max = 1440,
    r=1, g=1, b=0, format = function(minutes) return '|cFFFFFF00'.. floor(minutes/60) .. 'h' end,
    continentAlpha = .55, swipeTime = 1440
  },
  {max = 10081,
    r=0, g=1, b=0,
    continentAlpha = .3,
  }, -- 7 days + 1 minute
}

local numPins = 0
local QuestsByZone = {}
local QuestsByFaction = {}
local QuestsByReward = {}
local QuestsByTag = {}
local QuestsByID = {}
local QuestPositions = {}
local FilterInclusions = {rewardType = {}, worldQuestType = {}}
local FilterExclusions = {rewardType = {}, worldQuestType = {} }
local NotificationTypes = {}
local ZoneInfo = {}
local SummaryHeaders = {}

local FreePins = {}
local NumPinFrames = 1

local hasPendingQuestData
local notifyPlayed
local scanner, wmtt, WorldMapPOIFrame


local tasksQueue = {}
local function OnNext (func)
  if #tasksQueue == 0 then
    _G.WorldPlan:SetScript('OnUpdate', function()
      local func = tremove(tasksQueue, 1)
      if func then
        func()
      end
      if #tasksQueue == 0 then
        _G.WorldPlan:SetScript('OnUpdate', nil)
      end
    end)
  end
  tinsert(tasksQueue, func)
end

-- combines templates
local function DoMixins(frame,...)
  for i = 1, select('#', ...) do
    for k,v in pairs(select(i,...)) do
      frame[k] = v
    end
  end
  return frame
end

-- use tooltip object to extract item details
local ParseItemReward = function(questID)
  local rewardType = REWARD_ITEM
  local name, icon, quantity, quality, _, itemID = GetQuestLogRewardInfo(1, questID)
  if not itemID then
    return REWARD_ITEM
  end

  scanner:SetOwner(WorldPlan, "ANCHOR_NONE")
  scanner:SetItemByID(itemID)
  local ttl1 = _G['WorldPlanTooltipTextLeft1']
  local ttl2 = _G['WorldPlanTooltipTextLeft2']
  local ttl3 = _G['WorldPlanTooltipTextLeft3']
  local ttl4 = _G['WorldPlanTooltipTextLeft4']
  if ttl2 then
    local text = ttl2:GetText()
    -- Artifact Power
    if text and text:match("|cFFE6CC80") then
      --print('AP token!', text)
      local power
      if ttl4 then
        local text = ttl4:GetText()
        --print('tip line 4', text)
        if text then
          power = text:gsub("%p", ""):match("%d+")
          power = tonumber(power)
        end

      end
      rewardType = REWARD_ARTIFACT_POWER
      icon = "Interface\\ICONS\\inv_7xp_inscription_talenttome01"
      quantity = power
    elseif text and text:match("Item Level") then
      --print('equipment!', text)
      rewardType = REWARD_GEAR
      quantity = text:match("Item Level ([%d\+]+)")
    end
  end
  if ttl3 then
    local text = ttl3:GetText()
    -- Crafting Reagent
    if text and text:match("Crafting Reagent") then
      --print('reagent', text)
      rewardType = REWARD_REAGENT
    end
  end
  iprint('  item:', name, rewardType, icon, quantity)
  return rewardType, icon, quantity, name, itemID
end

-- update a masked texture without messing up its blending mask
local SetMaskedTexture = function(region, file, mask)
  mask = mask or POI_BORDER_MASK
  region:SetMask(nil)
  region:SetTexture(file)
  region:SetMask(mask)
end

-- tracking menu toggler
local DropDown_OnClick = function(self)
  local key = self.value
  if key then
    if WorldPlanData[key] then
      WorldPlanData[key] = nil
    else
      WorldPlanData[key] = true
    end
  end
end

function WorldPlan:print(...)
  local msg
  for i = 1, select('#', ...) do
    msg = (msg and (msg .. ' ') or '') .. tostring(select(i, ...))
  end
  DEFAULT_CHAT_FRAME:AddMessage("|cFF0088FFWorldPlan|r: " .. msg)
end

function WorldPlan:OnLoad ()
  WorldPlan = self
  scanner = _G.WorldPlanTooltip
  wmtt = _G.WorldMapTooltip
  WorldMapPOIFrame = _G.WorldMapPOIFrame

  WorldPlan:print('v'..WP_VERSION)

  self:RegisterEvent("QUESTLINE_UPDATE")
  self:RegisterEvent("QUEST_LOG_UPDATE")
  self:RegisterEvent("WORLD_MAP_UPDATE")
  self:RegisterEvent("WORLD_QUEST_COMPLETED_BY_SPELL")
  self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED")
  self:RegisterEvent("SKILL_LINES_CHANGED")
  self:RegisterEvent("ARTIFACT_XP_UPDATE")
  self:RegisterEvent("ADDON_LOADED")

  WorldPlan.modules = {
    WorldQuests, FilterBar, WorldPlanFlightMapMixin,
  }
  hooksecurefunc("UIDropDownMenu_Initialize", self.OnDropDownInitialize)
end

function WorldPlan:OnEvent (event, ...)
  print()
  print(event, ...)
  if event == 'ADDON_LOADED' then
    local addon = ...
    if addon == "Blizzard_FlightMap" then
      print('do mixin junk')
      self.OnFlightMapLoaded()

    end
    if IsLoggedIn() and not self.initialized then
      self:Setup()
    end
  elseif event == 'WORLD_MAP_UPDATE' then
    self:RefreshAll()
  end
  for i, module in ipairs(self.modules) do
    if module.OnEvent then
      print('forwarding to', tostring(module))
      module:OnEvent(event, ...)
    end
  end
end


function WorldPlan:Setup ()
  if not WorldPlanData then
    WorldPlanData = {key = 0 }
  end
  WorldPlanData.key = (WorldPlanData.key or 0) + 1
  self.db = WorldPlanData

  for i, module in ipairs(self.modules) do
    if module.Setup then module:Setup() end
    if not module.RegisterEvent then
      module.RegisterEvent = self.RegisterEvent
    end
  end
  self.initialized = true

end

function WorldPlan:RefreshAll (forced)
  for i, module in ipairs(self.modules) do
    if module.Refresh then
      module:Refresh()
    end
  end
end
function WorldPlan:UpdateAnchors ()
  for i, module in ipairs(self.modules) do
    if module.UpdateAnchors then
      module:UpdateAnchors()
    end
  end
end

-- insert visual options into the tracking button menu
WorldPlan.OnDropDownInitialize = function  (self, callback, dropType)
  if self ~= WorldMapFrameDropDown then
    return
  end

  local info = UIDropDownMenu_CreateInfo()
  info.text = ""
  info.isTitle = true
  UIDropDownMenu_AddButton(info)
  info.text = "|cFF00AAFFWorldPlan|r"
  info.isTitle = true
  UIDropDownMenu_AddButton(info)
  info.isTitle = nil
  info.disabled = nil
  info.keepShownOnClick = true
  info.tooltipOnButton = 1

  info.text = "Hidden World Quests"
  info.isNotRadio = true
  info.value = "ShowAllProfessionQuests"
  info.checked = ShowAllProfessionQuests
  info.tooltipTitle = "Hidden Quests"
  info.tooltipText = "Display work order and profession-related quests that are skipped by the default UI."
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)

  info.text = "Show Continent Pins"
  info.isNotRadio = true
  info.value = "DisplayContinentPins"
  info.checked = DisplayContinentPins
  info.tooltipTitle = "Continent Pins"
  info.tooltipText = "Display quest pins on the continent map (may get cramped)."
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)

  info.text = "Show Summary"
  info.isNotRadio = true
  info.value = "DisplayContinentSummary"
  info.tooltipTitle = "Summary Bar"
  info.tooltipText = "Display a summary of active world quests. Note: requires directly viewing Broken Isle and Dalaran maps to gain complete info."
  info.checked = DisplayContinentSummary
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)
end

function WorldQuests:Setup()
  -- refresh positions any time blizzard does so (i.e. mousewheel zoom)
  hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function()
    self:Refresh()
  end)

  -- hide the original world quest POIs
  hooksecurefunc("WorldMap_UpdateQuestBonusObjectives", function()
    for i = 1, NUM_WORLDMAP_TASK_POIS do
      local button = _G['WorldMapFrameTaskPOI'..i]
      if button and button.worldQuest then
        button:Hide()
      end
    end
  end)


end

function WorldQuests:OnEvent (event, ...)
  print('|cFFFFFF00'..tostring(self)..':OnEvent()'..event..'|r', ...)
  if event == 'QUEST_LOG_UPDATE' then
    local questID, added = ...
    if questID and added then
      self:GetPinByQuestID(questID)
    else
      self:GetPinsForMap()
    end
  elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then
    local questID = ...
    if questID and QuestsByID[questID] then
      self:ReleasePin(QuestsByID[questID])
    end
  elseif event == 'SKILL_LINES_CHANGED' then
    self:Refresh()
  end
end

function WorldQuests:AcquirePin (questID, pinTable)
  local pin = QuestsByID[questID]
  local isNew = false
  if not pin then
    isNew = true
    local numFree = #self.freePins
    if numFree >= 1 then
      pin = tremove(self.freePins, numFree)
      --print('|cFF00FF00Re-using', pin:GetName())
    else
      local name = 'WorldPlanQuestMarker' .. NumPinFrames
      --print('|cFF00FF00Creating', name)
      pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin')
      pin:SetFrameStrata('HIGH')
      pin:SetScript('OnEnter', function(self)
        TaskPOI_OnEnter(self)
      end)
      pin:SetScript('OnLeave', function(self)
        TaskPOI_OnLeave(self)
      end)
      pin:SetScript('OnMouseDown', TaskPOI_OnClick)

      NumPinFrames = NumPinFrames + 1

      --pin.iconBorder:SetVertexColor(0,0,0,1)

    end
    QuestsByID[questID] = pin
    pin.isNew = true
    pin.currentWidth = nil

    -- used by TaskPOI_x scripts
    pin.questID = questID
    pin.worldQuest = true
  else
    --print('|cFF00FF00Using', pin:GetName())
  end

  -- set display flags accordingly
  if pinTable then
    for k,v in pairs(pinTable) do
      pin[k] = v
    end
  end
  pin.throttle = nil
  pin.timeThreschold = nil
  return pin, isNew
end

-- remove from index and add it to the recycling heap
function WorldQuests:ReleasePin (pin)

  local id = pin.questId
  if id then
    QuestsByID[id] = nil
    for i, zone in pairs(QuestsByZone) do
      print('-', i, zone[i])
      zone[id] = nil
    end
  end
  if pin.factionID then
    QuestsByFaction[pin.factionID][id] = nil
  end
  pin:Hide()
  pin:ClearAllPoints()
  tinsert(self.freePins, pin)
  print('|cFFFF4400Clearing out', pin:GetName(),id)
end

-- create of update quest pins for a map and its underlying zones
function WorldQuests:GetPinsForMap (mapID)
  mapID = mapID or GetCurrentMapAreaID()
  superTrackedID = GetSuperTrackedQuestID()
  if not mapID then
    -- info not available yet
    return
  end
  if mapID == BROKEN_ISLES_ID then
    hasPendingQuestData = nil
    print('|cFF00FFFFRefreshQuestsForMap|r', mapID, GetMapNameByID(mapID), superTrackedID)
    self.fullSearch = true
    for i = 1, MC_GetNumZones(mapID) do
      local submapID, name, depth = MC_GetZoneInfo(mapID, i)
      self:GetPinsForMap(submapID)
    end
    self.fullSearch = nil
  elseif QuestsByZone[mapID] then
    local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID)
    local quest = QuestsByZone[mapID]
    local numQuests = 0
    if taskInfo and #taskInfo >= 1 then
      print('|cFF00FFFFRefreshQuestsForMap|r', mapID, GetMapNameByID(mapID), #taskInfo)
      wipe(QuestsByZone[mapID])
      ZoneInfo[mapID] = taskInfo
      for taskID, info in pairs(taskInfo) do
        local questID = info.questId

        info.mapID = mapID
        QuestsByZone[mapID][questID] = self:GetPinByQuestID(questID, info)
        numQuests = numQuests + 1
      end
    end
  end
  if hasNewQuestPins and not self.fullSearch then
    print('|cFF00FF00NEW PINS DO ANCHOR THINGS')
    self:Refresh ()
    hasNewQuestPins = nil
  end
end

-- create or update the pin using the given questID and C_TaskQuest results
function WorldQuests:GetPinByQuestID (questID, taskInfo)
  if (QuestMapFrame_IsQuestWorldQuest (questID)) then
    local questTitle, rewardIcon, rewardName, rewardCount, rewardStyle, rewardType, itemID, quantity, quality, _
    local pin = self:AcquirePin(questID, taskInfo)

    if pin.isNew then
      if not hasNewQuestPins then
        print('triggering new quest pins event')
      end

      hasNewQuestPins = true
    end

    if not HaveQuestData(questID) then
      print('|cFFFF4400Retrieval failed.')
      TQ_RequestPreloadRewardData(questID)
      hasPendingQuestData = true
    else
      print('|cFF00FF88HaveQuestData|r')
      pin.mapID = pin.mapID or C_TaskQuest.GetQuestZoneID(questID)

      -- set reward category
      local numRewards = GetNumQuestLogRewards(questID)
      local numCurrency = GetNumQuestLogRewardCurrencies(questID)
      local money = GetQuestLogRewardMoney(questID)
      if numRewards >= 1 then
        rewardType, rewardIcon, rewardCount, rewardName, itemID = ParseItemReward(questID)
      elseif numCurrency >= 1 then
        rewardName, rewardIcon, rewardCount = GetQuestLogRewardCurrencyInfo(1, questID)
        rewardType = REWARD_CURRENCY
      elseif money >= 1 then
        rewardIcon = ICON_MONEY
        rewardName = GetMoneyString(money)
        rewardType = REWARD_CASH
      end
      rewardStyle = POI_REWARD_TYPE[rewardType] or POI_DEFAULT_TYPE

      pin.itemNumber = rewardCount or pin.itemNumber
      pin.rewardType = rewardType or REWARD_ITEM
      pin.style = rewardStyle
      QuestsByID[questID] = pin

      -- title, faction, capped state
      local questTitle, factionID, capped = TQ_GetQuestInfoByQuestID(questID)
      if factionID then
        QuestsByFaction[factionID] = QuestsByFaction[factionID] or {}
        QuestsByFaction[factionID][questID] = pin
      end
      pin.title = questTitle or "|cFFFF0000Retrieving..."
      pin.factionID = factionID
      pin.capped = capped

      -- set tag details
      local tagID, tagName, worldQuestType, rarity, isElite, tradeskillLineIndex = GetQuestTagInfo(questID);
      local tagAtlas
      if worldQuestType == LE_QUEST_TAG_TYPE_PET_BATTLE then
        tagAtlas = "worldquest-icon-petbattle"
      elseif worldQuestType == LE_QUEST_TAG_TYPE_PVP then
        tagAtlas = "worldquest-icon-pvp-ffa"
      elseif worldQuestType == LE_QUEST_TAG_TYPE_PROFESSION then
        local id = tradeskillLineIndex and select(7, GetProfessionInfo(tradeskillLineIndex))
        if id then
          tagAtlas = WORLD_QUEST_ICONS_BY_PROFESSION[id]
        end
      elseif worldQuestType == LE_QUEST_TAG_TYPE_DUNGEON then
        tagAtlas = "worldquest-icon-dungeon"
      end
      pin.tagID = tagID
      pin.tagName = tagName
      pin.worldQuestType = worldQuestType
      pin.isElite = isElite
      pin.tradeskillLineIndex = tradeskillLineIndex
      pin.rarity = rarity
      pin.tagAtlas = tagAtlas
    end

    -- flag unresolved info
    if not (rewardIcon and rewardName) then
      if not pin.isPending then
        pin.isPending = true
        TQ_RequestPreloadRewardData (questID)
        pin.rewardType = pin.rewardType or REWARD_ITEM
        pin.style = pin.style or POI_REWARD_TYPE[REWARD_ITEM]

        if not hasPendingQuestData then
          hasPendingQuestData = true
          PlaySoundKitID(229)
        end
        --WorldPlan:print('|cFFFFFF00'..tostring(pin.title)..'|r waiting on texture info')
      end
    else
      if (rewardIcon and rewardName) then
        --WorldPlan:print('|cFF00FF00'..tostring(pin.title)..'|r has info', rewardIcon, rewardName)
        pin.hasUpdate = true
      end
      pin.isPending = nil
    end
    pin.itemTexture = rewardIcon or pin.itemTexture
    pin.itemName = rewardName or pin.itemName

    qprint('  |cFF00FFFF'..questID..'|r:->', (HaveQuestData(questID) and "|cFF00FF00HaveQuestData" or "-"), (C_TaskQuest.IsActive(questID) and "|cFF88FF00IsActive|r" or ""))
    qprint(' ', pin.title, pin.itemTexture, 'rewardType:', pin.rewardType, 'tag:', pin.tagID, 'style', pin.style )
  end
  return QuestsByID[questID]
end

function WorldQuests:Refresh(forced)
  print('pushing |cFF00FF88'..tostring(self)..':Refresh()|r')
  if not WorldMapPOIFrame:IsVisible() then
    return
  end
  self:Reset()
  self:UpdateAnchors()
  self:Cleanup ()
end

-- prepares elements for a map update
function WorldQuests:Reset ()
  wipe(QuestPositions)
  wipe(QuestsByReward)
  wipe(QuestsByTag)
  for questID, pin in pairs(QuestsByID) do
    pin.used = nil
  end
  qprint(tostring(self)..':|cFFFFFF00Reset()|r')
end

-- update visibility states of all pins
function WorldQuests:UpdateAnchors (submapID)
  local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo()
  if isMicroDungeon then
    return
  end

  local currentMap = GetCurrentMapAreaID()
  local submapID = submapID or currentMap

  if submapID == BROKEN_ISLES_ID and (not DisplayContinentPins) then
    print('not updating map for reasons')
    return
  end
  print('|cFF88FF00'..tostring(self)..':UpdateAnchors|r', submapID, GetMapNameByID(submapID), 'pin count:', numPins)
  local numZones = MC_GetNumZones(submapID)
  if numZones then
    for i = 1, numZones do
      local subMapID = MC_GetZoneInfo(submapID, i)
      self:UpdateAnchors(subMapID)
    end
  end
  local pins = QuestsByZone[submapID]

  if pins then
    local hostFrame = WorldMapPOIFrame
    local mapWidth, mapHeight = hostFrame:GetSize()
    for questID, pin in pairs(pins) do
      if pin:IsShowable(true) then
        pin:SetFrameLevel(PinBaseIndex+numPins)
        pin:SetAnchor(WorldMapPOIFrame, currentMap, mapWidth, mapHeight)
        pin.used = true
        numPins = numPins + 1
      end
    end
  end
end




-- shows, animates, or hides pins based on their current visibility flags
local debug_show = {}
local debug_animate = {}
local debug_hide = {}
function WorldQuests:Cleanup ()
  local showQuestPOI = GetCVarBool("questPOI")
  print('|cFFFFFF00'..tostring(self)..':Cleanup()|r')
  wipe(QuestsByReward)
  wipe(QuestsByTag)
  wipe(debug_show)
  wipe(debug_animate)
  wipe(debug_hide)
  local mapID = GetCurrentMapAreaID()
  isContinentMap = (mapID == BROKEN_ISLES_ID)

  -- continent or zone sizing
  local subStyle = isContinentMap and 'continent' or 'zone'

  numPins = 0
  for questID, pin in pairs(QuestsByID) do
    -- can we show it?
    if showQuestPOI and (pin.used) then
      pin.subStyle = pin.filtered and 'minimized' or subStyle
      pin:GetSizeMetrics()


      -- is it a new quest?
      if pin.isNew then
        if not pin.isAnimating then
          pin.isAnimating = true
          OnNext(function()
            pin.isNew = nil
            pin:Show()
            pin.FadeIn:Play()
          end)
          if not notifyPlayed then
            for k,v in pairs(NotificationTypes) do
              if v[pin[k]] then
                notifyPlayed = true
                PlaySoundKitID(23404)
              end
            end
          end
          tinsert(debug_animate,questID)
        end
        -- trap new but animating pins here
      else
        -- hard show existing pin
        pin:Show()
        tinsert(debug_show,questID)
      end
    else
      pin:Hide()
      tinsert(debug_hide,questID)
    end

    -- is it part of the current map?
    if pin.used then
      local rewardType = pin.rewardType
      local tagType = pin.worldQuestType
      if rewardType then
        QuestsByReward[rewardType] = QuestsByReward[rewardType] or {}
        tinsert(QuestsByReward[rewardType], pin)
      end
      if tagType then
        QuestsByTag[tagType] = QuestsByTag[tagType] or {}
        tinsert(QuestsByTag[tagType], pin)
      end
    end
  end
  print('   adding:', table.concat(debug_animate, ',' ))
  print('  showing:', table.concat(debug_show, ',' ))
  hasNewQuestPins = nil
  notifyPlayed = nil
end

-- data provider manipulations for the taxi map
WorldPlan.OnFlightMapLoaded = function()
  if true then return end
  -- todo: figure out how to layer inside the map canvas
  local res = {}
  local t = {}
  for k,v in pairs(FlightMapFrame) do
    tinsert(res, tostring(k))
  end

  table.sort(res)
  for i, k in ipairs(res) do
    print(k)
  end
  hooksecurefunc(FlightMapFrame, 'RefreshAll', function(self)
    print('|cFF0088FFWQDP RefreshAllData ', GetTime())

    WorldPlan:GetPinsForMap(self:GetMapID())

    for pin in self:EnumerateAllPins() do
      if pin.worldQuest then
        --print('got pin #', pin.questID)
        local wp = QuestsByID[pin.questID]
        if wp then
          wp:ClearAllPoints()
          wp:SetParent(FlightMapFrame.ScrollContainer)
          wp:SetFrameStrata('MEDIUM')
          wp:SetPoint('CENTER', pin, 'CENTER')
          wp:Show()
        end
      end
    end
  end)
end



local throttle = 0
local tooltip = CreateFrame ("GameTooltip", "VeneerWorldQuestsScanner", nil, "GameTooltipTemplate")
local tooltipLine1 = _G['VeneerWorldQuestsScannerTextLeft1']
local tooltipLine3 = _G['VeneerWorldQuestsScannerTextLeft3']
local GetTime, mod = GetTime, mod

function QuestPOI:OnLoad()
  self:RegisterEvent('SUPER_TRACKED_QUEST_CHANGED')
end

function QuestPOI:OnEvent(event, ...)
  if event == 'SUPER_TRACKED_QUEST_CHANGED' then
    if self:IsVisible() then
      self:Refresh()
    end
  end
end

local PIN_UPDATE_DELAY = .016
local TOP_PIN_ID
function QuestPOI:OnUpdate (sinceLast)
  -- control update check intervals
  self.throttle = (self.throttle or PIN_UPDATE_DELAY) - sinceLast
  if self.throttle <= 0 then
    -- factor overtime into the throttle timer
    self.throttle = PIN_UPDATE_DELAY - self.throttle
  else
    return
  end

  -- query for reward data if it wasn't found in the original scan
  local questID = self.questID
  if self.isPending then
    WorldQuests:GetPinByQuestID(questID)
    if not (self.PendingFade:IsPlaying() or self.isAnimating) then
      self.PendingFade:Play()
    end
    return
  else
    if self.PendingFade:IsPlaying() then
      self.PendingFade:Stop()
    end
  end

  if self.hasUpdate then
    self:Refresh()
    self.hasUpdate = nil
  end

  -- update time elements
  local tl = self.timeThreschold
  local timeLeft = TQ_GetQuestTimeLeftMinutes(questID)
  if timeLeft > 0 then
    for i, context in ipairs(PIN_TIME_CONTEXT) do
      if i > self.TimeleftStage then
        self.timeLabel:SetText(nil)
        break
      end


      if timeLeft <= context.max then
        if tl ~= i then
          tl = i
        end

        if context.format then
          self.timeLabel:SetText(context.format(timeLeft))
        else
          self.timeLabel:SetText(nil)
        end
        break
      end
    end
  else
    -- remove self in a timely manner
    if not self.isPending then
      self:Hide()
    end
  end
  self.timeThreschold = tl

  if self:IsMouseOver() then
    self.MouseGlow:Show()
  else
    self.MouseGlow:Hide()
  end
end

function QuestPOI:OnShow ()
  qprint('|cFFFFFF00["'..tostring(self.title)..'"]|r:OnShow()')
  -- pop this on principle
  self:Refresh()
end
function QuestPOI:OnHide()
  --qprint('|cFFFFFF00["'..tostring(self.title)..'"]|r:OnHide()')
end
function QuestPOI:Refresh ()
  print('|cFF00FF88["'..tostring(self.title)..'"]|r:Refresh()', tostring(self.title), "|T"..tostring(self.itemTexture)..":12:12|t", tostring(self.itemName))
  qprint(self.style)

  local questID = self.questId
  local style = self.style
  local borderMask = style.mask
  local borderFill = style.texture
  local iconBorder = self.iconBorder
  local icon = self.icon
  local count = self.count

  --WorldPlan:print(tostring(self.title), "|T"..tostring(self.itemTexture)..":16:16|t", tostring(self.itemName))
  SetMaskedTexture(icon, self.itemTexture or ICON_UNKNOWN, borderMask)
  icon:SetAllPoints(self)

  if self.itemName then
    local color = self.rewardColor or COMMON_COLOR
    if self.itemNumber and  self.target then
      self.count:SetText(color.hex .. tostring(self.itemNumber))
    else
      self.count:SetText(nil)
    end
  end

  SetMaskedTexture(iconBorder, borderFill, borderMask)
  local border = (self.rewardType and POI_REWARD_TYPE[self.rewardType]) or (WORLD_QUEST_QUALITY_COLORS[self.rarity] or COMMON_COLOR)
  iconBorder:SetVertexColor(border.r, border.g, border.b, border.a)
  iconBorder:SetDesaturated(true)

  local trackingBorder = self.supertrackBorder

  self.highlight:SetMask(nil)
  if questID == GetSuperTrackedQuestID() then
    trackingBorder:SetVertexColor(0,0,0,1)
  else
    trackingBorder:SetVertexColor(0,0,0,0.5)
  end
  self.highlight:SetAllPoints(trackingBorder)

  SetMaskedTexture(trackingBorder, borderFill, borderMask)
  self.highlight:SetMask(borderMask)

  local qType = self.worldQuestType
  self.tagIcon:SetAtlas(self.tagAtlas)
  self.tagIcon:SetTexCoord(0,1,0,1)


  if self.isElite then
    self.EliteDecal:Show()
  else
    self.EliteDecal:Hide()
  end

  if style.showNumber then
    self.label:SetText(self.itemNumber)
  else
    self.label:SetText(nil)
  end
  qprint('|cFF88FF00updated', questID, self.title, self.rewardType, (style.showNumber and self.itemNumber) or '')
end

function QuestPOI:SetAnchor(frame, mapID, mapWidth, mapHeight)
  self:ClearAllPoints()
  local dX, dY = TQ_GetQuestLocation(self.questID, mapID)
  if not dX or dX == 0 then
    local _, x, y = QuestPOIGetIconInfo(self.questID)
    if x and floor(x) ~= 0 then
      dX, dY = x, y
    else
      dX, dY = self.x, self.y
    end
  end
  self.x = dX
  self.y = dY

  print('  |cFF00FF00'..self.questID..':|r', format("%0.2f %0.2f", dX, dY))

  local pX = (dX * mapWidth)
  local pY = (-dY * mapHeight)

  self:SetParent(WorldMapPOIFrame)
  self:SetPoint('CENTER', frame, 'TOPLEFT', pX, pY)
end


function QuestPOI:IsShowable (ignoreFilters)
  local qType = self.worldQuestType
  local rType = self.rewardType
  self.filtered = nil
  print('  |cFFFF4400IsShowable()|r', self.tradeskillLineIndex, self.title)

  local whiteListed, blackListed
  for filterKey, includes in pairs(FilterInclusions) do
    local controlValue = self[filterKey]
    if FilterInclusions[filterKey][controlValue] then
      whiteListed = true
      break
    end
    if FilterExclusions[filterKey][controlValue] then
      blackListed = true
    end
  end
  print(blackListed, whiteListed)
  self.filtered = (blackListed and (not whiteListed))

  if not TQ_IsActive(self.questID) then
    print('  quest is dead')
    return false
  end
  if qType == LE_QUEST_TAG_TYPE_PROFESSION then

    if not (ShowAllProfessionQuests or (self.tradeskillLineIndex and GetProfessionInfo(self.tradeskillLineIndex))) then
      print('  non-profession')
      return false
    end
  end
  return true
end

function QuestPOI:UpdateTimer (timeLeft, timeType)
  print('|cFF0088FFUpdatePinTimer()|r')
end

--- Re-acquires size information and triggers things
function QuestPOI:GetSizeMetrics (style, subStyle)
  self.style = self.style or POI_DEFAULT_TYPE
  self.subStyle = self.subStyle or 'continent'

  style = style or self.style
  subStyle = style[subStyle or self.subStyle]


  self.currentWidth = subStyle.PinSize
  self.borderSize = subStyle.Border
  self.trackingBorderSize = subStyle.TrackingBorder
  self.tagSize = subStyle.TagSize
  self.TimeleftStage = subStyle.TimeleftStage
  self.NoIcon = subStyle.NoIcon

  self:SetSize(self.currentWidth, self.currentWidth)
end

-- triggered by OnSizeChanged script
function QuestPOI:OnSizeChanged ()
  local iconBorder = self.iconBorder
  local trackingBorder = self.supertrackBorder
  local tag = self.tagIcon
  local style = self.style or POI_DEFAULT_TYPE
  local mask = style.mask or POI_BORDER_FILL

  self.icon:SetMask(nil)
  self.iconBorder:SetMask(nil)
  self.supertrackBorder:SetMask(nil)


  local borderWidth = self.borderSize
  local trackingWidth = self.trackingBorderSize

  iconBorder:ClearAllPoints()
  iconBorder:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', -borderWidth + (style.x or 0), -borderWidth + (style.y or 0))
  iconBorder:SetPoint('TOPRIGHT', self, 'TOPRIGHT', borderWidth + (style.x or 0), borderWidth + (style.y or 0))

  trackingBorder:ClearAllPoints()
  trackingBorder:SetPoint('BOTTOMLEFT', iconBorder, 'BOTTOMLEFT', -trackingWidth, -trackingWidth)
  trackingBorder:SetPoint('TOPRIGHT', iconBorder, 'TOPRIGHT', trackingWidth, trackingWidth)

  if self.tagSize then
    tag:Show()
    tag:ClearAllPoints()
    tag:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', borderWidth, -borderWidth)
  else
    tag:Hide()
  end

  self.icon:SetMask(mask)
  self.iconBorder:SetMask(mask)
  self.supertrackBorder:SetMask(mask)

  if self.NoIcon then
    self.icon:Hide()
  else
    self.icon:Show()
  end

end



function FilterBar:OnEvent(event)
  if event == 'QUEST_LOG_UPDATE' then
    self:Refresh()
  end
end

function FilterBar:PassesFilterSet(filterKey, pin)
  local passesFilter = true
  for filterKey, filters in pairs(QuestFilters) do
    for rewardType, value in pairs(QuestFilters[filterKey]) do
      if value == 1 and rewardType == pin[filterKey] then
        passesFilter = true
      elseif value == -1 and rewardType == pin[filterKey] then
        passesFilter = false
      end
    end
  end
  return passesFilter
end

local debug_headers = {}
function FilterBar:Refresh()
  local mapID = GetCurrentMapAreaID()
  local blocks = self.SummaryHeaders
  local numHeaders = 0
  local lastButton
  local pinSize, borderSize, trackingSize = 20, 2, 2
  print('|cFF00FF88'..tostring(self)..':Refresh()|r')
  for index, info in pairs(POI_FILTER_OPTIONS) do
    local numQuests = 0
    local quests = QuestsByZone[mapID] or QuestsByID

    for questID, pin in pairs(quests) do
      if pin.used then
        if not info.filterKey then
          numQuests = numQuests + 1
        elseif pin[info.filterKey] == info.filterValue then
          numQuests = numQuests + 1
        end
      end
    end
    print(tostring(index).. ' ("'..tostring(info.label)..'"'.. tostring(rewardFilter).. ', '..tostring(tagFilter) .. ', '..tostring(numQuests)..')')

    if numQuests >= 1 then
      numHeaders = numHeaders + 1
      local button = blocks[numHeaders]
      if not blocks[numHeaders] then
        button = CreateFrame('Button', 'WorldPlanFilterButton'..numHeaders, WorldMapScrollFrame, 'WorldPlanFilterPin')
        button.iconBorder:SetTexture(info.fill or POI_BORDER_FILL)
        button.iconBorder:SetMask(info.mask or POI_BORDER_MASK)
        button.iconBorder:SetDesaturated(info.desaturated)
        button.supertrackBorder:SetTexture(info.fill or POI_BORDER_FILL)
        button.supertrackBorder:SetMask(info.mask or POI_BORDER_MASK)
        button.supertrackBorder:SetDesaturated(true)
        blocks[numHeaders] = button
      end

      button:SetID(index)
      button.lastButton = lastButton
      button:Refresh(info, (numHeaders == 1), numQuests)
      button:Show()
      lastButton = button
    end

  end
  -- hide trailing buttons
  for i = numHeaders + 1, #POI_FILTER_OPTIONS do
    if blocks[i] then
      blocks[i]:Hide()
    end
  end
end


function FilterPin:Refresh(info, isFirst, numQuests)
  info = info or POI_FILTER_OPTIONS[self:GetID()]
  isFirst = isFirst or self.isFirst
  numQuests = numQuests or self.numQuests

  self.isFirst = isFirst
  self.numQuests = numQuests
  self.filterKey = info.filterKey
  self.filterValue = info.filterValue
  self.tagID = info.tagID

  self.icon:ClearAllPoints()
  self.icon:SetTexture(info.texture)
  self.icon:SetAllPoints(self)
  self.supertrackBorder:Hide()
  self.label:SetText(numQuests)
  self:Show()


  if isFirst then
    self:SetPoint('TOP', WorldMapFrame.UIElementsFrame.TrackingOptionsButton, 'BOTTOM', 0, -5)
  else
    self:SetPoint('TOPRIGHT', self.lastButton, 'BOTTOMRIGHT', 0, -(3*2 + 1 + (info.spacing or 0)))
  end

  print(self.filterKey, self.filterValue)
  local r, g, b, a = 1,1,1,1
  if self.filterKey then
    if FilterInclusions[self.filterKey][self.filterValue] == true then
      r, g, b = 0, 1, 0
    elseif FilterExclusions[self.filterKey][self.filterValue] then
      r, g, b = 1, 0, 0
    end
  end
  self.iconBorder:SetVertexColor(r, g, b, a)


  self:GetSizeMetrics()
end

function FilterPin:OnLoad()
  self:RegisterForClicks('AnyUp')
  self:SetFrameStrata('HIGH')
  self:SetFrameLevel(151)
  self:SetScript('OnUpdate', nil)
end

function FilterPin:OnUpdate ()

end

function FilterPin:OnEnter ()
  local filter = POI_FILTER_OPTIONS[self:GetID()]
  local mapID = GetCurrentMapAreaID()
  local quests = (mapID == BROKEN_ISLES_ID) and QuestsByID or QuestsByZone[mapID]
  if quests then
    GameTooltip:SetOwner(self, 'ANCHOR_RIGHT')
    GameTooltip:AddLine(filter.label)
    local filterKey = self.filterKey
    local filterValue = self.filterValue
    if filterKey then
      for questID, pin in pairs(quests) do
        if pin.used and not pin.filtered then
          if  pin[filterKey] == filterValue then
            local style = pin.style or POI_FILTER_STYLE
            GameTooltip:AddLine(pin.title)
          end
        end
      end
    else
      GameTooltip:AddLine('Reset all filters')
    end
    GameTooltip:Show()
  end
end
function FilterPin:OnLeave ()
  if GameTooltip:IsOwned(self) then
    GameTooltip:Hide()
  end
end

-- shift-click: reset filter
-- click: rotate through include(1), exclude(-1), ignore(nil)
function FilterPin:OnClick (button)
  local filterKey = self.filterKey
  local filterValue = self.filterValue

  print('click', filterKey, filterValue)

  if not filterKey then
    -- resetting
    for k,v in pairs(FilterInclusions) do
      wipe(v)
    end
    for k,v in pairs(FilterExclusions) do
      wipe(v)
    end
  else
    local setExclude = (button == 'RightButton')
    if IsControlKeyDown() then
      if setExclude then
        if FilterExclusions[filterKey][filterValue] then
          FilterExclusions[filterKey][filterValue] = nil
        else
          FilterExclusions[filterKey][filterValue] = true
        end
        FilterInclusions[filterKey][filterValue] = nil
      else
        if FilterInclusions[filterKey][filterValue] == true then
          FilterInclusions[filterKey][filterValue] = nil
        else
          FilterInclusions[filterKey][filterValue] = true
        end
        FilterExclusions[filterKey][filterValue] = nil
      end
    elseif IsShiftKeyDown() then
      FilterInclusions[filterKey][filterValue] = nil
      FilterExclusions[filterKey][filterValue] = nil
    else
      print('limit to', filterKey, filterValue)
      if setExclude then
        FilterExclusions[filterKey][filterValue] = true
        FilterInclusions[filterKey][filterValue] = nil
      else
        FilterInclusions[filterKey][filterValue] = true
        FilterExclusions[filterKey][filterValue] = nil
      end

      for k, info  in ipairs(POI_FILTER_OPTIONS) do
        if info.filterKey and ((info.filterKey ~= filterKey) or (info.filterValue ~= filterValue)) then
          if setExclude then
            FilterExclusions[info.filterKey][info.filterValue] = nil
            FilterInclusions[info.filterKey][info.filterValue] = true
          else
            FilterExclusions[info.filterKey][info.filterValue] = true
            FilterInclusions[info.filterKey][info.filterValue] = nil
          end

        end
      end

    end
  end


  WorldPlan:RefreshAll()
end

--%debug%
local SetTimedCallbackForAllPins = function(seconds, callback)
  C_Timer.After(seconds, function()
    for id, pin in pairs(QuestsByID) do
      callback(pin)
    end
  end)
end

SLASH_WORLDPLAN1 = "/worldplan"
SLASH_WORLDPLAN2 = "/wp"
SlashCmdList.WORLDPLAN = function()
  print('command pop')
  WorldPlan:GetPinsForMap()
  WorldPlan:RefreshPins()

  SetTimedCallbackForAllPins(0, function(self) self.FadeIn:Play() self.FlashIn:Play()  end)
  SetTimedCallbackForAllPins(5, function(self) self.PendingFade:Play() end)
  SetTimedCallbackForAllPins(8, function(self) self.PendingFade:Stop() end)
end
--%end-debug%

for mapID, mapName in pairs(WORLD_QUEST_MAPS) do
  QuestsByZone[mapID] = {}
end
for index, color in pairs(ITEM_QUALITY_COLORS) do
  POI_REWARD_TYPE[index] = {
    r = color.r, g = color.g, b = color.b,
    hex = color.hex,
  }
end