view WorldPlan.lua @ 43:77c2ffb5c7f5 v1.0-rc11

- Removed possible refresh loop occurring in non-BrokenIsle zones
author Nenue
date Sun, 25 Dec 2016 15:47:17 -0500
parents 79e5e96e5f18
children db570c6a0ffb
line wrap: on
line source
-- WorldPlan.lua
-- Created: 8/16/2016 8:19 AM
-- %file-revision%
local addonFileName, db = ...
local print = DEVIAN_WORKSPACE and function(...) _G.print('WP', ...) end or function() end
local WP_VERSION = "1.0"
local tinsert, pairs, floor = tinsert, pairs, floor
local tremove, ipairs, wipe, unpack = tremove, ipairs, wipe, unpack
local select, type, tostring, tonumber = select, type, tostring, tonumber
local ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS
local BROKEN_ISLES_ID = 1007
local GetCurrentMapAreaID = GetCurrentMapAreaID
local GetTime, IsLoggedIn = GetTime, IsLoggedIn

-- Define tables here so the pointers match up
WorldPlanCore = { defaults = {}, modules = {}, TaskQueue = {}, }
WorldPlanQuestsMixin = {
  UsedPositions = {},
}
WorldPlanPOIMixin = {}
WorldPlanSummaryMixin = {}
db.filtersDirty = true
db.questsDirty = true
db.OrderedModules = {}
db.LoadedModules = {}
db.UsedFilters = {}
db.QuestsByZone = {}
db.QuestsByID = {}
db.TasksByID = {}
db.FreePins = {}
db.UsedPins = {}
db.ReportChunks = {}

-- default color templates
db.DefaultType = {
  a = 1,
  r = 1, g = 1, b = 1,
  x = 0, y = 0,
  desaturated = true,
  pinMask = "Interface\\Minimap\\UI-Minimap-Background",
  rewardMask = "Interface\\Minimap\\UI-Minimap-Background",
  texture = "Interface\\BUTTONS\\YELLOWORANGE64",
  continent = {
    iconWidth = 14,
    borderWidth = 2,
    highlightWidth = 1,
    TagSize = 8,
    maxAlertLevel = 0,
    showNumber = true,
    numberFontObject = 'WorldPlanFont'
  },
  zone = {
    iconWidth = 22,
    borderWidth = 3,
    highlightWidth = 2,
    TagSize = 12,
    maxAlertLevel = 3,
    showNumber = true,
    numberFontObject = 'WorldPlanNumberFontThin'
  },
  minimized = {
    r = 0, g = 0, b = 0, a = 0.1,
    iconWidth = 8,
    borderWidth = 2,
    alpha = 0.5,
    highlightWidth = 0,
    maxAlertLevel = 0,
    NoIcon = true,

    TagSize = 8,
    TimeleftStage = 1,
    showNumber = false,
    alpha = 0.1,
  }
}

db.DefaultConfig = {
  ShowAllProfessionQuests = false,
  DisplayContinentSummary = true,
  DisplayContinentPins = true,
  NotifyWhenNewQuests = true,
  EnablePins = true,
  FadeWhileGrouped = false,
  FlightMapAlphaLimits = {1, 1, 1},
  FlightMapScalingLimits = {1, 3, 1.5},
}



-- 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
  _G.WorldPlan:Refresh()
end

function db.print(...)
  for i = 1, select('#', ...) do
    tinsert(db.ReportChunks, tostring(select(i, ...)))
  end
end

function WorldPlanCore:print(...) db.print(...) end

function WorldPlanCore:AddHandler (frame)
  if not db.LoadedModules[frame] then
    print('|cFFFFFF00'..self:GetName()..':AddHandler()', frame:GetName(), self.initialized)
    db.LoadedModules[frame] = true
    tinsert(db.OrderedModules, frame)

    if frame.defaults then
      db.DefaultConfig[frame:GetName()] = frame.defaults
    end

    frame.GetTypeInfo = function(frame, typeID)
      return self:GetTypeInfo(frame, typeID)
    end

    frame.owningFrame = self
  else

    print('|cFFFF4400'..self:GetName()..':AddHandler()', frame:GetName())
  end
end

