114 lines
4.3 KiB
Lua
114 lines
4.3 KiB
Lua
local _G = ShaguTweaks.GetGlobalEnv()
|
|
local T = ShaguTweaks.T
|
|
local hooksecurefunc = ShaguTweaks.hooksecurefunc
|
|
local API = ShaguTweaks.API
|
|
|
|
local module = ShaguTweaks:register({
|
|
title = T["Range Color"],
|
|
description = T["Action buttons will be colored red when out of range."],
|
|
expansions = { ["vanilla"] = true },
|
|
category = T["Action Bar"],
|
|
enabled = nil,
|
|
})
|
|
|
|
local rangeTooltip
|
|
|
|
local function SpellRange(spellID)
|
|
if not spellID or not API or not API.IsSpellInRange then return end
|
|
|
|
local inRange = API.IsSpellInRange(spellID, "target")
|
|
if inRange ~= nil then
|
|
return inRange and 1 or 0
|
|
end
|
|
end
|
|
|
|
local function GetDisplayedActionSpell(action)
|
|
-- GetMacroSpell() only exposes the macro parser's cached primary spell.
|
|
-- For conditional macros (#showtooltip + [stealth]/[behind]/etc.), ask the
|
|
-- action tooltip which spell the client is displaying right now instead.
|
|
-- ClassicAPI's GameTooltip:GetSpell() then gives us the resolved spellID.
|
|
if not _G.GameTooltip or type(_G.GameTooltip.GetSpell) ~= "function" then return end
|
|
|
|
if not rangeTooltip then
|
|
rangeTooltip = CreateFrame("GameTooltip", "ShaguTweaksRangeTooltip", UIParent, "GameTooltipTemplate")
|
|
rangeTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
|
end
|
|
|
|
rangeTooltip:ClearLines()
|
|
rangeTooltip:SetAction(action)
|
|
local _, _, spellID = rangeTooltip:GetSpell()
|
|
rangeTooltip:Hide()
|
|
return spellID
|
|
end
|
|
|
|
local function GetActionRange(action)
|
|
-- ClassicAPI resolves the action to its real spell/item/macro descriptor.
|
|
if API and API.GetActionInfo then
|
|
local actionType, id = API.GetActionInfo(action)
|
|
|
|
if actionType == "spell" and id then
|
|
local result = SpellRange(id)
|
|
if result ~= nil then return result end
|
|
elseif actionType == "macro" and id then
|
|
-- Macro addons such as SuperCleveRoidMacros override
|
|
-- IsActionInRange() specifically so their own conditional parser
|
|
-- can choose the currently active spell ([behind], [stealth], etc.).
|
|
-- Let that authoritative macro-aware result win before trying
|
|
-- ClassicAPI's generic macro fallbacks.
|
|
local macroRange = IsActionInRange(action, "target")
|
|
if macroRange ~= nil then
|
|
return macroRange
|
|
end
|
|
|
|
-- Generic fallback for ordinary macros that are not handled by a
|
|
-- dedicated macro addon: try the currently displayed action spell.
|
|
local spellID = GetDisplayedActionSpell(action)
|
|
|
|
-- Macros without a resolvable dynamic tooltip keep the cheaper
|
|
-- primary-spell reader as a compatibility fallback.
|
|
if not spellID and API.GetMacroSpell then
|
|
local _, _, primarySpellID = API.GetMacroSpell(id)
|
|
spellID = primarySpellID
|
|
end
|
|
|
|
local result = SpellRange(spellID)
|
|
if result ~= nil then return result end
|
|
elseif actionType == "item" and id and API.IsItemInRange then
|
|
local inRange = API.IsItemInRange(id, "target")
|
|
if inRange ~= nil then
|
|
return inRange and 1 or 0
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Unresolved actions and older ClassicAPI versions retain the native
|
|
-- action-slot check as a compatibility fallback.
|
|
return IsActionInRange(action, "target")
|
|
end
|
|
|
|
module.enable = function(self)
|
|
hooksecurefunc("ActionButton_OnUpdate", function(elapsed)
|
|
if this.rangeTimer then
|
|
this.rangeTimer = this.rangeTimer - elapsed
|
|
if this.rangeTimer <= 0.2 then
|
|
local action = ActionButton_GetPagedID(this)
|
|
local inRange = GetActionRange(action)
|
|
local icon = _G[this:GetName() .. "Icon"]
|
|
|
|
if icon then
|
|
if inRange == 0 then
|
|
if not this.a then
|
|
this.r, this.g, this.b, this.a = 0.85, 0.18, 0.18, 1
|
|
end
|
|
icon:SetVertexColor(this.r, this.g, this.b, this.a)
|
|
elseif IsUsableAction(action) then
|
|
icon:SetVertexColor(1, 1, 1, 1)
|
|
end
|
|
end
|
|
|
|
this.rangeTimer = TOOLTIP_UPDATE_TIME
|
|
end
|
|
end
|
|
end, true)
|
|
end
|