mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-22 07:36:58 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99268643eb | |||
| 51d4c785fa | |||
| c81ce85950 | |||
| 5c08656532 | |||
| 43e23e7616 | |||
| f7b0318179 |
@@ -2,6 +2,88 @@
|
||||
|
||||
All notable changes to MetaHunt will be documented in this file.
|
||||
|
||||
|
||||
## [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.0
|
||||
## 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,6 +78,7 @@ 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))
|
||||
|
||||
+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.0"
|
||||
|
||||
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.0",
|
||||
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)
|
||||
|
||||
+279
-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,169 @@ 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)
|
||||
|
||||
-- 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()
|
||||
|
||||
@@ -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" },
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
+460
-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)
|
||||
|
||||
+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
@@ -298,7 +298,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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
+668
-246
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -898,12 +898,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 = "Mining", 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()
|
||||
|
||||
@@ -93,6 +93,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 +138,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 +165,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 +208,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 +275,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
|
||||
@@ -323,6 +380,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 +391,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
|
||||
|
||||
@@ -372,7 +432,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