Restore Real Health Numbers as a standalone module with a lazy Vanilla health estimator fallback, keep Big Health presentation-only, clarify the module tooltip, and fix aura dragging for BuffButton0/BuffButton32/TempEnchant1 without StartMoving().
280 lines
7.9 KiB
Lua
280 lines
7.9 KiB
Lua
local _G = ShaguTweaks.GetGlobalEnv()
|
|
local T = ShaguTweaks.T
|
|
local Abbreviate = ShaguTweaks.Abbreviate
|
|
local hooksecurefunc = hooksecurefunc or ShaguTweaks.hooksecurefunc
|
|
|
|
local module = ShaguTweaks:register({
|
|
title = T["Real Health Numbers"],
|
|
description = T["Shows health and power numbers on player, pet and target unit frames. On Vanilla servers, target health may be estimated when exact values are unavailable."],
|
|
expansions = { ["vanilla"] = true },
|
|
category = T["Unit Frames"],
|
|
enabled = nil,
|
|
})
|
|
|
|
module.enable = function(self)
|
|
if ShaguTweaks.RealHealthNumbersInstalled then return end
|
|
ShaguTweaks.RealHealthNumbersInstalled = true
|
|
|
|
if ShaguTweaks.libhealth and ShaguTweaks.libhealth.Enable then
|
|
ShaguTweaks.libhealth:Enable()
|
|
end
|
|
|
|
local function HideNativeText(text)
|
|
if text and text.SetAlpha then
|
|
text:SetAlpha(0)
|
|
end
|
|
end
|
|
|
|
local nativeTexts = {
|
|
PlayerFrameHealthBar and PlayerFrameHealthBar.TextString,
|
|
PlayerFrameManaBar and PlayerFrameManaBar.TextString,
|
|
TargetFrameHealthBar and TargetFrameHealthBar.TextString,
|
|
TargetFrameManaBar and TargetFrameManaBar.TextString,
|
|
PetFrameHealthBar and PetFrameHealthBar.TextString,
|
|
PetFrameManaBar and PetFrameManaBar.TextString,
|
|
_G.PlayerFrameAlternatePowerBarText,
|
|
_G.TargetHPText,
|
|
_G.TargetHPPercText,
|
|
}
|
|
|
|
for _, text in pairs(nativeTexts) do
|
|
HideNativeText(text)
|
|
end
|
|
|
|
self.valueTexts = self.valueTexts or {}
|
|
|
|
local function CreateValueText(parent, fontSize)
|
|
if not parent then return end
|
|
|
|
local text = parent:CreateFontString(nil, "OVERLAY", "GameFontWhite")
|
|
text:SetFont(STANDARD_TEXT_FONT, fontSize or 10, "OUTLINE")
|
|
text:SetHeight(32)
|
|
text:SetJustifyH("CENTER")
|
|
text:SetTextColor(1, 1, 1, 1)
|
|
return text
|
|
end
|
|
|
|
local function AnchorText(text, bar, y)
|
|
if not text or not bar then return end
|
|
|
|
text:ClearAllPoints()
|
|
text:SetPoint("CENTER", bar, "CENTER", 0, y or 0)
|
|
end
|
|
|
|
if not self.valueTexts.playerHealth then
|
|
self.valueTexts.playerHealth = CreateValueText(PlayerFrameHealthBar, 10)
|
|
self.valueTexts.playerMana = CreateValueText(PlayerFrameManaBar, 10)
|
|
self.valueTexts.targetHealth = CreateValueText(TargetFrameHealthBar, 10)
|
|
self.valueTexts.targetMana = CreateValueText(TargetFrameManaBar, 10)
|
|
|
|
if PetFrameHealthBar then
|
|
self.valueTexts.petHealth = CreateValueText(PetFrameHealthBar, 9)
|
|
end
|
|
|
|
if PetFrameManaBar then
|
|
self.valueTexts.petMana = CreateValueText(PetFrameManaBar, 9)
|
|
end
|
|
|
|
if _G.PlayerFrameAlternatePowerBar then
|
|
self.valueTexts.playerAlternatePower =
|
|
CreateValueText(_G.PlayerFrameAlternatePowerBar, 10)
|
|
end
|
|
end
|
|
|
|
local function PositionTexts()
|
|
local playerHealthY = PlayerFrameHealthBar:GetHeight() >= 20 and -7 or 0
|
|
local targetHealthY = TargetFrameHealthBar:GetHeight() >= 20 and -7 or 0
|
|
|
|
AnchorText(self.valueTexts.playerHealth, PlayerFrameHealthBar, playerHealthY)
|
|
AnchorText(self.valueTexts.playerMana, PlayerFrameManaBar, 0)
|
|
AnchorText(self.valueTexts.targetHealth, TargetFrameHealthBar, targetHealthY)
|
|
AnchorText(self.valueTexts.targetMana, TargetFrameManaBar, 0)
|
|
AnchorText(self.valueTexts.petHealth, PetFrameHealthBar, 0)
|
|
AnchorText(self.valueTexts.petMana, PetFrameManaBar, -2)
|
|
|
|
if _G.PlayerFrameAlternatePowerBar then
|
|
AnchorText(
|
|
self.valueTexts.playerAlternatePower,
|
|
_G.PlayerFrameAlternatePowerBar,
|
|
0
|
|
)
|
|
end
|
|
end
|
|
|
|
local function SetValue(text, value)
|
|
if not text then return end
|
|
|
|
if value == nil then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
text:SetText(tostring(Abbreviate(value)))
|
|
text:Show()
|
|
end
|
|
|
|
local function UpdateHealth(text, unit, useEstimator)
|
|
if not text then return end
|
|
|
|
if not UnitExists(unit) then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
if unit == "target" and (UnitIsDead("target") or UnitIsGhost("target")) then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
local cur = UnitHealth(unit)
|
|
local max = UnitHealthMax(unit)
|
|
local known = true
|
|
|
|
if useEstimator and ShaguTweaks.libhealth
|
|
and ShaguTweaks.libhealth.GetUnitHealth then
|
|
cur, max, known = ShaguTweaks.libhealth:GetUnitHealth(unit)
|
|
end
|
|
|
|
if not max or max <= 0 then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
if useEstimator and not known and max == 100 then
|
|
text:SetText(tostring(cur or 0) .. "%")
|
|
text:Show()
|
|
return
|
|
end
|
|
|
|
SetValue(text, cur or 0)
|
|
end
|
|
|
|
local function UpdatePower(text, unit)
|
|
if not text then return end
|
|
|
|
if not UnitExists(unit) then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
local max = UnitManaMax(unit)
|
|
if not max or max <= 0 then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
SetValue(text, UnitMana(unit) or 0)
|
|
end
|
|
|
|
local function UpdatePlayer()
|
|
UpdateHealth(self.valueTexts.playerHealth, "player", false)
|
|
UpdatePower(self.valueTexts.playerMana, "player")
|
|
end
|
|
|
|
local function UpdateTarget()
|
|
UpdateHealth(self.valueTexts.targetHealth, "target", true)
|
|
UpdatePower(self.valueTexts.targetMana, "target")
|
|
end
|
|
|
|
local function UpdatePet()
|
|
UpdateHealth(self.valueTexts.petHealth, "pet", false)
|
|
UpdatePower(self.valueTexts.petMana, "pet")
|
|
end
|
|
|
|
local function UpdateAlternatePower()
|
|
local bar = _G.PlayerFrameAlternatePowerBar
|
|
local text = self.valueTexts.playerAlternatePower
|
|
if not bar or not text then return end
|
|
|
|
if not bar:IsShown() then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
local _, max = bar:GetMinMaxValues()
|
|
if not max or max <= 0 then
|
|
text:SetText("")
|
|
text:Hide()
|
|
return
|
|
end
|
|
|
|
local value = bar:GetValue() or 0
|
|
SetValue(text, math.floor(value + 0.5))
|
|
end
|
|
|
|
self.valueEvents = self.valueEvents or CreateFrame("Frame")
|
|
local events = self.valueEvents
|
|
|
|
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
events:RegisterEvent("PLAYER_TARGET_CHANGED")
|
|
events:RegisterEvent("PET_UI_UPDATE")
|
|
events:RegisterEvent("UNIT_HEALTH")
|
|
events:RegisterEvent("UNIT_MAXHEALTH")
|
|
events:RegisterEvent("UNIT_MANA")
|
|
events:RegisterEvent("UNIT_MAXMANA")
|
|
events:RegisterEvent("UNIT_RAGE")
|
|
events:RegisterEvent("UNIT_MAXRAGE")
|
|
events:RegisterEvent("UNIT_ENERGY")
|
|
events:RegisterEvent("UNIT_MAXENERGY")
|
|
events:RegisterEvent("UNIT_FOCUS")
|
|
events:RegisterEvent("UNIT_MAXFOCUS")
|
|
events:RegisterEvent("UNIT_HAPPINESS")
|
|
events:RegisterEvent("UNIT_MAXHAPPINESS")
|
|
events:RegisterEvent("UNIT_DISPLAYPOWER")
|
|
|
|
events:SetScript("OnEvent", function()
|
|
if event == "PLAYER_ENTERING_WORLD" then
|
|
PositionTexts()
|
|
UpdatePlayer()
|
|
UpdateTarget()
|
|
UpdatePet()
|
|
UpdateAlternatePower()
|
|
elseif event == "PLAYER_TARGET_CHANGED" then
|
|
UpdateTarget()
|
|
elseif event == "PET_UI_UPDATE" then
|
|
UpdatePet()
|
|
elseif arg1 == "player" then
|
|
UpdatePlayer()
|
|
UpdateAlternatePower()
|
|
elseif arg1 == "target" then
|
|
UpdateTarget()
|
|
elseif arg1 == "pet" then
|
|
UpdatePet()
|
|
end
|
|
end)
|
|
|
|
local alternatePowerBar = _G.PlayerFrameAlternatePowerBar
|
|
if alternatePowerBar and self.valueTexts.playerAlternatePower then
|
|
hooksecurefunc(alternatePowerBar, "SetValue", UpdateAlternatePower)
|
|
hooksecurefunc(alternatePowerBar, "Show", UpdateAlternatePower)
|
|
hooksecurefunc(alternatePowerBar, "Hide", UpdateAlternatePower)
|
|
end
|
|
|
|
-- All enabled modules are initialized during VARIABLES_LOADED in an
|
|
-- unspecified table order. Reposition once on the next frame so this module
|
|
-- automatically fits either the stock bars or Unit Frame Big Health.
|
|
local deferred = CreateFrame("Frame")
|
|
deferred:SetScript("OnUpdate", function()
|
|
this:SetScript("OnUpdate", nil)
|
|
this:Hide()
|
|
|
|
PositionTexts()
|
|
|
|
for _, text in pairs(nativeTexts) do
|
|
HideNativeText(text)
|
|
end
|
|
|
|
UpdatePlayer()
|
|
UpdateTarget()
|
|
UpdatePet()
|
|
UpdateAlternatePower()
|
|
end)
|
|
end
|