view WorldQuests.lua @ 33:be4db60219ca

WorldPlan: - Toggling a reward filter cancels out other types by default. Use right mouse to clear. - Fixed filter bar info falling out of sync after player-triggered world map updates. 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 17:25:07 -0400
parents
children 0100d923d8c3
line wrap: on
line source
-- WorldPlan
-- WorldQuests.lua
-- Created: 11/2/2016 3:40 PM
-- %file-revision%

WorldPlanQuestsMixin = {
  QuestsByZone = {},
  QuestsByID = {},
  freePins = {},
}
local WorldQuests = WorldPlanQuestsMixin

local MC_GetNumZones, MC_GetZoneInfo = C_MapCanvas.GetNumZones, C_MapCanvas.GetZoneInfo
local TQ_GetQuestsForPlayerByMapID = C_TaskQuest.GetQuestsForPlayerByMapID -- This function is not yet documented
local TQ_GetQuestZoneID = C_TaskQuest.GetQuestZoneID
local GetMapInfo = GetMapInfo
local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
local qprint = DEVIAN_WORKSPACE and function(...) _G.print('POI', ...) end or function() end
local wqprint = DEVIAN_WORKSPACE and function(...) _G.print('WorldQuests', ...) end or function() end
local wprint = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end


local PinBaseIndex = 1600
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
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', }

local REWARD_CASH = WORLD_QUEST_REWARD_TYPE_FLAG_GOLD
local REWARD_ARTIFACT_POWER = WORLD_QUEST_REWARD_TYPE_FLAG_ARTIFACT_POWER
local REWARD_GEAR = WORLD_QUEST_REWARD_TYPE_FLAG_EQUIPMENT
local REWARD_CURRENCY = WORLD_QUEST_REWARD_TYPE_FLAG_ORDER_RESOURCES
local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS


local numPins = 0
local ZoneInfo = {}
local NumPinFrames = 1


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

