Quantcast

Keep snapshots of player stats from the past 5 seconds.

Johnny C. Lam [08-10-13 - 18:40]
Keep snapshots of player stats from the past 5 seconds.

A snapshot is taken each time one of the player's stats changes, with
changes that occur at the same time (based on GetTime()) collected into
the same snapshot.

OvalePaperDoll.stat is now a pointer to the head of the snapshot queue,
pointing to the most recent snapshot.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1006 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvalePaperDoll.lua
diff --git a/OvalePaperDoll.lua b/OvalePaperDoll.lua
index 3e15af3..e93ad35 100644
--- a/OvalePaperDoll.lua
+++ b/OvalePaperDoll.lua
@@ -15,6 +15,8 @@ Ovale.OvalePaperDoll = OvalePaperDoll

 --<private-static-properties>
 local OvaleEquipement = Ovale.OvaleEquipement
+local OvalePool = Ovale.OvalePool
+local OvaleQueue = Ovale.OvaleQueue
 local OvaleStance = Ovale.OvaleStance

 local select = select
@@ -37,6 +39,13 @@ local API_UnitRangedAttackPower = UnitRangedAttackPower
 local API_UnitSpellHaste = UnitSpellHaste
 local API_UnitStat = UnitStat

+-- Snapshot table pool.
+local self_pool = OvalePool:NewPool("OvalePaperDoll_pool")
+-- Snapshot queue: new snapshots are inserted at the front of the queue.
+local self_snapshot = OvaleQueue:NewDeque("OvalePaperDoll_snapshot")
+-- Time window (past number of seconds) for which snapshots are stored.
+local SNAPSHOT_WINDOW = 5
+
 local OVALE_PAPERDOLL_DEBUG = "paper_doll"
 local OVALE_SPELLDAMAGE_SCHOOL = {
 	DEATHKNIGHT = 4, -- Nature
@@ -97,14 +106,37 @@ OvalePaperDoll.level = API_UnitLevel("player")
 -- Player's current specialization.
 OvalePaperDoll.specialization = nil
 -- Most recent snapshot.
-OvalePaperDoll.stat = {}
-do
-	for k in pairs(OVALE_SNAPSHOT_STATS) do
-		OvalePaperDoll.stat[k] = 0
+OvalePaperDoll.stat = nil
+--</public-static-properties>
+
+--<private-static-methods>
+-- Return stat table for most recent snapshot no older than the given time.
+local function GetSnapshot(t)
+	local self = OvalePaperDoll
+	self:RemoveOldSnapshots()
+	local stat = self_snapshot:Front()
+	if not stat then
+		local newStat = self_pool:Get()
+		do
+			-- Initialize stat table.
+			for k in pairs(OVALE_SNAPSHOT_STATS) do
+				newStat[k] = 0
+			end
+			newStat.damageMultiplier = 1
+		end
+		newStat.snapshotTime = Ovale.now
+		self_snapshot:InsertFront(newStat)
+		stat = self_snapshot:Front()
+	elseif stat.snapshotTime < t then
+		local newStat = self_pool:Get()
+		self:SnapshotStats(newStat, stat)
+		newStat.snapshotTime = Ovale.now
+		self_snapshot:InsertFront(newStat)
+		stat = self_snapshot:Front()
 	end
-	OvalePaperDoll.stat.damageMultiplier = 1
+	return stat
 end
---</public-static-properties>
+--</private-static-methods>

 --<public-static-methods>
 function OvalePaperDoll:OnEnable()
@@ -115,6 +147,7 @@ function OvalePaperDoll:OnEnable()
 	self:RegisterEvent("PLAYER_DAMAGE_DONE_MODS")
 	self:RegisterEvent("PLAYER_ENTERING_WORLD", "UpdateStats")
 	self:RegisterEvent("PLAYER_LEVEL_UP")
+	self:RegisterEvent("PLAYER_REGEN_ENABLED")
 	self:RegisterEvent("PLAYER_TALENT_UPDATE", "UpdateStats")
 	self:RegisterEvent("SPELL_POWER_CHANGED")
 	self:RegisterEvent("UNIT_ATTACK_POWER")
@@ -126,6 +159,7 @@ function OvalePaperDoll:OnEnable()
 	self:RegisterEvent("UNIT_STATS")
 	self:RegisterMessage("Ovale_EquipmentChanged", "UpdateDamage")
 	self:RegisterMessage("Ovale_StanceChanged", "UpdateDamage")
+	self.stat = GetSnapshot(Ovale.now)
 end

 function OvalePaperDoll:OnDisable()
@@ -136,6 +170,7 @@ function OvalePaperDoll:OnDisable()
 	self:UnregisterEvent("PLAYER_DAMAGE_DONE_MODS")
 	self:UnregisterEvent("PLAYER_ENTERING_WORLD")
 	self:UnregisterEvent("PLAYER_LEVEL_UP")
+	self:UnregisterEvent("PLAYER_REGEN_ENABLED")
 	self:UnregisterEvent("PLAYER_TALENT_UPDATE")
 	self:UnregisterEvent("SPELL_POWER_CHANGED")
 	self:UnregisterEvent("UNIT_ATTACK_POWER")
@@ -150,10 +185,10 @@ function OvalePaperDoll:OnDisable()
 end

 function OvalePaperDoll:COMBAT_RATING_UPDATE(event)
+	self.stat = GetSnapshot(Ovale.now)
 	self.stat.meleeCrit = API_GetCritChance()
 	self.stat.rangedCrit = API_GetRangedCritChance()
 	self.stat.spellCrit = API_GetSpellCritChance(OVALE_SPELLDAMAGE_SCHOOL[self.class])
-	self.stat.snapshotTime = Ovale.now
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f", event, Ovale.now)
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %f%%", OVALE_SNAPSHOT_STATS.meleeCrit, self.stat.meleeCrit)
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %f%%", OVALE_SNAPSHOT_STATS.rangedCrit, self.stat.rangedCrit)
@@ -161,11 +196,11 @@ function OvalePaperDoll:COMBAT_RATING_UPDATE(event)
 end

 function OvalePaperDoll:MASTERY_UPDATE(event)
+	self.stat = GetSnapshot(Ovale.now)
 	if self.level < 80 then
 		self.stat.masteryEffect = 0
 	else
 		self.stat.masteryEffect = API_GetMasteryEffect()
-		self.stat.snapshotTime = Ovale.now
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %f%%",
 			event, Ovale.now, OVALE_SNAPSHOT_STATS.masteryEffect, self.stat.masteryEffect)
 	end
