Quantcast

Add additional phase of tracking to spellcasts: "after hit".

Johnny C. Lam [05-12-14 - 16:08]
Add additional phase of tracking to spellcasts: "after hit".

ApplySpellAfterHit() is called after the three previous phases:

    ApplySpellStartCast
    ApplySpellAfterCast
    ApplySpellOnHit
    ApplySpellAfterHit

Apply spell auras to the target during ApplySpellAfterHit().  Only remove
a spellcast from the spells-in-flight after it has applied any auras to
the target, which can take a bit of time due to server event lag.  The
simulator will continue to apply spells until it sees their aura effects
appear on the target in reality.

Remove the previous workaround for aura lag added to OvaleAura in r1411
and r1413.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1426 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleAura.lua
OvaleFuture.lua
diff --git a/OvaleAura.lua b/OvaleAura.lua
index df7bc0a..c55ab53 100644
--- a/OvaleAura.lua
+++ b/OvaleAura.lua
@@ -647,39 +647,9 @@ function OvaleAura:ResetState(state)
 	for guid, auraTable in pairs(state.aura) do
 		for auraId, whoseTable in pairs(auraTable) do
 			for casterGUID, aura in pairs(whoseTable) do
-				local keepAura = false
-				local auraFound = GetAura(self.aura, guid, auraId, casterGUID)
-				if auraFound and aura.lastUpdated <= auraFound.lastUpdated then
-					Ovale:Logf("    Aura %d on %s more recently updated outside simulator.", auraId, guid)
-				elseif state.lastSpellId and state.endCast and state.endCast < state.currentTime then
-					local si = OvaleData.spellInfo[state.lastSpellId]
-					if si and si.aura then
-						if guid == self_guid then
-							-- Check for auras that start within aura lag if they are auras applied onto the player.
-							if (si.aura.player and si.aura.player[aura.filter] and si.aura.player[aura.filter][auraId])
-									or (si.aura.target and si.aura.target[aura.filter] and si.aura.target[aura.filter][auraId]) then
-								if IsWithinAuraLag(aura.start, state.currentTime) then
-									keepAura = true
-								end
-							end
-						elseif si.aura.target and si.aura.target[aura.filter] and si.aura.target[aura.filter][auraId] then
-							-- Check for auras that start within twice the aura lag if they are auras applied onto a target.
-							if IsWithinAuraLag(aura.start, state.currentTime, 2) then
-								keepAura = true
-							end
-						end
-					end
-					if keepAura then
-						-- Reset the aura age relative to the state of the simulator.
-						aura.serial = state.serial
-						Ovale:Logf("    Aura %d on %s preserved in simulator for aura lag, start=%f, now=%f.", auraId, guid, aura.start, state.currentTime)
-					end
-				end
-				if not keepAura then
-					self_pool:Release(aura)
-					whoseTable[casterGUID] = nil
-					Ovale:Logf("    Aura %d on %s removed, now=%f.", auraId, guid, state.currentTime)
-				end
+				self_pool:Release(aura)
+				whoseTable[casterGUID] = nil
+				Ovale:Logf("    Aura %d on %s removed, now=%f.", auraId, guid, state.currentTime)
 			end
 			if not next(whoseTable) then
 				self_pool:Release(whoseTable)