function WorldQuests:Setup()


  for mapID, mapName in pairs(WORLD_QUEST_MAPS) do
    self.QuestsByZone[mapID] = {}
  end


  -- refresh positions any time blizzard does so (i.e. mousewheel zoom)
  hooksecurefunc("WorldMapScrollFrame_ReanchorQuestPOIs", function()
    self:Refresh(true)
  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
local WorldMapPOIFrame
local defaults = {}
function WorldQuests:OnLoad()
  print('|cFF00FF88'..self:GetName()..':OnLoad')

  WorldPlan:AddHandler(self, defaults)

  local rgbWhite = {1, 1, 1}
  WorldPlan:AddTypeInfo(self, REWARD_REAGENT, { r = 0, g = 1, b = 1 })
  WorldPlan:AddTypeInfo(self, REWARD_ARTIFACT_POWER, { r = 1, g = .25, b = .5, hasNumeric = true, numberRGB = rgbWhite })
  WorldPlan:AddTypeInfo(self, REWARD_GEAR, { r = .1, g = .2, b = 1 })
  WorldPlan:AddTypeInfo(self, REWARD_CURRENCY, { r = 1, g = 1, b = 0, hasNumeric = true, numberRGB = {1,1,0}, })
  WorldPlan:AddTypeInfo(self, REWARD_CASH, { r = 0, g = 0, b = 0, })

  for areaID, fileName in pairs(WORLD_QUEST_MAPS) do
    self.QuestsByZone[areaID] = {}
  end

  self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL')
  self:RegisterEvent('SKILL_LINES_CHANGED')

  WorldMapPOIFrame = _G.WorldMapPOIFrame

end

function WorldQuests:OnEvent (event, ...)
  local print = wqprint
  print('|cFFFFFF00'..self:GetName()..':OnEvent()'..event..'|r', GetTime(), ...)
  if event == 'QUEST_LOG_UPDATE' then
    local questID, added = ...
    if questID and added then
      local questPOI = self:AcquirePin(questID)
      self.isStale, self.isPending = questPOI:RefreshData()
    else
      self:RefreshData()
    end
    print('WorldMapFrame', WorldMapFrame:IsVisible(), 'hasUpdates:', self.isStale)
  elseif event == 'WORLD_MAP_UPDATE' then
    self.isStale = true
  elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then
    local questID = ...
    if questID and self.QuestsByID[questID] then
      self:ReleasePin(self.QuestsByID[questID])
    end
  elseif event == 'SKILL_LINES_CHANGED' then
    self.isStale = true
  end
end

local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation
function WorldQuests:AcquirePin (questID, mapID)
  local pin = self.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.GetTypeInfo = function(frame, typeID)
        return self:GetTypeInfo(typeID)
      end
      NumPinFrames = NumPinFrames + 1
      --pin.iconBorder:SetVertexColor(0,0,0,1)
    end
    pin:SetID(questID)
    pin.isNew = true
    pin.currentWidth = nil

    -- used by TaskPOI_x scripts
    pin.questID = questID
    pin.worldQuest = true

    self.QuestsByID[questID] = pin
  else
    --print('|cFF00FF00Using', pin:GetName())
  end
  mapID = mapID or TQ_GetQuestZoneID(questID)
  self.QuestsByZone[mapID][questID] = pin

  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
    self.QuestsByID[id] = nil
    for i, zone in pairs(self.QuestsByZone) do
      print('-', i, zone[i])
      zone[id] = nil
    end
  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:RefreshData (mapID)
  local print = wqprint
  mapID = mapID or GetCurrentMapAreaID()
  superTrackedID = GetSuperTrackedQuestID()
  if not mapID then
    -- info not available yet
    return
  end

  print('|cFF00FF88'..self:GetName()..':RefreshData()|r', 'map:', mapID, 'realMap:', GetCurrentMapAreaID())

  if mapID == BROKEN_ISLES_ID then
    self.isStale = false
    print('|cFF00FFFFContinent:|r', mapID, GetMapNameByID(mapID), superTrackedID)
    self.fullSearch = true
    for i = 1, MC_GetNumZones(mapID) do
      local submapID, name, depth = MC_GetZoneInfo(mapID, i)
      self:RefreshData(submapID)
    end
    self.fullSearch = nil
  elseif self.QuestsByZone[mapID] then
    local taskInfo = TQ_GetQuestsForPlayerByMapID(mapID)
    local numQuests = 0
    if taskInfo and #taskInfo >= 1 then
      print('|cFF00FFFF  Zone:|r', mapID, GetMapNameByID(mapID), #taskInfo)
      wipe(self.QuestsByZone[mapID])
      ZoneInfo[mapID] = taskInfo
      qprint('|cFFFF4400START of', GetMapNameByID(mapID))
      for taskID, info in pairs(taskInfo) do
        local questID = info.questId
        info.mapID = mapID
        local questPOI = self:AcquirePin(questID, mapID)
        local hasUpdate, isPending = questPOI:RefreshData(info)
        self.isStale = (self.isStale or hasUpdate)
        self.isPending = (self.isPending or isPending)
        numQuests = numQuests + 1
      end
      qprint('|cFFFF4400END of', GetMapNameByID(mapID))
    end
  end

  if not self.fullSearch then
    print('  hasUpdate:', self.isStale, 'isPending:', self.isPending, 'timer:', (self.OnNext and 'waiting' or ''))
    --WorldPlan.isStale = (self.isStale or WorldPlan.isStale)
  end

end

function WorldQuests:Refresh()
  local print = wqprint
  print('|cFF00FF88'..self:GetName()..':Refresh()|r')
  self:Reset()
  self:UpdateAnchors()
  self:Cleanup ()
end

-- prepares elements for a map update
function WorldQuests:Reset ()
  local print = wqprint
  print('|cFF00FF88'..self:GetName()..':Reset()|r')
  for questID, pin in pairs(self.QuestsByID) do
    pin.used = nil
  end
end

-- update visibility states of all pins
function WorldQuests:UpdateAnchors (submapID)

  local print = wqprint
  local db = WorldPlan.db
  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 db.DisplayContinentPins) then
    print('not updating map for reasons')
    return
  end
  print('|cFF88FF00'..self:GetName()..':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 = self.QuestsByZone[submapID]

  if pins then
    local hostFrame = WorldMapPOIFrame
    local mapWidth, mapHeight = hostFrame:GetSize()
    for questID, pin in pairs(pins) do
      pin:IsShowable()
      if pin.used then
        pin.hasUpdate = true
        pin:SetFrameLevel(PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins)
        print('level', PinBaseIndex+ (pin.whiteListed and 200 or 0) +numPins)
        pin:SetAnchor(WorldMapPOIFrame, currentMap, mapWidth, mapHeight)
        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 print = wqprint
  local showQuestPOI = db.EnablePins
  print('|cFFFFFF00'..tostring(self)..':Cleanup()|r')
  local mapID = GetCurrentMapAreaID()
  isContinentMap = (mapID == BROKEN_ISLES_ID)

  wipe(debug_show)
  wipe(debug_animate)
  wipe(debug_hide)
  -- continent or zone sizing
  local fadeGrouped = (db.FadeWhileGrouped and IsInGroup())

  numPins = 0
  for questID, pin in pairs(self.QuestsByID) do
    -- can we show it?
    if showQuestPOI and (pin.used) then

      pin.isStale = true
      if fadeGrouped then
        pin:SetAlpha(0.25)
      else
        pin:SetAlpha(1)
      end
      -- is it a new quest?
      if pin.isNew then
        if not pin.isAnimating then
          pin.isAnimating = true
          WorldPlan:OnNext(function()
            pin:ShowNew()
          end)
          tinsert(debug_animate,questID)
        else

          print('animating? ', questID, 'filtered:', pin.filtered)
        end
        -- trap new but animating pins here
      else
        -- hard show existing pin
        --print('refresh #', questID, 'filtered:', pin.filtered, 'hasUpdate', pin.hasUpdate)
        pin:Show()
        tinsert(debug_show,questID)
      end
    else
      if pin:IsShown() then
        tinsert(debug_hide,questID)
      end
      pin.isAnimating = nil
      pin.FadeIn:Stop()
      pin:Hide()
    end
  end
  print('   adding:', table.concat(debug_animate, ',' ))
  print('  refresh:', table.concat(debug_show, ',' ))
  print('  hiding:', table.concat(debug_hide, ',' ))
  hasNewQuestPins = nil
  notifyPlayed = nil
  self.isStale = nil
end

function WorldQuests:FilterCheckByID(questID)
  local pin = WorldQuests:GetPinByQuestID(questID)
  return pin:IsShowable()
end