@@ -177,24 +212,28 @@ function OvalePaperDoll:PLAYER_LEVEL_UP(event, level, ...)
 end

 function OvalePaperDoll:PLAYER_DAMAGE_DONE_MODS(event, unitId)
+	self.stat = GetSnapshot(Ovale.now)
 	self.stat.spellBonusHealing = API_GetSpellBonusHealing()
-	self.stat.snapshotTime = Ovale.now
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %d",
 		event, Ovale.now, OVALE_SNAPSHOT_STATS.spellBonusHealing, self.stat.spellBonusHealing)
 end

+function OvalePaperDoll:PLAYER_REGEN_ENABLED(event)
+	self_pool:Drain()
+end
+
 function OvalePaperDoll:SPELL_POWER_CHANGED(event)
+	self.stat = GetSnapshot(Ovale.now)
 	self.stat.spellBonusDamage = API_GetSpellBonusDamage(OVALE_SPELLDAMAGE_SCHOOL[self.class])
-	self.stat.snapshotTime = Ovale.now
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %d",
 		event, Ovale.now, OVALE_SNAPSHOT_STATS.spellBonusDamage, self.stat.spellBonusDamage)
 end

 function OvalePaperDoll:UNIT_ATTACK_POWER(event, unitId)
 	if unitId == "player" then
+		self.stat = GetSnapshot(Ovale.now)
 		local base, posBuff, negBuff = API_UnitAttackPower(unitId)
 		self.stat.attackPower = base + posBuff + negBuff
-		self.stat.snapshotTime = Ovale.now
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %d",
 			event, Ovale.now, OVALE_SNAPSHOT_STATS.attackPower, self.stat.attackPower)
 		self:UpdateDamage(event)
@@ -210,8 +249,8 @@ end

 function OvalePaperDoll:UNIT_RANGEDDAMAGE(event, unitId)
 	if unitId == "player" then
+		self.stat = GetSnapshot(Ovale.now)
 		self.stat.rangedHaste = API_GetRangedHaste()
-		self.stat.snapshotTime = Ovale.now
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %f%%",
 			event, Ovale.now, OVALE_SNAPSHOT_STATS.rangedHaste, self.stat.rangedHaste)
 	end
@@ -220,8 +259,8 @@ end
 function OvalePaperDoll:UNIT_RANGED_ATTACK_POWER(event, unitId)
 	if unitId == "player" then
 		local base, posBuff, negBuff = API_UnitRangedAttackPower(unitId)
+		self.stat = GetSnapshot(Ovale.now)
 		self.stat.rangedAttackPower = base + posBuff + negBuff
-		self.stat.snapshotTime = Ovale.now
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f: %s = %d",
 			event, Ovale.now, OVALE_SNAPSHOT_STATS.rangedAttackPower, self.stat.rangedAttackPower)
 	end
@@ -229,9 +268,9 @@ end

 function OvalePaperDoll:UNIT_SPELL_HASTE(event, unitId)
 	if unitId == "player" then
+		self.stat = GetSnapshot(Ovale.now)
 		self.stat.meleeHaste = API_GetMeleeHaste()
 		self.stat.spellHaste = API_UnitSpellHaste(unitId)