function WorldPlanCore:OnLoad ()

  self.Types = setmetatable({}, {
    __newindex = function(t, k, v)
      if type(v) == 'table' then
        print('adding owner', k)
        v = setmetatable(v, {
          __newindex = function(t2,k2,v2)
          if type(v2) == 'table' then
            --print('adding type', k2)
            v2 = setmetatable(v2, {__index = function(t3,k3)
              --print('##deferring to default key', k3)
              return db.DefaultType[k3]
            end})
          end
          rawset(t2,k2,v2)
        end})
      end
      rawset(t,k,v)
    end
  })

  self.Types[self] = {}

  for index, color in pairs(ITEM_QUALITY_COLORS) do
    self:AddTypeInfo(self, index, { r = color.r, g = color.g, b = color.b, hex = color.hex, })
  end


  db.print('v'..WP_VERSION)


  self:RegisterEvent("QUESTLINE_UPDATE")
  self:RegisterEvent("QUEST_LOG_UPDATE")
  self:RegisterEvent("WORLD_MAP_UPDATE")
  self:RegisterEvent("SPELLS_CHANGED")
  self:RegisterEvent('PLAYER_ENTERING_WORLD')
  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")
  self:RegisterEvent("PLAYER_LOGIN")
  --self:SetParent(WorldMapFrame)
end

function WorldPlanCore:OnShow()
  --print(self:GetName()..':OnShow()')
  --hooksecurefunc(self, 'SetScript', function(...) self:print('|cFFFFFF00'..self:GetName()..':SetScript()|r', ...) end)
end

local BROKEN_ISLE_MAPS = {
  [1007] = true, -- Broken Isle
  [1014] = true, -- Dalaran
  [1021] = true, -- Broken Shoree
  [1024] = true, -- Highmountain
  [1015] = true, -- Azsuna
  [1017] = true, -- Azsuna
  [1018] = true, -- Val'Sharah
  [1033] = true, -- Suramar
  [1077] = true, -- Dreamgrove
  [1096] = true, -- Eye of Azshara
  [1080] = true, -- Thunder Totem
  [1072] = true, -- True Shot Lodge,

}

function WorldPlanCore:SetCurrentMap()
  local mapAreaID = GetCurrentMapAreaID()
  local isBrokenIsle = BROKEN_ISLE_MAPS[mapAreaID]


  db.currentMapID = mapAreaID
  db.isContinentMap = (mapAreaID == BROKEN_ISLES_ID)
  db.useContinentType = (WorldMapDetailFrame:GetScale() < 1)

  for _, module in ipairs(db.OrderedModules) do
    if module.OnMapInfo then
      print('  |cFF00FFFF'..module:GetName() .. ':OnMapInfo()|r')
      module:OnMapInfo(isBrokenIsle, mapAreaID)
    end
  end
end


function WorldPlanCore:OnEvent (event, ...)

  print('|cFF00FF88'..self:GetName().. ':OnEvent()|r', event, GetTime(), 'init:', self.initialized)
  if event == 'ADDON_LOADED' then

    if IsLoggedIn() and not self.initialized then
      self:Setup()
    end
  else
    if (event == 'WORLD_MAP_UPDATE') or (event == 'PLAYER_ENTERING_WORLD') or (event == 'PLAYER_LOGIN') then
      print('|cFFFF4400currentMapID =', db.currentMapID, ...)
      self:SetCurrentMap()
    end
  end
end

