Johnny C. Lam [12-15-14 - 15:46]
diff --git a/Ovale.toc b/Ovale.toc
index 106f8a1..7432344 100644
--- a/Ovale.toc
+++ b/Ovale.toc
@@ -72,6 +72,7 @@ ShadowWordDeath.lua
Skada.lua
SpellDamage.lua
SteadyFocus.lua
+Totem.lua
conditions.lua
scripts\files.xml
diff --git a/SimulationCraft.lua b/SimulationCraft.lua
index 6b09dd4..e4e78f3 100644
--- a/SimulationCraft.lua
+++ b/SimulationCraft.lua
@@ -186,27 +186,6 @@ local EMIT_DISAMBIGUATION = {}
local EMIT_EXTRA_PARAMETERS = {}
local OPERAND_TOKEN_PATTERN = "[^.]+"
-local TOTEM_TYPE = {
- ["prismatic_crystal"] = "crystal", -- XXX
- ["capacitor_totem"] = "air",
- ["cloudburst_totem"] = "water",
- ["earth_elemental_totem"] = "earth",
- ["earthbind_totem"] = "earth",
- ["earthgrab_totem"] = "earth",
- ["fire_elemental_totem"] = "fire",
- ["grounding_totem"] = "air",
- ["healing_stream_totem"] = "water",
- ["healing_tide_totem"] = "water",
- ["magma_totem"] = "fire",
- ["mana_tide_totem"] = "water",
- ["searing_totem"] = "fire",
- ["spirit_link_totem"] = "air",
- ["storm_elemental_totem"] = "air",
- ["stone_bulwark_totem"] = "earth",
- ["tremor_totem"] = "earth",
- ["windwalk_totem"] = "air",
-}
-
local POTION_STAT = {
["draenic_agility"] = "agility",
["draenic_armor"] = "armor",
@@ -1065,6 +1044,23 @@ local function InitializeDisambiguation()
AddDisambiguation("shield_barrier", "shield_barrier_tank", "WARRIOR", "protection")
end
+local function IsTotem(name)
+ if strsub(name, 1, 13) == "wild_mushroom" then
+ -- Druids.
+ return true
+ elseif name == "prismatic_crystal" or name == "rune_of_power" then
+ -- Mages.
+ return true
+ elseif strsub(name, -7, -1) == "_statue" then
+ -- Monks.
+ return true
+ elseif strsub(name, -6, -1) == "_totem" then
+ -- Shamans.
+ return true
+ end
+ return false
+end
+
local EMIT_VISITOR = nil
-- Forward declarations of code generation functions.
local Emit = nil
@@ -2003,14 +1999,8 @@ EmitOperandAction = function(operand, parseNode, nodeList, annotation, action, t
local code
if property == "active" then
- if strsub(name, -6) == "_totem" then
- local totemType = TOTEM_TYPE[name]
- if totemType then
- code = format("TotemPresent(%s totem=%s)", totemType, name)
- else
- code = format("TotemPresent(%s)", name)
- symbol = false
- end
+ if IsTotem(name) then
+ code = format("TotemPresent(%s)", name)
else
code = format("%s%sPresent(%s)", target, prefix, buffName)
symbol = buffName
@@ -2047,14 +2037,8 @@ EmitOperandAction = function(operand, parseNode, nodeList, annotation, action, t
elseif property == "recharge_time" then
code = format("SpellChargeCooldown(%s)", name)
elseif property == "remains" then
- if strsub(name, -6) == "_totem" then
- local totemType = TOTEM_TYPE[name]
- if totemType then
- code = format("TotemRemaining(%s totem=%s)", totemType, name)
- else
- code = format("TotemRemaining(%s)", name)
- symbol = false
- end
+ if IsTotem(name) then
+ code = format("TotemRemaining(%s)", name)
else
code = format("%s%sRemaining(%s)", buffTarget, prefix, buffName)
symbol = buffName
@@ -2478,21 +2462,13 @@ EmitOperandPet = function(operand, parseNode, nodeList, annotation, action)
local name = tokenIterator()
local property = tokenIterator()
name = Disambiguate(name, annotation.class, annotation.specialization)
- local totemType = TOTEM_TYPE[name]
+ local isTotem = IsTotem(name)
local code
- if property == "active" then
- if totemType then
- code = format("TotemPresent(%s totem=%s)", totemType, name)
- else
- code = format("TotemPresent(%s)", name)
- end
- elseif property == "remains" then
- if totemType then
- code = format("TotemRemaining(%s totem=%s)", totemType, name)
- else
- code = format("TotemRemaining(%s)", name)
- end
+ if isTotem and property == "active" then
+ code = format("TotemPresent(%s)", name)
+ elseif isTotem and property == "remains" then
+ code = format("TotemRemaining(%s)", name)
else
-- Strip the "pet.<name>." from the operand and re-evaluate.
local pattern = format("^pet%%.%s%%.([%%w_.]+)", name)
@@ -2522,9 +2498,7 @@ EmitOperandPet = function(operand, parseNode, nodeList, annotation, action)
if ok and code then
annotation.astAnnotation = annotation.astAnnotation or {}
node = OvaleAST:ParseCode("expression", code, nodeList, annotation.astAnnotation)
- if totemType then
- AddSymbol(annotation, name)
- end
+ AddSymbol(annotation, name)
end
else
ok = false
@@ -2732,7 +2706,7 @@ EmitOperandSpecial = function(operand, parseNode, nodeList, annotation, action,
AddSymbol(annotation, fbName)
AddSymbol(annotation, ffbName)
elseif class == "MAGE" and operand == "buff.rune_of_power.remains" then
- code = "RuneOfPowerRemaining()"
+ code = "TotemRemaining(rune_of_power)"
elseif class == "MAGE" and operand == "dot.frozen_orb.ticking" then
-- The Frozen Orb is ticking if fewer than 10s have elapsed since it was cast.
local name = "frozen_orb"
diff --git a/Totem.lua b/Totem.lua
new file mode 100644
index 0000000..b620d22
--- /dev/null
+++ b/Totem.lua
@@ -0,0 +1,325 @@
+--[[--------------------------------------------------------------------
+ Copyright (C) 2014 Johnny C. Lam.
+ See the file LICENSE.txt for copying permission.
+--]]--------------------------------------------------------------------
+
+local OVALE, Ovale = ...
+local OvaleTotem = Ovale:NewModule("OvaleTotem", "AceEvent-3.0")
+Ovale.OvaleTotem = OvaleTotem
+
+--<private-static-properties>
+local OvaleProfiler = Ovale.OvaleProfiler
+
+-- Forward declarations for module dependencies.
+local OvaleData = nil
+local OvaleSpellBook = nil
+local OvaleState = nil
+
+local ipairs = ipairs
+local pairs = pairs
+local API_GetTotemInfo = GetTotemInfo
+local API_UnitClass = UnitClass
+local AIR_TOTEM_SLOT = AIR_TOTEM_SLOT -- FrameXML\Constants
+local EARTH_TOTEM_SLOT = EARTH_TOTEM_SLOT -- FrameXML\Constants
+local FIRE_TOTEM_SLOT = FIRE_TOTEM_SLOT -- FrameXML\Constants
+local INFINITY = math.huge
+local MAX_TOTEMS = MAX_TOTEMS -- FrameXML\Constants
+local WATER_TOTEM_SLOT = WATER_TOTEM_SLOT -- FrameXML\Constants
+
+-- Register for profiling.
+OvaleProfiler:RegisterProfiling(OvaleTotem)
+
+-- Player's class.
+local _, self_class = API_UnitClass("player")
+-- Current age of totem state.
+local self_serial = 0
+
+-- Classes that can have totems.
+local TOTEM_CLASS = {
+ DRUID = true, -- Wild Mushroom
+ MAGE = true, -- Rune of Power, Prismatic Crystal
+ MONK = true, -- Summon Black Ox Statue, Summon Jade Serpent Statue
+ SHAMAN = true, -- Totems
+}
+
+-- Maps totem type to the totem slot.
+local TOTEM_SLOT = {
+ air = AIR_TOTEM_SLOT,
+ earth = EARTH_TOTEM_SLOT,
+ fire = FIRE_TOTEM_SLOT,
+ water = WATER_TOTEM_SLOT,
+}
+
+-- Shaman's Totemic Recall destroys all totems.
+local TOTEMIC_RECALL = 36936
+--</private-static-properties>
+
+--<public-static-properties>
+-- Current totem information, indexed by slot.
+OvaleTotem.totem = {}
+--</public-static-properties>
+
+--<public-static-methods>
+function OvaleTotem:OnInitialize()
+ -- Resolve module dependencies.
+ OvaleData = Ovale.OvaleData
+ OvaleSpellBook = Ovale.OvaleSpellBook
+ OvaleState = Ovale.OvaleState
+end
+
+function OvaleTotem:OnEnable()
+ if TOTEM_CLASS[self_class] then
+ self:RegisterEvent("PLAYER_ENTERING_WORLD", "Update")
+ self:RegisterEvent("PLAYER_TALENT_UPDATE", "Update")
+ self:RegisterEvent("PLAYER_TOTEM_UPDATE", "Update")
+ self:RegisterEvent("UPDATE_SHAPESHIFT_FORM", "Update")
+ OvaleState:RegisterState(self, self.statePrototype)
+ end
+end
+
+function OvaleTotem:OnDisable()
+ if TOTEM_CLASS[self_class] then
+ OvaleState:UnregisterState(self)
+ self:UnregisterEvent("PLAYER_ENTERING_WORLD")
+ self:UnregisterEvent("PLAYER_TALENT_UPDATE")
+ self:UnregisterEvent("PLAYER_TOTEM_UPDATE")
+ self:UnregisterEvent("UPDATE_SHAPESHIFT_FORM")
+ end
+end
+
+function OvaleTotem:Update()
+ -- Advance age of current totem state.
+ self_serial = self_serial + 1
+end
+--</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+ State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvaleTotem.statePrototype = {}
+--</public-static-properties>
+
+--<private-static-properties>
+local statePrototype = OvaleTotem.statePrototype
+--</private-static-properties>
+
+--<state-properties>
+-- Totem state, indexed by slot (1 through 4).
+statePrototype.totem = nil
+--</state-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvaleTotem:InitializeState(state)
+ state.totem = {}
+ for slot = 1, MAX_TOTEMS do
+ state.totem[slot] = {}
+ end
+end
+
+-- Reset the state to the current conditions.
+function OvaleTotem:ResetState(state)
+ self:StartProfiling("OvaleTotem_ResetState")
+ for _, totem in pairs(state.totem) do
+ -- Remove outdated totems.
+ if totem.serial and totem.serial < self_serial then
+ for k in pairs(totem) do
+ totem[k] = nil
+ end
+ end
+ end
+ self:StopProfiling("OvaleTotem_ResetState")
+end
+
+-- Release state resources prior to removing from the simulator.
+function OvaleTotem:CleanState(state)
+ for slot, totem in pairs(state.totem) do
+ for k in pairs(totem) do
+ totem[k] = nil
+ end
+ state.totem[slot] = nil
+ end
+end
+
+-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
+function OvaleTotem:ApplySpellAfterCast(state, spellId, targetGUID, startCast, endCast, nextCast, isChanneled, spellcast)
+ self:StartProfiling("OvaleTotem_ApplySpellAfterCast")
+ if self_class == "SHAMAN" and spellId == TOTEMIC_RECALL then
+ -- Shaman's Totemic Recall destroys all totems.
+ for slot in ipairs(state.totem) do
+ state:DestroyTotem(slot)
+ end
+ else
+ local slot = state:GetTotemSlot(spellId)
+ if slot then
+ state:SummonTotem(spellId, slot)
+ end
+ end
+ self:StopProfiling("OvaleTotem_ApplySpellAfterCast")
+end
+--</public-static-methods>
+
+--<state-methods>
+-- Return the table holding the simulator's totem information for the given slot.
+statePrototype.GetTotem = function(state, slot)
+ OvaleTotem:StartProfiling("OvaleTotem_state_GetTotem")
+ slot = TOTEM_SLOT[slot] or slot
+ -- Populate the totem information from the current game state if it is outdated.
+ local totem = state.totem[slot]
+ if totem then
+ if not totem.isActive or not totem.serial or totem.serial < self_serial then
+ local haveTotem, name, startTime, duration, icon = API_GetTotemInfo(slot)
+ totem.isActive = haveTotem
+ totem.name = name
+ totem.start = startTime
+ totem.duration = duration
+ totem.icon = icon
+ totem.serial = self_serial
+ end
+ -- Advance the totem state to the current time.
+ if totem.isActive and totem.start + totem.duration <= state.currentTime then
+ state:DestroyTotem(slot)
+ end
+ end
+ OvaleTotem:StopProfiling("OvaleTotem_state_GetTotem")
+ return totem
+end
+
+-- Return the totem information in the given slot in the simulator.
+statePrototype.GetTotemInfo = function(state, slot)
+ local haveTotem, name, startTime, duration, icon
+ slot = TOTEM_SLOT[slot] or slot
+ local totem = state:GetTotem(slot)
+ if totem then
+ haveTotem = totem.isActive
+ name = totem.name
+ startTime = totem.start
+ duration = totem.duration
+ icon = totem.icon
+ end
+ return haveTotem, name, startTime, duration, icon
+end
+
+-- Return the number of totems previously summoned by the spell and the interval of time that at least one totem is active.
+statePrototype.GetTotemCount = function(state, spellId)
+ local start, ending
+ local count = 0
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.totem then
+ local buffPresent = true
+ -- "buff_totem" is the ID of the aura applied by the totem summoned by the spell.
+ -- If the aura is absent, then the totem is considered to be expired.
+ if si.buff_totem then
+ local aura = state:GetAura("player", si.buff_totem)
+ buffPresent = state:IsActiveAura(aura)
+ end
+ if buffPresent then
+ local texture = OvaleSpellBook:GetSpellTexture(spellId)
+ -- "max_totems" is the maximum number of the totem that can be summoned concurrently.
+ -- Default to allowing only one such totem.
+ local maxTotems = si.max_totems or 1
+ for slot in ipairs(state.totem) do
+ local totem = state:GetTotem(slot)
+ if totem.isActive and totem.icon == texture then
+ count = count + 1
+ -- Save earliest start time.
+ if not start or start > totem.start then
+ start = totem.start
+ end
+ -- Save latest ending time.
+ if not ending or ending < totem.start + totem.duration then
+ ending = totem.start + totem.duration
+ end
+ end
+ if count >= maxTotems then
+ break
+ end
+ end
+ end
+ end
+ return count, start, ending
+end
+
+-- Return the totem slot that will contain the totem summoned by the spell.
+statePrototype.GetTotemSlot = function(state, spellId)
+ OvaleTotem:StartProfiling("OvaleTotem_state_GetTotemSlot")
+ local totemSlot
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.totem then
+ -- Check if the totem summoned by the spell maps to a known totem slot.
+ totemSlot = TOTEM_SLOT[si.totem]
+ if not totemSlot then
+ -- Find the first available totem slot.
+ local availableSlot
+ for slot in ipairs(state.totem) do
+ local totem = state:GetTotem(slot)
+ if not totem.isActive then
+ availableSlot = slot
+ break
+ end
+ end
+
+ local texture = OvaleSpellBook:GetSpellTexture(spellId)
+ -- "max_totems" is the maximum number of the totem that can be summoned concurrently.
+ -- Default to allowing only one such totem.
+ local maxTotems = si.max_totems or 1
+ local count = 0
+ -- Find the totem slot with the oldest such totem.
+ local start = INFINITY
+ for slot in ipairs(state.totem) do
+ local totem = state:GetTotem(slot)
+ if totem.isActive and totem.icon == texture then
+ count = count + 1
+ if start > totem.start then
+ start = totem.start
+ totemSlot = slot
+ end
+ end
+ end
+ -- If there are fewer than the maximum number of totems, then summon into the first available slot.
+ if count < maxTotems then
+ totemSlot = availableSlot
+ end
+ end
+ -- Catch-all: if there are no totem slots for the spell, then summon the totem into the first totem slot.
+ totemSlot = totemSlot or 1
+ end
+ OvaleTotem:StopProfiling("OvaleTotem_state_GetTotemSlot")
+ return totemSlot
+end
+
+-- Summon a totem into the slot in the simulator at the current time.
+statePrototype.SummonTotem = function(state, spellId, slot)
+ OvaleTotem:StartProfiling("OvaleTotem_state_SummonTotem")
+ slot = TOTEM_SLOT[slot] or slot
+ state:Log("Spell %d summons totem into slot %d.", spellId, slot)
+ local name, _, icon = OvaleSpellBook:GetSpellInfo(spellId)
+ local duration = state:GetSpellInfoProperty(spellId, "duration")
+ local totem = state.totem[slot]
+ totem.isActive = true
+ -- The name is not always the same as the name of the summoning spell, but totems
+ -- are compared based on their icon/texture, so this inaccuracy doesn't break anything.
+ totem.name = name
+ totem.start = state.currentTime
+ -- Default to 15 seconds if no duration is found.
+ totem.duration = duration or 15
+ totem.icon = icon
+ OvaleTotem:StopProfiling("OvaleTotem_state_SummonTotem")
+end
+
+-- Destroy the totem in the slot.
+statePrototype.DestroyTotem = function(state, slot)
+ OvaleTotem:StartProfiling("OvaleTotem_state_DestroyTotem")
+ slot = TOTEM_SLOT[slot] or slot
+ state:Log("Destroying totem in slot %d.", slot)
+ local totem = state.totem[slot]
+ totem.isActive = false
+ totem.name = ""
+ totem.start = 0
+ totem.duration = 0
+ totem.icon = ""
+ OvaleTotem:StopProfiling("OvaleTotem_state_DestroyTotem")
+end
+--</state-methods>
diff --git a/conditions.lua b/conditions.lua
index 37a3eff..d9b67fc 100644
--- a/conditions.lua
+++ b/conditions.lua
@@ -35,7 +35,6 @@ local API_GetItemCooldown = GetItemCooldown
local API_GetItemCount = GetItemCount
local API_GetNumTrackingTypes = GetNumTrackingTypes
local API_GetTime = GetTime
-local API_GetTotemInfo = GetTotemInfo
local API_GetTrackingInfo = GetTrackingInfo
local API_GetUnitSpeed = GetUnitSpeed
local API_GetWeaponEnchantInfo = GetWeaponEnchantInfo
@@ -133,7 +132,7 @@ do
local function AfterWhiteHit(condition, state)
local seconds, comparator, limit = condition[1], condition[2], condition[3]
local value = 0
- Ovale:OneTimeMessage("Warning: 'AfterWhiteHit() is not implemented.")
+ Ovale:OneTimeMessage("Warning: 'AfterWhiteHit()' is not implemented.")
return TestValue(0, INFINITY, value, state.currentTime, -1, comparator, limit)
end
@@ -4257,6 +4256,7 @@ do
end
do
+ local RUNE_OF_POWER = 116011
local RUNE_OF_POWER_BUFF = 116014
--- Get the remaining time in seconds before the latest Rune of Power expires.
@@ -4271,20 +4271,11 @@ do
-- if RuneOfPowerRemaining() < CastTime(rune_of_power) Spell(rune_of_power)
local function RuneOfPowerRemaining(condition, state)
+ Ovale:OneTimeMessage("Warning: 'RuneOfPowerRemaining()' is deprecated; use 'TotemRemaining(rune_of_power)' instead.")
local comparator, limit = condition[1], condition[2]
- local aura = state:GetAura("player", RUNE_OF_POWER_BUFF, "HELPFUL")
- if state:IsActiveAura(aura) then
- local start, ending
- for totemSlot = 1, 2 do
- local haveTotem, name, startTime, duration = API_GetTotemInfo(totemSlot)
- if haveTotem and startTime and (not start or startTime > start) then
- start = startTime
- ending = startTime + duration
- end
- end
- if start then
- return TestValue(0, INFINITY, ending - start, start, -1, comparator, limit)
- end
+ local count, start, ending = state:GetTotemCount(RUNE_OF_POWER)
+ if count > 0 then
+ return TestValue(start, ending, 0, ending, -1, comparator, limit)
end
return Compare(0, comparator, limit)
end
@@ -4971,7 +4962,7 @@ do
comparator, limit = condition[1], condition[2]
start = 0
end
- Ovale:OneTimeMessage("Warning: 'LastSwing() is not implemented.")
+ Ovale:OneTimeMessage("Warning: 'LastSwing()' is not implemented.")
return TestValue(start, INFINITY, 0, start, 1, comparator, limit)
end
@@ -4998,7 +4989,7 @@ do
comparator, limit = condition[1], condition[2]
ending = 0
end
- Ovale:OneTimeMessage("Warning: 'NextSwing() is not implemented.")
+ Ovale:OneTimeMessage("Warning: 'NextSwing()' is not implemented.")
return TestValue(0, ending, 0, ending, -1, comparator, limit)
end
@@ -5443,72 +5434,99 @@ do
end
do
- local OVALE_TOTEMTYPE =
- {
- -- Death Knights
- ghoul = 1,
- -- Druid
- mushroom = 1,
- -- XXX Mage
- crystal = 4,
- -- Monks
- statue = 1,
- -- Shamans
- fire = 1,
- earth = 2,
- water = 3,
- air = 4
- }
+ -- Deprecated: totem types
+ local function CheckDeprecatedTotem(id, state)
+ local warning = false
+ local specialization = state.specialization
+ if id == "mushroom" then
+ warning = id
+ if specialization == 1 then
+ -- Balance.
+ id = 88747
+ elseif specialization == 4 then
+ -- Restoration.
+ id = 145205
+ end
+ elseif id == "statue" then
+ warning = id
+ if specialization == 1 then
+ -- Brewmaster.
+ id = 115315
+ elseif specialization == 2 then
+ -- Mistweaver.
+ id = 115313
+ end
+ elseif id == "ghoul" then
+ -- Ghouls are no longer totems, but pets summoned by Unholy Death Knights.
+ warning = id
+ end
+ if warning then
+ Ovale:OneTimeMessage("Warning: '%s' is deprecated; using '%s' instead.", warning, tostring(id))
+ end
+ return id
+ end
- --- Test if the totem for shamans, the mushroom for druids, the ghoul for death knights, or the statue for monks has expired.
+ --- Test if the totem has expired.
-- @name TotemExpires
-- @paramsig boolean
- -- @param id The totem ID of the totem, ghoul or statue, or the type of totem.
- -- Valid types: fire, water, air, earth, ghoul, mushroom, statue.
+ -- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
-- @param seconds Optional. The maximum number of seconds before the totem should expire.
-- Defaults to 0 (zero).
- -- @param totem Optional. Sets the specific totem to check of given totem ID type.
- -- Valid values: any totem spell ID
-- @return A boolean value.
-- @see TotemPresent, TotemRemaining
-- @usage
-- if TotemExpires(fire) Spell(searing_totem)
- -- if TotemPresent(water totem=healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)
+ -- if TotemPresent(healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)
local function TotemExpires(condition, state)
- local totemId, seconds = condition[1], condition[2]
+ local id, seconds = condition[1], condition[2]
seconds = seconds or 0
- if type(totemId) ~= "number" then
- totemId = OVALE_TOTEMTYPE[totemId]
+ if condition.totem then
+ id = condition.totem
+ Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemExpires()' is deprecated.")
end
- local haveTotem, name, startTime, duration = API_GetTotemInfo(totemId)
- if haveTotem and startTime and (not condition.totem or OvaleSpellBook:GetSpellName(condition.totem) == name) then
- return startTime + duration - seconds, INFINITY
+ id = CheckDeprecatedTotem(id, state)
+ if type(id) == "string" then
+ local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
+ if haveTotem and startTime then
+ return startTime + duration - seconds, INFINITY
+ end
+ else -- if type(id) == "number" then
+ local count, start, ending = state:GetTotemCount(id)
+ if count > 0 then
+ return ending - seconds, INFINITY
+ end
end
return 0, INFINITY
end
- --- Test if the totem for shamans, the ghoul for death knights, or the statue for monks is present.
+ --- Test if the totem is present.
-- @name TotemPresent
-- @paramsig boolean
- -- @param id The totem ID of the totem, ghoul or statue, or the type of totem.
- -- Valid types: fire, water, air, earth, ghoul, statue.
- -- @param totem Optional. Sets the specific totem to check of given totem ID type.
- -- Valid values: any totem spell ID
+ -- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
-- @return A boolean value.
-- @see TotemExpires, TotemRemaining
-- @usage
-- if not TotemPresent(fire) Spell(searing_totem)
- -- if TotemPresent(water totem=healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)
+ -- if TotemPresent(healing_stream_totem) and TotemExpires(water 3) Spell(totemic_recall)
local function TotemPresent(condition, state)
- local totemId = condition[1]
- if type(totemId) ~= "number" then
- totemId = OVALE_TOTEMTYPE[totemId]
+ local id = condition[1]
+ if condition.totem then
+ id = condition.totem
+ Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemPresent()' is deprecated.")
end
- local haveTotem, name, startTime, duration = API_GetTotemInfo(totemId)
- if haveTotem and startTime and (not condition.totem or OvaleSpellBook:GetSpellName(condition.totem) == name) then
- return startTime, startTime + duration
+ id = CheckDeprecatedTotem(id, state)
+ if type(id) == "string" then
+ local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
+ if haveTotem and startTime then
+ return startTime, startTime + duration
+ end
+ else -- if type(id) == "number" then
+ local count, start, ending = state:GetTotemCount(id)
+ if count > 0 then
+ return start, ending
+ end
end
return nil
end
@@ -5519,27 +5537,33 @@ do
--- Get the remaining time in seconds before a totem expires.
-- @name TotemRemaining
-- @paramsig number or boolean
- -- @param id The totem ID of the totem, ghoul or statue, or the type of totem.
- -- Valid types: fire, water, air, earth, ghoul, statue.
+ -- @param id The ID of the spell used to summon the totem or one of the four shaman totem categories (air, earth, fire, water).
-- @param operator Optional. Comparison operator: less, atMost, equal, atLeast, more.
-- @param number Optional. The number to compare against.
- -- @param totem Optional. Sets the specific totem to check of given totem ID type.
- -- Valid values: any totem spell ID
-- @return The number of seconds.
-- @return A boolean value for the result of the comparison.
-- @see TotemExpires, TotemPresent
-- @usage
- -- if TotemRemaining(water totem=healing_stream_totem) <2 Spell(totemic_recall)
+ -- if TotemRemaining(healing_stream_totem) <2 Spell(totemic_recall)
local function TotemRemaining(condition, state)
- local totemId, comparator, limit = condition[1], condition[2], condition[3]
- if type(totemId) ~= "number" then
- totemId = OVALE_TOTEMTYPE[totemId]
+ local id, comparator, limit = condition[1], condition[2], condition[3]
+ if condition.totem then
+ id = condition.totem
+ Ovale:OneTimeMessage("Warning: using 'totem' parameter in 'TotemRemaining()' is deprecated.")
end
- local haveTotem, name, startTime, duration = API_GetTotemInfo(totemId)
- if haveTotem and startTime and (not condition.totem or OvaleSpellBook:GetSpellName(condition.totem) == name) then
- local start, ending = startTime, startTime + duration
- return TestValue(start, ending, duration, start, -1, comparator, limit)
+ id = CheckDeprecatedTotem(id, state)
+ if type(id) == "string" then
+ local haveTotem, name, startTime, duration = state:GetTotemInfo(id)
+ if haveTotem and startTime then
+ local start, ending = startTime, startTime + duration
+ return TestValue(start, ending, 0, ending, -1, comparator, limit)
+ end
+ else -- if type(id) == "number" then
+ local count, start, ending = state:GetTotemCount(id)
+ if count > 0 then
+ return TestValue(start, ending, 0, ending, -1, comparator, limit)
+ end
end
return Compare(0, comparator, limit)
end
diff --git a/scripts/ovale_druid_spells.lua b/scripts/ovale_druid_spells.lua
index 436856a..f081ece 100644
--- a/scripts/ovale_druid_spells.lua
+++ b/scripts/ovale_druid_spells.lua
@@ -400,6 +400,7 @@ Define(wild_growth 48438)
Define(wild_growth_buff 48438)
SpellInfo(wild_growth_buff duration=7 haste=spell tick=1)
Define(wild_mushroom_heal 145205)
+ SpellInfo(wild_mushroom_heal duration=30 totem=1)
Define(wrath 5176)
SpellAddBuff(wrath solar_empowerment_buff=-1)
SpellAddTargetBuff(wrath sunfire_debuff=extend,4 if_spell=balance_of_power)
diff --git a/scripts/ovale_mage_spells.lua b/scripts/ovale_mage_spells.lua
index 553def6..eaf2373 100644
--- a/scripts/ovale_mage_spells.lua
+++ b/scripts/ovale_mage_spells.lua
@@ -182,7 +182,7 @@ Define(presence_of_mind_buff 12043)
Define(profound_magic_buff 145252)
SpellInfo(profound_magic_buff duration=10 max_stacks=4)
Define(prismatic_crystal 152087)
- SpellInfo(prismatic_crystal cd=90)
+ SpellInfo(prismatic_crystal cd=90 duration=12 totem=1)
Define(prismatic_crystal_talent 20)
Define(pyroblast 11366)
SpellAddBuff(pyroblast ice_floes_buff=0 if_spell=ice_floes)
@@ -195,11 +195,10 @@ Define(pyroblast_debuff 11366)
Define(pyromaniac_buff 166868)
SpellInfo(pyromaniac_buff duration=4)
Define(rune_of_power 116011)
+ SpellInfo(rune_of_power buff_totem=rune_of_power_buff duration=180 max_totems=2 totem=1)
SpellAddBuff(rune_of_power ice_floes_buff=0 if_spell=ice_floes)
- SpellAddBuff(rune_of_power rune_of_power_buff=1)
SpellAddBuff(rune_of_power presence_of_mind_buff=0 if_spell=presence_of_mind)
Define(rune_of_power_buff 116014)
- SpellInfo(rune_of_power_buff duration=180)
Define(scorch 2948)
Define(spellsteal 30449)
Define(supernova 157980)
diff --git a/scripts/ovale_monk_spells.lua b/scripts/ovale_monk_spells.lua
index 55a4225..cf9c07a 100644
--- a/scripts/ovale_monk_spells.lua
+++ b/scripts/ovale_monk_spells.lua
@@ -244,9 +244,9 @@ Define(stance_of_the_wise_serpent 115070)
SpellInfo(stance_of_the_wise_serpent to_stance=monk_stance_of_the_wise_serpent)
SpellInfo(stance_of_the_wise_serpent unusable=1 if_stance=monk_stance_of_the_wise_serpent)
Define(summon_black_ox_statue 115315)
- SpellInfo(summon_black_ox_statue cd=10)
+ SpellInfo(summon_black_ox_statue cd=10 duration=900 totem=1)
Define(summon_jade_serpent_statue 115313)
- SpellInfo(summon_jade_serpent_statue cd=10)
+ SpellInfo(summon_jade_serpent_statue cd=10 duration=900 totem=1)
Define(surging_mist 116694)
SpellInfo(surging_mist chi=-1 if_stance=monk_stance_of_the_wise_serpent)
SpellInfo(surging_mist replace=surging_mist_glyphed unusable=1 glyph=glyph_of_surging_mist)
diff --git a/scripts/ovale_shaman_spells.lua b/scripts/ovale_shaman_spells.lua
index 15c933d..f71ce45 100644
--- a/scripts/ovale_shaman_spells.lua
+++ b/scripts/ovale_shaman_spells.lua
@@ -59,10 +59,10 @@ Define(chain_lightning 421)
SpellAddBuff(chain_lightning maelstrom_weapon_buff=0 if_spell=maelstrom_weapon)
SpellAddBuff(chain_lightning enhanced_chain_lightning_buff=1 if_spell=enhanced_chain_lightning)
Define(cloudburst_totem 157153)
- SpellInfo(cloudburst_totem cd=30)
+ SpellInfo(cloudburst_totem cd=30 duration=15 totem=water)
Define(cloudburst_totem_talent 19)
Define(earth_elemental_totem 2062)
- SpellInfo(earth_elemental_totem cd=300)
+ SpellInfo(earth_elemental_totem cd=300 duration=60 totem=earth)
SpellInfo(earth_elemental_totem buff_cdr=cooldown_reduction_agility_buff specialization=enhancement)
Define(earth_shield 974)
SpellAddTargetBuff(earth_shield earth_shield_buff=1)
@@ -109,7 +109,7 @@ Define(feral_spirit 51533)
SpellInfo(feral_spirit addcd=60 glyph=glyph_of_ephemeral_spirits)
SpellInfo(feral_spirit buff_cdr=cooldown_reduction_agility_buff)
Define(fire_elemental_totem 2894)
- SpellInfo(fire_elemental_totem cd=300)
+ SpellInfo(fire_elemental_totem cd=300 duration=60 totem=fire)
SpellInfo(fire_elemental_totem cd=150 glyph=glyph_of_fire_elemental_totem)
SpellInfo(fire_elemental_totem buff_cdr=cooldown_reduction_agility_buff specialization=enhancement)
Define(fire_nova 1535)
@@ -150,13 +150,13 @@ Define(healing_rain 73920)
SpellAddBuff(healing_rain ancestral_swiftness_buff=0 if_spell=ancestral_swiftness)
SpellAddBuff(healing_rain maelstrom_weapon_buff=0 if_spell=maelstrom_weapon)
Define(healing_stream_totem 5394)
- SpellInfo(healing_stream_totem cd=30)
+ SpellInfo(healing_stream_totem cd=30 duration=15 totem=water)
Define(healing_surge 8004)
SpellAddBuff(healing_surge ancestral_swiftness_buff=0 if_spell=ancestral_swiftness)
SpellAddBuff(healing_surge tidal_waves_buff=-1 if_spell=tidal_waves)
SpellAddBuff(healing_surge unleash_life_buff=0 if_spell=unleash_life)
Define(healing_tide_totem 108280)
- SpellInfo(healing_tide_totem cd=180)
+ SpellInfo(healing_tide_totem cd=180 duration=10 totem=water)
Define(healing_wave 77472)
SpellAddBuff(healing_wave ancestral_swiftness_buff=0 if_spell=ancestral_swiftness)
SpellAddBuff(healing_wave tidal_waves_buff=-1 if_spell=tidal_waves)
@@ -203,6 +203,7 @@ Define(liquid_magma_talent 21)
Define(maelstrom_weapon_buff 53817)
SpellInfo(maelstrom_weapon_buff duration=30 max_stacks=5)
Define(magma_totem 8190)
+ SpellInfo(magma_totem duration=60 totem=fire)
Define(primal_elementalist_talent 17)
Define(primal_strike 73899)
SpellInfo(primal_strike cd=8)
@@ -215,8 +216,9 @@ Define(riptide 61295)
Define(riptide_buff 61295)
SpellInfo(riptide_buff duration=18 haste=spell tick=3)
Define(searing_totem 3599)
+ SpellInfo(searing_totem duration=60 totem=fire)
Define(spirit_link_totem 98008)
- SpellInfo(spirit_link_totem cd=180)
+ SpellInfo(spirit_link_totem cd=180 duration=6 totem=air)
Define(spirit_walk 58875)
SpellInfo(spirit_walk cd=60)
SpellInfo(spirit_walk addcd=-15 glyph=glyph_of_spirit_walk)
@@ -225,7 +227,7 @@ Define(spiritwalkers_grace 79206)
SpellInfo(spiritwalkers_grace addcd=-60 glyph=glyph_of_spiritual_focus)
SpellInfo(spiritwalkers_grace buff_cdr=cooldown_reduction_agility_buff specialization=enhancement)
Define(storm_elemental_totem 152256)
- SpellInfo(storm_elemental_totem cd=300)
+ SpellInfo(storm_elemental_totem cd=300 duration=60 totem=air)
Define(storm_elemental_totem_talent 20)
Define(stormstrike 17364)
SpellInfo(stormstrike cd=7.5)
@@ -240,7 +242,7 @@ Define(tidal_waves_buff 53390)
Define(totemic_persistence_talent 8)
Define(totemic_recall 36936)
Define(tremor_totem 8143)
- SpellInfo(tremor_totem cd=60)
+ SpellInfo(tremor_totem cd=60 duration=10 totem=earth)
Define(unleash_elements 73680)
SpellInfo(unleash_elements cd=15)
SpellInfo(unleash_elements cd_haste=melee gcd_haste=melee if_spell=flurry)
@@ -269,7 +271,7 @@ Define(windstrike 115356)
SpellRequire(windstrike cd 0=buff,echo_of_the_elements_buff if_spell=echo_of_the_elements)
SpellAddBuff(windstrike echo_of_the_elements_buff=0 if_spell=echo_of_the_elements)
Define(windwalk_totem 108273)
- SpellInfo(windwalk_totem cd=60)
+ SpellInfo(windwalk_totem cd=60 duration=6 totem=air)
# Pet spells (Primal Elementalist Talent)
Define(pet_empower 118350)