18 Commits
Author SHA1 Message Date
DuvelCorp afa9091ab0 Release 2.1.0: version bump + clearer SmartAmmo out-of-ammo message 2026-10-08 11:38:51 +02:00
DuvelCorp 19be5f76ea Changelog: drop pet level-up line, add pet login-notification fix 2026-10-08 09:22:15 +02:00
DuvelCorp 357bf1e6da MM widget: show SKIP on the action cell when the active ammo school is immune 2026-10-08 08:40:37 +02:00
DuvelCorp 14f33b35ca zAmmo low-ammo flash + usage tracker; zPet unhappy flash; split parent/child tooltips 2026-10-08 08:10:17 +02:00
DuvelCorp 69c6d44b8e zAmmo: bottom-counter total toggle + capacity-based counter colouring 2026-10-06 15:09:19 +02:00
DuvelCorp f6b495d68d zAmmo: total ammo meter (centered % + side gauge) with options 2026-10-04 20:37:54 +02:00
DuvelCorp bb5c875835 Changelog: 2.1.0 entries 2026-10-03 17:43:50 +02:00
DuvelCorp f8d1aa8033 zTrap: show trap arming cooldown (spiral + seconds) with a toggle 2026-10-02 23:03:29 +02:00
DuvelCorp ef8daaa568 Remove known/unknown filter (keep ability coloring); fix pet level-up subtitle 2026-10-02 23:00:12 +02:00
DuvelCorp af1a01ac2e Fix Beast Lore ability coloring (color chips) and filter-row layout 2026-10-02 21:46:11 +02:00
DuvelCorp 60171b559e Beast Lore: colour abilities by known status and add known/unknown filter 2026-10-02 21:08:45 +02:00
DuvelCorp c35258e615 Reuse best-matching pet record on stable scan to stop shell duplication 2026-09-17 11:25:53 +02:00
DuvelCorp b40f9c1095 Stop FOM feeding below-minimum-level food to high-level pets 2026-09-17 10:40:38 +02:00
DuvelCorp 1680c6fc51 Route feed-tracking debug to the /mth err window 2026-09-17 10:40:38 +02:00
DuvelCorp 05cfce240b Document Gitea lowercase-folder rename step in README 2026-09-15 08:38:13 +02:00
DuvelCorp d49ed5abcc Revert addon-folder texture resolution (assume MetaHunt folder) 2026-09-15 08:06:57 +02:00
DuvelCorp ea01c9ef15 Silence pet level/loyalty notifications right after loading 2026-09-15 07:34:39 +02:00
DuvelCorp 315f6bb4f1 Load marker texture by actual addon folder name (case-proof) 2026-09-15 07:34:39 +02:00
17 changed files with 860 additions and 27 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.
+10
View File
@@ -14,6 +14,16 @@ You play the best class, now you have the best companion for it (and its not you
- MetaHunt is fully compatible with QUIVER <3, and is not a replacement for it.
- The addon purposely disable itself on all Capycraft/Ravencraft Realms, following the DDOS attacks they conducted on other servers.
### Installation (READ THIS!) ⚠️⚠️⚠️
After you download the zip, you have a manual action to do because of Gitea behaviour.
When you unzip, you will find a "metahunt" root folder, you SHOULD rename it as exactly "MetaHunt", and then move that folder in your \Interface\AddOns\ directory. If you dont restore the capital letters in the folder name, the addon will be broken.
This is [a known issue from Gitea/Forgejo](https://codeberg.org/forgejo/forgejo/issues/800) when the system creates zip files, and there's nothing add-on devs can do to overcome it.
## Twow Data
MetaHunt ships with large twow datastores, and notably:
+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",
+2 -2
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
MTH = MTH or {
version = "2.0.0",
version = "2.1.0",
name = "MetaHunt",
modules = {},
config = {},
+10 -1
View File
@@ -4,6 +4,7 @@
local MTH_LT_Frame = nil
local MTH_LT_PendingPetCheck = false
local MTH_LT_SuppressUntil = 0
local MTH_LT_HunterLines = {
[1] = "You can now track beasts. Revolutionary: look for the things you intend to shoot.",
@@ -249,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
@@ -293,6 +294,13 @@ local function MTH_LT_HandlePetState()
store.pet = state
return
end
-- Just after a loading screen the pet's real level/loyalty can arrive a beat
-- late; record them as the baseline silently so we never announce progress
-- that actually happened in an earlier session (e.g. a fresh install).
if type(GetTime) == "function" and MTH_LT_SuppressUntil > 0 and GetTime() < MTH_LT_SuppressUntil then
store.pet = state
return
end
if state.level > (tonumber(pet.level) or 0) then
local line = MTH_LT_NextLine(store, "petLine", MTH_LT_PetLines)
if MTH_LT_MessageEnabled("petLevelUp", true) then MTH:Print(state.name .. " reached level " .. state.level .. ". " .. line) end
@@ -368,6 +376,7 @@ function MTH_LevelTracker_Initialize()
if event == "PLAYER_ENTERING_WORLD" then
local level = type(UnitLevel) == "function" and tonumber(UnitLevel("player")) or nil
if level then MTH_LT_Store().hunterLevel = level end
if type(GetTime) == "function" then MTH_LT_SuppressUntil = GetTime() + 8 end
MTH_LT_RequestPetCheck()
elseif event == "PLAYER_LEVEL_UP" then
MTH_LT_HandleHunterLevel(tonumber(arg1) or tonumber(UnitLevel("player")) or 1)
+42 -9
View File
@@ -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 "") .. "'"
+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)
+13 -3
View File
@@ -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
+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()