do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local systemTickRate = 0.03125000
local systemTimer = CreateTimer()
local utilInstances = {}
local utilInstancesCount = 0 -- A counter to track number of data in the utilInstances array. This is faster than using #utilInstances.
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
local function TimerTick()
-- Loop over each system instance and manually tick them, once an instance is out of ticks we remove it from the instance array.
local index = 1
local newTick = 0 -- Cache the new tick calculation to reduce code repetition.
while index <= utilInstancesCount do
newTick = utilInstances[index].remainingTicks - 1 or 0
if newTick <= 0 then
utilInstances[index]:destroy() -- Tell the instance to perform its final logic.
utilInstances[index] = utilInstances[utilInstancesCount] -- Move the last element in the array to this index.
utilInstances[utilInstancesCount] = nil -- Set the last element in the array to nil (remove it).
utilInstancesCount = utilInstancesCount - 1
else -- If we didn't move any instances around in the array, we move on to the next by incrementing.
utilInstances[index].remainingTicks = newTick
index = index + 1
end
end
-- If there aren't any more instances we stop the system timer from needlessly ticking.
if utilInstancesCount <= 0 then PauseTimer(systemTimer) end
end
-- -------------------------------------------------------------------------- --
-- Timed Evasion --
-- -------------------------------------------------------------------------- --
do
TimedEvasion = {}
TimedEvasion.__index = TimedEvasion
function TimedEvasion:destroy()
if self.isMissChance then
UnitAddMissChance(self.unit, -self.amount)
else
UnitAddEvasionChance(self.unit, -self.amount)
end
end
function TimedEvasion.new(unit, amount, duration, isMissChance)
local newInstance = {
unit = unit,
amount = amount,
remainingTicks = math.ceil(duration / systemTickRate),
isMissChance = isMissChance or false
}
setmetatable(newInstance, TimedEvasion)
-- Initialize the instance's effects.
if newInstance.isMissChance then
UnitAddMissChance(unit, amount)
else
UnitAddEvasionChance(unit, amount)
end
-- Update our global util instance count.
utilInstancesCount = utilInstancesCount + 1
utilInstances[utilInstancesCount] = newInstance
-- If this is the first instance in this system then the timer shouldn't be running, so we start it.
if utilInstancesCount == 1 then
TimerStart(systemTimer, systemTickRate, true, TimerTick)
end
end
end
-- -------------------------------------------------------------------------- --
-- Timed Critical Strike --
-- -------------------------------------------------------------------------- --
do
TimedCritical = {}
TimedCritical.__index = TimedCritical
function TimedCritical:destroy()
UnitAddCriticalStrike(self.unit, -self.chance, -self.multiplier)
end
function TimedCritical.new(unit, chance, multiplier, duration)
local newInstance = {
unit = unit,
chance = chance,
multiplier = multiplier,
remainingTicks = math.ceil(duration / systemTickRate)
}
setmetatable(newInstance, TimedCritical)
-- Initialize the instance's effects.
UnitAddCriticalStrike(unit, chance, multiplier)
-- Update our global util instance count.
utilInstancesCount = utilInstancesCount + 1
utilInstances[utilInstancesCount] = newInstance
-- If this is the first instance in this system then the timer shouldn't be running, so we start it.
if utilInstancesCount == 1 then
TimerStart(systemTimer, systemTickRate, true, TimerTick)
end
end
end
-- -------------------------------------------------------------------------- --
-- Timed Spell Power --
-- -------------------------------------------------------------------------- --
do
TimedSpellPower = {}
TimedSpellPower.__index = TimedSpellPower
function TimedSpellPower:destroy()
if self.isFlat then
UnitAddSpellPowerFlat(self.unit, -self.amount)
else
UnitAddSpellPowerPercent(self.unit, -self.amount)
end
end
function TimedSpellPower.new(unit, amount, duration, isFlat)
local newInstance = {
unit = unit,
amount = amount,
remainingTicks = math.ceil(duration / systemTickRate),
isFlat = isFlat or false
}
setmetatable(newInstance, TimedSpellPower)
-- Initialize the instance's effects.
if newInstance.isFlat then
UnitAddSpellPowerFlat(unit, amount)
else
UnitAddSpellPowerPercent(unit, amount)
end
-- Update our global util instance count.
utilInstancesCount = utilInstancesCount + 1
utilInstances[utilInstancesCount] = newInstance
-- If this is the first instance in this system then the timer shouldn't be running, so we start it.
if utilInstancesCount == 1 then
TimerStart(systemTimer, systemTickRate, true, TimerTick)
end
end
end
-- -------------------------------------------------------------------------- --
-- Timed Life Steal --
-- -------------------------------------------------------------------------- --
do
TimedLifeSteal = {}
TimedLifeSteal.__index = TimedLifeSteal
function TimedLifeSteal:destroy()
UnitAddLifeSteal(self.unit, -self.amount)
end
function TimedLifeSteal.new(unit, amount, duration)
local newInstance = {
unit = unit,
amount = amount,
remainingTicks = math.ceil(duration / systemTickRate)
}
setmetatable(newInstance, TimedLifeSteal)
-- Initialize the instance's effects.
UnitAddLifeSteal(unit, amount)
-- Update our global util instance count.
utilInstancesCount = utilInstancesCount + 1
utilInstances[utilInstancesCount] = newInstance
-- If this is the first instance in this system then the timer shouldn't be running, so we start it.
if utilInstancesCount == 1 then
TimerStart(systemTimer, systemTickRate, true, TimerTick)
end
end
end
-- -------------------------------------------------------------------------- --
-- Timed Spell Vamp --
-- -------------------------------------------------------------------------- --
do
TimedSpellVamp = {}
TimedSpellVamp.__index = TimedSpellVamp
function TimedSpellVamp:destroy()
UnitAddSpellVamp(self.unit, -self.amount)
end
function TimedSpellVamp.new(unit, amount, duration)
local newInstance = {
unit = unit,
amount = amount,
remainingTicks = math.ceil(duration / systemTickRate)
}
setmetatable(newInstance, TimedSpellVamp)
-- Initialize the instance's effects.
UnitAddSpellVamp(unit, amount)
-- Update our global util instance count.
utilInstancesCount = utilInstancesCount + 1
utilInstances[utilInstancesCount] = newInstance
-- If this is the first instance in this system then the timer shouldn't be running, so we start it.
if utilInstancesCount == 1 then
TimerStart(systemTimer, systemTickRate, true, TimerTick)
end
end
end
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function UnitAddEvasionChanceTimed(unit, amount, duration)
TimedEvasion.new(unit, amount, duration, false)
end
function UnitAddMissChanceTimed(unit, amount, duration)
TimedEvasion.new(unit, amount, duration, true)
end
function UnitAddCriticalStrikeTimed(unit, chance, multiplier, duration)
TimedCritical.new(unit, chance, multiplier, duration)
end
function UnitAddCriticalChanceTimed(unit, chance, duration)
TimedCritical.new(unit, chance, 0, duration)
end
function UnitAddCriticalMultiplierTimed(unit, multiplier, duration)
TimedCritical.new(unit, 0, multiplier, duration)
end
function UnitAddSpellPowerFlatTimed(unit, amount, duration)
TimedSpellPower.new(unit, amount, duration, true)
end
function UnitAddSpellPowerPercentTimed(unit, amount, duration)
TimedSpellPower.new(unit, amount, duration, false)
end
function AbilitySpellDamage(unit, ability, field)
local i = GetUnitUserData(unit)
return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, GetUnitAbilityLevel(unit, ability) - 1) + (SpellPower.flat[i] or 0)) * (1 + (SpellPower.percent[i] or 0))))
end
function AbilitySpellDamageEx(real, unit)
local i = GetUnitUserData(unit)
return I2S(R2I((real + (SpellPower.flat[i] or 0)) * (1 + (SpellPower.percent[i] or 0))))
end
function UnitAddLifeStealTimed(unit, amount, duration)
TimedLifeSteal.new(unit, amount, duration)
end
function UnitAddSpellVampTimed(unit, amount, duration)
TimedSpellVamp.new(unit, amount, duration)
end
end