mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-21 23:26:57 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04e04a4853 | |||
| 6c007f2c17 | |||
| c4911ef9fe | |||
| 51d4c785fa |
+113
@@ -2,6 +2,119 @@
|
||||
|
||||
All notable changes to MetaHunt will be documented in this file.
|
||||
|
||||
|
||||
## [1.5.2] - [Unreleased]
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- **zTrack — Find Fish**: Added new spell Find Fish to the zTrack list of trackings.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **zCraft — Smelting**: Smeling was previously not appearing in zCraft because incorrectly hardcoded as "Mining", this is fixed.
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- **Beast spawn coordinates — custom-zone remap**: Reworked 63 beast coordinate sets in the datastore. The largest changes move placeholder vanilla-zone coords onto the proper Turtle WoW custom zones for **Grim Reaches**, **Northwind**, and **Balor**, and clean up prairie beasts that were incorrectly spilling into **Thunder Bluff**.
|
||||
|
||||
- **Beast spawn coordinates — Stonetalon recalibration**: Recalibrated several **Stonetalon Mountains** beast spawn clusters to match the Twow updated zone bounds and improve marker placement consistency.
|
||||
|
||||
- **MM Widget — ClickThrough **: The whole widget is now clickable through, and ALT-drag to reposition it is thus no longer possible
|
||||
|
||||
- **MM Widget — clearer bottom-cell display**: The bottom cell now shows the cooldown of the current shot you would use, and only shows `USE` when that shot is ready.
|
||||
|
||||
- **Feed-O-Matic — NO-BUFF quarantine disabled**: The mechanism that permanently blacklisted a food item per pet family after a failed feed (no buff detected within 2.5 s) has been disabled. Foods are no longer quarantined on a no-buff timeout — the feed will simply retry without banning the item. This avoids false positives caused by server lag or client timeouts.
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- **zCraft — new zButton for crafting professions**: A new `zCraft` button (Options → zButtons → zCraft) scans your spellbook and shows a child button for each crafting profession you know — Alchemy, Blacksmithing, Cooking, Disenchanting, Enchanting, Engineering, Fishing, First Aid, Herbalism, Jewelcrafting, Leatherworking, Mining (Smelting), Skinning, Survival, and Tailoring. Clicking a child opens the corresponding tradeskill window. Disabled by default.
|
||||
|
||||
- **zBar — unified draggable toolbar for all zButtons**: A new "zBar" tab (Options → zButtons → zBar) groups all enabled zButton bars into a single draggable anchor. Alt-drag the zBar to move the whole group together. Options: Horizontal or Vertical layout; which side children pop out (Up/Down for horizontal, Left/Right for vertical); spacing between buttons; anchor icon size; per-bar include/exclude toggle and drag-to-reorder. Disabled by default — existing layouts are untouched until you enable it.
|
||||
|
||||
- **zAspect / zTrack — Smart Parent**: A new "Smart Parent" checkbox in Options → zButtons → zAspect and zTrack. When enabled, the parent button automatically toggles between your 1st and 2nd spell: if the 1st is active, the parent shows the 2nd, and vice-versa. Perfect for PvP — put Track Hidden and Track Humanoids as your first two tracking spells, and the parent always shows the one you're *not* currently using. When disabled, the parent always shows the 1st spell. Enabled by default.
|
||||
|
||||
- **zPet — Smart Parent**: A new "Smart Parent" checkbox in Options → zButtons → zPet. When enabled, the parent button dynamically changes based on your pet's state: Revive Pet if dead, Mend Pet if hurt, Feed Pet if unhappy, or your default spell otherwise. When disabled, the parent always shows the 1st spell. Enabled by default.
|
||||
|
||||
- **zMounts / zCompanions — Random Parent**: A new "Random Parent" checkbox in Options → zButtons → zMounts and zCompanions. When enabled, a random child mount is picked as the parent icon on each login and after each use. Disabled by default.
|
||||
|
||||
- **zMounts — AQ Mount Filter**: A new "AQ Mount Filter" checkbox in Options → zButtons → zMounts. When enabled, Qiraji mounts are hidden outside AQ40, and only Qiraji mounts are shown inside AQ40. Enabled by default.
|
||||
|
||||
- **zAspect — NamPower support**: The Aspect button now listens to `NamPower` aura events when available, so aspect changes update faster and without the old polling delay.
|
||||
|
||||
- **MM Widget — NamPower support**: The widget now uses `NamPower` aura updates when available, which makes ammo proc tracking faster and more reliable. It still falls back to the old tracking method if NamPower is not installed.
|
||||
|
||||
- **MM Widget — keybind **: The MM Widget options panel now has a "Set Key" button. Press any key (with optional ALT/CTRL/SHIFT modifiers), or mouse button/wheel to bind the MM Widget action, turning it into a One-button rotation feature.
|
||||
|
||||
- **MM Widget — Quiver no-clip option**: New "Use Quiver no-clip casting" checkbox in the MM Widget options. When enabled and Quiver is installed, Aimed Shot, Steady Shot, and Multi-Shot of the Action bind are routed through Quiver's no-clip system to avoid clipping your auto-shot swing timer.
|
||||
|
||||
- **MM Widget — Anchor **: Since the widget is now clickable-through, a new anchor system was implemented to move the widget: toggling the anchor button in the options shows 4 drag handles (top, bottom, left, right) outside the widget edge. Any handle can be dragged to reposition the widget.
|
||||
|
||||
- **Hunter Book — Families stats modifiers**: The Families page now also shows `Health`, `Damage`, and `Armor` modifiers for every family. Those modifier values can be shown as raw multipliers or as `%` with a footer checkbox, and are color-coded for easier reading. Additionaly the footer recap now also shows the total number of families, and the Named/Coord counts are plugged on the new accurate server's data instead of old pfQuest data.
|
||||
|
||||
- **FOM — keybind**: The Feed-O-Matic "Set Key" button in options now also accepts modifiers and mouse.
|
||||
|
||||
- **/mth food command**: Added a new slash command to inspect Feed-O-Matic state, clear one item ban, or reset all food bans and quarantines.
|
||||
|
||||
- **AntiDaze — NamPower support**: Daze detection now uses NamPower's aura events when available, replacing chat-message parsing for faster and more reliable aspect cancellation.
|
||||
|
||||
- **Chronometer — NamPower support**: Timer bars now start and stop instantly via NamPower aura events when available. Bar durations are read directly from the NamPower aura array instead of guessed from spell data.
|
||||
|
||||
- **Feed-O-Matic — NamPower support**: Mount and buff detection (Shadowmeld, Feign Death, eating, etc.) now uses the NamPower aura array when available, replacing the old tooltip-scraping method.
|
||||
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Pet lifecycle — duplicate / ghost pets in Stable**: Reworked pet identity with GUID-based matching and fixed stable tracking so the same pet is no longer split into multiple rows after tame, rename, stable swap, or `/reload`. Old stale pets should also stop reappearing in the Stable tab as fake active/stabled entries.
|
||||
|
||||
- **Pet lifecycle — wrong taming info shown on old pets**: Fixed several cases where MetaHunt could invent tame data for pets that were only seen later through stable swaps or normal pet refreshes. Taming info is now only recorded from a real tame event, so old pets should no longer show fake tame location / tame date data.
|
||||
|
||||
- **Hunter Book — Stable tab now trusts real stable slots**: The Stable tab now shows the current pet plus the pets that are actually in your stable slots, instead of trusting stale saved slot data from older sessions.
|
||||
|
||||
- **Hunter Book — pet training kept on the correct pet**: Fixed cases where a pet could keep its row but lose its Training section after lifecycle repairs or row merges. Training/spellbook data now stays attached to the right pet.
|
||||
|
||||
- **Hunter Book — stable feed item icons**: Fixed broken food icons in the Stable tab feed summary, including cases where foods like `Roasted Quail` were showing as a red square or question mark.
|
||||
|
||||
- **Minimap markers moving with the player since 1.4.0**: Reworked minimap marker zone resolution and calibration so pins stay anchored to world locations again instead of sliding with player movement. The minimap path now uses exact current-zone lookup in pfQuest space, while the world map keeps the WMA-aware logic introduced for 1.18.1 zone support.
|
||||
|
||||
- **Chronometer — "Grow bars upward" forgotten after `/reload`**: Checking the "Grow bars upward" option and reloading the UI would revert the bars back to growing downward, even though the checkbox still appeared checked. Fixed.
|
||||
|
||||
- **Chronometer — bars appear below the anchor instead of above when "Grow bars upward" is enabled**: Even with "Grow bars upward" checked, the bars were still spawning a bit below the anchor and stacking upward from there, because of an existing issue in old `CandyBar` library. Fixed — bars now correctly start at the anchor and grow upward.
|
||||
|
||||
- **Chronometer — Aimed Shot tracker removed**: Chronometer no longer tracks Aimed Shot cooldowns. The MM Widget now owns that behavior, so Chronometer keeps its hunter timers focused on the remaining spell and proc bars.
|
||||
|
||||
- **MM Widget — Quiver no-clip covers Multi-Shot**: The Quiver no-clip option now explicitly includes Multi-Shot alongside Aimed Shot and Steady Shot.
|
||||
|
||||
- **MM Widget — stale NamPower proc state**: The widget now resyncs from the NamPower aura array after aura events, so it no longer keeps showing a proc after the debuff is gone.
|
||||
|
||||
- **Quiver no-clip macro — Steady Shot castbar not showing**: If you were using Quiver and its no-clip macro for your shots, the Quiver castbar for those shots would not appear with MetaHunt enabled. Fixed — the castbar now appears correctly when using the macro.
|
||||
|
||||
|
||||
## [1.4.1] - 2026-03-24
|
||||
|
||||
### Added
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
## Interface: 11200
|
||||
## Version: 1.4.1
|
||||
## 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.
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# MetaHunt
|
||||
|
||||
MetaHunt is a Turtle WoW Hunter toolkit.
|
||||
It is a modern modular addon compiling brand new features, and improving a few old hunter add-ons, that were broken on twow.
|
||||
It is a modern modular addon compiling brand new features, and improving a few old hunter add-ons that were broken on Twow.
|
||||
It provides a bunch of useful tools for all huntards, wheter they are still levelling, lone-wolf, or HL raider.
|
||||
|
||||
### Important notes
|
||||
- Altough Nampower isn't mandatory for most of the features, it is a must-have for some.
|
||||
- If you were using old versions of Feed-O-Matic, ICU, zHunterMod and HunterHelper, you dont need those with MetaHunt and you should disable them to avoid conflicts.
|
||||
- MetaHunt is fully compatible with Quiver and is not a replacement for it.
|
||||
|
||||
|
||||
## Twow Data
|
||||
|
||||
MetaHunt ships with large Turtle WOW datastores :
|
||||
@@ -39,8 +45,8 @@ Old zHunterMod addon on steroids. Fully compatible with Twow, and enhanced with
|
||||
- zPet : Intelligent collapsible pet Bar regrouping all pet spells and providing an intelligent button that always show you the right spell to use depending on the situation
|
||||
- zTrack: All your trackings in one collapsible bar
|
||||
- zAspect: All your trackings in one collapsible bar
|
||||
- zRanged : All the ranged weampons currently in your bags to swap them fast
|
||||
- But also zMounts, zToys and zCompanions.
|
||||
- zRanged : All the ranged weapons currently in your bags to swap them fast
|
||||
- But also zBar, zCrafts, zMounts, zToys and zCompanions.
|
||||
|
||||
## Feed-O-Matic
|
||||
|
||||
@@ -72,7 +78,6 @@ Display the model of all beasts in the world. Always know what you are going to
|
||||
The Book is the main GUI of MetaHunt, it allows to display the data constantly collected about you and your pet, and browse the addon's data to find anything a Hunter needs.
|
||||
|
||||
- Browse the MetaHunt datastores of NPCs (Beast, vendors, masters) and open the map to locate them like in pfQuest
|
||||
- Model viewer of all beasts in the game.
|
||||
- Track all info about the pet abilities that you know and don't know yet
|
||||
- Track all info about your stabled pets
|
||||
- Keep an history of all your pets after you abandon them or if they run away.
|
||||
|
||||
+46
-4
@@ -25,16 +25,21 @@ local function AntiDaze_RegisterCombatEvents()
|
||||
if not AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
if MTH and MTH.nampower then
|
||||
AntiDazeFrame:RegisterEvent("DEBUFF_ADDED_SELF")
|
||||
else
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
end
|
||||
end
|
||||
|
||||
local function AntiDaze_UnregisterCombatEvents()
|
||||
if not AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
AntiDazeFrame:UnregisterEvent("DEBUFF_ADDED_SELF")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
@@ -87,10 +92,31 @@ local function AntiDaze_ResolvePlayerFromMessage(msg)
|
||||
return nil
|
||||
end
|
||||
|
||||
local AntiDaze_DazeSpellIdCache = {} -- [spellId] = true/false
|
||||
|
||||
local function AntiDaze_IsDazeSpellId(spellId)
|
||||
local cached = AntiDaze_DazeSpellIdCache[spellId]
|
||||
if cached ~= nil then return cached end
|
||||
if type(GetSpellRecField) ~= "function" then return false end
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
if name and (name == "Daze" or name == "Dazed") then
|
||||
AntiDaze_DazeSpellIdCache[spellId] = true
|
||||
return true
|
||||
end
|
||||
AntiDaze_DazeSpellIdCache[spellId] = false
|
||||
return false
|
||||
end
|
||||
|
||||
local function AntiDaze_CancelRelevantAspect(isSelfDazed)
|
||||
local packName = _G["ZHUNTER_ASPECT_PACK"] or "Aspect of the Pack"
|
||||
local cheetahName = _G["ZHUNTER_ASPECT_CHEETAH"] or "Aspect of the Cheetah"
|
||||
|
||||
-- Always use GetPlayerBuff + tooltip scan for cancellation.
|
||||
-- The aura-array slot index from GetUnitField does NOT map 1:1
|
||||
-- to the sequential buff index that CancelPlayerBuff expects
|
||||
-- (empty slots create gaps), so we cannot use the NamPower
|
||||
-- array for the cancel call. NamPower is still used for daze
|
||||
-- DETECTION via DEBUFF_ADDED_SELF in the event handler.
|
||||
for i = 0, 32 do
|
||||
if GetPlayerBuff(i, "HELPFUL") < 0 then
|
||||
break
|
||||
@@ -118,6 +144,12 @@ if AntiDazeFrame then
|
||||
AntiDazeFrame:RegisterEvent("VARIABLES_LOADED")
|
||||
AntiDazeFrame:SetScript("OnEvent", function()
|
||||
if event == "VARIABLES_LOADED" then
|
||||
-- Class gate: only hunters use AntiDaze.
|
||||
if MTH and MTH.IsClassGateBlocked and MTH:IsClassGateBlocked() then
|
||||
AntiDazeFrame:UnregisterAllEvents()
|
||||
AntiDazeFrame:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
if AntiDaze_IsEnabled() then
|
||||
AntiDaze_RegisterCombatEvents()
|
||||
else
|
||||
@@ -126,6 +158,16 @@ if AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
|
||||
-- NamPower path: DEBUFF_ADDED_SELF fires with spellId in arg3
|
||||
if event == "DEBUFF_ADDED_SELF" then
|
||||
local spellId = tonumber(arg3)
|
||||
if spellId and AntiDaze_IsDazeSpellId(spellId) then
|
||||
AntiDaze_CancelRelevantAspect(true)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Legacy path: parse chat messages
|
||||
local player = AntiDaze_ResolvePlayerFromMessage(arg1)
|
||||
if player then
|
||||
local you = _G["ZHUNTER_YOU"] or "You"
|
||||
|
||||
@@ -267,6 +267,12 @@ local function AutoStrip_OnEvent()
|
||||
.. " playerInCombat=" .. tostring((type(UnitAffectingCombat) == "function" and UnitAffectingCombat("player")) and 1 or 0))
|
||||
end
|
||||
if event == "VARIABLES_LOADED" then
|
||||
-- Class gate: only hunters use AutoStrip.
|
||||
if MTH and MTH.IsClassGateBlocked and MTH:IsClassGateBlocked() then
|
||||
AutoStrip_Frame:UnregisterAllEvents()
|
||||
AutoStrip_Frame:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_Trace("VARIABLES_LOADED saved.autostrip=" .. tostring(saved["autostrip"] and 1 or 0)
|
||||
.. " saved.display=" .. tostring(saved["display"] and 1 or 0))
|
||||
|
||||
@@ -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
|
||||
+5
-540
@@ -1,543 +1,8 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt: Beast Lore Scanner
|
||||
-- TWoW 1.18.1 — "Nightmares of Ursol"
|
||||
-- MetaHunt: Beast Lore Scanner (removed)
|
||||
------------------------------------------------------
|
||||
-- Fires when the player channels Beast Lore on a target.
|
||||
-- Captures all available data and stores it in
|
||||
-- MTH_SavedVariables.beastLoreScan.entries (account-wide).
|
||||
--
|
||||
-- Dedup rule: skips recording if the same creature name
|
||||
-- already exists in this zone within 30 yards of current
|
||||
-- player position (uses ds-minimap-sizes for yard scale).
|
||||
-- A chat message is always printed either way.
|
||||
-- This file previously contained a feature that recorded
|
||||
-- unknown beasts to SavedVariables when scanned with
|
||||
-- Beast Lore. The feature has been removed; the static
|
||||
-- beast database (ds-beasts) is now the sole data source.
|
||||
------------------------------------------------------
|
||||
|
||||
local SPELL_BEAST_LORE = "Beast Lore"
|
||||
local DEDUP_YARDS = 30
|
||||
local FALLBACK_DEDUP_PCT = 0.015 -- ~1.5% of map width if zone size unknown
|
||||
|
||||
-- ── Tooltip probe ────────────────────────────────────────────
|
||||
|
||||
local MTH_BLS_Probe = nil
|
||||
local function MTH_BLS_EnsureProbe()
|
||||
if not MTH_BLS_Probe then
|
||||
MTH_BLS_Probe = CreateFrame("GameTooltip", "MTH_BLSProbe", UIParent, "GameTooltipTemplate")
|
||||
MTH_BLS_Probe:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
end
|
||||
return MTH_BLS_Probe
|
||||
end
|
||||
|
||||
-- Read left/right text lines from the probe (Lua 5.0 compatible: getglobal)
|
||||
local function MTH_BLS_GetProbeLines()
|
||||
local lines = {}
|
||||
for i = 1, 24 do
|
||||
local L = getglobal("MTH_BLSProbeTextLeft" .. i)
|
||||
local R = getglobal("MTH_BLSProbeTextRight" .. i)
|
||||
local lText = L and L:GetText() or nil
|
||||
local rText = R and R:GetText() or nil
|
||||
if (not lText or lText == "") and (not rText or rText == "") then
|
||||
break
|
||||
end
|
||||
table.insert(lines, { left = lText or "", right = rText or "" })
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
-- Parse Beast Lore tooltip lines into structured fields.
|
||||
-- Uses string.find with captures (Lua 5.0, no string.match).
|
||||
local function MTH_BLS_ParseTooltip(lines)
|
||||
local data = {
|
||||
tameable = nil, -- true / false / nil (unknown)
|
||||
diet = nil,
|
||||
abilities = nil,
|
||||
armor = nil,
|
||||
health = nil,
|
||||
minDmg = nil,
|
||||
maxDmg = nil,
|
||||
}
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
local L = line.left or ""
|
||||
local R = line.right or ""
|
||||
|
||||
-- Tameability ──────────────────────────────────────────
|
||||
if string.find(L, "Cannot Be Tamed", 1, true) or string.find(R, "Cannot Be Tamed", 1, true) then
|
||||
data.tameable = false
|
||||
elseif string.find(L, "Can Be Tamed", 1, true) or string.find(R, "Can Be Tamed", 1, true)
|
||||
or string.find(L, "Tameable", 1, true) or string.find(R, "Tameable", 1, true) then
|
||||
data.tameable = true
|
||||
end
|
||||
|
||||
-- Diet ─────────────────────────────────────────────────
|
||||
if not data.diet then
|
||||
local _, _, v = string.find(L, "^Diet:%s*(.*)")
|
||||
if not v then _, _, v = string.find(R, "^Diet:%s*(.*)") end
|
||||
if v and v ~= "" then data.diet = v end
|
||||
end
|
||||
|
||||
-- Tamed Abilities ──────────────────────────────────────
|
||||
if not data.abilities then
|
||||
local _, _, v = string.find(L, "^Tamed Abilities:%s+(.*)")
|
||||
if not v then _, _, v = string.find(R, "^Tamed Abilities:%s+(.*)") end
|
||||
if v and v ~= "" then data.abilities = v end
|
||||
end
|
||||
|
||||
-- Damage range ("107 - 133 Damage") ────────────────────
|
||||
if not data.minDmg then
|
||||
local _, _, lo, hi = string.find(L, "(%d+) %- (%d+) Damage")
|
||||
if not lo then _, _, lo, hi = string.find(R, "(%d+) %- (%d+) Damage") end
|
||||
if lo and hi then
|
||||
data.minDmg = tonumber(lo)
|
||||
data.maxDmg = tonumber(hi)
|
||||
end
|
||||
end
|
||||
|
||||
-- Armor ────────────────────────────────────────────────
|
||||
if not data.armor then
|
||||
local _, _, v = string.find(L, "Armor:%s*(%d+)")
|
||||
if not v then _, _, v = string.find(R, "Armor:%s*(%d+)") end
|
||||
if v then data.armor = tonumber(v) end
|
||||
end
|
||||
|
||||
-- Health ───────────────────────────────────────────────
|
||||
if not data.health then
|
||||
local _, _, v = string.find(L, "Health:%s*(%d+)")
|
||||
if not v then _, _, v = string.find(R, "Health:%s*(%d+)") end
|
||||
if v then data.health = tonumber(v) end
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
-- ── Location helpers ─────────────────────────────────────────
|
||||
|
||||
-- pfQuest AreaTable ID → WMA ID bridge (GetCurrentMapAreaID returns pfQ IDs)
|
||||
local MTH_PFQ_TO_WMA = {
|
||||
[1]=27,[3]=17,[4]=19,[8]=38,[10]=34,[11]=40,[12]=30,[14]=4,[15]=141,[16]=181,
|
||||
[17]=11,[25]=519,[28]=22,[33]=37,[36]=15,[38]=35,[40]=39,[41]=32,[44]=36,
|
||||
[45]=16,[46]=29,[47]=26,[51]=28,[85]=20,[130]=21,[139]=23,[141]=41,[148]=42,
|
||||
[209]=650,[215]=9,[267]=24,[331]=43,[357]=121,[361]=182,[400]=61,[405]=101,
|
||||
[406]=81,[408]=505,[409]=504,[440]=161,[490]=201,[491]=641,[493]=241,
|
||||
[616]=501,[618]=281,[717]=611,[718]=516,[719]=609,[721]=518,[722]=639,
|
||||
[796]=520,[876]=500,[1176]=605,[1337]=521,[1377]=261,[1477]=607,[1497]=382,
|
||||
[1519]=301,[1537]=341,[1581]=514,[1583]=629,[1584]=623,[1637]=321,[1638]=362,
|
||||
[1657]=381,[1769]=700,[1941]=523,[1977]=619,[2017]=652,[2040]=509,[2057]=648,
|
||||
[2100]=517,[2159]=627,[2257]=659,[2366]=663,[2437]=603,[2557]=689,[2597]=401,
|
||||
[2677]=635,[2717]=617,[3277]=443,[3358]=461,[3428]=654,[3429]=625,[3456]=667,
|
||||
[3457]=682,[3478]=522,[4012]=502,[5023]=503,[5024]=511,[5053]=680,[5077]=670,
|
||||
[5086]=674,[5087]=676,[5097]=678,[5103]=672,[5121]=507,[5130]=601,[5135]=644,
|
||||
[5136]=643,[5138]=637,[5147]=655,[5148]=668,[5153]=645,[5163]=646,[5179]=510,
|
||||
[5204]=662,[5208]=665,[5225]=513,[5536]=512,[5557]=683,[5561]=685,[5581]=686,
|
||||
[5601]=691,[5602]=684,[5628]=693,[5640]=701,[5641]=698,[5642]=699,[5722]=702,
|
||||
[5723]=657,[5731]=705,[5734]=704,
|
||||
}
|
||||
|
||||
local function MTH_BLS_GetZoneId()
|
||||
if type(SetMapToCurrentZone) == "function" then
|
||||
pcall(SetMapToCurrentZone)
|
||||
end
|
||||
if type(GetCurrentMapAreaID) == "function" then
|
||||
local ok, id = pcall(GetCurrentMapAreaID)
|
||||
if ok and tonumber(id) and tonumber(id) > 0 then
|
||||
local pfq = tonumber(id)
|
||||
return MTH_PFQ_TO_WMA[pfq] or pfq
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function MTH_BLS_GetPlayerCoords()
|
||||
if type(SetMapToCurrentZone) == "function" then
|
||||
pcall(SetMapToCurrentZone)
|
||||
end
|
||||
if type(GetPlayerMapPosition) ~= "function" then return nil, nil end
|
||||
local ok, x, y = pcall(GetPlayerMapPosition, "player")
|
||||
if ok and x and y and (x ~= 0 or y ~= 0) then return x, y end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Returns true if a beast with this name is already in the static MTH_DS_Beasts database.
|
||||
-- We skip SavedVariables recording for beasts already catalogued in the DB.
|
||||
local function MTH_BLS_IsInStaticDB(name)
|
||||
if type(MTH_DS_Beasts) ~= "table" or not name or name == "" then return false end
|
||||
for _, row in pairs(MTH_DS_Beasts) do
|
||||
if type(row) == "table" and row.name == name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- ── Storage ──────────────────────────────────────────────────
|
||||
|
||||
local function MTH_BLS_EnsureStore()
|
||||
if type(MTH_SavedVariables) ~= "table" then
|
||||
MTH_SavedVariables = {}
|
||||
end
|
||||
if type(MTH_SavedVariables.beastLoreScan) ~= "table" then
|
||||
MTH_SavedVariables.beastLoreScan = {}
|
||||
end
|
||||
if type(MTH_SavedVariables.beastLoreScan.entries) ~= "table" then
|
||||
MTH_SavedVariables.beastLoreScan.entries = {}
|
||||
end
|
||||
return MTH_SavedVariables.beastLoreScan
|
||||
end
|
||||
|
||||
-- ── Dedup ────────────────────────────────────────────────────
|
||||
|
||||
-- Returns the existing entry for a same-named beast in the same zone,
|
||||
-- plus whether any of its recorded coords is within DEDUP_YARDS of (x, y).
|
||||
local function MTH_BLS_FindEntry(name, zoneId, x, y)
|
||||
local store = MTH_BLS_EnsureStore()
|
||||
|
||||
-- Normalize: treat 0 as unknown (entries are stored with nil for unknown zones)
|
||||
if not zoneId or zoneId == 0 then zoneId = nil end
|
||||
|
||||
local mapW, mapH = nil, nil
|
||||
if type(MTH_DS_MinimapSizes) == "table" and zoneId then
|
||||
local sz = MTH_DS_MinimapSizes[zoneId]
|
||||
if type(sz) == "table" then
|
||||
mapW = tonumber(sz[1])
|
||||
mapH = tonumber(sz[2])
|
||||
end
|
||||
end
|
||||
|
||||
local function IsNearby(ex, ey)
|
||||
if not ex or not ey or not x or not y then return false end
|
||||
local dx = ex - x
|
||||
local dy = ey - y
|
||||
if mapW and mapH then
|
||||
return math.sqrt((dx * mapW)^2 + (dy * mapH)^2) < DEDUP_YARDS
|
||||
else
|
||||
-- No map size data: use a generous fallback (~5% of map width)
|
||||
return math.sqrt(dx * dx + dy * dy) < 0.05
|
||||
end
|
||||
end
|
||||
|
||||
local entries = store.entries
|
||||
for i = 1, table.getn(entries) do
|
||||
local e = entries[i]
|
||||
-- Match by name and zone. When either side has no zone, match on name alone
|
||||
-- (coords will still be checked below; cross-zone false positives are
|
||||
-- unlikely given players don't share coordinate spaces across zones).
|
||||
local zoneMatch = (e.zoneId == zoneId) or (not e.zoneId) or (not zoneId)
|
||||
if type(e) == "table"
|
||||
and e.name == name
|
||||
and zoneMatch
|
||||
then
|
||||
-- Check all recorded coords for this beast.
|
||||
if type(e.coords) == "table" then
|
||||
for j = 1, table.getn(e.coords) do
|
||||
local c = e.coords[j]
|
||||
if type(c) == "table" and IsNearby(c.x, c.y) then
|
||||
return e, true -- entry found, nearby coord found
|
||||
end
|
||||
end
|
||||
elseif IsNearby(e.x, e.y) then
|
||||
-- Legacy single-coord entry
|
||||
return e, true
|
||||
end
|
||||
return e, false -- entry found, but no coord nearby
|
||||
end
|
||||
end
|
||||
|
||||
return nil, false -- no entry for this beast in this zone
|
||||
end
|
||||
|
||||
-- ── Main scan ────────────────────────────────────────────────
|
||||
-- Per-session cooldown: prevents re-recording the same beast in the
|
||||
-- same zone within SCAN_COOLDOWN seconds, as a safety net on top of
|
||||
-- the coordinate-based dedup.
|
||||
local MTH_BLS_ScanCooldowns = {}
|
||||
local SCAN_COOLDOWN = 120 -- 2 minutes
|
||||
|
||||
local function MTH_BLS_Scan()
|
||||
-- Basic target guards
|
||||
if type(UnitExists) == "function" and not UnitExists("target") then return end
|
||||
if type(UnitIsPlayer) == "function" and UnitIsPlayer("target") then return end
|
||||
|
||||
local name = (type(UnitName) == "function") and UnitName("target") or nil
|
||||
if not name or name == "" then return end
|
||||
|
||||
-- ── Unit data ───────────────────────────────────────────
|
||||
local level = (type(UnitLevel) == "function") and UnitLevel("target") or nil
|
||||
local family = (type(UnitCreatureFamily) == "function") and UnitCreatureFamily("target") or nil
|
||||
local healthMax = (type(UnitHealthMax) == "function") and UnitHealthMax("target") or nil
|
||||
|
||||
local minDmg, maxDmg, mainSpeed = nil, nil, nil
|
||||
if type(UnitDamage) == "function" then
|
||||
local ok, a, b, _, _, spd = pcall(UnitDamage, "target")
|
||||
if ok then minDmg, maxDmg, mainSpeed = a, b, spd end
|
||||
end
|
||||
if not mainSpeed and type(UnitAttackSpeed) == "function" then
|
||||
local ok, spd = pcall(UnitAttackSpeed, "target")
|
||||
if ok then mainSpeed = spd end
|
||||
end
|
||||
|
||||
local guid = nil
|
||||
if type(UnitGUID) == "function" then
|
||||
local ok, g = pcall(UnitGUID, "target")
|
||||
if ok and g then guid = g end
|
||||
end
|
||||
|
||||
-- ── Location ────────────────────────────────────────────
|
||||
local zoneId = MTH_BLS_GetZoneId()
|
||||
local x, y = MTH_BLS_GetPlayerCoords()
|
||||
local zone = (type(GetZoneText) == "function") and GetZoneText() or ""
|
||||
local subzone = (type(GetSubZoneText) == "function") and GetSubZoneText() or ""
|
||||
|
||||
-- Session cooldown: hard block on re-scanning the same beast in same zone
|
||||
local cooldownKey = tostring(name) .. "|" .. tostring(zone)
|
||||
local lastScan = MTH_BLS_ScanCooldowns[cooldownKey]
|
||||
if lastScan and (GetTime() - lastScan) < SCAN_COOLDOWN then
|
||||
MTH:Log("|cff888888Beast Lore: " .. tostring(name) .. " scanned recently — skipped|r")
|
||||
return
|
||||
end
|
||||
|
||||
-- ── Tooltip probe ───────────────────────────────────────
|
||||
local probe = MTH_BLS_EnsureProbe()
|
||||
probe:ClearLines()
|
||||
local probeOk = pcall(function() probe:SetUnit("target") end)
|
||||
local parsed = { tameable = nil, diet = nil, abilities = nil, armor = nil, health = nil }
|
||||
if probeOk then
|
||||
local lines = MTH_BLS_GetProbeLines()
|
||||
parsed = MTH_BLS_ParseTooltip(lines)
|
||||
end
|
||||
|
||||
-- Tooltip values override unit API (Beast Lore reveals more accurate data)
|
||||
if parsed.minDmg then minDmg = parsed.minDmg end
|
||||
if parsed.maxDmg then maxDmg = parsed.maxDmg end
|
||||
if parsed.health then healthMax = parsed.health end
|
||||
|
||||
-- Fallback tameability: family present = tameable
|
||||
if parsed.tameable == nil then
|
||||
parsed.tameable = (family and family ~= "") and true or false
|
||||
end
|
||||
|
||||
-- ── Dedup / upsert ──────────────────────────────────────
|
||||
local function R3(v)
|
||||
if type(v) ~= "number" then return nil end
|
||||
return math.floor(v * 1000 + 0.5) / 1000
|
||||
end
|
||||
local function R2(v)
|
||||
if type(v) ~= "number" then return nil end
|
||||
return math.floor(v * 100 + 0.5) / 100
|
||||
end
|
||||
|
||||
local rx, ry = R3(x), R3(y)
|
||||
-- Mark scanned now (before storing) so any rapid re-trigger is blocked
|
||||
MTH_BLS_ScanCooldowns[cooldownKey] = GetTime()
|
||||
local existing, nearbyFound = MTH_BLS_FindEntry(name, zoneId, x, y)
|
||||
|
||||
if nearbyFound then
|
||||
-- Already recorded at this location — do nothing.
|
||||
MTH:Log("|cff888888Beast Lore: " .. tostring(name)
|
||||
.. " already recorded nearby — skipped|r")
|
||||
return
|
||||
end
|
||||
|
||||
local store = MTH_BLS_EnsureStore()
|
||||
|
||||
if existing then
|
||||
-- Known beast, new location — add coord to its list.
|
||||
if type(existing.coords) ~= "table" then
|
||||
-- Migrate legacy single coord into coords list.
|
||||
existing.coords = {}
|
||||
if existing.x or existing.y then
|
||||
table.insert(existing.coords, { x = existing.x, y = existing.y, timestamp = existing.timestamp })
|
||||
end
|
||||
existing.x = nil
|
||||
existing.y = nil
|
||||
end
|
||||
table.insert(existing.coords, { x = rx, y = ry, timestamp = (type(time) == "function") and time() or nil })
|
||||
existing.lastUpdated = (type(time) == "function") and time() or nil
|
||||
-- Update abilities/data if newly revealed by this cast.
|
||||
if parsed.abilities and parsed.abilities ~= "" then existing.abilities = parsed.abilities end
|
||||
|
||||
local coordStr = (rx and ry)
|
||||
and string.format(" (%.1f, %.1f)", rx * 100, ry * 100)
|
||||
or ""
|
||||
local abilitiesStr = (existing.abilities and existing.abilities ~= "")
|
||||
and (" |cff88ddff[" .. existing.abilities .. "]|r")
|
||||
or ""
|
||||
MTH:Log("Beast Lore: |cffffaa00" .. tostring(name) .. "|r lv" .. (existing.level and tostring(existing.level) or "?")
|
||||
.. " — new coord added" .. coordStr
|
||||
.. abilitiesStr
|
||||
.. " |cff888888[#" .. tostring(table.getn(store.entries)) .. " +" .. tostring(table.getn(existing.coords)) .. " loc]|r")
|
||||
return
|
||||
end
|
||||
|
||||
-- Skip recording if this beast is already in the static database.
|
||||
if MTH_BLS_IsInStaticDB(name) then
|
||||
MTH:Log("|cff888888Beast Lore: " .. tostring(name) .. " is already in the MetaHunt database — skipped|r")
|
||||
return
|
||||
end
|
||||
|
||||
-- New beast entirely.
|
||||
local entry = {
|
||||
name = name,
|
||||
guid = guid,
|
||||
level = level,
|
||||
family = family,
|
||||
tameable = parsed.tameable,
|
||||
minDmg = minDmg and math.floor(minDmg + 0.5) or nil,
|
||||
maxDmg = maxDmg and math.floor(maxDmg + 0.5) or nil,
|
||||
attackSpeed = mainSpeed and R2(mainSpeed) or nil,
|
||||
armor = parsed.armor or nil,
|
||||
health = healthMax or nil,
|
||||
diet = parsed.diet or nil,
|
||||
abilities = parsed.abilities or nil,
|
||||
coords = { { x = rx, y = ry, timestamp = (type(time) == "function") and time() or nil } },
|
||||
zoneId = (zoneId and zoneId > 0) and zoneId or nil,
|
||||
zone = (zone and zone ~= "") and zone or nil,
|
||||
subzone = (subzone and subzone ~= "") and subzone or nil,
|
||||
timestamp = (type(time) == "function") and time() or nil,
|
||||
}
|
||||
table.insert(store.entries, entry)
|
||||
|
||||
-- ── Chat confirmation ────────────────────────────────────
|
||||
local tameStr = parsed.tameable
|
||||
and "|cff33ff55Tameable|r"
|
||||
or "|cffff4444Cannot be Tamed|r"
|
||||
local famStr = (family and family ~= "")
|
||||
and ("|cffffcc00" .. family .. "|r")
|
||||
or "|cff888888unknown family|r"
|
||||
local lvlStr = level and tostring(level) or "?"
|
||||
local coordStr = (x and y)
|
||||
and string.format(" (%.1f, %.1f)", x * 100, y * 100)
|
||||
or ""
|
||||
local zoneStr = (zone and zone ~= "") and (", " .. zone .. coordStr) or coordStr
|
||||
local idx = table.getn(store.entries)
|
||||
|
||||
local abilitiesStr = ""
|
||||
if parsed.abilities and parsed.abilities ~= "" then
|
||||
abilitiesStr = " |cff88ddff[" .. parsed.abilities .. "]|r"
|
||||
end
|
||||
|
||||
MTH:Log("|cff00ff88YOU FOUND AND RECORDED A NEW BEAST!|r |cffffaa00" .. tostring(name) .. "|r lv" .. lvlStr
|
||||
.. " — " .. tameStr .. " • " .. famStr
|
||||
.. zoneStr
|
||||
.. abilitiesStr
|
||||
.. " |cff888888[#" .. tostring(idx) .. "]|r")
|
||||
end
|
||||
|
||||
-- ── Detection: event-gated tooltip poll ─────────────────────
|
||||
-- SPELLCAST_CHANNEL_* events do not reliably fire in TurtleWoW 1.18.1.
|
||||
-- Strategy: spellcast events open a short window (MTH_BLS_WindowEnd).
|
||||
-- OnUpdate only runs during that window, checking the target tooltip
|
||||
-- for Beast Lore-specific lines ("Diet:", "Tamed Abilities:").
|
||||
-- If no event fires at all, the window stays closed and nothing runs.
|
||||
|
||||
local MTH_BLS_Frame = CreateFrame("Frame", "MTH_BeastLoreScanFrame")
|
||||
local MTH_BLS_PollAccum = 0
|
||||
local MTH_BLS_WindowEnd = 0 -- GetTime() deadline; 0 = window closed
|
||||
local MTH_BLS_PollLast = nil -- target name already scanned this window
|
||||
|
||||
local WINDOW_DURATION = 6 -- seconds to probe after Beast Lore cast
|
||||
|
||||
local function MTH_BLS_OpenWindow()
|
||||
MTH_BLS_WindowEnd = GetTime() + WINDOW_DURATION
|
||||
MTH_BLS_PollLast = nil
|
||||
end
|
||||
|
||||
-- [DISABLED] Beast Lore scan activation — not needed while full beast DB is shipped.
|
||||
-- Re-enable by removing the `if false then` / `end` wrapper below.
|
||||
if false then
|
||||
|
||||
-- Register all known spellcast event variants.
|
||||
MTH_BLS_Frame:RegisterEvent("SPELLCAST_CHANNEL_START")
|
||||
MTH_BLS_Frame:RegisterEvent("SPELLCAST_CHANNEL_STOP")
|
||||
MTH_BLS_Frame:RegisterEvent("SPELLCAST_START")
|
||||
MTH_BLS_Frame:RegisterEvent("SPELLCAST_STOP")
|
||||
MTH_BLS_Frame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START")
|
||||
MTH_BLS_Frame:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
MTH_BLS_Frame:RegisterEvent("UNIT_SPELLCAST_START")
|
||||
MTH_BLS_Frame:RegisterEvent("UNIT_SPELLCAST_STOP")
|
||||
MTH_BLS_Frame:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
MTH_BLS_Frame:SetScript("OnEvent", function()
|
||||
local evt = event or ""
|
||||
|
||||
if evt == "PLAYER_TARGET_CHANGED" then
|
||||
-- New target: reset so re-targeting after a cast works.
|
||||
MTH_BLS_PollLast = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Determine spell name from event args.
|
||||
local isUnitEvt = string.sub(evt, 1, 15) == "UNIT_SPELLCAST_"
|
||||
local spellName
|
||||
if isUnitEvt then
|
||||
if (arg1 or "") ~= "player" then return end
|
||||
spellName = arg2 or ""
|
||||
else
|
||||
spellName = arg1 or ""
|
||||
end
|
||||
|
||||
if string.find(spellName, SPELL_BEAST_LORE, 1, true) then
|
||||
MTH_BLS_OpenWindow()
|
||||
end
|
||||
end)
|
||||
|
||||
MTH_BLS_Frame:SetScript("OnUpdate", function()
|
||||
MTH_BLS_PollAccum = MTH_BLS_PollAccum + (arg1 or 0)
|
||||
if MTH_BLS_PollAccum < 2.0 then return end
|
||||
MTH_BLS_PollAccum = 0
|
||||
|
||||
if not (type(UnitExists) == "function" and UnitExists("target")) then return end
|
||||
if type(UnitIsPlayer) == "function" and UnitIsPlayer("target") then return end
|
||||
|
||||
local name = (type(UnitName) == "function") and UnitName("target") or nil
|
||||
if not name or name == "" then return end
|
||||
if name == MTH_BLS_PollLast then return end
|
||||
|
||||
-- Probe the target tooltip for Beast Lore-specific lines.
|
||||
local probe = MTH_BLS_EnsureProbe()
|
||||
probe:ClearLines()
|
||||
local probeOk = pcall(function() probe:SetUnit("target") end)
|
||||
if not probeOk then return end
|
||||
|
||||
local hasData = false
|
||||
for i = 1, 20 do
|
||||
local L = getglobal("MTH_BLSProbeTextLeft" .. i)
|
||||
if not L then break end
|
||||
local txt = (L.GetText and L:GetText()) or ""
|
||||
if string.find(txt, "Diet:", 1, true)
|
||||
or string.find(txt, "Tamed Abilities:", 1, true)
|
||||
or string.find(txt, "Cannot Be Tamed", 1, true)
|
||||
then
|
||||
hasData = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not hasData then return end
|
||||
|
||||
-- Beast Lore data confirmed — record and close window.
|
||||
MTH_BLS_PollLast = name
|
||||
MTH_BLS_WindowEnd = 0
|
||||
local ok, err = pcall(MTH_BLS_Scan)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("[BeastLore Scan] error: " .. tostring(err), "error")
|
||||
end
|
||||
end)
|
||||
|
||||
end -- [DISABLED] if false
|
||||
|
||||
-- ── Public accessors ─────────────────────────────────────────
|
||||
|
||||
-- Returns the first saved entry whose name matches (case-insensitive),
|
||||
-- or nil if none found.
|
||||
function MTH_BLS_FindSavedBeastByName(name)
|
||||
local store = MTH_BLS_EnsureStore()
|
||||
if not store or type(store.entries) ~= "table" then return nil end
|
||||
local wantLower = string.lower(name or "")
|
||||
if wantLower == "" then return nil end
|
||||
for i = 1, table.getn(store.entries) do
|
||||
local e = store.entries[i]
|
||||
if type(e) == "table" and string.lower(e.name or "") == wantLower then
|
||||
return e
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
MTH_CONST = MTH_CONST or {}
|
||||
MTH_CONST.version = MTH_CONST.version or "1.4.1"
|
||||
MTH_CONST.version = MTH_CONST.version or "1.5.1"
|
||||
|
||||
MTH_CONST.WEAPON_TYPES = {
|
||||
BOWS = "Bows",
|
||||
|
||||
@@ -167,7 +167,7 @@ local function MTH_FEED_IndexBagFoods()
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_FEED_HasPetFeedBuff()
|
||||
function MTH_FEED_HasPetFeedBuff()
|
||||
if type(UnitBuff) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
+12
-2
@@ -1,5 +1,5 @@
|
||||
MTH = MTH or {
|
||||
version = "1.4.1",
|
||||
version = "1.5.1",
|
||||
name = "MetaHunt",
|
||||
modules = {},
|
||||
config = {},
|
||||
@@ -1311,7 +1311,17 @@ function MTH:AnnounceLoadComplete()
|
||||
moduleList = table.concat(enabled, ", ")
|
||||
end
|
||||
|
||||
self:Print("Check ignition: OK! Version " .. tostring(self.version or "unknown") .. " loaded.")
|
||||
local namPowerStatus = ""
|
||||
if type(GetNampowerVersion) == "function" then
|
||||
local maj, min = GetNampowerVersion()
|
||||
maj = tonumber(maj) or 0
|
||||
min = tonumber(min) or 0
|
||||
if maj > 2 or (maj == 2 and min >= 40) then
|
||||
namPowerStatus = " | NamPower detected"
|
||||
self.nampower = true
|
||||
end
|
||||
end
|
||||
self:Print("Check Ignition.... OK! Version " .. tostring(self.version or "unknown") .. " loaded." .. namPowerStatus)
|
||||
if self:IsMessageEnabled("initModulesLoaded", false) then
|
||||
self:Print("Modules enabled : " .. moduleList .. ".")
|
||||
end
|
||||
|
||||
+425
-103
@@ -4,6 +4,7 @@ end
|
||||
|
||||
local MTH_PETS_SCHEMA_VERSION = 2
|
||||
local MTH_PETS_CORE_HOOK_BOUNDARY_KEY = "core-pet-rename-hook"
|
||||
local MTH_ST_FULL_DEBUG_TRACE = false
|
||||
MTH_PETS_TRACE_CONSISTENCY = false
|
||||
|
||||
local MTH_PetLifecycleEventFrame = nil
|
||||
@@ -24,8 +25,8 @@ local MTH_ST_Profile = {
|
||||
runawayMaxMs = 0,
|
||||
}
|
||||
local MTH_ST_Trace = {
|
||||
enabled = false,
|
||||
verbose = false,
|
||||
enabled = MTH_ST_FULL_DEBUG_TRACE and true or false,
|
||||
verbose = MTH_ST_FULL_DEBUG_TRACE and true or false,
|
||||
windowStart = 0,
|
||||
windowSeconds = 5,
|
||||
eventCount = 0,
|
||||
@@ -36,7 +37,7 @@ local MTH_ST_Trace = {
|
||||
local MTH_ST_Diag = {
|
||||
lines = {},
|
||||
maxLines = 200,
|
||||
chatEcho = false,
|
||||
chatEcho = MTH_ST_FULL_DEBUG_TRACE and true or false,
|
||||
}
|
||||
local MTH_ST_DiagCopyFrame = nil
|
||||
local MTH_ST_PrintStableDiag
|
||||
@@ -268,21 +269,27 @@ local function MTH_ST_EnsureDiagCopyFrame()
|
||||
end
|
||||
|
||||
function MTH_CommandStableDiag(mode)
|
||||
local lowerMode = string.lower(tostring(mode or "on"))
|
||||
MTH_ST_Diag.lines = {}
|
||||
MTH_ST_Diag.chatEcho = false
|
||||
MTH_ST_Diag.chatEcho = (lowerMode ~= "off") and true or false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Stable diagnostics are disabled.")
|
||||
MTH:Print("Stable diagnostics chat echo " .. (MTH_ST_Diag.chatEcho and "enabled" or "disabled") .. ".", "debug")
|
||||
end
|
||||
return false
|
||||
return MTH_ST_Diag.chatEcho
|
||||
end
|
||||
|
||||
function MTH_CommandStableTrace(mode)
|
||||
MTH_ST_Trace.enabled = false
|
||||
MTH_ST_Trace.verbose = false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Stable trace is disabled.")
|
||||
local lowerMode = string.lower(tostring(mode or "verbose"))
|
||||
MTH_ST_Trace.enabled = (lowerMode ~= "off") and true or false
|
||||
MTH_ST_Trace.verbose = (lowerMode == "verbose" or lowerMode == "on") and MTH_ST_Trace.enabled or false
|
||||
if MTH_ST_Trace.enabled then
|
||||
MTH_ST_TraceResetCounters()
|
||||
end
|
||||
return false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Stable trace " .. (MTH_ST_Trace.enabled and "enabled" or "disabled")
|
||||
.. " (verbose=" .. tostring(MTH_ST_Trace.verbose) .. ").", "debug")
|
||||
end
|
||||
return MTH_ST_Trace.enabled
|
||||
end
|
||||
|
||||
local function MTH_ST_ProfileNow()
|
||||
@@ -418,18 +425,29 @@ local function MTH_ST_ProfileFlushIfDue(force)
|
||||
end
|
||||
|
||||
function MTH_CommandStableProfile(mode)
|
||||
MTH_ST_Profile.enabled = false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Stable profiler is disabled.")
|
||||
local lowerMode = string.lower(tostring(mode or "off"))
|
||||
MTH_ST_Profile.enabled = (lowerMode ~= "off") and true or false
|
||||
if MTH_ST_Profile.enabled then
|
||||
MTH_ST_ProfileResetCounters()
|
||||
end
|
||||
return false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Stable profiler " .. (MTH_ST_Profile.enabled and "enabled" or "disabled") .. ".", "debug")
|
||||
end
|
||||
return MTH_ST_Profile.enabled
|
||||
end
|
||||
|
||||
local function MTH_PETS_LogConsistency(line)
|
||||
return
|
||||
if not MTH_PETS_TRACE_CONSISTENCY then
|
||||
return
|
||||
end
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[PETS] " .. tostring(line or ""), "debug")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PETS_LogTame(_) end
|
||||
local function MTH_PETS_LogTame(_)
|
||||
return
|
||||
end
|
||||
|
||||
local function MTH_PETS_FormatRowConsistency(row)
|
||||
if type(row) ~= "table" then
|
||||
@@ -814,6 +832,30 @@ local function MTH_PETS_NormalizeSystemMessageKey(rawMessage)
|
||||
return message
|
||||
end
|
||||
|
||||
local function MTH_PETS_GetUnitGuid(unitToken)
|
||||
local token = tostring(unitToken or "")
|
||||
if token == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local getUnitGuid = (type(getglobal) == "function" and getglobal("GetUnitGUID")) or (_G and _G["GetUnitGUID"])
|
||||
if type(getUnitGuid) == "function" then
|
||||
local ok, guid = pcall(getUnitGuid, token)
|
||||
if ok and type(guid) == "string" and guid ~= "" then
|
||||
return guid
|
||||
end
|
||||
end
|
||||
|
||||
if type(UnitGUID) == "function" then
|
||||
local ok, guid = pcall(UnitGUID, token)
|
||||
if ok and type(guid) == "string" and guid ~= "" then
|
||||
return guid
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PETS_IsRunawaySystemMessage(rawMessage)
|
||||
local message = MTH_PETS_NormalizeSystemMessage(rawMessage)
|
||||
if message == "" then
|
||||
@@ -979,7 +1021,7 @@ local function MTH_PETS_CaptureTargetSnapshot()
|
||||
if MTH_PETS_IsPlaceholderPetName(targetName) then
|
||||
return nil
|
||||
end
|
||||
local targetGuid = (type(UnitGUID) == "function") and UnitGUID("target") or nil
|
||||
local targetGuid = MTH_PETS_GetUnitGuid("target")
|
||||
local beastId = MTH_PETS_ParseCreatureIdFromGuid(targetGuid)
|
||||
local targetFamily = (type(UnitCreatureFamily) == "function") and UnitCreatureFamily("target") or nil
|
||||
local targetLevel = (type(UnitLevel) == "function") and UnitLevel("target") or nil
|
||||
@@ -1193,6 +1235,22 @@ local function MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PETS_IsTameConfirmationSource(source)
|
||||
local sourceText = tostring(source or "")
|
||||
return sourceText == "unit-pet-acquire" or sourceText == "refresh-current-pet"
|
||||
end
|
||||
|
||||
local function MTH_PETS_ResolveConfirmedTameBeastId(source, snapshot)
|
||||
if not MTH_PETS_IsTameConfirmationSource(source) then
|
||||
return nil
|
||||
end
|
||||
local resolved = MTH_PETS_GetPendingTameBeastId(snapshot)
|
||||
if resolved and resolved > 0 then
|
||||
return resolved
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PETS_MakeSignature(name, family, level)
|
||||
local cleanName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name))
|
||||
local cleanFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family))
|
||||
@@ -1200,7 +1258,7 @@ local function MTH_PETS_MakeSignature(name, family, level)
|
||||
return cleanName .. "|" .. cleanFamily .. "|" .. tostring(numericLevel)
|
||||
end
|
||||
|
||||
local function MTH_PETS_RowHasTameRecord(row)
|
||||
local function MTH_PETS_RowHasAnyTameMetadata(row)
|
||||
if type(row) ~= "table" then
|
||||
return false
|
||||
end
|
||||
@@ -1219,11 +1277,33 @@ local function MTH_PETS_RowHasTameRecord(row)
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_PETS_HasVerifiedTameRecord(row)
|
||||
if type(row) ~= "table" then
|
||||
return false
|
||||
end
|
||||
if row.tameVerified == true then
|
||||
return true
|
||||
end
|
||||
local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil
|
||||
if legacyTameContext and (legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp) then
|
||||
return true
|
||||
end
|
||||
if type(row.events) == "table" then
|
||||
for i = 1, table.getn(row.events) do
|
||||
local ev = row.events[i]
|
||||
if type(ev) == "table" and tostring(ev.type or "") == "pet-tamed" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PETS_BackfillTameMetadataFromEvents(row)
|
||||
if type(row) ~= "table" then
|
||||
return false
|
||||
end
|
||||
if MTH_PETS_RowHasTameRecord(row) then
|
||||
if MTH_PETS_HasVerifiedTameRecord(row) then
|
||||
return false
|
||||
end
|
||||
if type(row.events) ~= "table" then
|
||||
@@ -1234,7 +1314,7 @@ local function MTH_PETS_BackfillTameMetadataFromEvents(row)
|
||||
local stableFirstSeenAt = tonumber(row.stableFirstSeenAt) or tonumber(row.stabledAt) or 0
|
||||
for i = 1, table.getn(row.events) do
|
||||
local ev = row.events[i]
|
||||
if type(ev) == "table" and tostring(ev.type or "") == "pet-acquired" then
|
||||
if type(ev) == "table" and tostring(ev.type or "") == "pet-tamed" then
|
||||
local evAt = tonumber(ev.at) or 0
|
||||
local timelineOk = true
|
||||
if stableFirstSeenAt > 0 and evAt > 0 and evAt > stableFirstSeenAt then
|
||||
@@ -1302,12 +1382,204 @@ local function MTH_PETS_BackfillTameMetadataFromEvents(row)
|
||||
|
||||
if changed then
|
||||
row.tameRecorded = true
|
||||
row.tameVerified = true
|
||||
row.lastUpdated = time()
|
||||
end
|
||||
|
||||
return changed
|
||||
end
|
||||
|
||||
local function MTH_PETS_ParseNumericPetId(petId)
|
||||
local _, _, numericId = string.find(tostring(petId or ""), "^pet%-(%d+)$")
|
||||
return tonumber(numericId)
|
||||
end
|
||||
|
||||
local function MTH_PETS_GetActiveRowRepairScore(pets, petId, row)
|
||||
if type(row) ~= "table" then
|
||||
return -1, 0
|
||||
end
|
||||
local score = 0
|
||||
local resolvedPetId = tostring(petId or "")
|
||||
local petStore = type(pets) == "table" and pets.petStore or nil
|
||||
if resolvedPetId ~= "" and tostring((pets and pets.currentPetId) or "") == resolvedPetId then
|
||||
score = score + 1000000
|
||||
end
|
||||
if resolvedPetId ~= "" and type(petStore) == "table" and tostring(petStore.activeCurrentId or "") == resolvedPetId then
|
||||
score = score + 900000
|
||||
end
|
||||
if tonumber(row.stableSlot) and tonumber(row.stableSlot) > 0 then
|
||||
score = score + 500000
|
||||
end
|
||||
if type(row.guid) == "string" and row.guid ~= "" then
|
||||
score = score + 100000
|
||||
end
|
||||
if row.tameVerified == true then
|
||||
score = score + 50000
|
||||
end
|
||||
if row.tameRecorded == true then
|
||||
score = score + 10000
|
||||
end
|
||||
score = score + (tonumber(row.lastSeen) or tonumber(row.lastUpdated) or 0)
|
||||
return score, tonumber(MTH_PETS_ParseNumericPetId(resolvedPetId)) or 0
|
||||
end
|
||||
|
||||
local function MTH_PETS_MergeActiveRows(pets, winnerId, loserId)
|
||||
if type(pets) ~= "table" or type(pets.petStore) ~= "table" then
|
||||
return false
|
||||
end
|
||||
local petStore = pets.petStore
|
||||
local winnerRow = type(petStore.activeById) == "table" and petStore.activeById[winnerId] or nil
|
||||
local loserRow = type(petStore.activeById) == "table" and petStore.activeById[loserId] or nil
|
||||
if type(winnerRow) ~= "table" or type(loserRow) ~= "table" or tostring(winnerId) == tostring(loserId) then
|
||||
return false
|
||||
end
|
||||
|
||||
local copyIfMissingFields = {
|
||||
"guid", "signature", "beastId", "name", "family", "level",
|
||||
"happiness", "loyalty", "loyaltyLevel", "xp", "xpMax", "xpPercent",
|
||||
"origin", "originContext", "previousName", "stableRaw", "stableInfo",
|
||||
"zone", "subZone", "x", "y", "hunterLevel", "lastSource",
|
||||
"tameHunterLevel", "tameZone", "tameSubZone", "tameX", "tameY",
|
||||
"tameBeastId", "tameContext",
|
||||
}
|
||||
for i = 1, table.getn(copyIfMissingFields) do
|
||||
local field = copyIfMissingFields[i]
|
||||
local winnerValue = winnerRow[field]
|
||||
if winnerValue == nil or winnerValue == "" then
|
||||
local loserValue = loserRow[field]
|
||||
if loserValue ~= nil and loserValue ~= "" then
|
||||
winnerRow[field] = loserValue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(winnerRow.petSpellbook) ~= "table" and type(loserRow.petSpellbook) == "table" then
|
||||
winnerRow.petSpellbook = loserRow.petSpellbook
|
||||
winnerRow.lastPetSpellbookSkipAt = winnerRow.lastPetSpellbookSkipAt or loserRow.lastPetSpellbookSkipAt
|
||||
winnerRow.lastPetSpellbookSkipSource = winnerRow.lastPetSpellbookSkipSource or loserRow.lastPetSpellbookSkipSource
|
||||
winnerRow.lastPetSpellbookSkipReason = winnerRow.lastPetSpellbookSkipReason or loserRow.lastPetSpellbookSkipReason
|
||||
end
|
||||
if type(winnerRow.abilities) ~= "table" then
|
||||
winnerRow.abilities = {}
|
||||
end
|
||||
if type(loserRow.abilities) == "table" then
|
||||
for token, ability in pairs(loserRow.abilities) do
|
||||
if winnerRow.abilities[token] == nil then
|
||||
winnerRow.abilities[token] = ability
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(winnerRow.events) ~= "table" then
|
||||
winnerRow.events = {}
|
||||
end
|
||||
if type(loserRow.events) == "table" then
|
||||
for i = 1, table.getn(loserRow.events) do
|
||||
table.insert(winnerRow.events, loserRow.events[i])
|
||||
end
|
||||
end
|
||||
|
||||
local winnerCreatedAt = tonumber(winnerRow.createdAt) or 0
|
||||
local loserCreatedAt = tonumber(loserRow.createdAt) or 0
|
||||
if winnerCreatedAt <= 0 or (loserCreatedAt > 0 and loserCreatedAt < winnerCreatedAt) then
|
||||
winnerRow.createdAt = loserCreatedAt > 0 and loserCreatedAt or winnerRow.createdAt
|
||||
end
|
||||
local winnerFirstSeen = tonumber(winnerRow.firstSeen) or 0
|
||||
local loserFirstSeen = tonumber(loserRow.firstSeen) or 0
|
||||
if winnerFirstSeen <= 0 or (loserFirstSeen > 0 and loserFirstSeen < winnerFirstSeen) then
|
||||
winnerRow.firstSeen = loserFirstSeen > 0 and loserFirstSeen or winnerRow.firstSeen
|
||||
end
|
||||
local winnerLastSeen = tonumber(winnerRow.lastSeen) or 0
|
||||
local loserLastSeen = tonumber(loserRow.lastSeen) or 0
|
||||
if loserLastSeen > winnerLastSeen then
|
||||
winnerRow.lastSeen = loserLastSeen
|
||||
end
|
||||
local winnerLastUpdated = tonumber(winnerRow.lastUpdated) or 0
|
||||
local loserLastUpdated = tonumber(loserRow.lastUpdated) or 0
|
||||
if loserLastUpdated > winnerLastUpdated then
|
||||
winnerRow.lastUpdated = loserLastUpdated
|
||||
end
|
||||
|
||||
if winnerRow.tameVerified ~= true and loserRow.tameVerified == true then
|
||||
winnerRow.tameVerified = true
|
||||
winnerRow.tameRecorded = true
|
||||
winnerRow.tamedAt = winnerRow.tamedAt or loserRow.tamedAt
|
||||
winnerRow.tameHunterLevel = winnerRow.tameHunterLevel or loserRow.tameHunterLevel
|
||||
winnerRow.tameZone = winnerRow.tameZone or loserRow.tameZone
|
||||
winnerRow.tameSubZone = winnerRow.tameSubZone or loserRow.tameSubZone
|
||||
winnerRow.tameX = winnerRow.tameX or loserRow.tameX
|
||||
winnerRow.tameY = winnerRow.tameY or loserRow.tameY
|
||||
winnerRow.tameBeastId = winnerRow.tameBeastId or loserRow.tameBeastId
|
||||
end
|
||||
|
||||
if (winnerRow.stableSlot == nil or tonumber(winnerRow.stableSlot) == nil or tonumber(winnerRow.stableSlot) <= 0)
|
||||
and tonumber(loserRow.stableSlot) and tonumber(loserRow.stableSlot) > 0 then
|
||||
winnerRow.stableSlot = tonumber(loserRow.stableSlot)
|
||||
end
|
||||
|
||||
if type(petStore.stableSlotIndex) == "table" then
|
||||
for slotNumber, mappedPetId in pairs(petStore.stableSlotIndex) do
|
||||
if tostring(mappedPetId or "") == tostring(loserId) then
|
||||
petStore.stableSlotIndex[slotNumber] = tostring(winnerId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tostring(petStore.activeCurrentId or "") == tostring(loserId) then
|
||||
petStore.activeCurrentId = tostring(winnerId)
|
||||
end
|
||||
if tostring(pets.currentPetId or "") == tostring(loserId) then
|
||||
pets.currentPetId = tostring(winnerId)
|
||||
end
|
||||
if type(pets.currentPet) == "table" and tostring(pets.currentPet.id or "") == tostring(loserId) then
|
||||
pets.currentPet.id = tostring(winnerId)
|
||||
end
|
||||
|
||||
petStore.activeById[loserId] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_PETS_RepairActiveRowsByGuid(pets)
|
||||
if type(pets) ~= "table" or type(pets.petStore) ~= "table" or type(pets.petStore.activeById) ~= "table" then
|
||||
return
|
||||
end
|
||||
local groupedByGuid = {}
|
||||
for petId, row in pairs(pets.petStore.activeById) do
|
||||
if type(row) == "table" and type(row.guid) == "string" and row.guid ~= "" then
|
||||
local guid = row.guid
|
||||
if type(groupedByGuid[guid]) ~= "table" then
|
||||
groupedByGuid[guid] = {}
|
||||
end
|
||||
table.insert(groupedByGuid[guid], tostring(petId))
|
||||
end
|
||||
end
|
||||
|
||||
for _, petIds in pairs(groupedByGuid) do
|
||||
if type(petIds) == "table" and table.getn(petIds) > 1 then
|
||||
table.sort(petIds, function(a, b)
|
||||
local rowA = pets.petStore.activeById[a]
|
||||
local rowB = pets.petStore.activeById[b]
|
||||
local scoreA, numericA = MTH_PETS_GetActiveRowRepairScore(pets, a, rowA)
|
||||
local scoreB, numericB = MTH_PETS_GetActiveRowRepairScore(pets, b, rowB)
|
||||
if scoreA ~= scoreB then
|
||||
return scoreA > scoreB
|
||||
end
|
||||
if numericA ~= numericB then
|
||||
return numericA < numericB
|
||||
end
|
||||
return tostring(a) < tostring(b)
|
||||
end)
|
||||
local winnerId = petIds[1]
|
||||
for i = 2, table.getn(petIds) do
|
||||
local loserId = petIds[i]
|
||||
if pets.petStore.activeById[winnerId] and pets.petStore.activeById[loserId] then
|
||||
MTH_PETS_MergeActiveRows(pets, winnerId, loserId)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
if type(pets.petStore) ~= "table" then
|
||||
pets.petStore = {}
|
||||
@@ -1334,9 +1606,36 @@ local function MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
if petStore.activeCurrentId == nil then
|
||||
petStore.activeCurrentId = nil
|
||||
end
|
||||
MTH_PETS_RepairActiveRowsByGuid(pets)
|
||||
petStore.signatureIndex = {}
|
||||
petStore.guidIndex = {}
|
||||
local highestPetNumericId = 0
|
||||
|
||||
for _, row in pairs(petStore.activeById) do
|
||||
for petId, row in pairs(petStore.activeById) do
|
||||
if type(row) == "table" then
|
||||
local resolvedPetId = tostring(petId)
|
||||
if row.id == nil or tostring(row.id) == "" then
|
||||
row.id = resolvedPetId
|
||||
end
|
||||
local numericPetId = MTH_PETS_ParseNumericPetId(resolvedPetId)
|
||||
if numericPetId and numericPetId > highestPetNumericId then
|
||||
highestPetNumericId = numericPetId
|
||||
end
|
||||
if row.tameVerified ~= true then
|
||||
local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil
|
||||
if legacyTameContext and (legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp) then
|
||||
row.tameVerified = true
|
||||
end
|
||||
end
|
||||
if type(row.signature) == "string" and row.signature ~= "" then
|
||||
if type(petStore.signatureIndex[row.signature]) ~= "table" then
|
||||
petStore.signatureIndex[row.signature] = {}
|
||||
end
|
||||
table.insert(petStore.signatureIndex[row.signature], resolvedPetId)
|
||||
end
|
||||
if type(row.guid) == "string" and row.guid ~= "" then
|
||||
petStore.guidIndex[row.guid] = resolvedPetId
|
||||
end
|
||||
if row.loyaltyLevel == nil and type(row.loyalty) == "string" then
|
||||
row.loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(row.loyalty)
|
||||
end
|
||||
@@ -1351,9 +1650,21 @@ local function MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
end
|
||||
row.stableInfo.loyalty = nil
|
||||
end
|
||||
if row.tameVerified ~= true and MTH_PETS_RowHasAnyTameMetadata(row) then
|
||||
row.tameRecorded = nil
|
||||
end
|
||||
MTH_PETS_BackfillTameMetadataFromEvents(row)
|
||||
end
|
||||
end
|
||||
if highestPetNumericId >= tonumber(petStore.nextId) then
|
||||
petStore.nextId = highestPetNumericId + 1
|
||||
end
|
||||
if petStore.activeCurrentId ~= nil and petStore.activeById[tostring(petStore.activeCurrentId)] == nil then
|
||||
petStore.activeCurrentId = nil
|
||||
end
|
||||
if pets.currentPetId ~= nil and petStore.activeById[tostring(pets.currentPetId)] == nil then
|
||||
pets.currentPetId = petStore.activeCurrentId
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PETS_EnsureCurrentPetSchema(pets)
|
||||
@@ -1727,10 +2038,10 @@ local function MTH_PETS_MakeSnapshotFromLivePet()
|
||||
xpPercent = math.floor((xp / xpMax) * 1000 + 0.5) / 10
|
||||
end
|
||||
end
|
||||
local petGuid = (type(UnitGUID) == "function") and UnitGUID("pet") or nil
|
||||
local petGuid = MTH_PETS_GetUnitGuid("pet")
|
||||
local beastId = MTH_PETS_ParseCreatureIdFromGuid(petGuid)
|
||||
if beastId == nil and type(UnitGUID) == "function" then
|
||||
beastId = MTH_PETS_ParseCreatureIdFromGuid(UnitGUID("target"))
|
||||
if beastId == nil then
|
||||
beastId = MTH_PETS_ParseCreatureIdFromGuid(MTH_PETS_GetUnitGuid("target"))
|
||||
end
|
||||
local signature = MTH_PETS_MakeSignature(petName, petFamily, petLevel)
|
||||
return {
|
||||
@@ -1759,15 +2070,28 @@ local function MTH_PETS_SelectPetIdBySnapshot(store, snapshot, previousPetId)
|
||||
if previousRow.guid and snapshot.guid and previousRow.guid == snapshot.guid then
|
||||
return previousPetId
|
||||
end
|
||||
if previousRow.signature and snapshot.signature and previousRow.signature == snapshot.signature then
|
||||
return previousPetId
|
||||
end
|
||||
end
|
||||
|
||||
if snapshot.guid and store.guidIndex[snapshot.guid] and store.activeById[store.guidIndex[snapshot.guid]] then
|
||||
return store.guidIndex[snapshot.guid]
|
||||
end
|
||||
|
||||
if snapshot.guid and snapshot.guid ~= "" then
|
||||
for petId, row in pairs(store.activeById or {}) do
|
||||
if type(row) == "table" and row.guid == snapshot.guid then
|
||||
store.guidIndex[snapshot.guid] = tostring(petId)
|
||||
return tostring(petId)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if previousPetId and store.activeById[previousPetId] then
|
||||
local previousRow = store.activeById[previousPetId]
|
||||
if previousRow.signature and snapshot.signature and previousRow.signature == snapshot.signature then
|
||||
return previousPetId
|
||||
end
|
||||
end
|
||||
|
||||
if snapshot.signature and snapshot.signature ~= "" then
|
||||
local list = store.signatureIndex[snapshot.signature]
|
||||
if type(list) == "table" then
|
||||
@@ -1831,21 +2155,17 @@ local function MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context)
|
||||
end
|
||||
row.guid = snapshot.guid or row.guid
|
||||
row.beastId = snapshot.beastId or row.beastId
|
||||
local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table"
|
||||
if hasPendingTame and (row.tameBeastId == nil or tonumber(row.tameBeastId) == nil) then
|
||||
local resolvedTameBeastId = MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
|
||||
if resolvedTameBeastId and resolvedTameBeastId > 0 then
|
||||
row.tameBeastId = resolvedTameBeastId
|
||||
end
|
||||
end
|
||||
if hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet") and type(context) == "table" then
|
||||
local confirmedTameBeastId = MTH_PETS_ResolveConfirmedTameBeastId(source, snapshot)
|
||||
if confirmedTameBeastId and type(context) == "table" then
|
||||
row.tameRecorded = true
|
||||
row.tameVerified = true
|
||||
row.tamedAt = row.tamedAt or context.timestamp
|
||||
row.tameHunterLevel = row.tameHunterLevel or context.hunterLevel
|
||||
row.tameZone = row.tameZone or context.zone
|
||||
row.tameSubZone = row.tameSubZone or context.subZone
|
||||
row.tameX = row.tameX or context.x
|
||||
row.tameY = row.tameY or context.y
|
||||
row.tameBeastId = row.tameBeastId or confirmedTameBeastId
|
||||
end
|
||||
row.signature = snapshot.signature
|
||||
row.lastUpdated = now
|
||||
@@ -1870,30 +2190,6 @@ local function MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context)
|
||||
row.hunterLevel = context.hunterLevel or row.hunterLevel
|
||||
end
|
||||
end
|
||||
if not isStableScan and type(context) == "table" then
|
||||
local hasTameRecord = (row.tameRecorded == true)
|
||||
or tonumber(row.tamedAt) ~= nil
|
||||
or tonumber(row.tameBeastId) ~= nil
|
||||
or (type(row.tameZone) == "string" and row.tameZone ~= "")
|
||||
if not hasTameRecord and hasPendingTame then
|
||||
local sourceText = tostring(source or "")
|
||||
if sourceText == "unit-pet-acquire" or sourceText == "refresh-current-pet" then
|
||||
row.tameRecorded = true
|
||||
row.tamedAt = row.tamedAt or context.timestamp
|
||||
row.tameHunterLevel = row.tameHunterLevel or context.hunterLevel
|
||||
row.tameZone = row.tameZone or context.zone
|
||||
row.tameSubZone = row.tameSubZone or context.subZone
|
||||
row.tameX = row.tameX or context.x
|
||||
row.tameY = row.tameY or context.y
|
||||
if tonumber(row.tameBeastId) == nil then
|
||||
local resolvedTameBeastId = MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
|
||||
if resolvedTameBeastId and resolvedTameBeastId > 0 then
|
||||
row.tameBeastId = resolvedTameBeastId
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if type(row.abilities) ~= "table" then
|
||||
row.abilities = {}
|
||||
end
|
||||
@@ -1962,18 +2258,6 @@ local function MTH_PETS_UpsertActivePetFromSnapshot(pets, snapshot, source, opti
|
||||
if created then
|
||||
row.origin = source or "unknown"
|
||||
row.originContext = context
|
||||
local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table"
|
||||
local shouldRecordTame = hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet")
|
||||
if shouldRecordTame then
|
||||
row.tamedAt = context.timestamp
|
||||
row.tameHunterLevel = context.hunterLevel
|
||||
row.tameZone = context.zone
|
||||
row.tameSubZone = context.subZone
|
||||
row.tameX = context.x
|
||||
row.tameY = context.y
|
||||
row.tameBeastId = snapshot.beastId
|
||||
row.tameRecorded = true
|
||||
end
|
||||
if type(options) == "table" and options.stableSlot then
|
||||
row.origin = "stable-slot"
|
||||
end
|
||||
@@ -2131,6 +2415,45 @@ local function MTH_PETS_RecordStableSlot(pets, slot, raw1, raw2, raw3, raw4, raw
|
||||
pets.updatedAt = time()
|
||||
end
|
||||
|
||||
local function MTH_PETS_ReconcileStableSlots(pets, slotCount)
|
||||
if type(pets) ~= "table" or type(pets.petStore) ~= "table" then
|
||||
return
|
||||
end
|
||||
local petStore = pets.petStore
|
||||
if type(petStore.activeById) ~= "table" or type(petStore.stableSlotIndex) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local authoritativeByPetId = {}
|
||||
for slotNumber, petId in pairs(petStore.stableSlotIndex) do
|
||||
local numericSlot = tonumber(slotNumber)
|
||||
local mappedPetId = tostring(petId or "")
|
||||
if numericSlot and numericSlot > 0 and mappedPetId ~= "" and type(petStore.activeById[mappedPetId]) == "table" then
|
||||
if not slotCount or numericSlot <= slotCount then
|
||||
authoritativeByPetId[mappedPetId] = numericSlot
|
||||
else
|
||||
petStore.stableSlotIndex[slotNumber] = nil
|
||||
end
|
||||
else
|
||||
petStore.stableSlotIndex[slotNumber] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for petId, row in pairs(petStore.activeById) do
|
||||
if type(row) == "table" then
|
||||
local resolvedPetId = tostring(petId)
|
||||
local authoritativeSlot = authoritativeByPetId[resolvedPetId]
|
||||
if authoritativeSlot then
|
||||
row.stableSlot = authoritativeSlot
|
||||
elseif tonumber(row.stableSlot) and tonumber(row.stableSlot) > 0 then
|
||||
row.stableSlot = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pets.updatedAt = time()
|
||||
end
|
||||
|
||||
function MTH_PETS_RecordCurrentPetLearnedAbility(abilityName, rankNumber, source)
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
if type(pets) ~= "table" then
|
||||
@@ -2661,13 +2984,16 @@ function MTH_PETS_RefreshCurrentPet()
|
||||
row.events = {}
|
||||
end
|
||||
|
||||
local eventType = "pet-acquired"
|
||||
if not (created and (not hadPetBeforeRefresh)) then
|
||||
if cp.id ~= nil and previousCurrentId ~= nil and cp.id ~= previousCurrentId then
|
||||
eventType = "pet-swapped"
|
||||
local eventType = nil
|
||||
local confirmedTameBeastId = MTH_PETS_ResolveConfirmedTameBeastId("refresh-current-pet", snapshot)
|
||||
if created and (not hadPetBeforeRefresh) then
|
||||
if confirmedTameBeastId then
|
||||
eventType = "pet-tamed"
|
||||
else
|
||||
eventType = nil -- routine update, don't record
|
||||
eventType = "pet-acquired"
|
||||
end
|
||||
elseif cp.id ~= nil and previousCurrentId ~= nil and cp.id ~= previousCurrentId then
|
||||
eventType = "pet-swapped"
|
||||
end
|
||||
|
||||
local eventContext = nil
|
||||
@@ -2681,26 +3007,6 @@ function MTH_PETS_RefreshCurrentPet()
|
||||
})
|
||||
end
|
||||
|
||||
if eventType == "pet-acquired" then
|
||||
local hasTameRecord = (row.tameRecorded == true)
|
||||
or (tonumber(row.tamedAt) and tonumber(row.tamedAt) > 0)
|
||||
or (type(row.tameZone) == "string" and row.tameZone ~= "")
|
||||
if not hasTameRecord and type(eventContext) == "table" then
|
||||
row.tameRecorded = true
|
||||
row.tamedAt = row.tamedAt or eventContext.timestamp or now
|
||||
row.tameHunterLevel = row.tameHunterLevel or eventContext.hunterLevel
|
||||
row.tameZone = row.tameZone or eventContext.zone
|
||||
row.tameSubZone = row.tameSubZone or eventContext.subZone
|
||||
row.tameX = row.tameX or eventContext.x
|
||||
row.tameY = row.tameY or eventContext.y
|
||||
if tonumber(row.tameBeastId) == nil then
|
||||
local resolvedTameBeastId = MTH_PETS_ResolveTameBeastIdForRow(row, snapshot)
|
||||
if resolvedTameBeastId and resolvedTameBeastId > 0 then
|
||||
row.tameBeastId = resolvedTameBeastId
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if hadPendingTame and cp.id and cp.id ~= previousCurrentId then
|
||||
@@ -3117,6 +3423,10 @@ function MTH_ST_Scan(reason)
|
||||
if currentPetId and type(petsRoot.petStore) == "table" and type(petsRoot.petStore.activeById) == "table" then
|
||||
local currentRow = petsRoot.petStore.activeById[currentPetId]
|
||||
if type(currentRow) == "table" then
|
||||
MTH_ST_PrintStableDiag("scan currentRow petId=" .. tostring(currentPetId)
|
||||
.. " guid='" .. tostring(currentRow.guid or "") .. "'"
|
||||
.. " signature='" .. tostring(currentRow.signature or "") .. "'"
|
||||
.. " beastId='" .. tostring(currentRow.beastId or "") .. "'")
|
||||
local currentIcon = tostring(c1 or "")
|
||||
if currentIcon ~= "" then
|
||||
currentRow.icon = currentIcon
|
||||
@@ -3141,6 +3451,8 @@ function MTH_ST_Scan(reason)
|
||||
MTH_ST_PrintStableDiag("scan slots " .. table.concat(slotDiagParts, " | "))
|
||||
end
|
||||
|
||||
MTH_PETS_ReconcileStableSlots(petsRoot, slotCount)
|
||||
|
||||
MTH_PETS_MarkStableVisited(petsRoot, tostring(reason or "stable-scan"))
|
||||
|
||||
MTH_PETS_RefreshCurrentPet()
|
||||
@@ -3308,7 +3620,11 @@ local function MTH_PETS_HandleUnitPetTransition(source)
|
||||
if type(row.events) ~= "table" then
|
||||
row.events = {}
|
||||
end
|
||||
local confirmedTameBeastId = MTH_PETS_ResolveConfirmedTameBeastId(transitionSource, snapshot)
|
||||
local eventType = (not hadPet) and "pet-acquired" or "pet-updated"
|
||||
if not hadPet and confirmedTameBeastId then
|
||||
eventType = "pet-tamed"
|
||||
end
|
||||
-- Only record lifecycle events (acquire, rename, abandon), not routine updates
|
||||
if eventType ~= "pet-updated" then
|
||||
table.insert(row.events, {
|
||||
@@ -3777,8 +4093,20 @@ function MTH_ST_HandleSpellcastEvent(evt, eventArg1, eventArg2)
|
||||
local isUnitSpellcast = (evt == "UNIT_SPELLCAST_START" or evt == "UNIT_SPELLCAST_STOP"
|
||||
or evt == "UNIT_SPELLCAST_FAILED" or evt == "UNIT_SPELLCAST_INTERRUPTED"
|
||||
or evt == "UNIT_SPELLCAST_CHANNEL_START" or evt == "UNIT_SPELLCAST_CHANNEL_STOP")
|
||||
local isSpellcast = isUnitSpellcast
|
||||
or evt == "SPELLCAST_START" or evt == "SPELLCAST_STOP"
|
||||
or evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED"
|
||||
or evt == "SPELLCAST_CHANNEL_START" or evt == "SPELLCAST_CHANNEL_STOP"
|
||||
if not isSpellcast then
|
||||
return false
|
||||
end
|
||||
local castSpellName = eventArg1
|
||||
local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table"
|
||||
MTH_PETS_LogTame("Spellcast evt=" .. tostring(evt)
|
||||
.. " arg1='" .. tostring(eventArg1 or "") .. "'"
|
||||
.. " arg2='" .. tostring(eventArg2 or "") .. "'"
|
||||
.. " unitCast=" .. tostring(isUnitSpellcast)
|
||||
.. " pending=" .. tostring(hasPendingTame))
|
||||
if isUnitSpellcast then
|
||||
if eventArg1 ~= "player" then
|
||||
return true
|
||||
@@ -4115,12 +4443,6 @@ local function MTH_ST_OnEvent(frame, evt, eventArg1, eventArg2)
|
||||
if isStableUiEvt then
|
||||
stableDiagStart = MTH_ST_ProfileNow()
|
||||
MTH_ST_PrintStableDiag("evt=" .. tostring(evt) .. " phase=begin")
|
||||
if MTH_ST_Trace and not MTH_ST_Trace.enabled then
|
||||
MTH_ST_Trace.enabled = true
|
||||
MTH_ST_Trace.verbose = false
|
||||
MTH_ST_TraceResetCounters()
|
||||
MTH_ST_PrintStableDiag("trace=auto-enabled")
|
||||
end
|
||||
end
|
||||
|
||||
MTH_ST_DispatchEvent(frame, evt, eventArg1, eventArg2)
|
||||
|
||||
+119
-9
@@ -312,7 +312,7 @@ function SlashCmdList.MTH(msg, editbox)
|
||||
local lowerMsg = string.lower(msg)
|
||||
local _, _, lowerCmd, lowerArg = string.find(lowerMsg, "^(%S+)%s*(.-)%s*$")
|
||||
if msg == "" then
|
||||
MTH:Print("Available: /mth options, /mth book")
|
||||
MTH:Print("Available: /mth options, /mth book, /mth food")
|
||||
elseif lowerMsg == "err" or lowerMsg == "errors" or lowerMsg == "debug" then
|
||||
if MTH_DebugFrame and MTH_DebugFrame.Toggle then
|
||||
MTH_DebugFrame:Toggle()
|
||||
@@ -422,18 +422,128 @@ function SlashCmdList.MTH(msg, editbox)
|
||||
MTH:Print("|cffaaffaaArmed.|r Open any vendor, trainer, or stable master to capture their ID.")
|
||||
end
|
||||
end
|
||||
elseif lowerCmd == "bls" then
|
||||
if lowerArg == "reset" then
|
||||
if type(MTH_SavedVariables) == "table" then
|
||||
MTH_SavedVariables.beastLoreScan = { entries = {} }
|
||||
elseif lowerCmd == "food" then
|
||||
local sub = lowerArg
|
||||
local function MTH_FoodItemLabel(itemId)
|
||||
local id = tonumber(itemId)
|
||||
if not id then return tostring(itemId) end
|
||||
if type(GetItemInfo) == "function" then
|
||||
local name = GetItemInfo(id)
|
||||
if name and name ~= "" then
|
||||
return "|cffffcc00" .. name .. "|r |cffaaaaaa(" .. id .. ")|r"
|
||||
end
|
||||
end
|
||||
MTH:Print("|cffff4444Beast Lore scan data wiped.|r All recorded beasts cleared.")
|
||||
else
|
||||
MTH:Print("Beast Lore Scan: |cffffff00/mth bls reset|r — wipe all recorded beast data")
|
||||
return "|cffaaaaaa" .. tostring(id) .. "|r"
|
||||
end
|
||||
if sub == "" or sub == "status" then
|
||||
-- Show quarantine and exception state
|
||||
local feedStore = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.feedTracking or nil
|
||||
local quarantine = feedStore and feedStore.fomQuarantine and feedStore.fomQuarantine.byFamily or nil
|
||||
local exceptions = feedStore and feedStore.exceptions and feedStore.exceptions.byItemId or nil
|
||||
MTH:Print("|cffffff00=== Feed-O-Matic Status ===|r")
|
||||
-- Quarantine
|
||||
local qCount = 0
|
||||
if type(quarantine) == "table" then
|
||||
for family, byItem in pairs(quarantine) do
|
||||
if type(byItem) == "table" then
|
||||
for itemId, _ in pairs(byItem) do
|
||||
qCount = qCount + 1
|
||||
MTH:Print(" |cffff8800Quarantine:|r " .. MTH_FoodItemLabel(itemId) .. " (family=" .. tostring(family) .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if qCount == 0 then MTH:Print(" Quarantine: |cff00ff00empty|r") end
|
||||
-- Core exceptions
|
||||
local eCount = 0
|
||||
if type(exceptions) == "table" then
|
||||
for itemId, _ in pairs(exceptions) do
|
||||
if type(itemId) == "number" then -- skip string-key dupes
|
||||
eCount = eCount + 1
|
||||
MTH:Print(" |cffff4444Exception block:|r " .. MTH_FoodItemLabel(itemId))
|
||||
end
|
||||
end
|
||||
end
|
||||
if eCount == 0 then MTH:Print(" Exception blocks: |cff00ff00none|r") end
|
||||
-- FOM_RemovedFoods
|
||||
local rCount = 0
|
||||
if type(FOM_RemovedFoods) == "table" then
|
||||
for diet, list in pairs(FOM_RemovedFoods) do
|
||||
if type(list) == "table" then
|
||||
for _, itemId in ipairs(list) do
|
||||
rCount = rCount + 1
|
||||
MTH:Print(" |cffaaaaaaRemoved food:|r " .. MTH_FoodItemLabel(itemId) .. " (diet=" .. tostring(diet) .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if rCount == 0 then MTH:Print(" Removed foods: |cff00ff00none|r") end
|
||||
MTH:Print("|cffaaaaaa/mth food unban <id>|r — unban one item |cffaaaaaa/mth food reset|r — clear all bans")
|
||||
elseif string.find(sub, "^unban%s") or sub == "unban" then
|
||||
local _, _, itemArg = string.find(lowerArg, "^unban%s+(%S+)")
|
||||
local itemId = tonumber(itemArg)
|
||||
if not itemId then
|
||||
MTH:Print("|cffff4444Usage:|r /mth food unban <itemId>")
|
||||
MTH:Print("Example: /mth food unban 8952")
|
||||
elseif type(FOM_ClearItemBans) ~= "function" then
|
||||
MTH:Print("|cffff4444Error:|r Feed-O-Matic not loaded.")
|
||||
else
|
||||
local label = MTH_FoodItemLabel(itemId)
|
||||
FOM_ClearItemBans(itemId)
|
||||
MTH:Print("|cff00ff00Unbanned " .. label .. "|r — removed from quarantine, exceptions and removed-foods list.")
|
||||
end
|
||||
elseif sub == "reset" then
|
||||
local feedStore = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.feedTracking or nil
|
||||
if feedStore then
|
||||
if feedStore.fomQuarantine then feedStore.fomQuarantine = { byFamily = {} } end
|
||||
if feedStore.exceptions then feedStore.exceptions = { byItemId = {} } end
|
||||
end
|
||||
if type(FOM_RemovedFoods) == "table" then
|
||||
for diet, _ in pairs(FOM_RemovedFoods) do FOM_RemovedFoods[diet] = {} end
|
||||
end
|
||||
if type(FOM_FEED_SCAN_CACHE) ~= "nil" then FOM_FEED_SCAN_CACHE = nil end
|
||||
MTH:Print("|cff00ff00All Feed-O-Matic bans and quarantines cleared.|r")
|
||||
else
|
||||
MTH:Print("|cffffff00/mth food|r — show quarantine/ban status")
|
||||
MTH:Print("|cffffff00/mth food unban <itemId>|r — remove one item from all bans")
|
||||
MTH:Print("|cffffff00/mth food reset|r — clear ALL bans and quarantines")
|
||||
end
|
||||
elseif lowerCmd == "zbar" and lowerArg == "debug" then
|
||||
-- Dump the live state of every zBar button to chat so we can see which one is the ghost.
|
||||
local p = function(s) MTH:Print("[zBar] " .. tostring(s), "debug") end
|
||||
local root = (type(MTH_ZH_GetSavedRoot) == "function") and MTH_ZH_GetSavedRoot() or ZHunterMod_Saved
|
||||
local zs = root and root["_zbar"]
|
||||
p("=== zBar debug ===")
|
||||
p("enabled=" .. tostring(zs and zs.enabled) .. " anchor=" .. tostring(getglobal("MTH_ZBar_Anchor") ~= nil))
|
||||
p("direction=" .. tostring(zs and zs.direction) .. " childexpand=" .. tostring(zs and zs.childexpand) .. " childarrange=" .. tostring(zs and zs.childarrange))
|
||||
local allButtons = {
|
||||
"zButtonAspect","zButtonAmmo","zButtonTrack","zButtonTrap",
|
||||
"zButtonPet","zButtonRanged","zButtonMounts","zButtonCompanions","zButtonToys",
|
||||
}
|
||||
for _, bname in ipairs(allButtons) do
|
||||
local btn = getglobal(bname)
|
||||
local shown = btn and btn.IsShown and btn:IsShown()
|
||||
local bsaved = root and type(root[bname]) == "table" and root[bname]
|
||||
local enabled = bsaved and bsaved["enabled"]
|
||||
local zvis = zs and zs.visible and zs.visible[bname]
|
||||
local x, y, point = "?", "?", "?"
|
||||
if btn and btn.GetPoint then
|
||||
local pt, _, _, bx, by = btn:GetPoint()
|
||||
point = tostring(pt)
|
||||
x = tostring(bx and math.floor(bx + 0.5) or "?")
|
||||
y = tostring(by and math.floor(by + 0.5) or "?")
|
||||
end
|
||||
local bmtname = btn and tostring(btn.name) or "nil"
|
||||
p(bname .. ": shown=" .. tostring(shown) ..
|
||||
" enabled=" .. tostring(enabled) ..
|
||||
" zbarVisible=" .. tostring(zvis) ..
|
||||
" btn.name=" .. bmtname ..
|
||||
" anchor=(" .. point .. " " .. x .. "," .. y .. ")")
|
||||
end
|
||||
p("=== end ===")
|
||||
else
|
||||
MTH:Print("Unknown command: " .. tostring(msg))
|
||||
MTH:Print("Available: /mth options, /mth book, /mth npcid, /mth bls reset")
|
||||
MTH:Print("Available: /mth options, /mth book, /mth npcid, /mth food")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+157
-23
@@ -2,13 +2,16 @@ if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.families = {
|
||||
headerLabel = "Families",
|
||||
columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet" },
|
||||
columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet", "HP", "dmg", "Arm" },
|
||||
columnLayout = {
|
||||
{ x = 10, width = 106, align = "LEFT" },
|
||||
{ x = 122, width = 52, align = "LEFT" },
|
||||
{ x = 176, width = 52, align = "LEFT" },
|
||||
{ x = 232, width = 364, align = "LEFT" },
|
||||
{ x = 532, width = 112, align = "LEFT" },
|
||||
{ x = 10, width = 100, align = "LEFT" },
|
||||
{ x = 114, width = 38, align = "LEFT" },
|
||||
{ x = 154, width = 38, align = "LEFT" },
|
||||
{ x = 196, width = 364, align = "LEFT" },
|
||||
{ x = 496, width = 102, align = "LEFT" },
|
||||
{ x = 602, width = 30, align = "CENTER" },
|
||||
{ x = 636, width = 36, align = "CENTER" },
|
||||
{ x = 674, width = 38, align = "CENTER" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,6 +29,28 @@ local function MTH_BOOKTAB_FamiliesTrim(value)
|
||||
return text
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FormatFamilyStat(value)
|
||||
local numeric = tonumber(value)
|
||||
if not numeric then
|
||||
return "-"
|
||||
end
|
||||
local colorPrefix = "|cFF4DA6FF"
|
||||
if numeric < 1 then
|
||||
colorPrefix = "|cFFFF4040"
|
||||
elseif numeric > 1 then
|
||||
colorPrefix = "|cFF40FF40"
|
||||
end
|
||||
local text = nil
|
||||
if MTH_BOOK_STATE and MTH_BOOK_STATE.familyStatsPercent == true then
|
||||
text = tostring(math.floor((numeric * 100) + 0.5)) .. "%"
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
text = string.format("%.3f", numeric)
|
||||
text = string.gsub(text, "(%..-)0+$", "%1")
|
||||
text = string.gsub(text, "%.$", "")
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_ShouldDisplayAllDiet(familyName)
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(MTH_BOOKTAB_FamiliesTrim(familyName))
|
||||
return token == "bears" or token == "boars"
|
||||
@@ -51,12 +76,41 @@ end
|
||||
function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
local results = {}
|
||||
local families = MTH_DS_Families
|
||||
local beasts = MTH_DS_Beasts
|
||||
if type(families) ~= "table" then
|
||||
return results
|
||||
end
|
||||
|
||||
local abilityCounts = {}
|
||||
local canonicalMap = {}
|
||||
local familyCounts = {}
|
||||
|
||||
if type(beasts) == "table" then
|
||||
for _, beastRow in pairs(beasts) do
|
||||
if type(beastRow) == "table" then
|
||||
local familyName = MTH_BOOKTAB_FamiliesTrim(beastRow.family)
|
||||
if familyName ~= "" then
|
||||
local counts = familyCounts[familyName]
|
||||
if not counts then
|
||||
counts = { named = 0, coords = 0 }
|
||||
familyCounts[familyName] = counts
|
||||
end
|
||||
counts.named = counts.named + 1
|
||||
|
||||
if type(beastRow.coords) == "table" then
|
||||
for coordIndex = 1, table.getn(beastRow.coords) do
|
||||
local coord = beastRow.coords[coordIndex]
|
||||
if type(coord) == "table" then
|
||||
if coord[1] ~= nil and coord[2] ~= nil then
|
||||
counts.coords = counts.coords + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" and type(familyRow.abilities) == "table" then
|
||||
@@ -82,6 +136,7 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
|
||||
for familyName, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" then
|
||||
local beastCounts = familyCounts[familyName] or nil
|
||||
MTH_BOOKTAB_FamiliesTrace("BUILD " .. tostring(familyName) .. " icon=" .. tostring(familyRow.icon))
|
||||
local abilities = {}
|
||||
local seen = {}
|
||||
@@ -171,6 +226,10 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
end)
|
||||
|
||||
local dietText = ""
|
||||
local stats = type(familyRow.stats) == "table" and familyRow.stats or nil
|
||||
local healthStat = stats and tonumber(stats.health) or nil
|
||||
local damageStat = stats and tonumber(stats.damage) or nil
|
||||
local armorStat = stats and tonumber(stats.armor) or nil
|
||||
if MTH_BOOKTAB_ShouldDisplayAllDiet(familyName) then
|
||||
dietText = "ALL"
|
||||
else
|
||||
@@ -178,7 +237,8 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
if type(familyRow.food) == "table" then
|
||||
for i = 1, table.getn(familyRow.food) do
|
||||
local food = MTH_BOOKTAB_FamiliesTrim(familyRow.food[i])
|
||||
if food ~= "" then
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(food)
|
||||
if food ~= "" and token ~= "raw fish" and token ~= "raw meat" then
|
||||
table.insert(dietItems, string.upper(string.sub(food, 1, 1)) .. string.sub(food, 2))
|
||||
end
|
||||
end
|
||||
@@ -186,16 +246,22 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b)
|
||||
end)
|
||||
end
|
||||
dietText = table.concat(dietItems, ", ")
|
||||
dietText = table.concat(dietItems, "|")
|
||||
end
|
||||
|
||||
table.insert(results, {
|
||||
family = tostring(familyName),
|
||||
icon = familyRow.icon or nil,
|
||||
named = tonumber(familyRow.named) or 0,
|
||||
coords = tonumber(familyRow.coords) or 0,
|
||||
named = beastCounts and (tonumber(beastCounts.named) or 0) or 0,
|
||||
coords = beastCounts and (tonumber(beastCounts.coords) or 0) or 0,
|
||||
abilities = abilities,
|
||||
dietText = dietText,
|
||||
healthStat = healthStat,
|
||||
healthText = MTH_BOOKTAB_FormatFamilyStat(healthStat),
|
||||
damageStat = damageStat,
|
||||
damageText = MTH_BOOKTAB_FormatFamilyStat(damageStat),
|
||||
armorStat = armorStat,
|
||||
armorText = MTH_BOOKTAB_FormatFamilyStat(armorStat),
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -253,31 +319,76 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
ui.frame:SetPoint("BOTTOMRIGHT", listParent, "BOTTOMRIGHT", -6, 6)
|
||||
ui.frame:Hide()
|
||||
|
||||
local statsText = getglobal("MTH_BOOK_StatsText")
|
||||
local statsParent = statsText and statsText:GetParent() or nil
|
||||
if statsParent then
|
||||
ui.percentCheckbox = CreateFrame("CheckButton", "MTH_BOOK_FamiliesPercentCheckbox", statsParent, "UICheckButtonTemplate")
|
||||
ui.percentCheckbox:SetFrameStrata(statsParent:GetFrameStrata())
|
||||
ui.percentCheckbox:SetFrameLevel((statsParent:GetFrameLevel() or 0) + 20)
|
||||
ui.percentCheckbox:ClearAllPoints()
|
||||
if statsText then
|
||||
ui.percentCheckbox:SetPoint("LEFT", statsText, "RIGHT", 260, 0)
|
||||
else
|
||||
ui.percentCheckbox:SetPoint("TOPLEFT", statsParent, "TOPLEFT", 538, -526)
|
||||
end
|
||||
ui.percentCheckbox.label = ui.percentCheckbox:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.percentCheckbox.label:SetPoint("LEFT", ui.percentCheckbox, "RIGHT", 4, 0)
|
||||
ui.percentCheckbox.label:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.percentCheckbox.label:SetText("Show modifiers in %")
|
||||
ui.percentCheckbox:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
MTH_BOOK_STATE.familyStatsPercent = this:GetChecked() == 1
|
||||
MTH_BOOKTAB_RenderFamiliesList()
|
||||
end)
|
||||
ui.percentCheckbox:Hide()
|
||||
end
|
||||
|
||||
ui.headerFamily = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerFamily:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 4, -2)
|
||||
ui.headerFamily:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerFamily:SetText("Family")
|
||||
|
||||
ui.headerNamed = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerNamed:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 122, -2)
|
||||
ui.headerNamed:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 114, -2)
|
||||
ui.headerNamed:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerNamed:SetText("Named")
|
||||
|
||||
ui.headerCoords = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerCoords:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 176, -2)
|
||||
ui.headerCoords:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 154, -2)
|
||||
ui.headerCoords:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerCoords:SetText("Coords")
|
||||
|
||||
ui.headerAbilities = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerAbilities:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 232, -2)
|
||||
ui.headerAbilities:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 196, -2)
|
||||
ui.headerAbilities:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerAbilities:SetText("Abilities")
|
||||
|
||||
ui.headerDiet = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 532, -2)
|
||||
ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 496, -2)
|
||||
ui.headerDiet:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerDiet:SetText("Diet")
|
||||
|
||||
ui.headerHP = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerHP:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 602, -2)
|
||||
ui.headerHP:SetWidth(30)
|
||||
ui.headerHP:SetJustifyH("CENTER")
|
||||
ui.headerHP:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerHP:SetText("HP")
|
||||
|
||||
ui.headerDmg = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerDmg:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 636, -2)
|
||||
ui.headerDmg:SetWidth(36)
|
||||
ui.headerDmg:SetJustifyH("CENTER")
|
||||
ui.headerDmg:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerDmg:SetText("dmg")
|
||||
|
||||
ui.headerArm = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerArm:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 674, -2)
|
||||
ui.headerArm:SetWidth(38)
|
||||
ui.headerArm:SetJustifyH("CENTER")
|
||||
ui.headerArm:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerArm:SetText("Arm")
|
||||
|
||||
ui.rows = {}
|
||||
for i = 1, 28 do
|
||||
local row = CreateFrame("Frame", nil, ui.frame)
|
||||
@@ -298,24 +409,39 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
|
||||
row.family = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.family:SetPoint("LEFT", row, "LEFT", 24, 0)
|
||||
row.family:SetWidth(88)
|
||||
row.family:SetWidth(82)
|
||||
row.family:SetJustifyH("LEFT")
|
||||
|
||||
row.named = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.named:SetPoint("LEFT", row, "LEFT", 122, 0)
|
||||
row.named:SetWidth(52)
|
||||
row.named:SetPoint("LEFT", row, "LEFT", 114, 0)
|
||||
row.named:SetWidth(38)
|
||||
row.named:SetJustifyH("LEFT")
|
||||
|
||||
row.coords = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.coords:SetPoint("LEFT", row, "LEFT", 176, 0)
|
||||
row.coords:SetWidth(52)
|
||||
row.coords:SetPoint("LEFT", row, "LEFT", 154, 0)
|
||||
row.coords:SetWidth(38)
|
||||
row.coords:SetJustifyH("LEFT")
|
||||
|
||||
row.diet = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.diet:SetPoint("LEFT", row, "LEFT", 532, 0)
|
||||
row.diet:SetWidth(112)
|
||||
row.diet:SetPoint("LEFT", row, "LEFT", 496, 0)
|
||||
row.diet:SetWidth(102)
|
||||
row.diet:SetJustifyH("LEFT")
|
||||
|
||||
row.hp = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.hp:SetPoint("LEFT", row, "LEFT", 602, 0)
|
||||
row.hp:SetWidth(30)
|
||||
row.hp:SetJustifyH("CENTER")
|
||||
|
||||
row.dmg = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.dmg:SetPoint("LEFT", row, "LEFT", 636, 0)
|
||||
row.dmg:SetWidth(36)
|
||||
row.dmg:SetJustifyH("CENTER")
|
||||
|
||||
row.arm = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.arm:SetPoint("LEFT", row, "LEFT", 674, 0)
|
||||
row.arm:SetWidth(38)
|
||||
row.arm:SetJustifyH("CENTER")
|
||||
|
||||
row.abilityButtons = {}
|
||||
for b = 1, 16 do
|
||||
local button = CreateFrame("Button", nil, row)
|
||||
@@ -379,9 +505,14 @@ function MTH_BOOKTAB_SetFamiliesUIVisible(visible)
|
||||
if detailParent then detailParent:Hide() end
|
||||
if detailText then detailText:Hide() end
|
||||
if openMapButton then openMapButton:Hide() end
|
||||
if ui.percentCheckbox then
|
||||
ui.percentCheckbox:SetChecked(MTH_BOOK_STATE and MTH_BOOK_STATE.familyStatsPercent == true and 1 or nil)
|
||||
ui.percentCheckbox:Show()
|
||||
end
|
||||
ui.frame:Show()
|
||||
else
|
||||
ui.frame:Hide()
|
||||
if ui.percentCheckbox then ui.percentCheckbox:Hide() end
|
||||
if listParent then listParent:Show() end
|
||||
if detailParent then detailParent:Show() end
|
||||
if detailText then detailText:Show() end
|
||||
@@ -411,9 +542,12 @@ function MTH_BOOKTAB_RenderFamiliesList()
|
||||
rowFrame.named:SetText(tostring(row.named or 0))
|
||||
rowFrame.coords:SetText(tostring(row.coords or 0))
|
||||
rowFrame.diet:SetText(tostring(row.dietText or "-"))
|
||||
rowFrame.hp:SetText(MTH_BOOKTAB_FormatFamilyStat(row.healthStat))
|
||||
rowFrame.dmg:SetText(MTH_BOOKTAB_FormatFamilyStat(row.damageStat))
|
||||
rowFrame.arm:SetText(MTH_BOOKTAB_FormatFamilyStat(row.armorStat))
|
||||
|
||||
local offsetX = 232
|
||||
local abilityMaxRight = 526
|
||||
local offsetX = 196
|
||||
local abilityMaxRight = 490
|
||||
for b = 1, table.getn(rowFrame.abilityButtons) do
|
||||
local button = rowFrame.abilityButtons[b]
|
||||
local ability = row.abilities and row.abilities[b] or nil
|
||||
|
||||
@@ -547,7 +547,75 @@ MTH_BOOK_NormalizeStableFoodLink = function(itemId, itemLink, itemName)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_BOOK_FindStableFoodBagIcon(itemId, itemName)
|
||||
local getContainerNumSlots = (type(getglobal) == "function" and getglobal("GetContainerNumSlots")) or (_G and _G["GetContainerNumSlots"])
|
||||
local getContainerItemLink = (type(getglobal) == "function" and getglobal("GetContainerItemLink")) or (_G and _G["GetContainerItemLink"])
|
||||
local getContainerItemInfo = (type(getglobal) == "function" and getglobal("GetContainerItemInfo")) or (_G and _G["GetContainerItemInfo"])
|
||||
if type(getContainerNumSlots) ~= "function" or type(getContainerItemLink) ~= "function" or type(getContainerItemInfo) ~= "function" then
|
||||
return nil
|
||||
end
|
||||
local wantedItemId = tonumber(itemId)
|
||||
local wantedName = string.lower(tostring(itemName or ""))
|
||||
for bag = 0, 4 do
|
||||
local slotCount = tonumber(getContainerNumSlots(bag)) or 0
|
||||
for slot = 1, slotCount do
|
||||
local link = getContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local matched = false
|
||||
if wantedItemId then
|
||||
local _, _, parsedId = string.find(tostring(link), "item:(%d+)")
|
||||
if tonumber(parsedId) == wantedItemId then
|
||||
matched = true
|
||||
end
|
||||
end
|
||||
if not matched and wantedName ~= "" then
|
||||
local _, _, parsedName = string.find(tostring(link), "|h%[([^%]]+)%]|h")
|
||||
if parsedName and string.lower(tostring(parsedName)) == wantedName then
|
||||
matched = true
|
||||
end
|
||||
end
|
||||
if matched then
|
||||
local bagTexture = getContainerItemInfo(bag, slot)
|
||||
if bagTexture and bagTexture ~= "" then
|
||||
if type(bagTexture) == "string" and string.find(bagTexture, "\\", 1, true) then
|
||||
return bagTexture
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_BOOK_ResolveStableFoodIcon(itemId, iconHint, itemName, itemLink)
|
||||
local function resolveTextureCandidate(value)
|
||||
if value == nil or value == "" then
|
||||
return nil
|
||||
end
|
||||
if type(value) == "number" then
|
||||
return nil
|
||||
end
|
||||
local text = tostring(value)
|
||||
if text == "" then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "|Hitem:", 1, true) or string.find(text, "^item:%d+") then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "^table:", 1, true) then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "\\", 1, true) then
|
||||
return text
|
||||
end
|
||||
local asNumber = tonumber(text)
|
||||
if asNumber then
|
||||
return nil
|
||||
end
|
||||
return "Interface\\Icons\\" .. text
|
||||
end
|
||||
|
||||
local linkText = tostring(itemLink or "")
|
||||
local linkItemId = nil
|
||||
if linkText ~= "" then
|
||||
@@ -559,104 +627,55 @@ local function MTH_BOOK_ResolveStableFoodIcon(itemId, iconHint, itemName, itemLi
|
||||
|
||||
local numericItemId = tonumber(itemId) or linkItemId
|
||||
|
||||
if linkText ~= "" and type(GetItemInfo) == "function" then
|
||||
local _, _, _, _, _, _, _, _, _, linkTexture = GetItemInfo(linkText)
|
||||
if linkTexture and linkTexture ~= "" then
|
||||
if type(linkTexture) == "number" then
|
||||
return linkTexture
|
||||
end
|
||||
if string.find(tostring(linkTexture), "\\", 1, true) then
|
||||
return tostring(linkTexture)
|
||||
end
|
||||
local asNumber = tonumber(tostring(linkTexture))
|
||||
if asNumber then
|
||||
return asNumber
|
||||
end
|
||||
return "Interface\\Icons\\" .. tostring(linkTexture)
|
||||
end
|
||||
end
|
||||
|
||||
if type(iconHint) == "number" then
|
||||
return iconHint
|
||||
end
|
||||
|
||||
local iconHintText = tostring(iconHint or "")
|
||||
if iconHintText ~= "" then
|
||||
local numericHint = tonumber(iconHintText)
|
||||
if numericHint then
|
||||
return numericHint
|
||||
end
|
||||
end
|
||||
|
||||
local iconPath = iconHintText
|
||||
if iconPath ~= "" then
|
||||
if string.find(iconPath, "\\", 1, true) then
|
||||
return iconPath
|
||||
end
|
||||
return "Interface\\Icons\\" .. iconPath
|
||||
end
|
||||
|
||||
if numericItemId and type(GetItemInfo) == "function" then
|
||||
local _, _, _, _, _, _, _, _, _, infoTexture = GetItemInfo(numericItemId)
|
||||
if infoTexture and infoTexture ~= "" then
|
||||
if type(infoTexture) == "number" then
|
||||
return infoTexture
|
||||
end
|
||||
if string.find(tostring(infoTexture), "\\", 1, true) then
|
||||
return tostring(infoTexture)
|
||||
end
|
||||
local asNumber = tonumber(tostring(infoTexture))
|
||||
if asNumber then
|
||||
return asNumber
|
||||
end
|
||||
return "Interface\\Icons\\" .. tostring(infoTexture)
|
||||
local resolvedInfoTexture = resolveTextureCandidate(infoTexture)
|
||||
if resolvedInfoTexture then
|
||||
return resolvedInfoTexture
|
||||
end
|
||||
end
|
||||
|
||||
if numericItemId and type(GetItemIcon) == "function" then
|
||||
local directIcon = GetItemIcon(numericItemId)
|
||||
if directIcon and directIcon ~= "" then
|
||||
if type(directIcon) == "number" then
|
||||
return directIcon
|
||||
end
|
||||
if string.find(tostring(directIcon), "\\", 1, true) then
|
||||
return tostring(directIcon)
|
||||
end
|
||||
local asNumber = tonumber(tostring(directIcon))
|
||||
if asNumber then
|
||||
return asNumber
|
||||
end
|
||||
return "Interface\\Icons\\" .. tostring(directIcon)
|
||||
local resolvedDirectIcon = resolveTextureCandidate(directIcon)
|
||||
if resolvedDirectIcon then
|
||||
return resolvedDirectIcon
|
||||
end
|
||||
end
|
||||
|
||||
local resolvedHintIcon = resolveTextureCandidate(iconHint)
|
||||
if resolvedHintIcon then
|
||||
return resolvedHintIcon
|
||||
end
|
||||
|
||||
if linkText ~= "" and type(GetItemInfo) == "function" then
|
||||
local _, _, _, _, _, _, _, _, _, linkTexture = GetItemInfo(linkText)
|
||||
local resolvedLinkTexture = resolveTextureCandidate(linkTexture)
|
||||
if resolvedLinkTexture then
|
||||
return resolvedLinkTexture
|
||||
end
|
||||
end
|
||||
|
||||
if itemName and itemName ~= "" and type(GetItemIcon) == "function" then
|
||||
local namedIcon = GetItemIcon(itemName)
|
||||
if namedIcon and namedIcon ~= "" then
|
||||
if type(namedIcon) == "number" then
|
||||
return namedIcon
|
||||
end
|
||||
if string.find(tostring(namedIcon), "\\", 1, true) then
|
||||
return tostring(namedIcon)
|
||||
end
|
||||
local asNumber = tonumber(tostring(namedIcon))
|
||||
if asNumber then
|
||||
return asNumber
|
||||
end
|
||||
return "Interface\\Icons\\" .. tostring(namedIcon)
|
||||
local resolvedNamedIcon = resolveTextureCandidate(namedIcon)
|
||||
if resolvedNamedIcon then
|
||||
return resolvedNamedIcon
|
||||
end
|
||||
end
|
||||
|
||||
if numericItemId and type(MTH_DS_Items) == "table" and type(MTH_DS_Items[numericItemId]) == "table" then
|
||||
local dsIcon = tostring(MTH_DS_Items[numericItemId].icon or "")
|
||||
if dsIcon ~= "" then
|
||||
if string.find(dsIcon, "\\", 1, true) then
|
||||
return dsIcon
|
||||
end
|
||||
return "Interface\\Icons\\" .. dsIcon
|
||||
local resolvedDsIcon = resolveTextureCandidate(MTH_DS_Items[numericItemId].icon)
|
||||
if resolvedDsIcon then
|
||||
return resolvedDsIcon
|
||||
end
|
||||
end
|
||||
|
||||
local bagIcon = MTH_BOOK_FindStableFoodBagIcon(numericItemId, itemName)
|
||||
if bagIcon then
|
||||
return bagIcon
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1566,7 +1585,7 @@ function MTH_BOOKTAB_RenderStableCards()
|
||||
local abilitiesCount = MTH_BOOK_CountPetAbilitiesMap(row.abilities)
|
||||
local abilitiesText = MTH_BOOK_GetStableAbilitySummary(row.abilities)
|
||||
local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil
|
||||
local hasRecordedTame = row.tameRecorded == true
|
||||
local hasRecordedTame = type(MTH_PETS_HasVerifiedTameRecord) == "function" and MTH_PETS_HasVerifiedTameRecord(row) or (row.tameRecorded == true)
|
||||
if not hasRecordedTame and legacyTameContext then
|
||||
if legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp then
|
||||
hasRecordedTame = true
|
||||
|
||||
+85
-3
@@ -76,6 +76,7 @@ local MTH_BOOK_STATE = {
|
||||
npcZone = "all",
|
||||
npcInZoneOnly = false,
|
||||
npcHideNoZone = true,
|
||||
familyStatsPercent = false,
|
||||
petHideNoAbilities = true,
|
||||
petHideUnknown = true,
|
||||
forcedBeastId = nil,
|
||||
@@ -1261,6 +1262,22 @@ function MTH_BOOK_ResolveCurrentPetId(store)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_BOOK_GetAuthoritativeStableSlot(petId, store)
|
||||
if petId == nil or type(store) ~= "table" or type(store.stableSlotIndex) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
local wantedId = tostring(petId)
|
||||
for slotNumber, mappedPetId in pairs(store.stableSlotIndex) do
|
||||
if tostring(mappedPetId or "") == wantedId then
|
||||
local numericSlot = tonumber(slotNumber)
|
||||
if numericSlot and numericSlot > 0 then
|
||||
return numericSlot
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetStableDisplaySlot(petId, row, store)
|
||||
if type(row) ~= "table" then return nil end
|
||||
if type(store) ~= "table" then
|
||||
@@ -1269,7 +1286,7 @@ function MTH_BOOK_GetStableDisplaySlot(petId, row, store)
|
||||
if MTH_BOOK_IsCurrentActivePetId(store, petId) then
|
||||
return 0
|
||||
end
|
||||
local slotNumber = tonumber(row.stableSlot)
|
||||
local slotNumber = MTH_BOOK_GetAuthoritativeStableSlot(petId, store)
|
||||
if slotNumber and slotNumber > 0 then
|
||||
return slotNumber
|
||||
end
|
||||
@@ -1328,6 +1345,9 @@ local function MTH_BOOK_GetSortKey(entry, col)
|
||||
if col == 3 then return tonumber(entry.coords) or 0 end
|
||||
if col == 4 then return tonumber(entry.abilities and table.getn(entry.abilities) or 0) end
|
||||
if col == 5 then return MTH_BOOK_SafeLower(entry.dietText or "") end
|
||||
if col == 6 then return tonumber(entry.healthStat) end
|
||||
if col == 7 then return tonumber(entry.damageStat) end
|
||||
if col == 8 then return tonumber(entry.armorStat) end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.mode == "npcs" then
|
||||
@@ -1429,6 +1449,56 @@ local function MTH_BOOK_DefaultCompare(a, b)
|
||||
return MTH_BOOK_ItemSort(a, b)
|
||||
end
|
||||
|
||||
local function MTH_BOOK_FormatFamilyStatValue(value)
|
||||
local numeric = tonumber(value)
|
||||
if not numeric then
|
||||
return "-"
|
||||
end
|
||||
local colorPrefix = "|cFF4DA6FF"
|
||||
if numeric < 1 then
|
||||
colorPrefix = "|cFFFF4040"
|
||||
elseif numeric > 1 then
|
||||
colorPrefix = "|cFF40FF40"
|
||||
end
|
||||
local text = nil
|
||||
if MTH_BOOK_STATE.familyStatsPercent == true then
|
||||
text = tostring(math.floor((numeric * 100) + 0.5)) .. "%"
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
text = string.format("%.3f", numeric)
|
||||
text = string.gsub(text, "(%..-)0+$", "%1")
|
||||
text = string.gsub(text, "%.$", "")
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
|
||||
local function MTH_BOOK_EnsureFamiliesPercentCheckbox()
|
||||
local statsText = getglobal("MTH_BOOK_StatsText")
|
||||
if not statsText then return nil end
|
||||
if MTH_BOOK_STATE.familiesPercentCheckbox then
|
||||
return MTH_BOOK_STATE.familiesPercentCheckbox
|
||||
end
|
||||
local parent = statsText:GetParent()
|
||||
if not parent then return nil end
|
||||
local check = CreateFrame("CheckButton", "MTH_BOOK_FamiliesPercentCheckbox", parent, "UICheckButtonTemplate")
|
||||
if not check then return nil end
|
||||
check:SetFrameStrata(parent:GetFrameStrata())
|
||||
check:SetFrameLevel((parent:GetFrameLevel() or 0) + 20)
|
||||
check:ClearAllPoints()
|
||||
check:SetPoint("LEFT", parent, "TOPLEFT", 604, -540)
|
||||
check.label = check:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
check.label:SetPoint("LEFT", check, "RIGHT", 4, 0)
|
||||
check.label:SetTextColor(1.00, 0.82, 0.00)
|
||||
check.label:SetText("Show modifiers in %")
|
||||
check:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
MTH_BOOK_STATE.familyStatsPercent = this:GetChecked() == 1
|
||||
MTH_BOOK_UpdateResults()
|
||||
end)
|
||||
check:Hide()
|
||||
MTH_BOOK_STATE.familiesPercentCheckbox = check
|
||||
return check
|
||||
end
|
||||
|
||||
local function MTH_BOOK_ApplyActiveSort(results)
|
||||
local state = MTH_BOOK_GetSortState()
|
||||
if not state.col then return end
|
||||
@@ -3521,7 +3591,7 @@ function MTH_BOOK_UpdateDetail()
|
||||
end
|
||||
|
||||
local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil
|
||||
local hasRecordedTame = row.tameRecorded == true
|
||||
local hasRecordedTame = type(MTH_PETS_HasVerifiedTameRecord) == "function" and MTH_PETS_HasVerifiedTameRecord(row) or (row.tameRecorded == true)
|
||||
if not hasRecordedTame and legacyTameContext then
|
||||
if legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp then
|
||||
hasRecordedTame = true
|
||||
@@ -3630,6 +3700,7 @@ end
|
||||
local function MTH_BOOK_UpdateHeader()
|
||||
local stats = getglobal("MTH_BOOK_StatsText")
|
||||
if not stats then return end
|
||||
local familiesPercentCheckbox = MTH_BOOK_EnsureFamiliesPercentCheckbox()
|
||||
local mode = MTH_BOOK_STATE.mode or "pets"
|
||||
local tabDef = MTH_BOOK_GetTabDefinition(mode)
|
||||
local modeLabel = (tabDef and tabDef.headerLabel) or "Beasts"
|
||||
@@ -3647,6 +3718,7 @@ local function MTH_BOOK_UpdateHeader()
|
||||
local filtered = table.getn(MTH_BOOK_STATE.results)
|
||||
local pages = math.max(1, math.ceil(filtered / MTH_BOOK_STATE.pageSize))
|
||||
if mode == "families" then
|
||||
local familyTotal = table.getn(MTH_BOOK_STATE.results or {})
|
||||
local namedTotal = 0
|
||||
local coordsTotal = 0
|
||||
for i = 1, table.getn(MTH_BOOK_STATE.results or {}) do
|
||||
@@ -3656,9 +3728,16 @@ local function MTH_BOOK_UpdateHeader()
|
||||
coordsTotal = coordsTotal + (tonumber(row.coords) or 0)
|
||||
end
|
||||
end
|
||||
stats:SetText("Named: " .. tostring(namedTotal) .. " | Coords: " .. tostring(coordsTotal))
|
||||
stats:SetText("Families: " .. tostring(familyTotal) .. " | Named: " .. tostring(namedTotal) .. " | Coords: " .. tostring(coordsTotal))
|
||||
if familiesPercentCheckbox then
|
||||
familiesPercentCheckbox:SetChecked(MTH_BOOK_STATE.familyStatsPercent == true and 1 or nil)
|
||||
familiesPercentCheckbox:Show()
|
||||
end
|
||||
return
|
||||
end
|
||||
if familiesPercentCheckbox then
|
||||
familiesPercentCheckbox:Hide()
|
||||
end
|
||||
stats:SetText(modeLabel .. ": " .. total .. " | Filtered: " .. filtered .. " | Page " .. MTH_BOOK_STATE.page .. "/" .. pages)
|
||||
end
|
||||
|
||||
@@ -3946,6 +4025,9 @@ local function MTH_BOOK_GetRowValues(entry)
|
||||
tostring(entry.coords or 0),
|
||||
abilitiesText,
|
||||
dietText,
|
||||
MTH_BOOK_FormatFamilyStatValue(entry.healthStat),
|
||||
MTH_BOOK_FormatFamilyStatValue(entry.damageStat),
|
||||
MTH_BOOK_FormatFamilyStatValue(entry.armorStat),
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ BINDING_HEADER_ZTrackHeader = MTH_L("BINDING_HEADER_ZTrackHeader", "[MetaHunt] Z
|
||||
BINDING_HEADER_ZTrapHeader = MTH_L("BINDING_HEADER_ZTrapHeader", "[MetaHunt] ZTrap Buttons")
|
||||
BINDING_HEADER_ZPetHeader = MTH_L("BINDING_HEADER_ZPetHeader", "[MetaHunt] ZPet Buttons")
|
||||
BINDING_HEADER_ZAmmoHeader = MTH_L("BINDING_HEADER_ZAmmoHeader", "[MetaHunt] ZAmmo Buttons")
|
||||
BINDING_HEADER_ExpAmmoHeader = MTH_L("BINDING_HEADER_ExpAmmoHeader", "[MetaHunt] MM Widget")
|
||||
BINDING_NAME_EXPAMMOACTION = MTH_L("BINDING_NAME_EXPAMMOACTION", "MM Widget Action")
|
||||
|
||||
function MTH:GetLocalization(key, default)
|
||||
return MTH_L(key, default)
|
||||
|
||||
+116
-45
@@ -5,6 +5,7 @@ MTH_Map = {
|
||||
activeSource = "focus",
|
||||
providers = {},
|
||||
nodesByZone = {},
|
||||
minimapNodesByZone = {},
|
||||
worldPins = {},
|
||||
minimapPins = {},
|
||||
zoneNameToId = {},
|
||||
@@ -24,7 +25,7 @@ MTH_Map = {
|
||||
nodeRevision = 0,
|
||||
minimapForceRefreshInterval = 1.0,
|
||||
verboseMiniReasons = false,
|
||||
minimapTickActive = 0.20,
|
||||
minimapTickActive = 0.05,
|
||||
minimapTickIdle = 0.75,
|
||||
}
|
||||
|
||||
@@ -122,15 +123,47 @@ local function MTH_Map_StripThePrefix(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_Map_WmaToPfq(zoneId)
|
||||
if type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
return MTH_MAP_WMA_TO_PFQ[zoneId] or zoneId
|
||||
end
|
||||
return zoneId
|
||||
end
|
||||
|
||||
local function MTH_Map_PfqToWma(pfqId)
|
||||
if type(MTH_MAP_PFQ_TO_WMA) == "table" then
|
||||
return MTH_MAP_PFQ_TO_WMA[pfqId] or pfqId
|
||||
end
|
||||
return pfqId
|
||||
end
|
||||
|
||||
local function MTH_Map_GetMinimapSizes()
|
||||
if pfDB and pfDB["minimap"] and MTH_DS_MinimapSizes then
|
||||
if not MTH_Map._mergedMinimapSizes then
|
||||
local merged = {}
|
||||
for zoneId, size in pairs(pfDB["minimap"]) do
|
||||
merged[zoneId] = size
|
||||
end
|
||||
for zoneId, size in pairs(MTH_DS_MinimapSizes) do
|
||||
if merged[zoneId] == nil then
|
||||
merged[zoneId] = size
|
||||
end
|
||||
end
|
||||
MTH_Map._mergedMinimapSizes = merged
|
||||
end
|
||||
return MTH_Map._mergedMinimapSizes
|
||||
elseif pfDB and pfDB["minimap"] then
|
||||
return pfDB["minimap"]
|
||||
end
|
||||
return MTH_DS_MinimapSizes or {}
|
||||
end
|
||||
|
||||
local function MTH_Map_GetZoneNameById(zoneId)
|
||||
local normalizedId = tonumber(zoneId) or zoneId
|
||||
if not normalizedId then return nil end
|
||||
|
||||
-- Translate WMA→pfQ first so WMA IDs don't accidentally hit wrong pfQ entries.
|
||||
local pfqId = normalizedId
|
||||
if type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
pfqId = MTH_MAP_WMA_TO_PFQ[normalizedId] or normalizedId
|
||||
end
|
||||
local pfqId = MTH_Map_WmaToPfq(normalizedId)
|
||||
|
||||
if MTH_DS_ZoneNamesFallback and MTH_DS_ZoneNamesFallback[pfqId] then
|
||||
return MTH_DS_ZoneNamesFallback[pfqId]
|
||||
@@ -364,6 +397,8 @@ end
|
||||
|
||||
function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
local inputId = tonumber(zoneId)
|
||||
local pfqInputId = MTH_Map_WmaToPfq(inputId)
|
||||
local inputIsWma = inputId and pfqInputId ~= inputId or false
|
||||
local resolved = {
|
||||
inputId = inputId,
|
||||
zoneId = inputId,
|
||||
@@ -377,9 +412,9 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
return resolved
|
||||
end
|
||||
|
||||
local sizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"]) or {}
|
||||
local sizes = MTH_Map_GetMinimapSizes()
|
||||
local visited = {}
|
||||
local cursor = inputId
|
||||
local cursor = pfqInputId
|
||||
local depth = 0
|
||||
|
||||
while cursor and not visited[cursor] and depth < 8 do
|
||||
@@ -390,15 +425,17 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
end
|
||||
|
||||
local parent = tonumber(row.parent or row.continent)
|
||||
local outputCursor = inputIsWma and MTH_Map_PfqToWma(cursor) or cursor
|
||||
local outputParent = parent and (inputIsWma and MTH_Map_PfqToWma(parent) or parent) or nil
|
||||
if parent and not resolved.continentId then
|
||||
resolved.continentId = parent
|
||||
resolved.continentId = outputParent
|
||||
end
|
||||
|
||||
if parent and parent > 0 and parent ~= cursor then
|
||||
resolved.subzoneId = resolved.subzoneId or cursor
|
||||
resolved.parentId = parent
|
||||
resolved.subzoneId = resolved.subzoneId or outputCursor
|
||||
resolved.parentId = outputParent
|
||||
cursor = parent
|
||||
resolved.zoneId = cursor
|
||||
resolved.zoneId = inputIsWma and MTH_Map_PfqToWma(cursor) or cursor
|
||||
depth = depth + 1
|
||||
else
|
||||
break
|
||||
@@ -406,11 +443,17 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
end
|
||||
|
||||
local renderZoneId = resolved.zoneId
|
||||
local pfqRenderId = tonumber(cursor or pfqInputId)
|
||||
local pfqParentId = resolved.parentId and MTH_Map_WmaToPfq(resolved.parentId) or nil
|
||||
if not sizes[renderZoneId] then
|
||||
if sizes[inputId] then
|
||||
renderZoneId = inputId
|
||||
elseif resolved.parentId and sizes[resolved.parentId] then
|
||||
renderZoneId = resolved.parentId
|
||||
elseif pfqRenderId and sizes[pfqRenderId] then
|
||||
renderZoneId = pfqRenderId
|
||||
elseif pfqParentId and sizes[pfqParentId] then
|
||||
renderZoneId = pfqParentId
|
||||
end
|
||||
end
|
||||
|
||||
@@ -419,21 +462,13 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
return resolved
|
||||
end
|
||||
|
||||
-- Translate a pfQ AreaTable ID to a WMA ID (for zone matching against coord tuples).
|
||||
-- Falls back to the pfQ ID itself if no mapping exists (sub-zones, custom zones).
|
||||
local function MTH_Map_PfqToWma(pfqId)
|
||||
if type(MTH_MAP_PFQ_TO_WMA) == "table" then
|
||||
return MTH_MAP_PFQ_TO_WMA[pfqId] or pfqId
|
||||
end
|
||||
return pfqId
|
||||
end
|
||||
|
||||
function MTH_Map:GetCurrentMapID()
|
||||
function MTH_Map:GetCurrentMapID(space)
|
||||
local continent = GetCurrentMapContinent and GetCurrentMapContinent() or nil
|
||||
local zone = GetCurrentMapZone and GetCurrentMapZone() or nil
|
||||
local zoneName = nil
|
||||
local mapId = nil
|
||||
local worldMapShown = WorldMapFrame and WorldMapFrame.IsShown and WorldMapFrame:IsShown() or false
|
||||
local preferPfq = space == "pfq"
|
||||
|
||||
if continent and zone and continent > 0 and zone > 0 and GetMapZones then
|
||||
if not self.mapZoneCache[continent] then
|
||||
@@ -444,7 +479,9 @@ function MTH_Map:GetCurrentMapID()
|
||||
if mapId then
|
||||
local resolved = self:ResolveZoneHierarchy(mapId)
|
||||
local normalized = resolved.zoneId or mapId
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
if not preferPfq then
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
end
|
||||
self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real=nil mapId=%s normalized=%s", tostring(continent), tostring(zone), tostring(zoneName), tostring(mapId), tostring(normalized))
|
||||
return normalized
|
||||
end
|
||||
@@ -460,7 +497,9 @@ function MTH_Map:GetCurrentMapID()
|
||||
mapId = self:GetMapIDByName(real)
|
||||
local resolved = self:ResolveZoneHierarchy(mapId)
|
||||
local normalized = resolved.zoneId or mapId
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
if not preferPfq then
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
end
|
||||
self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real='%s' mapId=%s normalized=%s", tostring(continent), tostring(zone), tostring(zoneName), tostring(real), tostring(mapId), tostring(normalized))
|
||||
return normalized
|
||||
end
|
||||
@@ -470,6 +509,30 @@ function MTH_Map:GetCurrentMapID()
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_Map:GetCurrentMinimapMapID()
|
||||
local realZoneName = GetRealZoneText and GetRealZoneText() or nil
|
||||
if realZoneName and realZoneName ~= "" then
|
||||
local mapId = self:GetMapIDByName(realZoneName)
|
||||
if mapId then
|
||||
return mapId
|
||||
end
|
||||
end
|
||||
|
||||
local continent = GetCurrentMapContinent and GetCurrentMapContinent() or nil
|
||||
local zone = GetCurrentMapZone and GetCurrentMapZone() or nil
|
||||
if continent and zone and continent > 0 and zone > 0 and GetMapZones then
|
||||
if not self.mapZoneCache[continent] then
|
||||
self.mapZoneCache[continent] = { GetMapZones(continent) }
|
||||
end
|
||||
local zoneName = self.mapZoneCache[continent][zone]
|
||||
if zoneName and zoneName ~= "" then
|
||||
return self:GetMapIDByName(zoneName)
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_Map:RegisterProvider(key, provider)
|
||||
if not key or type(key) ~= "string" then return false end
|
||||
if type(provider) ~= "table" or type(provider.buildNodes) ~= "function" then return false end
|
||||
@@ -886,6 +949,7 @@ end
|
||||
function MTH_Map:RebuildNodes()
|
||||
self:BuildZoneLookup()
|
||||
self.nodesByZone = {}
|
||||
self.minimapNodesByZone = {}
|
||||
self.nodeRevision = (tonumber(self.nodeRevision) or 0) + 1
|
||||
self._lastWorldRenderKey = nil
|
||||
self._lastMiniState = nil
|
||||
@@ -899,15 +963,22 @@ function MTH_Map:RebuildNodes()
|
||||
local node = allNodes[i]
|
||||
if node and node.zoneId then
|
||||
local sourceZoneId = tonumber(node.zoneId)
|
||||
local resolved = self:ResolveZoneHierarchy(sourceZoneId)
|
||||
local zoneId = tonumber(resolved.zoneId or sourceZoneId)
|
||||
if zoneId then
|
||||
local worldResolved = self:ResolveZoneHierarchy(sourceZoneId)
|
||||
local worldZoneId = tonumber(worldResolved.zoneId or sourceZoneId)
|
||||
local pfqSourceZoneId = MTH_Map_WmaToPfq(sourceZoneId)
|
||||
local minimapZoneId = tonumber(pfqSourceZoneId)
|
||||
if worldZoneId then
|
||||
node.sourceZoneId = sourceZoneId
|
||||
node.zoneId = zoneId
|
||||
node.parentZoneId = resolved.parentId
|
||||
node.subzoneId = resolved.subzoneId
|
||||
if not self.nodesByZone[zoneId] then self.nodesByZone[zoneId] = {} end
|
||||
table.insert(self.nodesByZone[zoneId], node)
|
||||
node.zoneId = worldZoneId
|
||||
node.parentZoneId = worldResolved.parentId
|
||||
node.subzoneId = worldResolved.subzoneId
|
||||
node.minimapZoneId = minimapZoneId
|
||||
if not self.nodesByZone[worldZoneId] then self.nodesByZone[worldZoneId] = {} end
|
||||
table.insert(self.nodesByZone[worldZoneId], node)
|
||||
if minimapZoneId then
|
||||
if not self.minimapNodesByZone[minimapZoneId] then self.minimapNodesByZone[minimapZoneId] = {} end
|
||||
table.insert(self.minimapNodesByZone[minimapZoneId], node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -996,8 +1067,8 @@ function MTH_Map:UpdateMinimap()
|
||||
return
|
||||
end
|
||||
|
||||
local mapId = self:GetCurrentMapID()
|
||||
local nodes = mapId and self.nodesByZone[mapId] or nil
|
||||
local mapId = self:GetCurrentMinimapMapID()
|
||||
local nodes = mapId and self.minimapNodesByZone[mapId] or nil
|
||||
if not mapId or not nodes then
|
||||
self.lastMiniReason = "no-mapid-or-nodes"
|
||||
self._lastMiniState = nil
|
||||
@@ -1030,20 +1101,17 @@ function MTH_Map:UpdateMinimap()
|
||||
and miniState.zoom == zoom
|
||||
and miniState.nodeRevision == (self.nodeRevision or 0)
|
||||
then
|
||||
local dx = math.abs((xPlayer or 0) - (miniState.xPlayer or 0))
|
||||
local dy = math.abs((yPlayer or 0) - (miniState.yPlayer or 0))
|
||||
if dx < 0.05 and dy < 0.05 and now < (miniState.nextForceAt or 0) then
|
||||
self.lastMiniReason = self.verboseMiniReasons and "throttled-small-move" or "throttle"
|
||||
if xPlayer == miniState.xPlayer and yPlayer == miniState.yPlayer and now < (miniState.nextForceAt or 0) then
|
||||
self.lastMiniReason = self.verboseMiniReasons and "throttled-still" or "throttle"
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local minimapSizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"])
|
||||
local minimapSizes = MTH_Map_GetMinimapSizes()
|
||||
local mapSize = minimapSizes and minimapSizes[mapId]
|
||||
if not mapSize and type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
-- Fallback: try the pfQ key (for sub-zones not yet mapped to WMA)
|
||||
local pfqId = MTH_MAP_WMA_TO_PFQ[mapId]
|
||||
mapSize = pfqId and minimapSizes and minimapSizes[pfqId]
|
||||
if not mapSize then
|
||||
local wmaId = MTH_Map_PfqToWma(mapId)
|
||||
mapSize = wmaId and minimapSizes and minimapSizes[wmaId]
|
||||
end
|
||||
if not mapSize then
|
||||
self.lastMiniReason = self.verboseMiniReasons and "missing-minimap-size" or "missing-size"
|
||||
@@ -1278,6 +1346,9 @@ function MTH_Map:Init()
|
||||
|
||||
self.controller:SetScript("OnEvent", function(frame, eventName)
|
||||
eventName = eventName or event
|
||||
if eventName == "WORLD_MAP_UPDATE" and MTH_Map._syncingMapContext then
|
||||
return
|
||||
end
|
||||
if eventName == "PLAYER_ENTERING_WORLD" or eventName == "ZONE_CHANGED" or eventName == "ZONE_CHANGED_NEW_AREA" or eventName == "MINIMAP_ZONE_CHANGED" then
|
||||
if SetMapToCurrentZone and (not WorldMapFrame or not WorldMapFrame:IsShown()) then
|
||||
SetMapToCurrentZone()
|
||||
@@ -1320,15 +1391,15 @@ function MTH_Map:Init()
|
||||
end
|
||||
|
||||
if MTH_Map.enabled and MTH_Map.showMinimap then
|
||||
local mapId = MTH_Map:GetCurrentMapID()
|
||||
local mapId = MTH_Map:GetCurrentMinimapMapID()
|
||||
if not mapId and (frame._mthMapRecoverAt or 0) <= now then
|
||||
frame._mthMapRecoverAt = now + 2.0
|
||||
if SetMapToCurrentZone and (not WorldMapFrame or not WorldMapFrame:IsShown()) then
|
||||
SetMapToCurrentZone()
|
||||
mapId = MTH_Map:GetCurrentMapID()
|
||||
mapId = MTH_Map:GetCurrentMinimapMapID()
|
||||
end
|
||||
end
|
||||
local nodes = mapId and MTH_Map.nodesByZone and MTH_Map.nodesByZone[mapId] or nil
|
||||
local nodes = mapId and MTH_Map.minimapNodesByZone and MTH_Map.minimapNodesByZone[mapId] or nil
|
||||
local hasNodes = nodes and table.getn(nodes) > 0
|
||||
frame._mthTick = now + (hasNodes and (tonumber(MTH_Map.minimapTickActive) or 0.20) or (tonumber(MTH_Map.minimapTickIdle) or 0.75))
|
||||
if hasNodes then
|
||||
|
||||
@@ -252,8 +252,14 @@ local function MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
|
||||
engine.profile = profile
|
||||
if engine.SetCandyBarGroupGrowth then
|
||||
engine:SetCandyBarGroupGrowth("MTHChronometer", profile.growup and true or false)
|
||||
if engine.SetCandyBarGroupGrowth and engine.SetCandyBarGroupPoint and engine.anchor then
|
||||
local growup = profile.growup and true or false
|
||||
if growup then
|
||||
engine:SetCandyBarGroupPoint("MTHChronometer", "BOTTOM", engine.anchor, "TOP", 0, 0)
|
||||
else
|
||||
engine:SetCandyBarGroupPoint("MTHChronometer", "TOP", engine.anchor, "BOTTOM", 0, 0)
|
||||
end
|
||||
engine:SetCandyBarGroupGrowth("MTHChronometer", growup)
|
||||
end
|
||||
if engine.SetCandyBarGroupVerticalSpacing then
|
||||
engine:SetCandyBarGroupVerticalSpacing("MTHChronometer", profile.spacing or 0)
|
||||
|
||||
+306
-42
@@ -16,10 +16,112 @@ local function MTH_EA_OPT_GetCfg()
|
||||
end
|
||||
|
||||
local MTH_EA_OPT_built = false
|
||||
local MTH_EA_OPT_STATE = { bindStatus = nil, bindButton = nil, captureFrame = nil }
|
||||
|
||||
-- Helper: create a pre-wired checkbox
|
||||
local function MTH_EA_OPT_CB(container, name, label, yOff, isChecked, onClick)
|
||||
local cb = MTH_CreateCheckbox(container, name, label, yOff, 20)
|
||||
-- ── Layout constants ──────────────────────────────────────────
|
||||
-- Two equal columns separated by a gutter. Values are offsets from the
|
||||
-- container TOPLEFT corner (positive X right, negative Y down).
|
||||
local OPT_L = 20 -- left column X
|
||||
local OPT_R = 300 -- right column X
|
||||
local OPT_CW = 255 -- column width (used for text wrapping and sliders)
|
||||
|
||||
local function MTH_EA_OPT_GetBindText()
|
||||
if not GetBindingKey then return "Unbound" end
|
||||
local k1, k2 = GetBindingKey("EXPAMMO ACTION")
|
||||
if not k1 and not k2 then return "Unbound" end
|
||||
if k1 and k2 then return k1 .. " / " .. k2 end
|
||||
return k1 or k2 or "Unbound"
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_UpdateBindStatus()
|
||||
local bs = MTH_EA_OPT_STATE.bindStatus
|
||||
if not bs then return end
|
||||
bs:SetText("Key: " .. MTH_EA_OPT_GetBindText())
|
||||
end
|
||||
|
||||
-- key must already be the full chord string (e.g. "ALT-F", "BUTTON3", "MOUSEWHEELUP")
|
||||
local function MTH_EA_OPT_SaveBinding(key)
|
||||
if not SetBinding or not SaveBindings or not GetBindingKey then return end
|
||||
local old1, old2 = GetBindingKey("EXPAMMO ACTION")
|
||||
if old1 then SetBinding(old1) end
|
||||
if old2 then SetBinding(old2) end
|
||||
if key and key ~= "" then
|
||||
SetBinding(key, "EXPAMMO ACTION")
|
||||
end
|
||||
local bs = 1
|
||||
if GetCurrentBindingSet then bs = GetCurrentBindingSet() or 1 end
|
||||
SaveBindings(bs)
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_GetPrefix()
|
||||
return string.format("%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""))
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_StopCapture()
|
||||
if MTH_EA_OPT_STATE.captureFrame then
|
||||
MTH_EA_OPT_STATE.captureFrame:Hide()
|
||||
end
|
||||
local bb = MTH_EA_OPT_STATE.bindButton
|
||||
if bb then bb:SetText("Set Key") end
|
||||
MTH_EA_OPT_UpdateBindStatus()
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_MakeCaptureFrame()
|
||||
local f = CreateFrame("Frame", "MTH_ExpAmmoBindCapture", UIParent)
|
||||
f:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
f:SetAllPoints(UIParent)
|
||||
f:EnableKeyboard(true)
|
||||
f:EnableMouse(true)
|
||||
f:EnableMouseWheel(true)
|
||||
f:Hide()
|
||||
local bg = f:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(f)
|
||||
bg:SetTexture(0, 0, 0, 0.5)
|
||||
local lbl = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
lbl:SetPoint("CENTER", f, "CENTER", 0, 0)
|
||||
lbl:SetText("|cffffff00Press a key, mouse button, or scroll wheel|r\n|cffaaaaaa(ESC to cancel)|r")
|
||||
f:SetScript("OnKeyUp", function()
|
||||
local key = arg1
|
||||
if not key or key == "" then return end
|
||||
if key == "ESCAPE" then MTH_EA_OPT_StopCapture() return end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then return end
|
||||
if key == "ALT" or key == "CTRL" or key == "SHIFT" then return end
|
||||
MTH_EA_OPT_SaveBinding(MTH_EA_OPT_GetPrefix() .. key)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseUp", function()
|
||||
local btmap = { LeftButton="BUTTON1", RightButton="BUTTON2", MiddleButton="BUTTON3", Button4="BUTTON4", Button5="BUTTON5" }
|
||||
local mapped = btmap[arg1]
|
||||
if not mapped then return end
|
||||
local prefix = MTH_EA_OPT_GetPrefix()
|
||||
if prefix == "" and (mapped == "BUTTON1" or mapped == "BUTTON2") then return end
|
||||
MTH_EA_OPT_SaveBinding(prefix .. mapped)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseWheel", function()
|
||||
local wheelkey = (arg1 == 1 and "MOUSEWHEELUP") or (arg1 == -1 and "MOUSEWHEELDOWN") or nil
|
||||
if not wheelkey then return end
|
||||
MTH_EA_OPT_SaveBinding(MTH_EA_OPT_GetPrefix() .. wheelkey)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
return f
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_StartCapture()
|
||||
if not MTH_EA_OPT_STATE.captureFrame then
|
||||
MTH_EA_OPT_STATE.captureFrame = MTH_EA_OPT_MakeCaptureFrame()
|
||||
end
|
||||
MTH_EA_OPT_STATE.captureFrame:Show()
|
||||
local bb = MTH_EA_OPT_STATE.bindButton
|
||||
if bb then bb:SetText("Press key...") end
|
||||
end
|
||||
|
||||
-- Helper: checkbox anchored to a given X column
|
||||
local function MTH_EA_OPT_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()
|
||||
@@ -31,10 +133,38 @@ local function MTH_EA_OPT_CB(container, name, label, yOff, isChecked, onClick)
|
||||
end
|
||||
|
||||
-- Helper: section header label
|
||||
local function MTH_EA_OPT_Header(container, text, yOff)
|
||||
local function MTH_EA_OPT_Header(container, text, yOff, xOff)
|
||||
local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", 20, yOff)
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff)
|
||||
fs:SetText("|cffff9900" .. text .. "|r")
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Helper: small descriptive text
|
||||
local function MTH_EA_OPT_Tip(container, text, yOff, xOff, r, g, b)
|
||||
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(r or 0.6, g or 0.6, b or 0.6)
|
||||
fs:SetText(text)
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Enable or disable a list of widgets (supports Frames/Buttons and FontStrings)
|
||||
local function MTH_EA_OPT_SetGroupEnabled(widgets, enabled)
|
||||
for _, w in ipairs(widgets) do
|
||||
if w then
|
||||
if w.Enable and w.Disable then
|
||||
-- Frame / Button / CheckButton
|
||||
if enabled then w:Enable() else w:Disable() end
|
||||
end
|
||||
-- Dim all (including FontStrings and frames) via alpha
|
||||
if w.SetAlpha then
|
||||
w:SetAlpha(enabled and 1.0 or 0.35)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_BuildUI(container)
|
||||
@@ -42,27 +172,21 @@ local function MTH_EA_OPT_BuildUI(container)
|
||||
MTH_EA_OPT_built = true
|
||||
|
||||
local cfg = MTH_EA_OPT_GetCfg()
|
||||
local showHint = cfg.showHint ~= false
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Title
|
||||
---------------------------------------------------------------------------
|
||||
local title = container:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -16)
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, -16)
|
||||
title:SetText("MM Widget — Experimental Ammunition Tracker")
|
||||
|
||||
-- Short description
|
||||
local desc = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
desc:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -46)
|
||||
desc:SetWidth(540)
|
||||
desc:SetJustifyH("LEFT")
|
||||
desc:SetJustifyV("TOP")
|
||||
desc:SetText(
|
||||
"Tracks the 1.18.1 ammo cycling mechanic.\n"
|
||||
.. "Aimed Shot starts a 60s cycle: "
|
||||
.. "|cffff8833Explosive|r \226\134\146 |cff33ff55Poisonous|r \226\134\146 |cff6633ffEnchanted|r\n"
|
||||
.. "Each state is consumed by its matching shot (Multi-Shot / Serpent Sting / Arcane Shot)."
|
||||
)
|
||||
---------------------------------------------------------------------------
|
||||
-- LEFT COLUMN
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- ── Section: Module ──────────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Module", -112)
|
||||
-- ── Module ──────────────────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Module", -46)
|
||||
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
@@ -70,56 +194,196 @@ local function MTH_EA_OPT_BuildUI(container)
|
||||
end
|
||||
MTH_EA_OPT_CB(container, "MTH_ExpAmmoModuleCB",
|
||||
"Enable MM Widget module",
|
||||
-132, moduleEnabled,
|
||||
-66, OPT_L, moduleEnabled,
|
||||
function(v)
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
MTH:SetModuleEnabled("expammo", v)
|
||||
end
|
||||
if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("expammo", v) end
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetEnabled(v) end
|
||||
end)
|
||||
|
||||
-- ── Section: Tracker Visibility ─────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Tracker Visibility", -166)
|
||||
-- ── Tracker Visibility ───────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Tracker Visibility", -100)
|
||||
|
||||
MTH_EA_OPT_CB(container, "MTH_ExpAmmoShowLnLCB",
|
||||
"Show Lock and Load cell (top)",
|
||||
-186, cfg.showLnL ~= false,
|
||||
-120, OPT_L, cfg.showLnL ~= false,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetShowLnL(v) end end)
|
||||
|
||||
MTH_EA_OPT_CB(container, "MTH_ExpAmmoShowHintCB",
|
||||
-- "Show action hint cell" also gates the right column
|
||||
local rightGroup = {} -- filled below; toggled when this CB changes
|
||||
local showHintCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoShowHintCB",
|
||||
"Show action hint cell (bottom)",
|
||||
-210, cfg.showHint ~= false,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetShowHint(v) end end)
|
||||
-144, OPT_L, showHint,
|
||||
function(v)
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetShowHint(v) end
|
||||
MTH_EA_OPT_SetGroupEnabled(rightGroup, v)
|
||||
end)
|
||||
|
||||
MTH_EA_OPT_CB(container, "MTH_ExpAmmoHideOOCCB",
|
||||
"Hide widget when out of combat",
|
||||
-234, cfg.hideOOC == true,
|
||||
-168, OPT_L, cfg.hideOOC == true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetHideOOC(v) end end)
|
||||
|
||||
-- ── Section: Appearance ──────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Appearance", -268)
|
||||
-- ── Appearance ───────────────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Appearance", -202)
|
||||
|
||||
local sizeSlider = MTH_CreateSlider(container, "MTH_ExpAmmoCellSizeSlider",
|
||||
"Cell Size (px)", 20, 80, 4, -288)
|
||||
"Cell Size (px)", 20, 80, 4, -222)
|
||||
if sizeSlider then
|
||||
sizeSlider:ClearAllPoints()
|
||||
sizeSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 5, -222)
|
||||
sizeSlider:SetWidth(OPT_CW - 10)
|
||||
sizeSlider:SetValue(math.max(20, math.min(80, cfg.cellSize or 40)))
|
||||
sizeSlider.onChange = function(val)
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetCellSize(val) end
|
||||
end
|
||||
end
|
||||
|
||||
local gapSlider = MTH_CreateSlider(container, "MTH_ExpAmmoCellGapSlider",
|
||||
"Cell Spacing (px)", 0, 20, 1, -286)
|
||||
if gapSlider then
|
||||
gapSlider:ClearAllPoints()
|
||||
gapSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 5, -286)
|
||||
gapSlider:SetWidth(OPT_CW - 10)
|
||||
gapSlider:SetValue(math.max(0, math.min(20, cfg.cellGap or 2)))
|
||||
gapSlider.onChange = function(val)
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetCellGap(val) end
|
||||
end
|
||||
end
|
||||
|
||||
MTH_EA_OPT_CB(container, "MTH_ExpAmmoBigCDCB",
|
||||
"Large cooldown numbers (centered in cell)",
|
||||
-352, cfg.bigCD == true,
|
||||
-350, OPT_L, cfg.bigCD == true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBigCD(v) end end)
|
||||
|
||||
-- ── Notes ───────────────────────────────────────────────
|
||||
local tip = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
tip:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -388)
|
||||
tip:SetWidth(540)
|
||||
tip:SetJustifyH("LEFT")
|
||||
tip:SetTextColor(0.3, 0.7, 1.0)
|
||||
tip:SetText("Tip: hold ALT and drag the tracker widget to reposition it.")
|
||||
-- ── Position ─────────────────────────────────────────────
|
||||
MTH_EA_OPT_Header(container, "Position", -390)
|
||||
|
||||
local anchorButton = MTH_CreateActionButton(container, "MTH_ExpAmmoToggleAnchor",
|
||||
"Toggle Anchor", OPT_L, -410, 130, 22, function()
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.ToggleAnchor() end
|
||||
end)
|
||||
if anchorButton then anchorButton:Show() end
|
||||
|
||||
MTH_EA_OPT_Tip(container,
|
||||
"Shows 4 drag handles around the widget (top / bottom / left / right). Drag any one to reposition it.",
|
||||
-436, OPT_L)
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- RIGHT COLUMN — Action Hint Cell (keybind + casting)
|
||||
-- All widgets here are greyed out when the bottom cell is hidden.
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
-- Vertical divider line
|
||||
local divider = container:CreateTexture(nil, "BACKGROUND")
|
||||
divider:SetTexture(0.3, 0.3, 0.3, 0.6)
|
||||
divider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R - 10, -46)
|
||||
divider:SetPoint("BOTTOMLEFT", container, "TOPLEFT", OPT_R - 10, -530)
|
||||
divider:SetWidth(1)
|
||||
|
||||
-- Column title
|
||||
local rTitle = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
||||
rTitle:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -46)
|
||||
rTitle:SetText("|cffff9900" .. "Action Hint Cell" .. "|r")
|
||||
table.insert(rightGroup, rTitle)
|
||||
|
||||
|
||||
local rSubtitle = MTH_EA_OPT_Tip(container,
|
||||
"Options below apply to the bottom cell only.\n Disable \"Show action hint cell\" to hide the cell and these controls.",
|
||||
-64, OPT_R, 1.00, 1.00, 1.00)
|
||||
if rSubtitle then rSubtitle:SetWidth(290) end
|
||||
table.insert(rightGroup, rSubtitle)
|
||||
|
||||
local rNamPowerTip = MTH_EA_OPT_Tip(container,
|
||||
"Notes:\n - You need NAMPOWER for this to work reliably.\n - Aimed/Steady/Multi/Arcane Shots SHOULD BE on your action bars.",
|
||||
-100, OPT_R, 0.35, 0.65, 1.00)
|
||||
if rNamPowerTip then rNamPowerTip:SetWidth(290) end
|
||||
table.insert(rightGroup, rNamPowerTip)
|
||||
|
||||
-- ── Keybind ──────────────────────────────────────────────
|
||||
table.insert(rightGroup, MTH_EA_OPT_Header(container, "Keybind", -182, OPT_R))
|
||||
|
||||
local bindTip = MTH_EA_OPT_Tip(container,
|
||||
"One key to rule them all.\n Aimed Shot when ready, Steady Shot while Aimed is on cooldown,\n consume-spell (Multi / Serpent / Arcane) when proc is up.",
|
||||
-200, OPT_R, 0.65, 0.65, 0.65)
|
||||
if bindTip then bindTip:SetWidth(290) end
|
||||
table.insert(rightGroup, bindTip)
|
||||
|
||||
local bindStatus = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
bindStatus:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -248)
|
||||
bindStatus:SetText("Key: " .. MTH_EA_OPT_GetBindText())
|
||||
MTH_EA_OPT_STATE.bindStatus = bindStatus
|
||||
table.insert(rightGroup, bindStatus)
|
||||
|
||||
local bindButton = CreateFrame("Button", "MTH_ExpAmmoBindButton", container, "UIPanelButtonTemplate")
|
||||
bindButton:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -264)
|
||||
bindButton:SetWidth(90)
|
||||
bindButton:SetHeight(22)
|
||||
bindButton:SetText("Set Key")
|
||||
bindButton:EnableKeyboard(false)
|
||||
bindButton:SetScript("OnClick", function()
|
||||
if MTH_EA_OPT_STATE.captureFrame and MTH_EA_OPT_STATE.captureFrame:IsShown() then
|
||||
MTH_EA_OPT_StopCapture()
|
||||
else
|
||||
MTH_EA_OPT_StartCapture()
|
||||
end
|
||||
end)
|
||||
MTH_EA_OPT_STATE.bindButton = bindButton
|
||||
table.insert(rightGroup, bindButton)
|
||||
|
||||
local clearButton = CreateFrame("Button", "MTH_ExpAmmoBindClear", container, "UIPanelButtonTemplate")
|
||||
clearButton:SetPoint("LEFT", bindButton, "RIGHT", 6, 0)
|
||||
clearButton:SetWidth(70)
|
||||
clearButton:SetHeight(22)
|
||||
clearButton:SetText("Clear")
|
||||
clearButton:SetScript("OnClick", function()
|
||||
MTH_EA_OPT_SaveBinding(nil)
|
||||
end)
|
||||
table.insert(rightGroup, clearButton)
|
||||
|
||||
-- ── Casting ──────────────────────────────────────────────
|
||||
table.insert(rightGroup, MTH_EA_OPT_Header(container, "Casting", -304, OPT_R))
|
||||
|
||||
local quiverCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoQuiverNoClipCB",
|
||||
"Use Quiver no-clip casting",
|
||||
-324, OPT_R, cfg.useQuiverNoClip == true,
|
||||
function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetUseQuiverNoClip(v) end end)
|
||||
table.insert(rightGroup, quiverCB)
|
||||
|
||||
local quiverTip = MTH_EA_OPT_Tip(container,
|
||||
"|cff4d9cffRequires the Quiver addon.|r \n Routes Aimed/Steady/Multi cast through Quiver.CastNoClip \n to avoid clipping your auto-shot swing timer",
|
||||
-350, OPT_R, 1, 1, 1)
|
||||
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
|
||||
|
||||
function MTH_SetupExpAmmoOptions()
|
||||
|
||||
+64
-46
@@ -10,7 +10,7 @@ local MTH_FOM_STATE = {
|
||||
bindStatus = nil,
|
||||
bindButton = nil,
|
||||
bindClearButton = nil,
|
||||
bindingCapture = false,
|
||||
captureFrame = nil,
|
||||
ctrl = {},
|
||||
}
|
||||
|
||||
@@ -93,43 +93,79 @@ local function MTH_FOM_UpdateBindingStatus()
|
||||
if not bindStatus or not bindStatus.SetText then
|
||||
return
|
||||
end
|
||||
if MTH_FOM_STATE.bindingCapture then
|
||||
bindStatus:SetText(MTH_FOM_L("FOM_BIND_STATUS_PROMPT", "Feed key: press a key... (ESC to clear)"))
|
||||
else
|
||||
bindStatus:SetText(string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText()))
|
||||
end
|
||||
bindStatus:SetText(string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText()))
|
||||
end
|
||||
|
||||
local function MTH_FOM_StopBindingCapture()
|
||||
MTH_FOM_STATE.bindingCapture = false
|
||||
if MTH_FOM_STATE.captureFrame then
|
||||
MTH_FOM_STATE.captureFrame:Hide()
|
||||
end
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then
|
||||
MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(true)
|
||||
end
|
||||
MTH_FOM_STATE.bindButton:EnableKeyboard(false)
|
||||
MTH_FOM_STATE.bindButton:SetText(MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key"))
|
||||
end
|
||||
MTH_FOM_UpdateBindingStatus()
|
||||
end
|
||||
|
||||
local function MTH_FOM_StartBindingCapture()
|
||||
MTH_FOM_STATE.bindingCapture = true
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
MTH_FOM_STATE.bindButton:SetText(MTH_FOM_L("FOM_BIND_PRESS_KEY", "Press key..."))
|
||||
MTH_FOM_STATE.bindButton:EnableKeyboard(true)
|
||||
if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then
|
||||
MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(false)
|
||||
end
|
||||
end
|
||||
MTH_FOM_UpdateBindingStatus()
|
||||
local function MTH_FOM_BuildBindingChord(key)
|
||||
return string.format("%s%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""),
|
||||
key)
|
||||
end
|
||||
|
||||
local function MTH_FOM_BuildBindingChord(key)
|
||||
local chord = ""
|
||||
if IsControlKeyDown and IsControlKeyDown() and key ~= "LCTRL" and key ~= "RCTRL" then chord = chord .. "CTRL-" end
|
||||
if IsAltKeyDown and IsAltKeyDown() and key ~= "LALT" and key ~= "RALT" then chord = chord .. "ALT-" end
|
||||
if IsShiftKeyDown and IsShiftKeyDown() and key ~= "LSHIFT" and key ~= "RSHIFT" then chord = chord .. "SHIFT-" end
|
||||
return chord .. key
|
||||
local function MTH_FOM_MakeCaptureFrame()
|
||||
local f = CreateFrame("Frame", "MTH_FOMBindCapture", UIParent)
|
||||
f:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
f:SetAllPoints(UIParent)
|
||||
f:EnableKeyboard(true)
|
||||
f:EnableMouse(true)
|
||||
f:EnableMouseWheel(true)
|
||||
f:Hide()
|
||||
local bg = f:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(f)
|
||||
bg:SetTexture(0, 0, 0, 0.5)
|
||||
local lbl = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
lbl:SetPoint("CENTER", f, "CENTER", 0, 0)
|
||||
lbl:SetText("|cffffff00Press a key, mouse button, or scroll wheel|r\n|cffaaaaaa(ESC to cancel)|r")
|
||||
f:SetScript("OnKeyUp", function()
|
||||
local key = arg1
|
||||
if not key or key == "" then return end
|
||||
if key == "ESCAPE" then MTH_FOM_StopBindingCapture() return end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then return end
|
||||
if key == "ALT" or key == "CTRL" or key == "SHIFT" then return end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(key))
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseUp", function()
|
||||
local btmap = { LeftButton="BUTTON1", RightButton="BUTTON2", MiddleButton="BUTTON3", Button4="BUTTON4", Button5="BUTTON5" }
|
||||
local mapped = btmap[arg1]
|
||||
if not mapped then return end
|
||||
local prefix = string.format("%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""))
|
||||
if prefix == "" and (mapped == "BUTTON1" or mapped == "BUTTON2") then return end
|
||||
MTH_FOM_SaveBinding(prefix .. mapped)
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseWheel", function()
|
||||
local wheelkey = (arg1 == 1 and "MOUSEWHEELUP") or (arg1 == -1 and "MOUSEWHEELDOWN") or nil
|
||||
if not wheelkey then return end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(wheelkey))
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
return f
|
||||
end
|
||||
|
||||
local function MTH_FOM_StartBindingCapture()
|
||||
if not MTH_FOM_STATE.captureFrame then
|
||||
MTH_FOM_STATE.captureFrame = MTH_FOM_MakeCaptureFrame()
|
||||
end
|
||||
MTH_FOM_STATE.captureFrame:Show()
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
MTH_FOM_STATE.bindButton:SetText(MTH_FOM_L("FOM_BIND_PRESS_KEY", "Press key..."))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FOM_SaveBinding(key)
|
||||
@@ -254,31 +290,13 @@ function MTH_SetupFeedOMaticOptions()
|
||||
bindButton:SetWidth(90)
|
||||
bindButton:SetHeight(22)
|
||||
bindButton:SetText(MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key"))
|
||||
bindButton:EnableKeyboard(false)
|
||||
bindButton:SetScript("OnClick", function()
|
||||
if MTH_FOM_STATE.bindingCapture then
|
||||
if MTH_FOM_STATE.captureFrame and MTH_FOM_STATE.captureFrame:IsShown() then
|
||||
MTH_FOM_StopBindingCapture()
|
||||
else
|
||||
MTH_FOM_StartBindingCapture()
|
||||
end
|
||||
end)
|
||||
bindButton:SetScript("OnKeyDown", function()
|
||||
if not MTH_FOM_STATE.bindingCapture then
|
||||
return
|
||||
end
|
||||
local key = arg1
|
||||
if not key or key == "" then
|
||||
return
|
||||
end
|
||||
if key == "ESCAPE" then
|
||||
MTH_FOM_SaveBinding(nil)
|
||||
return
|
||||
end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then
|
||||
return
|
||||
end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(key))
|
||||
end)
|
||||
MTH_FOM_STATE.bindButton = bindButton
|
||||
|
||||
local bindClearButton = CreateFrame("Button", "MetaHuntOptionsFOMBindClearButton", container, "UIPanelButtonTemplate")
|
||||
|
||||
@@ -133,6 +133,11 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupMessagesOptions()
|
||||
end
|
||||
end
|
||||
elseif tabKey == "ZBar" then
|
||||
if type(MTH_SetupZBarOptions) == "function" then
|
||||
MTH_SetupZBarOptions()
|
||||
MTH_OPTIONS_SETUP["ZBar"] = true
|
||||
end
|
||||
elseif tabKey == "Pet" then
|
||||
if not MTH_OPTIONS_SETUP["Pet"] then
|
||||
MTH_SetupPetOptions()
|
||||
@@ -182,6 +187,11 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupToysOptions()
|
||||
MTH_OPTIONS_SETUP["Toys"] = true
|
||||
end
|
||||
elseif tabKey == "Craft" then
|
||||
if not MTH_OPTIONS_SETUP["Craft"] then
|
||||
MTH_SetupCraftOptions()
|
||||
MTH_OPTIONS_SETUP["Craft"] = true
|
||||
end
|
||||
elseif tabKey == "SmartAmmo" then
|
||||
if not MTH_OPTIONS_SETUP["SmartAmmo"] then
|
||||
MTH_SetupSmartAmmoOptions()
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,4 @@
|
||||
MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = true, chronometer = true }
|
||||
MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = false, chronometer = false }
|
||||
MTH_OPTIONS_TREE_BUTTONS = MTH_OPTIONS_TREE_BUTTONS or {}
|
||||
MTH_OPTIONS_STATE = MTH_OPTIONS_STATE or {}
|
||||
if MTH_OPTIONS_STATE.activeTab == nil and MTH_OPTIONS_ACTIVE_TAB ~= nil then
|
||||
@@ -30,7 +30,8 @@ MTH_OPTIONS_TABS = MTH_OPTIONS_TABS or {
|
||||
{ key = "General", label = "General", frame = "MetaHuntOptionsGeneral" },
|
||||
{ key = "Profiles", label = "Profiles", frame = "MetaHuntOptionsProfiles" },
|
||||
{ key = "Messages", label = "Messages", frame = "MetaHuntOptionsMessages" },
|
||||
{ key = "Pet", label = "ZPet", frame = "MetaHuntOptionsPet" },
|
||||
{ key = "ZBar", label = "zBar", frame = "MetaHuntOptionsZBar" },
|
||||
{ key = "Pet", label = "ZPet", frame = "MetaHuntOptionsPet" },
|
||||
{ key = "Track", label = "ZTrack", frame = "MetaHuntOptionsTrack" },
|
||||
{ key = "Aspect", label = "ZAspect", frame = "MetaHuntOptionsAspect" },
|
||||
{ key = "Trap", label = "ZTrap", frame = "MetaHuntOptionsTrap" },
|
||||
@@ -39,6 +40,7 @@ MTH_OPTIONS_TABS = MTH_OPTIONS_TABS or {
|
||||
{ key = "Mounts", label = "ZMounts", frame = "MetaHuntOptionsMounts" },
|
||||
{ key = "Companions", label = "ZCompanions", frame = "MetaHuntOptionsCompanions" },
|
||||
{ key = "Toys", label = "ZToys", frame = "MetaHuntOptionsToys" },
|
||||
{ key = "Craft", label = "ZCraft", frame = "MetaHuntOptionsCraft" },
|
||||
{ key = "SmartAmmo", label = "Smart Ammo", frame = "MetaHuntOptionsSmartAmmo" },
|
||||
{ key = "FeedOMatic", label = "FeedOMatic", frame = "MetaHuntOptionsFeedOMatic" },
|
||||
{ key = "AutoBuy", label = "Auto Buy", frame = "MetaHuntOptionsAutoBuy" },
|
||||
@@ -66,7 +68,8 @@ MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or {
|
||||
label = "zButtons",
|
||||
node = "zButtons",
|
||||
children = {
|
||||
{ label = "zAmmo", key = "Ammo" },
|
||||
{ label = "zBar", key = "ZBar" },
|
||||
{ label = "zAmmo", key = "Ammo" },
|
||||
{ label = "zPet", key = "Pet" },
|
||||
{ label = "zAspect", key = "Aspect" },
|
||||
{ label = "zTrack", key = "Track" },
|
||||
@@ -75,6 +78,7 @@ MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or {
|
||||
{ label = "zMounts", key = "Mounts" },
|
||||
{ label = "zCompanions", key = "Companions" },
|
||||
{ label = "zToys", key = "Toys" },
|
||||
{ label = "zCraft", key = "Craft" },
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+492
-32
@@ -37,7 +37,9 @@ local function MTH_RebuildSpellOrder(buttonName, buttonObj, itemList, maxButtons
|
||||
for i = 1, maxButtons do
|
||||
local spellIndex = ZHunterMod_Saved[buttonName]["spells"][i]
|
||||
if spellIndex and visible[spellIndex] ~= false then
|
||||
info[infoIndex] = itemList[spellIndex]
|
||||
-- String-based lists (zCraft) store names directly; numeric lists index into itemList
|
||||
local name = (type(spellIndex) == "number") and itemList[spellIndex] or spellIndex
|
||||
info[infoIndex] = name
|
||||
infoIndex = infoIndex + 1
|
||||
end
|
||||
end
|
||||
@@ -108,34 +110,34 @@ local function MTH_SetButtonEnabledState(buttonName, buttonObj, enabled)
|
||||
ZHunterMod_Saved[buttonName]["enabled"] = enabled and true or false
|
||||
|
||||
local resolvedButtonObj = buttonObj or getglobal(buttonName)
|
||||
if not resolvedButtonObj then return end
|
||||
|
||||
if not enabled then
|
||||
if resolvedButtonObj.Hide then
|
||||
resolvedButtonObj:Hide()
|
||||
end
|
||||
if resolvedButtonObj.count and resolvedButtonObj.name then
|
||||
for i = 1, resolvedButtonObj.count do
|
||||
local child = getglobal(resolvedButtonObj.name .. i)
|
||||
if child and child.Hide then
|
||||
child:Hide()
|
||||
if resolvedButtonObj then
|
||||
if not enabled then
|
||||
if resolvedButtonObj.Hide then resolvedButtonObj:Hide() end
|
||||
if resolvedButtonObj.count and resolvedButtonObj.name then
|
||||
for i = 1, resolvedButtonObj.count do
|
||||
local child = getglobal(resolvedButtonObj.name .. i)
|
||||
if child and child.Hide then child:Hide() end
|
||||
end
|
||||
end
|
||||
elseif ZHunterMod_Saved[buttonName]["parent"] and ZHunterMod_Saved[buttonName]["parent"]["hide"] then
|
||||
if resolvedButtonObj.Hide then resolvedButtonObj:Hide() end
|
||||
else
|
||||
if resolvedButtonObj.Show then resolvedButtonObj:Show() end
|
||||
MTH_RefreshButtonGeometry(buttonName, resolvedButtonObj)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if ZHunterMod_Saved[buttonName]["parent"] and ZHunterMod_Saved[buttonName]["parent"]["hide"] then
|
||||
if resolvedButtonObj.Hide then
|
||||
resolvedButtonObj:Hide()
|
||||
-- If zBar is active, re-apply its layout so this button is included/excluded.
|
||||
-- When re-enabling a button, also un-exclude it from the bar so it appears
|
||||
-- in the bar rather than as a standalone button.
|
||||
if type(MTH_ZBar_GetSaved) == "function" then
|
||||
local zs = MTH_ZBar_GetSaved()
|
||||
if enabled and zs.visible[buttonName] == false then
|
||||
zs.visible[buttonName] = true
|
||||
end
|
||||
if zs.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if resolvedButtonObj.Show then
|
||||
resolvedButtonObj:Show()
|
||||
end
|
||||
MTH_RefreshButtonGeometry(buttonName, resolvedButtonObj)
|
||||
end
|
||||
|
||||
local function MTH_GetButtonItemList(buttonName)
|
||||
@@ -170,7 +172,7 @@ local function MTH_GetButtonItemList(buttonName)
|
||||
if rangedList and table.getn(rangedList) > 0 then
|
||||
return rangedList
|
||||
end
|
||||
elseif buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" or buttonName == "zButtonToys" then
|
||||
elseif buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" or buttonName == "zButtonToys" or buttonName == "zButtonCraft" then
|
||||
if ZHunterMod_Saved and ZHunterMod_Saved[buttonName] and ZHunterMod_Saved[buttonName]["spells"] then
|
||||
return ZHunterMod_Saved[buttonName]["spells"]
|
||||
end
|
||||
@@ -212,7 +214,7 @@ local function MTH_EnsureAmmoOptionsWatcher()
|
||||
end
|
||||
|
||||
local function MTH_ZB_GetDefaultEnabled(buttonName)
|
||||
if buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" or buttonName == "zButtonToys" or buttonName == "zButtonRanged" then
|
||||
if buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" or buttonName == "zButtonToys" or buttonName == "zButtonCraft" or buttonName == "zButtonRanged" then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
@@ -269,6 +271,27 @@ local function MTH_ZB_EnsureButtonOptionDefaults(buttonName)
|
||||
end
|
||||
saved["parent"]["circle"] = saved["parent"]["circle"] and true or false
|
||||
|
||||
if buttonName == "zButtonAspect" or buttonName == "zButtonTrack" or buttonName == "zButtonPet" then
|
||||
if saved["parent"]["smart"] == nil then
|
||||
saved["parent"]["smart"] = true
|
||||
end
|
||||
saved["parent"]["smart"] = saved["parent"]["smart"] and true or false
|
||||
end
|
||||
|
||||
if buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" then
|
||||
if saved["parent"]["random"] == nil then
|
||||
saved["parent"]["random"] = false
|
||||
end
|
||||
saved["parent"]["random"] = saved["parent"]["random"] and true or false
|
||||
end
|
||||
|
||||
if buttonName == "zButtonMounts" then
|
||||
if saved["parent"]["aqfilter"] == nil then
|
||||
saved["parent"]["aqfilter"] = true
|
||||
end
|
||||
saved["parent"]["aqfilter"] = saved["parent"]["aqfilter"] and true or false
|
||||
end
|
||||
|
||||
if saved["firstbutton"] ~= "LEFT" and saved["firstbutton"] ~= "RIGHT" then
|
||||
saved["firstbutton"] = "RIGHT"
|
||||
end
|
||||
@@ -350,8 +373,17 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
end)
|
||||
end
|
||||
|
||||
local parentSection = MTH_ZB_EnsureSection(containerName.."ParentSection", MTH_ZB_L("ZB_SECTION_PARENT_BUTTON", "Parent Button"), -52, 116)
|
||||
local childrenSection = MTH_ZB_EnsureSection(containerName.."ChildrenSection", MTH_ZB_L("ZB_SECTION_CHILDREN_BUTTONS", "Children Buttons"), -184, 310)
|
||||
local parentSectionHeight = 116
|
||||
if buttonName == "zButtonAspect" or buttonName == "zButtonTrack" or buttonName == "zButtonPet" then
|
||||
parentSectionHeight = 200
|
||||
elseif buttonName == "zButtonMounts" then
|
||||
parentSectionHeight = 225
|
||||
elseif buttonName == "zButtonCompanions" then
|
||||
parentSectionHeight = 196
|
||||
end
|
||||
local childrenSectionY = -52 - parentSectionHeight - 16
|
||||
local parentSection = MTH_ZB_EnsureSection(containerName.."ParentSection", MTH_ZB_L("ZB_SECTION_PARENT_BUTTON", "Parent Button"), -52, parentSectionHeight)
|
||||
local childrenSection = MTH_ZB_EnsureSection(containerName.."ChildrenSection", MTH_ZB_L("ZB_SECTION_CHILDREN_BUTTONS", "Children Buttons"), childrenSectionY, 290)
|
||||
|
||||
local parentYOffset = -18
|
||||
local childrenYOffset = -18
|
||||
@@ -478,7 +510,7 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
end
|
||||
end
|
||||
end
|
||||
childrenYOffset = childrenYOffset - 50
|
||||
childrenYOffset = childrenYOffset - 40
|
||||
|
||||
local showTooltip = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowTooltip", MTH_ZB_L("ZB_LABEL_SHOW_TOOLTIP", "Show Tooltip"), childrenYOffset)
|
||||
if showTooltip then
|
||||
@@ -496,7 +528,7 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
if btnObj then btnObj.tooltip = checked end
|
||||
end)
|
||||
end
|
||||
childrenYOffset = childrenYOffset - 25
|
||||
childrenYOffset = childrenYOffset - 20
|
||||
|
||||
if buttonName == "zButtonAmmo" then
|
||||
local showAmmoName = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowAmmoName", MTH_ZB_L("ZB_LABEL_SHOW_AMMO_NAME", "Show ammo name"), childrenYOffset)
|
||||
@@ -524,9 +556,9 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
end
|
||||
end)
|
||||
end
|
||||
childrenYOffset = childrenYOffset - 35
|
||||
childrenYOffset = childrenYOffset - 25
|
||||
else
|
||||
childrenYOffset = childrenYOffset - 35
|
||||
childrenYOffset = childrenYOffset - 25
|
||||
end
|
||||
|
||||
local mainSize = MTH_CreateSlider(parentSection or container, containerName.."MainButtonSize", MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"), 10, 100, 1, parentYOffset)
|
||||
@@ -585,6 +617,98 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
end)
|
||||
end
|
||||
|
||||
-- Smart Parent: Aspect/Track/Pet
|
||||
if buttonName == "zButtonAspect" or buttonName == "zButtonTrack" or buttonName == "zButtonPet" then
|
||||
parentYOffset = parentYOffset - 25
|
||||
local smartParent = MTH_CreateCheckbox(parentSection or container, containerName.."SmartParent", MTH_ZB_L("ZB_LABEL_SMART_PARENT", "Smart Parent"), parentYOffset)
|
||||
if smartParent then
|
||||
smartParent:SetChecked(saved["parent"]["smart"])
|
||||
smartParent.buttonName = buttonName
|
||||
smartParent.buttonObj = buttonObj
|
||||
smartParent:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
local checked = this:GetChecked() == 1
|
||||
ZHunterMod_Saved[this.buttonName]["parent"]["smart"] = checked
|
||||
local btn = this.buttonObj or getglobal(this.buttonName)
|
||||
if btn then
|
||||
if not checked then
|
||||
local firstChild = getglobal(this.buttonName .. "1")
|
||||
if firstChild and firstChild.id then
|
||||
btn.id = firstChild.id
|
||||
ZSpellButton_UpdateButton(btn)
|
||||
ZSpellButton_UpdateCooldown(btn)
|
||||
end
|
||||
else
|
||||
local setupFunc = getglobal(this.buttonName .. "_SetupSizeAndPosition")
|
||||
if type(setupFunc) == "function" then
|
||||
setupFunc()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
local smartDescText
|
||||
if buttonName == "zButtonAspect" then
|
||||
smartDescText = "When ON, the parent toggles between your 1st and 2nd aspect. If the 1st is active, the parent shows the 2nd and vice-versa. Useful for quickly swapping between two aspects in combat. When OFF, the parent always shows the 1st aspect."
|
||||
elseif buttonName == "zButtonTrack" then
|
||||
smartDescText = "When ON, the parent toggles between your 1st and 2nd tracking. If the 1st is active, the parent shows the 2nd and vice-versa. Great for PvP to swap between Track Hidden and Track Humanoids. When OFF, the parent always shows the 1st tracking."
|
||||
elseif buttonName == "zButtonPet" then
|
||||
smartDescText = "When ON, the parent dynamically changes based on your pet state: Revive if dead, Mend if hurt, Feed if unhappy, or your default spell otherwise. When OFF, the parent always shows the 1st spell."
|
||||
else
|
||||
smartDescText = "Dynamically swap the parent icon based on context."
|
||||
end
|
||||
local smartDesc = (parentSection or container):CreateFontString(containerName.."SmartParentDesc", "ARTWORK", "GameFontNormalSmall")
|
||||
smartDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24)
|
||||
smartDesc:SetWidth(leftWidth - 50)
|
||||
smartDesc:SetJustifyH("LEFT")
|
||||
smartDesc:SetText(smartDescText)
|
||||
smartDesc:SetTextColor(0.5, 0.7, 1.0)
|
||||
end
|
||||
|
||||
-- Random Parent: Mounts/Companions
|
||||
if buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" then
|
||||
parentYOffset = parentYOffset - 25
|
||||
local randomParent = MTH_CreateCheckbox(parentSection or container, containerName.."RandomParent", MTH_ZB_L("ZB_LABEL_RANDOM_PARENT", "Random Parent"), parentYOffset)
|
||||
if randomParent then
|
||||
randomParent:SetChecked(saved["parent"]["random"])
|
||||
randomParent.buttonName = buttonName
|
||||
randomParent.buttonObj = buttonObj
|
||||
randomParent:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
local checked = this:GetChecked() == 1
|
||||
ZHunterMod_Saved[this.buttonName]["parent"]["random"] = checked
|
||||
end)
|
||||
end
|
||||
local randomDesc = (parentSection or container):CreateFontString(containerName.."RandomParentDesc", "ARTWORK", "GameFontNormalSmall")
|
||||
randomDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24)
|
||||
randomDesc:SetWidth(leftWidth - 50)
|
||||
randomDesc:SetJustifyH("LEFT")
|
||||
randomDesc:SetText(MTH_ZB_L("ZB_DESC_RANDOM_PARENT", "Pick a random child as parent on each login and after each use."))
|
||||
randomDesc:SetTextColor(0.5, 0.7, 1.0)
|
||||
end
|
||||
|
||||
-- AQ Mount Filter: Mounts only
|
||||
if buttonName == "zButtonMounts" then
|
||||
parentYOffset = parentYOffset - 45
|
||||
local aqFilter = MTH_CreateCheckbox(parentSection or container, containerName.."AQFilter", MTH_ZB_L("ZB_LABEL_AQ_FILTER", "AQ Mount Filter"), parentYOffset)
|
||||
if aqFilter then
|
||||
aqFilter:SetChecked(saved["parent"]["aqfilter"])
|
||||
aqFilter.buttonName = buttonName
|
||||
aqFilter.buttonObj = buttonObj
|
||||
aqFilter:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
local checked = this:GetChecked() == 1
|
||||
ZHunterMod_Saved[this.buttonName]["parent"]["aqfilter"] = checked
|
||||
end)
|
||||
end
|
||||
local aqDesc = (parentSection or container):CreateFontString(containerName.."AQFilterDesc", "ARTWORK", "GameFontNormalSmall")
|
||||
aqDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24)
|
||||
aqDesc:SetWidth(leftWidth - 50)
|
||||
aqDesc:SetJustifyH("LEFT")
|
||||
aqDesc:SetText(MTH_ZB_L("ZB_DESC_AQ_FILTER", "Hide Qiraji mounts outside AQ40. Show only Qiraji inside."))
|
||||
aqDesc:SetTextColor(0.5, 0.7, 1.0)
|
||||
end
|
||||
|
||||
MTH_SetButtonEnabledState(buttonName, buttonObj, saved["enabled"] and true or false)
|
||||
|
||||
local advHeader = container:CreateFontString(containerName.."AdvHeader", "ARTWORK", "GameFontHighlight")
|
||||
@@ -714,6 +838,303 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma
|
||||
end
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- zBar options
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
function MTH_SetupZBarOptions()
|
||||
if not MTH_ZBUTTONS_READY then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsZBar")
|
||||
if not container then return end
|
||||
MTH_ClearContainer(container)
|
||||
|
||||
if type(MTH_ZBar_GetSaved) ~= "function" then
|
||||
local err = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
err:SetPoint("CENTER", container, "CENTER", 0, 0)
|
||||
err:SetText("zBar module not loaded")
|
||||
err:SetTextColor(1, 0.5, 0.5)
|
||||
return
|
||||
end
|
||||
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
local containerWidth = container:GetWidth() or 500
|
||||
local leftWidth = 260
|
||||
local rightX = leftWidth + 44
|
||||
local rightWidth = containerWidth - rightX - 12
|
||||
|
||||
-- ===== Enable checkbox =====
|
||||
local enableBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarEnable",
|
||||
"Enable zBar — group all zButtons into a single draggable bar", -8)
|
||||
if enableBtn then
|
||||
enableBtn:SetChecked(s.enabled and true or false)
|
||||
enableBtn:SetScript("OnClick", function()
|
||||
if type(MTH_ZBar_SetEnabled) == "function" then
|
||||
MTH_ZBar_SetEnabled(this:GetChecked() == 1)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ===== Direction header =====
|
||||
local dirLabel = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
||||
dirLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -46)
|
||||
dirLabel:SetText("Bar Direction")
|
||||
|
||||
-- ===== Horizontal radio =====
|
||||
local horizBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarHoriz",
|
||||
"Horizontal (side by side)", -64)
|
||||
if horizBtn then
|
||||
horizBtn:SetChecked(s.direction ~= "VERTICAL")
|
||||
horizBtn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.direction = "HORIZONTAL"
|
||||
-- Auto-correct childexpand to a valid side for a horizontal bar
|
||||
if sv.childexpand ~= "TOP" and sv.childexpand ~= "BOTTOM" then
|
||||
sv.childexpand = "BOTTOM"
|
||||
end
|
||||
local v = getglobal("MetaHuntOptionsZBarVert")
|
||||
if v then v:SetChecked(false) end
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ===== Vertical radio =====
|
||||
local vertBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarVert",
|
||||
"Vertical (stacked)", -90)
|
||||
if vertBtn then
|
||||
vertBtn:SetChecked(s.direction == "VERTICAL")
|
||||
vertBtn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.direction = "VERTICAL"
|
||||
-- Auto-correct childexpand to a valid side for a vertical bar
|
||||
if sv.childexpand ~= "LEFT" and sv.childexpand ~= "RIGHT" then
|
||||
sv.childexpand = "RIGHT"
|
||||
end
|
||||
local h = getglobal("MetaHuntOptionsZBarHoriz")
|
||||
if h then h:SetChecked(false) end
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ===== Children expand side (contextual to bar direction) =====
|
||||
local expandLbl = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
||||
expandLbl:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -120)
|
||||
if s.direction == "VERTICAL" then
|
||||
expandLbl:SetText("Children expand side (Vertical bar):")
|
||||
else
|
||||
expandLbl:SetText("Children expand side (Horizontal bar):")
|
||||
end
|
||||
|
||||
local exp1Val, exp2Val, exp1Lbl, exp2Lbl
|
||||
if s.direction == "VERTICAL" then
|
||||
exp1Val = "LEFT" ; exp1Lbl = "Left"
|
||||
exp2Val = "RIGHT" ; exp2Lbl = "Right"
|
||||
else
|
||||
exp1Val = "TOP" ; exp1Lbl = "Top (above)"
|
||||
exp2Val = "BOTTOM" ; exp2Lbl = "Bottom (below)"
|
||||
end
|
||||
|
||||
local exp1Btn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarExp1", exp1Lbl, -138, 30)
|
||||
if exp1Btn then
|
||||
exp1Btn:SetChecked(s.childexpand == exp1Val)
|
||||
exp1Btn.zbarVal = exp1Val
|
||||
exp1Btn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.childexpand = this.zbarVal
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local exp2Btn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarExp2", exp2Lbl, -164, 30)
|
||||
if exp2Btn then
|
||||
exp2Btn:SetChecked(s.childexpand == exp2Val)
|
||||
exp2Btn.zbarVal = exp2Val
|
||||
exp2Btn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.childexpand = this.zbarVal
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ===== Children layout (how children #2+ are arranged relative to #1) =====
|
||||
local arrLbl = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
||||
arrLbl:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -196)
|
||||
arrLbl:SetText("Children layout:")
|
||||
|
||||
local arrVBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarArrV",
|
||||
"Vertical (stacked column)", -214, 30)
|
||||
if arrVBtn then
|
||||
arrVBtn:SetChecked((s.childarrange or "VERTICAL") == "VERTICAL")
|
||||
arrVBtn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.childarrange = "VERTICAL"
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local arrHBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarArrH",
|
||||
"Horizontal (side-by-side row)", -240, 30)
|
||||
if arrHBtn then
|
||||
arrHBtn:SetChecked((s.childarrange or "VERTICAL") == "HORIZONTAL")
|
||||
arrHBtn:SetScript("OnClick", function()
|
||||
if this:GetChecked() == 1 then
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.childarrange = "HORIZONTAL"
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
else
|
||||
this:SetChecked(true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- ===== Spacing slider =====
|
||||
local spacingSlider = MTH_CreateSlider(container, "MetaHuntOptionsZBarSpacing",
|
||||
"Spacing (px)", 0, 30, 1, -300)
|
||||
if spacingSlider then
|
||||
spacingSlider:SetWidth(leftWidth - 40)
|
||||
spacingSlider:SetValue(tonumber(s.spacing) or 4)
|
||||
spacingSlider.onChange = function(val)
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.spacing = math.floor((tonumber(val) or 4) + 0.5)
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== Button size slider =====
|
||||
local sizeSlider = MTH_CreateSlider(container, "MetaHuntOptionsZBarSize",
|
||||
"Button Size", 10, 100, 1, -360)
|
||||
if sizeSlider then
|
||||
sizeSlider:SetWidth(leftWidth - 40)
|
||||
sizeSlider:SetValue(tonumber(s.size) or 36)
|
||||
sizeSlider.onChange = function(val)
|
||||
if type(MTH_ZBar_SetSize) == "function" then
|
||||
MTH_ZBar_SetSize(val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== Right column: Bar Order =====
|
||||
local orderHeader = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
||||
orderHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightX, -10)
|
||||
orderHeader:SetText("Bar Order")
|
||||
|
||||
local LABELS = MTH_ZBar_GetAllButtonLabels()
|
||||
local numBars = table.getn(s.order)
|
||||
local listY = -35
|
||||
local downX = rightX + 28
|
||||
local upX = downX + 22
|
||||
local labelX = upX + 26
|
||||
local labelWidth = rightWidth - (labelX - rightX) - 8
|
||||
if labelWidth < 80 then labelWidth = 80 end
|
||||
|
||||
for i = 1, numBars do
|
||||
local buttonName = s.order[i]
|
||||
local displayName = (LABELS and LABELS[buttonName]) or buttonName
|
||||
|
||||
-- Include/exclude visibility toggle
|
||||
local toggleName = "MetaHuntOptionsZBarToggle" .. i
|
||||
local showToggle = CreateFrame("CheckButton", toggleName, container, "UICheckButtonTemplate")
|
||||
if showToggle then
|
||||
showToggle:ClearAllPoints()
|
||||
showToggle:SetPoint("TOPLEFT", container, "TOPLEFT", rightX, listY + 2)
|
||||
local tText = getglobal(toggleName .. "Text")
|
||||
if tText then tText:SetText("") end
|
||||
showToggle:SetChecked(s.visible[buttonName] ~= false)
|
||||
showToggle.zbarName = buttonName
|
||||
showToggle:SetScript("OnClick", function()
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
sv.visible[this.zbarName] = (this:GetChecked() == 1)
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Move Down (-)
|
||||
local downBtn = CreateFrame("Button", nil, container, "UIPanelButtonTemplate")
|
||||
downBtn:SetPoint("TOPLEFT", container, "TOPLEFT", downX, listY)
|
||||
downBtn:SetWidth(20)
|
||||
downBtn:SetHeight(20)
|
||||
downBtn:SetText("-")
|
||||
downBtn.zbarIndex = i
|
||||
downBtn:SetScript("OnClick", function()
|
||||
local idx = this.zbarIndex
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
local tot = table.getn(sv.order)
|
||||
if idx < tot then
|
||||
local tmp = sv.order[idx + 1]
|
||||
sv.order[idx + 1] = sv.order[idx]
|
||||
sv.order[idx] = tmp
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Move Up (+)
|
||||
local upBtn = CreateFrame("Button", nil, container, "UIPanelButtonTemplate")
|
||||
upBtn:SetPoint("TOPLEFT", container, "TOPLEFT", upX, listY)
|
||||
upBtn:SetWidth(20)
|
||||
upBtn:SetHeight(20)
|
||||
upBtn:SetText("+")
|
||||
upBtn.zbarIndex = i
|
||||
upBtn:SetScript("OnClick", function()
|
||||
local idx = this.zbarIndex
|
||||
local sv = MTH_ZBar_GetSaved()
|
||||
if idx > 1 then
|
||||
local tmp = sv.order[idx - 1]
|
||||
sv.order[idx - 1] = sv.order[idx]
|
||||
sv.order[idx] = tmp
|
||||
MTH_ResetAndSelectOptionsTab("ZBar")
|
||||
if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Bar name label
|
||||
local lbl = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
lbl:SetPoint("TOPLEFT", container, "TOPLEFT", labelX, listY)
|
||||
lbl:SetText(displayName)
|
||||
lbl:SetWidth(labelWidth)
|
||||
lbl:SetJustifyH("LEFT")
|
||||
|
||||
listY = listY - 25
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_SetupPetOptions()
|
||||
if not MTH_ZBUTTONS_READY then return end
|
||||
MTH_SetupButtonOptions("MetaHuntOptionsPet", "zButtonPet", "ZPet", 10)
|
||||
@@ -773,6 +1194,13 @@ function MTH_SetupToysOptions()
|
||||
MTH_SetupButtonOptions("MetaHuntOptionsToys", "zButtonToys", "ZToys", maxButtons)
|
||||
end
|
||||
|
||||
function MTH_SetupCraftOptions()
|
||||
if not MTH_ZBUTTONS_READY then return end
|
||||
local itemList = MTH_GetButtonItemList("zButtonCraft")
|
||||
local maxButtons = itemList and table.getn(itemList) or 1
|
||||
MTH_SetupButtonOptions("MetaHuntOptionsCraft", "zButtonCraft", "ZCraft", maxButtons)
|
||||
end
|
||||
|
||||
function MTH_SetupSmartAmmoOptions()
|
||||
if not MTH_ZBUTTONS_READY then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsSmartAmmo")
|
||||
@@ -1095,7 +1523,7 @@ function MTH_SetupGeneralOptions()
|
||||
end
|
||||
end)
|
||||
end
|
||||
ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadHelp", "Falls back to any available ammo when preferred ammo is missing.", -154)
|
||||
ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadHelp", "Falls back to any available ammo when equipped ammo is out of stock.", -154)
|
||||
|
||||
local weaponSwapEnabled = type(MTHSmartAmmo_GetWeaponSwapEnabled) == "function" and MTHSmartAmmo_GetWeaponSwapEnabled() and true or false
|
||||
local weaponSwapToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoWeaponSwapToggle", "Enable Weapon-Swap Auto Ammo", -180, weaponSwapEnabled)
|
||||
@@ -1277,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
|
||||
|
||||
+2
-1
@@ -64,7 +64,7 @@
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<!-- Content Areas -->
|
||||
<Frame name="$parentZBar" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentGeneral" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentProfiles" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentMessages" hidden="true" enableMouse="true"/>
|
||||
@@ -77,6 +77,7 @@
|
||||
<Frame name="$parentMounts" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCompanions" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentToys" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCraft" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentSmartAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentFeedOMatic" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAutoBuy" hidden="true" enableMouse="true"/>
|
||||
|
||||
@@ -10,7 +10,7 @@ VC.lastPublishedAt = nil
|
||||
VC.joinAt = nil
|
||||
VC.notified = false
|
||||
VC._invalidVersionWarned = false
|
||||
VC.maxPersistedPeers = 200
|
||||
VC.maxPersistedPeers = 10000
|
||||
VC.seenPeers = VC.seenPeers or {}
|
||||
|
||||
local function VC_GetTimeNow()
|
||||
@@ -141,7 +141,7 @@ end
|
||||
function VC:PrunePersistedPeers(limit)
|
||||
local maxCount = tonumber(limit) or tonumber(self.maxPersistedPeers) or 200
|
||||
if maxCount <= 0 then
|
||||
maxCount = 200
|
||||
maxCount = 10000
|
||||
end
|
||||
if type(self.seenPeers) ~= "table" then
|
||||
return
|
||||
|
||||
@@ -9,6 +9,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Bat",
|
||||
["named"] = 20,
|
||||
["coords"] = 18,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
@@ -25,6 +26,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Bear",
|
||||
["named"] = 45,
|
||||
["coords"] = 45,
|
||||
["stats"] = { ["health"] = 1.08, ["armor"] = 1.05, ["damage"] = 0.91 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
@@ -47,6 +49,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Boar",
|
||||
["named"] = 45,
|
||||
["coords"] = 87,
|
||||
["stats"] = { ["health"] = 1.04, ["armor"] = 1.09, ["damage"] = 0.9 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
@@ -69,6 +72,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Vulture",
|
||||
["named"] = 36,
|
||||
["coords"] = 101,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
@@ -86,6 +90,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Cat",
|
||||
["named"] = 85,
|
||||
["coords"] = 88,
|
||||
["stats"] = { ["health"] = 0.98, ["armor"] = 1, ["damage"] = 1.1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
@@ -105,6 +110,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Crab",
|
||||
["named"] = 32,
|
||||
["coords"] = 74,
|
||||
["stats"] = { ["health"] = 0.96, ["armor"] = 1.13, ["damage"] = 0.95 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fish",
|
||||
@@ -123,6 +129,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Crocolisk",
|
||||
["named"] = 27,
|
||||
["coords"] = 71,
|
||||
["stats"] = { ["health"] = 0.95, ["armor"] = 1.1, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
@@ -140,6 +147,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "ability_hunter_aspectofthefox",
|
||||
["named"] = 9,
|
||||
["coords"] = 51,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
@@ -157,6 +165,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Gorilla",
|
||||
["named"] = 24,
|
||||
["coords"] = 117,
|
||||
["stats"] = { ["health"] = 1.04, ["armor"] = 1, ["damage"] = 1.02 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
@@ -172,6 +181,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Hyena",
|
||||
["named"] = 26,
|
||||
["coords"] = 36,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
@@ -189,6 +199,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Owl",
|
||||
["named"] = 16,
|
||||
["coords"] = 59,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
@@ -205,6 +216,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Raptor",
|
||||
["named"] = 51,
|
||||
["coords"] = 115,
|
||||
["stats"] = { ["health"] = 0.95, ["armor"] = 1.03, ["damage"] = 1.1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
@@ -222,6 +234,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Scorpid",
|
||||
["named"] = 38,
|
||||
["coords"] = 36,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.1, ["damage"] = 0.94 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
@@ -237,6 +250,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Serpent1",
|
||||
["named"] = 10,
|
||||
["coords"] = 13,
|
||||
["stats"] = { ["health"] = 1.03, ["armor"] = 1.03, ["damage"] = 1.135 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
@@ -254,6 +268,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Spider",
|
||||
["named"] = 91,
|
||||
["coords"] = 213,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
@@ -269,6 +284,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_TallStrider",
|
||||
["named"] = 15,
|
||||
["coords"] = 112,
|
||||
["stats"] = { ["health"] = 1.05, ["armor"] = 1, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
@@ -286,6 +302,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Turtle",
|
||||
["named"] = 25,
|
||||
["coords"] = 42,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.13, ["damage"] = 0.9 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fish",
|
||||
@@ -303,6 +320,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_WindSerpent",
|
||||
["named"] = 27,
|
||||
["coords"] = 42,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"fish",
|
||||
"cheese",
|
||||
@@ -321,6 +339,7 @@ MTH_DS_Families = {
|
||||
["icon"] = "Ability_Hunter_Pet_Wolf",
|
||||
["named"] = 81,
|
||||
["coords"] = 99,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
|
||||
+1207
-1207
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -298,7 +301,7 @@ MTH_DS_WMAZones = {
|
||||
[42] = { pfqAreaId=148, mapId=1, left=2941.67, right=-3608.33, top=8333.33, bottom=3966.67, width=6550.00, height=4366.66 }, -- Darkshore
|
||||
[43] = { pfqAreaId=331, mapId=1, left=1700.00, right=-4066.67, top=4672.92, bottom=829.17, width=5766.67, height=3843.75 }, -- Ashenvale
|
||||
[61] = { pfqAreaId=400, mapId=1, left=-433.33, right=-4833.33, top=-3966.67, bottom=-6900.00, width=4400.00, height=2933.33 }, -- ThousandNeedles
|
||||
[81] = { pfqAreaId=406, mapId=1, left=3949.00, right=-2069.00, top=3441.00, bottom=-569.00, width=6018.00, height=4010.00 }, -- StonetalonMountains
|
||||
[81] = { pfqAreaId=406, mapId=1, left=3245.83, right=-1637.50, top=2916.67, bottom=-339.58, width=4883.33, height=3256.25 }, -- StonetalonMountains
|
||||
[101] = { pfqAreaId=405, mapId=1, left=4233.33, right=-262.50, top=452.08, bottom=-2545.83, width=4495.83, height=2997.91 }, -- Desolace
|
||||
[121] = { pfqAreaId=357, mapId=1, left=5441.67, right=-1508.33, top=-2366.67, bottom=-7000.00, width=6950.00, height=4633.33 }, -- Feralas
|
||||
[141] = { pfqAreaId=15, mapId=1, left=-975.00, right=-6225.00, top=-2033.33, bottom=-5533.33, width=5250.00, height=3500.00 }, -- Dustwallow
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -8,6 +8,8 @@ MTH_LocaleData.ui.enUS = {
|
||||
BINDING_HEADER_ZTrapHeader = "[MetaHunt] ZTrap Buttons",
|
||||
BINDING_HEADER_ZPetHeader = "[MetaHunt] ZPet Buttons",
|
||||
BINDING_HEADER_ZAmmoHeader = "[MetaHunt] ZAmmo Buttons",
|
||||
BINDING_HEADER_ExpAmmoHeader = "[MetaHunt] MM Widget",
|
||||
BINDING_NAME_EXPAMMOACTION = "MM Widget Action",
|
||||
|
||||
FEEDOMATIC_NAME = "Feed-O-Matic",
|
||||
FEEDOMATIC_DESC = "Helps a Hunter keep his pets well fed",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
local MTH_AutoBuy = {
|
||||
name = "autobuy",
|
||||
enabled = false,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
"MERCHANT_SHOW",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
local MTH_AutoQuest = {
|
||||
name = "autoquest",
|
||||
enabled = false,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"GOSSIP_SHOW",
|
||||
"QUEST_GREETING",
|
||||
|
||||
@@ -28,7 +28,6 @@ function MTH_ChronometerHunterInstallData(engine)
|
||||
engine:AddTimer(engine.SPELL, BS["Flare"], 30, 0, 0, 0, { cr = "YELLOW", rc = true })
|
||||
engine:AddTimer(engine.SPELL, BS["Hunter's Mark"], 120, 1, 0, 0, { cr = "RED", rc = true })
|
||||
engine:AddTimer(engine.SPELL, BS["Immolation Trap"], 60, 0, 0, 0, { gr = 1, cr = "ORANGE", ea = { [BS["Immolation Trap Effect"]] = 60 } })
|
||||
engine:AddTimer(engine.SPELL, BS["Aimed Shot"], 20, 0, 0, 0, { cr = "RED", rc = true, d = { tn = BS["Swiftshot"], tb = -2, ts = -2 } }) -- base 20s; Swiftshot reduces by 2s/rank (max -6s at rank 3)
|
||||
engine:AddTimer(engine.SPELL, BS["Kill Command"], 12, 0, 0, 0, { cr = "RED", rc = true })
|
||||
engine:AddTimer(engine.SPELL, BS["Rapid Fire"], 15, 0, 1, 1, { cr = "RED" })
|
||||
engine:AddTimer(engine.SPELL, BS["Scare Beast"], 10, 1, 0, 0, { cr = "BLUE", rc = true, d = { rs = 5 } })
|
||||
@@ -49,7 +48,7 @@ function MTH_ChronometerHunterInstallData(engine)
|
||||
engine:AddTimer(engine.EVENT, BS["Immolation Trap Effect"], 15, 1, 0, 1, { gr = 1, cr = "ORANGE", xn = BS["Immolation Trap"] })
|
||||
engine:AddTimer(engine.EVENT, BS["Improved Concussive Shot"], 3, 1, 0, 0, { a = 1, cr = "BLUE", xn = BS["Concussive Shot"] })
|
||||
engine:AddTimer(engine.EVENT, BS["Improved Wing Clip"], 5, 1, 0, 0, { a = 1, cr = "MAROON", xn = BS["Wing Clip"] })
|
||||
engine:AddTimer(engine.EVENT, BS["Lock and Load"], 10, 0, 1, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\ability_hunter_lockandload" }) -- 10s (tooltip confirmed; expires on Aimed Shot cast)
|
||||
engine:AddTimer(engine.EVENT, BS["Lock and Load"], 10, 0, 1, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\ability_hunter_lockandload" }) -- 10s (tooltip confirmed; expires when consumed)
|
||||
engine:AddTimer(engine.EVENT, BS["Scent of Blood"], 8, 0, 1, 1, { a = 1, cr = "RED" })
|
||||
engine:AddTimer(engine.EVENT, BS["Piercing Shot"], 8, 1, 0, 1, { a = 1, cr = "PURPLE", tx = "Interface\\Icons\\Ability_Hunter_DisarmingShot" })
|
||||
engine:AddTimer(engine.EVENT, BS["Entrapment"], 5, 1, 0, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\Spell_Nature_StrangleVines" })
|
||||
|
||||
@@ -58,6 +58,18 @@ Chronometer.dataSetup = {}
|
||||
|
||||
local latins = { I = 1, II = 2, III = 3, IV = 4, V = 5, VI = 6, VII = 7, VIII = 8, IX = 9, X = 10, XI = 11, XII = 12, XIII = 13, XIV = 14 }
|
||||
local BAR_GROUP = "MTHChronometer"
|
||||
|
||||
-- When growing up the group attachment point must flip so bars stack above the
|
||||
-- anchor rather than below it. CandyBar uses the stored group point as-is when
|
||||
-- positioning each bar, so SetCandyBarGroupGrowth alone is not enough.
|
||||
local function applyGrowup(self, growup)
|
||||
if growup then
|
||||
self:SetCandyBarGroupPoint(BAR_GROUP, "BOTTOM", self.anchor, "TOP", 0, 0)
|
||||
else
|
||||
self:SetCandyBarGroupPoint(BAR_GROUP, "TOP", self.anchor, "BOTTOM", 0, 0)
|
||||
end
|
||||
self:SetCandyBarGroupGrowth(BAR_GROUP, growup)
|
||||
end
|
||||
local PARSER_OWNER = "MTHChronometerHunter"
|
||||
|
||||
|
||||
@@ -556,7 +568,7 @@ function Chronometer:OnEnable()
|
||||
self._mth_enabled = true
|
||||
|
||||
self.profile = ensureProfile()
|
||||
self:SetCandyBarGroupGrowth(BAR_GROUP, self.profile.growup and true or false)
|
||||
applyGrowup(self, self.profile.growup and true or false)
|
||||
self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing or 0)
|
||||
if self.anchor then
|
||||
end
|
||||
@@ -650,15 +662,34 @@ function Chronometer:OnEnable()
|
||||
self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_SELF_DAMAGE", function(event, info) self:SELF_DAMAGE(event, info) end)
|
||||
self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF", function(event, info) self:SELF_DAMAGE(event, info) end)
|
||||
self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_FAILED_LOCALPLAYER", function(event, info) self:SPELL_FAILED(event, info) end)
|
||||
|
||||
-- NamPower: register authoritative self-aura events for instant bar
|
||||
-- kill and duration resync. The ParserLib chat paths remain active as
|
||||
-- they handle creature/target debuffs that NamPower cannot see.
|
||||
if MTH and MTH.nampower then
|
||||
self._nampowerSpellIdCache = {}
|
||||
self:RegisterEvent("BUFF_ADDED_SELF")
|
||||
self:RegisterEvent("BUFF_REMOVED_SELF")
|
||||
self:RegisterEvent("DEBUFF_ADDED_SELF")
|
||||
self:RegisterEvent("DEBUFF_REMOVED_SELF")
|
||||
end
|
||||
end
|
||||
|
||||
function Chronometer:VARIABLES_LOADED()
|
||||
self.profile = ensureProfile()
|
||||
if self._mth_enabled then
|
||||
applyGrowup(self, self.profile.growup and true or false)
|
||||
self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing or 0)
|
||||
end
|
||||
MTH_CHRON_RestoreAnchorFromProfile(self, "VARIABLES_LOADED")
|
||||
end
|
||||
|
||||
function Chronometer:PLAYER_ENTERING_WORLD()
|
||||
self.profile = ensureProfile()
|
||||
if self._mth_enabled then
|
||||
applyGrowup(self, self.profile.growup and true or false)
|
||||
self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing or 0)
|
||||
end
|
||||
MTH_CHRON_RestoreAnchorFromProfile(self, "PLAYER_ENTERING_WORLD")
|
||||
end
|
||||
|
||||
@@ -1249,6 +1280,85 @@ function Chronometer:CreateAnchor(text, r, g, b)
|
||||
return frame
|
||||
end
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
-- NamPower self-aura handlers
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
|
||||
-- Helper: resolve spellId → spell name, with cache
|
||||
local function NP_ResolveName(cache, spellId)
|
||||
if not spellId or spellId == 0 then return nil end
|
||||
local name = cache[spellId]
|
||||
if name then return name end
|
||||
name = GetSpellRecField(spellId, "name")
|
||||
if name and name ~= "" then
|
||||
cache[spellId] = name
|
||||
return name
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- BUFF / DEBUFF removed from the player → kill matching self-bar instantly
|
||||
function Chronometer:BUFF_REMOVED_SELF()
|
||||
if not self.profile.fadeonfade then return end
|
||||
local spellId = arg3
|
||||
local name = NP_ResolveName(self._nampowerSpellIdCache, spellId)
|
||||
if not name then return end
|
||||
self:KillBar(name)
|
||||
end
|
||||
|
||||
Chronometer.DEBUFF_REMOVED_SELF = Chronometer.BUFF_REMOVED_SELF
|
||||
|
||||
-- BUFF / DEBUFF added to the player → resync running bar duration
|
||||
function Chronometer:BUFF_ADDED_SELF()
|
||||
local spellId = arg3
|
||||
local name = NP_ResolveName(self._nampowerSpellIdCache, spellId)
|
||||
if not name then return end
|
||||
|
||||
-- Find the matching self-bar
|
||||
local bar = nil
|
||||
for i = 1, 20 do
|
||||
if self.bars[i].id and self.bars[i].name == name then
|
||||
local tgt = self.bars[i].target
|
||||
if tgt == "none" or tgt == UnitName("player") then
|
||||
bar = self.bars[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not bar then return end
|
||||
|
||||
-- Get authoritative remaining duration from NamPower aura array
|
||||
local remaining = nil
|
||||
local auras = GetUnitField("player", "aura")
|
||||
if auras then
|
||||
for slot = 1, 48 do
|
||||
if auras[slot] == spellId then
|
||||
local _, ms = GetPlayerAuraDuration(slot - 1)
|
||||
if ms and ms > 0 then
|
||||
remaining = ms / 1000
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not remaining then
|
||||
-- Fallback: use base DBC duration
|
||||
local dms = GetSpellDuration(spellId)
|
||||
if dms and dms > 0 then
|
||||
remaining = dms / 1000
|
||||
end
|
||||
end
|
||||
if not remaining then return end
|
||||
|
||||
-- Resync: update total first (allows SetTimeLeft to extend), then set remaining
|
||||
self:SetCandyBarTime(bar.id, remaining)
|
||||
self:SetCandyBarTimeLeft(bar.id, remaining)
|
||||
end
|
||||
|
||||
Chronometer.DEBUFF_ADDED_SELF = Chronometer.BUFF_ADDED_SELF
|
||||
|
||||
-- ────────────────────────────────────────────────────────────
|
||||
|
||||
function Chronometer:COMBAT_DEATH(event, info)
|
||||
if not self.profile.fadeonkill then return end
|
||||
if info.type == "experience" then
|
||||
@@ -1399,7 +1509,9 @@ function Chronometer:UNIT_AURA(unit)
|
||||
end
|
||||
|
||||
local hasFeedAura = false
|
||||
if type(UnitBuff) == "function" then
|
||||
if type(MTH_FEED_HasPetFeedBuff) == "function" then
|
||||
hasFeedAura = MTH_FEED_HasPetFeedBuff()
|
||||
elseif type(UnitBuff) == "function" then
|
||||
for i = 1, 32 do
|
||||
local texture = UnitBuff("pet", i)
|
||||
if not texture then
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
local MTH_ChronometerModule = {
|
||||
name = "chronometer",
|
||||
enabled = true,
|
||||
version = "1.4.1",
|
||||
events = {},
|
||||
initialized = false,
|
||||
}
|
||||
|
||||
+746
-243
File diff suppressed because it is too large
Load Diff
@@ -125,6 +125,11 @@ FOM_FamilyToLegacyMap = {
|
||||
|
||||
|
||||
|
||||
-- [DISABLED] NO-BUFF quarantine: set to true to re-enable.
|
||||
-- When false, failed feeds are still logged but items are never
|
||||
-- blacklisted and the quarantine store is never consulted.
|
||||
local FOM_QUARANTINE_ENABLED = false;
|
||||
|
||||
local function FOM_GetServerProfile()
|
||||
return FOM_ServerProfiles[FOM_SERVER_PROFILE] or {};
|
||||
end
|
||||
@@ -186,6 +191,7 @@ local function FOM_GetFamilyKeyFromInfo(petInfo)
|
||||
end
|
||||
|
||||
local function FOM_RegisterNoBuffQuarantine(itemID, petInfo)
|
||||
if (not FOM_QUARANTINE_ENABLED) then return; end
|
||||
local numericItem = tonumber(itemID);
|
||||
if (numericItem == nil) then
|
||||
return;
|
||||
@@ -217,6 +223,7 @@ local function FOM_RegisterNoBuffQuarantine(itemID, petInfo)
|
||||
end
|
||||
|
||||
local function FOM_IsQuarantined(itemID, petLevel, familyKey)
|
||||
if (not FOM_QUARANTINE_ENABLED) then return false; end
|
||||
local numericItem = tonumber(itemID);
|
||||
local numericPet = tonumber(petLevel);
|
||||
if (numericItem == nil or numericPet == nil or numericPet <= 0) then
|
||||
@@ -1187,7 +1194,7 @@ function FOM_OnUpdate(elapsed)
|
||||
FOM_CoreFinalizeAttempt(FOM_LastFeedAttempt.coreAttemptId, "rejected", "no-buff");
|
||||
local currentPetInfo = MTH_FOM_GetCorePetInfo();
|
||||
FOM_RegisterNoBuffQuarantine(FOM_LastFeedAttempt.itemId, currentPetInfo);
|
||||
if (type(MTH_FEED_BlockItemForPetLevel) == "function") then
|
||||
if (FOM_QUARANTINE_ENABLED and type(MTH_FEED_BlockItemForPetLevel) == "function") then
|
||||
pcall(MTH_FEED_BlockItemForPetLevel,
|
||||
FOM_LastFeedAttempt.itemId,
|
||||
tonumber(currentPetInfo and currentPetInfo.level) or nil,
|
||||
@@ -1274,6 +1281,43 @@ function FOM_CanFeed()
|
||||
end
|
||||
|
||||
|
||||
-- NamPower path: scan aura array by spellId → name, no tooltip needed
|
||||
if MTH and MTH.nampower and type(GetUnitField) == "function" and type(GetSpellRecField) == "function" then
|
||||
local auras = GetUnitField("player", "aura")
|
||||
if type(auras) == "table" then
|
||||
local dontFeedNames = {
|
||||
["Shadowmeld"] = true,
|
||||
["Feign Death"] = true,
|
||||
["Food"] = true,
|
||||
["Drink"] = true,
|
||||
["Refreshment"] = true,
|
||||
}
|
||||
for i = 1, 32 do
|
||||
local sid = tonumber(auras[i])
|
||||
if sid and sid > 0 then
|
||||
local name = GetSpellRecField(sid, "name")
|
||||
if name then
|
||||
if dontFeedNames[name] then
|
||||
FOM_DebugLog("Can't feed; buff detected: " .. name)
|
||||
return false
|
||||
end
|
||||
if UnitLevel("player") >= 40 then
|
||||
local lname = string.lower(name)
|
||||
for _, mountSub in FOM_MOUNT_NAME_SUBSTRINGS do
|
||||
if string.find(lname, mountSub) then
|
||||
FOM_DebugLog("Can't feed; mounted (" .. name .. ").")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Legacy path: texture + tooltip scanning
|
||||
local buff, buffIndex;
|
||||
local dontFeedBuffTextures = {
|
||||
"Interface\\Icons\\Ability_Ambush", -- NE Shadowmeld (maybe not unique buff icon?)
|
||||
@@ -1936,7 +1980,7 @@ function FOM_RemoveFood(diet, food)
|
||||
end
|
||||
|
||||
-- Clear quarantine, core exception blocks, and RemovedFoods ban for a specific item (called when user explicitly re-adds via /fom add)
|
||||
local function FOM_ClearItemBans(itemID)
|
||||
function FOM_ClearItemBans(itemID)
|
||||
local numericItem = tonumber(itemID);
|
||||
if (numericItem == nil) then return; end
|
||||
-- Clear from RemovedFoods across all diet lists
|
||||
@@ -2076,6 +2120,9 @@ end
|
||||
|
||||
-- Check Feed Effect
|
||||
function FOM_HasFeedEffect()
|
||||
if type(MTH_FEED_HasPetFeedBuff) == "function" then
|
||||
return MTH_FEED_HasPetFeedBuff()
|
||||
end
|
||||
|
||||
local i = 1;
|
||||
local buff;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
local MTH_FeedOMatic = {
|
||||
name = "feedomatic",
|
||||
enabled = false,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
"MERCHANT_SHOW",
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
local MTH_ICU = {
|
||||
name = "icu",
|
||||
enabled = false,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
},
|
||||
|
||||
@@ -7,7 +7,6 @@ MTH_SA_MANAGED_HOOKS = true
|
||||
local MTH_SmartAmmo = {
|
||||
name = "smartammo",
|
||||
enabled = true,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
-- PLAYER_ENTERING_WORLD handled by engine's own frame
|
||||
|
||||
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>
|
||||
@@ -6,7 +6,6 @@
|
||||
local MTH_Tooltips = {
|
||||
name = "tooltips",
|
||||
enabled = true,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"UPDATE_MOUSEOVER_UNIT",
|
||||
"UNIT_NAME_UPDATE",
|
||||
@@ -1486,11 +1485,8 @@ local function MTH_TT_AddTooltip(unit)
|
||||
local ammoVendorTooltipsEnabled = MTH_TT_IsAmmoVendorTooltipsEnabled()
|
||||
local vendorInfo = ammoVendorTooltipsEnabled and MTH_TT_FindVendorInfo(name) or nil
|
||||
local row = beastTooltipsEnabled and MTH_TT_FindBeastRow(name) or nil
|
||||
local savedEntry = beastTooltipsEnabled
|
||||
and (type(MTH_BLS_FindSavedBeastByName) == "function")
|
||||
and MTH_BLS_FindSavedBeastByName(name) or nil
|
||||
local scorpokAdded = MTH_TT_AddScorpokTargetsTooltip(unit, name)
|
||||
if not row and not vendorInfo and not scorpokAdded and not savedEntry then
|
||||
if not row and not vendorInfo and not scorpokAdded then
|
||||
state.lookupMiss = (state.lookupMiss or 0) + 1
|
||||
MTH_TT_StateSet("lookup-miss", name, normalized)
|
||||
MTH_TT_Log("lookup miss: unit='" .. tostring(name) .. "' normalized='" .. tostring(normalized) .. "'")
|
||||
@@ -1514,13 +1510,7 @@ local function MTH_TT_AddTooltip(unit)
|
||||
MTH_TT_Log("vendor tooltip added: unit='" .. tostring(name) .. "'")
|
||||
end
|
||||
|
||||
-- Determine which abilities source to display.
|
||||
local abilitiesSource = nil
|
||||
if row then
|
||||
abilitiesSource = row.abilities
|
||||
elseif savedEntry and savedEntry.abilities and savedEntry.abilities ~= "" then
|
||||
abilitiesSource = savedEntry.abilities
|
||||
end
|
||||
local abilitiesSource = row and row.abilities or nil
|
||||
|
||||
if abilitiesSource then
|
||||
local abilities = MTH_TT_ParseAbilities(abilitiesSource)
|
||||
@@ -1546,11 +1536,6 @@ local function MTH_TT_AddTooltip(unit)
|
||||
end
|
||||
end
|
||||
|
||||
if savedEntry then
|
||||
GameTooltip:AddLine("Beast recorded by you", 1.0, 0.85, 0.0)
|
||||
addedAnything = 1
|
||||
end
|
||||
|
||||
if addedAnything then
|
||||
GameTooltip:Show()
|
||||
state.added = (state.added or 0) + 1
|
||||
|
||||
@@ -67,6 +67,8 @@ local function ZHunterMod_EnsureButtonFrame(buttonName)
|
||||
created:SetPoint("TOP", UIParent, "TOP", 0, -464)
|
||||
elseif buttonName == "zButtonToys" then
|
||||
created:SetPoint("TOP", UIParent, "TOP", 0, -518)
|
||||
elseif buttonName == "zButtonCraft" then
|
||||
created:SetPoint("TOP", UIParent, "TOP", 0, -572)
|
||||
else
|
||||
created:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
||||
end
|
||||
@@ -85,7 +87,7 @@ function ZHunterMod_DeferredInit()
|
||||
local mounts = ZHunterMod_EnsureButtonFrame("zButtonMounts")
|
||||
local companions = ZHunterMod_EnsureButtonFrame("zButtonCompanions")
|
||||
local toys = ZHunterMod_EnsureButtonFrame("zButtonToys")
|
||||
|
||||
local craft = ZHunterMod_EnsureButtonFrame("zButtonCraft")
|
||||
|
||||
-- Continue with all frames that exist; do not hard-abort when one optional frame is missing.
|
||||
local missing = {}
|
||||
@@ -98,12 +100,13 @@ function ZHunterMod_DeferredInit()
|
||||
if not mounts then table.insert(missing, "Mounts") end
|
||||
if not companions then table.insert(missing, "Companions") end
|
||||
if not toys then table.insert(missing, "Toys") end
|
||||
if not craft then table.insert(missing, "Craft") end
|
||||
|
||||
if table.getn(missing) > 0 then
|
||||
MTH_ZH_Print("[ZHUNTER] Warning: missing frames: " .. table.concat(missing, ", ") .. ". Continuing with available buttons.", "error")
|
||||
end
|
||||
|
||||
if not (pet or aspect or track or trap or ranged or ammo or mounts or companions or toys) then
|
||||
if not (pet or aspect or track or trap or ranged or ammo or mounts or companions or toys or craft) then
|
||||
MTH_ZH_Print("zhunter buttons not created - check for addon conflicts!", "error")
|
||||
return
|
||||
end
|
||||
@@ -178,6 +181,12 @@ function ZHunterMod_DeferredInit()
|
||||
end
|
||||
end
|
||||
|
||||
if craft and zButtonCraft_OnEvent then
|
||||
if ZHunterMod_SafeRunButtonInit(craft, zButtonCraft_OnEvent, "zButtonCraft") then
|
||||
initialized = initialized + 1
|
||||
end
|
||||
end
|
||||
|
||||
if initialized <= 0 then
|
||||
MTH_ZH_Print("zhunter buttons not created - check for addon conflicts!", "error")
|
||||
return
|
||||
|
||||
@@ -87,6 +87,7 @@ ZHUNTER_TRACK_MINERALS = "Find Minerals"
|
||||
ZHUNTER_TRACK_HERBS = "Find Herbs"
|
||||
ZHUNTER_TRACK_TREASURE = "Find Treasure"
|
||||
ZHUNTER_TRACK_TREES = "Find Trees"
|
||||
ZHUNTER_TRACK_FISH = "Find Fish"
|
||||
|
||||
ZHUNTER_TRAP_FREEZING = "Freezing Trap"
|
||||
ZHUNTER_TRAP_FROST = "Frost Trap"
|
||||
|
||||
@@ -138,7 +138,6 @@ local function ZHunterMod_UpdateButtonCount(parent, found)
|
||||
end
|
||||
|
||||
function ZSpellButton_SetExpandDirection(parent, direction)
|
||||
local button = getglobal(parent.name.."1")
|
||||
local offset = parent:GetWidth() / 36
|
||||
local circleVisible = true
|
||||
if type(MTH_ZH_GetSavedTable) == "function" and parent and parent.name then
|
||||
@@ -154,11 +153,14 @@ function ZSpellButton_SetExpandDirection(parent, direction)
|
||||
parent.circle:Hide()
|
||||
end
|
||||
end
|
||||
-- Children have not been created yet; circle state is already updated above.
|
||||
if not (parent and parent.name) then return end
|
||||
if circleVisible then
|
||||
offset = offset * 5
|
||||
else
|
||||
offset = offset * 3
|
||||
end
|
||||
local button = getglobal(parent.name.."1")
|
||||
button:ClearAllPoints()
|
||||
if direction == "TOP" then
|
||||
button:SetPoint("BOTTOM", parent, "TOP", 0, offset)
|
||||
@@ -217,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)
|
||||
@@ -683,28 +685,53 @@ function ZSpellButtonParent_OnLoad()
|
||||
if this.SetScript then
|
||||
this:SetScript("OnDragStart", function()
|
||||
if IsAltKeyDown() then
|
||||
this:StartMoving()
|
||||
this.isMoving = true
|
||||
if type(MTH_ZBar_IsActive) == "function" and MTH_ZBar_IsActive() then
|
||||
-- Alt+drag any bar button to move the whole zBar via its invisible anchor
|
||||
local anchor = type(MTH_ZBar_GetAnchor) == "function" and MTH_ZBar_GetAnchor()
|
||||
if anchor then
|
||||
anchor:StartMoving()
|
||||
anchor.isMoving = true
|
||||
end
|
||||
else
|
||||
this:StartMoving()
|
||||
this.isMoving = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
this:SetScript("OnDragStop", function()
|
||||
if not this then
|
||||
return
|
||||
if not this then return end
|
||||
if type(MTH_ZBar_IsActive) == "function" and MTH_ZBar_IsActive() then
|
||||
local anchor = type(MTH_ZBar_GetAnchor) == "function" and MTH_ZBar_GetAnchor()
|
||||
if anchor and anchor.isMoving then
|
||||
if anchor.StopMovingOrSizing then anchor:StopMovingOrSizing() end
|
||||
anchor.isMoving = false
|
||||
if type(MTH_ZBar_SavePosition) == "function" then MTH_ZBar_SavePosition() end
|
||||
if type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
end
|
||||
else
|
||||
if this.StopMovingOrSizing then this:StopMovingOrSizing() end
|
||||
ZSpellButton_SaveParentPosition(this)
|
||||
this.isMoving = false
|
||||
end
|
||||
if this.StopMovingOrSizing then
|
||||
this:StopMovingOrSizing()
|
||||
end
|
||||
ZSpellButton_SaveParentPosition(this)
|
||||
this.isMoving = false
|
||||
end)
|
||||
this:SetScript("OnMouseUp", function()
|
||||
if this and this.isMoving then
|
||||
if this.StopMovingOrSizing then
|
||||
this:StopMovingOrSizing()
|
||||
if this.StopMovingOrSizing then this:StopMovingOrSizing() end
|
||||
if not (type(MTH_ZBar_IsActive) == "function" and MTH_ZBar_IsActive()) then
|
||||
ZSpellButton_SaveParentPosition(this)
|
||||
end
|
||||
ZSpellButton_SaveParentPosition(this)
|
||||
this.isMoving = false
|
||||
end
|
||||
-- Fallback: finish anchor movement started from OnDragStart
|
||||
if type(MTH_ZBar_IsActive) == "function" and MTH_ZBar_IsActive() then
|
||||
local anchor = type(MTH_ZBar_GetAnchor) == "function" and MTH_ZBar_GetAnchor()
|
||||
if anchor and anchor.isMoving then
|
||||
if anchor.StopMovingOrSizing then anchor:StopMovingOrSizing() end
|
||||
anchor.isMoving = false
|
||||
if type(MTH_ZBar_SavePosition) == "function" then MTH_ZBar_SavePosition() end
|
||||
if type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,6 +22,7 @@ Loads all zhunter addon code and wraps it in the MTH module system
|
||||
<Include file="zButtonMounts.xml"/>
|
||||
<Include file="zButtonCompanions.xml"/>
|
||||
<Include file="zButtonToys.xml"/>
|
||||
<Include file="zButtonCraft.xml"/>
|
||||
|
||||
<!-- Load main ZHunterMod.lua FIRST so ZHunterMod_Saved table exists before button files use it -->
|
||||
<Include file="ZHunterMod.lua"/>
|
||||
@@ -47,6 +48,11 @@ Loads all zhunter addon code and wraps it in the MTH module system
|
||||
<Include file="zButtonCompanions.lua"/>
|
||||
|
||||
<Include file="zButtonToys.lua"/>
|
||||
|
||||
<Include file="zButtonCraft.lua"/>
|
||||
|
||||
<!-- zBar: unified draggable anchor bar (must load after all zButton files) -->
|
||||
<Include file="zBar.lua"/>
|
||||
|
||||
<!-- Deferred initialization - runs LAST after all parsing is complete -->
|
||||
<Include file="DeferredInit.lua"/>
|
||||
|
||||
@@ -8,7 +8,6 @@ MTH_ZH_MANAGED_HOOKS = true
|
||||
local MTH_ZHunter = {
|
||||
name = "zhunter",
|
||||
enabled = true,
|
||||
version = "1.4.1",
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
-- PLAYER_ENTERING_WORLD handled by bootstrap frame + adjustment frames
|
||||
@@ -59,6 +58,7 @@ local function MTH_ZH_ApplyDefaultWidgetSpawnLayoutOnce()
|
||||
"zButtonMounts",
|
||||
"zButtonCompanions",
|
||||
"zButtonToys",
|
||||
"zButtonCraft",
|
||||
}
|
||||
|
||||
local startY = -140
|
||||
@@ -191,6 +191,7 @@ MTH_ZH_SetButtonsVisible = function(visible)
|
||||
"zButtonMounts",
|
||||
"zButtonCompanions",
|
||||
"zButtonToys",
|
||||
"zButtonCraft",
|
||||
}
|
||||
|
||||
local function MTH_ZH_SetChildButtonsVisible(parentButton, parentName, show)
|
||||
@@ -303,6 +304,7 @@ local function MTH_ZH_SetAdjustmentHandlersActive(active)
|
||||
{ frame = "MTH_ZH_MountsAdjust", handler = "MTH_ZH_MountsAdjust_OnEvent", getter = "zButtonMounts_GetSaved" },
|
||||
{ frame = "MTH_ZH_CompanionsAdjust", handler = "MTH_ZH_CompanionsAdjust_OnEvent", getter = "zButtonCompanions_GetSaved" },
|
||||
{ frame = "MTH_ZH_ToysAdjust", handler = "MTH_ZH_ToysAdjust_OnEvent", getter = "zButtonToys_GetSaved" },
|
||||
{ frame = "MTH_ZH_CraftAdjust", handler = "MTH_ZH_CraftAdjust_OnEvent", getter = "zButtonCraft_GetSaved" },
|
||||
}
|
||||
|
||||
local function isFeatureEnabled(getterName)
|
||||
@@ -348,7 +350,9 @@ local function MTH_ZH_ClearRuntimeFeatureFlags()
|
||||
end
|
||||
|
||||
MTH_ZH_RestoreRuntimeFeatureFlags = function()
|
||||
return
|
||||
if type(MTH_ZBar_Init) == "function" then
|
||||
MTH_ZBar_Init()
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_ZHunter:init()
|
||||
@@ -372,6 +376,9 @@ function MTH_ZHunter:setEnabled(enabled)
|
||||
MTH_ZH_SetAdjustmentHandlersActive(false)
|
||||
MTH_ZH_SetButtonsVisible(false)
|
||||
MTH_ZH_SetAuxFramesVisible(false)
|
||||
-- Hide the zBar anchor so it doesn't float while the module is off
|
||||
local zbarAnchor = getglobal("MTH_ZBar_Anchor")
|
||||
if zbarAnchor then zbarAnchor:Hide() end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
-- zBar: Unified draggable anchor bar for all zButton parent buttons.
|
||||
-- When enabled, all included zButton parents are anchored to the zBar anchor
|
||||
-- in a configurable row (horizontal) or column (vertical).
|
||||
-- Individual Alt+drag on each zButton is suppressed while zBar is active.
|
||||
|
||||
local ZBAR_FRAME_NAME = "MTH_ZBar_Anchor"
|
||||
|
||||
local ZBAR_ALL_BUTTONS = {
|
||||
"zButtonAspect",
|
||||
"zButtonAmmo",
|
||||
"zButtonTrack",
|
||||
"zButtonTrap",
|
||||
"zButtonPet",
|
||||
"zButtonRanged",
|
||||
"zButtonMounts",
|
||||
"zButtonCompanions",
|
||||
"zButtonToys",
|
||||
"zButtonCraft",
|
||||
}
|
||||
|
||||
local ZBAR_BUTTON_LABELS = {
|
||||
zButtonAspect = "zAspect",
|
||||
zButtonAmmo = "zAmmo",
|
||||
zButtonTrack = "zTrack",
|
||||
zButtonTrap = "zTrap",
|
||||
zButtonPet = "zPet",
|
||||
zButtonRanged = "zRanged",
|
||||
zButtonMounts = "zMounts",
|
||||
zButtonCompanions = "zCompanions",
|
||||
zButtonToys = "zToys",
|
||||
zButtonCraft = "zCraft",
|
||||
}
|
||||
|
||||
function MTH_ZBar_GetAllButtons()
|
||||
return ZBAR_ALL_BUTTONS
|
||||
end
|
||||
|
||||
function MTH_ZBar_GetAllButtonLabels()
|
||||
return ZBAR_BUTTON_LABELS
|
||||
end
|
||||
|
||||
-- Returns (and initialises on first access) the _zbar saved-variable table.
|
||||
function MTH_ZBar_GetSaved()
|
||||
local root = MTH_ZH_GetSavedRoot()
|
||||
if type(root["_zbar"]) ~= "table" then
|
||||
root["_zbar"] = {}
|
||||
end
|
||||
local s = root["_zbar"]
|
||||
|
||||
if s.enabled == nil then s.enabled = false end
|
||||
if s.direction == nil then s.direction = "HORIZONTAL" end
|
||||
if type(s.spacing) ~= "number" then s.spacing = 4 end
|
||||
if type(s.size) ~= "number" then s.size = 36 end
|
||||
-- childexpand: which side children appear on (TOP/BOTTOM for H bar, LEFT/RIGHT for V bar)
|
||||
if s.childexpand == nil then s.childexpand = "BOTTOM" end
|
||||
-- childarrange: how children #2+ are laid out (VERTICAL=column, HORIZONTAL=row)
|
||||
if s.childarrange == nil then s.childarrange = "VERTICAL" end
|
||||
if type(s.order) ~= "table" then
|
||||
s.order = {}
|
||||
for i = 1, table.getn(ZBAR_ALL_BUTTONS) do
|
||||
s.order[i] = ZBAR_ALL_BUTTONS[i]
|
||||
end
|
||||
end
|
||||
|
||||
if type(s.visible) ~= "table" then
|
||||
s.visible = {}
|
||||
end
|
||||
|
||||
-- Migration: ensure every known button is present in the order list and
|
||||
-- has an explicit visible entry.
|
||||
local present = {}
|
||||
for _, v in ipairs(s.order) do
|
||||
present[v] = true
|
||||
end
|
||||
for _, v in ipairs(ZBAR_ALL_BUTTONS) do
|
||||
if not present[v] then
|
||||
table.insert(s.order, v)
|
||||
end
|
||||
if s.visible[v] == nil then
|
||||
s.visible[v] = true
|
||||
end
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
-- Returns true when the zBar feature is both enabled AND the anchor frame
|
||||
-- has been created in memory.
|
||||
function MTH_ZBar_IsActive()
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
return (s.enabled == true) and (getglobal(ZBAR_FRAME_NAME) ~= nil)
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- Position persistence
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
function MTH_ZBar_SavePosition()
|
||||
local bar = getglobal(ZBAR_FRAME_NAME)
|
||||
if not (bar and bar.GetPoint) then return end
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
local point, _, relPoint, x, y = bar:GetPoint()
|
||||
if not point then return end
|
||||
s.point = tostring(point)
|
||||
s.relPoint = tostring(relPoint or point)
|
||||
s.x = math.floor((tonumber(x) or 0) + 0.5)
|
||||
s.y = math.floor((tonumber(y) or 0) + 0.5)
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- Anchor frame creation
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
-- Returns the invisible anchor frame (used internally and by button drag code).
|
||||
function MTH_ZBar_GetAnchor()
|
||||
return getglobal(ZBAR_FRAME_NAME)
|
||||
end
|
||||
|
||||
function MTH_ZBar_EnsureAnchor()
|
||||
local existing = getglobal(ZBAR_FRAME_NAME)
|
||||
if existing then
|
||||
existing:Show()
|
||||
return existing
|
||||
end
|
||||
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
|
||||
-- Invisible 1x1 movable frame — used only as a position anchor for the bar.
|
||||
-- Users interact via the zButton buttons themselves (Alt+drag any of them).
|
||||
local bar = CreateFrame("Frame", ZBAR_FRAME_NAME, UIParent)
|
||||
if not bar then return nil end
|
||||
|
||||
bar:SetWidth(1)
|
||||
bar:SetHeight(1)
|
||||
bar:SetFrameStrata("MEDIUM")
|
||||
bar:SetFrameLevel(5)
|
||||
bar:SetMovable(true)
|
||||
bar:SetClampedToScreen(true)
|
||||
|
||||
-- Restore saved position (or fall back to a sensible default)
|
||||
bar:ClearAllPoints()
|
||||
if s.point and s.x and s.y then
|
||||
bar:SetPoint(s.point, UIParent, s.relPoint or s.point, s.x, s.y)
|
||||
else
|
||||
bar:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 400, -140)
|
||||
end
|
||||
|
||||
bar:Show()
|
||||
return bar
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- Layout engine
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
-- Anchors every included+enabled zButton parent relative to the zBar anchor
|
||||
-- (or the previous button in the chain).
|
||||
function MTH_ZBar_ApplyLayout()
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
if not s.enabled then return end
|
||||
|
||||
local bar = getglobal(ZBAR_FRAME_NAME)
|
||||
if not bar then return end
|
||||
|
||||
local direction = s.direction or "HORIZONTAL"
|
||||
local spacing = tonumber(s.spacing) or 4
|
||||
|
||||
local prevFrame = bar
|
||||
local count = 0
|
||||
local placed = {}
|
||||
|
||||
for i = 1, table.getn(s.order) do
|
||||
local buttonName = s.order[i]
|
||||
if type(buttonName) == "string" then
|
||||
local btn = getglobal(buttonName)
|
||||
|
||||
local globalEnabled = ZHunterMod_Saved
|
||||
and type(ZHunterMod_Saved[buttonName]) == "table"
|
||||
and ZHunterMod_Saved[buttonName]["enabled"] ~= false
|
||||
and ZHunterMod_Saved[buttonName]["enabled"] ~= 0
|
||||
local zbarVisible = s.visible[buttonName] ~= false
|
||||
|
||||
if btn and globalEnabled and zbarVisible then
|
||||
btn:ClearAllPoints()
|
||||
if direction == "HORIZONTAL" then
|
||||
btn:SetPoint("LEFT", prevFrame, "RIGHT", spacing, 0)
|
||||
else
|
||||
btn:SetPoint("TOP", prevFrame, "BOTTOM", 0, -spacing)
|
||||
end
|
||||
-- Size, expand direction and child arrangement are handled by each
|
||||
-- bar's own _SetupSizeAndPosition, which now calls MTH_ZBar_ApplyToButton.
|
||||
local setupFunc = getglobal(buttonName .. "_SetupSizeAndPosition")
|
||||
if type(setupFunc) == "function" then
|
||||
setupFunc()
|
||||
end
|
||||
prevFrame = btn
|
||||
count = count + 1
|
||||
placed[buttonName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- For every button NOT placed in the bar, call its own setup function so that:
|
||||
-- * disabled buttons get Hide() called on them immediately
|
||||
-- * excluded buttons get their individual size/expand/position applied
|
||||
for _, buttonName in ipairs(ZBAR_ALL_BUTTONS) do
|
||||
if not placed[buttonName] then
|
||||
local setupFunc = getglobal(buttonName .. "_SetupSizeAndPosition")
|
||||
if type(setupFunc) == "function" then
|
||||
setupFunc()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- Per-button zBar override (called from each zButton's _SetupSizeAndPosition)
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
-- When zBar is active this is the MASTER for size, expand direction and child
|
||||
-- layout. Each zButton's _SetupSizeAndPosition calls this first; if it returns
|
||||
-- true the bar must skip its own saved settings entirely.
|
||||
function MTH_ZBar_ApplyToButton(btn, displayCount)
|
||||
if not (type(MTH_ZBar_IsActive) == "function" and MTH_ZBar_IsActive()) then
|
||||
return false
|
||||
end
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
local btnKey = (btn and btn.name) or (btn and btn.GetName and btn:GetName()) or ""
|
||||
-- Only take master control if this button is actually placed in the active bar.
|
||||
local globalEnabled = ZHunterMod_Saved
|
||||
and type(ZHunterMod_Saved[btnKey]) == "table"
|
||||
and ZHunterMod_Saved[btnKey]["enabled"] ~= false
|
||||
and ZHunterMod_Saved[btnKey]["enabled"] ~= 0
|
||||
if not globalEnabled or s.visible[btnKey] == false then
|
||||
return false
|
||||
end
|
||||
local btnSize = tonumber(s.size) or 36
|
||||
local expandDir = s.childexpand or "BOTTOM"
|
||||
local arrange = s.childarrange or "VERTICAL"
|
||||
local arHoriz = (expandDir == "LEFT")
|
||||
local arVert = (expandDir == "TOP")
|
||||
ZSpellButton_SetSize(btn, btnSize)
|
||||
ZSpellButton_SetExpandDirection(btn, expandDir)
|
||||
if displayCount and displayCount > 1 then
|
||||
local rows = (arrange == "VERTICAL") and displayCount or 1
|
||||
ZSpellButton_ArrangeChildren(btn, rows, displayCount, arHoriz, arVert)
|
||||
end
|
||||
if btn.circle then btn.circle:Hide() end
|
||||
return true
|
||||
end
|
||||
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
-- Enable / disable
|
||||
-- ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
|
||||
|
||||
-- Restores each bar to its individually saved position and hides the anchor.
|
||||
function MTH_ZBar_Release()
|
||||
local bar = getglobal(ZBAR_FRAME_NAME)
|
||||
if bar then bar:Hide() end
|
||||
|
||||
for _, buttonName in ipairs(ZBAR_ALL_BUTTONS) do
|
||||
local btn = getglobal(buttonName)
|
||||
if btn and ZHunterMod_Saved and type(ZHunterMod_Saved[buttonName]) == "table" then
|
||||
-- Restore individually saved position
|
||||
local parentSaved = ZHunterMod_Saved[buttonName]["parent"]
|
||||
if type(parentSaved) == "table" then
|
||||
local px = tonumber(parentSaved["x"])
|
||||
local py = tonumber(parentSaved["y"])
|
||||
local point = parentSaved["point"]
|
||||
local rPoint = parentSaved["relativePoint"] or point
|
||||
if px and py and type(point) == "string" and point ~= "" then
|
||||
btn:ClearAllPoints()
|
||||
btn:SetPoint(point, UIParent, rPoint, px, py)
|
||||
end
|
||||
end
|
||||
-- Restore size, circle, expand direction and child layout from the bar's own saved settings
|
||||
local setupFunc = getglobal(buttonName .. "_SetupSizeAndPosition")
|
||||
if type(setupFunc) == "function" then
|
||||
setupFunc()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Called when the user toggles the Enable checkbox in the options.
|
||||
function MTH_ZBar_SetEnabled(enabled)
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
s.enabled = enabled and true or false
|
||||
if enabled then
|
||||
MTH_ZBar_EnsureAnchor()
|
||||
MTH_ZBar_ApplyLayout()
|
||||
else
|
||||
MTH_ZBar_Release()
|
||||
end
|
||||
end
|
||||
|
||||
-- Resize all bar buttons (and persist the value).
|
||||
function MTH_ZBar_SetSize(size)
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
s.size = tonumber(size) or 36
|
||||
if s.enabled and type(MTH_ZBar_ApplyLayout) == "function" then
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end
|
||||
|
||||
-- Called from DeferredInit / RestoreRuntimeFeatureFlags after saved vars are
|
||||
-- fully loaded; creates the anchor and applies layout only when enabled.
|
||||
function MTH_ZBar_Init()
|
||||
local s = MTH_ZBar_GetSaved()
|
||||
|
||||
-- One-time migration: fix firstbutton values corrupted to "BOTTOM" by an
|
||||
-- early version of zBar that incorrectly wrote per-bar saved variables.
|
||||
if not s._firstbutton_fixed then
|
||||
for _, buttonName in ipairs(ZBAR_ALL_BUTTONS) do
|
||||
if ZHunterMod_Saved and type(ZHunterMod_Saved[buttonName]) == "table" then
|
||||
if ZHunterMod_Saved[buttonName]["firstbutton"] == "BOTTOM" then
|
||||
ZHunterMod_Saved[buttonName]["firstbutton"] = "RIGHT"
|
||||
end
|
||||
end
|
||||
end
|
||||
s._firstbutton_fixed = true
|
||||
-- Re-run each bar's own setup now so the fix takes immediate visual effect
|
||||
for _, buttonName in ipairs(ZBAR_ALL_BUTTONS) do
|
||||
local setupFunc = getglobal(buttonName .. "_SetupSizeAndPosition")
|
||||
if type(setupFunc) == "function" then setupFunc() end
|
||||
end
|
||||
end
|
||||
|
||||
if s.enabled then
|
||||
MTH_ZBar_EnsureAnchor()
|
||||
MTH_ZBar_ApplyLayout()
|
||||
end
|
||||
end
|
||||
@@ -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)
|
||||
@@ -898,12 +897,14 @@ function zButtonAmmo_SetupSizeAndPosition()
|
||||
if arrangeCount < 0 then
|
||||
arrangeCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonAmmo, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonAmmo, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonAmmo, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonAmmo, saved["rows"],
|
||||
arrangeCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonAmmo, arrangeCount)) then
|
||||
ZSpellButton_SetSize(zButtonAmmo, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonAmmo, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonAmmo, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonAmmo, saved["rows"],
|
||||
arrangeCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonAmmo_Reset()
|
||||
|
||||
@@ -39,6 +39,7 @@ local zButtonAspect_MinAuraIntervalCombat = 0.60
|
||||
local zButtonAspect_LastAuraTexture = nil
|
||||
local zButtonAspect_LastParentId = nil
|
||||
local zButtonAspect_AuraProbeTextLeft1 = nil
|
||||
local MTH_ZH_UseNamPower = false
|
||||
|
||||
local function zButtonAspect_GetSaved()
|
||||
local currentRoot = zButtonAspect_GetRoot()
|
||||
@@ -72,6 +73,9 @@ local function zButtonAspect_EnsureConfig()
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if saved["parent"]["smart"] == nil then
|
||||
saved["parent"]["smart"] = true
|
||||
end
|
||||
if not saved["children"] then
|
||||
saved["children"] = {}
|
||||
end
|
||||
@@ -167,6 +171,12 @@ function zButtonAspect_OnEvent()
|
||||
MTH_ZH_AspectAdjust:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||||
MTH_ZH_AspectAdjust:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
MTH_ZH_AspectAdjust:SetScript("OnEvent", MTH_ZH_AspectAdjust_OnEvent)
|
||||
if MTH and MTH.nampower then
|
||||
MTH_ZH_UseNamPower = true
|
||||
MTH_ZH_AspectAdjust:UnregisterEvent("PLAYER_AURAS_CHANGED")
|
||||
MTH_ZH_AspectAdjust:RegisterEvent("BUFF_ADDED_SELF")
|
||||
MTH_ZH_AspectAdjust:RegisterEvent("BUFF_REMOVED_SELF")
|
||||
end
|
||||
zButtonAspect_Tooltip = CreateFrame("GameTooltip", "MTH_ZH_AspectProbe", nil, "GameTooltipTemplate")
|
||||
if zButtonAspect_Tooltip and zButtonAspect_Tooltip.GetName then
|
||||
local tooltipName = zButtonAspect_Tooltip:GetName()
|
||||
@@ -221,12 +231,14 @@ function zButtonAspect_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonAspect, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonAspect, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonAspect, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonAspect, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonAspect, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonAspect, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonAspect, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonAspect, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonAspect, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonAspect_Reset()
|
||||
@@ -255,7 +267,11 @@ function MTH_ZH_AspectAdjust_OnEvent()
|
||||
if not zButtonAspect or not zButtonAspect.count then
|
||||
return
|
||||
end
|
||||
if event == "PLAYER_AURAS_CHANGED" then
|
||||
if event == "BUFF_ADDED_SELF" or event == "BUFF_REMOVED_SELF" then
|
||||
-- NamPower: targeted event, no throttle needed.
|
||||
-- arg3 may not be a reliable DBC ID so we skip spell-ID filtering and let
|
||||
-- the existing texture/change guard below handle early-exit cheaply.
|
||||
elseif event == "PLAYER_AURAS_CHANGED" then
|
||||
local now = GetTime and GetTime() or 0
|
||||
local minInterval = zButtonAspect_MinAuraInterval
|
||||
if UnitAffectingCombat and UnitAffectingCombat("player") then
|
||||
@@ -271,7 +287,8 @@ function MTH_ZH_AspectAdjust_OnEvent()
|
||||
zButtonAspect_SetupSizeAndPosition()
|
||||
zButtonAspect_EnsureValidParentId()
|
||||
end
|
||||
if event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_ENTERING_WORLD"
|
||||
if event == "PLAYER_AURAS_CHANGED" or event == "BUFF_ADDED_SELF" or event == "BUFF_REMOVED_SELF"
|
||||
or event == "PLAYER_ENTERING_WORLD"
|
||||
or event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
|
||||
zButtonAspect_EnsureValidParentId()
|
||||
if not zButtonAspect1.id then
|
||||
@@ -304,13 +321,19 @@ function MTH_ZH_AspectAdjust_OnEvent()
|
||||
end
|
||||
end
|
||||
local i = 1
|
||||
local texture = GetSpellTexture(zButtonAspect1.id, "spell")
|
||||
local texture1 = GetSpellTexture(zButtonAspect1.id, "spell")
|
||||
local texture2 = zButtonAspect2 and zButtonAspect2.id and GetSpellTexture(zButtonAspect2.id, "spell") or nil
|
||||
local buff = UnitBuff("player", i)
|
||||
local spellname, buffname
|
||||
local newParentId = zButtonAspect1.id
|
||||
local smartEnabled = zButtonAspect_GetSaved()["parent"]["smart"] ~= false
|
||||
while buff do
|
||||
if texture == buff and zButtonAspect2.id then
|
||||
newParentId = zButtonAspect2.id
|
||||
if smartEnabled and zButtonAspect2 and zButtonAspect2.id then
|
||||
if texture1 == buff then
|
||||
newParentId = zButtonAspect2.id
|
||||
elseif texture2 and texture2 == buff then
|
||||
newParentId = zButtonAspect1.id
|
||||
end
|
||||
end
|
||||
if needChildIconScan and buttontextures[buff] then
|
||||
if hasDuplicateChildTexture then
|
||||
@@ -332,10 +355,10 @@ function MTH_ZH_AspectAdjust_OnEvent()
|
||||
i = i + 1
|
||||
buff = UnitBuff("player", i)
|
||||
end
|
||||
if event == "PLAYER_AURAS_CHANGED" and zButtonAspect_LastAuraTexture == texture and zButtonAspect.id == newParentId and zButtonAspect_LastParentId == newParentId and not isParentTooltipOwned then
|
||||
if (event == "PLAYER_AURAS_CHANGED" or event == "BUFF_ADDED_SELF" or event == "BUFF_REMOVED_SELF") and zButtonAspect_LastAuraTexture == texture1 and zButtonAspect.id == newParentId and zButtonAspect_LastParentId == newParentId and not isParentTooltipOwned then
|
||||
return
|
||||
end
|
||||
zButtonAspect_LastAuraTexture = texture
|
||||
zButtonAspect_LastAuraTexture = texture1
|
||||
zButtonAspect_LastParentId = newParentId
|
||||
zButtonAspect.id = newParentId
|
||||
ZSpellButton_UpdateButton(zButtonAspect)
|
||||
|
||||
@@ -75,6 +75,9 @@ local function zButtonCompanions_EnsureConfig()
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if saved["parent"]["random"] == nil then
|
||||
saved["parent"]["random"] = false
|
||||
end
|
||||
if not saved["children"]["size"] then
|
||||
saved["children"]["size"] = 36
|
||||
end
|
||||
@@ -177,7 +180,14 @@ local function zButtonCompanions_ApplySpellIds(spellIds)
|
||||
end
|
||||
|
||||
if found > 0 then
|
||||
parent.id = spellIds[1]
|
||||
local saved = zButtonCompanions_GetSaved()
|
||||
if saved["parent"]["random"] and found > 1 then
|
||||
local pick = math.random(1, found)
|
||||
local child = getglobal("zButtonCompanions" .. pick)
|
||||
parent.id = child and child.id or spellIds[1]
|
||||
else
|
||||
parent.id = spellIds[1]
|
||||
end
|
||||
parent.isspell = 1
|
||||
ZSpellButton_UpdateButton(parent)
|
||||
parent:Enable()
|
||||
@@ -227,6 +237,17 @@ function zButtonCompanions_OnEvent()
|
||||
return
|
||||
end
|
||||
zButtonCompanions_CreateButtons()
|
||||
zButtonCompanions.afterclick = function(button)
|
||||
local saved = zButtonCompanions_GetSaved()
|
||||
if saved["parent"]["random"] and zButtonCompanions.found and zButtonCompanions.found > 1 then
|
||||
local pick = math.random(1, zButtonCompanions.found)
|
||||
local child = getglobal("zButtonCompanions" .. pick)
|
||||
if child and child.id then
|
||||
zButtonCompanions.id = child.id
|
||||
ZSpellButton_UpdateButton(zButtonCompanions)
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_ZH_CompanionsAdjust = CreateFrame("Frame", "MTH_ZH_CompanionsAdjust")
|
||||
MTH_ZH_CompanionsAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_CompanionsAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
@@ -264,12 +285,14 @@ function zButtonCompanions_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonCompanions, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonCompanions, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonCompanions, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonCompanions, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonCompanions, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonCompanions, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonCompanions, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonCompanions, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonCompanions, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonCompanions_Reset()
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
local function zButtonCraft_GetRoot()
|
||||
return MTH_ZH_GetSavedRoot()
|
||||
end
|
||||
|
||||
local root = zButtonCraft_GetRoot()
|
||||
if not root["zButtonCraft"] then
|
||||
root["zButtonCraft"] = {}
|
||||
root["zButtonCraft"]["spells"] = {}
|
||||
root["zButtonCraft"]["rows"] = 1
|
||||
root["zButtonCraft"]["horizontal"] = nil
|
||||
root["zButtonCraft"]["vertical"] = nil
|
||||
root["zButtonCraft"]["firstbutton"] = "RIGHT"
|
||||
root["zButtonCraft"]["enabled"] = 0
|
||||
root["zButtonCraft"]["tooltip"] = 1
|
||||
root["zButtonCraft"]["parent"] = {}
|
||||
root["zButtonCraft"]["parent"]["size"] = 36
|
||||
root["zButtonCraft"]["parent"]["hide"] = nil
|
||||
root["zButtonCraft"]["parent"]["circle"] = 1
|
||||
root["zButtonCraft"]["children"] = {}
|
||||
root["zButtonCraft"]["children"]["size"] = 36
|
||||
root["zButtonCraft"]["children"]["hideonclick"] = 1
|
||||
end
|
||||
|
||||
local ZHUNTER_CRAFT_BUTTON_MAX = 14
|
||||
|
||||
local zButtonCraft_LastSpellSignature = nil
|
||||
local zButtonCraft_LastRefreshAt = 0
|
||||
local zButtonCraft_MinRefreshInterval = 0.50
|
||||
|
||||
-- Exact spell names as they appear in the General spellbook tab.
|
||||
-- spellName = the name returned by GetSpellName() to match against (case-insensitive)
|
||||
-- display = label used for tooltips and options
|
||||
local ZCRAFT_PROFESSIONS = {
|
||||
{ spellName = "Alchemy", display = "Alchemy" },
|
||||
{ spellName = "Blacksmithing", display = "Blacksmithing" },
|
||||
{ spellName = "Disenchanting", display = "Disenchanting" },
|
||||
{ spellName = "Enchanting", display = "Enchanting" },
|
||||
{ spellName = "Engineering", display = "Engineering" },
|
||||
{ spellName = "Leatherworking", display = "Leatherworking" },
|
||||
{ spellName = "Tailoring", display = "Tailoring" },
|
||||
{ spellName = "Jewelcrafting", display = "Jewelcrafting" },
|
||||
{ spellName = "Smelting", display = "Smelting" },
|
||||
{ spellName = "Cooking", display = "Cooking" },
|
||||
{ spellName = "First Aid", display = "First Aid" },
|
||||
{ spellName = "Herbalism", display = "Herbalism" },
|
||||
{ spellName = "Survival", display = "Survival" },
|
||||
{ spellName = "Fishing", display = "Fishing" },
|
||||
{ spellName = "Skinning", display = "Skinning" },
|
||||
}
|
||||
|
||||
-- Build a lookup: lowercase spell name → ZCRAFT_PROFESSIONS entry
|
||||
local ZCRAFT_SPELL_LOOKUP = {}
|
||||
for i = 1, table.getn(ZCRAFT_PROFESSIONS) do
|
||||
ZCRAFT_SPELL_LOOKUP[string.lower(ZCRAFT_PROFESSIONS[i].spellName)] = ZCRAFT_PROFESSIONS[i]
|
||||
end
|
||||
|
||||
function zButtonCraft_GetSaved()
|
||||
local currentRoot = zButtonCraft_GetRoot()
|
||||
if not currentRoot["zButtonCraft"] then
|
||||
currentRoot["zButtonCraft"] = {}
|
||||
end
|
||||
return currentRoot["zButtonCraft"]
|
||||
end
|
||||
|
||||
local function zButtonCraft_EnsureConfig()
|
||||
local saved = zButtonCraft_GetSaved()
|
||||
saved["count"] = nil
|
||||
if saved["enabled"] == nil then
|
||||
saved["enabled"] = 0
|
||||
end
|
||||
if saved["tooltip"] == nil then
|
||||
saved["tooltip"] = 1
|
||||
end
|
||||
if not saved["children"] then
|
||||
saved["children"] = {}
|
||||
end
|
||||
if saved["children"]["hideonclick"] == nil then
|
||||
saved["children"]["hideonclick"] = 1
|
||||
end
|
||||
if not saved["spells"] then
|
||||
saved["spells"] = {}
|
||||
end
|
||||
if not saved["parent"] then
|
||||
saved["parent"] = {}
|
||||
end
|
||||
if not saved["parent"]["size"] then
|
||||
saved["parent"]["size"] = 36
|
||||
end
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if not saved["children"]["size"] then
|
||||
saved["children"]["size"] = 36
|
||||
end
|
||||
if not saved["rows"] or saved["rows"] < 1 then
|
||||
saved["rows"] = 1
|
||||
end
|
||||
if not saved["firstbutton"] then
|
||||
saved["firstbutton"] = "RIGHT"
|
||||
end
|
||||
end
|
||||
|
||||
local function zButtonCraft_ApplyRuntimeSettings()
|
||||
if not zButtonCraft then return end
|
||||
local saved = zButtonCraft_GetSaved()
|
||||
zButtonCraft.tooltip = saved["tooltip"] and true or false
|
||||
zButtonCraft.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false
|
||||
zButtonCraft.expandonhover = saved["children"] and saved["children"]["expandonhover"] and true or false
|
||||
zButtonCraft.fadetimer = saved["children"] and tonumber(saved["children"]["fadetimer"]) or 0
|
||||
end
|
||||
|
||||
local function zButtonCraft_GetSpellSignature(spellIds)
|
||||
if type(spellIds) ~= "table" or table.getn(spellIds) == 0 then return "" end
|
||||
local parts = {}
|
||||
for i = 1, table.getn(spellIds) do
|
||||
parts[i] = tostring(spellIds[i] or "")
|
||||
end
|
||||
return table.concat(parts, ",")
|
||||
end
|
||||
|
||||
-- Scans individual spells in the General tab and returns two parallel arrays:
|
||||
-- spellIds = spellbook index of each matched craft spell
|
||||
-- spellNames = display label for that profession
|
||||
-- Results are ordered by ZCRAFT_PROFESSIONS priority.
|
||||
local function zButtonCraft_CollectProfessions()
|
||||
if type(GetNumSpellTabs) ~= "function" or type(GetSpellTabInfo) ~= "function"
|
||||
or type(GetSpellName) ~= "function" then
|
||||
return {}, {}
|
||||
end
|
||||
local bookType = BOOKTYPE_SPELL or "spell"
|
||||
|
||||
-- Find the General tab (always tab 1, but look it up to be safe)
|
||||
local generalOffset, generalEntries
|
||||
local tabCount = GetNumSpellTabs() or 0
|
||||
for tabIndex = 1, tabCount do
|
||||
local tabName, _, tabOffset, tabEntries = GetSpellTabInfo(tabIndex)
|
||||
if tabName and string.lower(tabName) == "general" then
|
||||
generalOffset = tabOffset
|
||||
generalEntries = tabEntries
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not generalOffset or not generalEntries or generalEntries <= 0 then
|
||||
return {}, {}
|
||||
end
|
||||
|
||||
-- Scan every spell in General; collect matches keyed by profession order
|
||||
local found = {} -- profession display → spellbook index (first match wins)
|
||||
for i = 1, generalEntries do
|
||||
local spellIndex = generalOffset + i
|
||||
local spellName = GetSpellName(spellIndex, bookType)
|
||||
if spellName then
|
||||
local prof = ZCRAFT_SPELL_LOOKUP[string.lower(spellName)]
|
||||
if prof and not found[prof.display] then
|
||||
found[prof.display] = spellIndex
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Return results in ZCRAFT_PROFESSIONS order
|
||||
local spellIds = {}
|
||||
local spellNames = {}
|
||||
for profIndex = 1, table.getn(ZCRAFT_PROFESSIONS) do
|
||||
local prof = ZCRAFT_PROFESSIONS[profIndex]
|
||||
local spellIndex = found[prof.display]
|
||||
if spellIndex then
|
||||
table.insert(spellIds, spellIndex)
|
||||
table.insert(spellNames, prof.display)
|
||||
end
|
||||
end
|
||||
|
||||
return spellIds, spellNames
|
||||
end
|
||||
|
||||
local function zButtonCraft_ApplySpellIds(spellIds)
|
||||
if not zButtonCraft then return 0 end
|
||||
local parent = zButtonCraft
|
||||
local childCount = parent.count or ZHUNTER_CRAFT_BUTTON_MAX
|
||||
|
||||
for i = 1, childCount do
|
||||
local child = getglobal("zButtonCraft" .. i)
|
||||
if child then
|
||||
child:Hide()
|
||||
child.id = nil
|
||||
child.icon = nil
|
||||
child.isspell = nil
|
||||
end
|
||||
end
|
||||
|
||||
local found = 0
|
||||
for i = 1, table.getn(spellIds) do
|
||||
if found >= childCount then break end
|
||||
local spellId = spellIds[i]
|
||||
if spellId then
|
||||
found = found + 1
|
||||
local child = getglobal("zButtonCraft" .. found)
|
||||
if child then
|
||||
child.id = spellId
|
||||
child.isspell = 1
|
||||
ZSpellButton_UpdateButton(child)
|
||||
child:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found > 0 then
|
||||
parent.id = spellIds[1]
|
||||
parent.isspell = 1
|
||||
ZSpellButton_UpdateButton(parent)
|
||||
parent:Enable()
|
||||
parent:Show()
|
||||
else
|
||||
parent.id = nil
|
||||
parent:Hide()
|
||||
end
|
||||
|
||||
return found
|
||||
end
|
||||
|
||||
local function zButtonCraft_RefreshProfessions()
|
||||
if not zButtonCraft then return 0 end
|
||||
local saved = zButtonCraft_GetSaved()
|
||||
local freshIds, freshNames = zButtonCraft_CollectProfessions()
|
||||
|
||||
-- Build lookup: display name → spellbook index from the fresh scan
|
||||
local nameToId = {}
|
||||
for i = 1, table.getn(freshNames) do
|
||||
nameToId[freshNames[i]] = freshIds[i]
|
||||
end
|
||||
|
||||
-- Honour saved user order when it exists
|
||||
local existing = saved["spells"]
|
||||
if existing and table.getn(existing) > 0 then
|
||||
local orderedNames = {}
|
||||
local orderedIds = {}
|
||||
local seen = {}
|
||||
-- Keep entries that still exist, in the user's order
|
||||
for i = 1, table.getn(existing) do
|
||||
local name = existing[i]
|
||||
if nameToId[name] then
|
||||
table.insert(orderedNames, name)
|
||||
table.insert(orderedIds, nameToId[name])
|
||||
seen[name] = true
|
||||
end
|
||||
end
|
||||
-- Append any newly learned professions at the end
|
||||
for i = 1, table.getn(freshNames) do
|
||||
if not seen[freshNames[i]] then
|
||||
table.insert(orderedNames, freshNames[i])
|
||||
table.insert(orderedIds, freshIds[i])
|
||||
end
|
||||
end
|
||||
freshNames = orderedNames
|
||||
freshIds = orderedIds
|
||||
end
|
||||
|
||||
saved["spellIds"] = freshIds
|
||||
saved["spells"] = freshNames
|
||||
zButtonCraft.spells = freshNames
|
||||
zButtonCraft.found = zButtonCraft_ApplySpellIds(freshIds)
|
||||
zButtonCraft_ApplyRuntimeSettings()
|
||||
return zButtonCraft.found or 0
|
||||
end
|
||||
|
||||
function zButtonCraft_OnLoad()
|
||||
this:RegisterEvent("VARIABLES_LOADED")
|
||||
end
|
||||
|
||||
function zButtonCraft_OnEvent()
|
||||
if event == "VARIABLES_LOADED" then
|
||||
if not zButtonCraft then return end
|
||||
if UnitClass("player") ~= ZHUNTER_HUNTER then
|
||||
zButtonCraft:UnregisterAllEvents()
|
||||
zButtonCraft:Hide()
|
||||
return
|
||||
end
|
||||
zButtonCraft_CreateButtons()
|
||||
MTH_ZH_CraftAdjust = CreateFrame("Frame", "MTH_ZH_CraftAdjust")
|
||||
MTH_ZH_CraftAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_CraftAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_ZH_CraftAdjust:SetScript("OnEvent", MTH_ZH_CraftAdjust_OnEvent)
|
||||
zButtonCraft_SetupSizeAndPosition()
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonCraft_CreateButtons()
|
||||
zButtonCraft_EnsureConfig()
|
||||
if not zButtonCraft._mthChildrenCreated then
|
||||
ZSpellButton_CreateChildren(zButtonCraft, "zButtonCraft", ZHUNTER_CRAFT_BUTTON_MAX)
|
||||
zButtonCraft._mthChildrenCreated = 1
|
||||
end
|
||||
zButtonCraft_RefreshProfessions()
|
||||
end
|
||||
|
||||
function zButtonCraft_SetupSizeAndPosition()
|
||||
local saved = zButtonCraft_GetSaved()
|
||||
if saved["enabled"] == false or saved["enabled"] == 0 then
|
||||
if MTH_ZH_CraftAdjust then
|
||||
MTH_ZH_CraftAdjust:UnregisterAllEvents()
|
||||
MTH_ZH_CraftAdjust:SetScript("OnEvent", nil)
|
||||
end
|
||||
if zButtonCraft and zButtonCraft.Hide then
|
||||
zButtonCraft:Hide()
|
||||
end
|
||||
return
|
||||
end
|
||||
if MTH_ZH_CraftAdjust then
|
||||
MTH_ZH_CraftAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_CraftAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_ZH_CraftAdjust:SetScript("OnEvent", MTH_ZH_CraftAdjust_OnEvent)
|
||||
end
|
||||
local displayCount = zButtonCraft.found or 0
|
||||
if displayCount < 0 then displayCount = 0 end
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonCraft, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonCraft, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonCraft, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonCraft, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonCraft, saved["rows"],
|
||||
displayCount, saved["horizontal"], saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonCraft_Reset()
|
||||
local currentRoot = zButtonCraft_GetRoot()
|
||||
currentRoot["zButtonCraft"] = {}
|
||||
local saved = zButtonCraft_GetSaved()
|
||||
saved["spells"] = {}
|
||||
saved["rows"] = 1
|
||||
saved["horizontal"] = nil
|
||||
saved["vertical"] = nil
|
||||
saved["firstbutton"] = "RIGHT"
|
||||
saved["enabled"] = 1
|
||||
saved["tooltip"] = 1
|
||||
saved["parent"] = {}
|
||||
saved["parent"]["size"] = 36
|
||||
saved["parent"]["hide"] = nil
|
||||
saved["parent"]["circle"] = 1
|
||||
saved["children"] = {}
|
||||
saved["children"]["size"] = 36
|
||||
saved["children"]["hideonclick"] = 1
|
||||
zButtonCraft_EnsureConfig()
|
||||
end
|
||||
|
||||
function MTH_ZH_CraftAdjust_OnEvent()
|
||||
if not zButtonCraft then return end
|
||||
if event == "SPELLS_CHANGED" then
|
||||
local now = GetTime and GetTime() or 0
|
||||
if now > 0 and zButtonCraft_LastRefreshAt > 0 and (now - zButtonCraft_LastRefreshAt) < zButtonCraft_MinRefreshInterval then
|
||||
return
|
||||
end
|
||||
local spellIds, _ = zButtonCraft_CollectProfessions()
|
||||
local signature = zButtonCraft_GetSpellSignature(spellIds)
|
||||
if signature == zButtonCraft_LastSpellSignature and not GameTooltip:IsOwned(zButtonCraft) then
|
||||
return
|
||||
end
|
||||
zButtonCraft_LastSpellSignature = signature
|
||||
zButtonCraft_LastRefreshAt = now
|
||||
end
|
||||
zButtonCraft_RefreshProfessions()
|
||||
zButtonCraft_SetupSizeAndPosition()
|
||||
if GameTooltip:IsOwned(zButtonCraft) then
|
||||
ZSpellButtonParent_OnEnter(zButtonCraft)
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonCraft_KeyBinding(index)
|
||||
if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then return end
|
||||
local button
|
||||
if index then
|
||||
button = getglobal("zButtonCraft" .. index)
|
||||
else
|
||||
button = zButtonCraft
|
||||
end
|
||||
if button and button.id then
|
||||
CastSpell(button.id, "spell")
|
||||
if zButtonCraft.hideonclick then
|
||||
if type(ZSpellButton_SetChildrenExpanded) == "function" then
|
||||
ZSpellButton_SetChildrenExpanded(zButtonCraft, false)
|
||||
elseif zButtonCraft.children then
|
||||
zButtonCraft.children:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SLASH_zButtonCraft1 = "/ZCraft"
|
||||
SlashCmdList["zButtonCraft"] = function(msg)
|
||||
if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Craft button is disabled while module 'zhunter' is disabled.") then
|
||||
return
|
||||
end
|
||||
if msg == "reset" then
|
||||
zButtonCraft_Reset()
|
||||
zButtonCraft:ClearAllPoints()
|
||||
zButtonCraft:SetPoint("TOP", UIParent, "TOP", 0, -558)
|
||||
zButtonCraft_RefreshProfessions()
|
||||
zButtonCraft_SetupSizeAndPosition()
|
||||
else
|
||||
MTH_ZH_Print("Possible Commands: \"reset\"")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,14 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Frame name="zButtonCraftFrame">
|
||||
</Frame>
|
||||
<CheckButton name="zButtonCraft" inherits="ZSpellButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" relativeTo="UIParent" relativePoint="CENTER">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-288"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
</Ui>
|
||||
@@ -75,6 +75,12 @@ local function zButtonMounts_EnsureConfig()
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if saved["parent"]["random"] == nil then
|
||||
saved["parent"]["random"] = false
|
||||
end
|
||||
if saved["parent"]["aqfilter"] == nil then
|
||||
saved["parent"]["aqfilter"] = true
|
||||
end
|
||||
if not saved["children"]["size"] then
|
||||
saved["children"]["size"] = 36
|
||||
end
|
||||
@@ -177,7 +183,14 @@ local function zButtonMounts_ApplySpellIds(spellIds)
|
||||
end
|
||||
|
||||
if found > 0 then
|
||||
parent.id = spellIds[1]
|
||||
local saved = zButtonMounts_GetSaved()
|
||||
if saved["parent"]["random"] and found > 1 then
|
||||
local pick = math.random(1, found)
|
||||
local child = getglobal("zButtonMounts" .. pick)
|
||||
parent.id = child and child.id or spellIds[1]
|
||||
else
|
||||
parent.id = spellIds[1]
|
||||
end
|
||||
parent.isspell = 1
|
||||
ZSpellButton_UpdateButton(parent)
|
||||
parent:Enable()
|
||||
@@ -190,6 +203,49 @@ local function zButtonMounts_ApplySpellIds(spellIds)
|
||||
return found
|
||||
end
|
||||
|
||||
local function zButtonMounts_IsQirajiSpell(spellId)
|
||||
local bookType = BOOKTYPE_SPELL or "spell"
|
||||
local name = GetSpellName(spellId, bookType)
|
||||
if name and string.find(string.lower(name), "qiraji", 1, true) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function zButtonMounts_IsInAQ40()
|
||||
local zone = GetRealZoneText and GetRealZoneText() or ""
|
||||
if zone == "Temple of Ahn'Qiraj" or zone == "Ahn'Qiraj" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function zButtonMounts_FilterAQ(spellIds)
|
||||
local saved = zButtonMounts_GetSaved()
|
||||
if not saved["parent"]["aqfilter"] then
|
||||
return spellIds
|
||||
end
|
||||
local inAQ = zButtonMounts_IsInAQ40()
|
||||
local filtered = {}
|
||||
for i = 1, table.getn(spellIds) do
|
||||
local isQiraji = zButtonMounts_IsQirajiSpell(spellIds[i])
|
||||
if inAQ then
|
||||
if isQiraji then
|
||||
table.insert(filtered, spellIds[i])
|
||||
end
|
||||
else
|
||||
if not isQiraji then
|
||||
table.insert(filtered, spellIds[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fallback: if filtering removed everything, return original list
|
||||
if table.getn(filtered) == 0 then
|
||||
return spellIds
|
||||
end
|
||||
return filtered
|
||||
end
|
||||
|
||||
local function zButtonMounts_RefreshSpells()
|
||||
if not zButtonMounts then
|
||||
return 0
|
||||
@@ -207,6 +263,7 @@ local function zButtonMounts_RefreshSpells()
|
||||
saved["spellIds"] = spellIds
|
||||
saved["spells"] = spellNames
|
||||
zButtonMounts.spells = spellNames
|
||||
spellIds = zButtonMounts_FilterAQ(spellIds)
|
||||
zButtonMounts.found = zButtonMounts_ApplySpellIds(spellIds)
|
||||
zButtonMounts_ApplyRuntimeSettings()
|
||||
return zButtonMounts.found or 0
|
||||
@@ -227,9 +284,21 @@ function zButtonMounts_OnEvent()
|
||||
return
|
||||
end
|
||||
zButtonMounts_CreateButtons()
|
||||
zButtonMounts.afterclick = function(button)
|
||||
local saved = zButtonMounts_GetSaved()
|
||||
if saved["parent"]["random"] and zButtonMounts.found and zButtonMounts.found > 1 then
|
||||
local pick = math.random(1, zButtonMounts.found)
|
||||
local child = getglobal("zButtonMounts" .. pick)
|
||||
if child and child.id then
|
||||
zButtonMounts.id = child.id
|
||||
ZSpellButton_UpdateButton(zButtonMounts)
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_ZH_MountsAdjust = CreateFrame("Frame", "MTH_ZH_MountsAdjust")
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||||
MTH_ZH_MountsAdjust:SetScript("OnEvent", MTH_ZH_MountsAdjust_OnEvent)
|
||||
zButtonMounts_SetupSizeAndPosition()
|
||||
end
|
||||
@@ -261,18 +330,21 @@ function zButtonMounts_SetupSizeAndPosition()
|
||||
if MTH_ZH_MountsAdjust then
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_ZH_MountsAdjust:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||||
MTH_ZH_MountsAdjust:SetScript("OnEvent", MTH_ZH_MountsAdjust_OnEvent)
|
||||
end
|
||||
local displayCount = zButtonMounts.found or 0
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonMounts, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonMounts, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonMounts, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonMounts, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonMounts, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonMounts, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonMounts, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonMounts, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonMounts, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonMounts_Reset()
|
||||
|
||||
@@ -84,6 +84,9 @@ local function zButtonPet_EnsureConfig()
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if saved["parent"]["smart"] == nil then
|
||||
saved["parent"]["smart"] = true
|
||||
end
|
||||
if saved["enabled"] == nil then
|
||||
saved["enabled"] = 1
|
||||
end
|
||||
@@ -258,12 +261,14 @@ function zButtonPet_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonPet, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonPet, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonPet, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonPet, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonPet, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonPet, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonPet, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonPet, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonPet, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonPet_Reset()
|
||||
@@ -502,7 +507,11 @@ local function zButtonPet_RefreshParentSpell()
|
||||
local id = zButtonPet.id
|
||||
name = nil
|
||||
|
||||
if hasCurrentPet == false and hasLivePet == false then
|
||||
if saved["parent"]["smart"] == false then
|
||||
if choiceId then
|
||||
id = choiceId
|
||||
end
|
||||
elseif hasCurrentPet == false and hasLivePet == false then
|
||||
name = ZHUNTER_PET_TAMING
|
||||
elseif not status then
|
||||
name = ZHUNTER_PET_CALL
|
||||
|
||||
@@ -648,10 +648,12 @@ function zButtonRanged_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonRanged, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonRanged, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonRanged, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonRanged, saved["rows"], displayCount, saved["horizontal"], saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonRanged, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonRanged, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonRanged, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonRanged, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonRanged, saved["rows"], displayCount, saved["horizontal"], saved["vertical"])
|
||||
end
|
||||
zButtonRanged_UpdateButton(zButtonRanged)
|
||||
end
|
||||
|
||||
|
||||
@@ -267,12 +267,14 @@ function zButtonToys_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonToys, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonToys, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonToys, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonToys, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonToys, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonToys, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonToys, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonToys, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonToys, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonToys_Reset()
|
||||
|
||||
@@ -5,7 +5,7 @@ end
|
||||
local root = zButtonTrack_GetRoot()
|
||||
if not root["zButtonTrack"] then
|
||||
root["zButtonTrack"] = {}
|
||||
root["zButtonTrack"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
|
||||
root["zButtonTrack"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
|
||||
root["zButtonTrack"]["rows"] = 1
|
||||
root["zButtonTrack"]["horizontal"] = nil
|
||||
root["zButtonTrack"]["vertical"] = nil
|
||||
@@ -33,7 +33,8 @@ ZHunterMod_Track_Spells = {
|
||||
ZHUNTER_TRACK_MINERALS,
|
||||
ZHUNTER_TRACK_HERBS,
|
||||
ZHUNTER_TRACK_TREASURE,
|
||||
ZHUNTER_TRACK_TREES
|
||||
ZHUNTER_TRACK_TREES,
|
||||
ZHUNTER_TRACK_FISH
|
||||
}
|
||||
|
||||
local ZHUNTER_TRACK_MAX = table.getn(ZHunterMod_Track_Spells)
|
||||
@@ -93,6 +94,32 @@ local function zButtonTrack_FindSpellIdByTrackingName(trackingName)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function zButtonTrack_ScanBuffsForTrackingSpell()
|
||||
if not zButtonTrack or not zButtonTrack.count then
|
||||
return nil, nil
|
||||
end
|
||||
local childTextures = {}
|
||||
for i = 1, zButtonTrack.count do
|
||||
local child = getglobal("zButtonTrack" .. i)
|
||||
if child and child.id then
|
||||
local tex = GetSpellTexture and GetSpellTexture(child.id, "spell") or nil
|
||||
if tex then
|
||||
childTextures[tex] = child.id
|
||||
end
|
||||
end
|
||||
end
|
||||
local buffIndex = 1
|
||||
local buff = UnitBuff("player", buffIndex)
|
||||
while buff do
|
||||
if childTextures[buff] then
|
||||
return childTextures[buff], buff
|
||||
end
|
||||
buffIndex = buffIndex + 1
|
||||
buff = UnitBuff("player", buffIndex)
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function zButtonTrack_GetActiveTrackingSpellId()
|
||||
local activeName = nil
|
||||
local activeTexture = nil
|
||||
@@ -112,10 +139,26 @@ local function zButtonTrack_GetActiveTrackingSpellId()
|
||||
return byName, activeTexture
|
||||
end
|
||||
if activeTexture then
|
||||
return zButtonTrack_FindSpellIdByTrackingTexture(activeTexture), activeTexture
|
||||
local byTex = zButtonTrack_FindSpellIdByTrackingTexture(activeTexture)
|
||||
if byTex then
|
||||
return byTex, activeTexture
|
||||
end
|
||||
end
|
||||
local buff = GetTrackingTexture and GetTrackingTexture() or nil
|
||||
return zButtonTrack_FindSpellIdByTrackingTexture(buff), buff
|
||||
-- Fallback: scan player buffs for tracking spell icon textures
|
||||
local buffId, buffTex = zButtonTrack_ScanBuffsForTrackingSpell()
|
||||
if buffId then
|
||||
return buffId, buffTex
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function zButtonTrack_SmartParentId(activeChildId)
|
||||
local child1 = getglobal("zButtonTrack1")
|
||||
local child2 = getglobal("zButtonTrack2")
|
||||
if not child1 or not child1.id then return nil end
|
||||
if not child2 or not child2.id then return child1.id end
|
||||
if activeChildId == child1.id then return child2.id end
|
||||
return child1.id
|
||||
end
|
||||
|
||||
local function zButtonTrack_AfterClick(button)
|
||||
@@ -123,9 +166,19 @@ local function zButtonTrack_AfterClick(button)
|
||||
return
|
||||
end
|
||||
if button and button.id then
|
||||
zButtonTrack.id = button.id
|
||||
ZSpellButton_UpdateButton(zButtonTrack)
|
||||
ZSpellButton_UpdateCooldown(zButtonTrack)
|
||||
local saved = zButtonTrack_GetSaved()
|
||||
local newId
|
||||
if saved["parent"]["smart"] ~= false then
|
||||
newId = zButtonTrack_SmartParentId(button.id)
|
||||
else
|
||||
local firstChild = getglobal("zButtonTrack1")
|
||||
newId = firstChild and firstChild.id
|
||||
end
|
||||
if newId and newId ~= zButtonTrack.id then
|
||||
zButtonTrack.id = newId
|
||||
ZSpellButton_UpdateButton(zButtonTrack)
|
||||
ZSpellButton_UpdateCooldown(zButtonTrack)
|
||||
end
|
||||
zButtonTrack_SyncActiveChild(button.id)
|
||||
if GetSpellTexture then
|
||||
zButtonTrack_LastTrackingTexture = GetSpellTexture(button.id, "spell") or zButtonTrack_LastTrackingTexture
|
||||
@@ -156,34 +209,36 @@ local function zButtonTrack_RefreshTrackingState()
|
||||
if not zButtonTrack or not zButtonTrack.count then
|
||||
return
|
||||
end
|
||||
local newId, trackingTexture = zButtonTrack_GetActiveTrackingSpellId()
|
||||
local buff = trackingTexture or (GetTrackingTexture and GetTrackingTexture() or nil)
|
||||
if not newId and not buff then
|
||||
if zButtonTrack.id then
|
||||
zButtonTrack_SyncActiveChild(zButtonTrack.id)
|
||||
else
|
||||
zButtonTrack_LastTrackingTexture = nil
|
||||
zButtonTrack_SyncActiveChild(nil)
|
||||
local saved = zButtonTrack_GetSaved()
|
||||
local firstChild = getglobal("zButtonTrack1")
|
||||
|
||||
-- Smart OFF: always show first child
|
||||
if saved["parent"]["smart"] == false then
|
||||
if firstChild and firstChild.id and zButtonTrack.id ~= firstChild.id then
|
||||
zButtonTrack.id = firstChild.id
|
||||
ZSpellButton_UpdateButton(zButtonTrack)
|
||||
ZSpellButton_UpdateCooldown(zButtonTrack)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Smart ON: detect which tracking is active, show the OTHER one
|
||||
local activeId = zButtonTrack_GetActiveTrackingSpellId()
|
||||
local newId = zButtonTrack_SmartParentId(activeId)
|
||||
if not newId then
|
||||
newId = zButtonTrack.id or nil
|
||||
end
|
||||
if not newId then
|
||||
return
|
||||
end
|
||||
if zButtonTrack_NormalizeTexture(zButtonTrack_LastTrackingTexture) == zButtonTrack_NormalizeTexture(buff) and zButtonTrack.id == newId then
|
||||
zButtonTrack_SyncActiveChild(newId)
|
||||
return
|
||||
newId = firstChild and firstChild.id
|
||||
end
|
||||
if not newId then return end
|
||||
|
||||
-- Sync checked state on children
|
||||
zButtonTrack_SyncActiveChild(activeId)
|
||||
|
||||
if zButtonTrack.id == newId then return end
|
||||
|
||||
local isOwned = GameTooltip and GameTooltip.IsOwned and GameTooltip:IsOwned(zButtonTrack)
|
||||
zButtonTrack_LastTrackingTexture = buff
|
||||
zButtonTrack.id = newId
|
||||
ZSpellButton_UpdateButton(zButtonTrack)
|
||||
ZSpellButton_UpdateCooldown(zButtonTrack)
|
||||
zButtonTrack_SyncActiveChild(newId)
|
||||
if isOwned then
|
||||
ZSpellButtonParent_OnEnter(zButtonTrack)
|
||||
end
|
||||
@@ -221,6 +276,9 @@ local function zButtonTrack_EnsureConfig()
|
||||
if saved["parent"]["circle"] == nil then
|
||||
saved["parent"]["circle"] = 1
|
||||
end
|
||||
if saved["parent"]["smart"] == nil then
|
||||
saved["parent"]["smart"] = true
|
||||
end
|
||||
if not saved["children"] then
|
||||
saved["children"] = {}
|
||||
end
|
||||
@@ -295,7 +353,7 @@ function zButtonTrack_CreateButtons()
|
||||
for i=1, table.getn(ZHunterMod_Track_Spells) do
|
||||
if not tonumber(saved["spells"][i]) then
|
||||
info = ZHunterMod_Track_Spells
|
||||
saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
|
||||
break
|
||||
end
|
||||
local spellIndex = saved["spells"][i]
|
||||
@@ -323,6 +381,7 @@ function zButtonTrack_SetupSizeAndPosition()
|
||||
end
|
||||
if MTH_ZH_TrackAdjust then
|
||||
MTH_ZH_TrackAdjust:RegisterEvent("MINIMAP_UPDATE_TRACKING")
|
||||
MTH_ZH_TrackAdjust:RegisterEvent("PLAYER_AURAS_CHANGED")
|
||||
MTH_ZH_TrackAdjust:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_ZH_TrackAdjust:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_ZH_TrackAdjust:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||||
@@ -333,12 +392,14 @@ function zButtonTrack_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonTrack, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonTrack, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonTrack, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonTrack, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonTrack, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonTrack, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonTrack, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonTrack, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonTrack, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
zButtonTrack_RefreshTrackingState()
|
||||
end
|
||||
|
||||
@@ -346,7 +407,7 @@ function zButtonTrack_Reset()
|
||||
local currentRoot = zButtonTrack_GetRoot()
|
||||
currentRoot["zButtonTrack"] = {}
|
||||
local saved = zButtonTrack_GetSaved()
|
||||
saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
|
||||
saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
|
||||
saved["rows"] = 1
|
||||
saved["horizontal"] = nil
|
||||
saved["vertical"] = nil
|
||||
@@ -360,7 +421,7 @@ function zButtonTrack_Reset()
|
||||
saved["children"] = {}
|
||||
saved["children"]["size"] = 36
|
||||
saved["children"]["hideonclick"] = 1
|
||||
saved["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||
saved["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||
zButtonTrack_EnsureConfig()
|
||||
end
|
||||
|
||||
@@ -372,7 +433,7 @@ function MTH_ZH_TrackAdjust_OnEvent()
|
||||
zButtonTrack_CreateButtons()
|
||||
zButtonTrack_SetupSizeAndPosition()
|
||||
end
|
||||
if event == "MINIMAP_UPDATE_TRACKING" or event == "PLAYER_ENTERING_WORLD"
|
||||
if event == "MINIMAP_UPDATE_TRACKING" or event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_ENTERING_WORLD"
|
||||
or event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" or event == "LEARNED_SPELL_IN_TAB" then
|
||||
zButtonTrack_RefreshTrackingState()
|
||||
end
|
||||
|
||||
@@ -175,12 +175,14 @@ function zButtonTrap_SetupSizeAndPosition()
|
||||
if displayCount < 0 then
|
||||
displayCount = 0
|
||||
end
|
||||
ZSpellButton_SetSize(zButtonTrap, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonTrap, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonTrap, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonTrap, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
if not (type(MTH_ZBar_ApplyToButton) == "function" and MTH_ZBar_ApplyToButton(zButtonTrap, displayCount)) then
|
||||
ZSpellButton_SetSize(zButtonTrap, saved["parent"]["size"])
|
||||
ZSpellButton_SetSize(zButtonTrap, saved["children"]["size"], 1)
|
||||
ZSpellButton_SetExpandDirection(zButtonTrap, saved["firstbutton"])
|
||||
ZSpellButton_ArrangeChildren(zButtonTrap, saved["rows"],
|
||||
displayCount, saved["horizontal"],
|
||||
saved["vertical"])
|
||||
end
|
||||
end
|
||||
|
||||
function zButtonTrap_Reset()
|
||||
|
||||
Reference in New Issue
Block a user