From f6b495d68d1bbed86dd3da8ff6c0e1c1db19a030 Mon Sep 17 00:00:00 2001 From: DuvelCorp Date: Sun, 4 Oct 2026 20:37:54 +0200 Subject: [PATCH] zAmmo: total ammo meter (centered % + side gauge) with options --- CHANGELOG.md | 2 + api/options-zbuttons.lua | 30 ++++++ modules/zhunter/zButtonAmmo.lua | 176 ++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89eb4c1..3a7cf1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ All notable changes to MetaHunt will be documented in this file. - **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). + ### Fixed - **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. diff --git a/api/options-zbuttons.lua b/api/options-zbuttons.lua index 7d2a048..b7b421e 100644 --- a/api/options-zbuttons.lua +++ b/api/options-zbuttons.lua @@ -301,6 +301,14 @@ 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 end if buttonName == "zButtonTrap" then @@ -596,6 +604,28 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma end end, }) + 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, + }) end MTH_SetButtonEnabledState(buttonName, buttonObj, saved["enabled"] and true or false) diff --git a/modules/zhunter/zButtonAmmo.lua b/modules/zhunter/zButtonAmmo.lua index 4786c7f..9c57ab1 100644 --- a/modules/zhunter/zButtonAmmo.lua +++ b/modules/zhunter/zButtonAmmo.lua @@ -21,6 +21,7 @@ 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"]["lastEquipped"] = nil end @@ -431,6 +432,167 @@ 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 + +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 + function zButtonAmmo_UpdateButton(button) if not button then button = this @@ -447,6 +609,12 @@ 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 return end @@ -484,6 +652,10 @@ function zButtonAmmo_UpdateButton(button) countText:SetText("Out!") end end + if button == zButtonAmmo then + zButtonAmmo_UpdateTotalPercent(button) + zButtonAmmo_UpdateTotalGauge(button) + end end function zButtonAmmo_SetButtons(parent, ammoList) @@ -640,6 +812,9 @@ local function zButtonAmmo_EnsureSpellOrder(ammoList) if saved["showammoname"] == nil then saved["showammoname"] = 1 end + if saved["showtotalperc"] == nil then + saved["showtotalperc"] = 1 + end if not saved["parent"] then saved["parent"] = {} end @@ -933,6 +1108,7 @@ function zButtonAmmo_Reset() saved["children"]["size"] = 36 saved["children"]["hideonclick"] = 1 saved["showammoname"] = 1 + saved["showtotalperc"] = 1 saved["lastEquipped"] = nil end