Quantcast

Save the tick length as part of the aura properties.

Johnny C. Lam [03-28-13 - 04:33]
Save the tick length as part of the aura properties.

Set the tick information directly in AddAura method instead of the CLEU
event handler in case CLEU and UNIT_AURA events arrive out of order.

Adjust the API by changing methods that returned the spell haste
multiplier to return the tick length instead.  The primary use of the
spell haste multiplier was to compute the tick length later on anyway.

Drop the hasteMultiplier argument to GetDuration and GetTickLength since
those two methods are now never called with that argument set.

Enhance GetTickLength and GetDuration to accept spell list name in place
of spell ID.

Note that the various ``tick'' conditions can take a spell list.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@834 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleAura.lua
OvaleCondition.lua
OvaleData.lua
OvaleState.lua
diff --git a/OvaleAura.lua b/OvaleAura.lua
index 40338de..f1a9a2a 100644
--- a/OvaleAura.lua
+++ b/OvaleAura.lua
@@ -26,7 +26,6 @@ local select = select
 local strfind = string.find
 local tinsert = table.insert
 local tsort = table.sort
-local API_IsHarmfulSpell = IsHarmfulSpell
 local API_UnitAura = UnitAura

 local self_pool = OvalePool:NewPool("OvaleAura_pool")
@@ -48,21 +47,25 @@ local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuf
 	-- Re-use existing aura by updating its serial number and adding new information
 	-- if it differs from the old aura.
 	local mine = (unitCaster == "player")
-	local aura
+	local aura, oldAura
 	if mine then
-		if not auraList[spellId].mine then
+		oldAura = auraList[spellId].mine
+		if oldAura then
+			aura = oldAura
+		else
 			aura = self_pool:Get()
 			aura.gain = Ovale.now
 			auraList[spellId].mine = aura
 		end
-		aura = auraList[spellId].mine
 	else
-		if not auraList[spellId].other then
+		oldAura = auraList[spellId].other
+		if oldAura then
+			aura = oldAura
+		else
 			aura = self_pool:Get()
 			aura.gain = Ovale.now
 			auraList[spellId].other = aura
 		end
-		aura = auraList[spellId].other
 	end

 	aura.serial = self_serial
@@ -70,7 +73,8 @@ local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuf
 		count = 1
 	end

-	if not aura.ending or aura.ending < expirationTime or aura.stacks ~= count then
+	local isSameAura = oldAura and oldAura.duration == duration and oldAura.ending == expirationTime and oldAura.stacks == count
+	if not isSameAura and not aura.ending or aura.ending < expirationTime or aura.stacks ~= count then
 		aura.icon = icon
 		aura.stacks = count
 		aura.debuffType = debuffType