-		self.stat.snapshotTime = Ovale.now
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f", event, Ovale.now)
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %f%%", OVALE_SNAPSHOT_STATS.meleeHaste, self.stat.meleeHaste)
 		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %f%%", OVALE_SNAPSHOT_STATS.spellHaste, self.stat.spellHaste)
@@ -240,26 +279,28 @@ function OvalePaperDoll:UNIT_SPELL_HASTE(event, unitId)
 end

 function OvalePaperDoll:UNIT_STATS(event, unitId)
-	if unitId ~= "player" then return end
-	self.stat.strength = API_UnitStat(unitId, 1)
-	self.stat.agility = API_UnitStat(unitId, 2)
-	self.stat.stamina = API_UnitStat(unitId, 3)
-	self.stat.intellect = API_UnitStat(unitId, 4)
-	self.stat.spirit = API_UnitStat(unitId, 5)
-	self.stat.snapshotTime = Ovale.now
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f", event, Ovale.now)
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.agility, self.stat.agility)
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.intellect, self.stat.intellect)
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.spirit, self.stat.spirit)
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.stamina, self.stat.stamina)
-	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.strength, self.stat.strength)
-	self:COMBAT_RATING_UPDATE(event)
+	if unitId == "player" then
+		self.stat = GetSnapshot(Ovale.now)
+		self.stat.strength = API_UnitStat(unitId, 1)
+		self.stat.agility = API_UnitStat(unitId, 2)
+		self.stat.stamina = API_UnitStat(unitId, 3)
+		self.stat.intellect = API_UnitStat(unitId, 4)
+		self.stat.spirit = API_UnitStat(unitId, 5)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f", event, Ovale.now)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.agility, self.stat.agility)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.intellect, self.stat.intellect)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.spirit, self.stat.spirit)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.stamina, self.stat.stamina)
+		Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %d", OVALE_SNAPSHOT_STATS.strength, self.stat.strength)
+		self:COMBAT_RATING_UPDATE(event)
+	end
 end

 function OvalePaperDoll:UpdateDamage(event)
 	local minDamage, maxDamage, minOffHandDamage, maxOffHandDamage, _, _, damageMultiplier = API_UnitDamage("player")
 	local mainHandAttackSpeed, offHandAttackSpeed = API_UnitAttackSpeed("player")

+	self.stat = GetSnapshot(Ovale.now)
 	self.stat.damageMultiplier = damageMultiplier
 	if self.class == "DRUID" and OvaleStance:IsStance("druid_cat_form") then
 		-- Cat Form: 100% increased auto-attack damage.
@@ -304,7 +345,6 @@ function OvalePaperDoll:UpdateDamage(event)
 	else
 		self.stat.offHandWeaponDamage = 0
 	end
-	self.stat.snapshotTime = Ovale.now

 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "%s @ %f", event, Ovale.now)
 	Ovale:DebugPrintf(OVALE_PAPERDOLL_DEBUG, "    %s = %f", OVALE_SNAPSHOT_STATS.damageMultiplier, self.stat.damageMultiplier)
@@ -342,25 +382,38 @@ function OvalePaperDoll:GetSpellHasteMultiplier()
 	return 1 + self.stat.spellHaste / 100
 end

--- Snapshot the stats into the given table using the same keynames as self.stat.
--- If source is nil, then use the current player stats; otherwise, use the given stat table.
+-- Snapshot the stats into the given table using the same keynames as OVALE_SNAPSHOT_STATS.
+-- If source is nil, then use the most recent player stats; otherwise, use the given stat table.
 function OvalePaperDoll:SnapshotStats(t, source)
 	if not source then
 		self:UpdateStats()
-		source = self.stat
+		source = self_snapshot:Front()
 	end
 	for k in pairs(OVALE_SNAPSHOT_STATS) do
 		if source[k] then
 			t[k] = source[k]
 		end
 	end
-	-- Copy other properties that are relevant for auras that might be present.
+	-- Copy other properties that might be present that are relevant for auras.
 	-- TODO: Holy power?
 	if source.comboPoints then
 		t.comboPoints = source.comboPoints
 	end
 end

+-- Remove snapshots older than SNAPSHOT_WINDOW seconds from now, but always leave most recent snapshot.
+function OvalePaperDoll:RemoveOldSnapshots()
+	local now = Ovale.now
+	while self_snapshot:Size() > 1 do
+		local stat = self_snapshot:Back()
+		if stat then
+			if now - stat.snapshotTime < SNAPSHOT_WINDOW then break end
+			self_snapshot:RemoveBack()
+			self_pool:Release(stat)
+		end
+	end
+end
+
 function OvalePaperDoll:Debug(stat)
 	stat = stat or self.stat
 	Ovale:FormatPrint("Class: %s", self.class)