mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-10-08 17:50:00 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afa9091ab0 | ||
|
|
19be5f76ea | ||
|
|
357bf1e6da | ||
|
|
14f33b35ca | ||
|
|
69c6d44b8e | ||
|
|
f6b495d68d | ||
|
|
bb5c875835 | ||
|
|
f8d1aa8033 | ||
|
|
ef8daaa568 | ||
|
|
af1a01ac2e | ||
|
|
60171b559e | ||
|
|
c35258e615 | ||
|
|
b40f9c1095 | ||
|
|
1680c6fc51 | ||
|
|
05cfce240b | ||
|
|
d49ed5abcc | ||
|
|
ea01c9ef15 | ||
|
|
315f6bb4f1 |
@@ -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.
|
||||
|
||||
@@ -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
@@ -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 = {},
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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