@@ -87,6 +91,13 @@ local function AddAura(unitGUID, spellId, filter, unitCaster, icon, count, debuf
 		aura.source = unitCaster
 		aura.name = name
 		aura.value = value
+		if mine then
+			-- This is a new or refreshed aura applied by the player, so set the tick information if needed.
+			local si = OvaleData.spellInfo[spellId]
+			if si and si.tick then
+				aura.tick = OvaleData:GetTickLength(spellId)
+			end
+		end
 	end
 end

@@ -224,24 +235,11 @@ function OvaleAura:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
 		RemoveAurasForGUID(destGUID)
 	elseif strfind(event, "SPELL_AURA_") == 1 then
 		-- KNOWN BUG: an aura refreshed by a spell other than then one that applies it won't cause the CLEU event to fire.
-		local spellId, spellName, spellSchool, auraType = select(12, ...)
-
 		-- Only update for "*target" unit IDs.  All others are handled by UNIT_AURA event handler.
 		local unitId = OvaleGUID:GetUnitId(destGUID)
 		if unitId and unitId ~= "target" and strfind(unitId, "target") then
 			UpdateAuras(unitId, destGUID)
 		end
-
-		if sourceGUID == OvaleGUID:GetGUID("player") and (event == "SPELL_AURA_APPLIED" or event == "SPELL_AURA_REFRESH" or event == "SPELL_AURA_APPLIED_DOSE") then
-			local filter = "HELPFUL"
-			if API_IsHarmfulSpell(spellName) then
-				filter = "HARMFUL"
-			end
-			if self:GetAuraByGUID(destGUID, spellId, filter, true) then
-				local aura = self_aura[destGUID][filter][spellId].mine
-				aura.spellHasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier()
-			end
-		end
 	end
 end

@@ -313,7 +311,7 @@ function OvaleAura:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 		end
 	end
 	if not aura then return nil end
-	return aura.start, aura.ending, aura.stacks, aura.spellHasteMultiplier, aura.value, aura.gain
+	return aura.start, aura.ending, aura.stacks, aura.tick, aura.value, aura.gain
 end

 function OvaleAura:GetAura(unitId, spellId, filter, mine)
@@ -321,19 +319,19 @@ function OvaleAura:GetAura(unitId, spellId, filter, mine)
 	if type(spellId) == "number" then
 		return self:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 	elseif OvaleData.buffSpellList[spellId] then
-		local newStart, newEnding, newStacks, newSpellHasteMultiplier, newValue, newGain
+		local newStart, newEnding, newStacks, newTick, newValue, newGain
 		for _, v in pairs(OvaleData.buffSpellList[spellId]) do
-			local start, ending, stacks, spellHasteMultiplier, value, gain = self:GetAuraByGUID(guid, v, filter, mine, unitId)
+			local start, ending, stacks, tick, value, gain = self:GetAuraByGUID(guid, v, filter, mine, unitId)
 			if start and (not newStart or stacks > newStacks) then
 				newStart = start
 				newEnding = ending
 				newStacks = stacks
-				newSpellHasteMultiplier = spellHasteMultiplier
+				newTick = tick
 				newValue = value
 				newGain = gain
 			end
 		end
-		return newStart, newEnding, newStacks, newSpellHasteMultiplier, newValue, newGain
+		return newStart, newEnding, newStacks, newTick, newValue, newGain
 	elseif spellId == "Magic" or spellId == "Disease" or spellId == "Curse" or spellId == "Poison" then
 		return self:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 	end
@@ -426,7 +424,7 @@ function OvaleAura:Debug()
 		for filter, auraList in pairs(auraTable) do
 			for spellId, whoseTable in pairs(auraList) do
 				for whose, aura in pairs(whoseTable) do
-					Ovale:Print(guid.. " " ..filter.. " " ..whose.. " " ..spellId.. " " ..aura.name.. " stacks=" ..aura.stacks)
+					Ovale:Print(guid.. " " ..filter.. " " ..whose.. " " ..spellId.. " " ..aura.name.. " stacks=" ..aura.stacks.. " tick=" ..tostring(aura.tick))
 				end
 			end
 		end
diff --git a/OvaleCondition.lua b/OvaleCondition.lua
index 95bdc01..dabf5cd 100644
--- a/OvaleCondition.lua
+++ b/OvaleCondition.lua
@@ -310,8 +310,8 @@ local function getMine(condition)
 	return true
 end

--- Recherche un aura sur la cible et récupère sa durée et le nombre de stacks
--- return start, ending, stacks, spellHasteMultiplier
+-- Front-end for OvaleState:GetAura() using condition parameters.
+-- return start, ending, stacks, tick, value, gain
 local function GetAura(condition)
 	local unitId = getTarget(condition)
 	local spellId = condition[1]
@@ -320,21 +320,21 @@ local function GetAura(condition)

 	if not spellId then
 		Ovale:Log("GetAura: nil spellId")
-		return 0, 0, 0, 0, 0, 0
+		return nil
 	end
-	local start, ending, stacks, spellHasteMultiplier, value, gain = OvaleState:GetAura(unitId, spellId, filter, mine)
+	local start, ending, stacks, tick, value, gain = OvaleState:GetAura(unitId, spellId, filter, mine)

 	if not start then
 		Ovale:Log("GetAura: aura " .. tostring(spellId) .. " not found on " .. unitId .. " filter=" .. tostring(filter) .. " mine=" .. tostring(mine))
-		return 0, 0, 0, 0, 0, 0
+		return nil
 	end
 	local conditionStacks = condition.stacks or 1
 	if stacks and stacks < conditionStacks then
 		Ovale:Log("GetAura: aura " .. tostring(spellId) .. " found on " .. unitId .. " but stacks " .. stacks .. " < " .. conditionStacks)
-		return 0, 0, 0, 0, 0, 0
+		return nil
 	end
 	Ovale:Log("GetAura: aura " .. tostring(spellId) .. " found on " .. unitId .. " start=" .. tostring(start) .. " ending=" .. tostring(ending) .. " stacks=" .. tostring(stacks) .. "/" .. conditionStacks)
-	return start, ending, stacks, spellHasteMultiplier, value, gain
+	return start, ending, stacks, tick, value, gain
 end

 -- Returns:
@@ -562,10 +562,13 @@ OvaleCondition.conditions.debuffduration = OvaleCondition.conditions.buffduratio

 OvaleCondition.conditions.buffexpires = function(condition)
 	local start, ending = GetAura(condition)
+	if not start then
+		ending = 0
+	end
 	local timeBefore = avecHate(condition[2], condition.haste)
 	if Ovale.trace then
 		Ovale:Print("timeBefore = " .. tostring(timeBefore))
-		Ovale:Print("start = " .. tostring(ending))
+		Ovale:Print("ending = " .. tostring(ending))
 	end
 	return addTime(ending, -timeBefore)
 end
@@ -635,6 +638,9 @@ OvaleCondition.conditions.debuffgain = OvaleCondition.conditions.buffgain

 OvaleCondition.conditions.buffpresent = function(condition)
 	local start, ending = GetAura(condition)
+	if not start then
+		start, ending = 0, 0
+	end
 	local timeBefore = avecHate(condition[2], condition.haste)
 	return start, addTime(ending, -timeBefore)
 end
@@ -660,6 +666,7 @@ OvaleCondition.conditions.debuffpresent = OvaleCondition.conditions.buffpresent

 OvaleCondition.conditions.buffstacks = function(condition)
 	local start, ending, stacks = GetAura(condition)
+	stacks = stacks or 0
 	return start, ending, stacks, 0, 0
 end
 OvaleCondition.conditions.debuffstacks = OvaleCondition.conditions.buffstacks
@@ -1957,7 +1964,7 @@ end
 --- Get the number of seconds until the next tick of a periodic aura on the target.
 -- @name NextTick
 -- @paramsig number
--- @param id The aura spell ID.
+-- @param id The spell ID of the aura or the name of a spell list.
 -- @param filter Optional. The type of aura to check.
 --     Default is any.
 --     Valid values: any, buff, debuff
@@ -1968,11 +1975,10 @@ end
 -- @see Ticks, TicksRemain, TickTime

 OvaleCondition.conditions.nexttick = function(condition)
-	local start, ending, _, spellHasteMultiplier = GetAura(condition)
-	local tickLength = OvaleData:GetTickLength(condition[1], spellHasteMultiplier)
-	if ending and tickLength then
-		while ending - tickLength > OvaleState.currentTime do
-			ending = ending - tickLength
+	local start, ending, _, tick = GetAura(condition)
+	if ending and tick then
+		while ending - tick > OvaleState.currentTime do
+			ending = ending - tick
 		end
 		return 0, nil, 0, ending, -1
 	end
@@ -2512,7 +2518,7 @@ end
 --- Get the current tick value of a periodic aura on the target.
 -- @name TickValue
 -- @paramsig number or boolean
--- @param id The aura spell ID.
+-- @param id The spell ID of the aura or the name of a spell list.
 -- @param operator Optional. Comparison operator: equal, less, more.
 -- @param filter Optional. The type of aura to check.
 --     Default is any.
@@ -2536,7 +2542,7 @@ end
 --- Get the estimated total number of ticks of a periodic aura.
 -- @name Ticks
 -- @paramsig number or boolean
--- @param id The aura spell ID.
+-- @param id The spell ID of the aura or the name of a spell list.
 -- @param operator Optional. Comparison operator: equal, less, more.
 -- @param number Optional. The number to compare against.
 -- @return The number of ticks.
@@ -2544,14 +2550,25 @@ end
 -- @see NextTick, TicksRemain, TickTime

 OvaleCondition.conditions.ticks = function(condition)
-	-- TODO: extend to allow checking an existing DoT (how to get DoT duration?)
-	local spellId = condition[1]
-	local duration, tickLength = OvaleData:GetDuration(spellId, OvalePaperDoll:GetSpellHasteMultiplier(), OvaleState.state.combo, OvaleState.state.holy)
-	if tickLength then
-		local numTicks = floor(duration / tickLength + 0.5)
+	local start, ending, _, tick = GetAura(condition)
+	local duration, numTicks
+	if start then
+		-- Aura exists on the target
+		if ending and tick and tick > 0 then
+			duration = ending - start
+			numTicks = floor(duration / tick + 0.5)
+		end
+	else
+		duration, tick = OvaleData:GetDuration(condition[1], OvaleState.state.combo, OvaleState.state.holy)
+		if duration and tick and tick > 0 then
+			numTicks = floor(duration / tick + 0.5)
+		end
+	end
+	if numTicks then
 		return compare(numTicks, condition[2], condition[3])
+	else
+		return nil
 	end
-	return nil
 end

 --- Get the number of ticks that would be added if the dot is refreshed.
@@ -2568,7 +2585,7 @@ end
 --- Get the remaining number of ticks of a periodic aura on a target.
 -- @name TicksRemain
 -- @paramsig number
--- @param id The aura spell ID.
+-- @param id The spell ID of the aura or the name of a spell list.
 -- @param filter Optional. The type of aura to check.
 --     Default is any.
 --     Valid values: any, buff, debuff
@@ -2582,10 +2599,9 @@ end
 --     Spell(shadow_word_pain)

 OvaleCondition.conditions.ticksremain = function(condition)
-	local start, ending, _, spellHasteMultiplier = GetAura(condition)
-	local tickLength = OvaleData:GetTickLength(condition[1], spellHasteMultiplier)
-	if ending and tickLength then
-		return 0, nil, 1, ending, -1/tickLength
+	local start, ending, _, tick = GetAura(condition)
+	if ending and tick and tick > 0 then
+		return 0, nil, 1, ending, -1/tick
 	end
 	return nil
 end
@@ -2593,7 +2609,7 @@ end
 --- Get the number of seconds between ticks of a periodic aura on a target.
 -- @name TickTime
 -- @paramsig number or boolean
--- @param id The aura spell ID.
+-- @param id The spell ID of the aura or the name of a spell list.
 -- @param operator Optional. Comparison operator: equal, less, more.
 -- @param number Optional. The number to compare against.
 -- @param filter Optional. The type of aura to check.
@@ -2607,15 +2623,15 @@ end
 -- @see NextTick, Ticks, TicksRemain

 OvaleCondition.conditions.ticktime = function(condition)
-	local start, ending, _, spellHasteMultiplier = GetAura(condition)
-	if not start or not ending or start > OvaleState.currentTime or ending < OvaleState.currentTime then
-		spellHasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier()
+	local start, ending, _, tick = GetAura(condition)
+	if not tick then
+		tick = OvaleData:GetTickLength(condition[1])
 	end
-	local tickLength = OvaleData:GetTickLength(condition[1], spellHasteMultiplier)
-	if tickLength then
-		return compare(tickLength, condition[2], condition[3])
+	if tick then
+		return compare(tick, condition[2], condition[3])
+	else
+		return nil
 	end
-	return nil
 end

 --- Get the number of seconds elapsed since the player entered combat.
diff --git a/OvaleData.lua b/OvaleData.lua
index 1206945..36313d9 100644
--- a/OvaleData.lua
+++ b/OvaleData.lua
@@ -577,11 +577,21 @@ function OvaleData:GetDamage(spellId, attackpower, spellpower, combo)
 	return damage
 end

-function OvaleData:GetDuration(spellId, spellHasteMultiplier, combo, holy)
-	local si = self.spellInfo[spellId]
+function OvaleData:GetDuration(spellId, combo, holy)
+	local si
+	if type(spellId) == "number" then
+		si = self.spellInfo[spellId]
+	elseif OvaleData.buffSpellList[spellId] then
+		for _, v in pairs(OvaleData.buffSpellList[spellId]) do
+			si = self.spellInfo[v]
+			if si then
+				spellId = v
+				break
+			end
+		end
+	end
 	if si and si.duration then
 		local duration = si.duration
-		spellHasteMultiplier = spellHasteMultiplier or 1
 		combo = combo or 0
 		holy = holy or 1
 		if si.adddurationcp then
@@ -592,7 +602,7 @@ function OvaleData:GetDuration(spellId, spellHasteMultiplier, combo, holy)
 		end
 		if si.tick then	-- DoT
 			--DoT duration is tickLength * numberOfTicks.
-			local tickLength = self:GetTickLength(spellId, spellHasteMultiplier)
+			local tickLength = self:GetTickLength(spellId)
 			local numTicks = floor(duration / tickLength + 0.5)
 			duration = tickLength * numTicks
 			return duration, tickLength
@@ -603,13 +613,28 @@ function OvaleData:GetDuration(spellId, spellHasteMultiplier, combo, holy)
 	end
 end

-function OvaleData:GetTickLength(spellId, spellHasteMultiplier)
-	local si = self.spellInfo[spellId]
+function OvaleData:GetTickLength(spellId)
+	local si
+	if type(spellId) == "number" then
+		si = self.spellInfo[spellId]
+	elseif OvaleData.buffSpellList[spellId] then
+		for _, spellId in pairs(OvaleData.buffSpellList[spellId]) do
+			si = self.spellInfo[spellId]
+			if si then break end
+		end
+	end
 	if si then
 		local tick = si.tick or 3
-		spellHasteMultiplier = spellHasteMultiplier or 1
-		if si.haste == "spell" then
-			return tick / spellHasteMultiplier
+		local hasteMultiplier = 1
+		if si.haste then
+			if si.haste == "spell" then
+				hasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier()
+			elseif si.haste == "melee" then
+				hasteMultiplier = OvalePaperDoll:GetMeleeHasteMultiplier()
+			end
+		end
+		if si.haste then
+			return tick / hasteMultiplier
 		else
 			return tick
 		end
diff --git a/OvaleState.lua b/OvaleState.lua
index a32206d..c52d377 100644
--- a/OvaleState.lua
+++ b/OvaleState.lua
@@ -395,7 +395,7 @@ function OvaleState:ApplySpell(spellId, startCast, endCast, nextCast, nocd, targ

 							-- Set the duration to the proper length if it's a DoT.
 							if auraSpellInfo and auraSpellInfo.duration then
-								duration = OvaleData:GetDuration(auraSpellId, OvalePaperDoll:GetSpellHasteMultiplier(), self.state.combo, self.state.holy)
+								duration = OvaleData:GetDuration(auraSpellId, nil, self.state.combo, self.state.holy)
 							end

 							-- If aura is specified with a duration, then assume stacks == 1.
@@ -403,7 +403,7 @@ function OvaleState:ApplySpell(spellId, startCast, endCast, nextCast, nocd, targ
 								stacks = 1
 							end

-							local oldStart, oldEnding, oldStacks, oldSpellHasteMultiplier = self:GetAuraByGUID(auraGUID, auraSpellId, filter, true, target)
+							local oldStart, oldEnding, oldStacks, oldTick = self:GetAuraByGUID(auraGUID, auraSpellId, filter, true, target)
 							local newAura = self:NewAura(auraGUID, auraSpellId, filter)

 							newAura.mine = true
@@ -422,12 +422,10 @@ function OvaleState:ApplySpell(spellId, startCast, endCast, nextCast, nocd, targ
 										newAura.stacks = oldStacks + stacks
 									end
 									newAura.start = oldStart
-									if isDoT and oldEnding > newAura.start then
-										-- TODO: check that refreshed DoTs take a new snapshot of player stats.
-										local tickLength = OvaleData:GetTickLength(auraSpellId, oldSpellHasteMultiplier)
-										local k = floor((oldEnding - endCast) / tickLength)
-										newAura.ending = oldEnding - tickLength * k + duration
-										newAura.spellHasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier()
+									if isDoT and oldEnding > newAura.start and oldTick then
+										local k = floor((oldEnding - endCast) / oldTick)
+										newAura.ending = oldEnding - oldTick * k + duration
+										newAura.tick = OvaleData:GetTickLength(auraSpellId)
 									else
 										newAura.ending = endCast + duration
 									end
@@ -452,7 +450,7 @@ function OvaleState:ApplySpell(spellId, startCast, endCast, nextCast, nocd, targ
 								newAura.start = endCast
 								newAura.ending = endCast + duration
 								if isDoT then
-									newAura.spellHasteMultiplier = OvalePaperDoll:GetSpellHasteMultiplier()
+									newAura.tick = OvaleData:GetTickLength(auraSpellId)
 								end
 							end
 						end
@@ -550,7 +548,7 @@ function OvaleState:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 	end
 	if aura then
 		Ovale:Log("Found " .. filter .. " aura " .. spellId .. " on " .. tostring(guid))
-		return aura.start, aura.ending, aura.stacks, aura.spellHasteMultiplier, aura.value, aura.gain
+		return aura.start, aura.ending, aura.stacks, aura.tick, aura.value, aura.gain
 	else
 		Ovale:Log("Aura " .. spellId .. " not found in state for " .. tostring(guid))
 		return OvaleAura:GetAuraByGUID(guid, spellId, filter, mine, unitId)
@@ -562,19 +560,19 @@ function OvaleState:GetAura(unitId, spellId, filter, mine)
 	if type(spellId) == "number" then
 		return self:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 	elseif OvaleData.buffSpellList[spellId] then
-		local newStart, newEnding, newStacks, newSpellHasteMultiplier, newValue, newGain
+		local newStart, newEnding, newStacks, newTick, newValue, newGain
 		for _, v in pairs(OvaleData.buffSpellList[spellId]) do
-			local start, ending, stacks, spellHasteMultiplier, value, gain = self:GetAuraByGUID(guid, v, filter, mine, unitId)
+			local start, ending, stacks, tick, value, gain = self:GetAuraByGUID(guid, v, filter, mine, unitId)
 			if start and (not newStart or stacks > newStacks) then
 				newStart = start
 				newEnding = ending
 				newStacks = stacks
-				newSpellHasteMultiplier = spellHasteMultiplier
+				newTick = tick
 				newValue = value
 				newGain = gain
 			end
 		end
-		return newStart, newEnding, newStacks, newSpellHasteMultiplier, newValue, newGain
+		return newStart, newEnding, newStacks, newTick, newValue, newGain
 	elseif spellId == "Magic" or spellId == "Disease" or spellId == "Curse" or spellId == "Poison" then
 		return self:GetAuraByGUID(guid, spellId, filter, mine, unitId)
 	end