mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-21 23:26:57 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c007f2c17 | |||
| c4911ef9fe |
@@ -3,6 +3,25 @@
|
||||
All notable changes to MetaHunt will be documented in this file.
|
||||
|
||||
|
||||
## [1.5.1] - 2026-04-01
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- **zAmmo — bag ammo labels**: Quality-colored short-name labels overlaid on ammo items in bags and bank, with optional heat-colored DPS damage indicator. Three new checkboxes General options: bag labels, bank labels, and damage display. Works with both default bags and pfUI bags.
|
||||
|
||||
- **MM Widget — immunity detection**: The MM Widget keybind now detects when the current target is immune to a damage school (Fire / Nature / Arcane) and automatically skips the useless shot, casting Aimed or Steady Shot instead. Immunity is detected in two ways: reactively via combat log "immune" messages, and proactively via hardcoded creature-type rules (e.g. Elementals are always flagged Nature-immune on target). Immune cells show a red border.
|
||||
|
||||
- **MM Widget — rotation toggles**: Three new checkboxes in Options → ExpAmmo → Rotation let you permanently disable individual shots (Multi-Shot, Serpent Sting, Arcane Shot) — unchecked shots are skipped during their proc window.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Zone mapping — 22 beasts with raw zone IDs**: Fixed 8 unmapped zone IDs (Blackrock Depths, Molten Core, Wailing Caverns, Maraudon, Sunnyglade, Icepoint Rock, Winter Veil Vale, Gilneas City, Karazhan) that caused 22 beasts to display as "Zone xxx" instead of their proper zone name in the Hunter Book.
|
||||
|
||||
- **MM Widget — IsStateBlocked nil error**: Moved `MTH_EA_IsStateBlocked` definition before its first use, fixing a nil function call on load.
|
||||
|
||||
|
||||
## [1.5.0] - 2026-03-31
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
## Interface: 11200
|
||||
## Version: 1.5.0
|
||||
## Version: 1.5.1
|
||||
## Title: MetaHunt - |cff00ff00Hunter
|
||||
## Author: Metasploit and his Copilot ;)
|
||||
## Notes: Unified addon suite for huntards making old addons compatible with TurtleWoW, and adding an arsenal of never seen Hunter's utilities.
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
-- ============================================================
|
||||
-- Bag Ammo Labels — standalone overlay for bag & bank ammo slots
|
||||
-- Uses globals from zButtonAmmo.lua:
|
||||
-- zButtonAmmo_ShortArrowLabels, zButtonAmmo_ShortBulletLabels,
|
||||
-- zButtonAmmo_GetQualityColor, zButtonAmmo_GetColorGradient,
|
||||
-- zButtonAmmo_GetKnownAmmoTypes
|
||||
-- Uses DPS data from MTH_DS_AmmoItems[id].dps
|
||||
-- ============================================================
|
||||
|
||||
-- Saved variable cache (per-character, stored in "general" module)
|
||||
local cache = {} -- bagAmmoLabels, bankAmmoLabels, bagAmmoDamage
|
||||
|
||||
local function GetStore()
|
||||
return MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("general")
|
||||
end
|
||||
|
||||
local function GetCached(key, default)
|
||||
if cache[key] == nil then
|
||||
local store = GetStore()
|
||||
if type(store) == "table" and store[key] ~= nil then
|
||||
cache[key] = store[key] and true or false
|
||||
else
|
||||
cache[key] = default or false
|
||||
end
|
||||
end
|
||||
return cache[key]
|
||||
end
|
||||
|
||||
local function SetCached(key, enabled)
|
||||
cache[key] = enabled and true or false
|
||||
local store = GetStore()
|
||||
if type(store) == "table" then
|
||||
store[key] = cache[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- Public getters/setters
|
||||
function MTH_BagAmmoLabels_GetEnabled()
|
||||
return GetCached("bagAmmoLabels")
|
||||
end
|
||||
function MTH_BagAmmoLabels_SetEnabled(enabled)
|
||||
SetCached("bagAmmoLabels", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAll()
|
||||
end
|
||||
|
||||
function MTH_BankAmmoLabels_GetEnabled()
|
||||
return GetCached("bankAmmoLabels")
|
||||
end
|
||||
function MTH_BankAmmoLabels_SetEnabled(enabled)
|
||||
SetCached("bankAmmoLabels", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAllBank()
|
||||
end
|
||||
|
||||
function MTH_BagAmmoDamage_GetEnabled()
|
||||
return GetCached("bagAmmoDamage")
|
||||
end
|
||||
function MTH_BagAmmoDamage_SetEnabled(enabled)
|
||||
SetCached("bagAmmoDamage", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAll()
|
||||
MTH_BagAmmoLabels_RefreshAllBank()
|
||||
end
|
||||
|
||||
-- Heat color: red (7.5 DPS) → yellow → green (20.5 DPS); below 7.5 = grey
|
||||
local DPS_MIN = 7.5
|
||||
local DPS_MAX = 20.5
|
||||
|
||||
local function GetDpsHeatColor(dps)
|
||||
if not dps then return 1, 1, 1 end
|
||||
if dps < DPS_MIN then return 0.6, 0.6, 0.6 end
|
||||
local perc = (dps - DPS_MIN) / (DPS_MAX - DPS_MIN)
|
||||
return zButtonAmmo_GetColorGradient(perc)
|
||||
end
|
||||
|
||||
-- Look up DPS from MTH_DS_AmmoItems by numeric item ID
|
||||
local function GetAmmoDps(numericId)
|
||||
if not MTH_DS_AmmoItems then return nil end
|
||||
local entry = MTH_DS_AmmoItems[numericId]
|
||||
return entry and entry.dps
|
||||
end
|
||||
|
||||
-- Detect whether pfUI bags are active
|
||||
local function HasPfUI()
|
||||
return pfUI and pfUI.bags and pfUI.bag and true or false
|
||||
end
|
||||
|
||||
-- Is this bagID a bank bag?
|
||||
local BANK_BAGS = { [-1] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true }
|
||||
|
||||
local function IsBankBag(bagID)
|
||||
return BANK_BAGS[bagID] or false
|
||||
end
|
||||
|
||||
-- Apply or clear labels on a single bag-slot button
|
||||
local function UpdateButton(button, bagID, slot, showName, showDmg)
|
||||
if not button then return end
|
||||
|
||||
-- Lazy-create name label (TOPLEFT)
|
||||
if not button._mthAmmoLabel then
|
||||
button._mthAmmoLabel = button:CreateFontString(nil, "OVERLAY")
|
||||
button._mthAmmoLabel:SetPoint("TOPLEFT", button, "TOPLEFT", 1, -1)
|
||||
button._mthAmmoLabel:SetFont("Fonts\\ARIALN.ttf", 9, "OUTLINE")
|
||||
button._mthAmmoLabel:Hide()
|
||||
end
|
||||
-- Lazy-create damage label (CENTER, large + bold)
|
||||
if not button._mthAmmoDmgLabel then
|
||||
button._mthAmmoDmgLabel = button:CreateFontString(nil, "OVERLAY")
|
||||
button._mthAmmoDmgLabel:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
button._mthAmmoDmgLabel:SetFont("Fonts\\ARIALN.ttf", 10, "THICKOUTLINE")
|
||||
button._mthAmmoDmgLabel:Hide()
|
||||
end
|
||||
|
||||
local nameLabel = button._mthAmmoLabel
|
||||
local dmgLabel = button._mthAmmoDmgLabel
|
||||
|
||||
if not showName and not showDmg then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local link = GetContainerItemLink(bagID, slot)
|
||||
if not link then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local _, _, id = string.find(link, "Hitem:(%d+)")
|
||||
if not id then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local numericId = tonumber(id)
|
||||
local itemName, _, itemQuality, _, _, _, _, _, itemSlot = GetItemInfo(id)
|
||||
if not itemName then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local isAmmo = (itemSlot == "INVTYPE_AMMO")
|
||||
if not isAmmo then
|
||||
local knownAmmo = zButtonAmmo_GetKnownAmmoTypes and zButtonAmmo_GetKnownAmmoTypes()
|
||||
isAmmo = knownAmmo and knownAmmo[itemName]
|
||||
end
|
||||
if not isAmmo then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Name label
|
||||
if showName then
|
||||
local shortText = zButtonAmmo_ShortArrowLabels[itemName]
|
||||
or zButtonAmmo_ShortBulletLabels[itemName]
|
||||
if shortText then
|
||||
local cr, cg, cb = zButtonAmmo_GetQualityColor(itemQuality)
|
||||
nameLabel:SetTextColor(cr, cg, cb, 1)
|
||||
nameLabel:SetText(shortText)
|
||||
nameLabel:Show()
|
||||
else
|
||||
nameLabel:Hide()
|
||||
end
|
||||
else
|
||||
nameLabel:Hide()
|
||||
end
|
||||
|
||||
-- Damage label
|
||||
if showDmg then
|
||||
local dps = GetAmmoDps(numericId)
|
||||
if dps then
|
||||
local cr, cg, cb = GetDpsHeatColor(dps)
|
||||
dmgLabel:SetTextColor(cr, cg, cb, 1)
|
||||
dmgLabel:SetText(dps)
|
||||
dmgLabel:Show()
|
||||
else
|
||||
dmgLabel:Hide()
|
||||
end
|
||||
else
|
||||
dmgLabel:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear both labels on a button
|
||||
local function ClearButton(button)
|
||||
if not button then return end
|
||||
if button._mthAmmoLabel then button._mthAmmoLabel:Hide() end
|
||||
if button._mthAmmoDmgLabel then button._mthAmmoDmgLabel:Hide() end
|
||||
end
|
||||
|
||||
-- Forward declaration
|
||||
local RefreshAllVisibleFrames
|
||||
|
||||
-- Refresh labels on a single bag (works for any bagID)
|
||||
local function RefreshBag(bagID, showName, showDmg)
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
if numSlots == 0 then return end
|
||||
|
||||
-- pfUI path
|
||||
if HasPfUI() then
|
||||
local pfBag = pfUI.bags[bagID]
|
||||
if not pfBag or not pfBag.slots then return end
|
||||
for slot = 1, numSlots do
|
||||
local slotData = pfBag.slots[slot]
|
||||
local button = slotData and slotData.frame
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, slot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Default UI path
|
||||
|
||||
-- Main bank (bagID -1): buttons are BankFrameItem1..28
|
||||
if bagID == -1 then
|
||||
for slot = 1, numSlots do
|
||||
local button = getglobal("BankFrameItem" .. slot)
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, -1, slot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- All other bags (0-4 and 5-11): search all ContainerFrames
|
||||
for ci = 1, 13 do
|
||||
local frame = getglobal("ContainerFrame" .. ci)
|
||||
if frame and frame:IsVisible() and frame:GetID() == bagID then
|
||||
local frameName = frame:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(frameName .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Clear stale labels on any buttons beyond numSlots
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(frameName .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Public: refresh all inventory bags (0-4)
|
||||
function MTH_BagAmmoLabels_RefreshAll()
|
||||
RefreshAllVisibleFrames()
|
||||
end
|
||||
|
||||
-- Public: refresh all bank bags (-1, 5-11)
|
||||
function MTH_BagAmmoLabels_RefreshAllBank()
|
||||
RefreshAllVisibleFrames()
|
||||
end
|
||||
|
||||
-- Refresh all visible default UI ContainerFrames (frame-centric: no bagID lookup)
|
||||
RefreshAllVisibleFrames = function()
|
||||
local bagShowName = MTH_BagAmmoLabels_GetEnabled()
|
||||
local bankShowName = MTH_BankAmmoLabels_GetEnabled()
|
||||
local showDmg = MTH_BagAmmoDamage_GetEnabled()
|
||||
|
||||
if HasPfUI() then
|
||||
-- pfUI: refresh via pfUI bag structure
|
||||
for bagID = 0, 4 do
|
||||
RefreshBag(bagID, bagShowName, showDmg)
|
||||
end
|
||||
local bankBags = { -1, 5, 6, 7, 8, 9, 10, 11 }
|
||||
for _, bagID in ipairs(bankBags) do
|
||||
RefreshBag(bagID, bankShowName, showDmg)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Default UI: iterate all visible ContainerFrames
|
||||
for ci = 1, 13 do
|
||||
local cf = getglobal("ContainerFrame" .. ci)
|
||||
if cf and cf:IsVisible() then
|
||||
local bagID = cf:GetID()
|
||||
local showName = IsBankBag(bagID) and bankShowName or bagShowName
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
local fname = cf:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(fname .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Bank bag -1: BankFrameItem buttons
|
||||
if bankShowName or showDmg then
|
||||
local numSlots = GetContainerNumSlots(-1)
|
||||
for slot = 1, numSlots do
|
||||
local button = getglobal("BankFrameItem" .. slot)
|
||||
if button then
|
||||
UpdateButton(button, -1, slot, bankShowName, showDmg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Event-driven refresh: coalesce with a short delay
|
||||
do
|
||||
local frame = CreateFrame("Frame", "MTH_BagAmmoLabelsFrame", UIParent)
|
||||
local needsRefresh = false
|
||||
local elapsed = 0
|
||||
|
||||
frame:RegisterEvent("BAG_UPDATE")
|
||||
frame:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
|
||||
frame:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED")
|
||||
frame:RegisterEvent("BANKFRAME_OPENED")
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- After login/reload, delay 1s for item cache to populate
|
||||
elapsed = -0.95
|
||||
needsRefresh = true
|
||||
frame:Show()
|
||||
return
|
||||
end
|
||||
|
||||
if event == "BANKFRAME_OPENED" then
|
||||
if MTH_BankAmmoLabels_GetEnabled() or MTH_BagAmmoDamage_GetEnabled() then
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYERBANKSLOTS_CHANGED" or event == "PLAYERBANKBAGSLOTS_CHANGED" then
|
||||
if MTH_BankAmmoLabels_GetEnabled() or MTH_BagAmmoDamage_GetEnabled() then
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- BAG_UPDATE
|
||||
local bagID = arg1
|
||||
if not bagID then return end
|
||||
if IsBankBag(bagID) then
|
||||
if not MTH_BankAmmoLabels_GetEnabled() and not MTH_BagAmmoDamage_GetEnabled() then return end
|
||||
else
|
||||
if not MTH_BagAmmoLabels_GetEnabled() and not MTH_BagAmmoDamage_GetEnabled() then return end
|
||||
end
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end)
|
||||
|
||||
frame:SetScript("OnUpdate", function()
|
||||
elapsed = elapsed + (arg1 or 0.016)
|
||||
if elapsed < 0.05 then return end
|
||||
needsRefresh = false
|
||||
frame:Hide()
|
||||
RefreshAllVisibleFrames()
|
||||
end)
|
||||
frame:Hide()
|
||||
|
||||
-- Hook ContainerFrame OnShow: trigger a refresh when a bag becomes visible
|
||||
if not HasPfUI() then
|
||||
for ci = 1, 13 do
|
||||
local cf = getglobal("ContainerFrame" .. ci)
|
||||
if cf then
|
||||
local origOnShow = cf:GetScript("OnShow")
|
||||
cf:SetScript("OnShow", function()
|
||||
if origOnShow then origOnShow() end
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Post-hook ContainerFrame_Update for default UI (synchronous update)
|
||||
if type(ContainerFrame_Update) == "function" then
|
||||
local origCFU = ContainerFrame_Update
|
||||
ContainerFrame_Update = function(cf)
|
||||
origCFU(cf)
|
||||
if cf and cf:IsVisible() then
|
||||
local bagID = cf:GetID()
|
||||
local showDmg = MTH_BagAmmoDamage_GetEnabled()
|
||||
local showName
|
||||
if IsBankBag(bagID) then
|
||||
showName = MTH_BankAmmoLabels_GetEnabled()
|
||||
else
|
||||
showName = MTH_BagAmmoLabels_GetEnabled()
|
||||
end
|
||||
if showName or showDmg then
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
local fname = cf:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(fname .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
end
|
||||
end
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Frame hidden: clear all labels
|
||||
if cf then
|
||||
local fname = cf:GetName()
|
||||
if fname then
|
||||
for slot = 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
MTH_CONST = MTH_CONST or {}
|
||||
MTH_CONST.version = MTH_CONST.version or "1.5.0"
|
||||
MTH_CONST.version = MTH_CONST.version or "1.5.1"
|
||||
|
||||
MTH_CONST.WEAPON_TYPES = {
|
||||
BOWS = "Bows",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
MTH = MTH or {
|
||||
version = "1.5.0",
|
||||
version = "1.5.1",
|
||||
name = "MetaHunt",
|
||||
modules = {},
|
||||
config = {},
|
||||
|
||||
@@ -355,6 +355,33 @@ local function MTH_EA_OPT_BuildUI(container)
|
||||
if quiverTip then quiverTip:SetWidth(290) end
|
||||
table.insert(rightGroup, quiverTip)
|
||||
|
||||
-- ── Rotation ─────────────────────────────────────────────
|
||||
table.insert(rightGroup, MTH_EA_OPT_Header(container, "Rotation", -400, OPT_R))
|
||||
|
||||
local rotTip = MTH_EA_OPT_Tip(container,
|
||||
"Uncheck a shot to permanently skip it during its proc window.\n The keybind will cast Steady/Aimed instead.",
|
||||
-418, OPT_R, 0.65, 0.65, 0.65)
|
||||
if rotTip then rotTip:SetWidth(290) end
|
||||
table.insert(rightGroup, rotTip)
|
||||
|
||||
local multiCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockMultiCB",
|
||||
"Multi-Shot (Explosive)",
|
||||
-452, OPT_R, cfg.blockExplosive ~= true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockExplosive(not v) end end)
|
||||
table.insert(rightGroup, multiCB)
|
||||
|
||||
local serpentCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockSerpentCB",
|
||||
"Serpent Sting (Poisonous)",
|
||||
-476, OPT_R, cfg.blockPoisonous ~= true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockPoisonous(not v) end end)
|
||||
table.insert(rightGroup, serpentCB)
|
||||
|
||||
local arcaneCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockArcaneCB",
|
||||
"Arcane Shot (Enchanted)",
|
||||
-500, OPT_R, cfg.blockEnchanted ~= true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockEnchanted(not v) end end)
|
||||
table.insert(rightGroup, arcaneCB)
|
||||
|
||||
-- Apply initial enabled state
|
||||
MTH_EA_OPT_SetGroupEnabled(rightGroup, showHint)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt: SmartPet Options Panel
|
||||
-- TWoW 1.18.1
|
||||
------------------------------------------------------
|
||||
|
||||
local function MTH_SP_OPT_Ready()
|
||||
return type(MTH_GetFrame) == "function"
|
||||
and type(MTH_CreateCheckbox) == "function"
|
||||
and type(MTH_CreateSlider) == "function"
|
||||
end
|
||||
|
||||
local function MTH_SP_OPT_GetCfg()
|
||||
if not MTH_SmartPet or not MTH_SmartPet.GetConfig then return {} end
|
||||
return MTH_SmartPet.GetConfig()
|
||||
end
|
||||
|
||||
local MTH_SP_OPT_built = false
|
||||
|
||||
-- Layout constants
|
||||
local OPT_L = 16 -- left column X
|
||||
local OPT_R = 290 -- right column X
|
||||
local OPT_CW = 250 -- column width (for tip text wrapping)
|
||||
|
||||
-- Spacing constants
|
||||
local GAP_SECTION = 34 -- between sections
|
||||
local GAP_CB = 24 -- checkbox height (icon + text)
|
||||
local GAP_TIP = 16 -- small tip text height
|
||||
local GAP_BTN = 28 -- button height + margin
|
||||
local GAP_SLIDER = 52 -- slider total height (label + track + value)
|
||||
local GAP_CB_SLIDER = 40 -- checkbox to slider (checkbox + slider label above track)
|
||||
|
||||
-- Helper: checkbox with click handler
|
||||
local function SP_CB(container, name, label, yOff, xOff, isChecked, onClick)
|
||||
local cb = MTH_CreateCheckbox(container, name, label, yOff, xOff or OPT_L)
|
||||
if cb then
|
||||
cb:SetChecked(isChecked and 1 or 0)
|
||||
cb:SetScript("OnClick", function()
|
||||
local v = this:GetChecked()
|
||||
onClick(v == 1 or v == true)
|
||||
end)
|
||||
end
|
||||
return cb
|
||||
end
|
||||
|
||||
-- Helper: section header label
|
||||
local function SP_Header(container, text, yOff, xOff)
|
||||
local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff)
|
||||
fs:SetText("|cffff9900" .. text .. "|r")
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Helper: small descriptive text
|
||||
local function SP_Tip(container, text, yOff, xOff)
|
||||
local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff)
|
||||
fs:SetWidth(OPT_CW)
|
||||
fs:SetJustifyH("LEFT")
|
||||
fs:SetTextColor(0.4, 0.6, 1.0)
|
||||
fs:SetText(text)
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Taunt Mode cycle
|
||||
local TAUNT_MODES = { "auto", "growl", "cower", "off" }
|
||||
local TAUNT_LABELS = {
|
||||
auto = "Auto (solo=Growl, tank=Cower)",
|
||||
growl = "Always Growl",
|
||||
cower = "Always Cower",
|
||||
off = "Off (manual)",
|
||||
}
|
||||
|
||||
local function SP_NextTauntMode(current)
|
||||
for i = 1, 4 do
|
||||
if TAUNT_MODES[i] == current then
|
||||
return TAUNT_MODES[math.mod(i, 4) + 1]
|
||||
end
|
||||
end
|
||||
return "auto"
|
||||
end
|
||||
|
||||
-- Channel cycle
|
||||
local CHANNELS = { "SAY", "PARTY", "RAID", "GUILD" }
|
||||
|
||||
local function SP_NextChannel(current)
|
||||
for i = 1, 4 do
|
||||
if CHANNELS[i] == current then
|
||||
return CHANNELS[math.mod(i, 4) + 1]
|
||||
end
|
||||
end
|
||||
return "SAY"
|
||||
end
|
||||
|
||||
-- Build the UI
|
||||
local function MTH_SP_OPT_BuildUI(container)
|
||||
if MTH_SP_OPT_built then return end
|
||||
MTH_SP_OPT_built = true
|
||||
|
||||
local cfg = MTH_SP_OPT_GetCfg()
|
||||
local api = MTH_SmartPet or {}
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Title (large, like ExpAmmo)
|
||||
---------------------------------------------------------------------------
|
||||
local title = container:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, -16)
|
||||
title:SetText("Smart Pet - Pet Combat Management")
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Module enable checkbox
|
||||
---------------------------------------------------------------------------
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("smartpet", false) and true or false
|
||||
end
|
||||
SP_CB(container, "MTH_SP_OPT_ModuleEnable", "Enable Smart Pet module",
|
||||
-46, OPT_L, moduleEnabled, function(v)
|
||||
if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("smartpet", v) end
|
||||
end)
|
||||
|
||||
-- Columns start below the module checkbox
|
||||
local COL_TOP = -80
|
||||
|
||||
-- Vertical divider
|
||||
local divider = container:CreateTexture(nil, "BACKGROUND")
|
||||
divider:SetTexture(0.3, 0.3, 0.3, 0.6)
|
||||
divider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R - 14, COL_TOP)
|
||||
divider:SetPoint("BOTTOMLEFT", container, "TOPLEFT", OPT_R - 14, -530)
|
||||
divider:SetWidth(1)
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- LEFT COLUMN — Focus Management
|
||||
---------------------------------------------------------------------------
|
||||
local y = COL_TOP
|
||||
|
||||
SP_Header(container, "Focus Management", y)
|
||||
y = y - 22
|
||||
|
||||
-- Smart Focus
|
||||
SP_CB(container, "MTH_SP_OPT_SmartFocus", "Enable Smart Focus",
|
||||
y, OPT_L, cfg.smartFocus, function(v)
|
||||
if api.SetSmartFocus then api.SetSmartFocus(v) end
|
||||
end)
|
||||
y = y - GAP_CB
|
||||
SP_Tip(container, "Budget pet focus across DPS abilities.", y)
|
||||
y = y - GAP_TIP
|
||||
SP_Tip(container, "|cff80ff80High focus|r — all DPS abilities ON.", y)
|
||||
y = y - GAP_TIP
|
||||
SP_Tip(container, "|cffffcc00Mid focus|r — expensive ability first, others wait.", y)
|
||||
y = y - GAP_TIP
|
||||
SP_Tip(container, "|cffff8080Low focus|r — cheapest ability always ON (never idle).", y)
|
||||
y = y - GAP_TIP
|
||||
SP_Tip(container, "Taunt cost is always reserved.", y)
|
||||
y = y - GAP_SECTION
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- LEFT COLUMN — Taunt Management
|
||||
---------------------------------------------------------------------------
|
||||
SP_Header(container, "Taunt Management", y)
|
||||
y = y - GAP_TIP
|
||||
SP_Tip(container, "Controls Growl/Cower autocast based on party.", y)
|
||||
y = y - 22
|
||||
|
||||
local tauntBtn = CreateFrame("Button", "MTH_SP_OPT_TauntBtn", container, "UIPanelButtonTemplate")
|
||||
tauntBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, y)
|
||||
tauntBtn:SetWidth(250)
|
||||
tauntBtn:SetHeight(22)
|
||||
tauntBtn:SetText("Mode: " .. (TAUNT_LABELS[cfg.tauntMode] or "Auto"))
|
||||
tauntBtn:SetScript("OnClick", function()
|
||||
local c = MTH_SP_OPT_GetCfg()
|
||||
local newMode = SP_NextTauntMode(c.tauntMode)
|
||||
if api.SetTauntMode then api.SetTauntMode(newMode) end
|
||||
this:SetText("Mode: " .. (TAUNT_LABELS[newMode] or newMode))
|
||||
end)
|
||||
y = y - GAP_BTN
|
||||
|
||||
SP_CB(container, "MTH_SP_OPT_PvpDetaunt", "PVP Auto-Detaunt",
|
||||
y, OPT_L, cfg.pvpDetaunt, function(v)
|
||||
if api.SetPvpDetaunt then api.SetPvpDetaunt(v) end
|
||||
end)
|
||||
y = y - GAP_SECTION
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- LEFT COLUMN — CC-Break Prevention
|
||||
---------------------------------------------------------------------------
|
||||
SP_Header(container, "CC-Break Prevention", y)
|
||||
y = y - 22
|
||||
|
||||
SP_CB(container, "MTH_SP_OPT_CcBreak", "Check for breakable CC",
|
||||
y, OPT_L, cfg.ccBreakCheck, function(v)
|
||||
if api.SetCcBreakCheck then api.SetCcBreakCheck(v) end
|
||||
end)
|
||||
y = y - GAP_CB
|
||||
|
||||
local ccLabels = { block = "Block attack", warn = "Warn only" }
|
||||
local ccModeBtn = CreateFrame("Button", "MTH_SP_OPT_CcModeBtn", container, "UIPanelButtonTemplate")
|
||||
ccModeBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 20, y)
|
||||
ccModeBtn:SetWidth(180)
|
||||
ccModeBtn:SetHeight(20)
|
||||
ccModeBtn:SetText("CC Mode: " .. (ccLabels[cfg.ccBreakMode] or "Block"))
|
||||
ccModeBtn:SetScript("OnClick", function()
|
||||
local c = MTH_SP_OPT_GetCfg()
|
||||
local newMode = (c.ccBreakMode == "block") and "warn" or "block"
|
||||
if api.SetCcBreakMode then api.SetCcBreakMode(newMode) end
|
||||
this:SetText("CC Mode: " .. (ccLabels[newMode] or newMode))
|
||||
end)
|
||||
y = y - GAP_SECTION
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- RIGHT COLUMN — General
|
||||
---------------------------------------------------------------------------
|
||||
local ry = COL_TOP
|
||||
|
||||
-- The Button
|
||||
SP_Header(container, "The Button", ry, OPT_R)
|
||||
ry = ry - 22
|
||||
|
||||
SP_CB(container, "MTH_SP_OPT_TheButton", "Enable The Button keybind",
|
||||
ry, OPT_R, cfg.theButton, function(v)
|
||||
if api.SetTheButton then api.SetTheButton(v) end
|
||||
end)
|
||||
ry = ry - GAP_CB
|
||||
SP_Tip(container, "Enemy=attack, friendly=assist, none=recall.", ry, OPT_R)
|
||||
ry = ry - GAP_SECTION
|
||||
|
||||
-- Rush on Attack
|
||||
SP_CB(container, "MTH_SP_OPT_Rush", "Rush on initial attack",
|
||||
ry, OPT_R, cfg.rushOnAttack, function(v)
|
||||
if api.SetRushOnAttack then api.SetRushOnAttack(v) end
|
||||
end)
|
||||
ry = ry - GAP_CB
|
||||
SP_Tip(container, "Charge / Dash / Dive when pet is sent to attack.", ry, OPT_R)
|
||||
ry = ry - GAP_SECTION
|
||||
|
||||
-- NoChase
|
||||
SP_CB(container, "MTH_SP_OPT_NoChase", "NoChase (recall on mob flee)",
|
||||
ry, OPT_R, cfg.noChase, function(v)
|
||||
if api.SetNoChase then api.SetNoChase(v) end
|
||||
end)
|
||||
ry = ry - GAP_CB
|
||||
SP_Tip(container, "Recall pet when target attempts to flee.", ry, OPT_R)
|
||||
ry = ry - GAP_SECTION
|
||||
|
||||
-- AutoCower
|
||||
SP_Header(container, "AutoCower", ry, OPT_R)
|
||||
ry = ry - 22
|
||||
|
||||
SP_CB(container, "MTH_SP_OPT_AutoCower", "Enable AutoCower on low HP",
|
||||
ry, OPT_R, cfg.autoCower, function(v)
|
||||
if api.SetAutoCower then api.SetAutoCower(v) end
|
||||
end)
|
||||
ry = ry - GAP_CB_SLIDER
|
||||
|
||||
local acSlider = MTH_CreateSlider(container, "MTH_SP_OPT_AutoCowerSlider",
|
||||
"HP Threshold (%)", 5, 80, 5, ry)
|
||||
if acSlider then
|
||||
acSlider:ClearAllPoints()
|
||||
acSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
|
||||
acSlider:SetWidth(200)
|
||||
acSlider:SetValue(cfg.autoCowerPct or 30)
|
||||
local valText = getglobal("MTH_SP_OPT_AutoCowerSliderValue")
|
||||
if valText then valText:SetText(tostring(cfg.autoCowerPct or 30)) end
|
||||
acSlider.onChange = function(val)
|
||||
if api.SetAutoCowerPct then api.SetAutoCowerPct(val) end
|
||||
end
|
||||
end
|
||||
ry = ry - GAP_SLIDER
|
||||
|
||||
-- AutoWarn
|
||||
SP_Header(container, "AutoWarn", ry, OPT_R)
|
||||
ry = ry - 22
|
||||
|
||||
SP_CB(container, "MTH_SP_OPT_AutoWarn", "Warn when pet HP is low",
|
||||
ry, OPT_R, cfg.autoWarn, function(v)
|
||||
if api.SetAutoWarn then api.SetAutoWarn(v) end
|
||||
end)
|
||||
ry = ry - GAP_CB_SLIDER
|
||||
|
||||
local awSlider = MTH_CreateSlider(container, "MTH_SP_OPT_AutoWarnSlider",
|
||||
"HP Threshold (%)", 5, 60, 5, ry)
|
||||
if awSlider then
|
||||
awSlider:ClearAllPoints()
|
||||
awSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
|
||||
awSlider:SetWidth(200)
|
||||
awSlider:SetValue(cfg.autoWarnPct or 20)
|
||||
local valText = getglobal("MTH_SP_OPT_AutoWarnSliderValue")
|
||||
if valText then valText:SetText(tostring(cfg.autoWarnPct or 20)) end
|
||||
awSlider.onChange = function(val)
|
||||
if api.SetAutoWarnPct then api.SetAutoWarnPct(val) end
|
||||
end
|
||||
end
|
||||
ry = ry - GAP_SLIDER
|
||||
|
||||
local chanBtn = CreateFrame("Button", "MTH_SP_OPT_ChanBtn", container, "UIPanelButtonTemplate")
|
||||
chanBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
|
||||
chanBtn:SetWidth(180)
|
||||
chanBtn:SetHeight(22)
|
||||
chanBtn:SetText("Channel: " .. (cfg.autoWarnChannel or "SAY"))
|
||||
chanBtn:SetScript("OnClick", function()
|
||||
local c = MTH_SP_OPT_GetCfg()
|
||||
local newChan = SP_NextChannel(c.autoWarnChannel or "SAY")
|
||||
if api.SetAutoWarnChannel then api.SetAutoWarnChannel(newChan) end
|
||||
this:SetText("Channel: " .. newChan)
|
||||
end)
|
||||
ry = ry - GAP_CB
|
||||
SP_Tip(container, "Chat channel for low-HP warnings.", ry, OPT_R)
|
||||
end
|
||||
|
||||
-- Public setup function (called by options-shell.lua)
|
||||
function MTH_SetupSmartPetOptions()
|
||||
if not MTH_SP_OPT_Ready() then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsSmartPet")
|
||||
if not container then return end
|
||||
local ok, err = pcall(MTH_SP_OPT_BuildUI, container)
|
||||
if not ok then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[SmartPet Options] " .. tostring(err), "error")
|
||||
end
|
||||
MTH_SP_OPT_built = false
|
||||
end
|
||||
end
|
||||
@@ -1705,4 +1705,36 @@ function MTH_SetupGeneralOptions()
|
||||
ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsFoodHelp", foodHelp, -298)
|
||||
end
|
||||
|
||||
local bagsSection = ensureSection("MetaHuntGeneralBagsBox", "Bag Ammo Labels", topY - 372, 168, "right")
|
||||
if bagsSection then
|
||||
local bagLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBagLabelsToggle", "Add text to ammunitions in bags", -10,
|
||||
MTH_BagAmmoLabels_GetEnabled and MTH_BagAmmoLabels_GetEnabled() or false)
|
||||
if bagLabels then
|
||||
bagLabels:SetScript("OnClick", function()
|
||||
local enabled = MTH_ZB_IsChecked(this)
|
||||
if MTH_BagAmmoLabels_SetEnabled then MTH_BagAmmoLabels_SetEnabled(enabled) end
|
||||
end)
|
||||
end
|
||||
|
||||
local bankLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBankLabelsToggle", "Add text to ammunitions in bank", -36,
|
||||
MTH_BankAmmoLabels_GetEnabled and MTH_BankAmmoLabels_GetEnabled() or false)
|
||||
if bankLabels then
|
||||
bankLabels:SetScript("OnClick", function()
|
||||
local enabled = MTH_ZB_IsChecked(this)
|
||||
if MTH_BankAmmoLabels_SetEnabled then MTH_BankAmmoLabels_SetEnabled(enabled) end
|
||||
end)
|
||||
end
|
||||
|
||||
local dmgLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBagDmgToggle", "Show ammo damage (heat-coloured)", -62,
|
||||
MTH_BagAmmoDamage_GetEnabled and MTH_BagAmmoDamage_GetEnabled() or false)
|
||||
if dmgLabels then
|
||||
dmgLabels:SetScript("OnClick", function()
|
||||
local enabled = MTH_ZB_IsChecked(this)
|
||||
if MTH_BagAmmoDamage_SetEnabled then MTH_BagAmmoDamage_SetEnabled(enabled) end
|
||||
end)
|
||||
end
|
||||
|
||||
ensureHelpText(bagsSection, "MetaHuntGeneralBagDmgHelp", "Damage is heat-coloured from red (7.5) to green (20.5).", -88)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -10,6 +10,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 5,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 1.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[150] = 0,
|
||||
@@ -118,6 +119,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 15,
|
||||
["reqlevel"] = 10,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[150] = 0,
|
||||
@@ -275,6 +277,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 5,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 2,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[151] = 0,
|
||||
@@ -359,6 +362,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 15,
|
||||
["reqlevel"] = 10,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -494,6 +498,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 30,
|
||||
["reqlevel"] = 25,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -621,6 +626,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 30,
|
||||
["reqlevel"] = 25,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -736,18 +742,21 @@ MTH_DS_AmmoItems = {
|
||||
["name"] = "Feathered Arrow",
|
||||
["level"] = 35,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[3465] = {
|
||||
["name"] = "Exploding Shot",
|
||||
["level"] = 36,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[4960] = {
|
||||
["name"] = "Flash Pellet",
|
||||
["level"] = 7,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[5568] = {
|
||||
@@ -755,6 +764,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 18,
|
||||
["reqlevel"] = 13,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[2156] = 30.97,
|
||||
@@ -781,6 +791,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 10,
|
||||
["reqlevel"] = 5,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 2,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[8068] = {
|
||||
@@ -788,6 +799,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 20,
|
||||
["reqlevel"] = 15,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[8069] = {
|
||||
@@ -795,6 +807,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 35,
|
||||
["reqlevel"] = 30,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[9399] = {
|
||||
@@ -802,6 +815,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 40,
|
||||
["reqlevel"] = 35,
|
||||
["icon"] = "INV_Ammo_Arrow_01",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "arrow",
|
||||
["drops"] = {
|
||||
[6906] = 100,
|
||||
@@ -812,6 +826,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 42,
|
||||
["reqlevel"] = 37,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[10513] = {
|
||||
@@ -819,6 +834,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 49,
|
||||
["reqlevel"] = 44,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[10579] = {
|
||||
@@ -826,6 +842,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 42,
|
||||
["reqlevel"] = 37,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[11284] = {
|
||||
@@ -833,6 +850,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 45,
|
||||
["reqlevel"] = 40,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[734] = 0,
|
||||
@@ -934,6 +952,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 45,
|
||||
["reqlevel"] = 40,
|
||||
["icon"] = "INV_Weapon_ShortBlade_25",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[228] = 0,
|
||||
@@ -1043,6 +1062,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 52,
|
||||
["reqlevel"] = 47,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[9025] = 16,
|
||||
@@ -1053,6 +1073,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 59,
|
||||
["reqlevel"] = 54,
|
||||
["icon"] = "Ability_Hunter_CriticalShot",
|
||||
["dps"] = 20,
|
||||
["subtype"] = "arrow",
|
||||
["drops"] = {
|
||||
[9236] = 100,
|
||||
@@ -1063,6 +1084,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 61,
|
||||
["reqlevel"] = 56,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 20.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[10997] = 100,
|
||||
@@ -1073,6 +1095,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 57,
|
||||
["reqlevel"] = 52,
|
||||
["icon"] = "INV_Ammo_Bullet_03",
|
||||
["dps"] = 17.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[17078] = 0,
|
||||
@@ -1083,6 +1106,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 57,
|
||||
["reqlevel"] = 52,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 17.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[17078] = 0,
|
||||
@@ -1093,6 +1117,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 1,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 1.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[19316] = {
|
||||
@@ -1100,6 +1125,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 54,
|
||||
["reqlevel"] = 51,
|
||||
["icon"] = "Spell_Frost_IceShard",
|
||||
["dps"] = 14.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[13216] = 0,
|
||||
@@ -1113,6 +1139,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 54,
|
||||
["reqlevel"] = 51,
|
||||
["icon"] = "Spell_Frost_FrostBlast",
|
||||
["dps"] = 14.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[13216] = 0,
|
||||
|
||||
@@ -54,20 +54,27 @@ MTH_DS_ZoneNamesFallback = {
|
||||
[1519] = "Stormwind City",
|
||||
[1537] = "Ironforge",
|
||||
[1583] = "Blackrock Spire",
|
||||
[1584] = "Blackrock Depths",
|
||||
[1941] = "Caverns of Time",
|
||||
[1977] = "Zul'Gurub",
|
||||
[2040] = "Alah'Thalas",
|
||||
[2100] = "Maraudon",
|
||||
[2717] = "Molten Core",
|
||||
[2597] = "Alterac Valley",
|
||||
[3456] = "Naxxramas",
|
||||
[3457] = "Tower of Karazhan",
|
||||
[4012] = "Scarlet Enclave",
|
||||
[5023] = "Sunnyglade",
|
||||
[5024] = "Icepoint Rock",
|
||||
[5077] = "Crescent Grove",
|
||||
[5121] = "Tel'Abim",
|
||||
[5130] = "Winter Veil Vale",
|
||||
[5135] = "Scarlet Monastery Library",
|
||||
[5179] = "Gilneas",
|
||||
[5208] = "Gilneas City",
|
||||
[5225] = "Thalassian Highlands",
|
||||
[5536] = "Blackstone Island",
|
||||
[5723] = "Karazhan",
|
||||
[5561] = "Balor",
|
||||
[5581] = "Northwind",
|
||||
[5601] = "Dragonmaw Retreat",
|
||||
|
||||
@@ -216,12 +216,15 @@ MTH_MAP_WMA_TO_PFQ = {
|
||||
[607] = 1477, -- TheTempleOfAtalHakkar
|
||||
[609] = 719, -- BlackFathomDeeps
|
||||
[611] = 717, -- TheStockade
|
||||
[615] = 1584, -- BlackrockDepths (interior)
|
||||
[617] = 2717, -- MoltenCore
|
||||
[619] = 1977, -- ZulGurub
|
||||
[623] = 1584, -- BlackrockDepths
|
||||
[625] = 3429, -- RuinsofAhnQiraj
|
||||
[627] = 2159, -- OnyxiasLair
|
||||
[629] = 1583, -- BlackrockSpire
|
||||
[631] = 718, -- WailingCaverns (interior)
|
||||
[633] = 2100, -- Maraudon (interior)
|
||||
[635] = 2677, -- BlackwingLair
|
||||
[637] = 5138, -- TheDeadmines
|
||||
[639] = 722, -- RazorfenDowns
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<Include file="..\api\options-profiles.lua"/>
|
||||
<Include file="..\api\options-shell.lua"/>
|
||||
<Include file="..\api\options-zbuttons.lua"/>
|
||||
<Include file="..\api\bag-ammo-labels.lua"/>
|
||||
<Include file="..\api\options-feedomatic.lua"/>
|
||||
<Include file="..\api\hunterbook.xml"/>
|
||||
<Include file="..\api\hunterbook.lua"/>
|
||||
|
||||
@@ -91,6 +91,13 @@ local CELL_DEF = {
|
||||
icon = "Interface\\Icons\\Ability_TheBlackArrow" },
|
||||
}
|
||||
|
||||
-- ── Creature-type immunity rules ──────────────────────────────
|
||||
-- Maps UnitCreatureType("target") → set of ammo states the target resists.
|
||||
-- Used by PLAYER_TARGET_CHANGED to proactively flag blocked states.
|
||||
local CREATURE_TYPE_IMMUNITIES = {
|
||||
["Elemental"] = { [POISONOUS] = true }, -- Elementals are Nature-immune
|
||||
}
|
||||
|
||||
-- ── Talent gate ──────────────────────────────────────────────
|
||||
-- The module only shows if the player has the talent.
|
||||
-- Checked on login and whenever MTH_HT_OnScanComplete fires.
|
||||
@@ -304,11 +311,28 @@ local function MTH_EA_EnsureConfigDefaults()
|
||||
if cfg.bigCD == nil then cfg.bigCD = false end
|
||||
if cfg.hideOOC == nil then cfg.hideOOC = false end
|
||||
if cfg.useQuiverNoClip == nil then cfg.useQuiverNoClip = false end
|
||||
if cfg.blockExplosive == nil then cfg.blockExplosive = false end
|
||||
if cfg.blockPoisonous == nil then cfg.blockPoisonous = false end
|
||||
if cfg.blockEnchanted == nil then cfg.blockEnchanted = false end
|
||||
end
|
||||
|
||||
-- Combat state flag (avoids needing InCombatLockdown, which doesn't exist in 1.12)
|
||||
local inCombat = false
|
||||
|
||||
-- ── Runtime immunity / block tracking ─────────────────────────
|
||||
-- Volatile: refreshed on PLAYER_TARGET_CHANGED and combat-log "immune" messages.
|
||||
local MTH_EA_BlockedStates = {} -- [state] = true if creature-type or reactive immunity
|
||||
|
||||
function MTH_EA_IsStateBlocked(state)
|
||||
if not state or state == IDLE then return false end
|
||||
local cfg = MTH_EA_GetConfig()
|
||||
if state == EXPLOSIVE and cfg.blockExplosive then return true end
|
||||
if state == POISONOUS and cfg.blockPoisonous then return true end
|
||||
if state == ENCHANTED and cfg.blockEnchanted then return true end
|
||||
if MTH_EA_BlockedStates[state] then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Throttle for PLAYER_AURAS_CHANGED: even with the fast texture path this event
|
||||
-- fires 10-50+/sec in combat, so cap processing at ~6/sec.
|
||||
local MTH_EA_AurasChangedLastTime = 0
|
||||
@@ -493,6 +517,13 @@ end
|
||||
-- Action button: what spell to show and what spell to cast right now.
|
||||
-- Returns displaySpell, icon, r, g, b, cooldownRemaining, castSpell
|
||||
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.
|
||||
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)
|
||||
end
|
||||
|
||||
if rt.state == EXPLOSIVE then
|
||||
local cd = MTH_EA_GetSpellCD(SPELL_MULTI)
|
||||
return SPELL_MULTI, ICON_MULTI, 1.00, 0.60, 0.05, cd, (cd > 0 and SPELL_STEADY or SPELL_MULTI)
|
||||
@@ -654,6 +685,11 @@ MTH_EA_UpdateUI = function()
|
||||
end
|
||||
cdTexts[i]:SetText("")
|
||||
end
|
||||
|
||||
-- Red border override for blocked (immune / rotation-disabled) states
|
||||
if MTH_EA_IsStateBlocked(def.state) then
|
||||
c:SetBackdropBorderColor(1, 0, 0, 0.85)
|
||||
end
|
||||
end
|
||||
|
||||
-- ── TOP cell: Lock and Load ───────────────────────────────
|
||||
@@ -1158,6 +1194,8 @@ frame:RegisterEvent("SPELLCAST_STOP")
|
||||
frame:RegisterEvent("SPELLCAST_FAILED")
|
||||
frame:RegisterEvent("SPELLCAST_INTERRUPTED")
|
||||
frame:RegisterEvent("ACTIONBAR_SLOT_CHANGED") -- invalidate Aimed Shot slot cache
|
||||
frame:RegisterEvent("PLAYER_TARGET_CHANGED") -- proactive creature-type immunity
|
||||
frame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE") -- reactive "immune" detection
|
||||
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if not MTH_EA_IsEnabled() then return end
|
||||
@@ -1304,6 +1342,34 @@ frame:SetScript("OnEvent", function()
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_TARGET_CHANGED" then
|
||||
MTH_EA_BlockedStates = {}
|
||||
if UnitExists("target") then
|
||||
local creatureType = UnitCreatureType("target")
|
||||
if creatureType and CREATURE_TYPE_IMMUNITIES[creatureType] then
|
||||
for st, _ in pairs(CREATURE_TYPE_IMMUNITIES[creatureType]) do
|
||||
MTH_EA_BlockedStates[st] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_EA_UpdateUI()
|
||||
return
|
||||
end
|
||||
|
||||
if event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
|
||||
local msg = arg1 or ""
|
||||
if string.find(msg, "immune", 1, true) then
|
||||
for state, spell in pairs(CONSUME_SPELL) do
|
||||
if string.find(msg, spell, 1, true) then
|
||||
MTH_EA_BlockedStates[state] = true
|
||||
MTH_EA_UpdateUI()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_REGEN_DISABLED" then
|
||||
-- Entering combat
|
||||
inCombat = true
|
||||
@@ -1537,5 +1603,20 @@ MTH_ExpAmmo = {
|
||||
local cfg = MTH_EA_GetConfig()
|
||||
cfg.useQuiverNoClip = v and true or false
|
||||
end,
|
||||
SetBlockExplosive = function(v)
|
||||
local cfg = MTH_EA_GetConfig()
|
||||
cfg.blockExplosive = v and true or false
|
||||
MTH_EA_UpdateUI()
|
||||
end,
|
||||
SetBlockPoisonous = function(v)
|
||||
local cfg = MTH_EA_GetConfig()
|
||||
cfg.blockPoisonous = v and true or false
|
||||
MTH_EA_UpdateUI()
|
||||
end,
|
||||
SetBlockEnchanted = function(v)
|
||||
local cfg = MTH_EA_GetConfig()
|
||||
cfg.blockEnchanted = v and true or false
|
||||
MTH_EA_UpdateUI()
|
||||
end,
|
||||
ToggleAnchor = MTH_EA_ToggleAnchor,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
<!--
|
||||
MetaHunt: SmartPet Module Loader
|
||||
-->
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="engine.lua"/>
|
||||
</Ui>
|
||||
@@ -219,7 +219,7 @@ function ZSpellButton_SetSize(parent, size, setChildren)
|
||||
local cdscale = 0.75 / 36
|
||||
if setChildren then
|
||||
local button
|
||||
for i=1, parent.count do
|
||||
for i=1, (parent.count or 0) do
|
||||
button = getglobal(parent.name..i)
|
||||
if button then
|
||||
button:SetHeight(size)
|
||||
|
||||
@@ -38,7 +38,6 @@ local zButtonAmmo_LastFullScanAt = 0
|
||||
local zButtonAmmo_MinUnknownBagScanInterval = 5.0
|
||||
local zButtonAmmo_EmptyScanStreak = 0
|
||||
local zButtonAmmo_LastEquippedAmmoSeen = nil
|
||||
local zButtonAmmo_GetKnownAmmoTypes
|
||||
|
||||
local function zButtonAmmo_BagHasAmmoNow(bagId)
|
||||
local bag = tonumber(bagId)
|
||||
@@ -117,7 +116,7 @@ zButtonAmmo_GetKnownAmmoTypes = function()
|
||||
return knownAmmo
|
||||
end
|
||||
|
||||
local zButtonAmmo_ShortArrowLabels = {
|
||||
zButtonAmmo_ShortArrowLabels = {
|
||||
[ARROWS_ROUGH] = "Rough",
|
||||
[ARROWS_SHARP] = "Sharp",
|
||||
[ARROWS_RAZOR] = "Razor",
|
||||
@@ -131,7 +130,7 @@ local zButtonAmmo_ShortArrowLabels = {
|
||||
[ARROWS_DOOMSHOT] = "Doom",
|
||||
}
|
||||
|
||||
local zButtonAmmo_ShortBulletLabels = {
|
||||
zButtonAmmo_ShortBulletLabels = {
|
||||
[BULLETS_LIGHT] = "Light",
|
||||
[BULLETS_CRAFTLIGHT] = "C.Light",
|
||||
[BULLETS_FLASH] = "Flash",
|
||||
@@ -151,7 +150,7 @@ local zButtonAmmo_ShortBulletLabels = {
|
||||
[BULLETS_ICETHREADED] = "Ice",
|
||||
}
|
||||
|
||||
local function zButtonAmmo_GetColorGradient(perc)
|
||||
function zButtonAmmo_GetColorGradient(perc)
|
||||
perc = perc > 1 and 1 or perc
|
||||
perc = perc < 0 and 0 or perc
|
||||
local r1, g1, b1, r2, g2, b2
|
||||
@@ -188,7 +187,7 @@ local function zButtonAmmo_EnsureShortLabelText(button)
|
||||
return button and button.ammoShortLabelText
|
||||
end
|
||||
|
||||
local function zButtonAmmo_GetQualityColor(quality)
|
||||
function zButtonAmmo_GetQualityColor(quality)
|
||||
local tableRef = MTH_ITEM_QUALITY_COLORS or ITEM_QUALITY_COLORS
|
||||
if tableRef then
|
||||
local q = tonumber(quality)
|
||||
|
||||
Reference in New Issue
Block a user