Quantcast
--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 2012 Sidoine
    Copyright (C) 2012, 2013 Johnny C. Lam

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License in the LICENSE
    file accompanying this program.
--]]--------------------------------------------------------------------

-- Gather information about ennemies

local _, Ovale = ...
local OvaleEnemies = Ovale:NewModule("OvaleEnemies", "AceEvent-3.0", "AceTimer-3.0")
Ovale.OvaleEnemies = OvaleEnemies

--<private-static-properties>
-- Profiling set-up.
local Profiler = Ovale.Profiler
local profiler = nil
do
	local group = OvaleEnemies:GetName()
	Profiler:RegisterProfilingGroup(group)
	profiler = Profiler:GetProfilingGroup(group)
end

local bit_band = bit.band
local pairs = pairs
local tostring = tostring
local wipe = table.wipe
local API_GetTime = GetTime
local COMBATLOG_OBJECT_AFFILIATION_OUTSIDER = COMBATLOG_OBJECT_AFFILIATION_OUTSIDER
local COMBATLOG_OBJECT_REACTION_HOSTILE = COMBATLOG_OBJECT_REACTION_HOSTILE

-- Timer for reaper function to remove inactive enemies.
local self_reaperTimer = nil
local REAP_INTERVAL = 3

local OVALE_ENEMIES_DEBUG = "enemy"
--</private-static-properties>

--<public-static-properties>
-- enemyLastSeen[guid] = timestamp
OvaleEnemies.enemyLastSeen = {}
-- enemyName[guid] = name
OvaleEnemies.enemyName = {}

-- Total number of active enemies.
OvaleEnemies.activeEnemies = 0
--</public-static-properties>

--<public-static-methods>
function OvaleEnemies:OnEnable()
	if not self_reaperTimer then
		self_reaperTimer = self:ScheduleRepeatingTimer("RemoveInactiveEnemies", REAP_INTERVAL)
	end
	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	self:RegisterEvent("PLAYER_REGEN_DISABLED")
end

function OvaleEnemies:OnDisable()
	if not self_reaperTimer then
		self:CancelTimer(self_reaperTimer)
		self_reaperTimer = nil
	end
	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	self:UnregisterEvent("PLAYER_REGEN_DISABLED")
end

function OvaleEnemies:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, cleuEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, ...)
	if cleuEvent == "UNIT_DIED" then
		self:RemoveEnemy(destGUID, true)
	elseif sourceFlags and bit_band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0
			and bit_band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0
			and destFlags and bit_band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
		local now = API_GetTime()
		self:AddEnemy(sourceGUID, sourceName, now)
	elseif destGUID and bit_band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) > 0
			and bit_band(destFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0
			and sourceFlags and bit_band(sourceFlags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 then
		local now = API_GetTime()
		self:AddEnemy(destGUID, destName, now)
	end
end

function OvaleEnemies:PLAYER_REGEN_DISABLED()
	-- Reset enemy tracking when combat starts.
	wipe(self.enemyLastSeen)
	wipe(self.enemyName)
	self.activeEnemies = 0
end

-- Remove enemies that have been inactive for at least REAP_INTERVAL seconds.
-- These enemies are not in combat with your group, out of range, or
-- incapacitated and shouldn't count toward the number of active enemies.
function OvaleEnemies:RemoveInactiveEnemies()
	profiler.Start("OvaleEnemies_RemoveInactiveEnemies")
	local now = API_GetTime()
	for guid, timestamp in pairs(self.enemyLastSeen) do
		if now - timestamp > REAP_INTERVAL then
			self:RemoveEnemy(guid)
		end
	end
	profiler.Stop("OvaleEnemies_RemoveInactiveEnemies")
end

function OvaleEnemies:AddEnemy(guid, name, timestamp)
	profiler.Start("OvaleEnemies_AddEnemy")
	if guid then
		local seen = self.enemyLastSeen[guid]
		self.enemyLastSeen[guid] = timestamp
		self.enemyName[guid] = name
		if not seen then
			self.activeEnemies = self.activeEnemies + 1
			Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "New enemy (%d total): %s (%s)", self.activeEnemies, guid, name)
			Ovale.refreshNeeded["player"] = true
		end
	end
	profiler.Stop("OvaleEnemies_AddEnemy")
end

function OvaleEnemies:RemoveEnemy(guid, isDead)
	profiler.Start("OvaleEnemies_RemoveEnemy")
	if guid then
		local seen = self.enemyLastSeen[guid]
		local name = self.enemyName[guid]
		self.enemyLastSeen[guid] = nil
		if seen then
			if self.activeEnemies > 0 then
				self.activeEnemies = self.activeEnemies - 1
			end
			if isDead then
				Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy died (%d total): %s (%s)", self.activeEnemies, guid, name)
			else
				Ovale:DebugPrintf(OVALE_ENEMIES_DEBUG, "Enemy removed (%d total): %s (%s), last seen at %f", self.activeEnemies, guid, name, seen)
			end
			self:SendMessage("Ovale_InactiveUnit", guid)
			Ovale.refreshNeeded["player"] = true
		end
	end
	profiler.Stop("OvaleEnemies_RemoveEnemy")
end

function OvaleEnemies:Debug()
	for guid, timestamp in pairs(self.enemyLastSeen) do
		Ovale:FormatPrint("enemy %s (%s) last seen at %f", guid, self.enemyName[guid], timestamp)
	end
end
--</public-static-methods>