You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
8.3 KiB

4 years ago
--========================================================--
-- Scorpio UnitFrame Aura API --
-- --
-- Author : kurapica125@outlook.com --
-- Create Date : 2021/09/07 --
--========================================================--
--========================================================--
Scorpio "Scorpio.Secure.UnitFrame.AuraAPI" "1.0.0"
--========================================================--
namespace "Scorpio.Secure.UnitFrame"
import "System.Reactive"
import "System.Toolset"
__Final__() interface "UnitAuraPredicate" (function(_ENV)
local obUnitAura = Wow.FromUnitEvent("UNIT_AURA"):Next()
local scanResult = setmetatable({}, { __index = function(self, key) local ret = {} rawset(self, key, ret) return ret end })
local recycle = Recycle()
local singleSpellID = setmetatable({}, getmetatable(scanResult))
local singleSpellName = setmetatable({}, getmetatable(scanResult))
local auraFilter = { HELPFUL = 1, HARMFUL = 2, PLAYER = 3, RAID = 4, CANCELABLE = 5, NOT_CANCELABLE = 6, INCLUDE_NAME_PLATE_ONLY = 7, MAW = 8 }
local refreshAura, scanForUnit
if _G.UnitAuraSlots then
function refreshAura(cache, unit, filter, auraIdx, continuationToken, ...)
local singleSpellIDMap = singleSpellID[filter]
local singleSpellNameMap= singleSpellName[filter]
for i = 1, select("#", ...) do
local slot = select(i, ...)
local name, icon, count, dtype, duration, expires, caster, isStealable, nameplateShowPersonal, spellId = UnitAuraBySlot(unit, slot)
if singleSpellIDMap[spellId] then
cache[spellId] = auraIdx
end
if singleSpellNameMap[name] then
cache[name] = auraIdx
end
auraIdx = auraIdx + 1
end
return continuationToken and refreshAura(cache, unit, filter, auraIdx, UnitAuraSlots(unit, filter, 16, continuationToken))
end
function scanForUnit(cache, unit, filter)
return refreshAura(cache, unit, filter, 1, UnitAuraSlots(unit, filter, 16))
end
else
function scanForUnit(cache, unit, filter)
local singleSpellIDMap = singleSpellID[filter]
local singleSpellNameMap= singleSpellName[filter]
local auraIdx = 1
local name, icon, count, dtype, duration, expires, caster, isStealable, nameplateShowPersonal, spellId = UnitAura(unit, auraIdx, filter)
while name do
if singleSpellIDMap[spellId] then
cache[spellId] = auraIdx
end
if singleSpellNameMap[name] then
cache[name] = auraIdx
end
auraIdx = auraIdx + 1
name, icon, count, dtype, duration, expires, caster, isStealable, nameplateShowPersonal, spellId = UnitAura(unit, auraIdx, filter)
end
end
end
local function getUnitCache(unit, filter)
local guid = UnitGUID(unit)
local now = GetTime()
local fcache = scanResult[filter]
local cache = fcache[guid]
if not cache then
cache = recycle()
fcache[guid] = cache
cache[0] = now
scanForUnit(cache, unit, filter)
elseif cache[0] < now then
wipe(cache)
cache[0] = now
scanForUnit(cache, unit, filter)
end
return cache
end
local function passFilter(filter)
return XList(filter:gmatch("[%w_]+")):ToList():Sort(function(a, b) return auraFilter[a] < auraFilter[b] end):Join("|")
end
-- Clear Caches
Wow.FromEvent("PLAYER_REGEN_ENABLED"):Next():Subscribe(function()
for filter, fcache in pairs(scanResult) do
for guid, cache in pairs(fcache) do
recycle(wipe(cache))
end
wipe(fcache)
end
end)
-- Used for full scan
__Static__() __Arguments__{}
function PredicateUnitAura()
return obUnitAura
end
-- Predicate for one spell id
__Static__() __Arguments__{ String, Number } __Observable__()
function PredicateUnitAura(filter, id)
filter = passFilter(filter)
singleSpellID[filter][id] = true
return Operator(obUnitAura, function(observer, unit)
local cache = getUnitCache(unit, filter)
local auraID = cache[id]
return auraID and observer:OnNext(unit, filter, auraID)
end)
end
-- Predicate for one spell name
__Static__() __Arguments__{ String } __Observable__()
function PredicateUnitAura(filter, name)
filter = passFilter(filter)
singleSpellName[filter][name] = true
return Operator(obUnitAura, function(observer, unit)
local cache = getUnitCache(unit, filter)
local auraID = cache[name]
return auraID and observer:OnNext(unit, filter, auraID)
end)
end
__Arguments__{ String, struct { Number }}
__Static__() __Observable__()
function PredicateUnitAura(filter, ids)
filter = passFilter(filter)
local clone = recycle()
local len = #ids
for i = 1, len do
singleSpellID[filter][ids[i]] = true
clone[i] = ids[i]
end
return Operator(obUnitAura, function(observer, unit)
local cache = getUnitCache(unit, filter)
local auraIDs = recycle()
local index = 1
for i = 1, len do
local auraID = cache[clone[i]]
if auraID then
auraIDs[index] = auraID
index = index + 1
end
end
if index > 1 then
observer:OnNext(unit, filter, unpack(auraIDs))
end
recycle(wipe(auraIDs))
end)
end
__Arguments__{ String, struct { String }}
__Static__() __Observable__()
function PredicateUnitAura(filter, names)
filter = passFilter(filter)
local clone = recycle()
local len = #names
for i = 1, len do
singleSpellName[filter][names[i]] = true
clone[i] = names[i]
end
return Operator(obUnitAura, function(observer, unit)
local cache = getUnitCache(unit, filter)
local auraIDs = recycle()
local index = 1
for i = 1, len do
local auraID = cache[clone[i]]
if auraID then
auraIDs[index] = auraID
index = index + 1
end
end
if index > 1 then
observer:OnNext(unit, filter, unpack(auraIDs))
end
recycle(wipe(auraIDs))
end)
end
end)
__Static__()
Wow.UnitAura = UnitAuraPredicate.PredicateUnitAura
------------------------------------------------------------
-- Wow Classic --
------------------------------------------------------------
if Scorpio.IsRetail or Scorpio.IsBCC then return end
--- Try Get LibClassicDurations
pcall(LoadAddOn, "LibClassicDurations")
local ok, LibClassicDurations = pcall(_G.LibStub, "LibClassicDurations")
if not (ok and LibClassicDurations) then return end
LibClassicDurations:Register("Scorpio") -- tell library it's being used and should start working
_Parent.UnitAura = LibClassicDurations.UnitAuraWithBuffs
LibClassicDurations.RegisterCallback("Scorpio", "UNIT_BUFF", function(event, unit) return FireSystemEvent("UNIT_AURA", unit) end)