mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-10-08 09:46:32 +00:00
Merge pull request '2.1.0' (#2) from 2.1.0 into master
Reviewed-on: https://codeberg.org/Duvelcorp/MetaHunt/pulls/2
This commit is contained in:
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -30,8 +30,8 @@ MTH_FEED_TRACE = false
|
||||
|
||||
local function MTH_FEED_Trace(message)
|
||||
if not MTH_FEED_TRACE then return end
|
||||
if type(DEFAULT_CHAT_FRAME) == "table" and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff88aaff[MTH-FeedTrack]|r " .. tostring(message))
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.AddInfo) == "function" then
|
||||
MTH_DebugFrame:AddInfo("[MTH-FeedTrack] " .. tostring(message))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
MTH = MTH or {
|
||||
version = "2.0.0",
|
||||
version = "2.1.0",
|
||||
name = "MetaHunt",
|
||||
modules = {},
|
||||
config = {},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2138,16 +2138,45 @@ local function MTH_PETS_SelectPetIdBySnapshot(store, snapshot, previousPetId)
|
||||
if snapshot.signature and snapshot.signature ~= "" then
|
||||
local list = store.signatureIndex[snapshot.signature]
|
||||
if type(list) == "table" then
|
||||
local count = 0
|
||||
local candidateId = nil
|
||||
local liveIds = {}
|
||||
for _, listedId in pairs(list) do
|
||||
if listedId and store.activeById[listedId] then
|
||||
count = count + 1
|
||||
candidateId = listedId
|
||||
if listedId and store.activeById[tostring(listedId)] then
|
||||
table.insert(liveIds, tostring(listedId))
|
||||
end
|
||||
end
|
||||
if count == 1 then
|
||||
return candidateId
|
||||
local liveCount = table.getn(liveIds)
|
||||
if liveCount == 1 then
|
||||
return liveIds[1]
|
||||
elseif liveCount > 1 then
|
||||
-- Several records share this name+family (earlier fragmentation, or two
|
||||
-- pets with the same name). Reuse the best existing match instead of
|
||||
-- minting yet another empty shell that only worsens the fragmentation:
|
||||
-- require a level match, then prefer loyalty match and records that
|
||||
-- already carry tame/spellbook history, then most recently seen.
|
||||
local snapLevel = tonumber(snapshot.level)
|
||||
local snapLoyalty = tonumber(snapshot.loyaltyLevel)
|
||||
local bestId = nil
|
||||
local bestScore = -1
|
||||
local bestSeen = -1
|
||||
for i = 1, liveCount do
|
||||
local rowId = liveIds[i]
|
||||
local row = store.activeById[rowId]
|
||||
if type(row) == "table" and snapLevel and tonumber(row.level) == snapLevel then
|
||||
local score = 0
|
||||
if snapLoyalty and tonumber(row.loyaltyLevel) == snapLoyalty then score = score + 2 end
|
||||
if MTH_PETS_RowHasAnyTameMetadata(row) then score = score + 1 end
|
||||
if type(row.petSpellbook) == "table" then score = score + 1 end
|
||||
local seen = tonumber(row.lastSeen) or tonumber(row.lastUpdated) or 0
|
||||
if score > bestScore or (score == bestScore and seen > bestSeen) then
|
||||
bestScore = score
|
||||
bestSeen = seen
|
||||
bestId = rowId
|
||||
end
|
||||
end
|
||||
end
|
||||
if bestId then
|
||||
return bestId
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2364,10 +2393,14 @@ local function MTH_PETS_RecordStableSlot(pets, slot, raw1, raw2, raw3, raw4, raw
|
||||
local snapshotGuid = snapshot and snapshot.guid or nil
|
||||
local signatureMatches = (existingSignature and snapshotSignature and existingSignature == snapshotSignature) and true or false
|
||||
local guidMatches = (existingGuid and snapshotGuid and existingGuid ~= "" and existingGuid == snapshotGuid) and true or false
|
||||
-- An empty shell must not lock the slot: a richer same-signature record may
|
||||
-- exist that we should re-adopt instead (self-heals earlier fragmentation).
|
||||
local existingHasData = MTH_PETS_RowHasAnyTameMetadata(existingRow)
|
||||
or type(existingRow.petSpellbook) == "table" or existingRow.petNumber ~= nil
|
||||
|
||||
if signatureMatches or guidMatches then
|
||||
if guidMatches or (signatureMatches and existingHasData) then
|
||||
petId = existingId
|
||||
else
|
||||
elseif not signatureMatches then
|
||||
MTH_PETS_LogConsistency("RecordStableSlot remapping stale slot=" .. tostring(slotNumber)
|
||||
.. " existingId=" .. tostring(existingId)
|
||||
.. " existingSignature='" .. tostring(existingSignature or "") .. "'"
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -2483,10 +2483,20 @@ function FOM_FlatFoodList()
|
||||
local isExceptionReject = false;
|
||||
local isLevelOverrideReject = not FOM_IsItemPetLevelCompatible(itemID, petLevel);
|
||||
local isQuarantineReject = FOM_IsQuarantined(itemID, petLevel, familyKey);
|
||||
-- A food whose known level is below the pet's minimum is refused by the
|
||||
-- server ("not high enough"); skip it up front so FOM never keeps
|
||||
-- re-feeding a too-low stack just to free a bag slot.
|
||||
local isLevelRuleReject = false;
|
||||
if (petLevel > 0 and tonumber(foodLevel) ~= nil and type(MTH_FEED_GetDerivedMinFoodLevel) == "function") then
|
||||
local minFoodLevel = MTH_FEED_GetDerivedMinFoodLevel(petLevel);
|
||||
if (minFoodLevel ~= nil and tonumber(foodLevel) < minFoodLevel) then
|
||||
isLevelRuleReject = true;
|
||||
end
|
||||
end
|
||||
if (isExceptionReject) then
|
||||
rejectedByException = rejectedByException + 1;
|
||||
end
|
||||
if (isLevelOverrideReject) then
|
||||
if (isLevelOverrideReject or isLevelRuleReject) then
|
||||
rejectedByLevelOverride = rejectedByLevelOverride + 1;
|
||||
end
|
||||
if (isQuarantineReject) then
|
||||
@@ -2500,7 +2510,7 @@ function FOM_FlatFoodList()
|
||||
unknownLevelCandidates = unknownLevelCandidates + 1;
|
||||
end
|
||||
|
||||
if (isCompatible and not isExceptionReject and not isLevelOverrideReject and not isQuarantineReject) then
|
||||
if (isCompatible and not isExceptionReject and not isLevelOverrideReject and not isQuarantineReject and not isLevelRuleReject) then
|
||||
compatibleCount = compatibleCount + 1;
|
||||
local foodQuality = tonumber(foodLevel);
|
||||
if (foodQuality == nil) then
|
||||
@@ -2511,7 +2521,7 @@ function FOM_FlatFoodList()
|
||||
end
|
||||
end
|
||||
table.insert(foodList, {bag=bagNum, slot=itemNum, link=itemLink, itemId=itemID, count=itemCount, quality=foodQuality, useful=isUseful, temp=FOM_IsTemporaryFood(itemLink), knownLevel=hasKnownLevel});
|
||||
elseif (isAbovePreferredLevel and not isExceptionReject and not isLevelOverrideReject and not isQuarantineReject) then
|
||||
elseif (isAbovePreferredLevel and not isExceptionReject and not isLevelOverrideReject and not isQuarantineReject and not isLevelRuleReject) then
|
||||
overflowCompatibleCount = overflowCompatibleCount + 1;
|
||||
local overflowQuality = tonumber(foodLevel);
|
||||
if (overflowQuality == nil) then
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user