view ObjectiveCore.lua @ 22:9b3fa734abff

ObjectiveFrame - polish quest rewards display - implement money objectives - set line metrics in UpdateLine - set block metrics in UpdateBlock (sum of line metrics)
author Nenue
date Sat, 09 Apr 2016 07:32:45 -0400
parents d5ee940de273
children
line wrap: on
line source
--- ${PACKAGE_NAME}
-- @file-author@
-- @project-revision@ @project-hash@
-- @file-revision@ @file-hash@
-- Created: 3/26/2016 1:51 AM
local B, _G = select(2,...).frame, _G
local pairs, setmetatable, type, tostring, band, format = _G.pairs, _G.setmetatable, _G.type, _G.tostring, bit.band, string.format
local ipairs, tinsert, hooksecurefunc = _G.ipairs, _G.tinsert, _G.hooksecurefunc
local PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText = PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText
local AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS = AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS
local QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone = QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone
local AddAutoQuestPopUp = AddAutoQuestPopUp
local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
local print = B.print('Objectives')
local ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame = _G.ObjectiveTrackerFrame, _G.VeneerObjectiveScroll, _G.CreateFrame
local Wrapper = _G.VeneerObjectiveWrapper
local Scroller = VeneerObjectiveWrapper.scrollArea
local Scroll = _G.VeneerObjectiveScroll

--- Performance values
--[[
  self:RegisterEvent("QUEST_LOG_UPDATE");
	self:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED");
	self:RegisterEvent("QUEST_WATCH_LIST_CHANGED");
	self:RegisterEvent("QUEST_AUTOCOMPLETE");
	self:RegisterEvent("QUEST_ACCEPTED");
	self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED");
	self:RegisterEvent("SCENARIO_UPDATE");
	self:RegisterEvent("SCENARIO_CRITERIA_UPDATE");
	self:RegisterEvent("TRACKED_ACHIEVEMENT_UPDATE");
	self:RegisterEvent("ZONE_CHANGED_NEW_AREA");
	self:RegisterEvent("ZONE_CHANGED");
	self:RegisterEvent("QUEST_POI_UPDATE");
	self:RegisterEvent("VARIABLES_LOADED");
	self:RegisterEvent("QUEST_TURNED_IN");
	self:RegisterEvent("PLAYER_MONEY");
 ]]


--- These are the bitfields used by Blizzard_ObjectiveTracker to determine which segments get parsed.
--- They are replicated here so that plugins can make use of any securehook args involving this info.
local OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST                       -- 0x0100
local OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP = OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP	-- 0x0200
local OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE		-- 0x0400
local OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO	    		        -- 0x0800
local OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT	= OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT           -- 0x1000


local OBJECTIVE_TRACKER_UPDATE_STATIC = OBJECTIVE_TRACKER_UPDATE_STATIC					                         	-- 0x0000
local OBJECTIVE_TRACKER_UPDATE_ALL = OBJECTIVE_TRACKER_UPDATE_ALL			                    	          		-- 0xFFFF
local OBJECTIVE_TRACKER_UPDATE_ID = OBJECTIVE_TRACKER_UPDATE_ID                                           -- 0

local OBJECTIVE_TRACKER_UPDATE_QUEST = OBJECTIVE_TRACKER_UPDATE_QUEST				        	                  	-- 0x0001
local OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED = OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED		               		    -- 0x0002
local OBJECTIVE_TRACKER_UPDATE_TASK_ADDED = OBJECTIVE_TRACKER_UPDATE_TASK_ADDED		    	              		-- 0x0004
local OBJECTIVE_TRACKER_UPDATE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_SCENARIO			      	                 	-- 0x0008
local OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE = OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE		       	-- 0x0010
local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT			                   	-- 0x0020
local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED		  	      -- 0x0040
local OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED = OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED		-- 0x0080

local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_ALL -- default
--- Used to determine which trackers are listening for money events
mod.watchMoneyReasons = 0

--- Baseline defaults table; values defined in the files that they pertain to
mod.defaults = {}

