Quantcast
local oUF = oUF_Villain or oUF
assert(oUF, 'oUF not loaded')

local PLUGIN = _G["TrackingVillain"];

local max, floor = math.max, math.floor;
local tinsert, tremove, tsort, twipe = table.insert, table.remove, table.sort, table.wipe;

local playerGUID = UnitGUID("player")
local _FRAMES, _PROXIMITY, OnUpdateFrame = {}, {}
local minThrottle = 0.02
local numArrows, inRange, GPS
--[[
##########################################################
oUF TAGS
##########################################################
]]--
local taggedUnits = {}
local groupTagManager = CreateFrame("Frame")
groupTagManager:RegisterEvent("GROUP_ROSTER_UPDATE")
groupTagManager:SetScript("OnEvent", function()
	local group, count;
	twipe(taggedUnits)
	if IsInRaid() then
		group = "raid"
		count = GetNumGroupMembers()
	elseif IsInGroup() then
		group = "party"
		count = GetNumGroupMembers() - 1;
		taggedUnits["player"] = true
	else
		group = "solo"
		count = 1
	end
	for i = 1, count do
		local realName = group..i;
		if not UnitIsUnit(realName, "player") then
			taggedUnits[realName] = true
		end
	end
end);

oUF.Tags.OnUpdateThrottle['nearbyplayers:8'] = 0.25
oUF.Tags.Methods["nearbyplayers:8"] = function(unit)
    local unitsInRange, distance = 0;
    if UnitIsConnected(unit)then
        for taggedUnit, _ in pairs(taggedUnits)do
            if not UnitIsUnit(unit, taggedUnit) and UnitIsConnected(taggedUnit)then
                distance = Triangulate(unit, taggedUnit, true)
                if distance and distance <= 8 then
                    unitsInRange = unitsInRange + 1
                end
            end
        end
    end
    return unitsInRange
end

oUF.Tags.OnUpdateThrottle['nearbyplayers:10'] = 0.25
oUF.Tags.Methods["nearbyplayers:10"] = function(unit)
    local unitsInRange, distance = 0;
    if UnitIsConnected(unit)then
        for taggedUnit, _ in pairs(taggedUnits)do
            if not UnitIsUnit(unit, taggedUnit) and UnitIsConnected(taggedUnit)then
                distance = Triangulate(unit, taggedUnit, true)
                if distance and distance <= 10 then
                    unitsInRange = unitsInRange + 1
                end
            end
        end
    end
    return unitsInRange
end

oUF.Tags.OnUpdateThrottle['nearbyplayers:30'] = 0.25
oUF.Tags.Methods["nearbyplayers:30"] = function(unit)
    local unitsInRange, distance = 0;
    if UnitIsConnected(unit)then
        for taggedUnit, _ in pairs(taggedUnits)do
            if not UnitIsUnit(unit, taggedUnit) and UnitIsConnected(taggedUnit)then
                distance = Triangulate(unit, taggedUnit, true)
                if distance and distance <= 30 then
                    unitsInRange = unitsInRange + 1
                end
            end
        end
    end
    return unitsInRange
end

oUF.Tags.OnUpdateThrottle['distance'] = 0.25
oUF.Tags.Methods["distance"] = function(unit)
    if not UnitIsConnected(unit) or UnitIsUnit(unit, "player")then return "" end
    local dst = Triangulate("player", unit, true)
    if dst and dst > 0 then
        return format("%d", dst)
    end
    return ""
end
--[[
##########################################################
GPS ELEMENT
##########################################################
]]--
local sortFunc = function(a,b) return a[1] < b[1] end

local Update = function(self, elapsed)
	if self.elapsed and self.elapsed > (self.throttle or minThrottle) then
		numArrows = 0
		twipe(_PROXIMITY)
		for _, object in next, _FRAMES do
			if(object:IsShown()) then
				GPS = object.GPS
				local unit = object.unit
				if(unit) then
					if(GPS.PreUpdate) then GPS:PreUpdate(frame) end

					local outOfRange = GPS.outOfRange and UnitInRange(unit) or false

					if(not unit or not (UnitInParty(unit) or UnitInRaid(unit)) or UnitIsUnit(unit, "player") or not UnitIsConnected(unit) or (not GPS.OnlyProximity and ((GPS.onMouseOver and (GetMouseFocus() ~= object)) or outOfRange))) then
						GPS:Hide()
					else
						local distance, angle = Triangulate(unit)
						if not angle then
							GPS:Hide()
						else
							if(GPS.OnlyProximity == false) then
								GPS:Show()
							else
								GPS:Hide()
							end

							if GPS.Arrow then
								if(distance > 40) then
									GPS.Arrow:SetVertexColor(1,0.1,0.1)
								else
									if(distance > 30) then
										GPS.Arrow:SetVertexColor(0.4,0.8,0.1)
									else
										GPS.Arrow:SetVertexColor(0.1,1,0.1)
									end
									if(GPS.OnlyProximity and object.Health.percent and object.Health.percent < 80) then
										local value = object.Health.percent + distance
										_PROXIMITY[#_PROXIMITY + 1] = {value, GPS}
									end
								end
								GPS:Spin(angle)
							end

							if GPS.Text then
								GPS.Text:SetText(floor(distance))
							end

							if(GPS.PostUpdate) then GPS:PostUpdate(frame, distance, angle) end
							numArrows = numArrows + 1
						end
					end
				else
					GPS:Hide()
				end
			end
		end

        if(_PROXIMITY[1]) then
        	tsort(_PROXIMITY, sortFunc)
        	if(_PROXIMITY[1][2]) then
	        	_PROXIMITY[1][2]:Show()
	        end
        end

		self.elapsed = 0
		self.throttle = max(minThrottle, 0.005 * numArrows)
	else
		self.elapsed = (self.elapsed or 0) + elapsed
	end
end

local Enable = function(self)
	local unit = self.unit
	if(unit:find("raid") or unit:find("party")) then
		if not self.GPS then PLUGIN:CreateGPS(self) end
		tinsert(_FRAMES, self)

		if not OnUpdateFrame then
			OnUpdateFrame = CreateFrame("Frame")
			OnUpdateFrame:SetScript("OnUpdate", Update)
		end

		OnUpdateFrame:Show()
		return true
	end
end

local Disable = function(self)
	local GPS = self.GPS
	if GPS then
		for k, frame in next, _FRAMES do
			if(frame == self) then
				tremove(_FRAMES, k)
				GPS:Hide()
				break
			end
		end

		if #_FRAMES == 0 and OnUpdateFrame then
			OnUpdateFrame:Hide()
		end
	end
end

oUF:AddElement('GPS', nil, Enable, Disable)