Quantcast
--[[--------------------------------------------------------------------
    Ovale Spell Priority
    Copyright (C) 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.
--]]--------------------------------------------------------------------

-- This addon tracks the damage taken by the player from non-player sources.

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

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

local OvalePool = Ovale.OvalePool
local OvaleQueue = Ovale.OvaleQueue

-- Forward declarations for module dependencies.
local OvaleLatency = nil

local API_GetTime = GetTime
local API_UnitGUID = UnitGUID

-- Player's GUID.
local self_guid = nil
-- Damage event pool.
local self_pool = OvalePool("OvaleDamageTaken_pool")
-- Time window (past number of seconds) for which damage events are stored.
local DAMAGE_TAKEN_WINDOW = 20

local OVALE_DAMAGE_TAKEN_DEBUG = "damage_taken"
--</private-static-properties>

--<public-static-properties>
-- Damage event queue: new events are inserted at the front of the queue.
OvaleDamageTaken.damageEvent = OvaleQueue:NewDeque("OvaleDamageTaken_damageEvent")
--</public-static-properties>

--<public-static-methods>
function OvaleDamageTaken:OnInitialize()
	-- Resolve module dependencies.
	OvaleLatency = Ovale.OvaleLatency
end

function OvaleDamageTaken:OnEnable()
	self_guid = API_UnitGUID("player")
	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	self:RegisterEvent("PLAYER_REGEN_ENABLED")
end

function OvaleDamageTaken:OnDisable()
	self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
	self:UnregisterEvent("PLAYER_REGEN_ENABLED")
	self_pool:Drain()
end

function OvaleDamageTaken:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, cleuEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags, ...)
	local arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23 = ...

	if destGUID == self_guid and strsub(cleuEvent, -7) == "_DAMAGE" then
		profiler.Start("OvaleDamageTaken_COMBAT_LOG_EVENT_UNFILTERED")
		local now = API_GetTime()
		local eventPrefix = strsub(cleuEvent, 1, 6)
		if eventPrefix == "SWING_" then
			local amount = arg12
			Ovale:DebugPrintf(OVALE_DAMAGE_TAKEN_DEBUG, "%s caused %d damage.", cleuEvent, amount)
			self:AddDamageTaken(now, amount)
		elseif eventPrefix == "RANGE_" or eventPrefix == "SPELL_" then
			local spellName, amount = arg13, arg15
			Ovale:DebugPrintf(OVALE_DAMAGE_TAKEN_DEBUG, "%s (%s) caused %d damage.", cleuEvent, spellName, amount)
			self:AddDamageTaken(now, amount)
		end
		profiler.Stop("OvaleDamageTaken_COMBAT_LOG_EVENT_UNFILTERED")
	end
end

function OvaleDamageTaken:PLAYER_REGEN_ENABLED(event)
	self_pool:Drain()
end

function OvaleDamageTaken:AddDamageTaken(timestamp, damage)
	profiler.Start("OvaleDamageTaken_AddDamageTaken")
	local event = self_pool:Get()
	event.timestamp = timestamp
	event.damage = damage
	self.damageEvent:InsertFront(event)
	self:RemoveExpiredEvents(timestamp)
	profiler.Stop("OvaleDamageTaken_AddDamageTaken")
end

-- Return the total damage taken in the previous time interval (in seconds).
function OvaleDamageTaken:GetRecentDamage(interval, lagCorrection)
	local now = API_GetTime()
	local lowerBound = now - interval
	if lagCorrection then
		lowerBound = lowerBound - OvaleLatency:GetLatency()
	end
	self:RemoveExpiredEvents(now)

	local total = 0
	for i, event in self.damageEvent:FrontToBackIterator() do
		if event.timestamp < lowerBound then
			break
		end
		total = total + event.damage
	end
	return total
end

-- Remove all events that are more than DAMAGE_TAKEN_WINDOW seconds before the given timestamp.
function OvaleDamageTaken:RemoveExpiredEvents(timestamp)
	profiler.Start("OvaleDamageTaken_RemoveExpiredEvents")
	while true do
		local event = self.damageEvent:Back()
		if not event then break end
		if event then
			if timestamp - event.timestamp < DAMAGE_TAKEN_WINDOW then
				break
			end
			self.damageEvent:RemoveBack()
			self_pool:Release(event)
		end
	end
	profiler.Stop("OvaleDamageTaken_RemoveExpiredEvents")
end

function OvaleDamageTaken:Debug()
	self.damageEvent:Debug()
	for i, event in self.damageEvent:BackToFrontIterator() do
		Ovale:FormatPrint("%d: %d damage", event.timestamp, event.damage)
	end
end
--</public-static-methods>