@@ -709,8 +679,8 @@ function OvaleAura:ApplySpellAfterCast(state, spellId, targetGUID, startCast, en
 	end
 end

--- Apply the effects of the spell on the target's state when it lands on the target.
-function OvaleAura:ApplySpellOnHit(state, spellId, targetGUID, startCast, endCast, nextCast, isChanneled, nocd, spellcast)
+-- Apply the effects of the spell on the target's state after it lands on the target.
+function OvaleAura:ApplySpellAfterHit(state, spellId, targetGUID, startCast, endCast, nextCast, isChanneled, nocd, spellcast)
 	local si = OvaleData.spellInfo[spellId]
 	-- Apply the auras on the target.
 	if si and si.aura and si.aura.target then
diff --git a/OvaleFuture.lua b/OvaleFuture.lua
index 4717a20..be82ea7 100644
--- a/OvaleFuture.lua
+++ b/OvaleFuture.lua
@@ -68,16 +68,32 @@ local self_timeAuraAdded = nil
 local OVALE_UNKNOWN_GUID = 0

 -- These CLEU events are eventually received after a successful spellcast.
-local OVALE_CLEU_SPELLCAST_RESULTS = {
-	SPELL_AURA_APPLIED = true,
-	SPELL_AURA_APPLIED_DOSE = true,
-	SPELL_AURA_REFRESH = true,
-	SPELL_AURA_REMOVED_DOSE = true,
-	SPELL_CAST_SUCCESS = true,
-	SPELL_CAST_FAILED = true,
-	SPELL_DAMAGE = true,
-	SPELL_MISSED = true,
+local CLEU_AURA_EVENT = {
+	SPELL_AURA_APPLIED = "hit",
+	SPELL_AURA_APPLIED_DOSE = "hit",
+	SPELL_AURA_BROKEN = "hit",
+	SPELL_AURA_BROKEN_SPELL = "hit",
+	SPELL_AURA_REFRESH = "hit",
+	SPELL_AURA_REMOVED = "hit",
+	SPELL_AURA_REMOVED_DOSE = "hit",
 }
+local CLEU_SUCCESSFUL_SPELLCAST_EVENT = {
+--	SPELL_CAST_SUCCESS = "hit",
+	SPELL_CAST_FAILED = "miss",
+	SPELL_DAMAGE = "hit",
+	SPELL_DISPEL = "hit",
+	SPELL_DISPEL_FAILED = "miss",
+	SPELL_HEAL = "hit",
+	SPELL_INTERRUPT = "hit",
+	SPELL_MISSED = "miss",
+	SPELL_STOLEN = "hit",
+}
+do
+	-- All aura events are also successful spellcast events.
+	for cleuEvent, v in pairs(CLEU_AURA_EVENT) do
+		CLEU_SUCCESSFUL_SPELLCAST_EVENT[cleuEvent] = v
+	end
+end
 --</private-static-properties>

 --<public-static-properties>
@@ -209,19 +225,30 @@ local function AddSpellToQueue(spellId, lineId, startTime, endTime, channeled, a
 		-- This helps to later identify whether the spellcast succeeded by noting when
 		-- the aura is applied or refreshed.
 		if si.aura then
-			for target, auraTable in pairs(si.aura) do
-				for filter, auraList in pairs(auraTable) do
+			-- Look for target auras before player auras applied by the spell.
+			if not spellcast.auraId and si.aura.target then
+				for filter, auraList in pairs(si.aura.target) do
 					for auraId, spellData in pairs(auraList) do
 						if spellData and (spellData == "refresh" or (type(spellData) == "number" and spellData > 0)) then
 							spellcast.auraId = auraId
-							if target == "player" or (target == "target" and spellcast.target == self_guid) then
-								spellcast.removeOnSuccess = true
+							if spellcast.target ~= self_guid then
+								spellcast.removeOnAuraSuccess = true
 							end
 							break
 						end
 					end
 				end
 			end
+			if not spellcast.auraId and si.aura.player then
+				for filter, auraList in pairs(si.aura.player) do
+					for auraId, spellData in pairs(auraList) do
+						if spellData and (spellData == "refresh" or (type(spellData) == "number" and spellData > 0)) then
+							spellcast.auraId = auraId
+							break
+						end
+					end
+				end
+			end
 		end

 		-- Increase or reset any counters used by the Counter() condition.
@@ -232,9 +259,13 @@ local function AddSpellToQueue(spellId, lineId, startTime, endTime, channeled, a
 			local prev = self.counter[si.inccounter] or 0
 			self.counter[si.inccounter] = prev + 1
 		end
-	else
+	end
+
+	-- Set the condition for detecting a successful spellcast.
+	if not spellcast.removeOnAuraSuccess then
 		spellcast.removeOnSuccess = true
 	end
+
 	tinsert(self_activeSpellcast, spellcast)

 	OvaleScore:ScoreSpell(spellId)
@@ -254,7 +285,7 @@ local function RemoveSpellFromQueue(spellId, lineId)
 	Ovale.refreshNeeded["player"] = true
 end

--- UpdateLastSpellcast() is called at the end of the event handler for OVALE_CLEU_SPELLCAST_RESULTS[].
+-- UpdateLastSpellcast() is called at the end of the event handler for CLEU_SUCCESSFUL_SPELLCAST_EVENT[].
 -- It saves the given spellcast as the most recent one on its target and ensures that the spellcast
 -- snapshot values are correctly adjusted for buffs that are added or cleared simultaneously with the
 -- spellcast.
@@ -510,15 +541,19 @@ function OvaleFuture:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, cleuEvent, hi

 	-- Called when a missile reaches or misses its target
 	if sourceGUID == self_guid then
-		if OVALE_CLEU_SPELLCAST_RESULTS[cleuEvent] then
+		local success = CLEU_SUCCESSFUL_SPELLCAST_EVENT[cleuEvent]
+		if success then
 			local spellId, spellName = arg12, arg13
 			TracePrintf(spellId, "%s: %s (%d)", cleuEvent, spellName, spellId)
 			for index, spellcast in ipairs(self_activeSpellcast) do
-				if spellcast.allowRemove and (spellcast.spellId == spellId or spellcast.auraId == spellId) then
-					if not spellcast.channeled and (spellcast.removeOnSuccess or cleuEvent ~= "SPELL_CAST_SUCCESS") then
+				if spellcast.allowRemove and not spellcast.channeled and (spellcast.spellId == spellId or spellcast.auraId == spellId) then
+					spellcast.success = success
+					if spellcast.removeOnSuccess or (spellcast.removeOnAuraSuccess and CLEU_AURA_EVENT[cleuEvent]) then
 						TracePrintf(spellId, "    Spell finished: %s (%d)", spellName, spellId)
 						tremove(self_activeSpellcast, index)
 						UpdateLastSpellcast(spellcast)
+						local unitId = spellcast.target and OvaleGUID:GetUnitId(spellcast.target) or "player"
+						Ovale.refreshNeeded[unitId] = true
 						Ovale.refreshNeeded["player"] = true
 					end
 					break
@@ -751,10 +786,11 @@ statePrototype.ApplySpell = function(state, ...)
 	Ovale:Logf("Apply spell %d at %f currentTime=%f nextCast=%f endCast=%f targetGUID=%s", spellId, startCast, state.currentTime, nextCast, endCast, targetGUID)

 	--[[
-		Apply the effects of the spellcast in three phases.
+		Apply the effects of the spellcast in four phases.
 			1. Effects at the beginning of the spellcast.
 			2. Effects when the spell has been cast.
 			3. Effects when the spellcast hits the target.
+			4. Effects after the spellcast hits the target (possibly due to server lag).
 	--]]
 	-- If the spellcast has already started, then the effects have already occurred.
 	if startCast >= now then
@@ -764,6 +800,11 @@ statePrototype.ApplySpell = function(state, ...)
 	if endCast > now then
 		OvaleState:InvokeMethod("ApplySpellAfterCast", state, ...)
 	end
-	OvaleState:InvokeMethod("ApplySpellOnHit", state, ...)
+	if not spellcast or not spellcast.success then
+		OvaleState:InvokeMethod("ApplySpellOnHit", state, ...)
+	end
+	if not spellcast or not spellcast.success or spellcast.success == "hit" then
+		OvaleState:InvokeMethod("ApplySpellAfterHit", state, ...)
+	end
 end
 --</state-methods>