function WorldPlanCore:OnNext(func)


  tinsert(self.TaskQueue, func)
  --self:print('|cFF00FF00adding scheduled task #', #self.TaskQueue)
end

function WorldPlanCore:OnUpdate()
  if #self.TaskQueue >= 1 then
    local func = tremove(self.TaskQueue, 1)
    --self:print('|cFF00FF00running scheduled task #', #self.TaskQueue)
    func()
  end

  if self.isStale then
    -- these need to happen in load order
    for i, module in ipairs(db.OrderedModules) do
      if module:IsVisible() and module.isStale then
        print('|cFF00FF00internal '..module:GetName()..':Refresh()|r')
        module:Refresh()
      end
    end
    self.isStale = nil
  end

  if #db.ReportChunks >= 1 then

    DEFAULT_CHAT_FRAME:AddMessage("|cFF0088FF"..addonFileName.."|r: " .. table.concat(db.ReportChunks, ', '))
    wipe(db.ReportChunks)
  end

end

function WorldPlanCore:Setup ()
  print('|cFFFFFF00'..self:GetName()..':Setup()|r')

  if not WorldPlanData then
    WorldPlanData = {key = 0}
  end
  WorldPlanData.key = (WorldPlanData.key or 0) + 1
  db.Config = WorldPlanData
  for k,v in pairs(db.DefaultConfig) do
    --[===[@non-debug@
    if not db.Config[k] then
      db.Config[k] = v
    end

    --@end-non-debug@]===]
    --@debug@
    db.Config[k] = v
    --@end-debug@
  end


  db.currentMapID = GetCurrentMapAreaID()

  for i, module in ipairs(db.OrderedModules) do
    db.Config[module:GetName()] = db.Config[module:GetName()] or {}
    if module.Setup then module:Setup() end
    if not module.RegisterEvent then
      module.RegisterEvent = self.RegisterEvent
    end
  end


  self.initialized = true

  hooksecurefunc("UIDropDownMenu_Initialize", self.OnDropDownInitialize)

  hooksecurefunc("WorldMapTrackingOptionsDropDown_OnClick", function(button)
    print("|cFF0088FFWorldMapTrackingOptionsDropDown_OnClick|r")
    local value = button.value
    if (value == "worldQuestFilterOrderResources" or value == "worldQuestFilterArtifactPower" or
        value == "worldQuestFilterProfessionMaterials" or value == "worldQuestFilterGold" or
        value == "worldQuestFilterEquipment") then
      self:Refresh(true)
    end
  end)


  hooksecurefunc("WorldMapFrame_Update", function()
    print('|cFFFF4400WorldMapFrame_Update|r')
    for _,module in ipairs(db.OrderedModules) do
      if module.OnWorldMapFrameUpdate then
        print('  |cFFFF4400'..module:GetName()..'|r')
        module:OnWorldMapFrameUpdate()
      end
    end
  end)


  SLASH_WORLDPLAN1 = "/worldplan"
  SLASH_WORLDPLAN2 = "/wp"



  SlashCmdList.WORLDPLAN = function(args)
    local arg1, arg2, extraArgs = args:match("(%S+)%s*(%S*)%s*(.*)")

    if arg1 == 'wq' then
      if arg2 and WorldPlanQuests[arg2] then
        self:print('WorldPlanQuests:'..arg2..'()')
        WorldPlanQuests[arg2](WorldPlanQuests)
      elseif arg2 == 'flightscale' and extraArgs then
        local val1, val2, val3 = extraArgs:match("(%S+)%s*(%S*)%s*(%S*)")
        if tonumber(val1) and tonumber(val2) and tonumber(val3) then
          db.Config.FlightMapScalingLimits = {tonumber(val1), tonumber(val2), tonumber(val3)}
          self:print('FlightMapFrame scaling limits updated:', unpack(db.Config.FlightMapScalingLimits))
        else
          self:print('FlightMapFrame scaling limits:', unpack(db.Config.FlightMapScalingLimits))
        end
      elseif arg2 == 'flightalpha' and extraArgs then
        local val1, val2, val3 = extraArgs:match("(%S+)%s*(%S*)%s*(%S*)")
        if tonumber(val1) and tonumber(val2) and tonumber(val3) then
          db.Config.FlightMapAlphaLimits = {tonumber(val1), tonumber(val2), tonumber(val3)}
          self:print('FlightMapFrame alpha limits updated:', unpack(db.Config.FlightMapAlphaLimits))
        else
          self:print('FlightMapFrame alpha limits:', unpack(db.Config.FlightMapAlphaLimits))
        end
      else

        self:print('WorldPlanQuests:Refresh(true)')
        WorldPlanQuests:Refresh(true)
      end
    elseif arg1 == 'filter' then
      if arg2 and WorldPlanSummary[arg2] then
        self:print('WorldPlanSummary:'..arg2..'()')
        WorldPlanSummary[arg2](WorldPlanSummary)
      else
        self:print('WorldPlanSummary:Refresh(true)')
        WorldPlanSummary:Refresh(true)
      end
    else
      self:print('Refreshing data.')
      self:Refresh(true)
    end

  end
end

-- registers a template table
function WorldPlanCore:AddTypeInfo(owner, id, info)
  self.Types[owner] = self.Types[owner] or {}
  self.Types[owner][id] = info
  print('Type('..owner:GetName()..')('..id..') = '.. tostring(info))
end

-- recall a template table, with situational details filled in
function WorldPlanCore:GetTypeInfo(owner, typeID)
  local info, extraInfo
  if not owner then
    --print('## deferring to default type list')
  else
    --print('## pulling for', owner:GetName(), 'id =', typeID)
  end

  owner = owner or self
  if (not typeID) or (not self.Types[owner][typeID]) then
    --print('## sending list default')
    info = db.DefaultType
  else
    --print('## sent list definition', typeID)
    info = self.Types[owner][typeID]
  end

  local subType = 'continent'
  if (
    FlightMapFrame
    and FlightMapFrame:IsVisible()
    and FlightMapFrame:IsZoomedIn()
  ) or (
    not db.isContinentMap
  ) or (
    db.useContinentType == false
  ) then
    subType = 'zone'
  end

  return info, info[subType] or db.DefaultType[subType]
end

do
  local timeStates = {
    {maxSeconds = 60,
      r=1, g=0.25, b =0, format = function (minutes) return '|cFFFF4400'.. minutes .. 'm' end,
    },
    {maxSeconds = 240,
      r=1, g=.5, b=0, format = function(minutes) return '|cFFFF4400'.. floor(minutes/60) .. 'h' end,
    },
    {maxSeconds = 1440,
      r=1, g=1, b=0, format = function(minutes) return '|cFFFFFF00'.. floor(minutes/60) .. 'h' end,
    },
    {maxSeconds = 10081,
      r=0, g=1, b=0,
    }, -- 7 days + 1 minute
  }
  -- Generates a timeleft string
  function WorldPlanCore:GetTimeInfo(timeLeft, limit)
    for index = 1, limit do
      local state = timeStates[index]
      if timeLeft <= state.maxSeconds then
        local text
        if state.format then
          text = state.format(timeLeft)
        end
        return text, index
      end
    end
    return nil, nil
  end
end

function WorldPlanCore:Refresh (forced)
  print('|cFFFFFF00'..self:GetName()..':Refresh()|r forced:', forced, 'init:', self.initialized)
  if not self.initialized then
    return
  end

  for i, module in ipairs(db.OrderedModules) do
    if module.Refresh then
      print('|cFF00FF00external '..module:GetName()..':Refresh()|r')
      module:Refresh(forced)
    end
  end

  self.isStale = nil
end

-- insert visual options into the tracking button menu
WorldPlanCore.OnDropDownInitialize = function  (self, callback, dropType)
  if self ~= WorldMapFrameDropDown then
    return
  end
  local config = WorldPlanData
  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 = "Enable"
  info.isNotRadio = true
  info.value = "EnablePins"
  info.checked = config.EnablePins
  info.tooltipTitle = "Enable World Quest Overlays"
  info.tooltipText = "Toggle the detail layers here."
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)

  info.text = "Display All Profession Quests"
  info.isNotRadio = true
  info.value = "ShowAllProfessionQuests"
  info.checked = config.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 = config.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 = config.DisplayContinentSummary
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)


  info.text = "Nudge Pins"
  info.isNotRadio = true
  info.value = "NudgePins"
  info.tooltipTitle = "Pin Nudging"
  info.tooltipText = "Adjust the position of quest pins that overlap."
  info.checked = config.NudgePins
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)

  info.text = "Fade Whiled Grouped"
  info.isNotRadio = true
  info.value = "FadeWhileGrouped"
  info.tooltipTitle = "Group Fade"
  info.tooltipText = "Reduce pin alpha when grouped, so player dots are easier to see."
  info.checked = config.FadeWhileGrouped
  info.func = DropDown_OnClick
  UIDropDownMenu_AddButton(info)
end

--------------------------------------------------------------------------------------------------------------------
-------------------







--%end-debug%