--- Tracker display order
mod.orderedNames = {'Bonus', 'AutoQuest', 'Quest', 'Cheevs'}

--- ipairs() argument tables
mod.orderedHandlers = setmetatable({}, {__mode = "k"})
mod.orderedTrackers = setmetatable({}, {__mode = "k"})
mod.indexedTrackers = setmetatable({}, {__mode = "k"})

--- pairs() argument tables
mod.namedTrackers = setmetatable({}, {__mode = "k"})

local WRAPPER_ANCHOR_POINT = 'TOPRIGHT'
local WRAPPER_OFFSET_X = 0
local WRAPPER_OFFSET_Y = -200
local WRAPPER_MAX_HEIGHT = 670
local WRAPPER_WIDTH = 280
local WRAPPER_HEADER_HEIGHT = 24

mod.defaults.Wrapper = {
  AnchorPoint = WRAPPER_ANCHOR_POINT,
  OffsetX = WRAPPER_OFFSET_X,
  OffsetY = WRAPPER_OFFSET_Y,
  Height = WRAPPER_MAX_HEIGHT,
  Width = WRAPPER_WIDTH,
  HeaderHeight = WRAPPER_HEADER_HEIGHT,
  TextSpacing = 3,
  TitleSpacing = 3,
}



--- Tracker module definitions begin here; innards dealing with data retreival and output are defined further in
mod.DefaultHandler = {
  previousHeight = 0,

  name = "temp",
  displayName = "temp",
  updateReasonModule = 0xFF00,
  updateReasonEvent  = 0x00FF,

  numWatched = 0,   --- number of entries being handled
  numBlocks = 0,    --- number of blocks created
  actualBlocks = 0, --- number of blocks in use

  freeBlocks = {},  --- block heap
  usedBlocks = {},

  Info = {},        -- find data by ID
  BlockInfo = {},   -- find data by block ID
  Watched = {},     -- find watchIndex by data ID
  WatchInfo = {},   -- find data by watch index
  WatchBlock = {},  -- find block by watch index
}

mod.AutoQuest = {
  name = "AutoQuest",
  displayName = "Notice",
  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST +
      OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
}
mod.Quest = {
  name = "Quest",
  displayName = "Quests",
  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST +
      OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
}
mod.Cheevs = {
  name = "Cheevs",
  displayName = "Achievements",
  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT,
  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT +
      OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED,
}
mod.Bonus = {
  name = "Bonus",
  displayName = "Bonus Objectives",
  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE,
  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_TASK_ADDED
}

local Tracker_string = function (self)
  return self.name
end
local Tracker_call = function (self, reason)
  self:Update(reason)
end
local Handler_Initialize = function (self, name, index)
  print('Initializing |cFF00FFFF'..name..'|r module...')

  local handler = setmetatable(mod[name] or {}, {
    __tostring = Tracker_string,
    __call = Tracker_call
  })
  if type(mod.orderedHandlers[index]) == 'table' then
    return mod.orderedHandlers[index]
  end

  print('|cFFFFFF00Acquiring locals')
  local preset = {}
  for k, _ in pairs(handler) do
    preset[k] = true
  end


  print('|cFFFF8800Inheriting')
  for k, v in pairs(self) do
    if not handler[k] then
      if type(v) == 'table' then
        -- assume all tables to be local data; don't inherit or ref
        handler[k] = {}
      else
        handler[k] = self[k]
      end
      print('copying', k)
    end
  end
  print('|cFFFF4400'..tostring(name)..'|r:')
  for k, v in pairs(handler) do
    print(format("%32s %8s %s", (preset[k] and '|cFFFFFFFF' or '|cFFFFFF00') .. k .. '|r', type(v), tostring(v)))
  end

  mod[name] = handler

  local trackerName = 'Veneer'..name..'Tracker'
  local handler = mod[name]
  local frame = CreateFrame('Frame', trackerName, _G.VeneerObjectiveScroll, 'VeneerTrackerTemplate')
  frame.title:SetText(handler.displayName)
  mod.SetBlockStyle(frame, 'Tracker', 'Normal')
  handler.frame = frame
  handler.trackerName = trackerName
  handler.lines = {}
  mod.orderedTrackers[index] = frame
  mod.namedTrackers[name] = frame
  mod.indexedTrackers[handler] = frame
  print('|cFFFF0088' .. trackerName .. '|r created for |cFF00FFFF' .. handler.displayName .. '|r module')

  mod.orderedHandlers[index] = handler
  return true
