11 Commits
13 changed files with 784 additions and 13 deletions
+33
View File
@@ -3,6 +3,39 @@
All notable changes to MetaHunt will be documented in this file.
## [2.1.0] - 2026-10-08
### Added
- **Beast Lore — ability colouring**: In the Hunter's Book Beast Lore list, every beast's abilities are now coloured — **green** if your hunter already knows that exact rank, **red** if it's still unlearned — so you can spot at a glance which beasts can still teach you something new.
- **zTrap — trap cooldown**: The zTrap buttons now show the trap re-arm cooldown (the ~15s before you can lay another trap) as a cooldown spiral plus a seconds countdown, on both the parent and child buttons. A new "Show cooldown" toggle lives in Options → zButtons → zTrap (on by default).
- **zAmmo — total ammo meter**: The zAmmo parent button can now show how stocked you are on your equipped weapon's ammo, as a percentage of your quiver/pouch capacity (all matching arrows or bullets across your bags ÷ total quiver/pouch slots × 200). Two heat-coloured display styles (red when low → green when full) are available as toggles in Options → zButtons → zAmmo → Parent Button: a **centered %** on the button, and a **vertical gauge** beside it (which flips to the side opposite the children's expand direction). A third toggle, **"Counter shows total ammo"**, switches the parent's bottom counter from the equipped stack to your full bag total.
- **zAmmo — low-ammo flash**: The zAmmo parent pulses a red glow when your total ammo drops below your SmartAmmo warning threshold (default 200). Toggle in the Parent Button box (on by default).
- **zAmmo — ammo usage tracker**: Left-click the zAmmo parent to reset a usage counter, then hover it for a report of how much ammo you've burned since the reset — total, elapsed time, average per minute, and a per-type breakdown (how many Rough, Jagged, etc.) with percentages. Counts only ammo actually spent (restocking never corrupts it) and survives `/reload`. The parent tooltip now shows this report instead of the raw item tooltip, with a separate "Show parent tooltip" toggle; the Children "Show Tooltip" option now only affects the child buttons.
- **zPet — unhappy pet flash**: The zPet parent pulses a red glow while your pet is Unhappy, as a persistent "feed me" reminder (complements the existing flash-on-change). Toggle in the Parent Button box (on by default).
### Changed
- **zAmmo — capacity-based counter colouring**: The heat colour on the ammo stack counters now scales to your real quiver/pouch capacity instead of a fixed number, so a stack turns green as it approaches filling your quiver (falls back to the old scale when no quiver/pouch is equipped).
- **SmartAmmo — clearer out-of-ammo alert**: The centre-screen auto-fallback message now names the ammo that ran out (e.g. "Out of Rough Arrow!") instead of a generic "Out of ammo!", so you instantly know which stack emptied.
### Fixed
- **MM Widget — immune schools now show SKIP**: When the current target is immune to the active Explosive Ammunition school, the widget already skipped the special shot (casting Aimed/Steady instead), but the action cell still read "USE" — indistinguishable from a normal shot. It now shows a red **SKIP** with the skipped shot's icon so the skip is visible.
- **Feed-O-Matic — feeding food that's too low level**: With nearly-full bags, FeedOMatic could keep trying to feed a small stack whose level was too low for your pet, which the server refuses ("That food's level is not high enough"). It now skips below-minimum-level foods and only offers food the pet can actually eat.
- **Pets — taming & feeding history disappearing**: Fixed a long-standing issue where a pet's taming info and feeding history could vanish from the Book — caused by the server handing out non-unique pet GUIDs that split one pet into several empty duplicate records. The Stable scan now re-adopts the real record instead of an empty duplicate, so the data reappears on your next stable visit (no data rewrite needed).
- **Pet notifications on login**: Your pet's existing level and loyalty no longer trigger celebration pop-ups right after you log in or reload — a short grace period means only genuine in-session gains are announced.
## [2.0.0] - 2026-09-14
Resurrecting on Octowow.
+1 -1
View File
@@ -1,5 +1,5 @@
## Interface: 11200
## Version: 2.0.0
## Version: 2.1.0
## Title: MetaHunt - |cff00ff00Hunter
## Author: Metasploit and his Copilot ;)
## Notes: Unified addon suite for huntards making old addons compatible with Twow, and adding an arsenal of never seen Hunter's utilities.
+1 -1
View File
@@ -1,5 +1,5 @@
MTH_CONST = MTH_CONST or {}
MTH_CONST.version = MTH_CONST.version or "2.0.0"
MTH_CONST.version = MTH_CONST.version or "2.1.0"
MTH_CONST.WEAPON_TYPES = {
BOWS = "Bows",
+1 -1
View File
@@ -1,5 +1,5 @@
MTH = MTH or {
version = "2.0.0",
version = "2.1.0",
name = "MetaHunt",
modules = {},
config = {},
+1 -1
View File
@@ -250,7 +250,7 @@ local function MTH_LT_AnnounceTraining(kind, level, eventKind, preview)
local isMilestone = kind == "hunter" and (level == 10 or level == 20 or level == 30 or level == 40 or level == 50 or level == 60)
local flag = isMilestone and "level.huntermilestone" or (kind == "hunter" and "level.hunter" or "level.pet")
if type(MTH_FX_IsEnabled) == "function" and MTH_FX_IsEnabled(flag) and type(MTH_Celebrate) == "function" then
local subtitle = kind == "hunter" and (MTH_LT_HunterLines[level] or "New training is available.") or "New pet training is available. The trainer wants coin."
local subtitle = kind == "hunter" and (MTH_LT_HunterLines[level] or "New training is available.") or "Your beast grows stronger. Teach it something nasty."
if isMilestone and type(MTH_CelebrateBlackHole) == "function" then
MTH_CelebrateBlackHole(isLevelCap and headlineText or "Level " .. tostring(level), subtitle)
else
+1 -1
View File
@@ -754,7 +754,7 @@ function MTH_CelebrateAmmoFallback(previousAmmo, newAmmo)
if not MTH_FX_IsEnabled("combat.ammoout") or not Sauce or not MTH_FX_EnsureFrames() then return end
MTH_FX_TitleFS:SetTextColor(1, 0.15, 0.15)
MTH_FX_TitleFS:SetText("Out of ammo!")
MTH_FX_TitleFS:SetText("Out of " .. tostring(previousAmmo or "ammo") .. "!")
MTH_FX_SubFS:SetText("Equipped " .. tostring(newAmmo or "fallback ammunition"))
Sauce:Stop(MTH_FX_MsgFrame)
+10 -1
View File
@@ -3063,7 +3063,6 @@ local function MTH_BOOK_InitRankDropdown()
MTH_BOOK_UpdateRankDropdownText()
end
local function MTH_BOOK_InitPetLearnSourceDropdown()
local sourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown")
if not sourceDropdown then return end
@@ -4730,6 +4729,16 @@ MTH_BOOK_UpdateResults = function()
label = label .. ","
end
chip.text:SetText(label)
-- Colour by whether the hunter already knows this exact ability rank.
if MTH_BOOK_STATE.mode == "pets" and type(MTH_IsPetAbilityKnownByHunter) == "function" then
if MTH_IsPetAbilityKnownByHunter(ability.tooltipName or ability.name, ability.requestedRank) then
chip.text:SetTextColor(0.30, 1.00, 0.30)
else
chip.text:SetTextColor(1.00, 0.35, 0.35)
end
else
chip.text:SetTextColor(1, 1, 1)
end
local textWidth = chip.text:GetStringWidth() or 24
if textWidth < 16 then textWidth = 16 end
local chipWidth = ((MTH_BOOK_STATE.mode == "families") and 15 or 14) + textWidth
+112
View File
@@ -301,6 +301,40 @@ local function MTH_ZB_EnsureButtonOptionDefaults(buttonName)
saved["showammoname"] = true
end
saved["showammoname"] = saved["showammoname"] and true or false
if saved["showtotalperc"] == nil then
saved["showtotalperc"] = true
end
saved["showtotalperc"] = saved["showtotalperc"] and true or false
if saved["showtotalgauge"] == nil then
saved["showtotalgauge"] = false
end
saved["showtotalgauge"] = saved["showtotalgauge"] and true or false
if saved["counttotal"] == nil then
saved["counttotal"] = false
end
saved["counttotal"] = saved["counttotal"] and true or false
if saved["lowflash"] == nil then
saved["lowflash"] = true
end
saved["lowflash"] = saved["lowflash"] and true or false
if saved["parenttooltip"] == nil then
saved["parenttooltip"] = true
end
saved["parenttooltip"] = saved["parenttooltip"] and true or false
end
if buttonName == "zButtonPet" then
if saved["unhappyflash"] == nil then
saved["unhappyflash"] = true
end
saved["unhappyflash"] = saved["unhappyflash"] and true or false
end
if buttonName == "zButtonTrap" then
if saved["cooldown"] == nil then
saved["cooldown"] = true
end
saved["cooldown"] = saved["cooldown"] and true or false
end
return saved
@@ -459,6 +493,72 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
parentBox:Note(containerName.."AQFilterDesc", MTH_ZB_L("ZB_DESC_AQ_FILTER", "Hide Qiraji mounts outside AQ40. Show only Qiraji inside."))
end
if buttonName == "zButtonPet" then
parentBox:Checkbox(containerName.."UnhappyFlash", MTH_ZB_L("ZB_LABEL_UNHAPPY_FLASH", "Flash when pet is unhappy"), {
checked = saved["unhappyflash"],
onClick = function()
if not this then return end
ZHunterMod_Saved[buttonName]["unhappyflash"] = this:GetChecked() == 1
end,
})
end
if buttonName == "zButtonAmmo" then
parentBox:Checkbox(containerName.."ShowTotalPercent", MTH_ZB_L("ZB_LABEL_SHOW_TOTAL_PERCENT", "Show total % (center)"), {
checked = saved["showtotalperc"],
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
ZHunterMod_Saved[buttonName]["showtotalperc"] = checked
if buttonObj and buttonObj.ammoname and not buttonObj.isspell then
zButtonAmmo_UpdateButton(buttonObj)
end
end,
})
parentBox:Checkbox(containerName.."ShowTotalGauge", MTH_ZB_L("ZB_LABEL_SHOW_TOTAL_GAUGE", "Show total gauge (side)"), {
checked = saved["showtotalgauge"],
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
ZHunterMod_Saved[buttonName]["showtotalgauge"] = checked
if buttonObj and buttonObj.ammoname and not buttonObj.isspell then
zButtonAmmo_UpdateButton(buttonObj)
end
end,
})
parentBox:Checkbox(containerName.."CounterTotal", MTH_ZB_L("ZB_LABEL_COUNTER_TOTAL", "Counter shows total ammo"), {
checked = saved["counttotal"],
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
ZHunterMod_Saved[buttonName]["counttotal"] = checked
if buttonObj and buttonObj.ammoname and not buttonObj.isspell then
zButtonAmmo_UpdateButton(buttonObj)
end
end,
})
parentBox:Checkbox(containerName.."LowFlash", MTH_ZB_L("ZB_LABEL_LOW_AMMO_FLASH", "Flash when low on ammo"), {
checked = saved["lowflash"],
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
ZHunterMod_Saved[buttonName]["lowflash"] = checked
if buttonObj and buttonObj.ammoname and not buttonObj.isspell then
zButtonAmmo_UpdateButton(buttonObj)
end
end,
})
parentBox:Checkbox(containerName.."ParentTooltip", MTH_ZB_L("ZB_LABEL_SHOW_PARENT_TOOLTIP", "Show parent tooltip (usage report)"), {
checked = saved["parenttooltip"],
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
ZHunterMod_Saved[buttonName]["parenttooltip"] = checked
if buttonObj then buttonObj.parentTooltip = checked end
end,
})
end
local childrenBox = panel:Box("left", containerName.."ChildrenSection", MTH_ZB_L("ZB_SECTION_CHILDREN_BUTTONS", "Children Buttons"))
childrenBox:Slider(containerName.."ButtonSize", {
label = MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"),
@@ -557,6 +657,18 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
buttonObj.tooltip = saved["tooltip"] and true or false
end
if buttonName == "zButtonTrap" then
childrenBox:Checkbox(containerName.."ShowCooldown", MTH_ZB_L("ZB_LABEL_SHOW_TRAP_COOLDOWN", "Show cooldown"), {
checked = saved["cooldown"] and true or false,
onClick = function()
if not this then return end
local checked = this:GetChecked() == 1
if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end
ZHunterMod_Saved[buttonName]["cooldown"] = checked
end,
})
end
if buttonName == "zButtonAmmo" then
childrenBox:Checkbox(containerName.."ShowAmmoName", MTH_ZB_L("ZB_LABEL_SHOW_AMMO_NAME", "Show ammo name"), {
checked = saved["showammoname"],
+21 -5
View File
@@ -412,6 +412,13 @@ local ICON_STEADY = "Interface\\Icons\\Ability_Hunter_SteadyShot"
local ICON_MULTI = "Interface\\Icons\\Ability_UpgradeMoonGlaive"
local ICON_SERPENT = "Interface\\Icons\\Ability_Hunter_QuickShot"
local ICON_ARCANE = "Interface\\Icons\\Ability_ImpalingBolt"
-- Consume-spell icon per state (shown on the action cell).
local CONSUME_ICON = {
[EXPLOSIVE] = ICON_MULTI,
[POISONOUS] = ICON_SERPENT,
[ENCHANTED] = ICON_ARCANE,
}
-- Aura/effect textures (aura-dump debug capture)
local ICON_EXP = "Interface\\Icons\\Ability_SearingArrow" -- Explosive Ammunition debuff
local ICON_POI = "Interface\\Icons\\Ability_PoisonArrow" -- Poisonous Ammunition debuff
@@ -516,13 +523,15 @@ local function MTH_EA_GetSpellCD(spellName)
end
-- Action button: what spell to show and what spell to cast right now.
-- Returns displaySpell, icon, r, g, b, cooldownRemaining, castSpell
-- Returns displaySpell, icon, r, g, b, cooldownRemaining, castSpell, blocked
local function MTH_EA_GetActionSpell()
-- If current ammo state is blocked (creature immune or rotation toggle),
-- skip the consume-spell and fall through to Aimed/Steady instead.
-- skip the consume-spell and fall through to Aimed/Steady instead. The cell
-- shows the skipped consume-spell icon in red with a SKIP hint.
if rt.state ~= IDLE and MTH_EA_IsStateBlocked(rt.state) then
local cd = MTH_EA_GetSpellCD(SPELL_AIMED)
return SPELL_AIMED, ICON_AIMED, 1.00, 0.85, 0.00, cd, (cd > 0 and SPELL_STEADY or SPELL_AIMED)
local skipIcon = CONSUME_ICON[rt.state] or ICON_AIMED
return SPELL_AIMED, skipIcon, 1.00, 0.25, 0.25, cd, (cd > 0 and SPELL_STEADY or SPELL_AIMED), true
end
if rt.state == EXPLOSIVE then
@@ -718,9 +727,16 @@ MTH_EA_UpdateUI = function()
-- ── BOTTOM cell: action button ────────────────────────────
if cellBot then
local displaySpell, icon, r, g, b, cdRemain = MTH_EA_GetActionSpell()
local displaySpell, icon, r, g, b, cdRemain, _castSpell, blocked = MTH_EA_GetActionSpell()
if rt.state == IDLE then
if blocked then
-- Current ammo school is immune: skip the consume-spell.
cellBotBlink = 0
cellBotIcon:SetTexture(icon)
cellBotIcon:SetVertexColor(r, g, b, 1)
cellBot:SetBackdropBorderColor(1, 0, 0, 1)
cellBotCD:SetText("|cffff3030SKIP|r")
elseif rt.state == IDLE then
-- Always display Aimed Shot icon + its CD.
-- The action (keybind/click) casts Steady Shot while Aimed is on CD.
cellBotIcon:SetTexture(ICON_AIMED)
+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
+445 -1
View File
@@ -21,6 +21,9 @@ if not root["zButtonAmmo"] then
root["zButtonAmmo"]["children"]["size"] = 36
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
@@ -431,6 +434,397 @@ local function zButtonAmmo_GetEquippedAmmoInfo()
}
end
local function zButtonAmmo_EnsureTotalPercentText(button)
if button and not button.ammoTotalPercentText then
button.ammoTotalPercentText = button:CreateFontString(nil, "OVERLAY")
button.ammoTotalPercentText:SetPoint("CENTER", button, "CENTER", 0, 0)
button.ammoTotalPercentText:SetFont("Fonts\\ARIALN.ttf", 13, "THICKOUTLINE")
end
return button and button.ammoTotalPercentText
end
local function zButtonAmmo_ShouldShowTotalPercent()
local saved = zButtonAmmo_GetSaved()
return saved["showtotalperc"] and true or false
end
-- "Arrow" or "Bullet" for the currently equipped ranged weapon, or nil if none.
local function zButtonAmmo_GetEquippedAmmoCategory()
if not (GetInventoryItemLink and GetItemInfo) then
return nil
end
local link = GetInventoryItemLink("player", 18)
if not link then
return nil
end
local _, _, id = string.find(link, "Hitem:((%d+).-)")
if not id then
return nil
end
local _, _, _, _, _, weaponType = GetItemInfo(id)
if weaponType == MTH_WEAPON_GUNS then
return "Bullet"
elseif weaponType == MTH_WEAPON_BOWS or weaponType == MTH_WEAPON_CROSSBOWS then
return "Arrow"
end
return nil
end
-- Capacity = total slots of every equipped quiver (arrows) or ammo pouch
-- (bullets) multiplied by the per-slot stack size.
local zButtonAmmo_StackSize = 200
local function zButtonAmmo_GetAmmoCapacity(category)
if not category then
return 0
end
local wantedSubtype = (category == "Bullet") and "Ammo Pouch" or "Quiver"
local totalSlots = 0
for bag = 1, 4 do
local invSlot = ContainerIDToInventoryID and ContainerIDToInventoryID(bag) or nil
if invSlot then
local link = GetInventoryItemLink and GetInventoryItemLink("player", invSlot)
if link then
local _, _, id = string.find(link, "Hitem:((%d+).-)")
if id then
local _, _, _, _, _, bagSubtype = GetItemInfo(id)
if bagSubtype == wantedSubtype then
totalSlots = totalSlots + (GetContainerNumSlots(bag) or 0)
end
end
end
end
end
return totalSlots * zButtonAmmo_StackSize
end
-- Sum of all matching ammo across every bag, from the live cache.
local function zButtonAmmo_GetTotalAmmoCount(category)
if not category then
return 0
end
local total = 0
for _, info in pairs(zButtonAmmo_Cache) do
if info and info.type == category then
total = total + (tonumber(info.count) or 0)
end
end
return total
end
-- Divisor for per-stack heat colouring: real quiver/pouch capacity, or a
-- fixed fallback when no matching ammo bag is equipped.
local function zButtonAmmo_GetCountDivisor(category)
if category then
local capacity = zButtonAmmo_GetAmmoCapacity(category)
if capacity and capacity > 0 then
return capacity
end
end
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
return
end
if not zButtonAmmo_ShouldShowTotalPercent() then
totalText:SetText("")
return
end
local category = zButtonAmmo_GetEquippedAmmoCategory()
local capacity = zButtonAmmo_GetAmmoCapacity(category)
if not category or capacity <= 0 then
totalText:SetText("")
return
end
local perc = zButtonAmmo_GetTotalAmmoCount(category) / capacity
local r, g, b = zButtonAmmo_GetColorGradient(perc)
totalText:SetTextColor(r, g, b, 1)
totalText:SetText(math.floor(perc * 100 + 0.5) .. "%")
end
local function zButtonAmmo_ShouldShowTotalGauge()
local saved = zButtonAmmo_GetSaved()
return saved["showtotalgauge"] and true or false
end
local function zButtonAmmo_EnsureTotalGauge(button)
if not button then
return nil
end
if button.ammoTotalGauge then
return button.ammoTotalGauge
end
local gauge = CreateFrame("Frame", nil, button)
gauge:SetWidth(7)
gauge:SetHeight(button:GetHeight() or 36)
local bg = gauge:CreateTexture(nil, "BACKGROUND")
bg:SetAllPoints(gauge)
bg:SetTexture(0, 0, 0, 0.55)
local bar = CreateFrame("StatusBar", nil, gauge)
bar:SetAllPoints(gauge)
bar:SetOrientation("VERTICAL")
bar:SetStatusBarTexture("Interface\\TargetingFrame\\UI-StatusBar")
bar:SetMinMaxValues(0, 1)
bar:SetValue(0)
gauge.bar = bar
button.ammoTotalGauge = gauge
return gauge
end
local function zButtonAmmo_UpdateTotalGauge(button)
local gauge = zButtonAmmo_EnsureTotalGauge(button)
if not gauge then
return
end
if not zButtonAmmo_ShouldShowTotalGauge() then
gauge:Hide()
return
end
local category = zButtonAmmo_GetEquippedAmmoCategory()
local capacity = zButtonAmmo_GetAmmoCapacity(category)
if not category or capacity <= 0 then
gauge:Hide()
return
end
local perc = zButtonAmmo_GetTotalAmmoCount(category) / capacity
local fill = perc
if fill > 1 then fill = 1 end
if fill < 0 then fill = 0 end
gauge:SetHeight(button:GetHeight() or 36)
gauge:ClearAllPoints()
-- Gauge sits opposite the child expand direction.
local saved = zButtonAmmo_GetSaved()
if saved["firstbutton"] == "LEFT" then
gauge:SetPoint("LEFT", button, "RIGHT", 2, 0)
else
gauge:SetPoint("RIGHT", button, "LEFT", -2, 0)
end
gauge.bar:SetValue(fill)
local r, g, b = zButtonAmmo_GetColorGradient(perc)
gauge.bar:SetStatusBarColor(r, g, b)
gauge:Show()
end
local function zButtonAmmo_ShouldShowTotalCount()
local saved = zButtonAmmo_GetSaved()
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
@@ -447,6 +841,16 @@ function zButtonAmmo_UpdateButton(button)
if button.ammoShortLabelText then
button.ammoShortLabelText:SetText("")
end
if button == zButtonAmmo and button.ammoTotalPercentText then
button.ammoTotalPercentText:SetText("")
end
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
@@ -473,7 +877,7 @@ function zButtonAmmo_UpdateButton(button)
button.ammoslot = ammoInfo.slot
button.ammocount = ammoInfo.count
if countText then
local r, g, b = zButtonAmmo_GetColorGradient((ammoInfo.count or 0) / 2000)
local r, g, b = zButtonAmmo_GetColorGradient((ammoInfo.count or 0) / zButtonAmmo_GetCountDivisor(ammoInfo.type))
countText:SetTextColor(r, g, b, 1)
countText:SetText(ammoInfo.count)
end
@@ -484,6 +888,22 @@ function zButtonAmmo_UpdateButton(button)
countText:SetText("Out!")
end
end
if button == zButtonAmmo then
if countText and zButtonAmmo_ShouldShowTotalCount() then
local category = zButtonAmmo_GetEquippedAmmoCategory()
if category then
local total = zButtonAmmo_GetTotalAmmoCount(category)
local capacity = zButtonAmmo_GetAmmoCapacity(category)
local frac = capacity > 0 and (total / capacity) or (total / 2000)
local r, g, b = zButtonAmmo_GetColorGradient(frac)
countText:SetTextColor(r, g, b, 1)
countText:SetText(total)
end
end
zButtonAmmo_UpdateTotalPercent(button)
zButtonAmmo_UpdateTotalGauge(button)
zButtonAmmo_UpdateLowFlash(button)
end
end
function zButtonAmmo_SetButtons(parent, ammoList)
@@ -640,6 +1060,15 @@ local function zButtonAmmo_EnsureSpellOrder(ammoList)
if saved["showammoname"] == nil then
saved["showammoname"] = 1
end
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
@@ -684,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
@@ -810,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")
@@ -933,6 +1373,9 @@ function zButtonAmmo_Reset()
saved["children"]["size"] = 36
saved["children"]["hideonclick"] = 1
saved["showammoname"] = 1
saved["showtotalperc"] = 1
saved["lowflash"] = 1
saved["parenttooltip"] = 1
saved["lastEquipped"] = nil
end
@@ -1011,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
+80
View File
@@ -19,6 +19,7 @@ if not root["zButtonTrap"] then
root["zButtonTrap"]["children"] = {}
root["zButtonTrap"]["children"]["size"] = 36
root["zButtonTrap"]["children"]["hideonclick"] = 1
root["zButtonTrap"]["cooldown"] = true
end
ZHunterMod_Trap_Spells = {
@@ -55,6 +56,9 @@ local function zButtonTrap_EnsureConfig()
if saved["tooltip"] == nil then
saved["tooltip"] = 1
end
if saved["cooldown"] == nil then
saved["cooldown"] = true
end
if not saved["parent"] then
saved["parent"] = {}
end
@@ -100,6 +104,81 @@ local function zButtonTrap_ApplyRuntimeSettings()
zButtonTrap.fadetimer = saved["children"] and tonumber(saved["children"]["fadetimer"]) or 0
end
-- ---- Trap arming-cooldown display (shared trap re-arm, ~15s) ---------------
local MTH_ZH_TrapCD_Frame = nil
local MTH_ZH_TrapCD_Elapsed = 0
local function zButtonTrap_EnsureCooldownText(button)
if not button then return nil end
if button._mthCooldownText then return button._mthCooldownText end
local fs = button:CreateFontString(nil, "OVERLAY")
fs:SetFont("Fonts\\FRIZQT__.TTF", 14, "THICKOUTLINE")
fs:SetTextColor(1, 0.92, 0.3, 1)
fs:SetPoint("CENTER", button, "CENTER", 0, 0)
button._mthCooldownText = fs
return fs
end
local function zButtonTrap_ClearCooldown(button)
if not button then return end
if button._mthCooldownText then button._mthCooldownText:SetText("") end
if button.cooldown and button._mthCdActive then
CooldownFrame_SetTimer(button.cooldown, 0, 0, 0)
button._mthCdActive = nil
button._mthCdStart = nil
button._mthCdDur = nil
end
end
local function zButtonTrap_UpdateCooldownFor(button)
if not (button and button.id) then
zButtonTrap_ClearCooldown(button)
return
end
local start, duration, enable = GetSpellCooldown(button.id, "spell")
-- Skip the ~1.5s global cooldown; only show the real trap re-arm timer.
if enable == 1 and duration and duration > 1.5 and start and start > 0 then
local remaining = start + duration - GetTime()
if remaining > 0 then
if button.cooldown and (button._mthCdStart ~= start or button._mthCdDur ~= duration) then
CooldownFrame_SetTimer(button.cooldown, start, duration, 1)
button._mthCdStart = start
button._mthCdDur = duration
button._mthCdActive = 1
end
local fs = zButtonTrap_EnsureCooldownText(button)
if fs then fs:SetText(tostring(math.ceil(remaining))) end
return
end
end
zButtonTrap_ClearCooldown(button)
end
local function zButtonTrap_CooldownOnUpdate()
MTH_ZH_TrapCD_Elapsed = MTH_ZH_TrapCD_Elapsed + (arg1 or 0)
if MTH_ZH_TrapCD_Elapsed < 0.1 then return end
MTH_ZH_TrapCD_Elapsed = 0
if not zButtonTrap then return end
local saved = zButtonTrap_GetSaved()
if not saved["cooldown"] then
zButtonTrap_ClearCooldown(zButtonTrap)
for i = 1, (zButtonTrap.count or 0) do
zButtonTrap_ClearCooldown(getglobal("zButtonTrap"..i))
end
return
end
zButtonTrap_UpdateCooldownFor(zButtonTrap)
for i = 1, (zButtonTrap.count or 0) do
zButtonTrap_UpdateCooldownFor(getglobal("zButtonTrap"..i))
end
end
local function zButtonTrap_EnsureCooldownDriver()
if MTH_ZH_TrapCD_Frame then return end
MTH_ZH_TrapCD_Frame = CreateFrame("Frame", "MTH_ZH_TrapCooldownDriver")
MTH_ZH_TrapCD_Frame:SetScript("OnUpdate", zButtonTrap_CooldownOnUpdate)
end
function zButtonTrap_OnLoad()
this:RegisterEvent("VARIABLES_LOADED")
@@ -148,6 +227,7 @@ function zButtonTrap_CreateButtons()
end
zButtonTrap.found = ZSpellButton_SetButtons(zButtonTrap, info)
zButtonTrap_ApplyRuntimeSettings()
zButtonTrap_EnsureCooldownDriver()
end
function zButtonTrap_SetupSizeAndPosition()