Johnny C. Lam [11-10-13 - 00:31]
diff --git a/Ovale.toc b/Ovale.toc
index 1d5196d..3e075e7 100644
--- a/Ovale.toc
+++ b/Ovale.toc
@@ -29,7 +29,6 @@ OvaleLatency.lua
OvalePool.lua
OvalePoolGC.lua
OvalePoolRefCount.lua
-OvalePower.lua
OvaleQueue.lua
OvaleSpellBook.lua
OvaleStance.lua
@@ -39,8 +38,11 @@ OvaleDamageTaken.lua
OvalePaperDoll.lua
OvaleScore.lua
#
+OvaleState.lua
+#
OvaleAura.lua
OvaleComboPoints.lua
+OvalePower.lua
OvaleRecount.lua
OvaleScripts.lua
scripts\scripts.xml
@@ -51,13 +53,11 @@ OvaleSwing.lua
OvaleOptions.lua
OvaleFuture.lua
#
-OvaleState.lua
conditions\conditions.xml
-#
+OvaleCompile.lua
OvaleIcone.lua
OvaleIcone.xml
#
OvaleBestAction.lua
-OvaleCompile.lua
#
OvaleFrame.lua
diff --git a/OvaleAura.lua b/OvaleAura.lua
index d62f90f..3877199 100644
--- a/OvaleAura.lua
+++ b/OvaleAura.lua
@@ -20,6 +20,7 @@ local OvaleData = Ovale.OvaleData
local OvaleGUID = Ovale.OvaleGUID
local OvalePaperDoll = Ovale.OvalePaperDoll
local OvalePool = Ovale.OvalePool
+local OvaleState = Ovale.OvaleState
local ipairs = ipairs
local pairs = pairs
@@ -327,9 +328,11 @@ function OvaleAura:OnEnable()
self:RegisterEvent("UNIT_AURA")
self:RegisterMessage("Ovale_GroupChanged", RemoveAurasForMissingUnits)
self:RegisterMessage("Ovale_InactiveUnit")
+ OvaleState:RegisterState(self, self.statePrototype)
end
function OvaleAura:OnDisable()
+ OvaleState:UnregisterState(self)
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
self:UnregisterEvent("UNIT_AURA")
@@ -615,3 +618,338 @@ function OvaleAura:DebugListAura(unitId, filter)
end
end
--</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+ State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvaleAura.statePrototype = {
+ aura = nil,
+ serial = nil,
+}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvaleAura:InitializeState(state)
+ state.aura = {}
+ state.serial = 0
+end
+
+-- Reset the state to the current conditions.
+function OvaleAura:ResetState(state)
+ state.serial = state.serial + 1
+end
+
+-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
+function OvaleAura:ApplySpellOnPlayer(state, spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
+ -- If the spellcast has already ended, then the effects on the player have already occurred.
+ if endCast <= OvaleState.now then
+ return
+ end
+
+ -- Apply the auras on the player.
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.aura and si.aura.player then
+ state:ApplySpellAuras(spellId, startCast, endCast, OvaleGUID:GetGUID("player"), si.aura.player, spellcast)
+ end
+end
+
+-- Apply the effects of the spell on the target's state when it lands on the target.
+function OvaleAura:ApplySpellOnTarget(state, spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.aura and si.aura.target then
+ -- Apply the auras on the target.
+ state:ApplySpellAuras(spellId, startCast, endCast, targetGUID, si.aura.target, spellcast)
+ end
+end
+--</public-static-methods>
+
+-- Mix-in methods for simulator state.
+do
+ local statePrototype = OvaleAura.statePrototype
+
+ -- Apply the auras caused by the given spell in the simulator.
+ function statePrototype:ApplySpellAuras(spellId, startCast, endCast, guid, auraList, spellcast)
+ local state = self
+ for filter, filterInfo in pairs(auraList) do
+ for auraId, spellData in pairs(filterInfo) do
+ local si = OvaleData.spellInfo[auraId]
+ -- An aura is treated as a periodic aura if it sets "tick" explicitly in SpellInfo.
+ local isDoT = (si and si.tick)
+ local duration = spellData
+ local stacks = spellData
+
+ -- If aura is specified with a duration, then assume stacks == 1.
+ if type(duration) == "number" and duration > 0 then
+ stacks = 1
+ end
+ -- Set the duration to the proper length if it's a DoT.
+ if si and si.duration then
+ duration = state:GetDuration(auraId)
+ end
+
+ local start, ending, currentStacks, tick = state:GetAuraByGUID(guid, auraId, filter, true, target)
+ local newAura = state:NewAura(guid, auraId, filter)
+ newAura.mine = true
+
+ --[[
+ auraId=N, N > 0 N is duration, auraID is applied, add one stack
+ auraId=0 aura is removed
+ auraId=N, N < 0 N is number of stacks of aura removed
+ auraId=refresh auraId is refreshed, no change to stacks
+ --]]
+ if type(stacks) == "number" and stacks == 0 then
+ Ovale:Logf("Aura %d is completely removed", auraId)
+ newAura.stacks = 0
+ newAura.start = start
+ newAura.ending = endCast
+ elseif ending and endCast <= ending then
+ -- Spellcast ends before the aura expires.
+ if stacks == "refresh" or stacks > 0 then
+ if stacks == "refresh" then
+ Ovale:Logf("Aura %d is refreshed", auraId)
+ newAura.stacks = currentStacks
+ else -- if stacks > 0 then
+ newAura.stacks = currentStacks + stacks
+ Ovale:Logf("Aura %d gains a stack to %d because of spell %d (ending was %s)", auraId, newAura.stacks, spellId, ending)
+ end
+ newAura.start = start
+ if isDoT and ending > newAura.start and tick and tick > 0 then
+ -- Add new duration after the next tick is complete.
+ local remainingTicks = floor((ending - endCast) / tick)
+ newAura.ending = (ending - tick * remainingTicks) + duration
+ newAura.tick = OvaleAura:GetTickLength(auraId)
+ -- Re-snapshot stats for the DoT.
+ -- XXX This is not quite right because it uses the current player stats instead of the simulator's state.
+ OvalePaperDoll:SnapshotStats(newAura, spellcast)
+ newAura.damageMultiplier = state:GetDamageMultiplier(auraId)
+ else
+ newAura.ending = endCast + duration
+ end
+ Ovale:Logf("Aura %d ending is now %f", auraId, newAura.ending)
+ elseif stacks < 0 then
+ newAura.stacks = currentStacks + stacks
+ newAura.start = start
+ newAura.ending = ending
+ Ovale:Logf("Aura %d loses %d stack(s) to %d because of spell %d", auraId, -1 * stacks, newAura.stacks, spellId)
+ if newAura.stacks <= 0 then
+ Ovale:Logf("Aura %d is completely removed", auraId)
+ newAura.stacks = 0
+ newAura.ending = endCast
+ end
+ end
+ elseif type(stacks) == "number" and type(duration) == "number" and stacks > 0 and duration > 0 then
+ Ovale:Logf("New aura %d at %f on %s", auraId, endCast, guid)
+ newAura.stacks = stacks
+ newAura.start = endCast
+ newAura.ending = endCast + duration
+ if isDoT then
+ newAura.tick = OvaleAura:GetTickLength(auraId)
+ -- Snapshot stats for the DoT.
+ -- XXX This is not quite right because it uses the current player stats instead of the simulator's state.
+ OvalePaperDoll:SnapshotStats(newAura, spellcast)
+ newAura.damageMultiplier = state:GetDamageMultiplier(auraId)
+ end
+ end
+ end
+ end
+ end
+
+ function statePrototype:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
+ local state = self
+ local aura
+ if mine then
+ local auraTable = state.aura[guid]
+ if auraTable then
+ if filter then
+ local auraList = auraTable[filter]
+ if auraList then
+ if auraList[spellId] and auraList[spellId].serial == state.serial then
+ aura = auraList[spellId]
+ end
+ end
+ else
+ for auraFilter, auraList in pairs(auraTable) do
+ if auraList[spellId] and auraList[spellId].serial == state.serial then
+ aura = auraList[spellId]
+ filter = auraFilter
+ break
+ end
+ end
+ end
+ end
+ end
+ if aura then
+ if aura.stacks > 0 then
+ Ovale:Logf("Found %s aura %s on %s", filter, spellId, guid)
+ else
+ Ovale:Logf("Found %s aura %s on %s (removed)", filter, spellId, guid)
+ end
+ if auraFound then
+ for k, v in pairs(aura) do
+ auraFound[k] = v
+ end
+ end
+ return aura.start, aura.ending, aura.stacks, aura.gain
+ else
+ Ovale:Logf("Aura %s not found in state for %s", spellId, guid)
+ return OvaleAura:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
+ end
+ end
+
+ do
+ local aura = {}
+ local newAura = {}
+
+ function statePrototype:GetAura(unitId, spellId, filter, mine, auraFound)
+ local state = self
+ local guid = OvaleGUID:GetGUID(unitId)
+ if OvaleData.buffSpellList[spellId] then
+ if auraFound then wipe(newAura) end
+ local newStart, newEnding, newStacks, newGain
+ for auraId in pairs(OvaleData.buffSpellList[spellId]) do
+ if auraFound then wipe(aura) end
+ local start, ending, stacks, gain = state:GetAuraByGUID(guid, auraId, filter, mine, unitId, aura)
+ if start and (not newStart or stacks > newStacks) then
+ newStart = start
+ newEnding = ending
+ newStacks = stacks
+ newGain = gain
+ if auraFound then
+ wipe(newAura)
+ for k, v in pairs(aura) do
+ newAura[k] = v
+ end
+ end
+ end
+ end
+ if auraFound then
+ for k, v in pairs(newAura) do
+ auraFound[k] = v
+ end
+ end
+ return newStart, newEnding, newStacks, newGain
+ else
+ return state:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
+ end
+ end
+ end
+
+ -- Look for an aura on any target, excluding the given GUID.
+ -- Returns the earliest start time, the latest ending time, and the number of auras seen.
+ function statePrototype:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
+ local state = self
+ local start, ending, count = OvaleAura:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
+ -- TODO: This is broken because it doesn't properly account for removed auras in the current frame.
+ for guid, auraTable in pairs(state.aura) do
+ if guid ~= excludingGUID then
+ for auraFilter, auraList in pairs(auraTable) do
+ if not filter or auraFilter == filter then
+ local aura = auraList[spellId]
+ if aura and aura.serial == state.serial then
+ if aura.start and (not start or aura.start < start) then
+ start = aura.start
+ end
+ if aura.ending and (not ending or aura.ending > ending) then
+ ending = aura.ending
+ end
+ count = count + 1
+ end
+ end
+ end
+ end
+ end
+ return start, ending, count
+ end
+
+ function statePrototype:NewAura(guid, spellId, filter)
+ local state = self
+ if not state.aura[guid] then
+ state.aura[guid] = {}
+ end
+ if not state.aura[guid][filter] then
+ state.aura[guid][filter] = {}
+ end
+ if not state.aura[guid][filter][spellId] then
+ state.aura[guid][filter][spellId] = {}
+ end
+ local aura = state.aura[guid][filter][spellId]
+ aura.serial = state.serial
+ aura.mine = true
+ aura.gain = OvaleState.currentTime
+ return aura
+ end
+
+ function statePrototype:GetDamageMultiplier(spellId)
+ local state = self
+ local damageMultiplier = 1
+ if spellId then
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.damageAura then
+ local playerGUID = OvaleGUID:GetGUID("player")
+ for filter, auraList in pairs(si.damageAura) do
+ for auraSpellId, multiplier in pairs(auraList) do
+ local count = select(3, state:GetAuraByGUID(playerGUID, auraSpellId, filter, nil, "player"))
+ if count and count > 0 then
+ local auraSpellInfo = OvaleData.spellInfo[auraSpellId]
+ if auraSpellInfo.stacking and auraSpellInfo.stacking > 0 then
+ multiplier = 1 + (multiplier - 1) * count
+ end
+ damageMultiplier = damageMultiplier * multiplier
+ end
+ end
+ end
+ end
+ end
+ return damageMultiplier
+ end
+
+ -- Returns the duration, tick length, and number of ticks of an aura.
+ function statePrototype:GetDuration(auraSpellId)
+ local state = self
+ local si
+ if type(auraSpellId) == "number" then
+ si = OvaleData.spellInfo[auraSpellId]
+ elseif OvaleData.buffSpellList[auraSpellId] then
+ for spellId in pairs(OvaleData.buffSpellList[auraSpellId]) do
+ si = OvaleData.spellInfo[spellId]
+ if si then
+ auraSpellId = spellId
+ break
+ end
+ end
+ end
+ if si and si.duration then
+ local OvaleComboPoints = Ovale.OvaleComboPoints
+ local OvalePower = Ovale.OvalePower
+ local duration = si.duration
+ local combo = state.combo or 0
+ local holy = state.holy or 1
+ if si.adddurationcp then
+ duration = duration + si.adddurationcp * combo
+ end
+ if si.adddurationholy then
+ duration = duration + si.adddurationholy * (holy - 1)
+ end
+ if si.tick then -- DoT
+ --DoT duration is tick * numTicks.
+ local tick = OvaleAura:GetTickLength(auraSpellId)
+ local numTicks = floor(duration / tick + 0.5)
+ duration = tick * numTicks
+ return duration, tick, numTicks
+ end
+ return duration
+ end
+ end
+
+ -- Track a new Eclipse buff that starts at timestamp.
+ function statePrototype:AddEclipse(timestamp, spellId)
+ local state = self
+ local aura = state:NewAura(self_player_guid, spellId, "HELPFUL")
+ aura.start = timestamp
+ aura.ending = nil
+ aura.stacks = 1
+ end
+end
diff --git a/OvaleBestAction.lua b/OvaleBestAction.lua
index f5f2cde..b12ba04 100644
--- a/OvaleBestAction.lua
+++ b/OvaleBestAction.lua
@@ -17,6 +17,7 @@ local OvaleActionBar = Ovale.OvaleActionBar
local OvaleCondition = Ovale.OvaleCondition
local OvaleData = Ovale.OvaleData
local OvaleEquipement = Ovale.OvaleEquipement
+local OvaleFuture = Ovale.OvaleFuture
local OvalePaperDoll = Ovale.OvalePaperDoll
local OvalePool = Ovale.OvalePool
local OvalePower = Ovale.OvalePower
@@ -699,7 +700,7 @@ local OVALE_COMPUTE_VISITOR =
--<public-static-methods>
function OvaleBestAction:StartNewAction()
OvaleState:Reset()
- OvaleState:ApplyActiveSpells()
+ OvaleFuture:ApplyInFlightSpells()
self_serial = self_serial + 1
end
diff --git a/OvaleComboPoints.lua b/OvaleComboPoints.lua
index cabc92f..78e5b77 100644
--- a/OvaleComboPoints.lua
+++ b/OvaleComboPoints.lua
@@ -17,6 +17,7 @@ Ovale.OvaleComboPoints = OvaleComboPoints
local OvaleData = Ovale.OvaleData
local OvaleGUID = Ovale.OvaleGUID
local OvalePaperDoll = Ovale.OvalePaperDoll
+local OvaleState = Ovale.OvaleState
local API_GetComboPoints = GetComboPoints
local MAX_COMBO_POINTS = MAX_COMBO_POINTS
@@ -35,11 +36,13 @@ function OvaleComboPoints:OnEnable()
self:RegisterEvent("PLAYER_TARGET_CHANGED", "Refresh")
self:RegisterEvent("UNIT_COMBO_POINTS")
self:RegisterEvent("UNIT_TARGET", "UNIT_COMBO_POINTS")
+ OvaleState:RegisterState(self, self.statePrototype)
end
end
function OvaleComboPoints:OnDisable()
if OvalePaperDoll.class == "ROGUE" or OvalePaperDoll.class == "DRUID" then
+ OvaleState:UnregisterState(self)
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
self:UnregisterEvent("PLAYER_LOGIN")
@@ -92,3 +95,67 @@ function OvaleComboPoints:Debug()
Ovale:FormatPrint("Player has %d combo points on target %s.", self.combo, OvaleGUID:GetGUID("target"))
end
--</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+ State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvaleComboPoints.statePrototype = {
+ combo = nil,
+}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvaleComboPoints:InitializeState(state)
+ state.combo = 0
+end
+
+-- Reset the state to the current conditions.
+function OvaleComboPoints:ResetState(state)
+ state.combo = self.combo or 0
+end
+
+-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
+function OvaleComboPoints:ApplySpellOnPlayer(state, spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
+ -- If the spellcast has already ended, then the effects on the player have already occurred.
+ if endCast <= OvaleState.now then
+ return
+ end
+
+ local si = OvaleData.spellInfo[spellId]
+ if si and si.combo then
+ local cost = si.combo
+ local power = state.combo or 0
+ --[[
+ cost > 0 means that the spell generates combo points.
+ cost < 0 means that the spell costs combo points.
+ cost == 0 means that the spell uses all of the combo points (finisher).
+ --]]
+ if cost == 0 then
+ power = 0
+ else
+ power = power + cost
+ end
+ --[[
+ Add extra combo points generated by presence of a buff.
+ "buff_combo" is the spell ID of the buff that causes extra points to be generated or used.
+ "buff_combo_amount" is the number of extra points generated or used, defaulting to 1
+ (one extra point generated).
+ --]]
+ if si.buff_combo and state:GetAura("player", si.buff_combo, nil, true) then
+ local buffAmount = si.buff_combo_amount or 1
+ power = power + buffAmount
+ end
+ -- Clamp combo points to lower and upper limits.
+ if power < 0 then
+ power = 0
+ end
+ if power > MAX_COMBO_POINTS then
+ power = MAX_COMBO_POINTS
+ end
+ state.combo = power
+ end
+end
+--</public-static-methods>
diff --git a/OvaleFuture.lua b/OvaleFuture.lua
index d5c9e2c..490c059 100644
--- a/OvaleFuture.lua
+++ b/OvaleFuture.lua
@@ -23,6 +23,7 @@ local OvalePaperDoll = Ovale.OvalePaperDoll
local OvalePool = Ovale.OvalePool
local OvaleScore = Ovale.OvaleScore
local OvaleSpellBook = Ovale.OvaleSpellBook
+local OvaleState = Ovale.OvaleState
local ipairs = ipairs
local pairs = pairs
@@ -249,9 +250,11 @@ function OvaleFuture:OnEnable()
self:RegisterEvent("UNIT_SPELLCAST_START")
self:RegisterMessage("Ovale_AuraAdded")
self:RegisterMessage("Ovale_InactiveUnit")
+ OvaleState:RegisterState(self, self.statePrototype)
end
function OvaleFuture:OnDisable()
+ OvaleState:UnregisterState(self)
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
self:UnregisterEvent("UNIT_SPELLCAST_CHANNEL_START")
@@ -446,8 +449,10 @@ function OvaleFuture:COMBAT_LOG_EVENT_UNFILTERED(event, ...)
end
end
--- Apply spells that are being cast or are in flight.
-function OvaleFuture:ApplyInFlightSpells(now, ApplySpell)
+-- Apply the effects of spells that are being cast or are in flight, allowing us to
+-- ignore lag or missile travel time.
+function OvaleFuture:ApplyInFlightSpells()
+ local now = OvaleState.now
local index = 0
local spellcast, si
while true do
@@ -460,7 +465,7 @@ function OvaleFuture:ApplyInFlightSpells(now, ApplySpell)
if not (si and si.toggle) then
Ovale:Logf("now = %f, spellId = %d, endCast = %f", now, spellcast.spellId, spellcast.stop)
if now - spellcast.stop < 5 then
- ApplySpell(spellcast.spellId, spellcast.start, spellcast.stop, spellcast.stop, spellcast.nocd, spellcast.target, spellcast)
+ OvaleState:ApplySpell(spellcast.spellId, spellcast.start, spellcast.stop, spellcast.stop, spellcast.nocd, spellcast.target, spellcast)
else
tremove(self_activeSpellcast, index)
self_pool:Release(spellcast)
@@ -497,3 +502,59 @@ function OvaleFuture:Debug()
end
end
--</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+ State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvaleFuture.statePrototype = {
+ counter = nil,
+}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvaleFuture:InitializeState(state)
+ state.counter = {}
+end
+
+-- Reset the state to the current conditions.
+function OvaleFuture:ResetState(state)
+ for k, v in pairs(self.counter) do
+ state.counter[k] = self.counter[k]
+ end
+end
+
+-- Apply the effects of the spell at the start of the spellcast.
+function OvaleFuture:ApplySpellStart(state, spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
+ -- If the spellcast has already started, then the effects have already occurred.
+ if startCast < OvaleState.now then
+ return
+ end
+
+ local si = OvaleData.spellInfo[spellId]
+ if si then
+ -- Increment and reset spell counters.
+ if si.inccounter then
+ local id = si.inccounter
+ local value = state.counter[id] and state.counter[id] or 0
+ state.counter[id] = value + 1
+ end
+ if si.resetcounter then
+ local id = si.resetcounter
+ state.counter[id] = 0
+ end
+ end
+end
+--</public-static-methods>
+
+-- Mix-in methods for simulator state.
+do
+ local statePrototype = OvaleFuture.statePrototype
+
+ function statePrototype:GetCounterValue(id)
+ local state = self
+ return state.counter[id] and state.counter[id] or 0
+ end
+end
\ No newline at end of file
diff --git a/OvaleOptions.lua b/OvaleOptions.lua
index 00fe530..35abca0 100644
--- a/OvaleOptions.lua
+++ b/OvaleOptions.lua
@@ -19,6 +19,7 @@ local L = Ovale.L
local OvalePaperDoll = Ovale.OvalePaperDoll
local OvaleScripts = Ovale.OvaleScripts
local OvaleSpellBook = Ovale.OvaleSpellBook
+local OvaleState = Ovale.OvaleState
local strgmatch = string.gmatch
local strgsub = string.gsub
@@ -600,7 +601,7 @@ local self_options =
name = "Power",
type = "execute",
func = function()
- if Ovale.OvaleState then Ovale.OvaleState:DebugPower() end
+ OvaleState.state:DebugPower()
end
},
talent =
diff --git a/OvalePower.lua b/OvalePower.lua
index 5f4fba9..1b993cc 100644
--- a/OvalePower.lua
+++ b/OvalePower.lua
@@ -13,8 +13,13 @@ local OvalePower = Ovale:NewModule("OvalePower", "AceEvent-3.0")
Ovale.OvalePower = OvalePower
--<private-static-properties>
+local OvaleData = Ovale.OvaleData
+local OvaleState = Ovale.OvaleState
+
local pairs = pairs
+local select = select
local API_GetPowerRegen = GetPowerRegen
+local API_GetSpellInfo = GetSpellInfo
local API_UnitPower = UnitPower
local API_UnitPowerMax = UnitPowerMax
local API_UnitPowerType = UnitPowerType
@@ -94,9 +99,11 @@ function OvalePower:OnEnable()
self:RegisterEvent("UNIT_RANGEDDAMAGE")
self:RegisterEvent("UNIT_SPELL_HASTE", "UNIT_RANGEDDAMAGE")
self:RegisterMessage("Ovale_StanceChanged", "EventHandler")
+ OvaleState:RegisterState(self, self.statePrototype)
end
function OvalePower:OnDisable()
+ OvaleState:UnregisterState(self)
self:UnregisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
self:UnregisterEvent("PLAYER_ALIVE")
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
@@ -188,4 +195,117 @@ function OvalePower:Debug()
Ovale:FormatPrint("Active regen: %f", self.activeRegen)
Ovale:FormatPrint("Inactive regen: %f", self.inactiveRegen)
end
---</public-static-methods>
\ No newline at end of file
+--</public-static-methods>
+
+--[[----------------------------------------------------------------------------
+ State machine for simulator.
+--]]----------------------------------------------------------------------------
+
+--<public-static-properties>
+OvalePower.statePrototype = {
+ powerRate = nil,
+}
+--</public-static-properties>
+
+--<public-static-methods>
+-- Initialize the state.
+function OvalePower:InitializeState(state)
+ state.powerRate = {}
+end
+
+-- Reset the state to the current conditions.
+function OvalePower:ResetState(state)
+ -- Power levels for each resource.
+ for powerType in pairs(self.POWER_INFO) do
+ state[powerType] = self.power[powerType] or 0
+ end
+ -- Clear power regeneration rates for each resource.
+ for powerType in pairs(self.POWER_INFO) do
+ state.powerRate[powerType] = 0
+ end
+ -- Set power regeneration for current resource.
+ if Ovale.enCombat then
+ state.powerRate[self.powerType] = self.activeRegen
+ else
+ state.powerRate[self.powerType] = self.inactiveRegen
+ end
+end
+
+-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
+function OvalePower:ApplySpellOnPlayer(state, spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
+ -- If the spellcast has already ended, then the effects on the player have already occurred.
+ if endCast <= OvaleState.now then
+ return
+ end
+
+ local si = OvaleData.spellInfo[spellId]
+
+ -- Update power using information from GetSpellInfo() if there is no SpellInfo() for the spell's cost.
+ do
+ local cost, _, powerType = select(4, API_GetSpellInfo(spellId))
+ if cost and powerType then
+ powerType = self.POWER_TYPE[powerType]
+ if not si or not si[powerType] then
+ state[powerType] = state[powerType] - cost
+ end
+ end
+ end
+
+ if si then
+ -- Update power state except for eclipse energy.
+ for powerType, powerInfo in pairs(self.POWER_INFO) do
+ if powerType ~= "eclipse" then
+ local cost = si[powerType]
+ local power = state[powerType] or 0
+ if cost then
+ --[[
+ cost > 0 means that the spell costs resources.
+ cost < 0 means that the spell generates resources.
+ cost == 0 means that the spell uses all of the resources (zeroes it out).
+ --]]
+ if cost == 0 then
+ power = 0
+ else
+ power = power - cost
+ end
+ --[[
+ Add extra resource generated by presence of a buff.
+ "buff_<powerType>" is the spell ID of the buff that causes extra resources to be generated or used.
+ "buff_<powerType>_amount" is the amount of extra resources generated or used, defaulting to -1
+ (one extra resource generated).
+ --]]
+ local buffParam = "buff_" .. tostring(powerType)
+ local buffAmoumtParam = buffParam .. "_amount"
+ if si[buffParam] and state:GetAura("player", si[buffParam], nil, true) then
+ local buffAmount = si[buffAmountParam] or -1
+ power = power - buffAmount
+ end
+ -- Clamp power to lower and upper limits.
+ local mini = powerInfo.mini or 0
+ local maxi = powerInfo.maxi or self.maxPower[powerType]
+ if mini and power < mini then
+ power = mini
+ end
+ if maxi and power > maxi then
+ power = maxi
+ end
+ state[powerType] = power
+ end
+ end
+ end
+ end
+end
+--</public-static-methods>
+
+-- Mix-in methods for simulator state.
+do
+ local statePrototype = OvalePower.statePrototype
+
+ -- Print out the levels of each power type in the current state.
+ function statePrototype:DebugPower()
+ local state = self
+ for powerType in pairs(OvalePower.POWER_INFO) do
+ Ovale:FormatPrint("%s = %d", powerType, state[powerType])
+ end
+ end
+end
diff --git a/OvaleState.lua b/OvaleState.lua
index a6857d7..a52b56e 100644
--- a/OvaleState.lua
+++ b/OvaleState.lua
@@ -15,34 +15,37 @@ local OvaleState = Ovale:NewModule("OvaleState")
Ovale.OvaleState = OvaleState
--<private-static-properties>
-local OvaleAura = Ovale.OvaleAura
-local OvaleComboPoints = Ovale.OvaleComboPoints
local OvaleData = Ovale.OvaleData
-local OvaleFuture = Ovale.OvaleFuture
local OvaleGUID = Ovale.OvaleGUID
local OvalePaperDoll = Ovale.OvalePaperDoll
-local OvalePower = Ovale.OvalePower
+local OvaleQueue = Ovale.OvaleQueue
local OvaleSpellBook = Ovale.OvaleSpellBook
local OvaleStance = Ovale.OvaleStance
local floor = math.floor
local pairs = pairs
local select = select
+local tinsert = table.insert
+local tremove = table.remove
local tostring = tostring
local type = type
local wipe = table.wipe
local API_GetEclipseDirection = GetEclipseDirection
local API_GetRuneCooldown = GetRuneCooldown
local API_GetRuneType = GetRuneType
-local API_GetSpellInfo = GetSpellInfo
local API_GetTime = GetTime
local API_UnitHealth = UnitHealth
local API_UnitHealthMax = UnitHealthMax
-local MAX_COMBO_POINTS = MAX_COMBO_POINTS
+
+local self_statePrototype = {}
+local self_stateModules = OvaleQueue:NewQueue("OvaleState_stateModules")
local self_runes = {}
local self_runesCD = {}
+-- Whether the state of the simulator has been initialized.
+local self_stateIsInitialized = false
+
-- Aura IDs for Eclipse buffs.
local LUNAR_ECLIPSE = 48518
local SOLAR_ECLIPSE = 48517
@@ -52,73 +55,101 @@ local STARFALL = 48505
--<public-static-properties>
--the state in the current frame
-OvaleState.state = {rune={}, cd = {}, counter={}}
-OvaleState.aura = {}
-OvaleState.serial = 0
-for i=1,6 do
- OvaleState.state.rune[i] = {}
-end
+OvaleState.state = {}
--The spell being cast
OvaleState.currentSpellId = nil
+OvaleState.now = nil
OvaleState.maintenant = nil
OvaleState.currentTime = nil
OvaleState.attenteFinCast = nil
OvaleState.startCast = nil
OvaleState.endCast = nil
OvaleState.gcd = 1.5
-OvaleState.powerRate = {}
OvaleState.lastSpellId = nil
--</public-static-properties>
--<private-static-methods>
-local function ApplySpell(spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
- local self = OvaleState
- self:ApplySpell(spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
-end
-
--- Track a new Eclipse buff that starts at timestamp.
-local function AddEclipse(timestamp, spellId)
+-- XXX The way this function updates the rune state looks completely wrong.
+local function AddRune(atTime, runeType, value)
local self = OvaleState
- local newAura = self:NewAura(OvaleGUID:GetGUID("player"), spellId, "HELPFUL")
- newAura.start = timestamp
- newAura.ending = nil
- newAura.stacks = 1
+ for i = 1, 6 do
+ local rune = self.state.rune[i]
+ if (rune.type == runeType or rune.type == 4) and rune.cd <= atTime then
+ rune.cd = atTIme + 10
+ end
+ end
end
--</private-static-methods>
--<public-static-methods>
+function OvaleState:RegisterState(addon, statePrototype)
+ self_stateModules:Insert(addon)
+ self_statePrototype[addon] = statePrototype
+
+ -- Mix-in addon's state prototype into OvaleState.state.
+ for k, v in pairs(statePrototype) do
+ self.state[k] = v
+ end
+end
+
+function OvaleState:UnregisterState(addon)
+ stateModules = OvaleQueue:NewQueue("OvaleState_stateModules")
+ while self_stateModules:Size() > 0 do
+ local stateAddon = self_stateModules:Remove()
+ if stateAddon ~= addon then
+ stateModules:Insert(addon)
+ end
+ end
+ self_stateModules = stateModules
+
+ -- Remove mix-in methods from addon's state prototype.
+ local statePrototype = self_statePrototype[addon]
+ for k in pairs(statePrototype) do
+ self.state[k] = nil
+ end
+ self_stateModules[addon] = nil
+end
+
+function OvaleState:InvokeMethod(methodName, ...)
+ for _, addon in self_stateModules:Iterator() do
+ if addon[methodName] then
+ addon[methodName](addon, self.state, ...)
+ end
+ end
+end
+
function OvaleState:StartNewFrame()
- self.maintenant = API_GetTime()
+ if not self_stateIsInitialized then
+ self:InitializeState()
+ end
+ self.now = API_GetTime()
+ self.maintenant = self.now
self.gcd = self:GetGCD()
end
-function OvaleState:UpdatePowerRates()
- for powerType in pairs(OvalePower.POWER_INFO) do
- self.powerRate[powerType] = 0
- end
- -- Power regeneration for current power type.
- if Ovale.enCombat then
- self.powerRate[OvalePower.powerType] = OvalePower.activeRegen
- else
- self.powerRate[OvalePower.powerType] = OvalePower.inactiveRegen
+function OvaleState:InitializeState()
+ self:InvokeMethod("InitializeState")
+
+ self.state.rune = {}
+ for i = 1, 6 do
+ self.state.rune[i] = {}
end
+
+ -- Legacy fields
+ self.powerRate = self.state.powerRate
+
+ self_stateIsInitialized = true
end
function OvaleState:Reset()
self.lastSpellId = Ovale.lastSpellcast and Ovale.lastSpellcast.spellId
- self.serial = self.serial + 1
self.currentTime = self.maintenant
Ovale:Logf("Reset state with current time = %f", self.currentTime)
self.currentSpellId = nil
self.attenteFinCast = self.maintenant
- -- Snapshot the current power and regeneration rates.
- self.state.combo = OvaleComboPoints.combo
- for powerType in pairs(OvalePower.POWER_INFO) do
- self.state[powerType] = OvalePower.power[powerType]
- end
- self:UpdatePowerRates()
-
+ self:InvokeMethod("ResetState")
+
if OvalePaperDoll.class == "DEATHKNIGHT" then
for i=1,6 do
self.state.rune[i].type = API_GetRuneType(i)
@@ -140,16 +171,6 @@ function OvaleState:Reset()
v.enable = 0
v.toggled = nil
end
-
- for k,v in pairs(self.state.counter) do
- self.state.counter[k] = OvaleFuture.counter[k]
- end
-end
-
--- Apply the effects of spells that are being cast or are in flight, allowing us to
--- ignore lag or missile travel time.
-function OvaleState:ApplyActiveSpells()
- OvaleFuture:ApplyInFlightSpells(self.maintenant, ApplySpell)
end
--[[
@@ -200,30 +221,12 @@ end
-- Apply the effects of the spell at the start of the spellcast.
function OvaleState:ApplySpellStart(spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
- local si = OvaleData.spellInfo[spellId]
- --[[
- If the spellcast has already started, then the effects have already occurred,
- so only consider spells that are cast in the future in the simulator.
- --]]
- if startCast >= self.maintenant then
- if si then
- -- Increment and reset spell counters.
- if si.inccounter then
- local id = si.inccounter
- local value = self.state.counter[id] and self.state.counter[id] or 0
- self.state.counter[id] = value + 1
- end
- if si.resetcounter then
- local id = si.resetcounter
- self.state.counter[id] = 0
- end
- end
- end
+ self:InvokeMethod("ApplySpellStart", spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
end
-- Apply the effects of the spell on the player's state, assuming the spellcast completes.
function OvaleState:ApplySpellOnPlayer(spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
- local si = OvaleData.spellInfo[spellId]
+ self:InvokeMethod("ApplySpellOnPlayer", spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
--[[
If the spellcast has already ended, then the effects have already occurred,
so only consider spells that have not yet finished casting in the simulator.
@@ -234,21 +237,12 @@ function OvaleState:ApplySpellOnPlayer(spellId, startCast, endCast, nextCast, no
-- Adjust the player's resources.
self:ApplySpellCost(spellId, startCast, endCast)
-
- -- Apply the auras on the player.
- if si and si.aura and si.aura.player then
- self:ApplySpellAuras(spellId, startCast, endCast, OvaleGUID:GetGUID("player"), si.aura.player, spellcast)
- end
end
end
-- Apply the effects of the spell on the target's state when it lands on the target.
function OvaleState:ApplySpellOnTarget(spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
- local si = OvaleData.spellInfo[spellId]
- if si and si.aura and si.aura.target then
- -- Apply the auras on the target.
- self:ApplySpellAuras(spellId, startCast, endCast, targetGUID, si.aura.target, spellcast)
- end
+ self:InvokeMethod("ApplySpellOnTarget", spellId, startCast, endCast, nextCast, nocd, targetGUID, spellcast)
end
-- Adjust a spell cooldown in the simulator.
@@ -308,95 +302,8 @@ end
-- Adjust the player's resources in the simulator from casting the given spell.
function OvaleState:ApplySpellCost(spellId, startCast, endCast)
local si = OvaleData.spellInfo[spellId]
- local _, _, _, cost, _, powerType = API_GetSpellInfo(spellId)
-
- -- Update power using information from GetSpellInfo() if there is no user-defined SpellInfo() for the spell's cost.
- if cost and powerType then
- powerType = OvalePower.POWER_TYPE[powerType]
- if not si or not si[powerType] then
- self.state[powerType] = self.state[powerType] - cost
- end
- end
if si then
- -- Update power state, except for combo points, eclipse energy, and runes.
- for powerType, powerInfo in pairs(OvalePower.POWER_INFO) do
- if powerType ~= "eclipse" then
- local cost = si[powerType]
- if cost then
- --[[
- cost > 0 means that the spell costs resources.
- cost < 0 means that the spell generates resources.
- cost == 0 means that the spell uses all of the resources (zeroes it out).
- --]]
- if cost == 0 then
- self.state[powerType] = 0
- else
- self.state[powerType] = self.state[powerType] - cost
- end
- --[[
- Add extra resource generated by presence of a buff.
- "buff_<powerType>" is the spell ID of the buff that causes extra resources to be generated or used.
- "buff_<powerType>_amount" is the amount of extra resources generated or used, defaulting to -1
- (one extra resource generated).
- --]]
- local buffParam = "buff_" .. tostring(powerType)
- local buffAmoumtParam = buffParam .. "_amount"
- if si[buffParam] and self:GetAura("player", si[buffParam], nil, true) then
- local buffAmount = si[buffAmountParam] or -1
- self.state[powerType] = self.state[powerType] - buffAmount
- end
- -- Clamp self.state[powerType] to lower and upper limits.
- local mini = powerInfo.mini or 0
- local maxi = powerInfo.maxi or OvalePower.maxPower[powerType]
- if mini and self.state[powerType] < mini then
- self.state[powerType] = mini
- end
- if maxi and self.state[powerType] > maxi then
- self.state[powerType] = maxi
- end
- end
- end
- end
-
- --[[
- Combo points: This resource is handled specially because it has different semantics
- from other resources. In particular, it's a resource that's attached to the target
- and not to the player.
- --]]
- if si.combo then
- local combo = si.combo
- --[[
- Combo points have the opposite meaning from other resources:
-
- combo > 0 means that the spell generates resources.
- combo < 0 means that the spell costs resources.
- combo == 0 means that the spell uses all of the combo points.
- --]]
- if combo == 0 then
- self.state.combo = 0
- else
- self.state.combo = self.state.combo + combo
- end
- --[[
- Add extra combo points generated by presence of a buff.
- "buff_combo" is the spell ID of the buff that causes extra points to be generated or used.
- "buff_combo_amount" is the number of extra points generated or used, defaulting to 1
- (one extra resource generated).
- --]]
- if si.buff_combo and self:GetAura("player", si.buff_combo, nil, true) then
- local buffAmount = si.buff_combo_amount or 1
- self.state.combo = self.state.combo + buffAmount
- end
- -- Clamp self.state.combo to lower and upper limits.
- if self.state.combo < 0 then
- self.state.combo = 0
- end
- if self.state.combo > MAX_COMBO_POINTS then
- self.state.combo = MAX_COMBO_POINTS
- end
- end
-
-- Eclipse
if si.eclipse then
local energy = si.eclipse
@@ -417,7 +324,7 @@ function OvaleState:ApplySpellCost(spellId, startCast, endCast)
-- Clamp Eclipse energy to min/max values and note that an Eclipse state will be reached after the spellcast.
if self.state.eclipse <= -100 then
self.state.eclipse = -100
- AddEclipse(endCast, LUNAR_ECLIPSE)
+ self.state:AddEclipse(endCast, LUNAR_ECLIPSE)
-- Reaching Lunar Eclipse resets the cooldown of Starfall.
local cd = self:GetCD(STARFALL)
if cd then
@@ -427,118 +334,22 @@ function OvaleState:ApplySpellCost(spellId, startCast, endCast)
end
elseif self.state.eclipse >= 100 then
self.state.eclipse = 100
- AddEclipse(endCast, SOLAR_ECLIPSE)
+ self.state:AddEclipse(endCast, SOLAR_ECLIPSE)
end
end
-- Runes
if si.blood and si.blood < 0 then
- self:AddRune(startCast, 1, si.blood)
+ AddRune(startCast, 1, si.blood)
end
if si.unholy and si.unholy < 0 then
- self:AddRune(startCast, 2, si.unholy)
+ AddRune(startCast, 2, si.unholy)
end
if si.frost and si.frost < 0 then
- self:AddRune(startCast, 3, si.frost)
+ AddRune(startCast, 3, si.frost)
end
if si.death and si.death < 0 then
- self:AddRune(startCast, 4, si.death)
- end
- end
-end
-
--- XXX The way this function updates the rune state looks completely wrong.
-function OvaleState:AddRune(atTime, runeType, value)
- for i = 1, 6 do
- local rune = self.state.rune[i]
- if (rune.type == runeType or rune.type == 4) and rune.cd <= atTime then
- rune.cd = atTime + 10
- end
- end
-end
-
--- Apply the auras caused by the given spell in the simulator.
-function OvaleState:ApplySpellAuras(spellId, startCast, endCast, guid, auraList, spellcast)
- for filter, filterInfo in pairs(auraList) do
- for auraId, spellData in pairs(filterInfo) do
- local si = OvaleData.spellInfo[auraId]
- -- An aura is treated as a periodic aura if it sets "tick" explicitly in SpellInfo.
- local isDoT = (si and si.tick)
- local duration = spellData
- local stacks = spellData
-
- -- If aura is specified with a duration, then assume stacks == 1.
- if type(duration) == "number" and duration > 0 then
- stacks = 1
- end
- -- Set the duration to the proper length if it's a DoT.
- if si and si.duration then
- duration = self:GetDuration(auraId)
- end
-
- local start, ending, currentStacks, tick = self:GetAuraByGUID(guid, auraId, filter, true, target)
- local newAura = self:NewAura(guid, auraId, filter)
- newAura.mine = true
-
- --[[
- auraId=N, N > 0 N is duration, auraID is applied, add one stack
- auraId=0 aura is removed
- auraId=N, N < 0 N is number of stacks of aura removed
- auraId=refresh auraId is refreshed, no change to stacks
- --]]
- if type(stacks) == "number" and stacks == 0 then
- Ovale:Logf("Aura %d is completely removed", auraId)
- newAura.stacks = 0
- newAura.start = start
- newAura.ending = endCast
- elseif ending and endCast <= ending then
- -- Spellcast ends before the aura expires.
- if stacks == "refresh" or stacks > 0 then
- if stacks == "refresh" then
- Ovale:Logf("Aura %d is refreshed", auraId)
- newAura.stacks = currentStacks
- else -- if stacks > 0 then
- newAura.stacks = currentStacks + stacks
- Ovale:Logf("Aura %d gains a stack to %d because of spell %d (ending was %s)", auraId, newAura.stacks, spellId, ending)
- end
- newAura.start = start
- if isDoT and ending > newAura.start and tick and tick > 0 then
- -- Add new duration after the next tick is complete.
- local remainingTicks = floor((ending - endCast) / tick)
- newAura.ending = (ending - tick * remainingTicks) + duration
- newAura.tick = OvaleAura:GetTickLength(auraId)
- -- Re-snapshot stats for the DoT.
- -- XXX This is not quite right because it uses the current player stats instead of the simulator's state.
- OvalePaperDoll:SnapshotStats(newAura, spellcast)
- newAura.damageMultiplier = self:GetDamageMultiplier(auraId)
- else
- newAura.ending = endCast + duration
- end
- Ovale:Logf("Aura %d ending is now %f", auraId, newAura.ending)
- elseif stacks < 0 then
- newAura.stacks = currentStacks + stacks
- newAura.start = start
- newAura.ending = ending
- Ovale:Logf("Aura %d loses %d stack(s) to %d because of spell %d", auraId, -1 * stacks, newAura.stacks, spellId)
- if newAura.stacks <= 0 then
- Ovale:Logf("Aura %d is completely removed", auraId)
- newAura.stacks = 0
- newAura.ending = endCast
- end
- end
- elseif type(stacks) == "number" and type(duration) == "number" and stacks > 0 and duration > 0 then
- Ovale:Logf("New aura %d at %f on %s", auraId, endCast, guid)
- newAura.stacks = stacks
- newAura.start = endCast
- newAura.ending = endCast + duration
- if isDoT then
- newAura.tick = OvaleAura:GetTickLength(auraId)
- -- Snapshot stats for the DoT.
- -- XXX This is not quite right because it uses the current player stats instead of the simulator's state.
- OvalePaperDoll:SnapshotStats(newAura, spellcast)
- newAura.damageMultiplier = self:GetDamageMultiplier(auraId)
- end
- end
+ AddRune(startCast, 4, si.death)
end
end
end
@@ -634,147 +445,27 @@ function OvaleState:GetComputedSpellCD(spellId)
end
function OvaleState:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
- local aura
- if mine then
- local auraTable = self.aura[guid]
- if auraTable then
- if filter then
- local auraList = auraTable[filter]
- if auraList then
- if auraList[spellId] and auraList[spellId].serial == self.serial then
- aura = auraList[spellId]
- end
- end
- else
- for auraFilter, auraList in pairs(auraTable) do
- if auraList[spellId] and auraList[spellId].serial == self.serial then
- aura = auraList[spellId]
- filter = auraFilter
- break
- end
- end
- end
- end
- end
- if aura then
- if aura.stacks > 0 then
- Ovale:Logf("Found %s aura %s on %s", filter, spellId, guid)
- else
- Ovale:Logf("Found %s aura %s on %s (removed)", filter, spellId, guid)
- end
- if auraFound then
- for k, v in pairs(aura) do
- auraFound[k] = v
- end
- end
- return aura.start, aura.ending, aura.stacks, aura.gain
- else
- Ovale:Logf("Aura %s not found in state for %s", spellId, guid)
- return OvaleAura:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
- end
+ return self.state:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
end
-do
- local aura = {}
- local newAura = {}
-
- function OvaleState:GetAura(unitId, spellId, filter, mine, auraFound)
- local guid = OvaleGUID:GetGUID(unitId)
- if OvaleData.buffSpellList[spellId] then
- if auraFound then wipe(newAura) end
- local newStart, newEnding, newStacks, newGain
- for auraId in pairs(OvaleData.buffSpellList[spellId]) do
- if auraFound then wipe(aura) end
- local start, ending, stacks, gain = self:GetAuraByGUID(guid, auraId, filter, mine, unitId, aura)
- if start and (not newStart or stacks > newStacks) then
- newStart = start
- newEnding = ending
- newStacks = stacks
- newGain = gain
- if auraFound then
- wipe(newAura)
- for k, v in pairs(aura) do
- newAura[k] = v
- end
- end
- end
- end
- if auraFound then
- for k, v in pairs(newAura) do
- auraFound[k] = v
- end
- end
- return newStart, newEnding, newStacks, newGain
- else
- return self:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
- end
- end
+function OvaleState:GetAura(unitId, spellId, filter, mine, auraFound)
+ return self.state:GetAura(unitId, spellId, filter, mine, auraFound)
end
--- Look for an aura on any target, excluding the given GUID.
--- Returns the earliest start time, the latest ending time, and the number of auras seen.
function OvaleState:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
- local start, ending, count = OvaleAura:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
- -- TODO: This is broken because it doesn't properly account for removed auras in the current frame.
- for guid, auraTable in pairs(self.aura) do
- if guid ~= excludingGUID then
- for auraFilter, auraList in pairs(auraTable) do
- if not filter or auraFilter == filter then
- local aura = auraList[spellId]
- if aura and aura.serial == self.serial then
- if aura.start and (not start or aura.start < start) then
- start = aura.start
- end
- if aura.ending and (not ending or aura.ending > ending) then
- ending = aura.ending
- end
- count = count + 1
- end
- end
- end
- end
- end
- return start, ending, count
+ return self.state:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
end
function OvaleState:NewAura(guid, spellId, filter)
- if not self.aura[guid] then
- self.aura[guid] = {}
- end
- if not self.aura[guid][filter] then
- self.aura[guid][filter] = {}
- end
- if not self.aura[guid][filter][spellId] then
- self.aura[guid][filter][spellId] = {}
- end
- local aura = self.aura[guid][filter][spellId]
- aura.serial = self.serial
- aura.mine = true
- aura.gain = self.currentTime
- return aura
+ return self.state:NewAura(guid, spellId, filter)
end
function OvaleState:GetDamageMultiplier(spellId)
- local damageMultiplier = 1
- if spellId then
- local si = OvaleData.spellInfo[spellId]
- if si and si.damageAura then
- local playerGUID = OvaleGUID:GetGUID("player")
- for filter, auraList in pairs(si.damageAura) do
- for auraSpellId, multiplier in pairs(auraList) do
- local count = select(3, self:GetAuraByGUID(playerGUID, auraSpellId, filter, nil, "player"))
- if count and count > 0 then
- local auraSpellInfo = OvaleData.spellInfo[auraSpellId]
- if auraSpellInfo.stacking and auraSpellInfo.stacking > 0 then
- multiplier = 1 + (multiplier - 1) * count
- end
- damageMultiplier = damageMultiplier * multiplier
- end
- end
- end
- end
- end
- return damageMultiplier
+ return self.state:GetDamageMultiplier(spellId)
+end
+
+function OvaleState:GetDuration(auraSpellId)
+ return self.state:GetDuration(auraSpellId)
end
-- Returns 1 if moving toward Solar or -1 if moving toward Lunar.
@@ -861,45 +552,42 @@ function OvaleState:GetRunesCooldown(blood, frost, unholy, death, nodeath)
return maxCD
end
--- Returns the duration, tick length, and number of ticks of an aura.
-function OvaleState:GetDuration(auraSpellId)
- local si
- if type(auraSpellId) == "number" then
- si = OvaleData.spellInfo[auraSpellId]
- elseif OvaleData.buffSpellList[auraSpellId] then
- for spellId in pairs(OvaleData.buffSpellList[auraSpellId]) do
- si = OvaleData.spellInfo[spellId]
- if si then
- auraSpellId = spellId
- break
- end
- end
- end
- if si and si.duration then
- local duration = si.duration
- local combo = self.state.combo or 0
- local holy = self.state.holy or 1
- if si.adddurationcp then
- duration = duration + si.adddurationcp * combo
- end
- if si.adddurationholy then
- duration = duration + si.adddurationholy * (holy - 1)
- end
- if si.tick then -- DoT
- --DoT duration is tick * numTicks.
- local tick = OvaleAura:GetTickLength(auraSpellId)
- local numTicks = floor(duration / tick + 0.5)
- duration = tick * numTicks
- return duration, tick, numTicks
- end
- return duration
- end
+--[[------------------------------
+ Legacy methods for transition.
+--]]------------------------------
+function OvaleState:GetCounterValue(id)
+ return self.state:GetCounterValue(id)
end
--- Print out the levels of each power type in the current state.
-function OvaleState:DebugPower()
- for powerType in pairs(OvalePower.POWER_INFO) do
- Ovale:FormatPrint("%s = %d", powerType, self.state[powerType])
- end
+function OvaleState:GetCD(spellId)
+ return self.state:GetCD(spellId)
+end
+
+function OvaleState:GetComputedSpellCD(spellId)
+ return self.state:GetSpellCooldown(spellId)
+end
+
+function OvaleState:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
+ return self.state:GetAuraByGUID(guid, spellId, filter, mine, unitId, auraFound)
+end
+
+function OvaleState:GetAura(unitId, spellId, filter, mine, auraFound)
+ return self.state:GetAura(unitId, spellId, filter, mine, auraFound)
+end
+
+function OvaleState:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
+ return self.state:GetAuraOnAnyTarget(spellId, filter, mine, excludingGUID)
+end
+
+function OvaleState:NewAura(guid, spellId, filter)
+ return self.state:NewAura(guid, spellId, filter)
+end
+
+function OvaleState:GetDamageMultiplier(spellId)
+ return self.state:GetDamageMultiplier(spellId)
+end
+
+function OvaleState:GetDuration(auraSpellId)
+ return self.state:GetDuration(auraSpellId)
end
--</public-static-methods>
diff --git a/compiler.pl b/compiler.pl
index 2399aee..6eb1c42 100644
--- a/compiler.pl
+++ b/compiler.pl
@@ -101,6 +101,7 @@ $sp{OvaleCondition}{ParseCondition} = true;
$sp{OvaleCondition}{ParseRuneCondition} = true;
$sp{OvaleCondition}{TestValue} = true;
+$m{OvaleQueue}{NewQueue} = true;
$sp{OvaleQueue}{Front} = true;
$sp{OvaleQueue}{FrontToBackIterator} = true;
$sp{OvaleQueue}{InsertBack} = true;