end

local Event = {}
Event.QUEST_LOG_UPDATE =  function()
  return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE
end
Event.QUEST_ACCEPTED = function(questLogIndex, questID)
  if ( IsQuestTask(questID) ) then
    return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED, questID
  else
    if ( AUTO_QUEST_WATCH == "1" and GetNumQuestWatches() < MAX_WATCHABLE_QUESTS ) then
      AddQuestWatch(questLogIndex);
      SetSuperTrackedQuestID(questID);
    end
    return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST
  end
end
Event.QUEST_WATCH_LIST_CHANGED = function(questID, added)
  if ( added ) then
    if ( not IsQuestTask(questID) ) then
      return OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, questID, added
    end
  else
    return OBJECTIVE_TRACKER_UPDATE_QUEST, questID, added
  end
end
Event.QUEST_POI_UPDATE = function()

  QuestPOIUpdateIcons();
  if ( GetCVar("trackQuestSorting") == "proximity" ) then
    SortQuestWatches();
  end

  return OBJECTIVE_TRACKER_UPDATE_ALL

end
Event.SUPER_TRACKED_QUEST_CHANGED = function()
  return OBJECTIVE_TRACKER_UPDATE_QUEST
end
Event.ZONE_CHANGED = function()

  local inMicroDungeon = IsPlayerInMicroDungeon();
  if ( inMicroDungeon ~= mod.inMicroDungeon ) then
    if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
      SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
    end
    --SortQuestWatches();
    mod.inMicroDungeon = inMicroDungeon;
  end
end
Event.QUEST_AUTOCOMPLETE = function(questId)
  AddAutoQuestPopUp(questId, "COMPLETE");
  return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP
end
Event.SCENARIO_CRITERIA_UPDATE =  function()
  return OBJECTIVE_TRACKER_UPDATE_SCENARIO
end
Event.SCENARIO_UPDATE = function(newStage)
  if ( newStage ) then
    return OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE
  else
    return OBJECTIVE_TRACKER_UPDATE_SCENARIO
  end
end
Event.TRACKED_ACHIEVEMENT_UPDATE = function()
  return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT
end
Event.TRACKED_ACHIEVEMENT_LIST_CHANGED = function(achievementID, added)
  if ( added ) then
    return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, achievementID
  else
    return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT
  end
end
Event.ZONE_CHANGED_NEW_AREA = function ()
  if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
    SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
  end
  SortQuestWatches();
  mod.currentZoneArea = GetCurrentMapAreaID()
  print('Updating zone ID to', mod.currentZoneArea, '=', GetZoneText(), GetMinimapZoneText())


  return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED
end


Event.PLAYER_MONEY = function()
  if mod.watchMoneyReasons > 0 then
    return mod.watchMoneyReasons
  end
end
Event.CRITERIA_COMPLETE = function()
  return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE
end
Event.QUEST_TURN_IN = function(questID, xp, money)
  if ( IsQuestTask(questID) ) then
    mod.Bonus:OnTurnIn(questID, xp, money)
    print('updating bonus modules (code', OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, ',', questID, xp, money)
    return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, questID, xp, money
  else
    return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST, questID, xp, money
  end
end
mod.Event = Event

--- Done once per ui load
local BlizzHooks = {
  ['AddQuestWatch'] = 'AddQuestWatch',
  ['RemoveQuestWatch'] = 'RemoveQuestWatch',
  ['AbandonQuest'] = 'AbandonQuest',
  ['AcknowledgeAutoAcceptQuest'] = 'AcknowledgeAutoAcceptQuest',
  ['AddAutoQuestPopUp'] = 'AddAutoQuestPopUp',
  ['RemoveAutoQuestPopUp'] = 'RemoveAutoQuestPopUp',
  ['AddTrackedAchievement'] = 'AddTrackedAchievement',
  ['RemoveTrackedAchievement'] = 'RemoveTrackedAchievement',
  ['SetSuperTrackedQuestID'] = 'SetSuperTrackedQuestID'
}
local VeneerData

