From ff81794a0e93df4cb9190d76be8cfd4e1075836e Mon Sep 17 00:00:00 2001 From: "Johnny C. Lam" Date: Thu, 5 Dec 2013 08:19:21 +0000 Subject: [PATCH] Workaround for ticket 290 - Dream of Cenarius Second Charge Rip. When a buff expires simultaneous with a spellcast, a debuff applied by that spellcast may not snapshot the effects of the expired buff. Tag an expired buff as "consumed" if the aura ended early and the ending cast time of the previous spellcast is within 0.3s of the expiration time of the aura. Treat an aura as still "active" if it was consumed and we are checking within 0.3s of when it has expired. This allows an aura that provides a damage multiplier to still be added into the snapshot for any applied auras. git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1238 d5049fe3-3747-40f7-a4b5-f36d6801af5f --- OvaleAura.lua | 90 +++++++++++++++++++++++++++++++++++++++++++------------ OvaleFuture.lua | 9 +++++- 2 files changed, 79 insertions(+), 20 deletions(-) diff --git a/OvaleAura.lua b/OvaleAura.lua index 95ba2a5..2bdd753 100644 --- a/OvaleAura.lua +++ b/OvaleAura.lua @@ -23,9 +23,9 @@ local OvaleData = nil local OvaleFuture = nil local OvaleGUID = nil local OvalePaperDoll = nil +local OvaleSpellBook = nil local OvaleState = nil -local abs = math.abs local bit_band = bit.band local bit_bor = bit.bor local floor = math.floor @@ -63,6 +63,10 @@ local self_aura = {} -- Current age of auras per unit: self_serial[guid] = age. local self_serial = {} +-- Aura lag in milliseconds, with respect to the corresponding spellcast. +-- TODO: Promote this into a slider option in the config panel. +local self_auraLag = 300 + -- Some auras have a nil caster, so treat those as having a GUID of zero for indexing purposes. local UNKNOWN_GUID = 0 @@ -100,9 +104,6 @@ local CLEU_TICK_EVENTS = { local CLEU_SCHOOL_MASK_MAGIC = bit_bor(SCHOOL_MASK_ARCANE, SCHOOL_MASK_FIRE, SCHOOL_MASK_FROST, SCHOOL_MASK_HOLY, SCHOOL_MASK_NATURE, SCHOOL_MASK_SHADOW) -- --- --- - -- local function PutAura(auraDB, guid, auraId, casterGUID, aura) if not auraDB[guid] then @@ -207,6 +208,10 @@ local function RemoveAurasOnGUID(auraDB, guid) auraDB[guid] = nil end end + +local function IsWithinAuraLag(time1, time2) + return (time1 - time2 < self_auraLag/1000) and (time2 - time1 < self_auraLag/1000) +end -- -- @@ -216,6 +221,7 @@ function OvaleAura:OnInitialize() OvaleFuture = Ovale.OvaleFuture OvaleGUID = Ovale.OvaleGUID OvalePaperDoll = Ovale.OvalePaperDoll + OvaleSpellBook = Ovale.OvaleSpellBook OvaleState = Ovale.OvaleState end @@ -329,7 +335,15 @@ end function OvaleAura:IsActiveAura(aura, now) now = now or API_GetTime() - return (aura and aura.serial == self_serial[aura.guid] and aura.stacks > 0 and aura.start <= now and now <= aura.ending) + local boolean = false + if aura then + if aura.serial == self_serial[aura.guid] and aura.stacks > 0 and aura.start <= now and now <= aura.ending then + boolean = true + elseif aura.consumed and IsWithinAuraLag(aura.ending, now) then + boolean = true + end + end + return boolean end function OvaleAura:GainedAuraOnGUID(guid, atTime, auraId, casterGUID, filter, icon, count, debuffType, duration, expirationTime, isStealable, name, value1, value2, value3) @@ -378,6 +392,7 @@ function OvaleAura:GainedAuraOnGUID(guid, atTime, auraId, casterGUID, filter, ic end aura.gain = atTime aura.stacks = count + aura.consumed = nil aura.filter = filter aura.icon = icon aura.debuffType = debuffType @@ -387,13 +402,21 @@ function OvaleAura:GainedAuraOnGUID(guid, atTime, auraId, casterGUID, filter, ic -- Snapshot stats for auras applied by the player. if mine then -- Determine whether to snapshot player stats for the aura or to keep the existing stats. - local lastSpellcast = OvaleFuture.lastSpellcast - local lastSpellId = lastSpellcast and lastSpellcast.spellId - if lastSpellId and OvaleData:NeedNewSnapshot(auraId, lastSpellId) then - Ovale:DebugPrintf(OVALE_AURA_DEBUG, " Snapshot stats for %s %s (%d) on %s from %f, now=%f, aura.serial=%d", - filter, name, auraId, guid, lastSpellcast.snapshot.snapshotTime, atTime, aura.serial) - -- TODO: damageMultiplier isn't correct if lastSpellId spreads the DoT. - OvaleFuture:UpdateSnapshotFromSpellcast(aura, lastSpellcast) + local spellcast = OvaleFuture:LastInFlightSpell() + if spellcast and spellcast.stop and not IsWithinAuraLag(spellcast.stop, atTime) then + spellcast = OvaleFuture.lastSpellcast + if spellcast and spellcast.stop and not IsWithinAuraLag(spellcast.stop, atTime) then + spellcast = nil + end + end + if spellcast and spellcast.target == guid then + if OvaleData:NeedNewSnapshot(auraId, spellcast.spellId) then + local spellName = OvaleSpellBook:GetSpellName(spellcast.spellId) or "Unknown spell" + Ovale:DebugPrintf(OVALE_AURA_DEBUG, " Snapshot stats for %s %s (%d) on %s applied by %s (%d) from %f, now=%f, aura.serial=%d", + filter, name, auraId, guid, spellName, spellcast.spellId, spellcast.snapshot.snapshotTime, atTime, aura.serial) + -- TODO: damageMultiplier isn't correct if spellId spreads the DoT. + OvaleFuture:UpdateSnapshotFromSpellcast(aura, spellcast) + end end -- Set the tick information for known DoTs. @@ -421,15 +444,39 @@ end function OvaleAura:LostAuraOnGUID(guid, atTime, auraId, casterGUID) local aura = GetAura(self_aura, guid, auraId, casterGUID) - Ovale:DebugPrintf(OVALE_AURA_DEBUG, " Expiring %s %s (%s) from %s at %f.", - aura.filter, aura.name, auraId, guid, atTime) + local filter = aura.filter + Ovale:DebugPrintf(OVALE_AURA_DEBUG, " Expiring %s %s (%d) from %s at %f.", + filter, aura.name, auraId, guid, atTime) if aura.ending > atTime then aura.ending = atTime end - -- Clear old tick information. - aura.tick = nil - aura.ticksSeen = nil - aura.lastTickTime = nil + + local mine = (casterGUID == self_guid) + if mine then + -- Clear old tick information for player-applied periodic auras. + aura.tick = nil + aura.ticksSeen = nil + aura.lastTickTime = nil + + -- Check if the aura was consumed by the last spellcast. + -- The aura must have ended early, i.e., start + duration > ending. + if aura.start + aura.duration > aura.ending then + local spellcast + if guid == self_guid then + -- Player aura, so it was possibly consumed by an in-flight spell. + spellcast = OvaleFuture:LastInFlightSpell() + else + -- Non-player aura, so it was possibly consumed by a spell that landed on its target. + spellcast = OvaleFuture.lastSpellcast + end + if spellcast and spellcast.stop and IsWithinAuraLag(spellcast.stop, aura.ending) then + aura.consumed = true + local spellName = OvaleSpellBook:GetSpellName(spellcast.spellId) or "Unknown spell" + Ovale:DebugPrintf(OVALE_AURA_DEBUG, " Consuming %s %s (%d) on %s with %s (%d) at %f.", + filter, aura.name, auraId, guid, spellName, spellcast.spellId, spellcast.stop) + end + end + end self:SendMessage("Ovale_AuraRemoved", atTime, guid, auraId, aura.source) local unitId = OvaleGUID:GetUnitId(guid) @@ -687,7 +734,11 @@ statePrototype.IsActiveAura = function(state, aura, now) local boolean = false if aura then if aura.state then - boolean = (aura.serial == state.serial and aura.stacks > 0 and aura.start <= now and now <= aura.ending) + if aura.serial == state.serial and aura.stacks > 0 and aura.start <= now and now <= aura.ending then + boolean = true + elseif aura.consumed and IsWithinAuraLag(aura.ending, now) then + boolean = true + end else boolean = OvaleAura:IsActiveAura(aura, now) end @@ -793,6 +844,7 @@ statePrototype.ApplySpellAuras = function(state, spellId, guid, startCast, endCa Ovale:Logf("Aura %d is completely removed.", auraId) -- The aura is completely removed, so set ending to the time that the aura is removed. aura.ending = atTime + aura.consumed = true end end else diff --git a/OvaleFuture.lua b/OvaleFuture.lua index 6dff9de..bcc0f91 100644 --- a/OvaleFuture.lua +++ b/OvaleFuture.lua @@ -163,7 +163,7 @@ local function AddSpellToQueue(spellId, lineId, startTime, endTime, channeled, a if si.buffnocd then local now = API_GetTime() local aura = OvaleAura:GetAura("player", si.buffnocd) - if aura and aura.stacks > 0 and aura.start <= now and now <= aura.ending then + if OvaleAura:IsActiveAura(aura) then spellcast.nocd = true end end @@ -527,6 +527,13 @@ function OvaleFuture:ApplyInFlightSpells(state) end end +function OvaleFuture:LastInFlightSpell() + if #self_activeSpellcast > 0 then + return self_activeSpellcast[#self_activeSpellcast] + end + return self.lastSpellcast +end + function OvaleFuture:UpdateSnapshotFromSpellcast(dest, spellcast) if dest.snapshot then OvalePaperDoll:ReleaseSnapshot(dest.snapshot) -- 1.7.9.5