Quantcast

Fix OvaleEnemies to track the number of enemies correctly.

Johnny C. Lam [03-16-13 - 08:04]
Fix OvaleEnemies to track the number of enemies correctly.

Before this change, the enemies table was not being updated properly when
UNIT_DIED was received.

Split out the routines to add and remove enemies into public methods to
make the code more DRY.

API change: Remove method GetNumberOfEnemies() and introduce a public
static variable ``activeEnemies'' that holds the number of active enemies.

Schedule a timer to remove enemies that haven't been active for at least 3
seconds.  These are either not in combat with your group, out of range, or
incapacitated and shouldn't count toward your active enemies.

When a unit is no longer an active enemy, throw a new Ovale event
``Ovale_InactiveUnit'' that has the GUID of the dead unit as an event
parameter.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@780 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleCondition.lua
OvaleEnemies.lua
diff --git a/OvaleCondition.lua b/OvaleCondition.lua
index 8eabbe8..dcc9195 100644
--- a/OvaleCondition.lua
+++ b/OvaleCondition.lua
@@ -1195,7 +1195,7 @@ end
 -- if Enemies(more 4) Spell(fan_of_knives)

 OvaleCondition.conditions.enemies = function(condition)
-	return compare(OvaleEnemies:GetNumberOfEnemies(), condition[1], condition[2])
+	return compare(OvaleEnemies.activeEnemies, condition[1], condition[2])
 end

 --- Get the current amount of energy for feral druids, non-mistweaver monks, and rogues.
diff --git a/OvaleEnemies.lua b/OvaleEnemies.lua
index 1d4f5c1..4e60065 100644
--- a/OvaleEnemies.lua
+++ b/OvaleEnemies.lua
@@ -10,71 +10,120 @@
 -- Gather information about ennemies

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

 --<private-static-properties>
-local bit_band, pairs, select, tostring = bit.band, pairs, select, tostring
-local wipe = wipe
+local bit_band = bit.band
+local pairs = pairs
+local select = select
+local time = time
+local tostring = tostring
+local wipe = table.wipe

 local COMBATLOG_OBJECT_AFFILIATION_OUTSIDER = COMBATLOG_OBJECT_AFFILIATION_OUTSIDER
 local COMBATLOG_OBJECT_REACTION_HOSTILE = COMBATLOG_OBJECT_REACTION_HOSTILE

-numberOfEnemies = 0
-enemies = {}
+-- enemyLastSeen[guid] = timestamp
+local enemyLastSeen = {}
+-- enemyName[guid] = name
+local enemyName = {}
+-- timer for reaper function to remove inactive enemies
+local reaperTimer = nil
+local REAP_INTERVAL = 3
 --</private-static-properties>

+--<public-static-properties>
+OvaleEnemies.activeEnemies = 0
+--</public-static-properties>
+
+--<private-static-methods>
+function AddEnemy(guid, name, timestamp)
+	if not guid then return end
+	local seen = enemyLastSeen[guid]
+	enemyLastSeen[guid] = timestamp
+	enemyName[guid] = name
+	if not seen then
+		OvaleEnemies.activeEnemies = OvaleEnemies.activeEnemies + 1
+		Ovale:DebugPrint("enemy", "New enemy (" .. OvaleEnemies.activeEnemies .. " total): " .. guid .. "(" .. tostring(name) .. ")")
+		Ovale.refreshNeeded["player"] = true
+	end
+end
+
+function RemoveEnemy(guid, isDead)
+	if not guid then return end
+	local seen = enemyLastSeen[guid]
+	local name = enemyName[guid]
+	enemyLastSeen[guid] = nil
+	if seen then
+		if OvaleEnemies.activeEnemies > 0 then
+			OvaleEnemies.activeEnemies = OvaleEnemies.activeEnemies - 1
+		end
+		if isDead then
+			Ovale:DebugPrint("enemy", "Enemy died (" .. OvaleEnemies.activeEnemies .. " total): " .. guid .. " (" .. tostring(name) .. ")")
+		else
+			Ovale:DebugPrint("enemy", "Enemy removed: (" .. OvaleEnemies.activeEnemies .. " total): " .. guid .. " (" .. tostring(name) .. "), last seen at " .. seen)
+		end
+		OvaleEnemies:SendMessage("Ovale_InactiveUnit", guid)
+		Ovale.refreshNeeded["player"] = true
+	end
+end
+--</private-static-methods>
+
 --<public-static-methods>
 function OvaleEnemies:OnEnable()
+	if not reaperTimer then
+		reaperTimer = self:ScheduleRepeatingTimer("RemoveInactiveEnemies", REAP_INTERVAL)
+	end
 	self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
 	self:RegisterEvent("PLAYER_REGEN_DISABLED")
 end

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

 function OvaleEnemies:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
-	local time, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)
+	local timestamp, event, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags = select(1, ...)

 	if event == "UNIT_DIED" then
-		for k,v in pairs(enemies) do
-			if k==destGUID then
-				enemies[v] = nil
-				numberOfEnemies = numberOfEnemies - 1
-				Ovale.refreshNeeded["player"] = true
-				Ovale:DebugPrint("enemy", "enemy die")
-			end
-		end
-	elseif sourceFlags and not enemies[sourceGUID] 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
-		enemies[sourceGUID] = true
-		Ovale:DebugPrint("enemy", "new enemy source=" .. tostring(sourceName))
-		numberOfEnemies = numberOfEnemies + 1
-		Ovale.refreshNeeded["player"] = true
-	elseif destGUID and not enemies[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
-		enemies[destGUID] = true
-		Ovale:DebugPrint("enemy", "new enemy dest=".. tostring(destName))
-		numberOfEnemies = numberOfEnemies + 1
-		Ovale.refreshNeeded["player"] = true
+		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
+		AddEnemy(sourceGUID, sourceName, Ovale.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
+		AddEnemy(destGUID, destName, Ovale.now)
 	end
 end

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

-function OvaleEnemies:GetNumberOfEnemies()
-	if not numberOfEnemies then
-		numberOfEnemies = 0
+-- 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()
+	for guid, timestamp in pairs(enemyLastSeen) do
+		if Ovale.now - timestamp > REAP_INTERVAL then
+			RemoveEnemy(guid)
+		end
 	end
-	return numberOfEnemies
 end
---</public-static-methods>

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