Quantcast
--[[
##############################################################################
_____/\\\\\\\\\\\____/\\\________/\\\__/\\\________/\\\__/\\\\\\\\\\\_       #
 ___/\\\/////////\\\_\/\\\_______\/\\\_\/\\\_______\/\\\_\/////\\\///__      #
  __\//\\\______\///__\//\\\______/\\\__\/\\\_______\/\\\_____\/\\\_____     #
   ___\////\\\__________\//\\\____/\\\___\/\\\_______\/\\\_____\/\\\_____    #
    ______\////\\\________\//\\\__/\\\____\/\\\_______\/\\\_____\/\\\_____   #
     _________\////\\\______\//\\\/\\\_____\/\\\_______\/\\\_____\/\\\_____  #
      __/\\\______\//\\\______\//\\\\\______\//\\\______/\\\______\/\\\_____ #
       _\///\\\\\\\\\\\/________\//\\\________\///\\\\\\\\\/____/\\\\\\\\\\\_#
        ___\///////////___________\///___________\/////////_____\///////////_#
##############################################################################
S U P E R - V I L L A I N - U I   By: Munglunch                              #
##############################################################################
##########################################################
LOCALIZED LUA FUNCTIONS
##########################################################
]]--
--[[ GLOBALS ]]--
local _G = _G;
local unpack    = _G.unpack;
local select    = _G.select;
local pairs     = _G.pairs;
local ipairs    = _G.ipairs;
local type      = _G.type;
local error     = _G.error;
local pcall     = _G.pcall;
local tostring  = _G.tostring;
local tonumber  = _G.tonumber;
local tinsert   = _G.tinsert;
local string    = _G.string;
local math      = _G.math;
local bit       = _G.bit;
local table     = _G.table;
--[[ STRING METHODS ]]--
local lower, upper = string.lower, string.upper;
local find, format, split = string.find, string.format, string.split;
local match, gmatch, gsub = string.match, string.gmatch, string.gsub;
--[[ MATH METHODS ]]--
local floor = math.floor;  -- Basic
--[[ BINARY METHODS ]]--
local band, bor = bit.band, bit.bor;
--[[ TABLE METHODS ]]--
local tremove, tcopy, twipe, tsort, tconcat = table.remove, table.copy, table.wipe, table.sort, table.concat;
--[[
##########################################################
GET ADDON DATA
##########################################################
]]--
local SuperVillain, L = unpack(select(2, ...));
local MOD = SuperVillain.Registry:Expose('SVPlate');
--[[
##########################################################
LOCALS AND TRACKER FRAME
##########################################################
]]--
local NextUpdate = 0;
local SpellIDTest = {
  [47540]=true,[88625]=true,[88684]=true,[88685]=true,
  [89485]=true,[10060]=true,[33206]=true,[62618]=true,
  [724]=true,[14751]=true,[34861]=true,[47788]=true,
  [18562]=true,[17116]=true,[48438]=true,[33891]=true,
  [974]=true,[17116]=true,[16190]=true,[61295]=true,
  [20473]=true,[31842]=true,[53563]=true,[31821]=true,
  [85222]=true,[115175]=true,[115294]=true,[115310]=true,
  [116670]=true,[116680]=true,[116849]=true,[116995]=true,
  [119611]=true,[132120]=true
};
local SpellEventTest = {
  ["SPELL_HEAL"] = true,
  ["SPELL_AURA_APPLIED"] = true,
  ["SPELL_CAST_START"] = true,
  ["SPELL_CAST_SUCCESS"] = true,
  ["SPELL_PERIODIC_HEAL"] = true,
};

local HealSpecs = {
  [L['Restoration']] = true,
  [L['Holy']] = true,
  [L['Discipline']] = true,
  [L['Mistweaver']] = true
};

local TrackingManager = CreateFrame("Frame");
local CPoints = {};
local TrackedPlatesCP = {};
local LastKnownTarget = false;
local LastKnownPoints = 0;
local Healers = {};
local Events = {};
--[[
##########################################################
PARSE FUNCTIONS
##########################################################
]]--
local function TrackViaScores()
  local curTime = GetTime()
  if curTime > NextUpdate then NextUpdate = curTime + 3; else return; end
  local ready = false
  local scores = GetNumBattlefieldScores()
  if(scores > 0) then
    for i = 1, scores do
      local name, _, _, _, _, faction, _, class, _, _, _, _, _, _, _, talentSpec = GetBattlefieldScore(i)

      if name and talentSpec then
        if(HealSpecs[talentSpec]) then
          Healers[name] = true
          ready = true
        elseif(Healers[name]) then
          Healers[name] = nil
        end
      end
    end
    if(ready) then
      MOD:RequestScanUpdate(false, false, name, "UpdateHealerIcon")
    end
  end
end

local function PlateIsHealer(name)
  if name then
    if Healers[name] then
      return true
    else
      RequestBattlefieldScoreData()
    end
  end
end

local function PlateIsEnemy(flags)
  if (band(flags, COMBATLOG_OBJECT_REACTION_FRIENDLY) == 0) and (band(flags, COMBATLOG_OBJECT_CONTROL_PLAYER) > 0) then
    return true
  end
end

