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.
335 lines
13 KiB
335 lines
13 KiB
--========================================================--
|
|
-- Scorpio UnitFrame Cast API --
|
|
-- --
|
|
-- Author : kurapica125@outlook.com --
|
|
-- Create Date : 2021/03/06 --
|
|
--========================================================--
|
|
|
|
--========================================================--
|
|
Scorpio "Scorpio.Secure.UnitFrame.HealthAPI" "1.0.0"
|
|
--========================================================--
|
|
|
|
namespace "Scorpio.Secure.UnitFrame"
|
|
|
|
import "System.Reactive"
|
|
import "System.Toolset"
|
|
|
|
------------------------------------------------------------
|
|
-- UNIT HEALTH --
|
|
------------------------------------------------------------
|
|
local _PlayerClass = select(2, UnitClass("player"))
|
|
local _DISPELLABLE = ({
|
|
["MAGE"] = { Curse = Color.CURSE, },
|
|
["DRUID"] = { Poison = Color.POISON, Curse = Color.CURSE, Magic = Color.MAGIC },
|
|
["PALADIN"] = { Poison = Color.POISON, Disease = Color.DISEASE, Magic = Color.MAGIC },
|
|
["PRIEST"] = { Disease = Color.DISEASE,Magic = Color.MAGIC },
|
|
["SHAMAN"] = { Curse = Color.CURSE, Magic = Color.MAGIC },
|
|
["WARLOCK"] = { Magic = Color.MAGIC, },
|
|
["MONK"] = { Poison = Color.POISON, Disease = Color.DISEASE, Magic = Color.MAGIC },
|
|
})[_PlayerClass] or false
|
|
|
|
local _UnitGUIDMap = {}
|
|
local _UnitHealthMap = {}
|
|
|
|
local _UnitHealthSubject = Subject()
|
|
local _UnitMaxHealthSubject = Subject()
|
|
|
|
function RegisterFrequentHealthUnit(unit, guid, health)
|
|
local oguid = _UnitGUIDMap[unit]
|
|
if oguid == guid then return end
|
|
|
|
_UnitGUIDMap[unit] = guid
|
|
if guid then
|
|
_UnitHealthMap[guid] = health
|
|
_UnitGUIDMap[guid] = (_UnitGUIDMap[guid] or 0) + 1
|
|
end
|
|
|
|
if oguid and _UnitGUIDMap[oguid] then
|
|
_UnitGUIDMap[oguid] = _UnitGUIDMap[oguid] - 1
|
|
|
|
if _UnitGUIDMap[oguid] <= 0 then
|
|
_UnitGUIDMap[oguid] = nil
|
|
_UnitHealthMap[oguid] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
__CombatEvent__ "SWING_DAMAGE" "RANGE_DAMAGE" "SPELL_DAMAGE" "SPELL_PERIODIC_DAMAGE" "DAMAGE_SPLIT" "DAMAGE_SHIELD" "ENVIRONMENTAL_DAMAGE" "SPELL_HEAL" "SPELL_PERIODIC_HEAL"
|
|
function COMBAT_HEALTH_CHANGE(_, event, _, _, _, _, _, destGUID, _, _, _, arg12, arg13, arg14, arg15, arg16)
|
|
local health = _UnitHealthMap[destGUID]
|
|
if not health then return end
|
|
|
|
local change = 0
|
|
|
|
if event == "SWING_DAMAGE" then
|
|
-- amount : arg12
|
|
-- overkill : arg13
|
|
change = (arg13 > 0 and arg13 or 0) - arg12
|
|
elseif event == "RANGE_DAMAGE" or event == "SPELL_DAMAGE" or event == "SPELL_PERIODIC_DAMAGE" or event == "DAMAGE_SPLIT" or event == "DAMAGE_SHIELD" then
|
|
-- amount : arg15
|
|
-- overkill : arg16
|
|
change = (arg16 > 0 and arg16 or 0) - arg15
|
|
elseif event == "ENVIRONMENTAL_DAMAGE" then
|
|
-- amount : arg13
|
|
-- overkill : arg14
|
|
change = (arg14 > 0 and arg14 or 0) - arg13
|
|
elseif event == "SPELL_HEAL" or event == "SPELL_PERIODIC_HEAL" then
|
|
-- amount : arg15
|
|
-- overhealing : arg16
|
|
change = arg15 - arg16
|
|
end
|
|
|
|
if change == 0 then return end
|
|
|
|
health = health + change
|
|
if change < 0 then
|
|
if health < 0 then health = 0 end
|
|
elseif change > 0 then
|
|
local unit = GetUnitFromGUID(destGUID)
|
|
if not unit then
|
|
_UnitGUIDMap[destGUID] = nil
|
|
_UnitHealthMap[destGUID]= nil
|
|
return
|
|
end
|
|
|
|
local max = UnitHealthMax(unit)
|
|
if health > max then health = max end
|
|
end
|
|
_UnitHealthMap[destGUID] = health
|
|
|
|
-- Distribute the new health
|
|
for unit in GetUnitsFromGUID(destGUID) do
|
|
_UnitHealthSubject:OnNext(unit)
|
|
end
|
|
end
|
|
|
|
__SystemEvent__()
|
|
function PLAYER_ENTERING_WORLD()
|
|
-- Clear the Registered Units
|
|
wipe(_UnitGUIDMap)
|
|
wipe(_UnitHealthMap)
|
|
end
|
|
|
|
__SystemEvent__(Scorpio.IsRetail and "UNIT_HEALTH" or "UNIT_HEALTH_FREQUENT")
|
|
function UNIT_HEALTH(unit)
|
|
local guid = UnitGUID(unit)
|
|
if _UnitHealthMap[guid] then
|
|
_UnitHealthMap[guid] = UnitHealth(unit)
|
|
end
|
|
|
|
_UnitHealthSubject:OnNext(unit)
|
|
end
|
|
|
|
__SystemEvent__()
|
|
function UNIT_MAXHEALTH(unit)
|
|
_UnitMaxHealthSubject:OnNext(unit)
|
|
_UnitHealthSubject:OnNext(unit)
|
|
end
|
|
|
|
__Static__() __AutoCache__()
|
|
function Wow.UnitHealth()
|
|
-- Use the Next for a tiny delay after the UnitHealthMax
|
|
return Wow.FromUnitEvent(_UnitHealthSubject):Next():Map(UnitHealth)
|
|
end
|
|
|
|
__Static__() __AutoCache__()
|
|
function Wow.UnitHealthFrequent()
|
|
-- Based on the CLEU
|
|
return Wow.FromUnitEvent(_UnitHealthSubject):Next():Map(function(unit)
|
|
local guid = UnitGUID(unit)
|
|
local health = _UnitHealthMap[guid]
|
|
if health and _UnitGUIDMap[unit] == guid then return health end
|
|
|
|
-- Register the unit
|
|
health = health or UnitHealth(unit)
|
|
RegisterFrequentHealthUnit(unit, guid, health)
|
|
return health
|
|
end)
|
|
end
|
|
|
|
__Static__() __AutoCache__()
|
|
function Wow.UnitHealthPercent()
|
|
return Wow.FromUnitEvent(_UnitHealthSubject):Next():Map(function(unit)
|
|
local health = UnitHealth(unit)
|
|
local max = UnitHealthMax(unit)
|
|
|
|
return floor(0.5 + (health and max and health / max * 100) or 0)
|
|
end)
|
|
end
|
|
|
|
__Static__() __AutoCache__()
|
|
function Wow.UnitHealthPercentFrequent()
|
|
-- Based on the CLEU
|
|
return Wow.FromUnitEvent(_UnitHealthSubject):Next():Map(function(unit)
|
|
local guid = UnitGUID(unit)
|
|
local health = _UnitHealthMap[guid]
|
|
|
|
if not (health and _UnitGUIDMap[unit] == guid) then
|
|
-- Register the unit
|
|
health = health or UnitHealth(unit)
|
|
RegisterFrequentHealthUnit(unit, guid, health)
|
|
end
|
|
|
|
local max = UnitHealthMax(unit)
|
|
return floor(0.5 + (health and max and health / max * 100) or 0)
|
|
end)
|
|
end
|
|
|
|
__Static__() __AutoCache__()
|
|
function Wow.UnitHealthMax()
|
|
local minMax = { min = 0 }
|
|
return Wow.FromUnitEvent(_UnitMaxHealthSubject):Map(function(unit)
|
|
minMax.max = UnitHealthMax(unit) or 100
|
|
return minMax
|
|
end)
|
|
end
|
|
|
|
__Static__() __AutoCache__() -- Too complex to do it here, leave it to the indicators or map chains
|
|
function Wow.UnitHealPrediction()
|
|
return Wow.FromUnitEvent("UNIT_HEALTH", "UNIT_MAXHEALTH", "UNIT_HEAL_PREDICTION", "UNIT_ABSORB_AMOUNT_CHANGED", "UNIT_HEAL_ABSORB_AMOUNT_CHANGED"):Next()
|
|
end
|
|
|
|
__Arguments__{ (ColorType + Boolean)/nil, ColorType/nil }
|
|
__Static__()
|
|
function Wow.UnitConditionColor(useClassColor, smoothEndColor)
|
|
local defaultColor = type(useClassColor) == "table" and useClassColor or Color.GREEN
|
|
useClassColor = useClassColor == true
|
|
|
|
if smoothEndColor then
|
|
local cache = Color{ r = 1, g = 1, b = 1 }
|
|
local br, bg, bb = smoothEndColor.r, smoothEndColor.g, smoothEndColor.b
|
|
|
|
if _DISPELLABLE then
|
|
return Wow.FromUnitEvent("UNIT_HEALTH", "UNIT_MAXHEALTH", "UNIT_AURA"):Next():Map(function(unit)
|
|
local index = 1
|
|
repeat
|
|
local n, _, _, d = UnitAura(unit, index, "HARMFUL")
|
|
local color = _DISPELLABLE[d]
|
|
if color then return color end
|
|
index = index + 1
|
|
until not n
|
|
|
|
local health = UnitHealth(unit)
|
|
local maxHealth = UnitHealthMax(unit)
|
|
local pct = health / maxHealth
|
|
local dcolor = defaultColor
|
|
|
|
if useClassColor then
|
|
local _, cls= UnitClass(unit)
|
|
if cls then dcolor = Color[cls] end
|
|
end
|
|
|
|
cache.r = br + (dcolor.r - br) * pct
|
|
cache.g = bg + (dcolor.g - bg) * pct
|
|
cache.b = bb + (dcolor.b - bb) * pct
|
|
|
|
return cache
|
|
end)
|
|
else
|
|
return Wow.FromUnitEvent(_UnitHealthSubject):Next():Map(function(unit)
|
|
local health = _UnitHealthMap[unit] or UnitHealth(unit)
|
|
local maxHealth = UnitHealthMax(unit)
|
|
local pct = health / maxHealth
|
|
local dcolor = defaultColor
|
|
|
|
if useClassColor then
|
|
local _, cls= UnitClass(unit)
|
|
if cls then dcolor = Color[cls] end
|
|
end
|
|
|
|
cache.r = br + (dcolor.r - br) * pct
|
|
cache.g = bg + (dcolor.g - bg) * pct
|
|
cache.b = bb + (dcolor.b - bb) * pct
|
|
|
|
return cache
|
|
end)
|
|
end
|
|
else
|
|
if _DISPELLABLE then
|
|
return Wow.FromUnitEvent("UNIT_AURA"):Next():Map(function(unit)
|
|
local index = 1
|
|
repeat
|
|
local n, _, _, d = UnitAura(unit, index, "HARMFUL")
|
|
local color = _DISPELLABLE[d]
|
|
if color then return color end
|
|
index = index + 1
|
|
until not n
|
|
|
|
local dcolor = defaultColor
|
|
|
|
if useClassColor then
|
|
local _, cls= UnitClass(unit)
|
|
if cls then dcolor = Color[cls] end
|
|
end
|
|
return dcolor
|
|
end)
|
|
else
|
|
return Wow.FromUnitEvent():Map(function(unit)
|
|
local dcolor = defaultColor
|
|
|
|
if useClassColor then
|
|
local _, cls= UnitClass(unit)
|
|
if cls then dcolor = Color[cls] end
|
|
end
|
|
return dcolor
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
------------------------------------------------------------
|
|
-- Wow Classic --
|
|
------------------------------------------------------------
|
|
if Scorpio.IsRetail then return end
|
|
|
|
_Parent.UnitGetIncomingHeals = _G.UnitGetIncomingHeals or Toolset.fakefunc
|
|
_Parent.UnitGetTotalAbsorbs = _G.UnitGetTotalAbsorbs or Toolset.fakefunc
|
|
_Parent.UnitGetTotalHealAbsorbs = _G.UnitGetTotalHealAbsorbs or Toolset.fakefunc
|
|
|
|
--- Try Get LibHealComm
|
|
pcall(LoadAddOn, "LibHealComm-4.0")
|
|
|
|
local ok, LibHealComm = pcall(_G.LibStub, "LibHealComm-4.0")
|
|
if not (ok and LibHealComm) then return end
|
|
|
|
local function HealComm_HealUpdated(event, _, _, _, _, ...)
|
|
for i = 1, select("#", ...) do
|
|
local guid = select(i, ...)
|
|
|
|
if guid then
|
|
for unit in GetUnitsFromGUID(guid) do
|
|
FireSystemEvent("UNIT_HEAL_PREDICTION", unit)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function HealComm_HealModified(event, guid)
|
|
for unit in GetUnitsFromGUID(guid) do
|
|
FireSystemEvent("UNIT_HEAL_PREDICTION", unit)
|
|
end
|
|
end
|
|
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_HealStarted", HealComm_HealUpdated)
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_HealStopped", HealComm_HealUpdated)
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_HealDelayed", HealComm_HealUpdated)
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_HealUpdated", HealComm_HealUpdated)
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_ModifierChanged", HealComm_HealModified)
|
|
LibHealComm.RegisterCallback("Scorpio", "HealComm_GUIDDisappeared", HealComm_HealModified)
|
|
|
|
INCOMING_HEAL_WINDOW = 4
|
|
|
|
_Parent.UnitGetIncomingHeals = _G.UnitGetIncomingHeals or function(unit, casterUnit)
|
|
local guid = UnitGUID(unit)
|
|
local incoming = 0
|
|
|
|
if casterUnit then
|
|
local casterGUID = UnitGUID(casterUnit)
|
|
|
|
incoming = (LibHealComm:GetHealAmount(guid, LibHealComm.ALL_HEALS, GetTime() + INCOMING_HEAL_WINDOW, casterGUID) or 0) * (LibHealComm:GetHealModifier(guid) or 1)
|
|
else
|
|
incoming = (LibHealComm:GetHealAmount(guid, LibHealComm.ALL_HEALS, GetTime() + INCOMING_HEAL_WINDOW) or 0) * (LibHealComm:GetHealModifier(guid) or 1)
|
|
end
|
|
|
|
return incoming
|
|
end
|
|
|