zAmmo low-ammo flash + usage tracker; zPet unhappy flash; split parent/child tooltips

This commit is contained in:
DuvelCorp
2026-10-08 08:10:17 +02:00
parent 69c6d44b8e
commit 14f33b35ca
5 changed files with 369 additions and 1 deletions
+12 -1
View File
@@ -797,7 +797,11 @@ function ZSpellButtonParent_OnEnter(frame)
ZSpellButton_SetChildrenExpanded(frame, true)
end
ZSpellButton_ResetFadeTimer(frame)
if frame.tooltip and frame.id then
local showParentTooltip = frame.parentTooltip
if showParentTooltip == nil then
showParentTooltip = frame.tooltip
end
if showParentTooltip and frame.id then
GameTooltip:SetOwner(frame, "ANCHOR_TOPLEFT")
local msg, rank
@@ -807,6 +811,9 @@ function ZSpellButtonParent_OnEnter(frame)
msg = msg.." ("..rank..")"
end
GameTooltip:SetText(msg, 1, 1, 1)
elseif frame.suppressItemTooltip then
GameTooltip:ClearLines()
GameTooltip:SetText(frame.ammoname or frame.ammolink or "Ammo", 1, 1, 1)
else
local showedItemTooltip = nil
if frame.ammobag and frame.ammoslot and type(GameTooltip.SetBagItem) == "function" then
@@ -838,6 +845,10 @@ function ZSpellButtonParent_OnEnter(frame)
end
end
if type(frame.extraTooltip) == "function" then
frame.extraTooltip(GameTooltip)
end
GameTooltip:AddLine("Alt+Drag To Move This Button")
GameTooltip:Show()
end
+240
View File
@@ -22,6 +22,8 @@ if not root["zButtonAmmo"] then
root["zButtonAmmo"]["children"]["hideonclick"] = 1
root["zButtonAmmo"]["showammoname"] = 1
root["zButtonAmmo"]["showtotalperc"] = 1
root["zButtonAmmo"]["lowflash"] = 1
root["zButtonAmmo"]["parenttooltip"] = 1
root["zButtonAmmo"]["lastEquipped"] = nil
end
@@ -521,6 +523,139 @@ local function zButtonAmmo_GetCountDivisor(category)
return 2000
end
-- Reset-based ammo usage tracker (reliable, retrospective — no prediction).
-- session (consumed total + per-name breakdown + resetAt) persists in saved;
-- the baseline tables are runtime only.
local zButtonAmmo_LastByName = nil
local zButtonAmmo_LastCategory = nil
local zButtonAmmo_SettleUntil = 0
local function zButtonAmmo_EnsureSession()
local saved = zButtonAmmo_GetSaved()
if type(saved["session"]) ~= "table" then
saved["session"] = {}
end
local session = saved["session"]
if tonumber(session["consumed"]) == nil then
session["consumed"] = 0
end
if type(session["byName"]) ~= "table" then
session["byName"] = {}
end
if tonumber(session["resetAt"]) == nil then
session["resetAt"] = (time and time()) or 0
end
return session
end
function zButtonAmmo_ResetSession()
local session = zButtonAmmo_EnsureSession()
session["consumed"] = 0
session["byName"] = {}
session["resetAt"] = (time and time()) or 0
zButtonAmmo_LastByName = nil
zButtonAmmo_LastCategory = nil
if MTH and MTH.Print then
MTH:Print("Ammo usage counter reset.")
end
end
local function zButtonAmmo_CurrentCountsByName(category)
local counts = {}
for name, info in pairs(zButtonAmmo_Cache) do
if info and info.type == category then
counts[name] = (tonumber(info.count) or 0)
end
end
return counts
end
local function zButtonAmmo_SampleConsumption()
local category = zButtonAmmo_GetEquippedAmmoCategory()
if not category then
zButtonAmmo_LastByName = nil
zButtonAmmo_LastCategory = nil
return
end
local current = zButtonAmmo_CurrentCountsByName(category)
local now = GetTime and GetTime() or 0
local last = zButtonAmmo_LastByName
local categoryChanged = (category ~= zButtonAmmo_LastCategory)
zButtonAmmo_LastCategory = category
-- Baseline-only during the post-load settle window, on weapon/category
-- swaps, and on the first sample — these are not real shots.
if now < (zButtonAmmo_SettleUntil or 0) or categoryChanged or last == nil then
zButtonAmmo_LastByName = current
return
end
local session = zButtonAmmo_EnsureSession()
for name, lastCount in pairs(last) do
local cur = current[name] or 0
if cur < lastCount then
local delta = lastCount - cur
session["consumed"] = (tonumber(session["consumed"]) or 0) + delta
session["byName"][name] = (tonumber(session["byName"][name]) or 0) + delta
end
end
zButtonAmmo_LastByName = current
end
local function zButtonAmmo_FormatElapsed(seconds)
if seconds < 0 then seconds = 0 end
local mins = math.floor(seconds / 60)
local hrs = math.floor(mins / 60)
if hrs > 0 then
return hrs .. "h " .. (mins - hrs * 60) .. "m"
elseif mins > 0 then
return mins .. "m"
end
return math.floor(seconds) .. "s"
end
function zButtonAmmo_AppendSessionReport(tooltip)
if not (tooltip and tooltip.AddLine) then
return
end
local category = zButtonAmmo_GetEquippedAmmoCategory()
if category then
local total = zButtonAmmo_GetTotalAmmoCount(category)
tooltip:AddLine("In bags: " .. total .. " " .. (category == "Bullet" and "bullets" or "arrows"), 0.8, 0.8, 0.8)
end
local session = zButtonAmmo_EnsureSession()
local used = tonumber(session["consumed"]) or 0
local elapsed = ((time and time()) or 0) - (tonumber(session["resetAt"]) or 0)
if elapsed < 0 then elapsed = 0 end
tooltip:AddLine("Used since reset: " .. used .. " (" .. zButtonAmmo_FormatElapsed(elapsed) .. ")", 1, 0.82, 0)
if used > 0 and elapsed >= 60 then
tooltip:AddLine(string.format("Avg: ~%.1f /min", used / (elapsed / 60)), 0.7, 0.7, 0.7)
end
local byName = session["byName"]
if type(byName) == "table" and used > 0 then
local rows = {}
for name, cnt in pairs(byName) do
if (tonumber(cnt) or 0) > 0 then
table.insert(rows, { name = name, count = tonumber(cnt) or 0 })
end
end
if table.getn(rows) > 0 then
table.sort(rows, function(a, b) return a.count > b.count end)
tooltip:AddLine(" ")
tooltip:AddLine("Breakdown:", 0.9, 0.9, 0.9)
for i = 1, table.getn(rows) do
local row = rows[i]
local frac = row.count / used
local right = row.count .. " " .. math.floor(frac * 100 + 0.5) .. "%"
if type(tooltip.AddDoubleLine) == "function" then
tooltip:AddDoubleLine(row.name, right, 1, 1, 1, 0.6, 0.8, 1)
else
tooltip:AddLine(row.name .. ": " .. right, 1, 1, 1)
end
end
end
end
tooltip:AddLine("Left-click: reset counter", 0.5, 0.5, 0.5)
end
local function zButtonAmmo_UpdateTotalPercent(button)
local totalText = zButtonAmmo_EnsureTotalPercentText(button)
if not totalText then
@@ -610,6 +745,86 @@ local function zButtonAmmo_ShouldShowTotalCount()
return saved["counttotal"] and true or false
end
local function zButtonAmmo_ShouldLowFlash()
local saved = zButtonAmmo_GetSaved()
return saved["lowflash"] and true or false
end
-- Low-ammo threshold mirrors SmartAmmo's when available.
local function zButtonAmmo_GetLowThreshold()
if type(MTHSmartAmmo_GetLowAmmoThreshold) == "function" then
local t = tonumber(MTHSmartAmmo_GetLowAmmoThreshold())
if t and t > 0 then
return t
end
end
return 200
end
local function zButtonAmmo_EnsureLowFlash(button)
if not button then
return nil
end
if button.ammoLowFlash then
return button.ammoLowFlash
end
local tex = button:CreateTexture(nil, "OVERLAY")
tex:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
tex:SetBlendMode("ADD")
tex:SetVertexColor(1, 0.12, 0.12)
tex:SetPoint("CENTER", button, "CENTER", 0, 0)
tex:SetWidth((button:GetWidth() or 36) * 1.6)
tex:SetHeight((button:GetHeight() or 36) * 1.6)
tex:Hide()
button.ammoLowFlash = tex
return tex
end
local MTH_ZH_AmmoLowFlashDriver
local zButtonAmmo_LowFlashElapsed = 0
local function zButtonAmmo_LowFlashOnUpdate()
if not (zButtonAmmo and zButtonAmmo.ammoLowFlash and zButtonAmmo.ammoLowFlash:IsShown()) then
return
end
zButtonAmmo_LowFlashElapsed = zButtonAmmo_LowFlashElapsed + (arg1 or 0)
zButtonAmmo.ammoLowFlash:SetAlpha(0.45 + 0.4 * math.sin(zButtonAmmo_LowFlashElapsed * 4))
end
local function zButtonAmmo_SetLowFlashRunning(running)
if running and not MTH_ZH_AmmoLowFlashDriver then
MTH_ZH_AmmoLowFlashDriver = CreateFrame("Frame", "MTH_ZH_AmmoLowFlashDriver")
end
if not MTH_ZH_AmmoLowFlashDriver then
return
end
MTH_ZH_AmmoLowFlashDriver:SetScript("OnUpdate", running and zButtonAmmo_LowFlashOnUpdate or nil)
end
local function zButtonAmmo_UpdateLowFlash(button)
local tex = zButtonAmmo_EnsureLowFlash(button)
if not tex then
return
end
local low = false
if zButtonAmmo_ShouldLowFlash() then
local category = zButtonAmmo_GetEquippedAmmoCategory()
if category then
local total = zButtonAmmo_GetTotalAmmoCount(category)
if total < zButtonAmmo_GetLowThreshold() then
low = true
end
end
end
if low then
tex:Show()
zButtonAmmo_SetLowFlashRunning(true)
else
tex:Hide()
zButtonAmmo_SetLowFlashRunning(false)
end
end
function zButtonAmmo_UpdateButton(button)
if not button then
button = this
@@ -632,6 +847,10 @@ function zButtonAmmo_UpdateButton(button)
if button == zButtonAmmo and button.ammoTotalGauge then
button.ammoTotalGauge:Hide()
end
if button == zButtonAmmo and button.ammoLowFlash then
button.ammoLowFlash:Hide()
zButtonAmmo_SetLowFlashRunning(false)
end
return
end
@@ -683,6 +902,7 @@ function zButtonAmmo_UpdateButton(button)
end
zButtonAmmo_UpdateTotalPercent(button)
zButtonAmmo_UpdateTotalGauge(button)
zButtonAmmo_UpdateLowFlash(button)
end
end
@@ -843,6 +1063,12 @@ local function zButtonAmmo_EnsureSpellOrder(ammoList)
if saved["showtotalperc"] == nil then
saved["showtotalperc"] = 1
end
if saved["lowflash"] == nil then
saved["lowflash"] = 1
end
if saved["parenttooltip"] == nil then
saved["parenttooltip"] = 1
end
if not saved["parent"] then
saved["parent"] = {}
end
@@ -887,6 +1113,7 @@ local function zButtonAmmo_ApplyRuntimeSettings()
end
local saved = zButtonAmmo_GetSaved()
zButtonAmmo.tooltip = saved["tooltip"] and true or false
zButtonAmmo.parentTooltip = saved["parenttooltip"] and true or false
zButtonAmmo.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false
zButtonAmmo.expandonhover = saved["children"] and saved["children"]["expandonhover"] and true or false
zButtonAmmo.fadetimer = saved["children"] and tonumber(saved["children"]["fadetimer"]) or 0
@@ -1013,6 +1240,16 @@ function zButtonAmmo_OnEvent()
end
zButtonAmmo.customSetButtons = zButtonAmmo_SetButtons
zButtonAmmo.customUpdateButton = zButtonAmmo_UpdateButton
zButtonAmmo.extraTooltip = zButtonAmmo_AppendSessionReport
zButtonAmmo.suppressItemTooltip = true
zButtonAmmo.beforeclick = function(btn)
if btn ~= zButtonAmmo then
return false
end
zButtonAmmo_ResetSession()
return true
end
zButtonAmmo_SettleUntil = (GetTime and GetTime() or 0) + 5
zButtonAmmo_CreateButtons()
MTH_ZH_AmmoAdjust = CreateFrame("Frame", "MTH_ZH_AmmoAdjust")
MTH_ZH_AmmoAdjust:RegisterEvent("UNIT_INVENTORY_CHANGED")
@@ -1137,6 +1374,8 @@ function zButtonAmmo_Reset()
saved["children"]["hideonclick"] = 1
saved["showammoname"] = 1
saved["showtotalperc"] = 1
saved["lowflash"] = 1
saved["parenttooltip"] = 1
saved["lastEquipped"] = nil
end
@@ -1215,6 +1454,7 @@ function MTH_ZH_AmmoAdjust_OnEvent()
local listChanged = (ZHunterMod_Ammo_Buttons ~= ammoList)
ZHunterMod_Ammo_Buttons = ammoList
zButtonAmmo_RebuildCache()
zButtonAmmo_SampleConsumption()
local cacheSignature = zButtonAmmo_GetCacheSignature(ammoList)
local rebuiltButtons = false
if listChanged or cacheSignature ~= zButtonAmmo_LastCacheSignature then
+66
View File
@@ -57,6 +57,68 @@ local function zButtonPet_GetSaved()
return currentRoot["zButtonPet"]
end
-- Persistent "feed me" nudge: pulse the zPet parent while the pet is Unhappy.
local MTH_ZH_PetHappyFlashDriver
local zButtonPet_HappyFlashElapsed = 0
local function zButtonPet_EnsureHappyFlash()
local button = getglobal("zButtonPet")
if not button then
return nil
end
if button.petHappyFlash then
return button.petHappyFlash
end
local tex = button:CreateTexture(nil, "OVERLAY")
tex:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
tex:SetBlendMode("ADD")
tex:SetVertexColor(1, 0.15, 0.15)
tex:SetPoint("CENTER", button, "CENTER", 0, 0)
tex:SetWidth((button:GetWidth() or 36) * 1.6)
tex:SetHeight((button:GetHeight() or 36) * 1.6)
tex:Hide()
button.petHappyFlash = tex
return tex
end
local function zButtonPet_HappyFlashOnUpdate()
local button = getglobal("zButtonPet")
if not (button and button.petHappyFlash and button.petHappyFlash:IsShown()) then
return
end
zButtonPet_HappyFlashElapsed = zButtonPet_HappyFlashElapsed + (arg1 or 0)
button.petHappyFlash:SetAlpha(0.4 + 0.4 * math.sin(zButtonPet_HappyFlashElapsed * 3))
end
local function zButtonPet_SetHappyFlashRunning(running)
if running and not MTH_ZH_PetHappyFlashDriver then
MTH_ZH_PetHappyFlashDriver = CreateFrame("Frame", "MTH_ZH_PetHappyFlashDriver")
end
if not MTH_ZH_PetHappyFlashDriver then
return
end
MTH_ZH_PetHappyFlashDriver:SetScript("OnUpdate", running and zButtonPet_HappyFlashOnUpdate or nil)
end
local function zButtonPet_UpdateHappyFlash(happiness)
local tex = zButtonPet_EnsureHappyFlash()
if not tex then
return
end
local saved = zButtonPet_GetSaved()
local enabled = saved["unhappyflash"]
if enabled == nil then
enabled = 1
end
if enabled and happiness ~= nil and tonumber(happiness) == 1 then
tex:Show()
zButtonPet_SetHappyFlashRunning(true)
else
tex:Hide()
zButtonPet_SetHappyFlashRunning(false)
end
end
local function zButtonPet_EnsureConfig()
local saved = zButtonPet_GetSaved()
saved["count"] = nil
@@ -131,6 +193,9 @@ local function zButtonPet_EnsureConfig()
saved["visible"][i] = 1
end
end
if saved["unhappyflash"] == nil then
saved["unhappyflash"] = 1
end
end
local function zButtonPet_ApplyRuntimeSettings()
@@ -347,6 +412,7 @@ local function zButtonPet_RefreshSavedPetState(saved, corePetInfo)
end
local prevHappiness = saved["pet"]["happiness"]
saved["pet"]["happiness"] = happiness
zButtonPet_UpdateHappyFlash(happiness)
-- Animations: flash the pet button when happiness or loyalty changes.
if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function" then