local function ParseArenaHealers()
  local numOps = GetNumArenaOpponentSpecs()
  if not (numOps > 1) then return end
  for i=1, 5 do
    local name = UnitName(format('arena%d', i))
    if name and name ~= UNKNOWN then
      local s = GetArenaOpponentSpec(i)
      local _, talentSpec = nil, UNKNOWN
      if s and s > 0 then
        _, talentSpec = GetSpecializationInfoByID(s)
      end
      if talentSpec and HealSpecs[talentSpec] then
        Healers[name] = true
      end
    end
  end
end

local function ClearTracking()
  CPoints = twipe(CPoints)
  Healers = twipe(Healers)
end

local function ClearExpiredPoints()
  local saved
  for plate,_ in pairs(TrackedPlatesCP) do
    if(plate.guid ~= LastKnownTarget) then
      for i=1, MAX_COMBO_POINTS do
        plate.frame.combo[i]:Hide()
      end
    else
      saved = plate
    end
  end
  CPoints = {}
  TrackedPlatesCP = {}

  if(saved) then
    TrackedPlatesCP[saved] = true
  end

  if(LastKnownTarget) then
    CPoints[LastKnownTarget] = LastKnownPoints;
  end
end

local function TrackingEventHandler(self, event, ...)
    local handler = Events[event]
    if handler then handler(...) end
end
--[[
##########################################################
EVENTS
##########################################################
]]--
function Events.UNIT_COMBO_POINTS(unit)
  MOD:UpdateComboPointsByUnitID(unit)
end


function Events.PLAYER_ENTERING_WORLD()
  ClearTracking()
  local inInstance, instanceType = IsInInstance()
  if inInstance and instanceType == 'arena' then
    TrackingManager:RegisterEvent("UNIT_NAME_UPDATE")
    TrackingManager:RegisterEvent("ARENA_OPPONENT_UPDATE");
    ParseArenaHealers()
  else
    TrackingManager:UnregisterEvent("UNIT_NAME_UPDATE")
    TrackingManager:UnregisterEvent("ARENA_OPPONENT_UPDATE")
  end
  return
end

function Events.UNIT_NAME_UPDATE()
  ParseArenaHealers()
  return
end

function Events.PLAYER_TARGET_CHANGED()
  ClearExpiredPoints()
  return
end

function Events.ARENA_OPPONENT_UPDATE()
  ParseArenaHealers()
  return
end

function Events.UPDATE_BATTLEFIELD_SCORE()
  TrackViaScores()
  return
end

function Events.COMBAT_LOG_EVENT_UNFILTERED(...)
  local timestamp, combatevent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlag, spellid  = ...
  if PlateIsEnemy(sourceFlags) and sourceGUID and sourceName then
    if SpellEventTest[combatevent] then
      if SpellIDTest[spellid] then
        local rawName = split("-", sourceName)
        if not Healers[rawName] then
          Healers[rawName] = true
          MOD:RequestScanUpdate(destGUID, false, false, "UpdateHealerIcon")
        end
      end
    end
  end
end
--[[
##########################################################
MODULE FUNCTIONS
##########################################################
]]--
function MOD:EnableTracking()
  TrackingManager:SetScript("OnEvent", TrackingEventHandler)
  TrackingManager:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
  TrackingManager:RegisterEvent("PLAYER_ENTERING_WORLD")
  TrackingManager:RegisterEvent("UPDATE_BATTLEFIELD_SCORE")
  if (MOD.UseCombo) then
    TrackingManager:RegisterEvent("UNIT_COMBO_POINTS")
    TrackingManager:RegisterEvent("PLAYER_TARGET_CHANGED")
  end
  ClearTracking()
end

function MOD:DisableTracking()
  TrackingManager:SetScript("OnEvent", nil)
  TrackingManager:UnregisterAllEvents()
  ClearTracking()
end

function MOD:UpdateHealerIcon(plate)
  local rawName = plate.ref.nametext
  if rawName then
    local healerIcon = plate.frame.health.icon
    if PlateIsHealer(rawName) then
      healerIcon:Show()
    else
      healerIcon:Hide()
    end
  end
end

function MOD:UpdateComboPoints(plate)
  local frame = plate.frame
  local numPoints = CPoints[plate.guid] or 0
  if(numPoints == 0) then
    TrackedPlatesCP[plate] = nil
  else
    TrackedPlatesCP[plate] = true
  end
  for i=1, MAX_COMBO_POINTS do
    if(i <= numPoints) then
      frame.combo[i]:Show()
    else
      frame.combo[i]:Hide()
    end
  end
end

function MOD:UpdateComboPointsByUnitID(unit)
  if(unit == "player" or unit == "vehicle") then
    local guid = UnitGUID("target")
    if (not guid) then return end
    local numPoints = GetComboPoints(UnitHasVehicleUI('player') and 'vehicle' or 'player', 'target')
    if(numPoints > 0) then
      if(LastKnownTarget ~= guid) then
        CPoints[LastKnownTarget] = nil
      end
    end
    LastKnownTarget = guid
    LastKnownPoints = numPoints
    CPoints[guid] = numPoints
    MOD:RequestScanUpdate(guid, false, false, "UpdateComboPoints")
  end
end