mod.SetWatchMoney = function(watchMoney, reason)
  if watchMoney then
    if band(mod.watchMoneyReasons, reason) == 0 then
      mod.watchMoneyReasons = mod.watchMoneyReasons + reason;
    end
  else
    if band(mod.watchMoneyReasons, reason) > 0 then
      mod.watchMoneyReasons = mod.watchMoneyReasons - reason;
    end
  end
end


function mod:OnInitialize()
  local c = mod.Conf.Wrapper
  VeneerData = _G.VeneerData
  VeneerData.CallLog = VeneerData.CallLog or {}
  if not mod.isHooked then
    mod.isHooked = true
    for blizzFunc, veneerFunc in pairs(BlizzHooks) do
      if mod[veneerFunc] then
        hooksecurefunc(blizzFunc, mod[veneerFunc])
      else
        hooksecurefunc(blizzFunc, function(...)
          print('|cFFFF0088securehook('..tostring(blizzFunc)..')|r args:', ...)
          tinsert(VeneerData.CallLog, {blizzFunc, ...})
        end)
      end
    end
  end

  mod.Conf.TasksLog = mod.Conf.TasksLog or {}

  Scroller:SetScrollChild(Scroll)
  Scroller:SetWidth(c.Width)
  Scroll:SetWidth(c.Width)
  Scroll:ClearAllPoints()
  Scroll:SetPoint('TOP', Scroller, 'TOP')
  ObjectiveTrackerFrame:UnregisterAllEvents()
  ObjectiveTrackerFrame:Hide()
end

function mod:OnEvent (event, ...)
  local isHandled
  print('OnEvent(|cFF00FF00'.. event ..'|r):', ...)
  local reason, arg1, arg2, arg3
  if Event[event] then
    if type(Event[event]) == 'function' then
      PlaySoundFile([[Interface\Addons\SharedMedia_MyMedia\sound\Info.ogg]])
      reason, arg1, arg2, arg3 = Event[event](...)
    elseif type(Event[event]) == 'table' then
      PlaySoundFile([[Interface\Addons\SharedMedia_MyMedia\sound\Link.ogg]])
      for i, action in ipairs(Event[event]) do
        if type(action) == 'function' then
          reason, arg1, arg2, arg3 = action(event, ...)
        else
          reason = action
        end

        if reason then
          mod:Update(reason, arg1, arg2, arg3)
        end
      end
    else
      PlaySoundFile([[Interface\Addons\SharedMedia_MyMedia\sound\Heart.ogg]])
      reason = Event[event]
    end
  else
    PlaySoundFile([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]])
  end
  if reason then
    mod:Update(reason, arg1, arg2, arg3)
  else
    print('no reason value returned')
    PlaySoundFile([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]])
  end
end

--- Done any time the the minimize button is toggled up
function mod:OnEnable()
  for id, name in ipairs(mod.orderedNames) do
    if not mod.orderedHandlers[id] then
      Handler_Initialize(mod.DefaultHandler, name, id)
    end
  end

  for event, action in pairs(Event) do
    print('|cFFFF0088listen to', event, 'for action|r', tostring(action))
    Wrapper:RegisterEvent(event)
  end

  Wrapper:SetScript('OnEvent', mod.OnEvent)

  local c = mod.Conf.Wrapper
  --Wrapper:SetPoint(c.AnchorPoint, UIParent, c.AnchorPoint, c.OffsetX, c.OffsetY)
  --B.Conf.FramePosition[Wrapper:GetName()] = {c.AnchorPoint, c.AnchorPoint, c.OffsetX, c.OffsetY}
  Wrapper:SetWidth(c.Width)


  mod.InitializeWidgets()
  mod:Update()
end

function mod:OnDisable()
  Wrapper:UnregisterAllEvents()
end