From a486d924c122eb07596badbfb1075b6492aee876 Mon Sep 17 00:00:00 2001 From: "Johnny C. Lam" Date: Sun, 11 May 2014 08:10:29 +0000 Subject: [PATCH] Teach OvaleCooldown about spell charges. Lazily update the cooldown information of spells based on the age of the cooldown state, which is flagged to be updated when a spell's charges or cooldowns is changed or if a spell is successfully cast. Add a GetSpellCharges() method to the state machine to act like the Blizzard API function of the same name. Fixes ticket 357. Age cooldown state properly. git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1419 d5049fe3-3747-40f7-a4b5-f36d6801af5f --- OvaleCooldown.lua | 207 ++++++++++++++++++++++++------------ conditions/SpellChargeCooldown.lua | 9 +- conditions/SpellCharges.lua | 10 +- conditions/SpellCooldown.lua | 18 +--- scripts/ovale_hunter_spells.lua | 5 +- scripts/ovale_monk_spells.lua | 2 +- scripts/ovale_warlock_spells.lua | 8 +- 7 files changed, 161 insertions(+), 98 deletions(-) diff --git a/OvaleCooldown.lua b/OvaleCooldown.lua index 6ba4aae..6a98618 100644 --- a/OvaleCooldown.lua +++ b/OvaleCooldown.lua @@ -9,7 +9,7 @@ --]]-------------------------------------------------------------------- local _, Ovale = ... -local OvaleCooldown = Ovale:NewModule("OvaleCooldown") +local OvaleCooldown = Ovale:NewModule("OvaleCooldown", "AceEvent-3.0") Ovale.OvaleCooldown = OvaleCooldown -- @@ -20,6 +20,7 @@ local OvalePaperDoll = nil local OvaleStance = nil local OvaleState = nil +local API_GetSpellCharges = GetSpellCharges local API_GetSpellCooldown = GetSpellCooldown local API_UnitHealth = UnitHealth local API_UnitHealthMax = UnitHealthMax @@ -27,6 +28,8 @@ local API_UnitClass = UnitClass -- Player's class. local _, self_class = API_UnitClass("player") +-- Current age of cooldown state. +local self_serial = 0 -- -- @@ -40,11 +43,22 @@ function OvaleCooldown:OnInitialize() end function OvaleCooldown:OnEnable() + self:RegisterEvent("SPELL_UPDATE_CHARGES", "Update") + self:RegisterEvent("SPELL_UPDATE_USABLE", "Update") + self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED", "Update") OvaleState:RegisterState(self, self.statePrototype) end function OvaleCooldown:OnDisable() OvaleState:UnregisterState(self) + self:UnregisterEvent("SPELL_UPDATE_CHARGES") + self:UnregisterEvent("SPELL_UPDATE_USABLE") + self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED") +end + +function OvaleCooldown:Update() + -- Advance age of current cooldown state. + self_serial = self_serial + 1 end -- Return the GCD after the given spellId is cast. @@ -118,9 +132,12 @@ end -- Reset the state to the current conditions. function OvaleCooldown:ResetState(state) for _, cd in pairs(state.cd) do - cd.start = nil - cd.duration = nil - cd.enable = 0 + -- Remove outdated cooldown state. + if cd.serial and cd.serial < self_serial then + for k in pairs(cd) do + cd[k] = nil + end + end end end @@ -139,95 +156,153 @@ function OvaleCooldown:ApplySpellAfterCast(state, spellId, targetGUID, startCast local si = OvaleData.spellInfo[spellId] if si then local cd = state:GetCD(spellId) - if cd then - cd.start = isChanneled and startCast or endCast - cd.duration = si.cd or 0 - cd.enable = 1 - - -- Test for no cooldown. - if nocd then - cd.duration = 0 - else - -- There is no cooldown if the buff named by "buffnocd" parameter is present. - if si.buffnocd then - local aura = state:GetAura("player", si.buffnocd) - if state:IsActiveAura(aura) then - Ovale:Logf("buffnocd stacks = %s, start = %s, ending = %s, startCast = %f", aura.stacks, aura.start, aura.ending, startCast) - if aura.start <= startCast and startCast < aura.ending then - cd.duration = 0 - end - end - end + cd.start = isChanneled and startCast or endCast + cd.duration = si.cd or 0 + cd.enable = 1 - -- There is no cooldown if the target's health percent is below what's specified - -- with the "targetlifenocd" parameter. - local target = OvaleGUID:GetUnitId(targetGUID) - if target and si.targetlifenocd then - local healthPercent = API_UnitHealth(target) / API_UnitHealthMax(target) * 100 - if healthPercent < si.targetlifenocd then - cd.duration = 0 - end + -- If the spell has charges, then remove a charge. + if cd.charges and cd.charges > 0 then + cd.chargeStart = cd.start + cd.charges = cd.charges - 1 + if cd.charges == 0 then + cd.duration = cd.chargeDuration + end + end + + -- Test for no cooldown. + if nocd then + cd.duration = 0 + else + -- There is no cooldown if the buff named by "buff_no_cd" parameter is present. + local buffNoCooldown = si.buff_no_cd or si.buffnocd + if buffNoCooldown then + local aura = state:GetAura("player", buffNoCooldown) + if state:IsActiveAura(aura, cd.start) then + Ovale:Logf("buff_no_cd stacks = %s, start = %s, ending = %s, cd.start = %f", aura.stacks, aura.start, aura.ending, cd.start) + cd.duration = 0 end end - -- Adjust cooldown duration if it is affected by haste: "cd_haste=melee" or "cd_haste=spell". - if cd.duration > 0 and si.cd_haste then - if si.cd_haste == "melee" then - cd.duration = cd.duration / state:GetMeleeHasteMultiplier(spellcast.snapshot) - elseif si.cd_haste == "spell" then - cd.duration = cd.duration / state:GetSpellHasteMultiplier(spellcast.snapshot) + -- There is no cooldown if the target's health percent is below what's specified + -- with the "target_health_pct_no_cd" parameter. + local target = OvaleGUID:GetUnitId(targetGUID) + local targetHealthPctNoCooldown = si.target_health_pct_no_cd or si.targetlifenocd + if target and targetHealthPctNoCooldown then + local healthPercent = API_UnitHealth(target) / API_UnitHealthMax(target) * 100 + if healthPercent < targetHealthPctNoCooldown then + cd.duration = 0 end end + end - Ovale:Logf("Spell %d cooldown info: start=%f, duration=%f", spellId, cd.start, cd.duration) + -- Adjust cooldown duration if it is affected by haste: "cd_haste=melee" or "cd_haste=spell". + if cd.duration > 0 and si.cd_haste then + if si.cd_haste == "melee" then + cd.duration = cd.duration / state:GetMeleeHasteMultiplier(spellcast.snapshot) + elseif si.haste == "ranged" then + cd.duration = cd.duration / OvalePaperDoll:GetSpellHasteMultiplier() + elseif si.cd_haste == "spell" then + cd.duration = cd.duration / state:GetSpellHasteMultiplier(spellcast.snapshot) + end end + + Ovale:Logf("Spell %d cooldown info: start=%f, duration=%f", spellId, cd.start, cd.duration) end end -- -- +statePrototype.DebugCooldown = function(state) + for spellId, cd in pairs(state.cd) do + if cd.start then + if cd.charges then + Ovale:FormatPrint("Spell %s cooldown: start=%f, duration=%f, charges=%d, maxCharges=%d, chargeStart=%f, chargeDuration=%f", + spellId, cd.start, cd.duration, cd.charges, cd.start, cd.duration) + else + Ovale:FormatPrint("Spell %s cooldown: start=%f, duration=%f", spellId, cd.start, cd.duration) + end + end + end +end + -- Return the table holding the simulator's cooldown information for the given spell. statePrototype.GetCD = function(state, spellId) - if spellId then - local si = OvaleData.spellInfo[spellId] - if si and si.cd then - local cdname = si.sharedcd and si.sharedcd or spellId - if not state.cd[cdname] then - state.cd[cdname] = {} + local cdName = spellId + local si = OvaleData.spellInfo[spellId] + if si and si.sharedcd then + cdName = si.sharedcd + end + if not state.cd[cdName] then + state.cd[cdName] = {} + end + + -- Populate the cooldown information from the current game state if it is outdated. + local cd = state.cd[cdName] + if not cd.start or not cd.serial or cd.serial < self_serial then + local start, duration, enable = API_GetSpellCooldown(spellId) + if start and start > 0 then + charges = 0 + end + if si and si.forcecd then + if si.forcecd then + start, duration = API_GetSpellCooldown(si.forcecd) end - return state.cd[cdname] + end + cd.serial = self_serial + cd.start = start + cd.duration = duration + cd.enable = enable + + local charges, maxCharges, chargeStart, chargeDuration = API_GetSpellCharges(spellId) + if charges then + cd.charges = charges + cd.maxCharges = maxCharges + cd.chargeStart = chargeStart + cd.chargeDuration = chargeDuration end end - return nil + + -- Advance the cooldown state to the current time. + local now = state.currentTime + if cd.start then + if cd.start + cd.duration <= now then + cd.start = 0 + cd.duration = 0 + end + end + if cd.charges then + local charges, maxCharges, chargeStart, chargeDuration = cd.charges, cd.maxCharges, cd.chargeStart, cd.chargeDuration + while chargeStart + chargeDuration <= now and charges < maxCharges do + chargeStart = chargeStart + chargeDuration + charges = charges + 1 + end + cd.charges = charges + cd.chargeStart = chargeStart + end + + return cd end -- Return the cooldown for the spell in the simulator. statePrototype.GetSpellCooldown = function(state, spellId) - local start, duration, enable local cd = state:GetCD(spellId) - if cd and cd.start then - start, duration, enable = cd.start, cd.duration, cd.enable - else - start, duration, enable = API_GetSpellCooldown(spellId) - local si = OvaleData.spellInfo[spellId] - if si and si.forcecd then - start, duration = API_GetSpellCooldown(si.forcecd) - end - end - return start, duration, enable + return cd.start, cd.duration, cd.enable +end + +-- Return the information on the number of charges for the spell in the simulator. +statePrototype.GetSpellCharges = function(state, spellId) + local cd = state:GetCD(spellId) + return cd.charges, cd.maxCharges, cd.chargeStart, cd.chargeDuration end -- Force the cooldown of a spell to reset at the specified time. statePrototype.ResetSpellCooldown = function(state, spellId, atTime) - if atTime >= state.currentTime then - local start, duration, enable = state:GetSpellCooldown(spellId) - if start + duration > state.currentTime then - local cd = state:GetCD(spellId) - if cd then - cd.start = state.currentTime - cd.duration = atTime - state.currentTime - cd.enable = 1 - end + local now = state.currentTime + if atTime >= now then + local cd = state:GetCD(spellId) + if cd.start + cd.duration > now then + cd.start = now + cd.duration = atTime - now end end end diff --git a/conditions/SpellChargeCooldown.lua b/conditions/SpellChargeCooldown.lua index 723836f..655a75f 100644 --- a/conditions/SpellChargeCooldown.lua +++ b/conditions/SpellChargeCooldown.lua @@ -1,6 +1,6 @@ --[[-------------------------------------------------------------------- Ovale Spell Priority - Copyright (C) 2013 Johnny C. Lam + Copyright (C) 2013, 2014 Johnny C. Lam This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License in the LICENSE @@ -11,10 +11,11 @@ local _, Ovale = ... do local OvaleCondition = Ovale.OvaleCondition + local OvaleState = Ovale.OvaleState - local API_GetSpellCharges = GetSpellCharges local Compare = OvaleCondition.Compare local TestValue = OvaleCondition.TestValue + local state = OvaleState.state --- Get the cooldown in seconds on a spell before it gains another charge. -- @name SpellChargeCooldown @@ -31,8 +32,8 @@ do local function SpellChargeCooldown(condition) local spellId, comparator, limit = condition[1], condition[2], condition[3] - local charges, maxCharges, start, duration = API_GetSpellCharges(spellId) - if charges < maxCharges then + local charges, maxCharges, start, duration = state:GetSpellCharges(spellId) + if charges and charges < maxCharges then return TestValue(start, start + duration, duration, start, -1, comparator, limit) end return Compare(0, comparator, limit) diff --git a/conditions/SpellCharges.lua b/conditions/SpellCharges.lua index c7827cd..977c590 100644 --- a/conditions/SpellCharges.lua +++ b/conditions/SpellCharges.lua @@ -1,6 +1,6 @@ --[[-------------------------------------------------------------------- Ovale Spell Priority - Copyright (C) 2013 Johnny C. Lam + Copyright (C) 2013, 2014 Johnny C. Lam This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License in the LICENSE @@ -11,9 +11,10 @@ local _, Ovale = ... do local OvaleCondition = Ovale.OvaleCondition + local OvaleState = Ovale.OvaleState - local API_GetSpellCharges = GetSpellCharges local Compare = OvaleCondition.Compare + local state = OvaleState.state --- Get the number of charges of the spell. -- @name SpellCharges @@ -30,8 +31,9 @@ do local function SpellCharges(condition) local spellId, comparator, limit = condition[1], condition[2], condition[3] - local value = API_GetSpellCharges(spellId) - return Compare(value, comparator, limit) + local charges, maxCharges, start, duration = state:GetSpellCharges(spellId) + charges = charges or 0 + return Compare(charges, comparator, limit) end OvaleCondition:RegisterCondition("charges", true, SpellCharges) diff --git a/conditions/SpellCooldown.lua b/conditions/SpellCooldown.lua index e8977a4..da57cbe 100644 --- a/conditions/SpellCooldown.lua +++ b/conditions/SpellCooldown.lua @@ -1,22 +1,19 @@ --[[-------------------------------------------------------------------- Ovale Spell Priority Copyright (C) 2012, 2013 Sidoine - Copyright (C) 2012, 2013 Johnny C. Lam + Copyright (C) 2012, 2013, 2014 Johnny C. Lam This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License in the LICENSE file accompanying this program. --]]-------------------------------------------------------------------- - local _, Ovale = ... do local OvaleCondition = Ovale.OvaleCondition - local OvaleSpellBook = Ovale.OvaleSpellBook local OvaleState = Ovale.OvaleState - local type = type local Compare = OvaleCondition.Compare local TestValue = OvaleCondition.TestValue local state = OvaleState.state @@ -35,18 +32,7 @@ do local function SpellCooldown(condition) local spellId, comparator, limit = condition[1], condition[2], condition[3] - local start, duration - if type(spellId) == "string" then - local sharedCd = state.cd[spellId] - if not sharedCd then - return nil - end - start, duration = sharedCd.start, sharedCd.duration - elseif not OvaleSpellBook:IsKnownSpell(spellId) then - return nil - else - start, duration = state:GetSpellCooldown(spellId) - end + local start, duration = state:GetSpellCooldown(spellId) if start > 0 and duration > 0 then return TestValue(start, start + duration, duration, start, -1, comparator, limit) end diff --git a/scripts/ovale_hunter_spells.lua b/scripts/ovale_hunter_spells.lua index bff664b..abd8123 100644 --- a/scripts/ovale_hunter_spells.lua +++ b/scripts/ovale_hunter_spells.lua @@ -56,9 +56,8 @@ Define(cobra_shot 77767) Define(counter_shot 147362) SpellInfo(counter_shot cd=24) Define(crouching_tiger_hidden_chimera_talent 3) -Define(deterrence 19263) - SpellInfo(deterrence cd=180) - SpellInfo(deterrence addcd=-60 talent=crouching_tiger_hidden_chimera_talent) +Define(deterrence 148467) + SpellInfo(deterrence cd=5) Define(dire_beast 120679) SpellInfo(dire_beast cd=30) Define(dire_beast_talent 11) diff --git a/scripts/ovale_monk_spells.lua b/scripts/ovale_monk_spells.lua index 316b815..0d9487d 100644 --- a/scripts/ovale_monk_spells.lua +++ b/scripts/ovale_monk_spells.lua @@ -19,7 +19,7 @@ Define(breath_of_fire 115181) SpellInfo(breath_of_fire chi=2) Define(brewmaster_training 117967) Define(chi_brew 115399) - SpellInfo(chi_brew cd=45 chi=-2) + SpellInfo(chi_brew chi=-2) Define(chi_brew_talent 9) Define(chi_burst 123986) SpellInfo(chi_burst cd=30) diff --git a/scripts/ovale_warlock_spells.lua b/scripts/ovale_warlock_spells.lua index 5226fc3..dbb70fe 100644 --- a/scripts/ovale_warlock_spells.lua +++ b/scripts/ovale_warlock_spells.lua @@ -43,18 +43,18 @@ Define(dark_regeneration 108359) Define(dark_regeneration_talent 1) Define(dark_intent 109773) Define(dark_soul_knowledge 113861) - SpellInfo(dark_soul_knowledge cd=120) + SpellInfo(dark_soul_knowledge cd=120 talent=!archimondes_darkness_talent) SpellAddBuff(dark_soul_knowledge dark_soul_knowledge_buff=1) Define(dark_soul_knowledge_buff 113858) SpellInfo(dark_soul_knowledge_buff duration=20) Define(dark_soul_instability 113858) - SpellInfo(dark_soul_instability cd=120) + SpellInfo(dark_soul_instability cd=120 talent=!archimondes_darkness_talent) SpellAddBuff(dark_soul_instability dark_soul_instability_buff=1) Define(dark_soul_instability_buff 113858) SpellInfo(dark_soul_instability_buff duration=20) Define(dark_soul_misery 113860) - SpellInfo(dark_soul_misery cd=120) - SpellAddBuff(dark_soul_misery dark_soul_misery_buff=1) + SpellInfo(dark_soul_misery cd=120 talent=!archimondes_darkness_talent) + SpellAddBuffoul_misery dark_soul_misery_buff=1) Define(dark_soul_misery_buff 113858) SpellInfo(dark_soul_misery_buff duration=20) Define(demonic_circle_teleport 48020) -- 1.7.9.5